Skip to content

The Language API ​

Penrose provides convinience functions for easier integration of Penrose languages in web applications. Check out our docs on React and SolidJS integration.

For lower-level integration, check out the optimization API.

Example ​

Below is an example of compiling, optimizing, and rendering a diagram in Penrose programmatically. Here we define a simple trio of Substance, Style, and Domain programs in trio.js.

javascript
import { compile, optimize, toSVG, showError } from "@penrose/core";
import trio from "./trio.js";

const compiled = await compile(trio);
// handle compilation errors
if (compiled.isErr()) {
  throw new Error(showError(compiled.error));
}
const converged = optimize(compiled.value);
// handle optimization errors
if (converged.isErr()) {
  throw new Error(showError(converged.error));
}
// render the diagram state as an SVG
const rendered = await toSVG(converged.value, async () => undefined);
const container = document.getElementById("diagram");
container.appendChild(rendered);
javascript
const domain = `
type Set
predicate Disjoint(Set s1, Set s2)
predicate Intersecting(Set s1, Set s2)
predicate Subset(Set s1, Set s2)
`;
const style = `
canvas {
    width = 800
    height = 700
  }
  
  forall Set x {
    shape x.icon = Circle { }
    shape x.text = Equation {
      string : x.label
      fontSize : "32px"
    }
    ensure contains(x.icon, x.text)
    encourage norm(x.text.center - x.icon.center) == 0
    layer x.text above x.icon
  }
  
  forall Set x; Set y
  where Subset(x, y) {
    ensure disjoint(y.text, x.icon, 10)
    ensure contains(y.icon, x.icon, 5)
    layer x.icon above y.icon
  }
  
  forall Set x; Set y
  where Disjoint(x, y) {
    ensure disjoint(x.icon, y.icon)
  }
  
  forall Set x; Set y
  where Intersecting(x, y) {
    ensure overlapping(x.icon, y.icon)
    ensure disjoint(y.text, x.icon)
    ensure disjoint(x.text, y.icon)
  }  
`;
const substance = `
Set A, B, C, D, E, F, G

Subset(B, A)
Subset(C, A)
Subset(D, B)
Subset(E, B)
Subset(F, C)
Subset(G, C)

Disjoint(E, D)
Disjoint(F, G)
Disjoint(B, C)

AutoLabel All
`;

export default { domain, substance, style, variation: "test" };
html
<!doctype html>
<html>
  <body>
    <div id="diagram"></div>
  </body>
</html>

Reference ​

This section describes the public API for Penrose; there are other things exported, but those are not currently considered part of the public API, so they may change. In contrast, any breaking change to these particular items must be accompanied by a bump to the Penrose major version, so you can rely on them via SemVer.

PenroseState ​

This type holds all the data for a Penrose diagram that has already been compiled. You can pass it to step or stepTimes or optimize to get a new PenroseState, or you can display it to the user via toSVG.

diagram ​

This is a convenience function which encapsulates usage of PenroseState: it just takes in a Penrose trio, an HTML element to attach the diagram to, and a function for resolving paths to embedded SVG images.

Example:

html
<!doctype html>
<html>
  <body>
    <div id="diagram"></div>
  </body>
</html>
javascript
import { fetchResolver } from "@penrose/components";
import { diagram } from "@penrose/core";
import trio from "./trio.js";

await diagram(trio, document.getElementById("diagram"), fetchResolver);

compile ​

This function takes a Penrose trio and returns a PenroseState; see the example at the top of this page. In particular it returns a Promise of a Result of a PenroseState; refer to those links to find more detail on how to use those generic types. The error case of the Result is a PenroseError; see below.

optimize ​

This function takes a PenroseState and fully optimizes it, then returns a Result of a PenroseState, where the error case is a PenroseError. See the example at the top of this page.

step ​

This function takes a PenroseState and some options, currently the only one of which is a callback; Penrose will keep optimizing until this callback returns false, then return a Result of a PenroseState, where the error case is a PenroseError.

Example:

javascript
import { PenroseState, step } from "@penrose/core";

const stepMillis = (state, millis) => {
  let elapsed = false;
  setTimeout(() => {
    elapsed = true;
  }, millis);
  return step(state, { until: () => elapsed });
};

stepTimes ​

This function takes a PenroseState and some options, currently the only one of which is a callback; Penrose will keep optimizing until this callback returns false, then return a Result of a PenroseState, where the error case is a PenroseError.

isOptimized ​

This function takes in a PenroseState and returns true iff its layout is already fully optimized.

Example:

javascript
import { compile, isOptimized, showError } from "@penrose/core";

const example = async (trio) => {
  const compiled = await compile(trio);
  if (compiled.isErr()) throw Error(showError(compiled.error));
  console.log(isOptimized(compiled.value)); // false
  const optimized = optimize(trio);
  console.log(isOptimized(optimized.value)); // true
  return optimized;
};

PenroseError ​

This type represents an error returned from Penrose. To consume it, see showError below.

showError ​

javascript
import { compile, showError } from "@penrose/core";

const compiled = await compile({
  substance: "howdy pardner",
  style: "yeehaw",
  domain: "this diagram ain't big enough for the two of us",
  variation: "super varied",
});

if (compiled.isErr()) {
  // spoiler alert: it is indeed Err
  console.error(compiled.error);
}

toSVG ​

This function renders a PenroseState as an SVGSVGElement.

Released under the MIT License.