Thick Solids from Complex Faces
Thick solids are powerful 3D modeling features that create volumetric objects by adding thickness to planar or curved faces. While simple circular or rectangular faces are common starting points, thick solids truly shine when working with complex, curved surfaces created through advanced operations like lofting.
Understanding Thick Solids
A thick solid operation takes a face (planar or curved) and extrudes it in the direction of its surface normal(s) to create a solid with the specified thickness. This operation is particularly useful for:
- Shell structures: Creating hollow objects with consistent wall thickness
- Architectural elements: Building walls, roofs, and structural components
- Mechanical parts: Designing brackets, housings, and enclosures
- Organic shapes: Creating complex curved objects with controlled thickness
Complex Face Creation Through Lofting
To demonstrate the versatility of thick solids, we'll create a complex curved face using the loft operation. Lofting creates a smooth surface between multiple cross-sectional curves, allowing us to build sophisticated organic shapes.
The Process
- Create Multiple Curved Cross-Sections: Use interpolation algorithms to create smooth curves through defined points
- Loft Between Curves: Generate a complex surface that smoothly transitions between the cross-sections
- Apply Thick Solid: Add uniform thickness to create a 3D solid
Convex vs Concave Considerations
Thick solid operations work on both:
- Convex faces: Outward-curving surfaces (like the outside of a sphere)
- Concave faces: Inward-curving surfaces (like the inside of a bowl)
The algorithm automatically determines the correct direction for thickness application based on the face normals.
Practical Applications
This technique is invaluable for:
- Product Design: Creating ergonomic handles, grips, and curved components
- Architecture: Designing complex roof structures and curved walls
- Art and Sculpture: Building organic, flowing forms
- Engineering: Developing aerodynamic shapes and fluid-flow components
Interactive Examples
The following examples demonstrate creating thick solids from complex lofted surfaces, showing how sophisticated 3D geometry can emerge from simple point-based definitions.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"5dd18ebda288b926": {
"id": "5dd18ebda288b926",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "bp1",
"output": "result",
"data": {}
},
{
"node": "bp2",
"output": "result",
"data": {}
},
{
"node": "bp3",
"output": "result",
"data": {}
},
{
"node": "bp4",
"output": "result",
"data": {}
},
{
"node": "bp5",
"output": "result",
"data": {}
}
]
}
},
"position": [
381.4700511813048,
-759.23178075394
]
},
"bp1": {
"id": "bp1",
"name": "bitbybit.vector.vectorXYZ",
"customName": "bottom point 1",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -3,
"y": 0,
"z": -2
},
"inputs": {},
"position": [
-200.2509251796141,
-1530.6237744580028
]
},
"bp2": {
"id": "bp2",
"name": "bitbybit.vector.vectorXYZ",
"customName": "bottom point 2",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -1,
"y": 0,
"z": 0
},
"inputs": {},
"position": [
-200.6438837464586,
-1185.4839083695801
]
},
"bp3": {
"id": "bp3",
"name": "bitbybit.vector.vectorXYZ",
"customName": "bottom point 3",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 1
},
"inputs": {},
"position": [
-201.51603233009413,
-841.1047664734106
]
},
"bp4": {
"id": "bp4",
"name": "bitbybit.vector.vectorXYZ",
"customName": "bottom point 4",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 1,
"y": 0,
"z": 0
},
"inputs": {},
"position": [
-201.80719478417944,
-501.30789904857716
]
},
"bp5": {
"id": "bp5",
"name": "bitbybit.vector.vectorXYZ",
"customName": "bottom point 5",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 3,
"y": 0,
"z": -2
},
"inputs": {},
"position": [
-203.40586709326126,
-159.85873216886546
]
},
"a78038f1fcf20364": {
"id": "a78038f1fcf20364",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "mp1",
"output": "result",
"data": {}
},
{
"node": "mp2",
"output": "result",
"data": {}
},
{
"node": "mp3",
"output": "result",
"data": {}
},
{
"node": "mp4",
"output": "result",
"data": {}
},
{
"node": "mp5",
"output": "result",
"data": {}
}
]
}
},
"position": [
359.8960910544257,
639.4093382334145
]
},
"mp1": {
"id": "mp1",
"name": "bitbybit.vector.vectorXYZ",
"customName": "middle point 1",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -2,
"y": 2,
"z": -1
},
"inputs": {},
"position": [
-207.40751268680148,
182.20607249446778
]
},
"mp2": {
"id": "mp2",
"name": "bitbybit.vector.vectorXYZ",
"customName": "middle point 2",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -0.5,
"y": 2,
"z": 1.5
},
"inputs": {},
"position": [
-208.55231756947538,
522.7333695018547
]
},
"mp3": {
"id": "mp3",
"name": "bitbybit.vector.vectorXYZ",
"customName": "middle point 3",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 2,
"z": 2
},
"inputs": {},
"position": [
-209.95734117017489,
863.2062131607103
]
},
"mp4": {
"id": "mp4",
"name": "bitbybit.vector.vectorXYZ",
"customName": "middle point 4",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0.5,
"y": 2,
"z": 1.5
},
"inputs": {},
"position": [
-209.44110803182636,
1202.5708000257098
]
},
"mp5": {
"id": "mp5",
"name": "bitbybit.vector.vectorXYZ",
"customName": "middle point 5",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 2,
"y": 2,
"z": -1
},
"inputs": {},
"position": [
-206.9406246017676,
1536.4767388714486
]
},
"dc177fbbaa4a29dd": {
"id": "dc177fbbaa4a29dd",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "tp1",
"output": "result",
"data": {}
},
{
"node": "tp2",
"output": "result",
"data": {}
},
{
"node": "tp3",
"output": "result",
"data": {}
},
{
"node": "tp4",
"output": "result",
"data": {}
},
{
"node": "tp5",
"output": "result",
"data": {}
}
]
}
},
"position": [
325.21285353635943,
2380.7128964061235
]
},
"tp1": {
"id": "tp1",
"name": "bitbybit.vector.vectorXYZ",
"customName": "top point 1",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -1.5,
"y": 4,
"z": 0
},
"inputs": {},
"position": [
-205.696683844526,
1880.3195147672093
]
},
"tp2": {
"id": "tp2",
"name": "bitbybit.vector.vectorXYZ",
"customName": "top point 2",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -0.5,
"y": 4,
"z": 0.5
},
"inputs": {},
"position": [
-203.37989474966056,
2226.564708840056
]
},
"tp3": {
"id": "tp3",
"name": "bitbybit.vector.vectorXYZ",
"customName": "top point 3",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 4,
"z": 1
},
"inputs": {},
"position": [
-202.31448593117972,
2571.9360743612506
]
},
"tp4": {
"id": "tp4",
"name": "bitbybit.vector.vectorXYZ",
"customName": "top point 4",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0.5,
"y": 4,
"z": 0.5
},
"inputs": {},
"position": [
-194.57652933131516,
2918.9034255832003
]
},
"tp5": {
"id": "tp5",
"name": "bitbybit.vector.vectorXYZ",
"customName": "top point 5",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 1.5,
"y": 4,
"z": 0
},
"inputs": {},
"position": [
-193.45283493150532,
3276.391368502901
]
},
"bottomwire": {
"id": "bottomwire",
"name": "bitbybit.occt.shapes.wire.interpolatePoints",
"customName": "bottom wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"periodic": false,
"tolerance": 0.1
},
"inputs": {
"points": {
"connections": [
{
"node": "5dd18ebda288b926",
"output": "list",
"data": {}
}
]
}
},
"position": [
800,
100
]
},
"middlewire": {
"id": "middlewire",
"name": "bitbybit.occt.shapes.wire.interpolatePoints",
"customName": "middle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"periodic": false,
"tolerance": 0.1
},
"inputs": {
"points": {
"connections": [
{
"node": "a78038f1fcf20364",
"output": "list",
"data": {}
}
]
}
},
"position": [
800,
600
]
},
"topwire": {
"id": "topwire",
"name": "bitbybit.occt.shapes.wire.interpolatePoints",
"customName": "top wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"periodic": false,
"tolerance": 0.1
},
"inputs": {
"points": {
"connections": [
{
"node": "dc177fbbaa4a29dd",
"output": "list",
"data": {}
}
]
}
},
"position": [
800,
1100
]
},
"43d7b57c779072dd": {
"id": "43d7b57c779072dd",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "bottomwire",
"output": "result",
"data": {}
},
{
"node": "middlewire",
"output": "result",
"data": {}
},
{
"node": "topwire",
"output": "result",
"data": {}
}
]
}
},
"position": [
1200,
600
]
},
"loftface": {
"id": "loftface",
"name": "bitbybit.occt.operations.loft",
"customName": "loft surface",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"makeSolid": false
},
"inputs": {
"shapes": {
"connections": [
{
"node": "43d7b57c779072dd",
"output": "list",
"data": {}
}
]
}
},
"position": [
1600,
600
]
},
"thicksolid": {
"id": "thicksolid",
"name": "bitbybit.occt.operations.makeThickSolidSimple",
"customName": "thick solid",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"offset": 0.1
},
"inputs": {
"shape": {
"connections": [
{
"node": "loftface",
"output": "result",
"data": {}
}
]
},
"offset": {
"connections": [
{
"node": "96c873eef1c78cc7",
"output": "result",
"data": {}
}
]
}
},
"position": [
2002.8551692459098,
976.481011449629
]
},
"96c873eef1c78cc7": {
"id": "96c873eef1c78cc7",
"name": "bitbybit.math.numberSlider",
"customName": "number slider",
"data": {
"options": {
"min": 0.1,
"max": 1,
"step": 0.05,
"width": 300,
"updateOnDrag": false
},
"number": 0.1
},
"inputs": {},
"position": [
1539.8694476885494,
1164.9000799960202
]
},
"translatesolid": {
"id": "translatesolid",
"name": "bitbybit.occt.transforms.translate",
"customName": "translate solid",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"translation": [
0,
0,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "thicksolid",
"output": "result",
"data": {}
}
]
},
"translation": {
"connections": [
{
"node": "translationvector",
"output": "result",
"data": {}
}
]
}
},
"position": [
2396.2389738447455,
1214.1709845358337
]
},
"translationvector": {
"id": "translationvector",
"name": "bitbybit.vector.vectorXYZ",
"customName": "offset vector",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 3
},
"inputs": {},
"position": [
1991.411559175958,
1412.0155515566744
]
},
"draworiginal": {
"id": "draworiginal",
"name": "bitbybit.draw.drawAnyAsync",
"customName": "draw loft surface",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"entity": {
"connections": [
{
"node": "loftface",
"output": "result",
"data": {}
}
]
},
"options": {
"connections": [
{
"node": "originalstyle",
"output": "result",
"data": {}
}
]
}
},
"position": [
2987.3294698123277,
115.68146773595515
]
},
"drawsolid": {
"id": "drawsolid",
"name": "bitbybit.draw.drawAnyAsync",
"customName": "draw thick solid",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"entity": {
"connections": [
{
"node": "translatesolid",
"output": "result",
"data": {}
}
]
},
"options": {
"connections": [
{
"node": "solidstyle",
"output": "result",
"data": {}
}
]
}
},
"position": [
2954.7988868780058,
1421.6642319213433
]
},
"originalstyle": {
"id": "originalstyle",
"name": "bitbybit.draw.optionsOcctShapeSimple",
"customName": "surface style",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"precision": 0.01,
"drawFaces": true,
"faceColour": "#00ff00",
"drawEdges": true,
"edgeColour": "#ffffff",
"edgeWidth": 2
},
"inputs": {},
"position": [
2386.091083151451,
-325.07566707744127
]
},
"solidstyle": {
"id": "solidstyle",
"name": "bitbybit.draw.optionsOcctShapeSimple",
"customName": "solid style",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"precision": 0.01,
"drawFaces": true,
"faceColour": "#ff6600",
"drawEdges": true,
"edgeColour": "#ffffff",
"edgeWidth": 2
},
"inputs": {},
"position": [
2394.9661875543998,
1607.1417164468255
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><variables><variable id="bottomPoints">bottomPoints</variable><variable id="middlePoints">middlePoints</variable><variable id="topPoints">topPoints</variable><variable id="bottomWire">bottomWire</variable><variable id="middleWire">middleWire</variable><variable id="topWire">topWire</variable><variable id="wireList">wireList</variable><variable id="loftSurface">loftSurface</variable><variable id="thickSolid">thickSolid</variable><variable id="translatedSolid">translatedSolid</variable><variable id="thickness">thickness</variable><variable id="offsetVector">offsetVector</variable><variable id="surfaceOptions">surfaceOptions</variable><variable id="solidOptions">solidOptions</variable></variables><block type="variables_set" id="bp_list" x="50" y="50"><field name="VAR" id="bottomPoints">bottomPoints</field><value name="VALUE"><block type="lists_create_with" id="bp_create"><mutation items="5"></mutation><value name="ADD0"><block type="bitbybit.point.pointXYZ" id="bp1"><value name="X"><block type="math_number" id="bp1_x"><field name="NUM">-3</field></block></value><value name="Y"><block type="math_number" id="bp1_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="bp1_z"><field name="NUM">-2</field></block></value></block></value><value name="ADD1"><block type="bitbybit.point.pointXYZ" id="bp2"><value name="X"><block type="math_number" id="bp2_x"><field name="NUM">-1</field></block></value><value name="Y"><block type="math_number" id="bp2_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="bp2_z"><field name="NUM">0</field></block></value></block></value><value name="ADD2"><block type="bitbybit.point.pointXYZ" id="bp3"><value name="X"><block type="math_number" id="bp3_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="bp3_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="bp3_z"><field name="NUM">1</field></block></value></block></value><value name="ADD3"><block type="bitbybit.point.pointXYZ" id="bp4"><value name="X"><block type="math_number" id="bp4_x"><field name="NUM">1</field></block></value><value name="Y"><block type="math_number" id="bp4_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="bp4_z"><field name="NUM">0</field></block></value></block></value><value name="ADD4"><block type="bitbybit.point.pointXYZ" id="bp5"><value name="X"><block type="math_number" id="bp5_x"><field name="NUM">3</field></block></value><value name="Y"><block type="math_number" id="bp5_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="bp5_z"><field name="NUM">-2</field></block></value></block></value></block></value><next><block type="variables_set" id="mp_list" x="50" y="150"><field name="VAR" id="middlePoints">middlePoints</field><value name="VALUE"><block type="lists_create_with" id="mp_create"><mutation items="5"></mutation><value name="ADD0"><block type="bitbybit.point.pointXYZ" id="mp1"><value name="X"><block type="math_number" id="mp1_x"><field name="NUM">-2</field></block></value><value name="Y"><block type="math_number" id="mp1_y"><field name="NUM">2</field></block></value><value name="Z"><block type="math_number" id="mp1_z"><field name="NUM">-1</field></block></value></block></value><value name="ADD1"><block type="bitbybit.point.pointXYZ" id="mp2"><value name="X"><block type="math_number" id="mp2_x"><field name="NUM">-0.5</field></block></value><value name="Y"><block type="math_number" id="mp2_y"><field name="NUM">2</field></block></value><value name="Z"><block type="math_number" id="mp2_z"><field name="NUM">1.5</field></block></value></block></value><value name="ADD2"><block type="bitbybit.point.pointXYZ" id="mp3"><value name="X"><block type="math_number" id="mp3_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="mp3_y"><field name="NUM">2</field></block></value><value name="Z"><block type="math_number" id="mp3_z"><field name="NUM">2</field></block></value></block></value><value name="ADD3"><block type="bitbybit.point.pointXYZ" id="mp4"><value name="X"><block type="math_number" id="mp4_x"><field name="NUM">0.5</field></block></value><value name="Y"><block type="math_number" id="mp4_y"><field name="NUM">2</field></block></value><value name="Z"><block type="math_number" id="mp4_z"><field name="NUM">1.5</field></block></value></block></value><value name="ADD4"><block type="bitbybit.point.pointXYZ" id="mp5"><value name="X"><block type="math_number" id="mp5_x"><field name="NUM">2</field></block></value><value name="Y"><block type="math_number" id="mp5_y"><field name="NUM">2</field></block></value><value name="Z"><block type="math_number" id="mp5_z"><field name="NUM">-1</field></block></value></block></value></block></value><next><block type="variables_set" id="tp_list" x="50" y="250"><field name="VAR" id="topPoints">topPoints</field><value name="VALUE"><block type="lists_create_with" id="tp_create"><mutation items="5"></mutation><value name="ADD0"><block type="bitbybit.point.pointXYZ" id="tp1"><value name="X"><block type="math_number" id="tp1_x"><field name="NUM">-1.5</field></block></value><value name="Y"><block type="math_number" id="tp1_y"><field name="NUM">4</field></block></value><value name="Z"><block type="math_number" id="tp1_z"><field name="NUM">0</field></block></value></block></value><value name="ADD1"><block type="bitbybit.point.pointXYZ" id="tp2"><value name="X"><block type="math_number" id="tp2_x"><field name="NUM">-0.5</field></block></value><value name="Y"><block type="math_number" id="tp2_y"><field name="NUM">4</field></block></value><value name="Z"><block type="math_number" id="tp2_z"><field name="NUM">0.5</field></block></value></block></value><value name="ADD2"><block type="bitbybit.point.pointXYZ" id="tp3"><value name="X"><block type="math_number" id="tp3_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="tp3_y"><field name="NUM">4</field></block></value><value name="Z"><block type="math_number" id="tp3_z"><field name="NUM">1</field></block></value></block></value><value name="ADD3"><block type="bitbybit.point.pointXYZ" id="tp4"><value name="X"><block type="math_number" id="tp4_x"><field name="NUM">0.5</field></block></value><value name="Y"><block type="math_number" id="tp4_y"><field name="NUM">4</field></block></value><value name="Z"><block type="math_number" id="tp4_z"><field name="NUM">0.5</field></block></value></block></value><value name="ADD4"><block type="bitbybit.point.pointXYZ" id="tp5"><value name="X"><block type="math_number" id="tp5_x"><field name="NUM">1.5</field></block></value><value name="Y"><block type="math_number" id="tp5_y"><field name="NUM">4</field></block></value><value name="Z"><block type="math_number" id="tp5_z"><field name="NUM">0</field></block></value></block></value></block></value><next><block type="variables_set" id="bw_create" x="50" y="350"><field name="VAR" id="bottomWire">bottomWire</field><value name="VALUE"><block type="bitbybit.occt.shapes.wire.interpolatePoints" id="bottom_wire"><value name="Points"><block type="variables_get" id="get_bp"><field name="VAR" id="bottomPoints">bottomPoints</field></block></value><value name="Periodic"><block type="logic_boolean" id="bp_periodic"><field name="BOOL">FALSE</field></block></value><value name="Tolerance"><block type="math_number" id="bp_tolerance"><field name="NUM">0.1</field></block></value></block></value><next><block type="variables_set" id="mw_create" x="50" y="450"><field name="VAR" id="middleWire">middleWire</field><value name="VALUE"><block type="bitbybit.occt.shapes.wire.interpolatePoints" id="middle_wire"><value name="Points"><block type="variables_get" id="get_mp"><field name="VAR" id="middlePoints">middlePoints</field></block></value><value name="Periodic"><block type="logic_boolean" id="mp_periodic"><field name="BOOL">FALSE</field></block></value><value name="Tolerance"><block type="math_number" id="mp_tolerance"><field name="NUM">0.1</field></block></value></block></value><next><block type="variables_set" id="tw_create" x="50" y="550"><field name="VAR" id="topWire">topWire</field><value name="VALUE"><block type="bitbybit.occt.shapes.wire.interpolatePoints" id="top_wire"><value name="Points"><block type="variables_get" id="get_tp"><field name="VAR" id="topPoints">topPoints</field></block></value><value name="Periodic"><block type="logic_boolean" id="tp_periodic"><field name="BOOL">FALSE</field></block></value><value name="Tolerance"><block type="math_number" id="tp_tolerance"><field name="NUM">0.1</field></block></value></block></value><next><block type="variables_set" id="wl_create" x="50" y="650"><field name="VAR" id="wireList">wireList</field><value name="VALUE"><block type="lists_create_with" id="wire_list"><mutation items="3"></mutation><value name="ADD0"><block type="variables_get" id="get_bw"><field name="VAR" id="bottomWire">bottomWire</field></block></value><value name="ADD1"><block type="variables_get" id="get_mw"><field name="VAR" id="middleWire">middleWire</field></block></value><value name="ADD2"><block type="variables_get" id="get_tw"><field name="VAR" id="topWire">topWire</field></block></value></block></value><next><block type="variables_set" id="loft_create" x="50" y="750"><field name="VAR" id="loftSurface">loftSurface</field><value name="VALUE"><block type="bitbybit.occt.operations.loft" id="loft_surface"><value name="Shapes"><block type="variables_get" id="get_wl"><field name="VAR" id="wireList">wireList</field></block></value><value name="MakeSolid"><block type="logic_boolean" id="loft_makesolid"><field name="BOOL">FALSE</field></block></value></block></value><next><block type="variables_set" id="thick_slider" x="50" y="850"><field name="VAR" id="thickness">thickness</field><value name="VALUE"><block type="math_number" id="thickness_val"><field name="NUM">0.1</field></block></value><next><block type="variables_set" id="thick_create" x="50" y="950"><field name="VAR" id="thickSolid">thickSolid</field><value name="VALUE"><block type="bitbybit.occt.operations.makeThickSolidSimple" id="thick_solid"><value name="Shape"><block type="variables_get" id="get_loft"><field name="VAR" id="loftSurface">loftSurface</field></block></value><value name="Offset"><block type="variables_get" id="get_thick"><field name="VAR" id="thickness">thickness</field></block></value></block></value><next><block type="variables_set" id="offset_vec" x="50" y="1050"><field name="VAR" id="offsetVector">offsetVector</field><value name="VALUE"><block type="bitbybit.vector.vectorXYZ" id="offset_vector"><value name="X"><block type="math_number" id="offset_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="offset_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="offset_z"><field name="NUM">3</field></block></value></block></value><next><block type="variables_set" id="trans_create" x="50" y="1150"><field name="VAR" id="translatedSolid">translatedSolid</field><value name="VALUE"><block type="bitbybit.occt.transforms.translate" id="translate_solid"><value name="Shape"><block type="variables_get" id="get_thick_solid"><field name="VAR" id="thickSolid">thickSolid</field></block></value><value name="Translation"><block type="variables_get" id="get_offset"><field name="VAR" id="offsetVector">offsetVector</field></block></value></block></value><next><block type="variables_set" id="surf_options" x="50" y="1250"><field name="VAR" id="surfaceOptions">surfaceOptions</field><value name="VALUE"><block type="bitbybit.draw.optionsOcctShapeSimple" id="surface_options"><value name="Precision"><block type="math_number" id="surf_precision"><field name="NUM">0.01</field></block></value><value name="DrawFaces"><block type="logic_boolean" id="surf_drawfaces"><field name="BOOL">TRUE</field></block></value><value name="FaceColour"><block type="colour_picker" id="surf_facecolor"><field name="COLOUR">#00ff00</field></block></value><value name="DrawEdges"><block type="logic_boolean" id="surf_drawedges"><field name="BOOL">TRUE</field></block></value><value name="EdgeColour"><block type="colour_picker" id="surf_edgecolor"><field name="COLOUR">#ffffff</field></block></value><value name="EdgeWidth"><block type="math_number" id="surf_edgewidth"><field name="NUM">2</field></block></value></block></value><next><block type="variables_set" id="solid_options" x="50" y="1350"><field name="VAR" id="solidOptions">solidOptions</field><value name="VALUE"><block type="bitbybit.draw.optionsOcctShapeSimple" id="solid_options"><value name="Precision"><block type="math_number" id="solid_precision"><field name="NUM">0.01</field></block></value><value name="DrawFaces"><block type="logic_boolean" id="solid_drawfaces"><field name="BOOL">TRUE</field></block></value><value name="FaceColour"><block type="colour_picker" id="solid_facecolor"><field name="COLOUR">#ff6600</field></block></value><value name="DrawEdges"><block type="logic_boolean" id="solid_drawedges"><field name="BOOL">TRUE</field></block></value><value name="EdgeColour"><block type="colour_picker" id="solid_edgecolor"><field name="COLOUR">#ffffff</field></block></value><value name="EdgeWidth"><block type="math_number" id="solid_edgewidth"><field name="NUM">2</field></block></value></block></value><next><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="draw_surface" x="50" y="1450"><value name="Entity"><block type="variables_get" id="get_loft_surface"><field name="VAR" id="loftSurface">loftSurface</field></block></value><value name="Options"><block type="variables_get" id="get_surf_opts"><field name="VAR" id="surfaceOptions">surfaceOptions</field></block></value><next><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="draw_solid" x="50" y="1550"><value name="Entity"><block type="variables_get" id="get_trans_solid"><field name="VAR" id="translatedSolid">translatedSolid</field></block></value><value name="Options"><block type="variables_get" id="get_solid_opts"><field name="VAR" id="solidOptions">solidOptions</field></block></value></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></xml>
// Import required modules from global bitbybit variable
const { wire } = bitbybit.occt.shapes;
const { operations } = bitbybit.occt;
const { transforms } = bitbybit.occt;
const { InterpolationDto, LoftDto, ThisckSolidSimpleDto, TranslateDto } = Bit.Inputs.OCCT;
type Point3 = Bit.Inputs.Base.Point3;
type Vector3 = Bit.Inputs.Base.Vector3;
// Define a type alias for pointers
type TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;
type TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;
type TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;
const start = async () => {
// Define points for bottom curve (wave-like pattern)
const bottomPoints = [
[-3, 0, -2],
[-1, 0, 0],
[0, 0, 1],
[1, 0, 0],
[3, 0, -2]
] as Point3[];
// Define points for middle curve (elevated and more curved)
const middlePoints = [
[-2, 2, -1],
[-0.5, 2, 1.5],
[0, 2, 2],
[0.5, 2, 1.5],
[2, 2, -1]
] as Point3[];
// Define points for top curve (simpler, less curved)
const topPoints = [
[-1.5, 4, 0],
[-0.5, 4, 0.5],
[0, 4, 1],
[0.5, 4, 0.5],
[1.5, 4, 0]
] as Point3[];
// Create interpolated wires from points
const bottomWireOpt = new InterpolationDto();
bottomWireOpt.points = bottomPoints;
bottomWireOpt.periodic = false;
bottomWireOpt.tolerance = 0.1;
const bottomWire = await wire.interpolatePoints(bottomWireOpt);
const middleWireOpt = new InterpolationDto();
middleWireOpt.points = middlePoints;
middleWireOpt.periodic = false;
middleWireOpt.tolerance = 0.1;
const middleWire = await wire.interpolatePoints(middleWireOpt);
const topWireOpt = new InterpolationDto();
topWireOpt.points = topPoints;
topWireOpt.periodic = false;
topWireOpt.tolerance = 0.1;
const topWire = await wire.interpolatePoints(topWireOpt);
// Create lofted surface from the three wires
const loftOpt = new LoftDto<TopoDSWirePointer>();
loftOpt.shapes = [bottomWire, middleWire, topWire];
loftOpt.makeSolid = false;
const loftSurface = await operations.loft(loftOpt);
// Create thick solid from the lofted surface
const thickness = 0.1;
const thickSolidOpt = new ThisckSolidSimpleDto<TopoDSFacePointer>();
thickSolidOpt.shape = loftSurface;
thickSolidOpt.offset = thickness;
const thickSolid = await operations.makeThickSolidSimple(thickSolidOpt);
// Translate the thick solid for better visualization
const offsetVector = [0, 0, 3] as Vector3;
const translateOpt = new TranslateDto<TopoDSShapePointer>();
translateOpt.shape = thickSolid;
translateOpt.translation = offsetVector;
const translatedSolid = await transforms.translate(translateOpt);
// Create drawing options for the original surface (green)
const surfaceOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();
surfaceOptions.precision = 0.01;
surfaceOptions.drawFaces = true;
surfaceOptions.faceColour = "#00ff00";
surfaceOptions.drawEdges = true;
surfaceOptions.edgeColour = "#ffffff";
surfaceOptions.edgeWidth = 2;
// Create drawing options for the thick solid (orange)
const solidOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();
solidOptions.precision = 0.01;
solidOptions.drawFaces = true;
solidOptions.faceColour = "#ff6600";
solidOptions.drawEdges = true;
solidOptions.edgeColour = "#ffffff";
solidOptions.edgeWidth = 2;
// Draw both the original lofted surface and the thick solid
bitbybit.draw.drawAnyAsync({ entity: loftSurface, options: surfaceOptions });
bitbybit.draw.drawAnyAsync({ entity: translatedSolid, options: solidOptions });
}
start();
Key Learning Points
Design Considerations
When working with thick solids from complex faces:
- Surface Continuity: Ensure your lofted surface has smooth transitions to avoid artifacts in the thick solid
- Thickness Distribution: The algorithm maintains consistent thickness perpendicular to the surface
- Self-Intersections: Complex shapes may create self-intersecting geometry; consider your design carefully
- Normal Direction: The thickness direction is determined automatically by face normals
Advanced Techniques
- Multi-Surface Lofting: Create even more complex shapes by lofting between numerous cross-sections
- Hybrid Operations: Combine with other OCCT operations like fillets, chamfers, or boolean operations
Performance Tips
- Point Density: Balance curve smoothness with computational efficiency
- Surface Complexity: More complex lofted surfaces require more processing time
- Memory Usage: Large thick solids can consume significant memory resources
Summary
Thick solids from complex lofted faces represent one of the most powerful techniques in 3D modeling. By combining:
- Interpolated curves for smooth cross-sections
- Loft operations for complex surface generation
- Thick solid operations for volumetric creation
You can create sophisticated 3D geometry that would be difficult or impossible to achieve through simple primitive operations. This approach bridges the gap between mathematical precision and organic design freedom.



