Skip to main content

The Manifold kernel

Manifold is a kernel for solids made of triangles. Its booleans are fast, and they always give a closed solid, a mesh with no holes or loose edges that a slicer or a game engine accepts as it is. Reach for it when a model needs hundreds or thousands of cuts, when it is a lattice or a pattern, or when the input is already a mesh, such as a scan. Choosing a kernel compares it with OpenCascade and JSCAD.

What it does not have: true curves and fillets. A Manifold cylinder is a prism of circularSegments sides, and a round edge is a set of flat faces.

Two kinds of shape​

  • Solids, under bitbybit.manifold.manifold: shapes makes cubes, spheres, cylinders and solids from meshes; booleans fuses, cuts and splits; operations hulls, slices, refines and smooths; transforms moves, turns, scales and warps; evaluate measures.
  • Cross sections, under bitbybit.manifold.crossSection: flat outlines in the XY plane, from squares, rectangles, circles and polygons, with booleans and offsets of their own, which operations turns into solids by extruding, revolving, lofting or sweeping.

Manifold builds along Z. A shape extruded from a cross section rises along Z, which lies flat in Bitbybit's Y-up scene, so turn it -90 degrees about X to stand it up, as the example below does.

A cross section extruded with a twist​

A square is extruded eight units with a quarter twist and a top four tenths as wide, then turned to stand up:

Bitbybit Platform

A twisted tower from a square in Rete

rete logoRete
Script Source (rete)
{
"id": "rete-v2-json",
"nodes": {
"square": {
"id": "square",
"name": "bitbybit.manifold.crossSection.shapes.square",
"customName": "square",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"center": true,
"size": 3
},
"inputs": {},
"position": [
40,
40
]
},
"extrude": {
"id": "extrude",
"name": "bitbybit.manifold.crossSection.operations.extrude",
"customName": "extrude",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"height": 8,
"nDivisions": 40,
"twistDegrees": 90,
"scaleTopX": 0.4,
"scaleTopY": 0.4,
"center": false
},
"inputs": {
"crossSection": {
"connections": [
{
"node": "square",
"output": "result",
"data": {}
}
]
}
},
"position": [
460,
40
]
},
"standUp": {
"id": "standUp",
"name": "bitbybit.manifold.manifold.transforms.rotate",
"customName": "rotate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"manifold": {
"connections": [
{
"node": "extrude",
"output": "result",
"data": {}
}
]
},
"vector": {
"connections": [
{
"node": "turn",
"output": "result",
"data": {}
}
]
}
},
"position": [
880,
40
]
},
"turn": {
"id": "turn",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -90,
"y": 0,
"z": 0
},
"inputs": {},
"position": [
460,
520
]
}
},
"modules": {}
}

nDivisions is how many layers the extrusion is built from: a twist needs enough of them to look smooth.

From OpenCascade into Manifold​

A model can start exact and finish fast. bitbybit.occt.shapeToManifoldMesh meshes an OpenCascade shape into the form Manifold takes, and manifold.manifold.shapes.manifoldFromMesh makes a Manifold solid of it. Here a box is rounded in OpenCascade, where fillets are exact, then carried into Manifold and hollowed there by a sphere just large enough to break through each face:

Bitbybit Platform

A filleted OpenCascade box cut in Manifold in Rete

rete logoRete
Script Source (rete)
{
"id": "rete-v2-json",
"nodes": {
"box": {
"id": "box",
"name": "bitbybit.occt.shapes.solid.createBox",
"customName": "box",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"width": 4,
"length": 4,
"height": 4,
"center": [
0,
0,
0
],
"originOnCenter": true
},
"inputs": {},
"position": [
40,
40
]
},
"fillet": {
"id": "fillet",
"name": "bitbybit.occt.fillets.filletEdges",
"customName": "fillet edges",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 0.6
},
"inputs": {
"shape": {
"connections": [
{
"node": "box",
"output": "result",
"data": {}
}
]
}
},
"position": [
460,
40
]
},
"mesh": {
"id": "mesh",
"name": "bitbybit.occt.shapeToManifoldMesh",
"customName": "shape to manifold mesh",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"precision": 0.01
},
"inputs": {
"shape": {
"connections": [
{
"node": "fillet",
"output": "result",
"data": {}
}
]
}
},
"position": [
880,
40
]
},
"solid": {
"id": "solid",
"name": "bitbybit.manifold.manifold.shapes.manifoldFromMesh",
"customName": "manifold from mesh",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"mesh": {
"connections": [
{
"node": "mesh",
"output": "result",
"data": {}
}
]
}
},
"position": [
1300,
40
]
},
"sphere": {
"id": "sphere",
"name": "bitbybit.manifold.manifold.shapes.sphere",
"customName": "sphere",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 2.5,
"circularSegments": 64
},
"inputs": {},
"position": [
1300,
420
]
},
"cut": {
"id": "cut",
"name": "bitbybit.manifold.manifold.booleans.differenceTwo",
"customName": "difference two",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"manifold1": {
"connections": [
{
"node": "solid",
"output": "result",
"data": {}
}
]
},
"manifold2": {
"connections": [
{
"node": "sphere",
"output": "result",
"data": {}
}
]
}
},
"position": [
1720,
40
]
}
},
"modules": {}
}

