Using Penrose with Vanilla JS
Both the Language API and Optimization API are exported from @penrose/core, currently released as an ECMAScript module (ESM). If you are making a web page without a build tool, you can use one of the CDNs with built-in ESM support. Here's an example of using JSPM, where we use the JSPM Generator to create the HTML boilerplate for importing our dependencies.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Penrose Vanilla JS Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<!--
JSPM Generator Import Map
Edit URL: https://generator.jspm.io/#U2VhYGBkTM4vSmVwKEjNK8ovTtUH8RyM9Qz0DAA+kyABHgA
-->
<script type="importmap">
{
"imports": {
"@penrose/core": "https://ga.jspm.io/npm:@penrose/core@3.2.0/dist/index.js"
},
"scopes": {
"https://ga.jspm.io/": {
"@datastructures-js/queue": "https://ga.jspm.io/npm:@datastructures-js/queue@4.2.3/index.js",
"@penrose/optimizer": "https://ga.jspm.io/npm:@penrose/optimizer@3.2.0/dist/index.js",
"consola": "https://ga.jspm.io/npm:consola@2.15.3/dist/consola.browser.js",
"crypto": "https://ga.jspm.io/npm:@jspm/core@2.0.1/nodelibs/browser/crypto.js",
"immutable": "https://ga.jspm.io/npm:immutable@4.3.1/dist/immutable.es.js",
"lodash": "https://ga.jspm.io/npm:lodash@4.17.21/lodash.js",
"mathjax-full/js/": "https://ga.jspm.io/npm:mathjax-full@3.2.2/js/",
"mhchemparser/dist/mhchemParser.js": "https://ga.jspm.io/npm:mhchemparser@4.2.1/dist/mhchemParser.js",
"moo": "https://ga.jspm.io/npm:moo@0.5.2/moo.js",
"nearley": "https://ga.jspm.io/npm:nearley@2.20.1/lib/nearley.js",
"poly-partition": "https://ga.jspm.io/npm:poly-partition@1.0.2/lib/index.js",
"seedrandom": "https://ga.jspm.io/npm:seedrandom@3.0.5/index.js",
"true-myth": "https://ga.jspm.io/npm:true-myth@4.1.1/dist/cjs/index.js"
}
}
}
</script>
<!-- ES Module Shims: Import maps polyfill for olrder browsers without import maps support (eg Safari 16.3) -->
<script
async
src="https://ga.jspm.io/npm:es-module-shims@1.7.3/dist/es-module-shims.js"
crossorigin="anonymous"
></script>
<script type="module">
import { compile, optimize, toSVG, showError } from "@penrose/core";
const trio = {
substance: `
Set A
Label A $e=mc^2$
`,
style: `canvas {
width = 150
height = 150
}
forall Set A {
center = (0, 0)
Circle {
center: center
r: 50
}
Equation {
center: center
string: A.label
}
}
`,
domain: `type Set`,
variation: `test`,
};
const compiled = await compile(trio);
if (compiled.isErr()) console.error(showError(compiled.error));
const optimized = optimize(compiled.value);
if (optimized.isErr()) console.error(showError(optimized.error));
document
.getElementById("penrose")
.appendChild(await toSVG(optimized.value));
</script>
<div id="penrose"></div>
</body>
</html>
To run this example, copy the code above into an HTML file (e.g. index.html) and run an local HTTP server to view the page:
npx http-server .You can also check out this example live here.
Experimental bundled ESM
INFO
This feature is experimental as of v3.2.0 and is subject to changes.
The default ESM module requires a CDN or a bundler to download all the dependencies of core. This experimental release format is an ESM module that includes all dependencies in one ESM module. To import @penrose/core from the bundle:
import { compile, optimize } from "@penrose/core/bundle";DANGER
The bundled core module currently uses IIFE to provide a seemingly synchronous API. However, be aware that any function imported from @penrose/core/bundle might be undefined when used immediately after importing. To work around this, do explicit checks on whether the functions are loaded before using them:
import { compile, optimize } from "@penrose/core/bundle";
if (compile && optimize) {
// actually call `compile` and `optimize`
}