Gem Star
Create a gem-like star decoration by lofting a star-shaped wire profile to a single point, then mirroring it to form a complete solid. This technique produces a faceted ornament with clean geometric edges perfect for 3D printing.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"5e57eae1657aea31": {
"id": "5e57eae1657aea31",
"name": "bitbybit.occt.shapes.wire.createStarWire",
"customName": "star wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
],
"numRays": 7,
"outerRadius": 2,
"innerRadius": 1,
"offsetOuterEdges": 0,
"half": false
},
"inputs": {
"numRays": {
"connections": [
{
"node": "ec4ccdd7a4e0fffd",
"output": "result",
"data": {}
}
]
},
"innerRadius": {
"connections": [
{
"node": "50d75ef638874b78",
"output": "result",
"data": {}
}
]
},
"outerRadius": {
"connections": [
{
"node": "0e256ecfcb272290",
"output": "result",
"data": {}
}
]
}
},
"position": [
303.8359375,
257.4296875
]
},
"2863fd8e5468e5c2": {
"id": "2863fd8e5468e5c2",
"name": "bitbybit.occt.operations.loftAdvanced",
"customName": "loft advanced",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"makeSolid": false,
"closed": false,
"periodic": false,
"straight": false,
"nrPeriodicSections": 10,
"useSmoothing": false,
"maxUDegree": 3,
"tolerance": 1e-7,
"parType": "approxCentripetal"
},
"inputs": {
"startVertex": {
"connections": [
{
"node": "10f4d392e548741d",
"output": "result",
"data": {}
}
]
},
"shapes": {
"connections": [
{
"node": "ef6935d712da501e",
"output": "list",
"data": {}
}
]
}
},
"position": [
1067.7734375,
258.98046875
]
},
"10f4d392e548741d": {
"id": "10f4d392e548741d",
"name": "bitbybit.point.pointXYZ",
"customName": "point xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 1,
"z": 0
},
"inputs": {
"y": {
"connections": [
{
"node": "7cf863f12527021d",
"output": "result",
"data": {}
}
]
}
},
"position": [
341.390625,
836.90234375
]
},
"ef6935d712da501e": {
"id": "ef6935d712da501e",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "5e57eae1657aea31",
"output": "result",
"data": {}
}
]
}
},
"position": [
682.6916365702044,
295.8792190122451
]
},
"df3f4fdf5f57a985": {
"id": "df3f4fdf5f57a985",
"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": [
1073.6113448640426,
969.3075670949257
]
},
"4a4e86156bf19c27": {
"id": "4a4e86156bf19c27",
"name": "bitbybit.occt.transforms.mirrorAlongNormal",
"customName": "mirror along normal",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"origin": [
0,
0,
0
],
"normal": [
0,
0,
1
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "2863fd8e5468e5c2",
"output": "result",
"data": {}
}
]
},
"normal": {
"connections": [
{
"node": "df3f4fdf5f57a985",
"output": "result",
"data": {}
}
]
}
},
"position": [
1591.38575693654,
515.3982413387316
]
},
"7cf863f12527021d": {
"id": "7cf863f12527021d",
"name": "bitbybit.math.numberSlider",
"customName": "height",
"data": {
"options": {
"min": 0.3,
"max": 2,
"step": 0.1,
"width": 350,
"updateOnDrag": false
},
"number": 0.7
},
"inputs": {},
"position": [
-865.9706756696274,
904.9092012843769
]
},
"ec4ccdd7a4e0fffd": {
"id": "ec4ccdd7a4e0fffd",
"name": "bitbybit.math.numberSlider",
"customName": "rays",
"data": {
"options": {
"min": 3,
"max": 10,
"step": 1,
"width": 350,
"updateOnDrag": false
},
"number": 5
},
"inputs": {},
"position": [
-863.9657349173299,
150.89827431051816
]
},
"0e256ecfcb272290": {
"id": "0e256ecfcb272290",
"name": "bitbybit.math.numberSlider",
"customName": "radius",
"data": {
"options": {
"min": 0.5,
"max": 10,
"step": 1,
"width": 350,
"updateOnDrag": false
},
"number": 4.5
},
"inputs": {},
"position": [
-864.0098749123165,
343.3710281531416
]
},
"50d75ef638874b78": {
"id": "50d75ef638874b78",
"name": "bitbybit.math.divide",
"customName": "divide",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"first": 1,
"second": 3
},
"inputs": {
"first": {
"connections": [
{
"node": "0e256ecfcb272290",
"output": "result",
"data": {}
}
]
},
"second": {
"connections": [
{
"node": "4a454ee80f751d90",
"output": "result",
"data": {}
}
]
}
},
"position": [
-274.89306340515157,
482.9178748932348
]
},
"7e41bd1c9b56e1a3": {
"id": "7e41bd1c9b56e1a3",
"name": "bitbybit.occt.shapes.shell.sewFaces",
"customName": "sew faces",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"tolerance": 1e-7
},
"inputs": {
"shapes": {
"connections": [
{
"node": "138540da63234c1f",
"output": "list",
"data": {}
}
]
}
},
"position": [
2416.651211257443,
258.95334004279306
]
},
"138540da63234c1f": {
"id": "138540da63234c1f",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "2863fd8e5468e5c2",
"output": "result",
"data": {}
},
{
"node": "4a4e86156bf19c27",
"output": "result",
"data": {}
}
]
}
},
"position": [
2013.1502071524446,
302.743712041642
]
},
"407fc16fa203ac34": {
"id": "407fc16fa203ac34",
"name": "bitbybit.occt.shapes.solid.fromClosedShell",
"customName": "from closed shell",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "7e41bd1c9b56e1a3",
"output": "result",
"data": {}
}
]
}
},
"position": [
2805.8384443346117,
256.9268367860743
]
},
"e008b38e87affffe": {
"id": "e008b38e87affffe",
"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": "407fc16fa203ac34",
"output": "result",
"data": {}
}
]
},
"options": {
"connections": [
{
"node": "ff2600d7008d41b9",
"output": "result",
"data": {}
}
]
}
},
"position": [
3404.2469368733186,
456.35190509974706
]
},
"ff2600d7008d41b9": {
"id": "ff2600d7008d41b9",
"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": true,
"edgeColour": "#ffffff",
"edgeWidth": 2
},
"inputs": {
"faceMaterial": {
"connections": [
{
"node": "761190716585036e",
"output": "result",
"data": {}
}
]
}
},
"position": [
2919.5846046215524,
751.7293976155988
]
},
"761190716585036e": {
"id": "761190716585036e",
"name": "bitbybit.babylon.material.pbrMetallicRoughness.create",
"customName": "pbr metallic roughness",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"name": "Custom Material",
"baseColor": "#4dffb2",
"emissiveColor": "#000000",
"metallic": 0.8,
"roughness": 0.3,
"alpha": 1,
"backFaceCulling": false,
"zOffset": 2
},
"inputs": {},
"position": [
2507.003296169671,
794.6144429355872
]
},
"403e0edb628a37d0": {
"id": "403e0edb628a37d0",
"name": "bitbybit.babylon.scene.enableSkybox",
"customName": "enable skybox",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"skybox": "city",
"size": 1000,
"blur": 0.1,
"environmentIntensity": 0.7,
"hideSkybox": true
},
"inputs": {},
"position": [
359.13009888578586,
1334.7705653906194
]
},
"c0cc8a34ea594210": {
"id": "c0cc8a34ea594210",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "e008b38e87affffe",
"output": "result",
"data": {}
}
]
}
},
"position": [
3786.373818412958,
494.14064175565136
]
},
"56126f3d0e3841b1": {
"id": "56126f3d0e3841b1",
"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.3,
"doNotUpdateMaxZ": true
},
"inputs": {
"meshes": {
"connections": [
{
"node": "c0cc8a34ea594210",
"output": "list",
"data": {}
}
]
}
},
"position": [
4179.995583299783,
463.1824591469443
]
},
"4a454ee80f751d90": {
"id": "4a454ee80f751d90",
"name": "bitbybit.math.numberSlider",
"customName": "inner radius",
"data": {
"options": {
"min": 1.5,
"max": 4,
"step": 0.1,
"width": 350,
"updateOnDrag": false
},
"number": 3.2
},
"inputs": {},
"position": [
-858.2630928403424,
590.7627222238717
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><variables><variable id="rays">rays</variable><variable id="outerR">outerRadius</variable><variable id="div">innerRadiusDivisor</variable><variable id="h">height</variable><variable id="innerR">innerRadius</variable><variable id="wire">starWire</variable><variable id="pt">topPoint</variable><variable id="loft">loftShape</variable><variable id="mir">mirroredShape</variable><variable id="list">shapeList</variable><variable id="sewn">sewnShell</variable><variable id="solid">solidShape</variable><variable id="mesh">drawnMesh</variable></variables><block type="bitbybit.babylon.scene.enableSkybox" id="sky"><value name="Skybox"><block type="bitbybit.babylon.enums.skyboxEnum" id="skyEnum"><field name="bitbybit.babylon.enums.skyboxEnum">'city'</field></block></value><value name="Size"><block type="math_number" id="skySz"><field name="NUM">1000</field></block></value><value name="Blur"><block type="math_number" id="skyBl"><field name="NUM">0.1</field></block></value><value name="EnvironmentIntensity"><block type="math_number" id="skyInt"><field name="NUM">0.7</field></block></value><value name="HideSkybox"><block type="logic_boolean" id="skyHide"><field name="BOOL">TRUE</field></block></value><next><block type="variables_set" id="v1"><field name="VAR" id="rays">rays</field><value name="VALUE"><block type="math_number" id="n1"><field name="NUM">5</field></block></value><next><block type="variables_set" id="v2"><field name="VAR" id="outerR">outerRadius</field><value name="VALUE"><block type="math_number" id="n2"><field name="NUM">4.5</field></block></value><next><block type="variables_set" id="v3"><field name="VAR" id="div">innerRadiusDivisor</field><value name="VALUE"><block type="math_number" id="n3"><field name="NUM">3.2</field></block></value><next><block type="variables_set" id="v4"><field name="VAR" id="h">height</field><value name="VALUE"><block type="math_number" id="n4"><field name="NUM">0.7</field></block></value><next><block type="variables_set" id="v5"><field name="VAR" id="innerR">innerRadius</field><value name="VALUE"><block type="math_arithmetic" id="calc"><field name="OP">DIVIDE</field><value name="A"><block type="variables_get" id="gOut"><field name="VAR" id="outerR">outerRadius</field></block></value><value name="B"><block type="variables_get" id="gDiv"><field name="VAR" id="div">innerRadiusDivisor</field></block></value></block></value><next><block type="variables_set" id="v6"><field name="VAR" id="wire">starWire</field><value name="VALUE"><block type="base_time_await_return" id="aw1"><value name="Promise"><block type="bitbybit.occt.shapes.wire.createStarWire" id="star"><value name="Center"><block type="bitbybit.vector.vectorXYZ" id="vc1"><value name="X"><block type="math_number" id="x1"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="y1"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="z1"><field name="NUM">0</field></block></value></block></value><value name="Direction"><block type="bitbybit.vector.vectorXYZ" id="vd1"><value name="X"><block type="math_number" id="dx1"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="dy1"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="dz1"><field name="NUM">0</field></block></value></block></value><value name="NumRays"><block type="variables_get" id="gRays"><field name="VAR" id="rays">rays</field></block></value><value name="OuterRadius"><block type="variables_get" id="gOR"><field name="VAR" id="outerR">outerRadius</field></block></value><value name="InnerRadius"><block type="variables_get" id="gIR"><field name="VAR" id="innerR">innerRadius</field></block></value><value name="Half"><block type="logic_boolean" id="half"><field name="BOOL">FALSE</field></block></value></block></value></block></value><next><block type="variables_set" id="v7"><field name="VAR" id="pt">topPoint</field><value name="VALUE"><block type="bitbybit.point.pointXYZ" id="pt1"><value name="X"><block type="math_number" id="px"><field name="NUM">0</field></block></value><value name="Y"><block type="variables_get" id="py"><field name="VAR" id="h">height</field></block></value><value name="Z"><block type="math_number" id="pz"><field name="NUM">0</field></block></value></block></value><next><block type="variables_set" id="v8"><field name="VAR" id="loft">loftShape</field><value name="VALUE"><block type="base_time_await_return" id="aw2"><value name="Promise"><block type="bitbybit.occt.operations.loftAdvanced" id="loft"><value name="Shapes"><block type="lists_create_with" id="l1"><mutation items="1"></mutation><value name="ADD0"><block type="variables_get" id="gw"><field name="VAR" id="wire">starWire</field></block></value></block></value><value name="StartVertex"><block type="variables_get" id="gpt"><field name="VAR" id="pt">topPoint</field></block></value><value name="MakeSolid"><block type="logic_boolean" id="ms"><field name="BOOL">FALSE</field></block></value><value name="Closed"><block type="logic_boolean" id="cl"><field name="BOOL">FALSE</field></block></value><value name="Periodic"><block type="logic_boolean" id="pe"><field name="BOOL">FALSE</field></block></value><value name="Straight"><block type="logic_boolean" id="st"><field name="BOOL">FALSE</field></block></value><value name="NrPeriodicSections"><block type="math_number" id="np"><field name="NUM">10</field></block></value><value name="UseSmoothing"><block type="logic_boolean" id="us"><field name="BOOL">FALSE</field></block></value><value name="MaxUDegree"><block type="math_number" id="mu"><field name="NUM">3</field></block></value><value name="Tolerance"><block type="math_number" id="tol"><field name="NUM">1e-7</field></block></value><value name="ParType"><block type="text" id="par"><field name="TEXT">approxCentripetal</field></block></value></block></value></block></value><next><block type="variables_set" id="v9"><field name="VAR" id="mir">mirroredShape</field><value name="VALUE"><block type="base_time_await_return" id="aw3"><value name="Promise"><block type="bitbybit.occt.transforms.mirrorAlongNormal" id="mirr"><value name="Shape"><block type="variables_get" id="gl"><field name="VAR" id="loft">loftShape</field></block></value><value name="Origin"><block type="bitbybit.vector.vectorXYZ" id="vo"><value name="X"><block type="math_number" id="vox"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="voy"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="voz"><field name="NUM">0</field></block></value></block></value><value name="Normal"><block type="bitbybit.vector.vectorXYZ" id="vn"><value name="X"><block type="math_number" id="vnx"><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="vny"><field name="NUM">1</field></block></value><value name="Z"><block type="math_number" id="vnz"><field name="NUM">0</field></block></value></block></value></block></value></block></value><next><block type="variables_set" id="v10"><field name="VAR" id="list">shapeList</field><value name="VALUE"><block type="lists_create_with" id="l2"><mutation items="2"></mutation><value name="ADD0"><block type="variables_get" id="gl2"><field name="VAR" id="loft">loftShape</field></block></value><value name="ADD1"><block type="variables_get" id="gm"><field name="VAR" id="mir">mirroredShape</field></block></value></block></value><next><block type="variables_set" id="v11"><field name="VAR" id="sewn">sewnShell</field><value name="VALUE"><block type="base_time_await_return" id="aw4"><value name="Promise"><block type="bitbybit.occt.shapes.shell.sewFaces" id="sew"><value name="Shapes"><block type="variables_get" id="gls"><field name="VAR" id="list">shapeList</field></block></value><value name="Tolerance"><block type="math_number" id="tol2"><field name="NUM">1e-7</field></block></value></block></value></block></value><next><block type="variables_set" id="v12"><field name="VAR" id="solid">solidShape</field><value name="VALUE"><block type="base_time_await_return" id="aw5"><value name="Promise"><block type="bitbybit.occt.shapes.solid.fromClosedShell" id="fromSh"><value name="Shape"><block type="variables_get" id="gsew"><field name="VAR" id="sewn">sewnShell</field></block></value></block></value></block></value><next><block type="variables_set" id="v13"><field name="VAR" id="mesh">drawnMesh</field><value name="VALUE"><block type="base_time_await_return" id="aw6"><value name="Promise"><block type="bitbybit.draw.drawAnyAsync" id="draw"><value name="Entity"><block type="variables_get" id="gsol"><field name="VAR" id="solid">solidShape</field></block></value><value name="Options"><block type="bitbybit.draw.optionsOcctShapeMaterial" id="dopt"><value name="Precision"><block type="math_number" id="dp"><field name="NUM">0.01</field></block></value><value name="FaceMaterial"><block type="bitbybit.babylon.material.pbrMetallicRoughness.create" id="mat"><value name="Name"><block type="text" id="mn"><field name="TEXT">Custom Material</field></block></value><value name="BaseColor"><block type="colour_picker" id="bc"><field name="COLOUR">#4dffb2</field></block></value><value name="EmissiveColor"><block type="colour_picker" id="ec"><field name="COLOUR">#000000</field></block></value><value name="Metallic"><block type="math_number" id="met"><field name="NUM">0.8</field></block></value><value name="Roughness"><block type="math_number" id="rog"><field name="NUM">0.3</field></block></value><value name="Alpha"><block type="math_number" id="alp"><field name="NUM">1</field></block></value><value name="BackFaceCulling"><block type="logic_boolean" id="bfc"><field name="BOOL">FALSE</field></block></value><value name="ZOffset"><block type="math_number" id="zoff"><field name="NUM">2</field></block></value></block></value><value name="DrawEdges"><block type="logic_boolean" id="de"><field name="BOOL">TRUE</field></block></value><value name="EdgeColour"><block type="colour_picker" id="deco"><field name="COLOUR">#ffffff</field></block></value><value name="EdgeWidth"><block type="math_number" id="dew"><field name="NUM">2</field></block></value></block></value></block></value></block></value><next><block type="bitbybit.advanced.navigation.zoomOn" id="zoom"><value name="Meshes"><block type="lists_create_with" id="lz"><mutation items="1"></mutation><value name="ADD0"><block type="variables_get" id="gmsh"><field name="VAR" id="mesh">drawnMesh</field></block></value></block></value><value name="IncludeChildren"><block type="logic_boolean" id="inc"><field name="BOOL">TRUE</field></block></value><value name="AnimationSpeed"><block type="math_number" id="spd"><field name="NUM">0.8</field></block></value><value name="Offset"><block type="math_number" id="off"><field name="NUM">-0.3</field></block></value><value name="DoNotUpdateMaxZ"><block type="logic_boolean" id="dnz"><field name="BOOL">TRUE</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></xml>
const { StarDto, LoftAdvancedDto, MirrorAlongNormalDto, SewDto, ShapeDto } = Bit.Inputs.OCCT;
const { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;
const { SkyboxDto } = Bit.Inputs.BabylonScene;
const { DrawOcctShapeOptions } = Bit.Inputs.Draw;
const { ZoomOnDto } = Bit.Advanced.Navigation;
const { skyboxEnum } = Bit.Inputs.Base;
const { approxParametrizationTypeEnum } = Bit.Inputs.OCCT;
type TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;
const { wire, shell, solid } = bitbybit.occt.shapes;
const { operations, transforms } = bitbybit.occt;
const start = async () => {
const skyboxOpt = new SkyboxDto();
skyboxOpt.skybox = skyboxEnum.city;
skyboxOpt.hideSkybox = true;
bitbybit.babylon.scene.enableSkybox(skyboxOpt);
// 1. Variables
const rays = 5;
const outerRadius = 4.5;
const innerRadiusDivisor = 3.2;
const height = 0.7;
const innerRadius = outerRadius / innerRadiusDivisor;
// 2. Create Star Wire
const starDto = new StarDto();
starDto.center = [0, 0, 0];
starDto.direction = [0, 1, 0];
starDto.numRays = rays;
starDto.outerRadius = outerRadius;
starDto.innerRadius = innerRadius;
starDto.half = false;
const starWire = await wire.createStarWire(starDto);
// 3. Loft to Top Point
const loftDto = new LoftAdvancedDto<TopoDSShapePointer>();
loftDto.shapes = [starWire];
loftDto.startVertex = [0, height, 0];
loftDto.makeSolid = false;
loftDto.closed = false;
loftDto.periodic = false;
loftDto.straight = false;
loftDto.nrPeriodicSections = 10;
loftDto.useSmoothing = false;
loftDto.maxUDegree = 3;
loftDto.tolerance = 1e-7;
loftDto.parType = approxParametrizationTypeEnum.approxCentripetal;
const loftShape = await operations.loftAdvanced(loftDto);
// 4. Mirror to create bottom half
const mirrorDto = new MirrorAlongNormalDto<TopoDSShapePointer>();
mirrorDto.shape = loftShape;
mirrorDto.origin = [0, 0, 0];
mirrorDto.normal = [0, 1, 0];
const mirroredShape = await transforms.mirrorAlongNormal(mirrorDto);
// 5. Sew Faces into a Shell
const sewDto = new SewDto<TopoDSShapePointer>();
sewDto.shapes = [loftShape, mirroredShape];
sewDto.tolerance = 1e-7;
const sewnShell = await shell.sewFaces(sewDto);
// 6. Create Solid from Shell
const solidDto = new ShapeDto<TopoDSShapePointer>();
solidDto.shape = sewnShell;
const solidShape = await solid.fromClosedShell(solidDto);
// 7. Visualization
const materialOptions = new PBRMetallicRoughnessDto();
materialOptions.name = 'Custom Material';
materialOptions.baseColor = '#4dffb2';
materialOptions.emissiveColor = '#000000';
materialOptions.metallic = 0.8;
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 = true;
drawOptions.edgeColour = '#ffffff';
drawOptions.edgeWidth = 2;
drawOptions.faceMaterial = material;
const drawnMesh = await bitbybit.draw.drawAnyAsync({
entity: solidShape,
options: drawOptions,
});
const zoomDto = new ZoomOnDto();
zoomDto.meshes = [drawnMesh];
zoomDto.includeChildren = true;
zoomDto.animationSpeed = 0.8;
zoomDto.offset = -0.3;
zoomDto.doNotUpdateMaxZ = true;
bitbybit.advanced.navigation.zoomOn(zoomDto);
};
start();
How It Works
This tutorial demonstrates the classic technique of creating a double-sided gem or jewel shape. We start with a star-shaped wire at the base and loft it upward to converge at a single point, forming a pyramid-like top half. The lofting operation creates smooth triangular faces that connect each edge of the star to the apex point, giving the ornament its characteristic faceted appearance.
To complete the gem, we mirror the top half along the horizontal plane to create an identical bottom half. These two halves are then sewn together into a closed shell and converted into a solid 3D object. The result is a symmetrical star gem with sharp faceted edges that catch light beautifully when rendered with metallic materials.
Algorithm Steps
1. Define parameters:
- Number of star rays
- Outer radius and inner radius
- Height of the apex point
2. Create star wire:
- Generate star-shaped polygon at origin
- Star lies flat on XZ plane
3. Loft to apex point:
- Start from star wire base
- Connect all edges to single point at height
- Creates pyramid/cone-like top half
4. Mirror geometry:
- Reflect top half along Y=0 plane
- Creates matching bottom half
5. Sew surfaces:
- Join top and bottom halves at star perimeter
- Forms closed watertight shell
6. Convert to solid:
- Transform closed shell into solid body
- Ready for 3D printing or further operations
7. Render with metallic material and edge highlighting
Adjust the number of rays, radius ratio, and height to create stars ranging from delicate ornaments to bold geometric decorations.



