Frost Flower
❄️ Create a delicate 3D printable snowflake using interpolated curves and 12-fold rotational symmetry! This tutorial demonstrates how smooth B-spline curves and offset operations can create organic, nature-inspired shapes.
The algorithm:
- Define branch points - a simple zigzag pattern for one branch
- Interpolate a smooth curve - creates flowing organic lines
- Rotate 180° the branch to create bilateral symmetry
- Combine wires into a single branch pair
- Create outer and inner offsets - hollow structure for the branch
- Create face from wires - outer and reversed inner wire form a hollow face
- Rotate copies at 30° intervals (0°, 30°, 60°, 90°, 120°, 150°) for 12-fold symmetry
- Boolean union to merge all faces into one shape
- Extrude the unified face to create a 3D solid
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"10790118cdf49ef5": {
"id": "10790118cdf49ef5",
"name": "bitbybit.lists.createList",
"customName": "branch points",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "b2c3d4e5f6a7b8c9",
"output": "result",
"data": {}
},
{
"node": "c3d4e5f6a7b8c9d0",
"output": "result",
"data": {}
},
{
"node": "d4e5f6a7b8c9d0e1",
"output": "result",
"data": {}
},
{
"node": "e5f6a7b8c9d0e1f2",
"output": "result",
"data": {}
},
{
"node": "f6a7b8c9d0e1f2a3",
"output": "result",
"data": {}
}
]
}
},
"position": [
-19.189718877162463,
-112.3488571445287
]
},
"b2c3d4e5f6a7b8c9": {
"id": "b2c3d4e5f6a7b8c9",
"name": "bitbybit.vector.vectorXYZ",
"customName": "p1 center",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 0
},
"inputs": {},
"position": [
-500,
-829.3909592519012
]
},
"c3d4e5f6a7b8c9d0": {
"id": "c3d4e5f6a7b8c9d0",
"name": "bitbybit.vector.vectorXYZ",
"customName": "p2",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0.3,
"y": 1.5,
"z": 0
},
"inputs": {
"y": {
"connections": [
{
"node": "a7b8c9d0e1f2a3b4",
"output": "result",
"data": {}
}
]
}
},
"position": [
-502.9649600050009,
-432.45784415768435
]
},
"d4e5f6a7b8c9d0e1": {
"id": "d4e5f6a7b8c9d0e1",
"name": "bitbybit.vector.vectorXYZ",
"customName": "p3",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": -0.2,
"y": 2.5,
"z": 0
},
"inputs": {
"y": {
"connections": [
{
"node": "b8c9d0e1f2a3b4c5",
"output": "result",
"data": {}
}
]
}
},
"position": [
-500.3648459590738,
-94.84724933772955
]
},
"e5f6a7b8c9d0e1f2": {
"id": "e5f6a7b8c9d0e1f2",
"name": "bitbybit.vector.vectorXYZ",
"customName": "p4",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0.4,
"y": 3.5,
"z": 0
},
"inputs": {
"y": {
"connections": [
{
"node": "c9d0e1f2a3b4c5d6",
"output": "result",
"data": {}
}
]
}
},
"position": [
-499.33146676313015,
241.20028778349584
]
},
"f6a7b8c9d0e1f2a3": {
"id": "f6a7b8c9d0e1f2a3",
"name": "bitbybit.vector.vectorXYZ",
"customName": "p5 tip",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 4,
"z": 0
},
"inputs": {
"y": {
"connections": [
{
"node": "d0e1f2a3b4c5d6e7",
"output": "result",
"data": {}
}
]
}
},
"position": [
-501.04900375684275,
583.0171073307207
]
},
"a7b8c9d0e1f2a3b4": {
"id": "a7b8c9d0e1f2a3b4",
"name": "bitbybit.math.twoNrOperation",
"customName": "scale p2",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"first": 1.5,
"second": 1,
"operation": "multiply"
},
"inputs": {
"second": {
"connections": [
{
"node": "1e850d8dc20caff8",
"output": "result",
"data": {}
}
]
}
},
"position": [
-900.6044812680875,
-394.05341251949295
]
},
"b8c9d0e1f2a3b4c5": {
"id": "b8c9d0e1f2a3b4c5",
"name": "bitbybit.math.twoNrOperation",
"customName": "scale p3",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"first": 2.5,
"second": 1,
"operation": "multiply"
},
"inputs": {
"second": {
"connections": [
{
"node": "1e850d8dc20caff8",
"output": "result",
"data": {}
}
]
}
},
"position": [
-901.6946134678033,
-57.86819114370999
]
},
"c9d0e1f2a3b4c5d6": {
"id": "c9d0e1f2a3b4c5d6",
"name": "bitbybit.math.twoNrOperation",
"customName": "scale p4",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"first": 3.5,
"second": 1,
"operation": "multiply"
},
"inputs": {
"second": {
"connections": [
{
"node": "1e850d8dc20caff8",
"output": "result",
"data": {}
}
]
}
},
"position": [
-901.7349754193569,
279.76755591216505
]
},
"d0e1f2a3b4c5d6e7": {
"id": "d0e1f2a3b4c5d6e7",
"name": "bitbybit.math.twoNrOperation",
"customName": "scale p5",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"first": 4,
"second": 1,
"operation": "multiply"
},
"inputs": {
"second": {
"connections": [
{
"node": "1e850d8dc20caff8",
"output": "result",
"data": {}
}
]
}
},
"position": [
-904.1493607486843,
622.9298609814933
]
},
"1e850d8dc20caff8": {
"id": "1e850d8dc20caff8",
"name": "bitbybit.math.numberSlider",
"customName": "size",
"data": {
"options": {
"min": 0.5,
"max": 2,
"step": 0.1,
"width": 350,
"updateOnDrag": false
},
"number": 0.8
},
"inputs": {},
"position": [
-1606.0985639130654,
8.059750241166496
]
},
"f2a3b4c5d6e7f8a9": {
"id": "f2a3b4c5d6e7f8a9",
"name": "bitbybit.occt.shapes.wire.interpolatePoints",
"customName": "branch wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"periodic": false,
"tolerance": 0.00001
},
"inputs": {
"points": {
"connections": [
{
"node": "10790118cdf49ef5",
"output": "list",
"data": {}
}
]
}
},
"position": [
350,
-150
]
},
"ffe20eaeb32b3bf3": {
"id": "ffe20eaeb32b3bf3",
"name": "bitbybit.lists.createList",
"customName": "branch pair",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "f2a3b4c5d6e7f8a9",
"output": "result",
"data": {}
},
{
"node": "f36e80a97decb81f",
"output": "result",
"data": {}
}
]
}
},
"position": [
1291.4100259202773,
-283.2006960053717
]
},
"c5d6e7f8a9b0c1d2": {
"id": "c5d6e7f8a9b0c1d2",
"name": "bitbybit.occt.shapes.wire.combineEdgesAndWiresIntoAWire",
"customName": "combine pair",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shapes": {
"connections": [
{
"node": "ffe20eaeb32b3bf3",
"output": "list",
"data": {}
}
]
}
},
"position": [
1668.199732984824,
-318.4404083821864
]
},
"d8e9f0a1b2c3d4e5": {
"id": "d8e9f0a1b2c3d4e5",
"name": "bitbybit.babylon.material.pbrMetallicRoughness.create",
"customName": "ice material",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"name": "Ice Material",
"baseColor": "#aed6f1",
"emissiveColor": "#5dade2",
"metallic": 0.1,
"roughness": 0.3,
"alpha": 1,
"backFaceCulling": false,
"zOffset": 2
},
"inputs": {},
"position": [
6177.793312393963,
658.0492290944441
]
},
"f36e80a97decb81f": {
"id": "f36e80a97decb81f",
"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": 180
},
"inputs": {
"shape": {
"connections": [
{
"node": "f2a3b4c5d6e7f8a9",
"output": "result",
"data": {}
}
]
},
"axis": {
"connections": [
{
"node": "c618b8b468644f69",
"output": "result",
"data": {}
}
]
}
},
"position": [
814.9207572250788,
13.526956147226372
]
},
"c618b8b468644f69": {
"id": "c618b8b468644f69",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 1
},
"inputs": {},
"position": [
354.34131852780666,
247.436177661857
]
},
"aafc1a1cc4f05cc9": {
"id": "aafc1a1cc4f05cc9",
"name": "bitbybit.vector.span",
"customName": "span",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"step": 30,
"min": 0,
"max": 150
},
"inputs": {},
"position": [
3994.6539471789133,
542.1165752364911
]
},
"c4cc647386d496dc": {
"id": "c4cc647386d496dc",
"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": 0
},
"inputs": {
"axis": {
"connections": [
{
"node": "2a6358261ce5c0ec",
"output": "result",
"data": {}
}
]
},
"angle": {
"connections": [
{
"node": "a96ec7afaa952e7e",
"output": "result",
"data": {}
}
]
},
"shape": {
"connections": [
{
"node": "4fc11aa9465e7979",
"output": "result",
"data": {}
}
]
}
},
"position": [
4835.030692763062,
77.89488642183412
]
},
"2a6358261ce5c0ec": {
"id": "2a6358261ce5c0ec",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 1
},
"inputs": {},
"position": [
3992.159011248188,
203.70760024122325
]
},
"a96ec7afaa952e7e": {
"id": "a96ec7afaa952e7e",
"name": "bitbybit.lists.flatten",
"customName": "flatten",
"data": {
"nrLevels": 1
},
"inputs": {
"list": {
"connections": [
{
"node": "aafc1a1cc4f05cc9",
"output": "result",
"data": {}
}
]
}
},
"position": [
4380.026815245925,
577.7207099160814
]
},
"6c9ae97ce51ddd9a": {
"id": "6c9ae97ce51ddd9a",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "c4cc647386d496dc",
"output": "result",
"data": {}
}
]
}
},
"position": [
5267.154058434832,
118.35312435492582
]
},
"e59d5309cfd324a3": {
"id": "e59d5309cfd324a3",
"name": "bitbybit.occt.operations.offset",
"customName": "offset",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"distance": 0.2,
"tolerance": 0.1
},
"inputs": {
"shape": {
"connections": [
{
"node": "c5d6e7f8a9b0c1d2",
"output": "result",
"data": {}
}
]
}
},
"position": [
2233.846702112692,
-106.24786077242689
]
},
"9e6506e07218e5a3": {
"id": "9e6506e07218e5a3",
"name": "bitbybit.occt.booleans.union",
"customName": "union",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"keepEdges": false
},
"inputs": {
"shapes": {
"connections": [
{
"node": "6c9ae97ce51ddd9a",
"output": "list",
"data": {}
}
]
}
},
"position": [
5643.297343841302,
78.95068988656377
]
},
"07387c14a53ebb24": {
"id": "07387c14a53ebb24",
"name": "bitbybit.occt.operations.offset",
"customName": "offset",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"distance": 0.4,
"tolerance": 0.1
},
"inputs": {
"shape": {
"connections": [
{
"node": "c5d6e7f8a9b0c1d2",
"output": "result",
"data": {}
}
]
}
},
"position": [
2234.3856310618603,
-562.0330783760298
]
},
"4fc11aa9465e7979": {
"id": "4fc11aa9465e7979",
"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": "5de1b098fce6dfcf",
"output": "list",
"data": {}
}
]
}
},
"position": [
3995.4108912768634,
-227.03899956655695
]
},
"5b529ffd1e568e39": {
"id": "5b529ffd1e568e39",
"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": "e59d5309cfd324a3",
"output": "result",
"data": {}
}
]
}
},
"position": [
2780.21747433819,
-111.00950762810912
]
},
"5de1b098fce6dfcf": {
"id": "5de1b098fce6dfcf",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "07387c14a53ebb24",
"output": "result",
"data": {}
},
{
"node": "5b529ffd1e568e39",
"output": "result",
"data": {}
}
]
}
},
"position": [
3414.7858123370843,
-198.74496479338936
]
},
"6230b98666b9d6aa": {
"id": "6230b98666b9d6aa",
"name": "bitbybit.occt.operations.extrude",
"customName": "extrude",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"direction": [
0,
1,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "9e6506e07218e5a3",
"output": "result",
"data": {}
}
]
},
"direction": {
"connections": [
{
"node": "c428418880b0f286",
"output": "result",
"data": {}
}
]
}
},
"position": [
6562.383745239804,
310.6838197635185
]
},
"c428418880b0f286": {
"id": "c428418880b0f286",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 0.2
},
"inputs": {},
"position": [
5641.977218824047,
399.4563808447789
]
},
"d70c1db8bd4f5bda": {
"id": "d70c1db8bd4f5bda",
"name": "bitbybit.draw.drawAnyAsync",
"customName": "draw any async",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"entity": {
"connections": [
{
"node": "6230b98666b9d6aa",
"output": "result",
"data": {}
}
]
},
"options": {
"connections": [
{
"node": "f15d2dae484d0420",
"output": "result",
"data": {}
}
]
}
},
"position": [
7018.838053887316,
449.91706231399286
]
},
"f15d2dae484d0420": {
"id": "f15d2dae484d0420",
"name": "bitbybit.draw.optionsOcctShapeMaterial",
"customName": "options occt shape material",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"precision": 0.01,
"drawEdges": false,
"edgeColour": "#ffffff",
"edgeWidth": 2
},
"inputs": {
"faceMaterial": {
"connections": [
{
"node": "d8e9f0a1b2c3d4e5",
"output": "result",
"data": {}
}
]
}
},
"position": [
6565.390708494095,
623.8495899202425
]
},
"3686b8996a912c6e": {
"id": "3686b8996a912c6e",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "d70c1db8bd4f5bda",
"output": "result",
"data": {}
}
]
}
},
"position": [
7435.644536917809,
485.5881162747557
]
},
"68f7335e5351419f": {
"id": "68f7335e5351419f",
"name": "bitbybit.advanced.navigation.zoomOn",
"customName": "zoom on",
"async": true,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"meshes": [],
"includeChildren": true,
"animationSpeed": 0.8,
"offset": 0,
"doNotUpdateMaxZ": true
},
"inputs": {
"meshes": {
"connections": [
{
"node": "3686b8996a912c6e",
"output": "list",
"data": {}
}
]
}
},
"position": [
7819.690818307023,
444.7169923460303
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><variables><variable id="size">size</variable><variable id="branchWire">branchWire</variable><variable id="combinedWire">combinedWire</variable><variable id="outerWire">outerWire</variable><variable id="innerWire">innerWire</variable><variable id="hollowFace">hollowFace</variable><variable id="snowflake">snowflake</variable></variables><block type="variables_set" id="set_size" x="-600" y="-200"><field name="VAR" id="size">size</field><value name="VALUE"><block type="math_number" id="size_val"><field name="NUM">0.8</field></block></value><next><block type="variables_set" id="set_branch"><field name="VAR" id="branchWire">branchWire</field><value name="VALUE"><block type="bitbybit.occt.shapes.wire.interpolatePoints" id="interpolate"><value name="Points"><block type="lists_create_with" id="points_list"><mutation items="5"></mutation><value name="ADD0"><block type="bitbybit.vector.vectorXYZ" id="p1"><value name="X"><block type="math_number" id="0cEo+Ip}9)2yTTrI8P/:"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="zF^84NIO!1$CAG#`$IFj"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="FBYdsseG5oBFi,lC+ES+"><field name="NUM">0</field></block></value></block></value><value name="ADD1"><block type="bitbybit.vector.vectorXYZ" id="p2"><value name="X"><block type="math_number" id="-e*mJcUm_mPJ)RHh+2V}"><field name="NUM">0.3</field></block></value><value name="Y"><block type="math_arithmetic" id="p2_y"><field name="OP">MULTIPLY</field><value name="A"><block type="math_number" id="-U+{:[^%Lmr]|6?}N6e4"><field name="NUM">1.5</field></block></value><value name="B"><block type="variables_get" id="Y^PlHD:{KK8yzM?:XfrB"><field name="VAR" id="size">size</field></block></value></block></value><value name="Z"><block type="math_number" id="gLE7qQ)5}X6l0[l@ROzz"><field name="NUM">0</field></block></value></block></value><value name="ADD2"><block type="bitbybit.vector.vectorXYZ" id="p3"><value name="X"><block type="math_number" id="T5YX=gBwo_/:X=ia/,y,"><field name="NUM">-0.2</field></block></value><value name="Y"><block type="math_arithmetic" id="p3_y"><field name="OP">MULTIPLY</field><value name="A"><block type="math_number" id="I2t{Gg1vQ^+,IcRFE{KE"><field name="NUM">2.5</field></block></value><value name="B"><block type="variables_get" id="7dgM[iwU3]/xSV$f@Q8i"><field name="VAR" id="size">size</field></block></value></block></value><value name="Z"><block type="math_number" id="@w[nzi8lat*V]=|d(vz-"><field name="NUM">0</field></block></value></block></value><value name="ADD3"><block type="bitbybit.vector.vectorXYZ" id="p4"><value name="X"><block type="math_number" id="mBsw#,C[0Y2ge+IAVk$#"><field name="NUM">0.4</field></block></value><value name="Y"><block type="math_arithmetic" id="p4_y"><field name="OP">MULTIPLY</field><value name="A"><block type="math_number" id="9b{Hl=OK7Uvm{xE=4T[p"><field name="NUM">3.5</field></block></value><value name="B"><block type="variables_get" id="hV/vmu8F8=l9ORA@k)^y"><field name="VAR" id="size">size</field></block></value></block></value><value name="Z"><block type="math_number" id="cy2f]su`G[gV9R]zf!;u"><field name="NUM">0</field></block></value></block></value><value name="ADD4"><block type="bitbybit.vector.vectorXYZ" id="p5"><value name="X"><block type="math_number" id="{5NGl|(0Y)F(h+}Ba2^N"><field name="NUM">0</field></block></value><value name="Y"><block type="math_arithmetic" id="p5_y"><field name="OP">MULTIPLY</field><value name="A"><block type="math_number" id="O)1%zkM@Qv!{)xdjH$kL"><field name="NUM">4</field></block></value><value name="B"><block type="variables_get" id="(T7iB;w7k5A}%r2=Z^4N"><field name="VAR" id="size">size</field></block></value></block></value><value name="Z"><block type="math_number" id="5BjgFx1Al$KEWZX29394"><field name="NUM">0</field></block></value></block></value></block></value><value name="Periodic"><block type="logic_boolean" id="P@fP?Q#s|2d4p+DB*Ud:"><field name="BOOL">FALSE</field></block></value><value name="Tolerance"><block type="math_number" id="3x0/Z:_{eEAUXjJcN117"><field name="NUM">0.00001</field></block></value></block></value><next><block type="variables_set" id="set_combined"><field name="VAR" id="combinedWire">combinedWire</field><value name="VALUE"><block type="bitbybit.occt.shapes.wire.combineEdgesAndWiresIntoAWire" id="combine"><value name="Shapes"><block type="lists_create_with" id="wire_pair"><mutation items="2"></mutation><value name="ADD0"><block type="variables_get" id="}oi(y3uSd]q5pG=q)RZ`"><field name="VAR" id="branchWire">branchWire</field></block></value><value name="ADD1"><block type="bitbybit.occt.transforms.rotate" id="rotate_180"><value name="Shape"><block type="variables_get" id=";vQp7=iJ}R{H9vSw{,;z"><field name="VAR" id="branchWire">branchWire</field></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="z_axis"><value name="X"><block type="math_number" id="x4|RHMjRr})HHAwJKTNK"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="2`MP3Eqvk$+d-]C)%4ig"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="::L+%r=8`lZ|}~)dwO2)"><field name="NUM">1</field></block></value></block></value><value name="Angle"><block type="math_number" id="X}q*VsNQ$l+P^o^Qoo-0"><field name="NUM">180</field></block></value></block></value></block></value></block></value><next><block type="variables_set" id="set_outer"><field name="VAR" id="outerWire">outerWire</field><value name="VALUE"><block type="bitbybit.occt.operations.offset" id="offset_outer"><value name="Shape"><block type="variables_get" id="4|)BVRZ,Q;$Uef!my%%E"><field name="VAR" id="combinedWire">combinedWire</field></block></value><value name="Distance"><block type="math_number" id="FR!J)y@V).Kx[5|9d`QM"><field name="NUM">0.4</field></block></value><value name="Tolerance"><block type="math_number" id="EdH1/7{|yIN;7_nxsTaF"><field name="NUM">0.1</field></block></value></block></value><next><block type="variables_set" id="set_inner"><field name="VAR" id="innerWire">innerWire</field><value name="VALUE"><block type="bitbybit.occt.shapes.wire.reversedWire" id="reverse"><value name="Shape"><block type="bitbybit.occt.operations.offset" id="offset_inner"><value name="Shape"><block type="variables_get" id="~+,3ZmIe!k_Amv*U}0f!"><field name="VAR" id="combinedWire">combinedWire</field></block></value><value name="Distance"><block type="math_number" id="gFyJ/Rhs6bgBG?Bmuneh"><field name="NUM">0.2</field></block></value><value name="Tolerance"><block type="math_number" id="#?!IfP!Tv|v-Q`urL5]:"><field name="NUM">0.1</field></block></value></block></value></block></value><next><block type="variables_set" id="set_face"><field name="VAR" id="hollowFace">hollowFace</field><value name="VALUE"><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="variables_get" id="k1-~yX:vLsCWPdD1dpmp"><field name="VAR" id="outerWire">outerWire</field></block></value><value name="ADD1"><block type="variables_get" id="7%d9qyMQp@Ql$f|4L~3j"><field name="VAR" id="innerWire">innerWire</field></block></value></block></value><value name="Planar"><block type="logic_boolean" id="*!r]]?`Sgk9w|~E-J9:1"><field name="BOOL">TRUE</field></block></value></block></value><next><block type="variables_set" id="set_snowflake"><field name="VAR" id="snowflake">snowflake</field><value name="VALUE"><block type="bitbybit.occt.booleans.union" id="union"><value name="Shapes"><block type="lists_create_with" id="rotated_faces"><mutation items="6"></mutation><value name="ADD0"><block type="variables_get" id="?Zp]Y6qF++qot]$,9b$}"><field name="VAR" id="hollowFace">hollowFace</field></block></value><value name="ADD1"><block type="bitbybit.occt.transforms.rotate" id="rot_30"><value name="Shape"><block type="variables_get" id="gHrs~ZsFA1WC.)e}oNIZ"><field name="VAR" id="hollowFace">hollowFace</field></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="JWL3~_T=pD!9FE`Kz|4S"><value name="X"><block type="math_number" id="mlo?T~xoNJBE~:DoO?Y["><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="Pgyluls`M_fPP(zf(B%G"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="CXC,OoM[j,jGzU3Po9Aw"><field name="NUM">1</field></block></value></block></value><value name="Angle"><block type="math_number" id="Q0@zl.ErTsr1C8q+WF|9"><field name="NUM">30</field></block></value></block></value><value name="ADD2"><block type="bitbybit.occt.transforms.rotate" id="rot_60"><value name="Shape"><block type="variables_get" id=".TrI/~_X@EEE}xJau`cJ"><field name="VAR" id="hollowFace">hollowFace</field></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="7m!1ZlQL,51ST5{m9p^v"><value name="X"><block type="math_number" id="4!ZK%J}0:xr*g/I9JF*q"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="zc)g(;:Yhf(~e~.#N=M0"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="+3*Jf7!ksPh+JjMB)]Ta"><field name="NUM">1</field></block></value></block></value><value name="Angle"><block type="math_number" id="@;_^y$QOn=H8hs^=N;^P"><field name="NUM">60</field></block></value></block></value><value name="ADD3"><block type="bitbybit.occt.transforms.rotate" id="rot_90"><value name="Shape"><block type="variables_get" id="DVyc/S`Kj,3o1T+s~Mjl"><field name="VAR" id="hollowFace">hollowFace</field></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="Hl;fh.qN?*ahc3jMk%eU"><value name="X"><block type="math_number" id="Jz!dHG]5t$E`c~=*oOEK"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="cyZGi{(5en+W.(vVyBFg"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="qBf?%da(ZOIM=+l8HR^r"><field name="NUM">1</field></block></value></block></value><value name="Angle"><block type="math_number" id="%}]}CQKoh;M*9a1do{.^"><field name="NUM">90</field></block></value></block></value><value name="ADD4"><block type="bitbybit.occt.transforms.rotate" id="rot_120"><value name="Shape"><block type="variables_get" id="T@#70c*,3AhmS,eGh%!4"><field name="VAR" id="hollowFace">hollowFace</field></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id=";i;I|S`({:klK^.m%@E]"><value name="X"><block type="math_number" id=":pG7y|2jLIvZ$!+lzY]t"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="wAhf5N9Iely4AQ61T:ym"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="sX{t7K8KsrSRHs/$^!ra"><field name="NUM">1</field></block></value></block></value><value name="Angle"><block type="math_number" id="4tG$qH,sdr%sF*[p4dK="><field name="NUM">120</field></block></value></block></value><value name="ADD5"><block type="bitbybit.occt.transforms.rotate" id="rot_150"><value name="Shape"><block type="variables_get" id="qow;fCo|}A]XU7fjY[w5"><field name="VAR" id="hollowFace">hollowFace</field></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="b16qfaz::v0%}^jlm:],"><value name="X"><block type="math_number" id="|pU0.fnx?lv7eJQ0~52s"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="FT4Gpfm^wXV)/IGZf%%M"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="d!dLoUGpwR0GGxGB,XOf"><field name="NUM">1</field></block></value></block></value><value name="Angle"><block type="math_number" id="cQaCa.^Yi_is+_68*_iL"><field name="NUM">150</field></block></value></block></value></block></value><value name="KeepEdges"><block type="logic_boolean" id="A[U4i$0+e]3VE1Rh-w36"><field name="BOOL">FALSE</field></block></value></block></value><next><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="draw"><value name="Entity"><block type="bitbybit.occt.operations.extrude" id="extrude"><value name="Shape"><block type="variables_get" id="`9mN~_0U7T*jkd)4.dz$"><field name="VAR" id="snowflake">snowflake</field></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="extrude_dir"><value name="X"><block type="math_number" id="+UB:o+Ty)N.:nf6jK^Rk"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="[_3e(hj8ca^Rah@vQPvN"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="VTe_uufMt!=R%N)b@J5B"><field name="NUM">0.2</field></block></value></block></value></block></value><value name="Options"><block type="bitbybit.draw.optionsOcctShapeMaterial" id="draw_options"><value name="Precision"><block type="math_number" id="g^D*5^x#4a]P-?!u@pFW"><field name="NUM">0.01</field></block></value><value name="FaceMaterial"><block type="bitbybit.babylon.material.pbrMetallicRoughness.create" id="ice_material"><value name="Name"><block type="text" id="+v7nt)]}1lyiDr+rFdwZ"><field name="TEXT">Ice Material</field></block></value><value name="BaseColor"><block type="colour_picker" id="/Pt]Pb(DpuzU/u|PQL6|"><field name="COLOUR">#aed6f1</field></block></value><value name="EmissiveColor"><block type="colour_picker" id="8h)h*LCSIjtxRVBR#Ag3"><field name="COLOUR">#5dade2</field></block></value><value name="Metallic"><block type="math_number" id="y;~0`JPiOgxO6?HLD;~+"><field name="NUM">0.1</field></block></value><value name="Roughness"><block type="math_number" id=")ViEvjRnFl#f{+Izmjd="><field name="NUM">0.3</field></block></value><value name="Alpha"><block type="math_number" id="BrSJzmx2G?n#=Jb#6o:l"><field name="NUM">1</field></block></value><value name="BackFaceCulling"><block type="logic_boolean" id="+@vIBti[dZ*,O6R18dk,"><field name="BOOL">FALSE</field></block></value><value name="ZOffset"><block type="math_number" id="altH)_G9kM:B/w]vE:6."><field name="NUM">2</field></block></value></block></value><value name="DrawEdges"><block type="logic_boolean" id="Igt+biKegq]1!cji_9BQ"><field name="BOOL">FALSE</field></block></value><value name="EdgeColour"><block type="colour_picker" id="WO?ex}eW%*UKHBpr%J2%"><field name="COLOUR">#ffffff</field></block></value><value name="EdgeWidth"><block type="math_number" id="sHU]|*P.aMuXQRH:+QO5"><field name="NUM">2</field></block></value></block></value></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></xml>
const { InterpolationDto, RotateDto, ShapesDto, OffsetDto, FaceFromWiresDto, ExtrudeDto, UnionDto, ShapeDto } = Bit.Inputs.OCCT;
const { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;
const { DrawOcctShapeOptions } = Bit.Inputs.Draw;
const { ZoomOnDto } = Bit.Advanced.Navigation;
type Point3 = Bit.Inputs.Base.Point3;
type TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;
type TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;
type TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;
const { wire, face } = bitbybit.occt.shapes;
const { operations, transforms, booleans } = bitbybit.occt;
const start = async () => {
const size = 0.8;
const branchPoints: Point3[] = [
[0, 0, 0],
[0.3, 1.5 * size, 0],
[-0.2, 2.5 * size, 0],
[0.4, 3.5 * size, 0],
[0, 4 * size, 0],
];
const interpolation = new InterpolationDto();
interpolation.points = branchPoints;
interpolation.periodic = false;
interpolation.tolerance = 0.00001;
const branchWire = await wire.interpolatePoints(interpolation);
const rotate180 = new RotateDto<TopoDSWirePointer>();
rotate180.shape = branchWire;
rotate180.axis = [0, 0, 1];
rotate180.angle = 180;
const branchWireRotated = await transforms.rotate(rotate180);
const combine = new ShapesDto<TopoDSWirePointer>();
combine.shapes = [branchWire, branchWireRotated];
const combinedWire = await wire.combineEdgesAndWiresIntoAWire(combine);
const offsetOuter = new OffsetDto<TopoDSShapePointer, TopoDSFacePointer>();
offsetOuter.shape = combinedWire;
offsetOuter.distance = 0.4;
offsetOuter.tolerance = 0.1;
const outerWire = (await operations.offset(offsetOuter)) as unknown as TopoDSWirePointer;
const offsetInner = new OffsetDto<TopoDSShapePointer, TopoDSFacePointer>();
offsetInner.shape = combinedWire;
offsetInner.distance = 0.2;
offsetInner.tolerance = 0.1;
const innerWireRaw = (await operations.offset(offsetInner)) as unknown as TopoDSWirePointer;
const reverse = new ShapeDto<TopoDSWirePointer>();
reverse.shape = innerWireRaw;
const innerWire = await wire.reversedWire(reverse);
const faceFromWires = new FaceFromWiresDto<TopoDSWirePointer>();
faceFromWires.shapes = [outerWire, innerWire];
faceFromWires.planar = true;
const hollowFace = await face.createFaceFromWires(faceFromWires);
const angles = [0, 30, 60, 90, 120, 150];
const rotatedFaces: TopoDSShapePointer[] = [];
for (const angle of angles) {
if (angle === 0) {
rotatedFaces.push(hollowFace);
continue;
}
const rotate = new RotateDto<TopoDSFacePointer>();
rotate.shape = hollowFace;
rotate.axis = [0, 0, 1];
rotate.angle = angle;
rotatedFaces.push(await transforms.rotate(rotate));
}
const union = new UnionDto<TopoDSShapePointer>();
union.shapes = rotatedFaces;
union.keepEdges = false;
const snowflakeFace = await booleans.union(union);
const extrude = new ExtrudeDto<TopoDSShapePointer>();
extrude.shape = snowflakeFace;
extrude.direction = [0, 0, 0.2];
const snowflakeSolid = await operations.extrude(extrude);
const materialOptions = new PBRMetallicRoughnessDto();
materialOptions.name = 'Ice Material';
materialOptions.baseColor = '#aed6f1';
materialOptions.emissiveColor = '#5dade2';
materialOptions.metallic = 0.1;
materialOptions.roughness = 0.3;
materialOptions.alpha = 1;
materialOptions.backFaceCulling = false;
materialOptions.zOffset = 2;
const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);
const drawOptions = new DrawOcctShapeOptions();
drawOptions.precision = 0.01;
drawOptions.drawEdges = false;
drawOptions.edgeColour = '#ffffff';
drawOptions.edgeWidth = 2;
drawOptions.faceMaterial = material;
const starMesh = await bitbybit.draw.drawAnyAsync({
entity: snowflakeSolid,
options: drawOptions,
});
const options = new ZoomOnDto();
options.meshes = [starMesh];
bitbybit.advanced.navigation.zoomOn(options);
};
start();
How It Works
Branch Design
Each branch is defined by 5 points creating a gentle zigzag pattern:
- Center point at origin (0, 0, 0)
- Alternating left/right offsets as Y increases (0.3, -0.2, 0.4...)
- Tip point centered for a clean finish (0, 4*size, 0)
The interpolatePoints operation creates a smooth B-spline curve through these points.
Bilateral Symmetry
The branch wire is rotated 180° around the Z-axis, creating a mirrored copy. Both wires are then combined into a single wire, forming the classic "double-branched" snowflake arm look.
Creating Hollow Structure
Instead of solid geometry, this creates a delicate hollow branch:
- Outer offset: The combined wire is offset outward by 0.4 units
- Inner offset: The combined wire is offset outward by 0.2 units, then reversed (direction flipped)
- Face from wires: The outer and reversed inner wire create a face with a hollow center - like a ribbon
12-Fold Symmetry
The hollow face is rotated at 30° intervals (0°, 30°, 60°, 90°, 120°, 150°), creating 6 rotated copies plus the original. This creates a complex 12-branch pattern when the bilateral branches overlap, giving the appearance of intricate snowflake geometry.
Final 3D Solid
- Boolean Union: All rotated faces are merged into a single flat 2D shape
- Extrude: The unified shape is extruded 0.2 units along the Z-axis to create the final 3D printable solid
Customization ❄️
- Size slider: Scale the entire snowflake (controls the Y coordinates of branch points)
- Offset distances: Adjust the 0.4 and 0.2 values to change the branch thickness
- Edit point positions: Modify the zigzag pattern for different branch styles
- Rotation angles: Change the 30° step to create different symmetries (try 60° for 6-fold, or 45° for 8-fold)
3D Printing Tips
- The offset technique creates thin, elegant branches - great for decorative ornaments
- The boolean union creates a single manifold mesh - no intersecting geometry!
- 0.1-0.15mm layer height for fine detail
- 10-15% infill (it's mostly thin walls anyway)
- White or translucent filament for icy look
- Consider adding a small cylinder at the center for a hanging hole
Happy holidays! ❄️