The way back gives only flat triangles: keep the OpenCascade shape if you need exact geometry again, for STEP or another fillet.

Meshes and recipes​

manifold.manifold.manifoldToMesh hands out a solid's triangles as plain data, which bitbybit.manifold.mesh reads, for your own processing or export. bitbybit.manifold.recipes builds whole solids described as data, polygons, extrusions, turns, lofts, cuts and unions, in one call, and draws thousands of repeated parts as instances.

Freeing memory​

Manifold keeps its shapes in a worker, like OpenCascade: manifold.deleteManifoldOrCrossSection and deleteManifoldsOrCrossSections free the ones a script no longer needs. Shapes live in a worker explains why.

Every function​

Solids​

FunctionWhat it does
booleans.addFuses two solids into one closed, watertight solid.
booleans.differenceCuts every further solid in the list out of the first one.
booleans.differenceTwoCuts the second solid out of the first, the same as subtract.
booleans.intersectKeeps only the volume two solids share, dropping everything else.
booleans.intersectionKeeps only the volume all the solids in a list share.
booleans.intersectionTwoKeeps only the volume two solids share, the same as intersect.
booleans.minkowskiDifferenceSweeps the second solid over the whole surface of the first and cuts away everything it passes through, shrinking the first solid by the shape of the second - the Minkowski difference, the erosion that undoes a Minkowski sum.
booleans.minkowskiSumSweeps the second solid over the whole surface of the first and fuses everything it passes through, growing the first solid by the shape of the second - the Minkowski sum, which is how a solid is rounded or padded by a sphere.
booleans.splitCuts a solid with another solid and keeps both pieces: the part inside the cutter and the part outside it.
booleans.splitByPlaneCuts a solid with a plane and keeps both pieces.
booleans.splitByPlaneOnOffsetsCuts a solid into slabs with several parallel planes, all with the same normal, at the given distances from the origin.
booleans.subtractCuts the second solid out of the first, leaving what remains of the first.
booleans.trimByPlaneCuts a solid with a plane and keeps only the part on the side the normal points to.
booleans.unionFuses all the solids in a list into one.
booleans.unionTwoFuses two solids into one, the same as add.
evaluate.boundingBoxFinds the axis-aligned box around every vertex of a solid.
evaluate.genusCounts the holes through a solid, the way a ring has one and a sphere none.
evaluate.isEmptyTells whether a solid has no triangles at all, as the result of an intersection of shapes that do not overlap would.
evaluate.minGapMeasures the smallest distance between two solids, searching no farther than searchLength.
evaluate.numEdgeCounts the edges of a solid's mesh, each shared by two triangles.
evaluate.numPropCounts the property channels each vertex of a solid carries; the position alone takes three.
evaluate.numPropVertCounts the property vertices of a solid, which is at least numVert: a vertex whose neighboring triangles carry different properties, such as a sharp edge with two normals, is stored more than once.
evaluate.numTriCounts the triangles of a solid's mesh, which is its whole surface.
evaluate.numVertCounts the vertices of a solid's mesh, the corners its triangles share.
evaluate.originalIDReads the id of a solid that is an original, as operations.asOriginal or a freshly built solid makes it; a solid produced from others by an operation reports -1.
evaluate.rayCastShoots a ray segment from one point to another and lists every place it crosses the surface of a solid, nearest first; an empty list when it misses.
evaluate.statusTells why a solid came out empty: NoError, or a reason such as NotManifold or InvalidConstruction when the mesh it was built from was not a closed surface.
evaluate.surfaceAreaMeasures the total surface area of a solid, in square model units.
evaluate.toleranceReads the tolerance of a solid, the rounding error that has built up over the transforms and operations that made it.
evaluate.volumeMeasures the volume of a solid, in cubic model units.
manifoldsToMeshesTurns several solids into plain mesh data, as manifoldToMesh does for one.
manifoldToMeshTurns a solid into plain mesh data: vertex properties, triangle indexes and the runs that group triangles by their original shape.
operations.asOriginalMakes a copy of a solid that counts as a new original, so the copy can carry its own vertex properties, such as a different UV mapping, and be told apart from what it was built from.
operations.calculateCurvatureComputes how strongly the surface bends at every vertex and stores it in property channels: Gaussian curvature at gaussianIdx, mean curvature at meanIdx.
operations.calculateNormalsComputes a normal for every vertex from the mesh and stores it in three property channels starting at normalIdx.
operations.composePacks several solids into one, the inverse of decompose.
operations.decomposeSplits a solid into its separate, unconnected pieces, the inverse of compose.
operations.hullWraps a solid in its convex hull, the smallest shape without dents that contains it, like shrink-wrap pulled tight over it.
operations.hullPointsWraps points, solids or a mix of both in one convex hull, the smallest shape without dents that contains them all.
operations.projectFlattens a solid onto the XY plane and returns its outline as a cross-section, like its shadow under a light straight above.
operations.refineAdds triangles to a solid by splitting every edge into number pieces; with 2, each triangle becomes four.
operations.refineToLengthAdds triangles to a solid by splitting every edge into pieces of roughly the given length, adding inner vertices to keep the triangles even.
operations.refineToToleranceAdds triangles to a smoothed solid until every point of its mesh lies within tolerance of the smooth surface, so tightly curved regions get more triangles than flat ones.
operations.reserveIdsReserves a run of count unique mesh ids and returns the first, for marking sets of triangles that can be found again after later operations.
operations.setPropertiesRewrites the vertex properties of a solid with a function that receives each vertex's position and old properties and fills in the new ones.
operations.setToleranceGives a solid a new tolerance, the rounding error it is allowed to carry, and simplifies its mesh when the tolerance grows.
operations.simplifyRemoves vertices from a solid's mesh while keeping every surface within tolerance of where it was, to cut the triangle count.
operations.sliceCuts a solid with a plane parallel to the XY plane at the given Z height and returns the flat section as a cross-section.
operations.smoothByNormalsMarks a solid for smoothing using the normals stored in its vertex properties, so a later refine or refineToLength bends the new triangles into a smooth surface.
operations.smoothOutMarks a solid for smoothing by working out tangents from its triangles, so a later refine or refineToLength bends the new triangles into a smooth surface.
shapes.cubeCreates a cube solid with the given side length.
shapes.cylinderCreates a cylinder solid standing along Z, or a cone when the top radius differs from the bottom one.
shapes.fromPolygonPointsBuilds a solid from a list of triangles, each given as three points, merging points that coincide.
shapes.manifoldFromMeshBuilds a solid from plain mesh data, the form manifoldToMesh hands out, so a mesh can make a round trip through other tools.
shapes.sphereCreates a sphere solid of the given radius, centered on the origin.
shapes.tetrahedronCreates a tetrahedron solid centered on the origin, with one corner at [1, 1, 1] and the others placed symmetrically.
transforms.mirrorMirrors a solid across the plane through the origin with the given normal.
transforms.rotateRotates a solid about the origin by Euler angles in degrees: first about X, then Y, then Z.
transforms.rotateXYZRotates a solid about the origin by separate angles in degrees about X, then Y, then Z.
transforms.scaleScales a solid by a factor per axis, about the origin; it takes the same inputs as scale3D, so use equal factors for a uniform scale.
transforms.scale3DScales a solid by a separate factor along X, Y and Z, about the origin.
transforms.transformApplies a 4x4 matrix to a solid.
transforms.transformsApplies a list of 4x4 matrices to a solid one after another, first to last, and returns the final result.
transforms.translateMoves a solid by a vector, in model units.
transforms.translateByVectorsMakes one moved copy of a solid per vector, for laying out repeats.
transforms.translateXYZMoves a solid by separate distances along X, Y and Z, in model units.
transforms.warpMoves every vertex of a solid with a function of your own that changes the vertex position in place, for bends, tapers and other free deformations.

