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.