Skip to content

jolars/eunoia

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

571 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Eunoia

Build and Test Crates.io npm version PyPI version CRAN Badge codecov

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.

An area-proportional Euler diagram of three overlapping sets labelled Adventure, Comedy, and Drama.

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.

The Eunoia ecosystem

The pure-Rust core powers bindings in several languages, all backed by the same fitting engine:

Language Package Install
Rust eunoia cargo add eunoia
R eulerr (repo) install.packages("eulerr")
Python eunoia (repo) pip install eunoia
Julia Eunoia.jl (see the package README)
JavaScript @jolars/eunoia npm install @jolars/eunoia
Web app eunoia.bz build diagrams in the browser

JavaScript / TypeScript

WebAssembly bindings are published as @jolars/eunoia:

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

License

Eunoia is distributed under the terms of either the MIT license or the Apache License 2.0, at your option.

About

A Rust library for Euler and Venn Diagrams

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Contributing

Stars

Watchers

Forks

Sponsor this project

 

Contributors