Cross sections​

FunctionWhat it does
booleans.addFuses two cross-sections into one outline, holes and all.
booleans.differenceCuts every further cross-section in the list out of the first one.
booleans.differenceTwoCuts the second cross-section out of the first, the same as subtract.
booleans.intersectKeeps only the area two cross-sections share, dropping everything else.
booleans.intersectionKeeps only the area all the cross-sections in a list share.
booleans.intersectionTwoKeeps only the area two cross-sections share, the same as intersect.
booleans.subtractCuts the second cross-section out of the first, leaving what remains of the first.
booleans.unionFuses all the cross-sections in a list into one.
booleans.unionTwoFuses two cross-sections into one, the same as add.
crossSectionFromPointsBuilds a cross-section from one polygon given as points; only the X and Y of each point are used.
crossSectionFromPolygonsBuilds a cross-section from several polygons given as points, for instance an outline and its holes; only the X and Y of each point are used.
crossSectionsToPointsReads several cross-sections back as polygons with 3D points, as crossSectionToPoints does for one.
crossSectionsToPolygonsReads several cross-sections back as their polygons, as crossSectionToPolygons does for one.
crossSectionToPointsReads a cross-section back as its polygons with 3D points, Z set to 0, ready for drawing as polylines.
crossSectionToPolygonsReads a cross-section back as its polygons, each a list of 2D points.
evaluate.areaMeasures the area of a cross-section, in square model units, holes excluded.
evaluate.boundsFinds the rectangle around a cross-section as two 2D points: the minimum corner, then the maximum corner.
evaluate.isEmptyTells whether a cross-section has no contours at all.
evaluate.numContourCounts the contours of a cross-section: its outer outlines and its holes.
evaluate.numVertCounts the vertices of a cross-section over all its contours.
operations.composePacks several cross-sections or polygons into one cross-section without fusing them, the inverse of decompose.
operations.decomposeSplits a cross-section into its separate, unconnected outlines, each with its own holes, the inverse of compose.
operations.extrudeSweeps a cross-section along Z into a solid of the given height.
operations.hullWraps a cross-section in its convex hull, the smallest outline without dents that contains it, like a rubber band stretched around it.
operations.loftJoins cross-sections placed by frames into a solid, each section to the next by straight sides, closed at both ends: a taper from one outline to another, or one outline through many frames.
operations.offsetMoves the outline of a cross-section outward by delta, or inward for a negative delta, so an outer contour grows and a hole shrinks.
operations.revolveSpins a cross-section around the Y axis into a solid, like a lathe; only the part of the outline on the positive X side is used.
operations.simplifyRemoves points of a cross-section that lie within epsilon of the line between their neighbors, dropping near-duplicates and collinear points.
operations.sweepCarries a cross-section along a path of points into a solid, square to the path at every point and turning with it without twisting, like a pipe, a rail or a cable.
shapes.circleCreates a circular cross-section of the given radius, centered on the origin and drawn with circularSegments straight sides.
shapes.createBuilds a cross-section from polygons given as 2D points, fusing overlapping ones so the result has no crossings.
shapes.rectangleCreates a rectangular cross-section with length along X and height along Y; with center true it is centered on the origin, otherwise its corner sits there.
shapes.squareCreates a square cross-section of the given side; with center true it is centered on the origin, otherwise its corner sits there.
transforms.mirrorMirrors a cross-section across the line through the origin that is perpendicular to the given normal.
transforms.rotateRotates a cross-section about the origin by an angle in degrees, counterclockwise.
transforms.scaleScales a cross-section uniformly about the origin by a factor.
transforms.scale2DScales a cross-section by a separate factor along X and Y, about the origin.
transforms.transformApplies a 3x3 matrix to a cross-section, for any combination of move, turn, scale and shear in the plane.
transforms.translateMoves a cross-section by a 2D vector, in model units.
transforms.translateXYMoves a cross-section by separate distances along X and Y, in model units.
transforms.warpMoves every point of a cross-section with a function of your own that changes the point in place, then fuses the result so any crossings the move introduced are cleaned up.

