Compound Shapes
Compound shapes are one of the most powerful features in OCCT that allow you to group multiple geometric entities into a single cohesive unit. They serve as containers that can hold any combination of OCCT shape types - wires, edges, faces, shells, and solids - treating them as one unified entity.
What Are Compound Shapes?
A compound is essentially a collection of shapes that behave as a single OCCT shape object. Think of it as a group in a drawing application, but with much more sophisticated geometric capabilities. When you create a compound, you're not merging the geometries like with boolean operations - instead, you're creating a hierarchical structure that maintains individual shape identities while enabling collective operations.
This grouping mechanism is particularly valuable when working with complex assemblies, repetitive patterns, or when you need to apply transformations to multiple objects simultaneously. Compounds preserve the original topology of each constituent shape while providing the convenience of single-entity manipulation.
Key Benefits of Using Compounds
Performance Optimization: One of the most significant advantages of compounds is their impact on rendering performance. When you have many individual shapes, each one requires its own mesh generation and GPU draw call. Compounds in Bitbybit consolidate these into a single mesh, dramatically reducing the computational overhead and improving frame rates.
Unified Transformations: Compounds allow you to treat multiple shapes as a single entity for transformations. Whether you're rotating, translating, scaling, or applying complex transformations, all constituent shapes move together as one cohesive unit. This eliminates the need to individually transform each shape and ensures perfect synchronization.
Simplified Organization: In complex models with hundreds or thousands of individual elements, compounds provide a natural organizational structure. You can group related shapes logically, making your code more maintainable and your geometric operations more intuitive.
Memory Efficiency: By grouping shapes into compounds, you reduce the overhead associated with managing individual shape references, leading to more efficient memory usage in large-scale applications.
Performance Problem: Individual Shape Creation
When creating multiple similar shapes individually, you encounter significant performance bottlenecks. Each shape requires separate mesh generation, individual GPU draw calls, and independent memory allocation. This becomes particularly problematic when working with patterns or arrays of objects.
The example below demonstrates this performance issue by creating individual spheres in a 3D grid pattern. Notice how each sphere is created and rendered separately, leading to reduced performance and slower frame rates as the number of objects increases.
If you're not experiencing noticeable performance issues in the first example, try increasing the grid size substantially (to 8 or 10) to see the difference. The performance impact becomes much more apparent with larger numbers of objects.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"b446c4e9ffd24726": {
"id": "b446c4e9ffd24726",
"name": "bitbybit.vector.span",
"customName": "span",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"step": 2.5,
"min": -10,
"max": 1
},
"inputs": {
"min": {
"connections": [
{
"node": "f88fd357ced411f1",
"output": "result",
"data": {}
}
]
},
"max": {
"connections": [
{
"node": "c9ddf3d81304fa53",
"output": "result",
"data": {}
}
]
}
},
"position": [
1033.7463569641113,
513.0007019042969
]
},
"c9ddf3d81304fa53": {
"id": "c9ddf3d81304fa53",
"name": "bitbybit.math.numberSlider",
"customName": "number slider",
"data": {
"options": {
"min": 2,
"max": 10,
"step": 1,
"width": 350,
"updateOnDrag": false
},
"number": 5
},
"inputs": {},
"position": [
-58.33047223289259,
628.7886361749063
]
},
"f88fd357ced411f1": {
"id": "f88fd357ced411f1",
"name": "bitbybit.math.negate",
"customName": "negate",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"number": 1
},
"inputs": {
"number": {
"connections": [
{
"node": "c9ddf3d81304fa53",
"output": "result",
"data": {}
}
]
}
},
"position": [
546.3032722473145,
366.40267944382504
]
},
"6c01c1c47ff8a874": {
"id": "6c01c1c47ff8a874",
"name": "bitbybit.point.pointXYZ",
"customName": "point xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 0
},
"inputs": {
"x": {
"connections": [
{
"node": "c509b5aced40b98d",
"output": "result",
"data": {}
}
]
},
"y": {
"connections": [
{
"node": "c509b5aced40b98d",
"output": "result",
"data": {}
}
]
},
"z": {
"connections": [
{
"node": "c509b5aced40b98d",
"output": "result",
"data": {}
}
]
}
},
"position": [
1771.9867359310028,
470.02919755656137
]
},
"c509b5aced40b98d": {
"id": "c509b5aced40b98d",
"name": "bitbybit.lists.flatten",
"customName": "flatten",
"data": {
"nrLevels": 1
},
"inputs": {
"list": {
"connections": [
{
"node": "b446c4e9ffd24726",
"output": "result",
"data": {}
}
]
}
},
"position": [
1402.6814793938981,
548.4041387968635
]
},
"859b8e895567d5fd": {
"id": "859b8e895567d5fd",
"name": "bitbybit.occt.shapes.solid.createSphere",
"customName": "sphere",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1,
"center": [
0,
0,
0
]
},
"inputs": {
"center": {
"connections": [
{
"node": "6c01c1c47ff8a874",
"output": "result",
"data": {}
}
]
}
},
"position": [
2142.757180037668,
427.66801356001173
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><variables><variable id="`1z$H~OFP(!H<V+Z-pKB">gridSize</variable><variable id=")k=XMIcv%#l`%T-$7T![">sphereRadius</variable><variable id=":n:ZQ!`gJW~i7j?tqY$D">spacing</variable><variable id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</variable><variable id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</variable><variable id="GhT2mN8P~oK%j}#h{fys">yPositions</variable><variable id="LsA=SzqGD@VfxV^t@,Pt">zPositions</variable><variable id="_@iM|?pHy@h]z{cQ!<|V">x</variable><variable id="mHgT2mN8P~oK%j}#h{fy">y</variable><variable id="l[Y?:G{pVGY!Y:K+N3O[">z</variable></variables><block type="variables_set" id="$3)Ai-IM4U6,PsVL?K8s" x="-179" y="-214"><field name="VAR" id="`1z$H~OFP(!H<V+Z-pKB">gridSize</field><value name="VALUE"><block type="math_number" id="8IwD^]vU#OI}m!N;^@M$"><field name="NUM">3</field></block></value><next><block type="variables_set" id="Vv6?y]d~D/r/@HLgm1Zf"><field name="VAR" id=")k=XMIcv%#l`%T-$7T![">sphereRadius</field><value name="VALUE"><block type="math_number" id="_o:;w%c0K@*[U]:nBk7$"><field name="NUM">1</field></block></value><next><block type="variables_set" id="|+F;Vdr+g72pEMKK7]Gn"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field><value name="VALUE"><block type="math_number" id="rO[Rl|j{Y~@D1!c.]N#k"><field name="NUM">2.5</field></block></value><next><block type="variables_set" id="j*p9GaGLR]LN(||8d]+E"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field><value name="VALUE"><block type="math_arithmetic" id="oJb/q(G+h;#qnA1WZP4I"><field name="OP">DIVIDE</field><value name="A"><block type="math_arithmetic" id="j@_wHe4[xz3Q%Uw8Q!}p"><field name="OP">MULTIPLY</field><value name="A"><block type="variables_get" id="8;K6p?PGQo%A3#nf4#*y"><field name="VAR" id="`1z$H~OFP(!H<V+Z-pKB">gridSize</field></block></value><value name="B"><block type="variables_get" id="n5qjfOm=VEjTfgCq%qM<"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value></block></value><value name="B"><block type="math_number" id="rR[vAJBOm7.f4MU(|zPD"><field name="NUM">2</field></block></value></block></value><next><block type="variables_set" id="qD|l#M)KjFH:SfSLQp~b"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="5NbS7|.{Kt)uc=a4;e1f"><value name="Step"><block type="variables_get" id="FrWGfn6Y,]NZ%7{!7Q#c"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="OAcQ/[GF+)TwJT@NpT1D"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="o4a6PZ*5gKP:B1n;Yps{"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id=":pMfpZ5<4~cQ<#4r/F~e"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id="qD|l#M)KjFH:SfSLQp~c"><field name="VAR" id="GhT2mN8P~oK%j}#h{fys">yPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="5NbS7|.{Kt)uc=a4;e2f"><value name="Step"><block type="variables_get" id="FrWGfn6Y,]NZ%7{!7Q#d"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="OAcQ/[GF+)TwJT@NpT2D"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="o4a6PZ*5gKP:B1n;Yps}"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id=":pMfpZ5<4~cQ<#4r/F~f"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id=":Y$|_jOLGD/nPkSG[j%{"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="JWUfp@v)1?h,L{lL0VVt"><value name="Step"><block type="variables_get" id="i,9I@j[H:`kkY(;T)6lN"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="v8%|j)uDpj8<x.2XVhOL"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="%wCnN7P1$2f>K@pO+X)g"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id="oHGi/p>Yz~a[dFq-vr#)"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="controls_for" id="gHNF^1;1#(|H96$SfOUF"><field name="VAR" id="_@iM|?pHy@h]z{cQ!<|V">x</field><value name="FROM"><block type="math_number" id="[Pq@#=w5C,r,;6T#q%<{"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="<D%+3}uJOr~Y!uy0GXgJ"><value name="VALUE"><block type="variables_get" id="BPAyYm2eD(d*/c>*A7DT"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="YpRHU-j+L{<BPBHBp+w)"><field name="NUM">1</field></block></value><statement name="DO"><block type="controls_for" id="gHNF^1;1#(|H96$SfOUG"><field name="VAR" id="mHgT2mN8P~oK%j}#h{fy">y</field><value name="FROM"><block type="math_number" id="[Pq@#=w5C,r,;6T#q%<}"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="<D%+3}uJOr~Y!uy0GXgK"><value name="VALUE"><block type="variables_get" id="BPAyYm2eD(d*/c>*A7DU"><field name="VAR" id="GhT2mN8P~oK%j}#h{fys">yPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="YpRHU-j+L{<BPBHBp+x)"><field name="NUM">1</field></block></value><statement name="DO"><block type="controls_for" id="QGNj]!$;TE4u~g5r]<M@"><field name="VAR" id="l[Y?:G{pVGY!Y:K+N3O[">z</field><value name="FROM"><block type="math_number" id="D[9>k7F.{%i:W<|xp;:;"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="x(qG=2}I@=({$FKk<L7`"><value name="VALUE"><block type="variables_get" id="9Ng/yKvYqeC?i`jOF~y6"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="fIjM`Pqg<+m?m_WC|xYp"><field name="NUM">1</field></block></value><statement name="DO"><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="*k[B)FPbP]GF.}##+E6D"><value name="Entity"><block type="bitbybit.occt.shapes.solid.createSphere" id="?^p7xpXjH2+S_=nYP]NO"><value name="Radius"><block type="variables_get" id="LwTQRZc;Rc6H7YH0xbQJ"><field name="VAR" id=")k=XMIcv%#l`%T-$7T![">sphereRadius</field></block></value><value name="Center"><block type="lists_create_with" id="HBp=:cPt,T=7ow#(W*[Q"><mutation items="3"></mutation><value name="ADD0"><block type="lists_getIndex" id="-aMn^PD~L1yl7B}X;jke"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="MnPg/eR3qr7]^,}$3}Gt"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field></block></value><value name="AT"><block type="variables_get" id="B*Qa8#Yf1?hpL[2[Z0GJ"><field name="VAR" id="_@iM|?pHy@h]z{cQ!<|V">x</field></block></value></block></value><value name="ADD1"><block type="lists_getIndex" id="-aMn^PD~L1yl7B}X;jkf"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="MnPg/eR3qr7]^,}$3}Gu"><field name="VAR" id="GhT2mN8P~oK%j}#h{fys">yPositions</field></block></value><value name="AT"><block type="variables_get" id="B*Qa8#Yf1?hpL[2[Z0GK"><field name="VAR" id="mHgT2mN8P~oK%j}#h{fy">y</field></block></value></block></value><value name="ADD2"><block type="lists_getIndex" id=":f!o]^?|{r|[Qx5(qEwN"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="UPI[a-Td_v3xL=PNRR8D"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field></block></value><value name="AT"><block type="variables_get" id="eRGaEaWh(Nrt/e$j1h(."><field name="VAR" id="l[Y?:G{pVGY!Y:K+N3O[">z</field></block></value></block></value></block></value></block></value></block></statement></block></statement></block></statement></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></xml>
// Import required DTOs for creating spheres
const { SphereDto } = Bit.Inputs.OCCT;
// Import type definitions for type safety
type Point3 = Bit.Inputs.Base.Point3;
// Get access to OCCT modules and utility functions
const { solid } = bitbybit.occt.shapes;
const { vector } = bitbybit;
// Define the main function to create individual spheres (performance problem)
const start = async () => {
// Grid parameters
const gridSize = 3; // Number of spheres per side
const sphereRadius = 1; // Radius of each sphere
const spacing = 2.5; // Distance between sphere centers
// Calculate grid boundaries
const halfGrid = (gridSize * spacing) / 2;
// Generate grid positions using span functions for 3D grid
const xPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
const yPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
const zPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
// Create individual spheres using nested loops for 3D grid
// This approach creates each sphere separately, leading to performance issues
for (const xPos of xPositions) {
for (const yPos of yPositions) {
for (const zPos of zPositions) {
// Define sphere creation options
const sphereOptions = new SphereDto();
sphereOptions.radius = sphereRadius;
sphereOptions.center = [xPos, yPos, zPos];
// Create and immediately draw each sphere individually
// This is inefficient as each sphere requires its own mesh and draw call
const sphere = await solid.createSphere(sphereOptions);
// Each draw call is separate, creating performance overhead
bitbybit.draw.drawAnyAsync({
entity: sphere
});
}
}
}
}
// Execute the function
start();
Solution: Creating Compounds for Better Performance
The solution to this performance problem is to use compounds. Instead of creating individual shapes that are rendered separately, you create all the shapes first and then combine them into a single compound. This compound is then treated as one entity for rendering purposes, dramatically improving performance.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"b446c4e9ffd24726": {
"id": "b446c4e9ffd24726",
"name": "bitbybit.vector.span",
"customName": "span",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"step": 2.5,
"min": -10,
"max": 1
},
"inputs": {
"min": {
"connections": [
{
"node": "f88fd357ced411f1",
"output": "result",
"data": {}
}
]
},
"max": {
"connections": [
{
"node": "c9ddf3d81304fa53",
"output": "result",
"data": {}
}
]
}
},
"position": [
1033.7463569641113,
513.0007019042969
]
},
"c9ddf3d81304fa53": {
"id": "c9ddf3d81304fa53",
"name": "bitbybit.math.numberSlider",
"customName": "number slider",
"data": {
"options": {
"min": 2,
"max": 10,
"step": 1,
"width": 350,
"updateOnDrag": false
},
"number": 5
},
"inputs": {},
"position": [
-58.33047223289259,
628.7886361749063
]
},
"f88fd357ced411f1": {
"id": "f88fd357ced411f1",
"name": "bitbybit.math.negate",
"customName": "negate",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"number": 1
},
"inputs": {
"number": {
"connections": [
{
"node": "c9ddf3d81304fa53",
"output": "result",
"data": {}
}
]
}
},
"position": [
546.3032722473145,
366.40267944382504
]
},
"6c01c1c47ff8a874": {
"id": "6c01c1c47ff8a874",
"name": "bitbybit.point.pointXYZ",
"customName": "point xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 0
},
"inputs": {
"x": {
"connections": [
{
"node": "c509b5aced40b98d",
"output": "result",
"data": {}
}
]
},
"y": {
"connections": [
{
"node": "c509b5aced40b98d",
"output": "result",
"data": {}
}
]
},
"z": {
"connections": [
{
"node": "c509b5aced40b98d",
"output": "result",
"data": {}
}
]
}
},
"position": [
1771.9867359310028,
470.02919755656137
]
},
"c509b5aced40b98d": {
"id": "c509b5aced40b98d",
"name": "bitbybit.lists.flatten",
"customName": "flatten",
"data": {
"nrLevels": 1
},
"inputs": {
"list": {
"connections": [
{
"node": "b446c4e9ffd24726",
"output": "result",
"data": {}
}
]
}
},
"position": [
1402.6814793938981,
548.4041387968635
]
},
"859b8e895567d5fd": {
"id": "859b8e895567d5fd",
"name": "bitbybit.occt.shapes.solid.createSphere",
"customName": "sphere",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1,
"center": [
0,
0,
0
]
},
"inputs": {
"center": {
"connections": [
{
"node": "6c01c1c47ff8a874",
"output": "result",
"data": {}
}
]
}
},
"position": [
2142.757180037668,
427.66801356001173
]
},
"b33c2e10e390804f": {
"id": "b33c2e10e390804f",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "859b8e895567d5fd",
"output": "result",
"data": {}
}
]
}
},
"position": [
2517.4087816268816,
468.10613836610617
]
},
"1c25083cf4fbd5d8": {
"id": "1c25083cf4fbd5d8",
"name": "bitbybit.occt.shapes.compound.makeCompound",
"customName": "make compound",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shapes": {
"connections": [
{
"node": "b33c2e10e390804f",
"output": "list",
"data": {}
}
]
}
},
"position": [
2897.9006903391196,
427.5889225632506
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><variables><variable id="`1z$H~OFP(!H<V+Z-pKB">gridSize</variable><variable id=")k=XMIcv%#l`%T-$7T![">sphereRadius</variable><variable id=":n:ZQ!`gJW~i7j?tqY$D">spacing</variable><variable id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</variable><variable id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</variable><variable id="m!k0wX7S4Q>.m4L+Lz+P">yPositions</variable><variable id="LsA=SzqGD@VfxV^t@,Pt">zPositions</variable><variable id="^[yZXg_VCL;}e3C?J2HZ">spheres</variable><variable id="_@iM|?pHy@h]z{cQ!<|V">x</variable><variable id="Q=9hU?;o@L2Y7_v}[V<y">y</variable><variable id="$w0uK_j5}e*hWR.e#J%T">compound</variable><variable id="l[Y?:G{pVGY!Y:K+N3O[">z</variable></variables><block type="variables_set" id="$3)Ai-IM4U6,PsVL?K8s" x="-179" y="-214"><field name="VAR" id="`1z$H~OFP(!H<V+Z-pKB">gridSize</field><value name="VALUE"><block type="math_number" id="8IwD^]vU#OI}m!N;^@M$"><field name="NUM">3</field></block></value><next><block type="variables_set" id="Vv6?y]d~D/r/@HLgm1Zf"><field name="VAR" id=")k=XMIcv%#l`%T-$7T![">sphereRadius</field><value name="VALUE"><block type="math_number" id="_o:;w%c0K@*[U]:nBk7$"><field name="NUM">1</field></block></value><next><block type="variables_set" id="|+F;Vdr+g72pEMKK7]Gn"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field><value name="VALUE"><block type="math_number" id="rO[Rl|j{Y~@D1!c.]N#k"><field name="NUM">2.5</field></block></value><next><block type="variables_set" id="j*p9GaGLR]LN(||8d]+E"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field><value name="VALUE"><block type="math_arithmetic" id="oJb/q(G+h;#qnA1WZP4I"><field name="OP">DIVIDE</field><value name="A"><block type="math_arithmetic" id="j@_wHe4[xz3Q%Uw8Q!}p"><field name="OP">MULTIPLY</field><value name="A"><block type="variables_get" id="8;K6p?PGQo%A3#nf4#*y"><field name="VAR" id="`1z$H~OFP(!H<V+Z-pKB">gridSize</field></block></value><value name="B"><block type="variables_get" id="n5qjfOm=VEjTfgCq%qM<"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value></block></value><value name="B"><block type="math_number" id="rR[vAJBOm7.f4MU(|zPD"><field name="NUM">2</field></block></value></block></value><next><block type="variables_set" id="qD|l#M)KjFH:SfSLQp~b"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="5NbS7|.{Kt)uc=a4;e1f"><value name="Step"><block type="variables_get" id="FrWGfn6Y,]NZ%7{!7Q#c"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="OAcQ/[GF+)TwJT@NpT1D"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="o4a6PZ*5gKP:B1n;Yps{"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id=":pMfpZ5<4~cQ<#4r/F~e"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id="3Mx)5K_|m~CL:@3Ly8K."><field name="VAR" id="m!k0wX7S4Q>.m4L+Lz+P">yPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="Kt)uc=a4;e1f5NbS7|."><value name="Step"><block type="variables_get" id="7{!7Q#cFrWGfn6Y,]NZ%"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="TwJT@NpT1DOAcQ/[GF+)"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="B1n;Yps{o4a6PZ*5gKP:"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id="<#4r/F~e:pMfpZ5<4~cQ"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id=":Y$|_jOLGD/nPkSG[j%{"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="JWUfp@v)1?h,L{lL0VVt"><value name="Step"><block type="variables_get" id="i,9I@j[H:`kkY(;T)6lN"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="v8%|j)uDpj8<x.2XVhOL"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="%wCnN7P1$2f>K@pO+X)g"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id="oHGi/p>Yz~a[dFq-vr#)"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id="ZD4Z/|hKO$?<~#5<M=U]"><field name="VAR" id="^[yZXg_VCL;}e3C?J2HZ">spheres</field><value name="VALUE"><block type="lists_create_with" id="Lq%DLh@>`qBxK^DQqRFt"><mutation items="0"></mutation></block></value><next><block type="controls_for" id="gHNF^1;1#(|H96$SfOUF"><field name="VAR" id="_@iM|?pHy@h]z{cQ!<|V">x</field><value name="FROM"><block type="math_number" id="[Pq@#=w5C,r,;6T#q%<{"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="<D%+3}uJOr~Y!uy0GXgJ"><value name="VALUE"><block type="variables_get" id="BPAyYm2eD(d*/c>*A7DT"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="YpRHU-j+L{<BPBHBp+w)"><field name="NUM">1</field></block></value><statement name="DO"><block type="controls_for" id="QGNj]!$;TE4u~g5r]<M@"><field name="VAR" id="Q=9hU?;o@L2Y7_v}[V<y">y</field><value name="FROM"><block type="math_number" id="D[9>k7F.{%i:W<|xp;:;"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="x(qG=2}I@=({$FKk<L7`"><value name="VALUE"><block type="variables_get" id="9Ng/yKvYqeC?i`jOF~y6"><field name="VAR" id="m!k0wX7S4Q>.m4L+Lz+P">yPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="fIjM`Pqg<+m?m_WC|xYp"><field name="NUM">1</field></block></value><statement name="DO"><block type="controls_for" id="$rjCzIq1W#ZXg.cyG,r{"><field name="VAR" id="l[Y?:G{pVGY!Y:K+N3O[">z</field><value name="FROM"><block type="math_number" id="]^KK4ih72XOco%%P@0)_"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="$Ol5(r7=`({!{(-_wleI"><value name="VALUE"><block type="variables_get" id="xcD-K5R_*l]2|!,w;cU9"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="S5$LWl[^p74E`K92w@/S"><field name="NUM">1</field></block></value><statement name="DO"><block type="lists_setIndex" id="o)Yfl]JL@sPdT|fqQ68r"><mutation at="false"></mutation><field name="MODE">INSERT</field><field name="WHERE">LAST</field><value name="LIST"><block type="variables_get" id="qiYN=Sg4;HRK}hk_dCY8"><field name="VAR" id="^[yZXg_VCL;}e3C?J2HZ">spheres</field></block></value><value name="TO"><block type="bitbybit.occt.shapes.solid.createSphere" id="e_|.LA#,+!bNt^9%5-#Z"><value name="Radius"><block type="variables_get" id=":~k6lEuN.^.8e`bK(u-,"><field name="VAR" id=")k=XMIcv%#l`%T-$7T![">sphereRadius</field></block></value><value name="Center"><block type="lists_create_with" id="{$1IePScoYa2/-VEN*w|"><mutation items="3"></mutation><value name="ADD0"><block type="lists_getIndex" id="[]eV!rhP=o2GxDOl3=d#"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="n]8rl1@+pm};1tNyS5!s"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field></block></value><value name="AT"><block type="variables_get" id="QED#;-QU_Vgj#AA{`sPx"><field name="VAR" id="_@iM|?pHy@h]z{cQ!<|V">x</field></block></value></block></value><value name="ADD1"><block type="lists_getIndex" id="1q,P(3sbt/1m(62d5k[["><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="G`j~@2?LH#VZefNtz*:6"><field name="VAR" id="m!k0wX7S4Q>.m4L+Lz+P">yPositions</field></block></value><value name="AT"><block type="variables_get" id="$;/rS|eq0VjyHN{B2Z#="><field name="VAR" id="Q=9hU?;o@L2Y7_v}[V<y">y</field></block></value></block></value><value name="ADD2"><block type="lists_getIndex" id="b~d$%Lvschy]sZ[LC1+7"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="%OJnsFj27D.Bh|rQ}@,("><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field></block></value><value name="AT"><block type="variables_get" id=")a#SrmO;r`$53f9.mXvK"><field name="VAR" id="l[Y?:G{pVGY!Y:K+N3O[">z</field></block></value></block></value></block></value></block></value></block></statement></block></statement></block></statement><next><block type="variables_set" id="KQUJn_D`*w?@d~uq%FIl"><field name="VAR" id="$w0uK_j5}e*hWR.e#J%T">compound</field><value name="VALUE"><block type="bitbybit.occt.shapes.compound.makeCompound" id="l{]Zj>:FwY@}K[%C`6Kd"><value name="Shapes"><block type="variables_get" id="vQjkJ`X%P:w:!}1rZ`^m"><field name="VAR" id="^[yZXg_VCL;}e3C?J2HZ">spheres</field></block></value></block></value><next><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="E1r!Td=%`8)NKx/sSq]d"><value name="Entity"><block type="variables_get" id="Ul$3y@vMWF%?1m*9BfKw"><field name="VAR" id="$w0uK_j5}e*hWR.e#J%T">compound</field></block></value></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></next></block></xml>
// Import required DTOs for creating spheres, compounds, and transformations
const { SphereDto, CompoundShapesDto, RotateDto, TranslateDto } = Bit.Inputs.OCCT;
// Import type definitions for type safety
type Point3 = Bit.Inputs.Base.Point3;
type Vector3 = Bit.Inputs.Base.Vector3;
type TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;
type TopoDSCompoundPointer = Bit.Inputs.OCCT.TopoDSCompoundPointer;
// Get access to OCCT modules and utility functions
const { solid, compound } = bitbybit.occt.shapes;
const { transforms } = bitbybit.occt;
const { vector } = bitbybit;
// Define the main function to create a compound
const start = async () => {
// Grid parameters
const gridSize = 5; // Number of spheres per side
const sphereRadius = 1; // Radius of each sphere
const spacing = 2.5; // Distance between sphere centers
// Calculate grid boundaries
const halfGrid = (gridSize * spacing) / 2;
// Generate grid positions using span functions
const xPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
const yPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
const zPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
// Create array to store all spheres before compounding
const spheres: TopoDSSolidPointer[] = [];
// Create all spheres first and store them in the array
for (const xPos of xPositions) {
for (const yPos of yPositions) {
for (const zPos of zPositions) {
// Define sphere creation options
const sphereOptions = new SphereDto();
sphereOptions.radius = sphereRadius;
sphereOptions.center = [xPos, yPos, zPos];
// Create sphere and add to array
const sphere = await solid.createSphere(sphereOptions);
spheres.push(sphere);
}
}
}
// Create compound from all spheres
const compoundOptions = new CompoundShapesDto<TopoDSSolidPointer>();
compoundOptions.shapes = spheres;
const sphereCompound = await compound.makeCompound(compoundOptions);
// Draw compound
bitbybit.draw.drawAnyAsync({
entity: sphereCompound
});
}
// Execute the function
start();
Unified Transformations: Moving Compounds as Single Entities
Another major advantage of compounds is the ability to transform the entire group as a single entity. When you rotate a compound, all constituent shapes move together in perfect synchronization. This eliminates the complexity of managing individual transformations for each shape and demonstrates how compounds maintain their internal relationships during transformations.
- Rete
- Blockly
- TypeScript
{
"id": "rete-v2-json",
"nodes": {
"b446c4e9ffd24726": {
"id": "b446c4e9ffd24726",
"name": "bitbybit.vector.span",
"customName": "span",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"step": 2.5,
"min": -10,
"max": 1
},
"inputs": {
"min": {
"connections": [
{
"node": "f88fd357ced411f1",
"output": "result",
"data": {}
}
]
},
"max": {
"connections": [
{
"node": "31426ff201881699",
"output": "result",
"data": {}
}
]
}
},
"position": [
1033.7463569641113,
513.0007019042969
]
},
"31426ff201881699": {
"id": "31426ff201881699",
"name": "bitbybit.math.numberSlider",
"customName": "number slider",
"data": {
"options": {
"min": 2,
"max": 10,
"step": 1,
"width": 350,
"updateOnDrag": false
},
"number": 5
},
"inputs": {},
"position": [
-58.33047223289259,
628.7886361749063
]
},
"f88fd357ced411f1": {
"id": "f88fd357ced411f1",
"name": "bitbybit.math.negate",
"customName": "negate",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"number": 1
},
"inputs": {
"number": {
"connections": [
{
"node": "31426ff201881699",
"output": "result",
"data": {}
}
]
}
},
"position": [
546.3032722473145,
366.40267944382504
]
},
"6c01c1c47ff8a874": {
"id": "6c01c1c47ff8a874",
"name": "bitbybit.point.pointXYZ",
"customName": "point xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0,
"z": 0
},
"inputs": {
"x": {
"connections": [
{
"node": "bacbbb6931b036ab",
"output": "result",
"data": {}
}
]
},
"y": {
"connections": [
{
"node": "bacbbb6931b036ab",
"output": "result",
"data": {}
}
]
},
"z": {
"connections": [
{
"node": "bacbbb6931b036ab",
"output": "result",
"data": {}
}
]
}
},
"position": [
1771.9867359310028,
470.02919755656137
]
},
"bacbbb6931b036ab": {
"id": "bacbbb6931b036ab",
"name": "bitbybit.lists.flatten",
"customName": "flatten",
"data": {
"nrLevels": 1
},
"inputs": {
"list": {
"connections": [
{
"node": "b446c4e9ffd24726",
"output": "result",
"data": {}
}
]
}
},
"position": [
1402.6814793938981,
548.4041387968635
]
},
"859b8e895567d5fd": {
"id": "859b8e895567d5fd",
"name": "bitbybit.occt.shapes.solid.createSphere",
"customName": "sphere",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1,
"center": [
0,
0,
0
]
},
"inputs": {
"center": {
"connections": [
{
"node": "6c01c1c47ff8a874",
"output": "result",
"data": {}
}
]
}
},
"position": [
2142.757180037668,
427.66801356001173
]
},
"0e366b3b840415bd": {
"id": "0e366b3b840415bd",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "859b8e895567d5fd",
"output": "result",
"data": {}
}
]
}
},
"position": [
2517.4087816268816,
468.10613836610617
]
},
"1c25083cf4fbd5d8": {
"id": "1c25083cf4fbd5d8",
"name": "bitbybit.occt.shapes.compound.makeCompound",
"customName": "make compound",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shapes": {
"connections": [
{
"node": "0e366b3b840415bd",
"output": "list",
"data": {}
}
]
}
},
"position": [
2897.9006903391196,
427.5889225632506
]
},
"03199bba6f897964": {
"id": "03199bba6f897964",
"name": "bitbybit.occt.transforms.rotate",
"customName": "rotate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"axis": [
0,
0,
1
],
"angle": 45
},
"inputs": {
"shape": {
"connections": [
{
"node": "1c25083cf4fbd5d8",
"output": "result",
"data": {}
}
]
}
},
"position": [
3290.919250683614,
427.358216136201
]
}
}
}
<xml xmlns="https://developers.google.com/blockly/xml"><variables><variable id="`1z$H~OFP(!H<V+Z-pKB">gridSize</variable><variable id=")k=XMIcv%#l`%T-$7T![">sphereRadius</variable><variable id=":n:ZQ!`gJW~i7j?tqY$D">spacing</variable><variable id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</variable><variable id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</variable><variable id="f3u{`MGx/_;nrJ{HQ)Vv">yPositions</variable><variable id="LsA=SzqGD@VfxV^t@,Pt">zPositions</variable><variable id="^[yZXg_VCL;}e3C?J2HZ">spheres</variable><variable id="_@iM|?pHy@h]z{cQ!<|V">x</variable><variable id="^sH`@x{#BZMPwi5wkZDl">y</variable><variable id="$w0uK_j5}e*hWR.e#J%T">compound</variable><variable id="l[Y?:G{pVGY!Y:K+N3O[">z</variable><variable id="~8,HE3]mwPV7/Xp$fA/U">rotatedCompound</variable></variables><block type="variables_set" id="$3)Ai-IM4U6,PsVL?K8s" x="-179" y="-214"><field name="VAR" id="`1z$H~OFP(!H<V+Z-pKB">gridSize</field><value name="VALUE"><block type="math_number" id="8IwD^]vU#OI}m!N;^@M$"><field name="NUM">5</field></block></value><next><block type="variables_set" id="Vv6?y]d~D/r/@HLgm1Zf"><field name="VAR" id=")k=XMIcv%#l`%T-$7T![">sphereRadius</field><value name="VALUE"><block type="math_number" id="_o:;w%c0K@*[U]:nBk7$"><field name="NUM">1</field></block></value><next><block type="variables_set" id="|+F;Vdr+g72pEMKK7]Gn"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field><value name="VALUE"><block type="math_number" id="rO[Rl|j{Y~@D1!c.]N#k"><field name="NUM">2.5</field></block></value><next><block type="variables_set" id="j*p9GaGLR]LN(||8d]+E"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field><value name="VALUE"><block type="math_arithmetic" id="oJb/q(G+h;#qnA1WZP4I"><field name="OP">DIVIDE</field><value name="A"><block type="math_arithmetic" id="j@_wHe4[xz3Q%Uw8Q!}p"><field name="OP">MULTIPLY</field><value name="A"><block type="variables_get" id="8;K6p?PGQo%A3#nf4#*y"><field name="VAR" id="`1z$H~OFP(!H<V+Z-pKB">gridSize</field></block></value><value name="B"><block type="variables_get" id="n5qjfOm=VEjTfgCq%qM<"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value></block></value><value name="B"><block type="math_number" id="rR[vAJBOm7.f4MU(|zPD"><field name="NUM">2</field></block></value></block></value><next><block type="variables_set" id="qD|l#M)KjFH:SfSLQp~b"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="5NbS7|.{Kt)uc=a4;e1f"><value name="Step"><block type="variables_get" id="FrWGfn6Y,]NZ%7{!7Q#c"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="OAcQ/[GF+)TwJT@NpT1D"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="o4a6PZ*5gKP:B1n;Yps{"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id=":pMfpZ5<4~cQ<#4r/F~e"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id="6N}4V:^[_f*VqpLR.D||"><field name="VAR" id="f3u{`MGx/_;nrJ{HQ)Vv">yPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="_-p(4tqQ$4DKo=g};d$["><value name="Step"><block type="variables_get" id="14b%^jn9:^IZO*oZ3^gk"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="m`1v{_J`$1ADhDj)ikbD"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="+e@+JeL9IzdvI+=U6:}Z"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id="Y0=m;gP]miUdAGaWL%}I"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id=":Y$|_jOLGD/nPkSG[j%{"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field><value name="VALUE"><block type="bitbybit.vector.span" id="JWUfp@v)1?h,L{lL0VVt"><value name="Step"><block type="variables_get" id="i,9I@j[H:`kkY(;T)6lN"><field name="VAR" id=":n:ZQ!`gJW~i7j?tqY$D">spacing</field></block></value><value name="Min"><block type="math_single" id="v8%|j)uDpj8<x.2XVhOL"><field name="OP">NEG</field><value name="NUM"><block type="variables_get" id="%wCnN7P1$2f>K@pO+X)g"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><value name="Max"><block type="variables_get" id="oHGi/p>Yz~a[dFq-vr#)"><field name="VAR" id="9h$|Zd5s~oK%j}#h{fxs">halfGrid</field></block></value></block></value><next><block type="variables_set" id="ZD4Z/|hKO$?<~#5<M=U]"><field name="VAR" id="^[yZXg_VCL;}e3C?J2HZ">spheres</field><value name="VALUE"><block type="lists_create_with" id="Lq%DLh@>`qBxK^DQqRFt"><mutation items="0"></mutation></block></value><next><block type="controls_for" id="gHNF^1;1#(|H96$SfOUF"><field name="VAR" id="_@iM|?pHy@h]z{cQ!<|V">x</field><value name="FROM"><block type="math_number" id="[Pq@#=w5C,r,;6T#q%<{"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="<D%+3}uJOr~Y!uy0GXgJ"><value name="VALUE"><block type="variables_get" id="BPAyYm2eD(d*/c>*A7DT"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="YpRHU-j+L{<BPBHBp+w)"><field name="NUM">1</field></block></value><statement name="DO"><block type="controls_for" id="QGNj]!$;TE4u~g5r]<M@"><field name="VAR" id="^sH`@x{#BZMPwi5wkZDl">y</field><value name="FROM"><block type="math_number" id="D[9>k7F.{%i:W<|xp;:;"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="x(qG=2}I@=({$FKk<L7`"><value name="VALUE"><block type="variables_get" id="9Ng/yKvYqeC?i`jOF~y6"><field name="VAR" id="f3u{`MGx/_;nrJ{HQ)Vv">yPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="fIjM`Pqg<+m?m_WC|xYp"><field name="NUM">1</field></block></value><statement name="DO"><block type="controls_for" id="yR_cV%C_^ueS:r#RKW@m"><field name="VAR" id="l[Y?:G{pVGY!Y:K+N3O[">z</field><value name="FROM"><block type="math_number" id="__;,,TN8D9m{GV$@@.vy"><field name="NUM">1</field></block></value><value name="TO"><block type="lists_length" id="I/cz}B}5][,bL;FeM4xV"><value name="VALUE"><block type="variables_get" id="{6-K%720vj#d#~2hSr@o"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field></block></value></block></value><value name="BY"><block type="math_number" id="2yt0OxiD{]Nz4=qMP^]D"><field name="NUM">1</field></block></value><statement name="DO"><block type="lists_setIndex" id=")/#soBq=Z8hBN=;f9LbP"><mutation at="false"></mutation><field name="MODE">INSERT</field><field name="WHERE">LAST</field><value name="LIST"><block type="variables_get" id="m2Ti#]hfC0cBU+a_8VKL"><field name="VAR" id="^[yZXg_VCL;}e3C?J2HZ">spheres</field></block></value><value name="TO"><block type="bitbybit.occt.shapes.solid.createSphere" id="!~^LRKj+LFN_Sv$^zkf^"><value name="Radius"><block type="variables_get" id="#KyF}c=|fugDi_@g%2?i"><field name="VAR" id=")k=XMIcv%#l`%T-$7T![">sphereRadius</field></block></value><value name="Center"><block type="lists_create_with" id="ts]#y:D.P8ZczIgCTt61"><mutation items="3"></mutation><value name="ADD0"><block type="lists_getIndex" id="TWU!+srn7IF/3x+LCM*f"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="qD2)KGmF(O6iI.[*hxtG"><field name="VAR" id="XaGTZ9qR7lIR}ESFQ@o*">xPositions</field></block></value><value name="AT"><block type="variables_get" id="Zxh||0IKn0`qo;;YDl9Q"><field name="VAR" id="_@iM|?pHy@h]z{cQ!<|V">x</field></block></value></block></value><value name="ADD1"><block type="lists_getIndex" id="IIOrUZl|7zVpL2ql{Y-]"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="VyP--Tdl?2e`L+Z(b%4B"><field name="VAR" id="f3u{`MGx/_;nrJ{HQ)Vv">yPositions</field></block></value><value name="AT"><block type="variables_get" id="[evQf-B4(^WLCvarh-?p"><field name="VAR" id="^sH`@x{#BZMPwi5wkZDl">y</field></block></value></block></value><value name="ADD2"><block type="lists_getIndex" id="O9pFFsD-ptM}G=vrn#50"><mutation statement="false" at="true"></mutation><field name="MODE">GET</field><field name="WHERE">FROM_START</field><value name="VALUE"><block type="variables_get" id="lwzhYnnc8-P!~W?O21)3"><field name="VAR" id="LsA=SzqGD@VfxV^t@,Pt">zPositions</field></block></value><value name="AT"><block type="variables_get" id="7o@,a,PB_ZI|]n~ut6]?"><field name="VAR" id="l[Y?:G{pVGY!Y:K+N3O[">z</field></block></value></block></value></block></value></block></value></block></statement></block></statement></block></statement><next><block type="variables_set" id="KQUJn_D`*w?@d~uq%FIl"><field name="VAR" id="$w0uK_j5}e*hWR.e#J%T">compound</field><value name="VALUE"><block type="bitbybit.occt.shapes.compound.makeCompound" id="l{]Zj>:FwY@}K[%C`6Kd"><value name="Shapes"><block type="variables_get" id="vQjkJ`X%P:w:!}1rZ`^m"><field name="VAR" id="^[yZXg_VCL;}e3C?J2HZ">spheres</field></block></value></block></value><next><block type="variables_set" id="8:Z_%MJo{v!{;I[TZVd2"><field name="VAR" id="~8,HE3]mwPV7/Xp$fA/U">rotatedCompound</field><value name="VALUE"><block type="bitbybit.occt.transforms.rotate" id="aS6MWlm*l@K%D2qLEfGl"><value name="Shape"><block type="variables_get" id="eL>$+SYKl0Rg^IbYiPTg"><field name="VAR" id="$w0uK_j5}e*hWR.e#J%T">compound</field></block></value><value name="Axis"><block type="bitbybit.vector.vectorXYZ" id="5jG5O<+)o{2kqM{GFhZC"><value name="X"><block type="math_number" id="qY]M*qJAzXY%IyV:7%j["><field name="NUM">0</field></block></value><value name="Y"><block type="math_number" id="b<I4Hd.{@8vvFjm5t]/d"><field name="NUM">0</field></block></value><value name="Z"><block type="math_number" id="DhST+$Hc2q@wh)ZSa|3}"><field name="NUM">1</field></block></value></block></value><value name="Angle"><block type="math_number" id="dBq~sI6|OIqtZS}wE!Vv"><field name="NUM">45</field></block></value></block></value><next><block type="bitbybit.draw.drawAnyAsyncNoReturn" id="E1r!Td=%`8)NKx/sSq]d"><value name="Entity"><block type="variables_get" id="Ul$3y@vMWF%?1m*9BfKw"><field name="VAR" id="~8,HE3]mwPV7/Xp$fA/U">rotatedCompound</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></xml>
// Import required DTOs for creating spheres, compounds, and transformations
const { SphereDto, CompoundShapesDto, RotateDto, TranslateDto } = Bit.Inputs.OCCT;
// Import type definitions for type safety
type Point3 = Bit.Inputs.Base.Point3;
type Vector3 = Bit.Inputs.Base.Vector3;
type TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;
type TopoDSCompoundPointer = Bit.Inputs.OCCT.TopoDSCompoundPointer;
// Get access to OCCT modules and utility functions
const { solid, compound } = bitbybit.occt.shapes;
const { transforms } = bitbybit.occt;
const { vector } = bitbybit;
// Define the main function to create and transform a compound (unified transformation)
const start = async () => {
// Grid parameters
const gridSize = 5; // Number of spheres per side
const sphereRadius = 1; // Radius of each sphere
const spacing = 2.5; // Distance between sphere centers
// Calculate grid boundaries
const halfGrid = (gridSize * spacing) / 2;
// Generate grid positions using span functions
const xPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
const yPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
const zPositions = vector.span({
min: -halfGrid,
max: halfGrid,
step: spacing
});
// Create array to store all spheres before compounding
const spheres: TopoDSSolidPointer[] = [];
// Create all spheres first and store them in the array
for (const xPos of xPositions) {
for (const yPos of yPositions) {
for (const zPos of zPositions) {
// Define sphere creation options
const sphereOptions = new SphereDto();
sphereOptions.radius = sphereRadius;
sphereOptions.center = [xPos, yPos, zPos];
// Create sphere and add to array
const sphere = await solid.createSphere(sphereOptions);
spheres.push(sphere);
}
}
}
// Create compound from all spheres
const compoundOptions = new CompoundShapesDto<TopoDSSolidPointer>();
compoundOptions.shapes = spheres;
const sphereCompound = await compound.makeCompound(compoundOptions);
// Transform the entire compound as a single entity
// First, rotate the compound around the Z-axis
const rotateOptions = new RotateDto<TopoDSCompoundPointer>();
rotateOptions.shape = sphereCompound;
rotateOptions.axis = [0, 0, 1]; // Z-axis rotation
rotateOptions.angle = 45; // 45 degrees
const rotatedCompound = await transforms.rotate(rotateOptions);
// Draw the transformed compound
// All spheres move together as a single unified entity
bitbybit.draw.drawAnyAsync({
entity: rotatedCompound
});
}
// Execute the function
start();
Applications and Best Practices
Compounds are particularly valuable in several scenarios:
Assembly Management: When working with mechanical assemblies, architectural models, or any complex structure with multiple components, compounds provide a natural hierarchy for organization and manipulation.
Performance Optimization: For applications requiring real-time interaction with many objects, compounds dramatically reduce the computational overhead of rendering and transforming multiple shapes.
Parametric Design: In parametric workflows where you need to generate multiple variations of pattern-based designs, compounds allow you to treat complex patterns as single entities for easy modification.
Animation and Simulation: When animating multiple objects that need to move together, compounds ensure perfect synchronization without the complexity of coordinating individual transformations.
When implementing compounds in your projects, consider grouping logically related shapes, maintaining reasonable compound sizes for optimal performance, and using nested compounds for hierarchical organization when dealing with very complex assemblies. The examples above demonstrate the fundamental patterns that scale effectively from simple arrays to complex industrial applications.



