Shapes

A DiagramSpec says what to draw (the size of each set and of each overlap) but carries no geometry. Geometry only appears at fit time, when you pick a shape primitive and the fitter solves for the position and size of one such shape per set so that the overlaps reproduce the requested areas as closely as possible. The same spec can therefore be drawn with any of four primitives:

  • Circle: the default, simplest, and fastest.
  • Ellipse: the most expressive, and the only primitive that scales to four- and five-set Venn diagrams.
  • Square and Rectangle: axis-aligned, for a layout-board look and exact, rotation-free regions.

Every primitive is area-true: the fitter never distorts a shape’s reported area to improve a fit. What differs between them is how many degrees of freedom each shape gives the optimizer, and that is what this page is about.

Circle

A circle is [x, y, r]: a center and a radius, three degrees of freedom per set. It has no orientation, so there is nothing to rotate and nothing to skew; the fitter only chooses where each disc sits and how big it is. That makes circles the quickest shape to fit and the easiest to read, and for two or three sets they are usually all you need. Circles are the default for euler(), and eunoia has canonical Venn layouts for them up to three sets.

A symmetric three-set spec fitted with circles.

Ellipse

An ellipse is [x, y, a, b, φ]: a center, two semi-axes, and a rotation angle, giving five degrees of freedom per set. The extra two parameters let a set stretch and turn, which is what you need when the pairwise overlaps are lopsided enough that no arrangement of round discs can satisfy them. Ellipses are also the only primitive that reaches beyond three sets: eunoia ships canonical four- and five-set Venn arrangements (the Wilkinson and Edwards ellipse layouts) for them.

That freedom comes at a cost: more free parameters mean a rougher loss surface, so ellipse fits benefit from more restarts and lean more often on the optimizer’s global-escape stage (see the fitter pipeline for how that works).

A four-set spec fitted with ellipses; no four-circle diagram realizes every region.

Square

A square is [x, y, side]: a center and a side length, three degrees of freedom, the same count as a circle. Squares are kept axis-aligned: rotation is not part of the parameter vector, so the diagram keeps a tidy, grid-like orientation. Because the intersection of axis-aligned squares is itself an axis-aligned rectangle, the overlap areas are computed exactly in closed form, with no polygon clipping. Like circles, squares support canonical Venn layouts up to three sets; there is no axis-aligned-square Venn for four or more sets.

The same three-set spec fitted with axis-aligned squares.

Rectangle

A rectangle is [x, y, w, h]: a center, a width, and a height, giving four degrees of freedom. It is the square with a free aspect ratio: wide or tall, but, like the square, never rotated. It keeps the same exact closed-form region areas and the same three-set limit on canonical Venn layouts. Prefer rectangles over squares when your sets differ enough in size that a fixed aspect ratio is too constraining.

A two-set spec with unequal sizes fitted with rectangles.

Choosing a shape

A few rules of thumb:

  • Two or three sets: start with circles. They are fast, legible, and usually faithful. Check the goodness-of-fit metrics. If a three-set fit is poor, the overlaps are probably too lopsided for circles, and ellipses will do better.
  • Four or five sets: use ellipses. They are the only primitive expressive enough to realize these layouts, and the only one with canonical Venn arrangements that far.
  • A grid or layout-board look, or exact rotation-free regions: use squares or rectangles. Pick rectangles when the sets vary enough in size that a fixed aspect ratio would be too constraining.

The canonical Venn support per primitive sets a hard ceiling on set count for true Venn diagrams:

ShapeParametersDegrees of freedomCanonical Venn
Circle[x, y, r]3up to 3 sets
Ellipse[x, y, a, b, φ]5up to 5 sets
Square[x, y, side]3up to 3 sets
Rectangle[x, y, w, h]4up to 3 sets

Area-proportional Euler diagrams are not bound by these ceilings (you can fit more sets), but the more free parameters a shape has, the harder the fit, so expect ellipse fits to take longer and to vary more between runs than circle fits.

Selecting a shape in code

In JavaScript, the shape is the shape option on euler() and venn(). It accepts "circle", "ellipse", "square", or "rectangle"; euler() defaults to "circle" and venn() to "ellipse" (the only shape that covers n = 4..5).

import { euler } from "@jolars/eunoia";

const layout = euler({
  sets: { A: 10, B: 10, "A&B": 3 },
  shape: "ellipse",
});

In Rust, the shape is a type parameter on the fitter, defaulting to Circle:

use eunoia::{Ellipse, Fitter};

let layout = Fitter::<Ellipse>::new(&spec).fit()?;

A type becomes fittable by implementing the DiagramShape trait, which also defines each shape’s two parameter encodings (a human-friendly geometric one and an internal optimizer one). For the trait and the encodings in full, see the architecture notes in CONTRIBUTING.md and the rustdoc. (Polygon is used for output and region extraction, not for fitting, so it is not one of these primitives.)

See also

Documentation for Eunoia v1.7.0