Try out iShape with an interactive demo.
- Stars Rotation
- Shapes Editor
- Path Offset
- Polygon Offset
- Triangulation
- iCurve Boolean Playground
- iCurve TypeCurve
- Boolean Operations: union, intersection, difference, and exclusion.
- Curves: Boolean operations on lines, quadratic and cubic Bézier curves, and elliptic arcs.
- Polygons: with holes, self-intersections, and multiple paths.
- Simplification: removes degenerate vertices and merges collinear edges.
- Fill Rules: even-odd, non-zero, positive and negative.
- ishape_wasm.js
- ishape_bg_wasm.wasm
You can find it at: pkg
Place these files in a directory that your HTML file can access; in this example, the directory is named ./ishape
You can install the iShape library from NPM:
npm install ishape_wasmThe NPM package is available here
After installing the NPM package, you can import it in your JavaScript or TypeScript file as follows:
import init, { Overlay, OverlayRule, FillRule } from './ishape/ishape_wasm.js';
// Your code hereHere is a simple HTML example that demonstrates how to use the iShape library for union operation. Full example is available here
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>iShape</title>
<style>
#result {
background-color: #f5f5f5;
border: 1px solid #ccc;
padding: 10px;
white-space: pre-wrap;
font-family: monospace;
}
textarea {
width: 100%;
height: 150px;
padding: 10px;
font-family: monospace;
margin-bottom: 10px;
}
</style>
<script type="module">
import init, { Overlay, OverlayRule, FillRule} from './ishape/ishape_wasm.js';
init();
document.getElementById('union').addEventListener('click', () => {
const subjInput = document.getElementById('subjInput').value;
const clipInput = document.getElementById('clipInput').value;
const subj = JSON.parse(subjInput);
const clip = JSON.parse(clipInput);
const overlay = Overlay.new_with_subj_and_clip(subj, clip);
// apply union operation
const union = overlay.overlay(OverlayRule.Union, FillRule.EvenOdd);
// add more operations if required
// ...
const resultText = JSON.stringify(union, null, 2);
document.getElementById('result').innerText = `Result:\n${resultText}`;
});
</script>
</head>
<body>
<textarea id="subjInput" placeholder='Enter "subj" polygon here...'>[[[200, 300], [200, 100], [400, 100], [400, 300]]]</textarea>
<textarea id="clipInput" placeholder='Enter "clip" polygon here...'>[[[300, 400], [300, 200], [500, 200], [500, 400]]]</textarea>
<button id="union">Union</button>
<pre id="result"></pre>
</body>
</html>Import classes and initialize the WebAssembly module using init(). Use the imported classes to perform geometric operations.
Build a Delaunay mesh with a target edge length, then relax its interior vertices while keeping outer and hole boundaries fixed:
import init, { Triangulator } from 'ishape_wasm';
await init();
const triangulator = new Triangulator();
const delaunay = triangulator.uniform_triangulate(shape, 40);
triangulator.free();
const relaxation = delaunay.relax_mut({ maxIterations: 24, tolerance: 0 });
const mesh = delaunay.to_triangulation();
delaunay.free();uniform_triangulate(path, edgeLength) accepts a contour, shape, or multiple
shapes and returns Delaunay directly. It splits boundary edges and adds an
interior lattice. The target edge length must be finite, positive, and above the
integer engine's coordinate precision. For the previous refinement method, use
triangulate(path).into_delaunay() and refine_with_circumcenters(maxArea).
relax_mut() modifies the mesh in place and returns { iterations, converged }.
Both options are optional: maxIterations defaults to 8, and tolerance to 0.
Invalid options throw before modifying the mesh. The
Tessellation demo
defaults to Uniform with relaxation enabled. Both subdivision methods can be
compared using the same triangle, centroid-net, and convex-polygon views.
CurveBuilder uses the familiar Canvas-style path methods. Every contour must
be closed before calling build(). The returned CurveGeometry is reusable:
it can be passed directly into another Boolean operation without converting it
to JavaScript data first.
import init, {
CurveBuilder,
CurveOverlay,
OverlayRule,
FillRule,
} from 'ishape_wasm';
await init();
const subjectBuilder = new CurveBuilder();
subjectBuilder.moveTo(0, 0);
subjectBuilder.bezierCurveTo(25, -30, 75, -30, 100, 0);
subjectBuilder.lineTo(100, 80);
subjectBuilder.lineTo(0, 80);
subjectBuilder.closeContour();
const subject = subjectBuilder.build();
const clipBuilder = new CurveBuilder();
clipBuilder.moveTo(40, -10);
clipBuilder.lineTo(120, -10);
clipBuilder.lineTo(120, 50);
clipBuilder.lineTo(40, 50);
clipBuilder.closeContour();
const clip = clipBuilder.build();
const operation = new CurveOverlay(subject, clip);
const result = operation.overlay(OverlayRule.Intersect, FillRule.NonZero);
// Ordinary typed JavaScript data for rendering or serialization.
console.log(result.toData());Use quadraticCurveTo() for quadratic Bézier segments,
ellipticArcTo() for an arc in the active contour, or addEllipse() to append
a complete ellipse as a new closed contour. Advanced callers can use
CurveOverlay.withScale(), setApproximation(), and conversionReport() to
control and inspect the discrete precision model. Approximation settings are
optional, so callers only specify what they need:
operation.setApproximation({ minChordLength: 0.001 });To resolve self-intersections without a clip, use
CurveOverlay.fromSubject(geometry).resolveSubject(fillRule).
| A,B | A ∪ B | A ∩ B | A - B | B - A | A ⊕ B |
|---|---|---|---|---|---|