Eunoia is a Rust library for area-proportional Euler and Venn diagrams. Give it a set of sizes and intersections and it fits a layout of shapes whose areas match your data as closely as possible. It is a ground-up rewrite of the R package eulerr: faster, more flexible, and built to power bindings across many languages.
Highlights
- Four shape families: fit with circles, ellipses, squares, or rectangles; the engine is shape-agnostic until fit time.
- Robust fitting: MDS initialization followed by a global/local optimization pipeline (LM, CMA-ES, trust-region) that mirrors and improves on eulerr.
- Venn diagrams too: canonical n-set Venn arrangements (circles for n≤3, ellipses for n=4-5) independent of the fitter.
- Renderable output: region polygon extraction, clipping, and automatic label placement, ready to draw as SVG.
- Many targets: pure Rust core that ships to JavaScript via WebAssembly and to other languages through a small C ABI.
Quick Start (Rust)
cargo add eunoia
use eunoia::geometry::shapes::Ellipse; use eunoia::{DiagramSpecBuilder, Fitter, InputType}; fn main() { let spec = DiagramSpecBuilder::new() .set("Adventure", 20.0) .set("Comedy", 14.0) .set("Drama", 18.0) .intersection(&["Adventure", "Comedy"], 6.0) .intersection(&["Adventure", "Drama"], 5.0) .intersection(&["Comedy", "Drama"], 4.0) .intersection(&["Adventure", "Comedy", "Drama"], 2.0) .input_type(InputType::Exclusive) .build() .unwrap(); // Swap `Ellipse` for `Circle`, `Square`, or `Rectangle`. let layout = Fitter::<Ellipse>::new(&spec).seed(1).fit().unwrap(); println!( "{} shapes, loss = {:.2e}", layout.shapes().len(), layout.loss() ); }
layout.shapes() returns the fitted geometry; the plotting module turns a
Layout into region polygons and label anchors for rendering. See the
rustdoc for the full API.
Web App
Eunoia powers a web app for interactive diagram fitting and exploration, located
at https://eunoia.bz/app. The source code for the app resides in the web/
directory of this repo and is built on the JavaScript (npm) package, using
Svelte.
The Eunoia Ecosystem
The pure-Rust core powers bindings in several languages, all backed by the same fitting engine:
| Language | Package | Install |
|---|---|---|
| R | eulerr (repo) |
install.packages("eulerr") |
| Python | eunoia (repo) |
pip install eunoia |
| Julia | Eunoia.jl (repo) |
]add Eunoia |
| JavaScript | @jolars/eunoia (this repo) |
npm install @jolars/eunoia |
JavaScript/TypeScript
A TypeScript package is published as
@jolars/eunoia. Here's a
minimal example of fitting an Euler diagram in Node or a bundler environment:
import { euler, venn } from "@jolars/eunoia"; // Fit an Euler diagram from set sizes const layout = euler({ sets: { A: 5, B: 2, "A&B": 1 }, shape: "circle", // "circle" | "ellipse" | "square" | "rectangle" output: "shapes", // "shapes" | "polygons" | "regions" inputType: "exclusive", // "exclusive" | "inclusive" seed: 42, }); if (layout.mode === "shapes" && layout.shape === "circle") { for (const c of layout.circles) { console.log(c.label, c.x, c.y, c.radius); } } // Or build a canonical n-set Venn diagram const v = venn({ n: 3, output: "regions" });
A renderer-agnostic SVG serializer is available at @jolars/eunoia/svg. The
default entry is built with wasm-pack --target bundler, so it works with any
modern bundler (Vite, Webpack, Rollup, esbuild) and Node 20+.
Browser/Observable (No Bundler)
The default entry imports the .wasm module directly, which only a bundler can
resolve. For a plain HTML page, an Observable
notebook, or any environment without a build step, use the @jolars/eunoia/web
entry instead: a single self-contained ESM file with the WebAssembly module
inlined. Call init() once before fitting:
<script type="module"> import { euler, init } from "https://esm.sh/@jolars/eunoia/web"; await init(); // instantiate the embedded WebAssembly module (once) const layout = euler({ sets: { A: 5, B: 2, "A&B": 1 } }); console.log(layout.circles); </script>
In Observable:
eunoia = import("https://esm.sh/@jolars/eunoia/web"); await eunoia.init(); layout = eunoia.euler({ sets: { A: 5, B: 2, "A&B": 1 } });
@jolars/eunoia/svg is pure JavaScript (no WebAssembly), so it already works
directly from a CDN with no init().
Full runnable examples (a Quarto document and a standalone HTML page) are in
examples/.
Documentation
- Narrative docs: eunoia.bz/docs/
- Rust API reference: docs.rs/eunoia
License
Eunoia is distributed under the terms of either the MIT license or the Apache License 2.0, at your option.
