Checking a shape
Most mistakes in modelling announce themselves: a function stops with an error, and the errors page explains how to read it. A few do not. An operation can finish and return a shape that is not the one you meant:
- A cut that misses leaves the shape as it was, with no error.
- A fillet too large for the faces beside an edge can return a solid that is not valid.
- A face made from several wires keeps a hole only when the hole's wire runs against the outline.
- Extruding a wire gives a shell, not a solid.
So it pays to check a result, especially one the rest of a model builds on.
Ask the kernel
The kernel answers questions about a shape with numbers and yes or no, rather than new shapes:
| Question | Function |
|---|---|
| How much material is there? | bitbybit.occt.shapes.solid.getSolidVolume, getSolidSurfaceArea |
| Is it a valid shape? | bitbybit.occt.shapeFix.isValid |
| How big is it, and where? | bitbybit.occt.analysis.measure.tightBoundingBox |
| How close are two shapes, and where? | bitbybit.occt.analysis.measure.extrema |
| Do parts collide? | bitbybit.occt.analysis.clashes.betweenShapes |
| What is it made of? | bitbybit.occt.shapes.solid.debugInfo and the get* functions of each shape type |
Here a cylinder is cut through a box, and the result's volume and validity are read back. A volume smaller than the box's 64 says the cut happened:
- Rete
- Blockly
- TypeScript
{
"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
]
},
"hole": {
"id": "hole",
"name": "bitbybit.occt.shapes.solid.createCylinder",
"customName": "cylinder",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1,
"height": 6,
"center": [
0,
-3,
0
],
"direction": [
0,
1,
0
],
"angle": 360,
"originOnCenter": false
},
"inputs": {},
"position": [
40,
460
]
},
"tools": {
"id": "tools",
"name": "bitbybit.lists.createList",
"customName": "create list",
"async": false,
"drawable": false,
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "hole",
"output": "result",
"data": {}
}
]
}
},
"position": [
460,
460
]
},
"cut": {
"id": "cut",
"name": "bitbybit.occt.booleans.difference",
"customName": "difference",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"keepEdges": false,
"strategy": "oneAfterAnother"
},
"inputs": {
"shape": {
"connections": [
{
"node": "box",
"output": "result",
"data": {}
}
]
},
"shapes": {
"connections": [
{
"node": "tools",
"output": "list",
"data": {}
}
]
}
},
"position": [
460,
40
]
},
"volume": {
"id": "volume",
"name": "bitbybit.occt.shapes.solid.getSolidVolume",
"customName": "get solid volume",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "cut",
"output": "result",
"data": {}
}
]
}
},
"position": [
880,
40
]
},
"valid": {
"id": "valid",
"name": "bitbybit.occt.shapeFix.isValid",
"customName": "is valid",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "cut",
"output": "result",
"data": {}
}
]
}
},
"position": [
880,
300
]
}
},
"modules": {}
}
<xml xmlns="https://developers.google.com/blockly/xml"><variables><variable id="cut">cut</variable></variables><block type="variables_set" x="40" y="40"><field name="VAR" id="cut">cut</field><value name="VALUE"><block type="bitbybit.occt.booleans.difference"><value name="Shape"><block type="bitbybit.occt.shapes.solid.createBox"><value name="Width"><block type="math_number"><field name="NUM">4</field></block></value><value name="Length"><block type="math_number"><field name="NUM">4</field></block></value><value name="Height"><block type="math_number"><field name="NUM">4</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ"><value name="X"><block type="math_number"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number"><field name="NUM">0</field></block></value></block></value><value name="OriginOnCenter"><block type="logic_boolean"><field name="BOOL">TRUE</field></block></value></block></value><value name="Shapes"><block type="bitbybit.lists.addItem"><value name="List"><block type="bitbybit.lists.createEmptyList"></block></value><value name="Item"><block type="bitbybit.occt.shapes.solid.createCylinder"><value name="Radius"><block type="math_number"><field name="NUM">1</field></block></value><value name="Height"><block type="math_number"><field name="NUM">6</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ"><value name="X"><block type="math_number"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number"><field name="NUM">-3</field></block></value><value name="Z"><block type="math_number"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ"><value name="X"><block type="math_number"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number"><field name="NUM">0</field></block></value></block></value><value name="Angle"><block type="math_number"><field name="NUM">360</field></block></value><value name="OriginOnCenter"><block type="logic_boolean"><field name="BOOL">FALSE</field></block></value></block></value><value name="Clone"><block type="logic_boolean"><field name="BOOL">TRUE</field></block></value></block></value><value name="KeepEdges"><block type="logic_boolean"><field name="BOOL">FALSE</field></block></value><value name="Strategy"><block type="bitbybit.occt.enums.booleanStrategyEnum"><field name="bitbybit.occt.enums.booleanStrategyEnum">'oneAfterAnother'</field></block></value></block></value><next><block type="bitbybit.draw.drawAnyAsyncNoReturn"><value name="Entity"><block type="variables_get"><field name="VAR" id="cut">cut</field></block></value></block></next></block></xml>
const box = await bitbybit.occt.shapes.solid.createBox({ width: 4, length: 4, height: 4, center: [0, 0, 0] });
const hole = await bitbybit.occt.shapes.solid.createCylinder({ radius: 1, height: 6, center: [0, -3, 0] });
const cut = await bitbybit.occt.booleans.difference({ shape: box, shapes: [hole], keepEdges: false });
const volume = await bitbybit.occt.shapes.solid.getSolidVolume({ shape: cut });
const valid = await bitbybit.occt.shapeFix.isValid({ shape: cut });
console.log(`Volume ${volume.toFixed(2)}, valid: ${valid}`);
await bitbybit.draw.drawAnyAsync({ entity: cut });
In TypeScript the answer is written to the console. In Rete, point at the output of get solid volume or is valid to read the value it gave.
Repair what came from elsewhere
Shapes read from STEP or IGES files, or made from imported meshes, sometimes arrive with gaps or faces that do not quite meet. bitbybit.occt.shapeFix checks and repairs them:
validityReportsays which parts of a shape are faulty and why;freeBoundariesfinds the edges that bound only one face, the rims of the gaps;basicShapeRepaircloses small gaps and fixes wire and face defects, andfixShellandfixSolidrepair shells and solids;sewWithReportsews loose faces together into a shell and says what it joined.
Check with isValid, repair, and check again.
Every function
| Function | What it does |
|---|---|
basicShapeRepair | Runs the kernel's general repair over a shape: closes small gaps, fixes wire and face defects and brings tolerances into the given range. |
fixEdgeOrientationsAlongWire | Rebuilds a wire so its edges run head to tail in one direction along it. |
fixShell | Repairs a shell: turns its faces so they all face the same way and fixes its faces and edges. |
fixSmallEdgeOnWire | Removes edges shorter than precsmall from a wire and closes the gaps they leave, so a tiny sliver no longer breaks a fillet or a face. |
fixSolid | Repairs a solid, or makes one from a closed shell, with its shells fixed and turned so the material is inside. |
freeBoundaries | Finds the edges of a shape that bound only one face, joined into wires: the rims of an open shell's openings, or a lone face's outline and holes. |
isValid | Tells whether the shape is well formed, which a successful operation does not always guarantee. |
orientClosedSolid | Turns a closed solid so its material is inside, which gives an inside-out solid its positive volume back; only the orientation changes. |
sewWithReport | Sews the faces of shapes together along edges that lie within tolerance of each other, as shapes.shell.sewFaces does, and reports what it joined and what it left open. |
validityReport | Checks a shape as isValid does and reports each faulty sub-shape with the checks it fails, and the spread of its tolerances. |



