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
.
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);
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" };
<!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:
<!doctype html>
<html>
<body>
<div id="diagram"></div>
</body>
</html>
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:
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:
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
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
.