Simple Holes
Hollow shapes are everywhere around us - from simple picture frames and washers to complex architectural elements like windows, decorative panels, and mechanical components. In 3D modeling, creating holes in faces and solids is a fundamental technique that opens up countless design possibilities.
Whether you're designing a mounting bracket with bolt holes, creating decorative lattice work, or modeling architectural elements with openings, understanding how to properly create hollow shapes is essential. This tutorial will guide you through the process step by step.
Most Simple Case - Hollow Face From 2 Wires
Creating hollow shapes starts with understanding how to make a face with a hole. The key insight is that you need two wire shapes: an outer boundary that defines the overall shape, and an inner boundary that defines the hole. The crucial step is reversing the inner wire to ensure proper orientation, then combining both wires to create a single face.
This technique is fundamental because it establishes the basic principle that applies to more complex scenarios. Once you master creating a simple hollow face, you can extend this to create 3D solids and even shapes with multiple holes.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"b0069119ec687f11": {
"id": "b0069119ec687f11",
"name": "bitbybit.occt.shapes.wire.createSquareWire",
"customName": "square wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"size": 20,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {},
"position": [
223.93108749389648,
163.19955444335938
]
},
"dc7b60c317041e4d": {
"id": "dc7b60c317041e4d",
"name": "bitbybit.occt.shapes.wire.createSquareWire",
"customName": "square wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"size": 7,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {},
"position": [
223.09658432006836,
563.6718444824219
]
},
"25d9fb9a9dcc3e31": {
"id": "25d9fb9a9dcc3e31",
"name": "bitbybit.occt.shapes.wire.reversedWire",
"customName": "reversed wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "dc7b60c317041e4d",
"output": "result",
"data": {}
}
]
}
},
"position": [
624.2257705801807,
562.6551012575662
]
},
"98bd8ebbc85d0692": {
"id": "98bd8ebbc85d0692",
"name": "bitbybit.occt.shapes.face.createFaceFromWires",
"customName": "face from wires",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"planar": true
},
"inputs": {
"shapes": {
"connections": [
{
"node": "9e00c37356aadd34",
"output": "list",
"data": {}
}
]
}
},
"position": [
1546.8197047075262,
159.51746997237115
]
},
"9e00c37356aadd34": {
"id": "9e00c37356aadd34",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "b0069119ec687f11",
"output": "result",
"data": {}
},
{
"node": "25d9fb9a9dcc3e31",
"output": "result",
"data": {}
}
]
}
},
"position": [
1093.4795203231492,
201.57381450571927
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="Z%mk)FMhtJF^)mdL?!=+" x="-424" y="-347"><value name="Entity"><block type="bitbybit.occt.shapes.face.createFaceFromWires" id="hy1i#4xa1d4uOH+O0}i+"><value name="Shapes"><block type="lists_create_with" id="bNs_Dg6oM5G,=sNdaTXT"><mutation items="2"></mutation><value name="ADD0"><block type="bitbybit.occt.shapes.wire.createSquareWire" id="`q0Tb@52Gc;4x*ijv=h2"><value name="Size"><block type="math_number" id="z-oithPH;Z3{z55v2,?]"><field name="NUM">20</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ" id="qgThX/SdZ8/SUhL/7c-P"><value name="X"><block type="math_number" id="MYHdYxfv#G_G)?%M9.ci"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="(%?.g@BzRdBKJ,UB.B/!"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id=";cKej|~0=,T^(.nJp_PL"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="6WT4yi+,NX@WI~r^1_YL"><value name="X"><block type="math_number" id="{2sY0NVhq5(6=ZGTBPU("><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="%om/(ym.H*#.o=*|t4?b"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="v{e{8PLv_p;Bl(]{qlP#"><field name="NUM">0</field></block></value></block></value></block></value><value name="ADD1"><block type="bitbybit.occt.shapes.wire.reversedWire" id="G2q[H[1OGk9R%@*jG(8t"><value name="Shape"><block type="bitbybit.occt.shapes.wire.createSquareWire" id="DmldVYN^(R~s6$c:2{ul"><value name="Size"><block type="math_number" id="3LFQAA6%(1ll:mq7tkU0"><field name="NUM">7</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ" id="ae~e`MN~(:nDhJ_(TKyc"><value name="X"><block type="math_number" id="IDlLV1s%D:Wvo4$`EMr8"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="|HCo=!2q*eKga8tfL-Wi"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="/lyVCT:DZfxKxn@R3$Nn"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="c`JZk4zH7f?gT}hF93xW"><value name="X"><block type="math_number" id="l0e^L?1ZW#M^mELCvcd#"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="3~zisU/+K|+PpmwmnhI="><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="|RULQxargM3iJR@,`tT:"><field name="NUM">0</field></block></value></block></value></block></value></block></value></block></value><value name="Planar"><block type="logic_boolean" id="!r/BCa`l#LvPSJbIRfST"><field name="BOOL">TRUE</field></block></value></block></value></block></xml>
// Import required types and DTOs for creating wire shapes and faces
const { SquareDto, FaceFromWiresDto } = Bit.Inputs.OCCT;
// Import the wire pointer type for type safety
type TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;
// Get direct access to OCCT wire and face creation functions
const { wire } = bitbybit.occt.shapes;
const { face } = bitbybit.occt.shapes;
// Define the main function to create a simple hole
const start = async () => {
// Create the outer square wire (boundary of the shape)
const outerWireOptions = new SquareDto();
outerWireOptions.size = 20;
outerWireOptions.center = [0, 0, 0];
outerWireOptions.direction = [0, 1, 0];
const outerWire = await wire.createSquareWire(outerWireOptions);
// Create the inner square wire (defines the hole)
const innerWireOptions = new SquareDto();
innerWireOptions.size = 7;
innerWireOptions.center = [0, 0, 0];
innerWireOptions.direction = [0, 1, 0];
const innerWire = await wire.createSquareWire(innerWireOptions);
// Reverse the inner wire - this is crucial for proper hole creation
const reversedInnerWire = await wire.reversedWire({ shape: innerWire });
// Create a face from both wires - outer boundary and inner hole
const faceOptions = new FaceFromWiresDto<TopoDSWirePointer>();
faceOptions.shapes = [outerWire, reversedInnerWire];
faceOptions.planar = true;
const faceWithHole = await face.createFaceFromWires(faceOptions);
// Draw the resulting face with hole
bitbybit.draw.drawAnyAsync({
entity: faceWithHole
});
}
// Execute the function
start();
Hollow Solid From Hollow Face
A flat face with a hole is just the beginning. In real-world applications, you typically need 3D solid objects - think of structural components like mounting plates, decorative panels with cutouts, or mechanical parts with through-holes for bolts and pins.
The transition from a 2D hollow face to a 3D hollow solid is achieved through extrusion. This operation takes your flat face and extends it in a specific direction, creating volume while maintaining the hole structure. This technique is widely used in manufacturing and architectural design where you need consistent cross-sections along a length.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"a40791de993f0afc": {
"id": "a40791de993f0afc",
"name": "bitbybit.occt.shapes.wire.createRectangleWire",
"customName": "rectangle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"width": 20,
"length": 14,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {},
"position": [
235.26992416381836,
273.7322082519531
]
},
"1a3c61327ab90831": {
"id": "1a3c61327ab90831",
"name": "bitbybit.occt.fillets.fillet2d",
"customName": "fillet 2d",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1
},
"inputs": {
"shape": {
"connections": [
{
"node": "a40791de993f0afc",
"output": "result",
"data": {}
}
]
}
},
"position": [
761.8246534376177,
271.4830489880828
]
},
"9fdc2e1304cae30e": {
"id": "9fdc2e1304cae30e",
"name": "bitbybit.occt.shapes.wire.createRectangleWire",
"customName": "rectangle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"width": 7,
"length": 7,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {},
"position": [
234.6604951337187,
713.0507836860832
]
},
"bf6923103a9464d5": {
"id": "bf6923103a9464d5",
"name": "bitbybit.occt.fillets.fillet2d",
"customName": "fillet 2d",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1
},
"inputs": {
"shape": {
"connections": [
{
"node": "9fdc2e1304cae30e",
"output": "result",
"data": {}
}
]
}
},
"position": [
762.3586133674172,
716.1975827887076
]
},
"545e11b6bfac5bf8": {
"id": "545e11b6bfac5bf8",
"name": "bitbybit.occt.transforms.rotate",
"customName": "rotate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"axis": [
0,
0,
1
],
"angle": 45
},
"inputs": {
"shape": {
"connections": [
{
"node": "bf6923103a9464d5",
"output": "result",
"data": {}
}
]
},
"axis": {
"connections": [
{
"node": "c03b0a64e4ab9e5e",
"output": "result",
"data": {}
}
]
}
},
"position": [
1419.2137032562428,
711.311651307647
]
},
"c03b0a64e4ab9e5e": {
"id": "c03b0a64e4ab9e5e",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 1,
"z": 0
},
"inputs": {},
"position": [
1036.2484097490146,
1144.6910721170389
]
},
"79dee5807bc33d08": {
"id": "79dee5807bc33d08",
"name": "bitbybit.occt.shapes.wire.reversedWire",
"customName": "reversed wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "545e11b6bfac5bf8",
"output": "result",
"data": {}
}
]
}
},
"position": [
1824.44838930116,
710.5503728648622
]
},
"fae4dbe0969ccc77": {
"id": "fae4dbe0969ccc77",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "1a3c61327ab90831",
"output": "result",
"data": {}
},
{
"node": "79dee5807bc33d08",
"output": "result",
"data": {}
}
]
}
},
"position": [
2306.6073628003414,
318.4353457639395
]
},
"40bd4a435439bc41": {
"id": "40bd4a435439bc41",
"name": "bitbybit.occt.shapes.face.createFaceFromWires",
"customName": "face from wires",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"planar": true
},
"inputs": {
"shapes": {
"connections": [
{
"node": "fae4dbe0969ccc77",
"output": "list",
"data": {}
}
]
}
},
"position": [
2694.089089729928,
278.257134124378
]
},
"510ee0e3b16e68a9": {
"id": "510ee0e3b16e68a9",
"name": "bitbybit.occt.operations.extrude",
"customName": "extrude",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"direction": [
0,
1,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "40bd4a435439bc41",
"output": "result",
"data": {}
}
]
},
"direction": {
"connections": [
{
"node": "65cf9c51bd41a415",
"output": "result",
"data": {}
}
]
}
},
"position": [
3086.756349822829,
277.2924471685186
]
},
"65cf9c51bd41a415": {
"id": "65cf9c51bd41a415",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 2,
"z": 0
},
"inputs": {},
"position": [
2689.6156662338262,
662.7799832820917
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="main_draw" x="-200" y="-200"><value name="Entity"><block type="bitbybit.occt.operations.extrude" id="extrude_face"><value name="Shape"><block type="bitbybit.occt.shapes.face.createFaceFromWires" id="face_from_wires"><value name="Shapes"><block type="lists_create_with" id="wire_list"><mutation items="2"></mutation><value name="ADD0"><block type="bitbybit.occt.fillets.fillet2d" id="outer_fillet"><value name="Shape"><block type="bitbybit.occt.shapes.wire.createRectangleWire" id="outer_rectangle"><value name="Width"><block type="math_number" id="outer_width"><field name="NUM">20</field></block></value><value name="Length"><block type="math_number" id="outer_length"><field name="NUM">14</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ" id="outer_center"><value name="X"><block type="math_number" id="outer_center_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="outer_center_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="outer_center_z"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="outer_direction"><value name="X"><block type="math_number" id="outer_dir_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="outer_dir_y"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="outer_dir_z"><field name="NUM">0</field></block></value></block></value></block></value><value name="Radius"><block type="math_number" id="outer_fillet_radius"><field name="NUM">1</field></block></value></block></value><value name="ADD1"><block type="bitbybit.occt.shapes.wire.reversedWire" id="reversed_inner"><value name="Shape"><block type="bitbybit.occt.transforms.rotate" id="rotate_inner"><value name="Shape"><block type="bitbybit.occt.fillets.fillet2d" id="inner_fillet"><value name="Shape"><block type="bitbybit.occt.shapes.wire.createRectangleWire" id="inner_rectangle"><value name="Width"><block type="math_number" id="inner_width"><field name="NUM">7</field></block></value><value name="Length"><block type="math_number" id="inner_length"><field name="NUM">7</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ" id="inner_center"><value name="X"><block type="math_number" id="inner_center_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="inner_center_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="inner_center_z"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="inner_direction"><value name="X"><block type="math_number" id="inner_dir_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="inner_dir_y"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="inner_dir_z"><field name="NUM">0</field></block></value></block></value></block></value><value name="Radius"><block type="math_number" id="inner_fillet_radius"><field name="NUM">1</field></block></value></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="rotation_axis"><value name="X"><block type="math_number" id="rot_axis_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="rot_axis_y"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="rot_axis_z"><field name="NUM">0</field></block></value></block></value><value name="Angle"><block type="math_number" id="rotation_angle"><field name="NUM">45</field></block></value></block></value></block></value></block></value><value name="Planar"><block type="logic_boolean" id="planar_flag"><field name="BOOL">TRUE</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="extrude_direction"><value name="X"><block type="math_number" id="extrude_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="extrude_y"><field name="NUM">2</field></block></value><value name="Z"><block type="math_number" id="extrude_z"><field name="NUM">0</field></block></value></block></value></block></value></block></xml>
// Import required DTOs for creating wires, fillets, faces, and extrusion operations
const { RectangleDto, FilletDto, FaceFromWiresDto, ExtrudeDto } = Bit.Inputs.OCCT;
// Import wire pointer type for type safety
type TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;
type TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;
// Get access to OCCT modules for creating shapes and operations
const { wire, face } = bitbybit.occt.shapes;
const { fillets, operations, transforms } = bitbybit.occt;
// Define the main function to create an extruded solid with a hole
const start = async () => {
// Create the outer rectangular wire
const outerWireOptions = new RectangleDto();
outerWireOptions.width = 20;
outerWireOptions.length = 14;
outerWireOptions.center = [0, 0, 0];
outerWireOptions.direction = [0, 1, 0];
const outerWire = await wire.createRectangleWire(outerWireOptions);
// Apply fillet to the outer wire for rounded corners
const outerFilletOptions = new FilletDto<TopoDSWirePointer>();
outerFilletOptions.radius = 1;
outerFilletOptions.shape = outerWire;
const filletedOuterWire = await fillets.fillet2d(outerFilletOptions);
// Create the inner rectangular wire (hole)
const innerWireOptions = new RectangleDto();
innerWireOptions.width = 7;
innerWireOptions.length = 7;
innerWireOptions.center = [0, 0, 0];
innerWireOptions.direction = [0, 1, 0];
const innerWire = await wire.createRectangleWire(innerWireOptions);
// Apply fillet to the inner wire
const innerFilletOptions = new FilletDto<TopoDSWirePointer>();
innerFilletOptions.radius = 1;
innerFilletOptions.shape = innerWire;
const filletedInnerWire = await fillets.fillet2d(innerFilletOptions);
// Rotate the inner wire by 45 degrees for visual interest
const rotatedInnerWire = await transforms.rotate({
shape: filletedInnerWire,
axis: [0, 1, 0],
angle: 45
});
// Reverse the rotated inner wire for proper hole creation
const reversedInnerWire = await wire.reversedWire({ shape: rotatedInnerWire });
// Create a face from both wires
const faceOptions = new FaceFromWiresDto<TopoDSWirePointer>();
faceOptions.shapes = [filletedOuterWire, reversedInnerWire];
faceOptions.planar = true;
const faceWithHole = await face.createFaceFromWires(faceOptions);
// Extrude the face to create a 3D solid
const extrudeOptions = new ExtrudeDto<TopoDSFacePointer>();
extrudeOptions.shape = faceWithHole;
extrudeOptions.direction = [0, 2, 0]; // Extrude 2 units in Y direction
const solidWithHole = await operations.extrude(extrudeOptions);
// Draw the resulting 3D solid with hole
bitbybit.draw.drawAnyAsync({
entity: solidWithHole
});
}
// Execute the function
start();
Hollow Solid With Multiple Holes
Once you understand the basic principle, creating shapes with multiple holes becomes straightforward. This technique is particularly valuable in manufacturing scenarios where you need multiple mounting points, ventilation openings, or weight reduction features in a single component.
The process simply involves creating additional inner wires and including them all in the wire list when creating your face. Notice in this example that we reversed the outer wire instead of each inner wire - this is purely an optimization to reduce computations and doesn't affect the final result.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"a40791de993f0afc": {
"id": "a40791de993f0afc",
"name": "bitbybit.occt.shapes.wire.createRectangleWire",
"customName": "rectangle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"width": 23,
"length": 14,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {},
"position": [
235.26992416381836,
273.7322082519531
]
},
"1a3c61327ab90831": {
"id": "1a3c61327ab90831",
"name": "bitbybit.occt.fillets.fillet2d",
"customName": "fillet 2d",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1
},
"inputs": {
"shape": {
"connections": [
{
"node": "a40791de993f0afc",
"output": "result",
"data": {}
}
]
}
},
"position": [
761.8246534376177,
271.4830489880828
]
},
"9fdc2e1304cae30e": {
"id": "9fdc2e1304cae30e",
"name": "bitbybit.occt.shapes.wire.createRectangleWire",
"customName": "rectangle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"width": 7,
"length": 7,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {},
"position": [
234.6604951337187,
713.0507836860832
]
},
"bf6923103a9464d5": {
"id": "bf6923103a9464d5",
"name": "bitbybit.occt.fillets.fillet2d",
"customName": "fillet 2d",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1
},
"inputs": {
"shape": {
"connections": [
{
"node": "9fdc2e1304cae30e",
"output": "result",
"data": {}
}
]
}
},
"position": [
762.3586133674172,
716.1975827887076
]
},
"545e11b6bfac5bf8": {
"id": "545e11b6bfac5bf8",
"name": "bitbybit.occt.transforms.rotate",
"customName": "rotate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"axis": [
0,
0,
1
],
"angle": 45
},
"inputs": {
"shape": {
"connections": [
{
"node": "bf6923103a9464d5",
"output": "result",
"data": {}
}
]
},
"axis": {
"connections": [
{
"node": "c03b0a64e4ab9e5e",
"output": "result",
"data": {}
}
]
}
},
"position": [
1419.2137032562428,
711.311651307647
]
},
"c03b0a64e4ab9e5e": {
"id": "c03b0a64e4ab9e5e",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 1,
"z": 0
},
"inputs": {},
"position": [
1036.2484097490146,
1144.6910721170389
]
},
"fae4dbe0969ccc77": {
"id": "fae4dbe0969ccc77",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "df6e81362634050f",
"output": "result",
"data": {}
},
{
"node": "c0bb7d03c361c283",
"output": "result",
"data": {}
},
{
"node": "0a01e494339874c8",
"output": "result",
"data": {}
}
]
}
},
"position": [
2540.74688432131,
303.1831624969799
]
},
"40bd4a435439bc41": {
"id": "40bd4a435439bc41",
"name": "bitbybit.occt.shapes.face.createFaceFromWires",
"customName": "face from wires",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"planar": true
},
"inputs": {
"shapes": {
"connections": [
{
"node": "fae4dbe0969ccc77",
"output": "list",
"data": {}
}
]
}
},
"position": [
2928.8572926604593,
261.24033092051985
]
},
"510ee0e3b16e68a9": {
"id": "510ee0e3b16e68a9",
"name": "bitbybit.occt.operations.extrude",
"customName": "extrude",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"direction": [
0,
1,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "40bd4a435439bc41",
"output": "result",
"data": {}
}
]
},
"direction": {
"connections": [
{
"node": "65cf9c51bd41a415",
"output": "result",
"data": {}
}
]
}
},
"position": [
3345.5625775683575,
258.25897099730906
]
},
"65cf9c51bd41a415": {
"id": "65cf9c51bd41a415",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 2,
"z": 0
},
"inputs": {},
"position": [
2925.911440965675,
572.0506528082972
]
},
"d07a5d8d4e4897f9": {
"id": "d07a5d8d4e4897f9",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -5,
"y": 0,
"z": 0
},
"inputs": {},
"position": [
1535.7769532741295,
1486.0062390796131
]
},
"c0bb7d03c361c283": {
"id": "c0bb7d03c361c283",
"name": "bitbybit.occt.transforms.translate",
"customName": "translate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"translation": [
0,
0,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "545e11b6bfac5bf8",
"output": "result",
"data": {}
}
]
},
"translation": {
"connections": [
{
"node": "5c200720b28aad06",
"output": "result",
"data": {}
}
]
}
},
"position": [
2019.4985713264,
712.2232369281196
]
},
"5c200720b28aad06": {
"id": "5c200720b28aad06",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 5,
"y": 0,
"z": 0
},
"inputs": {},
"position": [
1539.2598257536324,
1129.4882093673511
]
},
"0a01e494339874c8": {
"id": "0a01e494339874c8",
"name": "bitbybit.occt.transforms.translate",
"customName": "translate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"translation": [
0,
0,
0
]
},
"inputs": {
"translation": {
"connections": [
{
"node": "d07a5d8d4e4897f9",
"output": "result",
"data": {}
}
]
},
"shape": {
"connections": [
{
"node": "545e11b6bfac5bf8",
"output": "result",
"data": {}
}
]
}
},
"position": [
2024.2778084598685,
1042.4050693983645
]
},
"df6e81362634050f": {
"id": "df6e81362634050f",
"name": "bitbybit.occt.shapes.wire.reversedWire",
"customName": "reversed wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "1a3c61327ab90831",
"output": "result",
"data": {}
}
]
}
},
"position": [
2035.891381182449,
263.7139287874433
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="main_draw" x="-200" y="-200"><value name="Entity"><block type="bitbybit.occt.operations.extrude" id="extrude_face"><value name="Shape"><block type="bitbybit.occt.shapes.face.createFaceFromWires" id="face_from_wires"><value name="Shapes"><block type="lists_create_with" id="wire_list"><mutation items="3"></mutation><value name="ADD0"><block type="bitbybit.occt.shapes.wire.reversedWire" id="reversed_outer"><value name="Shape"><block type="bitbybit.occt.fillets.fillet2d" id="outer_fillet"><value name="Shape"><block type="bitbybit.occt.shapes.wire.createRectangleWire" id="outer_rectangle"><value name="Width"><block type="math_number" id="outer_width"><field name="NUM">23</field></block></value><value name="Length"><block type="math_number" id="outer_length"><field name="NUM">14</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ" id="outer_center"><value name="X"><block type="math_number" id="outer_center_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="outer_center_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="outer_center_z"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="outer_direction"><value name="X"><block type="math_number" id="outer_dir_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="outer_dir_y"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="outer_dir_z"><field name="NUM">0</field></block></value></block></value></block></value><value name="Radius"><block type="math_number" id="outer_fillet_radius"><field name="NUM">1</field></block></value></block></value></block></value><value name="ADD1"><block type="bitbybit.occt.transforms.translate" id="translate_first_hole"><value name="Shape"><block type="bitbybit.occt.transforms.rotate" id="rotate_inner"><value name="Shape"><block type="bitbybit.occt.fillets.fillet2d" id="inner_fillet"><value name="Shape"><block type="bitbybit.occt.shapes.wire.createRectangleWire" id="inner_rectangle"><value name="Width"><block type="math_number" id="inner_width"><field name="NUM">7</field></block></value><value name="Length"><block type="math_number" id="inner_length"><field name="NUM">7</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ" id="inner_center"><value name="X"><block type="math_number" id="inner_center_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="inner_center_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="inner_center_z"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="inner_direction"><value name="X"><block type="math_number" id="inner_dir_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="inner_dir_y"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="inner_dir_z"><field name="NUM">0</field></block></value></block></value></block></value><value name="Radius"><block type="math_number" id="inner_fillet_radius"><field name="NUM">1</field></block></value></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="rotation_axis"><value name="X"><block type="math_number" id="rot_axis_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="rot_axis_y"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="rot_axis_z"><field name="NUM">0</field></block></value></block></value><value name="Angle"><block type="math_number" id="rotation_angle"><field name="NUM">45</field></block></value></block></value><value name="Translation"><block type="bitbybit.vector.vectorXYZ" id="first_hole_translation"><value name="X"><block type="math_number" id="first_hole_x"><field name="NUM">5</field></block></value><value name="Y"><block type="math_number" id="first_hole_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="first_hole_z"><field name="NUM">0</field></block></value></block></value></block></value><value name="ADD2"><block type="bitbybit.occt.transforms.translate" id="translate_second_hole"><value name="Shape"><block type="bitbybit.occt.transforms.rotate" id="rotate_inner_2"><value name="Shape"><block type="bitbybit.occt.fillets.fillet2d" id="inner_fillet_2"><value name="Shape"><block type="bitbybit.occt.shapes.wire.createRectangleWire" id="inner_rectangle_2"><value name="Width"><block type="math_number" id="inner_width_2"><field name="NUM">7</field></block></value><value name="Length"><block type="math_number" id="inner_length_2"><field name="NUM">7</field></block></value><value name="Center"><block type="bitbybit.point.pointXYZ" id="inner_center_2"><value name="X"><block type="math_number" id="inner_center_x_2"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="inner_center_y_2"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="inner_center_z_2"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="inner_direction_2"><value name="X"><block type="math_number" id="inner_dir_x_2"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="inner_dir_y_2"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="inner_dir_z_2"><field name="NUM">0</field></block></value></block></value></block></value><value name="Radius"><block type="math_number" id="inner_fillet_radius_2"><field name="NUM">1</field></block></value></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="rotation_axis_2"><value name="X"><block type="math_number" id="rot_axis_x_2"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="rot_axis_y_2"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="rot_axis_z_2"><field name="NUM">0</field></block></value></block></value><value name="Angle"><block type="math_number" id="rotation_angle_2"><field name="NUM">45</field></block></value></block></value><value name="Translation"><block type="bitbybit.vector.vectorXYZ" id="second_hole_translation"><value name="X"><block type="math_number" id="second_hole_x"><field name="NUM">-5</field></block></value><value name="Y"><block type="math_number" id="second_hole_y"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="second_hole_z"><field name="NUM">0</field></block></value></block></value></block></value></block></value><value name="Planar"><block type="logic_boolean" id="planar_flag"><field name="BOOL">TRUE</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="extrude_direction"><value name="X"><block type="math_number" id="extrude_x"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="extrude_y"><field name="NUM">2</field></block></value><value name="Z"><block type="math_number" id="extrude_z"><field name="NUM">0</field></block></value></block></value></block></value></block></xml>
// Import required DTOs for creating complex shapes with multiple holes
const { RectangleDto, FilletDto, FaceFromWiresDto, ExtrudeDto } = Bit.Inputs.OCCT;
// Import type definitions for type safety
type TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;
type TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;
// Get access to OCCT modules
const { wire, face } = bitbybit.occt.shapes;
const { fillets, operations, transforms } = bitbybit.occt;
// Define the main function to create a solid with multiple holes
const start = async () => {
// Create the outer rectangular wire (larger this time)
const outerWireOptions = new RectangleDto();
outerWireOptions.width = 23;
outerWireOptions.length = 14;
outerWireOptions.center = [0, 0, 0];
outerWireOptions.direction = [0, 1, 0];
const outerWire = await wire.createRectangleWire(outerWireOptions);
// Apply fillet to the outer wire
const outerFilletOptions = new FilletDto<TopoDSWirePointer>();
outerFilletOptions.radius = 1;
outerFilletOptions.shape = outerWire;
const filletedOuterWire = await fillets.fillet2d(outerFilletOptions);
// Reverse the outer wire (optimization - reverse once instead of multiple inner wires)
const reversedOuterWire = await wire.reversedWire({ shape: filletedOuterWire });
// Create the base inner rectangular wire
const innerWireOptions = new RectangleDto();
innerWireOptions.width = 7;
innerWireOptions.length = 7;
innerWireOptions.center = [0, 0, 0];
innerWireOptions.direction = [0, 1, 0];
const innerWire = await wire.createRectangleWire(innerWireOptions);
// Apply fillet to the inner wire
const innerFilletOptions = new FilletDto<TopoDSWirePointer>();
innerFilletOptions.radius = 1;
innerFilletOptions.shape = innerWire;
const filletedInnerWire = await fillets.fillet2d(innerFilletOptions);
// Rotate the inner wire for visual interest
const rotatedInnerWire = await transforms.rotate({
shape: filletedInnerWire,
axis: [0, 1, 0],
angle: 45
});
// Create first hole by translating the rotated wire to the right
const firstHole = await transforms.translate({
shape: rotatedInnerWire,
translation: [5, 0, 0]
});
// Create second hole by translating the rotated wire to the left
const secondHole = await transforms.translate({
shape: rotatedInnerWire,
translation: [-5, 0, 0]
});
// Create a face from the outer boundary and multiple holes
const faceOptions = new FaceFromWiresDto<TopoDSWirePointer>();
faceOptions.shapes = [reversedOuterWire, firstHole, secondHole];
faceOptions.planar = true;
const faceWithHoles = await face.createFaceFromWires(faceOptions);
// Extrude the face to create a 3D solid with multiple holes
const extrudeOptions = new ExtrudeDto<TopoDSFacePointer>();
extrudeOptions.shape = faceWithHoles;
extrudeOptions.direction = [0, 2, 0];
const solidWithHoles = await operations.extrude(extrudeOptions);
// Draw the resulting 3D solid with multiple holes
bitbybit.draw.drawAnyAsync({
entity: solidWithHoles
});
}
// Execute the function
start();
Practical Applications
The techniques you've learned in this tutorial have wide-ranging applications across many industries and design disciplines:
Manufacturing and Engineering Creating hollow shapes is essential for designing mechanical components like mounting brackets, flanges, and structural elements. The ability to add precise holes for bolts, pins, and fasteners is fundamental to mechanical design.
Architecture and Construction From decorative screens and facade elements to structural components with service openings, hollow shapes are everywhere in architecture. These techniques can help you design ventilation grilles, decorative panels, and complex structural elements.
Product Design Whether designing consumer electronics with cooling vents, furniture with weight-reducing cutouts, or decorative objects with intricate patterns, hollow shapes add both functionality and aesthetic appeal.
Prototyping and 3D Printing Creating hollow shapes is crucial for 3D printing applications where you need to reduce material usage, create internal channels, or design parts that require post-processing access through holes.
Key Principles to Remember
The fundamental principle behind all hollow shape creation is proper wire orientation. When combining multiple wires to create a face, the system needs to understand which areas should be solid and which should be empty. This is achieved through wire direction and the reversal operation.
Remember that you can extend these basic techniques to create more complex geometries. The same principles apply whether you're creating simple through-holes or complex multi-level hollow structures. The key is understanding how to properly orient and combine your wire boundaries.
As you become more comfortable with these techniques, you'll find that creating hollow shapes becomes an intuitive part of your 3D modeling workflow, opening up new possibilities for both functional and artistic designs.