Meshes​

FunctionWhat it does
evaluate.backsideTells whether one run of triangles faces the other way than the original mesh it came from, as the inner surface left by a subtraction does.
evaluate.extrasReads the properties of one vertex beyond its position, such as normals or colors stored in extra channels.
evaluate.hasNormalsTells whether the first three extra property channels of one run of triangles hold vertex normals, which manifold.operations.calculateNormals writes there.
evaluate.numPropCounts the property channels each vertex of a mesh carries; the position alone takes three.
evaluate.numRunCounts the triangle runs of a mesh: each run is a stretch of consecutive triangles that came from the same instance of the same input shape.
evaluate.numTriCounts the triangles of a mesh, which together make its whole surface.
evaluate.numVertCounts the property vertices of a mesh, which can exceed the geometric vertices where neighboring triangles carry different properties.
evaluate.positionReads the position of one vertex of a mesh.
evaluate.tangentReads the tangent of one half-edge of a smoothed mesh: the direction the surface leaves the edge's start vertex in, as three numbers plus a weight.
evaluate.transformReads the column-major 4x4 matrix that carries the original mesh onto one run of triangles, the placement of that instance.
evaluate.vertsReads the three vertex indexes of one triangle of a mesh, in counterclockwise order.
operations.mergeFills in the mesh's merge information so it can become a solid again: vertices on open edges within the tolerance are merged, keeping existing entries.

Recipes​

FunctionWhat it does
buildBuilds each root of a recipe as a solid placed by its matrix, in root order.
meshesBuilds a recipe into the meshes its roots draw, each step several roots place meshed once in its own coordinates, and gives every root a placement: its mesh, its matrix and its tag.
surfaceMeshesGives the roots of a recipe made of triangle meshes alone as meshes, placed as build places its solids, without building anything.