diff --git a/README.md b/README.md index 73ae004c..f5203a8c 100644 --- a/README.md +++ b/README.md @@ -87,5 +87,59 @@ Bit By Bit Developers company will keep these core algorithms that you can find ## About Bit By Bit Developers platform Bit By Bit Developers web platform allows creators to program geometry through simple visual programming language or choose monaco typescript editor with full intellisense of bitbybit API. This cloud platform can fulfil many practical, educational and artistic needs of its users. Through familiar programming interface used in tools such as Scratch and Blockly.Games we expose powerful 3D algorithms that make it easier to implement various parametric tasks. Our goal is to make it very simple for users to share their ideas and designs. We want to encourage everyone to engage in the future of this tool. +# Development Setup + +## First Time Setup and Testing + +For first-time developers working on this project, follow these steps to set up the development environment and run all unit tests: + +### Prerequisites +- Node.js (v16 or higher recommended) +- npm (comes with Node.js) +- Git + +### Quick Start +1. Clone the repository: + ```bash + git clone https://github.com/bitbybit-dev/bitbybit.git + cd bitbybit + ``` + +2. Run the complete first-time setup (this will install all dependencies, build all packages, and run all unit tests): + ```bash + npm run first-time-setup + ``` + +### Available Commands + +- `npm run first-time-setup` - Complete setup for new developers (installs dependencies, builds packages, runs tests) +- `npm run setup` - Install dependencies and build all packages without running tests +- `npm run setup-and-test` - Install dependencies, build packages, and run all unit tests +- `npm run test` - Run all unit tests (requires packages to be built first) +- `npm run ci-packages` - Install dependencies for all packages +- `npm run build-packages` - Build all packages +- `npm run rebuild-all-packages` - Clean and rebuild all packages + +### Cross-Platform Compatibility +All commands are now cross-platform compatible and work on Windows, macOS, and Linux. The project uses: +- `rimraf` for cross-platform file deletion +- Standard npm scripts for package management + +### Running Individual Package Tests +You can also run tests for individual packages: +- `npm run test-base` - Test base package +- `npm run test-occt` - Test OCCT package +- `npm run test-core` - Test core package +- `npm run test-jscad` - Test JSCAD package +- `npm run test-manifold` - Test Manifold package +- `npm run test-threejs` - Test ThreeJS package + +### Troubleshooting +If you encounter issues during setup: +1. Make sure you have Node.js v16+ installed +2. Clear npm cache: `npm cache clean --force` +3. Delete node_modules and package-lock.json, then run `npm install` +4. If on Windows, make sure to run commands in a proper terminal (Command Prompt, PowerShell, or WSL) + ## Major Dependencies BabylonJS, ThreeJS, OpenCascade, Manifold, JSCAD, Verbnurbs \ No newline at end of file diff --git a/docs/learn/code/common/occt/modeling/parametric-art/_category_.json b/docs/learn/code/common/occt/modeling/parametric-art/_category_.json new file mode 100644 index 00000000..547df5a3 --- /dev/null +++ b/docs/learn/code/common/occt/modeling/parametric-art/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Parametric Art", + "position": 2, + "link": { + "type": "generated-index", + "title": "Parametric Art", + "description": "Explore the world of parametric art and learn how to create stunning designs using algorithms.", + "slug": "/code/common/occt/modeling/parametric-art" + } +} \ No newline at end of file diff --git a/docs/learn/code/common/occt/modeling/parametric-art/simple-flower.md b/docs/learn/code/common/occt/modeling/parametric-art/simple-flower.md new file mode 100644 index 00000000..69b87c7f --- /dev/null +++ b/docs/learn/code/common/occt/modeling/parametric-art/simple-flower.md @@ -0,0 +1,114 @@ +--- +sidebar_position: 1 +title: Simple Flower +sidebar_label: Simple Flower +description: Create simple flower +tags: [code, occt, rete, blockly, typescript] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; + +OCCT category icon with a stylized logo representation + +Creating organic, flower-like forms demonstrates the power of parametric design in generating natural-looking geometry through mathematical operations. This tutorial explores how to build a simple flower using curve interpolation, mirroring, and rotational patterns—fundamental techniques that can be applied to create complex botanical forms, decorative elements, and artistic sculptures. + +The flower design process combines several key concepts: defining control points for smooth curves, using symmetry operations to create balanced shapes, and employing rotational arrays to generate repetitive patterns. These techniques are essential for parametric art, architectural ornamentation, and procedural modeling applications. + +## Understanding the Flower Construction Process + +Our approach breaks down flower creation into logical steps that mirror how natural flowers develop. We start with a single petal defined by a curved path, create symmetry through mirroring, add thickness to make it solid, then rotate copies around a central axis to form the complete flower. + +This method is highly parametric—by adjusting the control points, rotation angles, or thickness values, you can quickly generate different flower variations. The same principles apply to creating other organic forms like leaves, shells, or abstract sculptural elements. + + + + + + + controlPointscurvemirrorNormalmirroredCurvecombinedWiresflowerFacethickFlowerrotationAxisrotationAnglesflowerPetalsanglerotatedPetalfinalFlowercontrolPoints00020.531.1-0.570010curvecontrolPointsFALSE1e-7mirrorNormal100mirroredCurvecurve000mirrorNormalcombinedWirescurvemirroredCurveflowerFacecombinedWiresFALSEthickFlowerflowerFace0.1rotationAxis220rotationAngles300360flowerPetalsangle1rotationAngles1rotatedPetalthickFlowerrotationAxisGETFROM_STARTrotationAnglesangleINSERTLASTflowerPetalsrotatedPetalfinalFlowerflowerPetalsfinalFlower0.01Custom Material#14ffa5#0000000.60.51FALSE2TRUE#0000002-100-100-1003#ffffff#ffffff1024TRUE0TRUE0.20.00010.00210000#090a0b#a4f9ef'to top'0100","version":"0.20.7","type":"blockly"}} + title="Simple flower" + /> + + + {\n const backgroundOpt = new SceneTwoColorLinearGradientDto();\n backgroundOpt.colorFrom = \"#090a0b\";\n backgroundOpt.colorTo = \"#a4f9ef\";\n backgroundOpt.direction = gradientDirectionEnum.toTop;\n scene.twoColorLinearGradient(backgroundOpt);\n\n const dirLightOpt = new DirectionalLightDto();\n dirLightOpt.intensity = 3;\n scene.drawDirectionalLight(dirLightOpt);\n}\n\n// Define the main function to create a parametric flower\nconst start = async () => {\n createEnvironment();\n // Flower parameters - easily adjustable for different designs\n const thickness = 0.1; // Thickness of the flower petals\n const rotationStep = 30; // Degrees between each petal (12 petals total)\n const rotationAxis: Vector3 = [2, 2, 0]; // Axis for petal rotation\n\n // Define control points for the flower petal curve\n const controlPoints: Point3[] = [\n [0, 0, 0], // Start point (flower center)\n [2, 0.5, 3], // First control point (petal width)\n [1.1, -0.5, 7], // Second control point (petal curve)\n [0, 0, 10] // End point (petal tip)\n ];\n\n // Create interpolated curve through control points\n const curveOptions = new InterpolationDto();\n curveOptions.points = controlPoints;\n curveOptions.periodic = false;\n curveOptions.tolerance = 1e-7;\n const petalCurve = await wire.interpolatePoints(curveOptions);\n\n // Mirror the curve to create symmetry for the petal\n const mirrorOptions = new MirrorAlongNormalDto();\n mirrorOptions.shape = petalCurve;\n mirrorOptions.origin = [0, 0, 0];\n mirrorOptions.normal = [1, 0, 0]; // Mirror along X-axis\n const mirroredCurve = await transforms.mirrorAlongNormal(mirrorOptions);\n\n // Combine the original and mirrored curves into a single wire\n const combineOptions = new ShapesDto();\n combineOptions.shapes = [petalCurve, mirroredCurve];\n const combinedWire = await wire.combineEdgesAndWiresIntoAWire(combineOptions);\n\n // Create a face from the combined wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = combinedWire;\n faceOptions.planar = false; // Allow non-planar surface\n const petalFace = await face.createFaceFromWire(faceOptions);\n\n // Create a thick solid from the face\n const thickOptions = new ThisckSolidSimpleDto();\n thickOptions.shape = petalFace;\n thickOptions.offset = thickness;\n const thickPetal = await operations.makeThickSolidSimple(thickOptions);\n\n // Generate rotation angles for petals (0° to 360° in steps)\n const rotationAngles = vector.span({\n min: 0,\n max: 360,\n step: rotationStep\n });\n\n // Create all flower petals by rotating the base petal\n const flowerPetalPromises: Promise[] = [];\n for (const angle of rotationAngles) {\n const rotateOptions = new RotateDto();\n rotateOptions.shape = thickPetal;\n rotateOptions.axis = rotationAxis;\n rotateOptions.angle = angle;\n\n const rotatedPetal = transforms.rotate(rotateOptions);\n flowerPetalPromises.push(rotatedPetal);\n }\n\n const flowerPetals = await Promise.all(flowerPetalPromises);\n\n // Combine all petals into a single compound shape\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = flowerPetals;\n const flower = await compound.makeCompound(compoundOptions);\n\n const pbrOptions = new Bit.Inputs.BabylonMaterial.PBRMetallicRoughnessDto();\n pbrOptions.baseColor = \"#14ffa5\";\n pbrOptions.metallic = 0.6;\n pbrOptions.roughness = 0.6;\n pbrOptions.zOffset = 2;\n const pbrMaterial = pbrMetallicRoughness.create(pbrOptions);\n\n const drawOpt = new Bit.Inputs.Draw.DrawOcctShapeMaterialOptions();\n drawOpt.faceMaterial = pbrMaterial;\n drawOpt.edgeColour = \"#000000\";\n\n // Draw the completed flower with material options\n bitbybit.draw.drawAnyAsync({\n entity: flower,\n options: drawOpt\n });\n}\n\n// Execute the flower creation function\nstart();","version":"0.20.7","type":"typescript"}} + title="Simple flower" + /> + + + +## Code Explanation + +The flower creation process follows a systematic approach that demonstrates several important geometric modeling concepts: + +### **Petal Shape Definition** +The flower begins with four carefully chosen control points that define the basic petal profile: +- Start point `[0, 0, 0]` at the flower center +- Intermediate points `[2, 0.5, 3]` and `[1.1, -0.5, 7]` that control petal width and curvature +- End point `[0, 0, 10]` at the petal tip + +These points are connected using `interpolatePoints`, which creates a smooth B-spline curve through the control points. The `periodic: false` setting ensures the curve doesn't loop back on itself, while the small tolerance value `1e-7` ensures high precision in the curve calculation. + +### **Symmetry Through Mirroring** +To create a realistic petal shape, the curve is mirrored along the X-axis using `mirrorAlongNormal`. This creates a symmetrical petal with both sides having identical but opposite curvature. The mirroring operation is fundamental in creating balanced, natural-looking organic forms. + +### **Wire Combination and Solid Creation** +The original and mirrored curves are combined using `combineEdgesAndWiresIntoAWire` to form a closed profile. This closed wire is then converted to a face using `createFaceFromWire`, and finally given thickness using `makeThickSolidSimple` with a 0.1 unit offset. + +### **Rotational Pattern Generation** +The complete flower emerges through rotational multiplication. The `vector.span` function generates 12 evenly spaced angles from 0° to 360° in 30° increments. Each petal is rotated around the axis `[2, 2, 0]`, which creates a slight tilt that adds visual interest to the final form. + +### **Implementation Differences** + +**Rete Version**: Uses visual node connections and automatic list flattening to handle the rotation angles. The visual interface makes it easy to see data flow and adjust parameters through node properties. + +**Blockly Version**: Employs standard programming loops since `flatten` operations aren't available. Variables and list management follow traditional programming patterns, making it accessible to users familiar with block-based coding. + +**TypeScript Version**: Provides full type safety with proper DTO usage and async/await patterns. This version is most suitable for production environments and offers the best development experience with IntelliSense and error checking. + +## Advanced Interactive Enhancement + +The example below demonstrates how to extend static geometric models with dynamic visual effects. By combining the flower geometry with a sophisticated particle system, we create an immersive interactive experience where thousands of particles emit from the flower surface and respond to mouse movement with realistic physics. + +This advanced implementation showcases several cutting-edge techniques: + +### **Surface-Based Particle Emission** +Rather than emitting particles from simple points or volumes, this system calculates the surface area of each triangle in the flower mesh and distributes particles proportionally. This creates a natural, organic emission pattern that follows the flower's complex geometry perfectly. + +### **Custom Physics Simulation** +The particle system implements realistic physics including: +- **Gravity Effects**: Particles fall naturally with customizable gravitational pull +- **Drag Forces**: Air resistance slows particles over time for realistic motion +- **Mouse Repulsion**: Interactive forces push particles away from cursor position +- **Drift Behavior**: Some particles follow random drift patterns for organic movement + +### **Multi-Layered Visual Design** +The flower uses multiple overlapping layers rotated at different angles to create depth and visual richness. Combined with translucent materials and additive blending, this produces an ethereal, luminous effect. + +### **Performance Optimization** +Despite managing up to 25,000 particles simultaneously, the system maintains smooth performance through efficient update functions and memory management techniques. + +**Move your mouse over the flower to interact with the particle field:** + + + + {\n const dirLightOpt = new DirectionalLightDto();\n dirLightOpt.intensity = 3;\n scene.drawDirectionalLight(dirLightOpt);\n}\n\n// Define the main function to create a parametric flower\nconst start = async () => {\n createEnvironment();\n // Flower parameters - easily adjustable for different designs\n const thickness = 0.1; // Thickness of the flower petals\n const rotationStep = 60; // Degrees between each petal (12 petals total)\n const rotationAxis: Vector3 = [2, 2, 0]; // Axis for petal rotation\n\n // Define control points for the flower petal curve\n const controlPoints: Point3[] = [\n [0, 0, 0], // Start point (flower center)\n [5, 1.25, 7.5], // First control point (petal width)\n [2.75, -1.25, 17.5], // Second control point (petal curve)\n [0, 0, 25] // End point (petal tip)\n ];\n\n // Create interpolated curve through control points\n const curveOptions = new InterpolationDto();\n curveOptions.points = controlPoints;\n curveOptions.periodic = false;\n curveOptions.tolerance = 1e-7;\n const petalCurve = await wire.interpolatePoints(curveOptions);\n\n // Mirror the curve to create symmetry for the petal\n const mirrorOptions = new MirrorAlongNormalDto();\n mirrorOptions.shape = petalCurve;\n mirrorOptions.origin = [0, 0, 0];\n mirrorOptions.normal = [1, 0, 0]; // Mirror along X-axis\n const mirroredCurve = await transforms.mirrorAlongNormal(mirrorOptions);\n\n // Combine the original and mirrored curves into a single wire\n const combineOptions = new ShapesDto();\n combineOptions.shapes = [petalCurve, mirroredCurve];\n const combinedWire = await wire.combineEdgesAndWiresIntoAWire(combineOptions);\n\n // Create a face from the combined wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = combinedWire;\n faceOptions.planar = false; // Allow non-planar surface\n const petalFace = await face.createFaceFromWire(faceOptions);\n\n // Create a thick solid from the face\n const thickOptions = new ThisckSolidSimpleDto();\n thickOptions.shape = petalFace;\n thickOptions.offset = thickness;\n const thickPetal = await operations.makeThickSolidSimple(thickOptions);\n\n // Generate rotation angles for petals (0° to 360° in steps)\n const rotationAngles = vector.span({\n min: 0,\n max: 360,\n step: rotationStep\n });\n\n const rotationAngles2 = vector.span({\n min: 30,\n max: 360,\n step: rotationStep\n });\n\n // Create all flower petals by rotating the base petal\n const flowerPetalPromises: Promise[] = [];\n const flowerPetalPromises2: Promise[] = [];\n\n for (const angle of rotationAngles) {\n const rotateOptions = new RotateDto();\n rotateOptions.shape = thickPetal;\n rotateOptions.axis = rotationAxis;\n rotateOptions.angle = angle;\n\n const rotatedPetal = transforms.rotate(rotateOptions);\n flowerPetalPromises.push(rotatedPetal);\n }\n\n for (const angle of rotationAngles2) {\n const rotateOptions = new RotateDto();\n rotateOptions.shape = thickPetal;\n rotateOptions.axis = rotationAxis;\n rotateOptions.angle = angle;\n\n const rotatedPetal = transforms.rotate(rotateOptions);\n flowerPetalPromises2.push(rotatedPetal);\n }\n\n const flowerPetals = await Promise.all(flowerPetalPromises);\n const flowerPetals2 = await Promise.all(flowerPetalPromises2);\n\n // Combine all petals into a single compound shape\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = flowerPetals;\n const flower = await compound.makeCompound(compoundOptions);\n compoundOptions.shapes = flowerPetals2;\n const flower2 = await compound.makeCompound(compoundOptions);\n\n const pbrOptions = new Bit.Inputs.BabylonMaterial.PBRMetallicRoughnessDto();\n pbrOptions.baseColor = \"#111111\";\n pbrOptions.metallic = 0.6;\n pbrOptions.roughness = 0.6;\n pbrOptions.alpha = 0.4;\n pbrOptions.zOffset = 2;\n const pbrMaterial = pbrMetallicRoughness.create(pbrOptions);\n\n const drawOpt = new Bit.Inputs.Draw.DrawOcctShapeMaterialOptions();\n drawOpt.faceMaterial = pbrMaterial;\n drawOpt.edgeColour = \"#ffffff\";\n drawOpt.drawEdges = true;\n drawOpt.precision = 0.1;\n\n // Draw the completed flower with material options\n const flowerMesh = await bitbybit.draw.drawAnyAsync({\n entity: flower,\n options: drawOpt\n });\n\n const flowerMesh2 = await bitbybit.draw.drawAnyAsync({\n entity: flower2,\n options: drawOpt\n });\n\n const emitterMesh = flowerMesh.getChildMeshes()[0] as BABYLON.Mesh;\n emitterMesh.isPickable = false;\n\n const emitterMesh2 = flowerMesh2.getChildMeshes()[0] as BABYLON.Mesh;\n emitterMesh2.isPickable = false;\n\n const scene = bitbybit.babylon.scene.getScene();\n\n const purpleStartColor = new BABYLON.Color4(0.7, 0.3, 1.0, 1.0);\n const purpleMidColor = new BABYLON.Color4(1.0, 0.4, 0.8, 1.0);\n\n const blueStartColor = new BABYLON.Color4(0.2, 0.7, 1.0, 1.0);\n const blueMidColor = new BABYLON.Color4(0.5, 0.8, 1.0, 1.0);\n\n // This object will be shared with both particle systems to track the mouse.\n const mouseTracker = { position: null as BABYLON.Vector3 | null };\n\n // Create all the 3D assets: emitters, particle systems, and the interaction plane.\n\n // Remove any old observable before adding a new one.\n if (scene.metadata && scene.metadata.observable) {\n scene.metadata.observable.remove();\n }\n\n // Centralized mouse interaction logic.\n const resObs = scene.onPointerObservable.add((pointerInfo) => {\n if (pointerInfo.type === BABYLON.PointerEventTypes.POINTERMOVE) {\n // We only check for hits against our single, invisible interaction plane.\n const pickInfo = scene.pick(scene.pointerX, scene.pointerY, (mesh) => mesh === emitterMesh);\n if (pickInfo.hit) {\n // If we hit the plane, update the shared tracker object's position.\n mouseTracker.position = pickInfo.pickedPoint;\n } else {\n // If the mouse is not over the plane, clear the position.\n mouseTracker.position = null;\n }\n }\n });\n\n if (scene.metadata) {\n scene.metadata.observable = resObs;\n } else {\n scene.metadata = { observable: resObs };\n }\n\n createParticleSystemForMesh(emitterMesh, scene, purpleStartColor, purpleMidColor, mouseTracker);\n createParticleSystemForMesh(emitterMesh2, scene, blueStartColor, blueMidColor, mouseTracker);\n\n}\n\n// Execute the flower creation function\nstart();\n\n\n// The core particle system definition.\nfunction createParticleSystemForMesh(\n emitterMesh: BABYLON.Mesh,\n scene: BABYLON.Scene,\n animStartColor: BABYLON.Color4,\n animMidColor: BABYLON.Color4,\n mouseTracker: { position: BABYLON.Vector3 | null }\n): BABYLON.ParticleSystem {\n\n const animEndColor = new BABYLON.Color4(0.1, 0.2, 0.8, 0.0);\n const DRIFTER_CHANCE = 0.07;\n const DRIFTER_SPEED = 0.4;\n\n const particleSystem = new BABYLON.ParticleSystem(\"particles_\" + emitterMesh.name, 25000, scene);\n particleSystem.particleTexture = new BABYLON.Texture(\"https://assets.babylonjs.com/textures/flare.png\", scene);\n particleSystem.emitter = emitterMesh;\n particleSystem.particleEmitterType = createUniformMeshParticleEmitter(emitterMesh);\n\n particleSystem.color1 = animEndColor.clone();\n particleSystem.color2 = animEndColor.clone();\n particleSystem.colorDead = animEndColor.clone();\n particleSystem.minSize = 0;\n particleSystem.maxSize = 0;\n particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;\n particleSystem.minLifeTime = 4.0;\n particleSystem.maxLifeTime = 8.0;\n particleSystem.emitRate = 2000;\n particleSystem.minEmitPower = 0.1;\n particleSystem.maxEmitPower = 0.5;\n particleSystem.gravity = new BABYLON.Vector3(0, -1.0, 0);\n (particleSystem as any).dragFactor = 0.97;\n\n particleSystem.updateFunction = function (particles) {\n const repulsionRadius = 20.0, repulsionStrength = 30;\n const scaledUpdateSpeed = this._scaledUpdateSpeed;\n const mousePickPosition = mouseTracker.position;\n const regularStartSize = 0.35, regularEndSize = 0.1;\n const largeStartSize = 0.8, largeEndSize = 0.2;\n\n for (let index = 0; index < particles.length; index++) {\n const particle = particles[index] as Particle;\n particle.age += scaledUpdateSpeed;\n if (particle.age >= particle.lifeTime) {\n particles.splice(index, 1); this._stockParticles.push(particle); index--; continue;\n }\n\n if (particle.age === scaledUpdateSpeed) {\n particle.isLarge = (Math.random() < 0.05);\n particle.isDrifter = (Math.random() < DRIFTER_CHANCE);\n if (particle.isDrifter) {\n const driftVector = new BABYLON.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);\n particle.driftDirection = driftVector.normalize();\n }\n }\n\n particle.direction.scaleInPlace(this.dragFactor);\n if (particle.isDrifter) {\n const driftForce = particle.driftDirection.scale(DRIFTER_SPEED * scaledUpdateSpeed);\n particle.direction.addInPlace(driftForce);\n } else {\n particle.direction.addInPlace(this.gravity.scale(scaledUpdateSpeed));\n }\n\n if (mousePickPosition) {\n const distance = BABYLON.Vector3.Distance(particle.position, mousePickPosition);\n if (distance < repulsionRadius) {\n const forceDirection = particle.position.subtract(mousePickPosition).normalize();\n const forceMagnitude = repulsionStrength * (1 - distance / repulsionRadius);\n const forceVector = forceDirection.scale(forceMagnitude * scaledUpdateSpeed);\n particle.direction.addInPlace(forceVector);\n }\n }\n\n particle.position.addInPlace(particle.direction.scale(scaledUpdateSpeed));\n\n const startSize = particle.isLarge ? largeStartSize : regularStartSize;\n const endSize = particle.isLarge ? largeEndSize : regularEndSize;\n const lifeRatio = particle.age / particle.lifeTime;\n const fadeInDuration = 0.1;\n\n if (lifeRatio < fadeInDuration) {\n const fadeInRatio = lifeRatio / fadeInDuration;\n particle.size = BABYLON.Scalar.Lerp(0, startSize, fadeInRatio);\n BABYLON.Color4.LerpToRef(animEndColor, animStartColor, fadeInRatio, particle.color);\n } else {\n const mainLifeRatio = (lifeRatio - fadeInDuration) / (1 - fadeInDuration);\n particle.size = BABYLON.Scalar.Lerp(startSize, endSize, mainLifeRatio);\n if (mainLifeRatio < 0.5) {\n BABYLON.Color4.LerpToRef(animStartColor, animMidColor, mainLifeRatio * 2, particle.color);\n } else {\n BABYLON.Color4.LerpToRef(animMidColor, animEndColor, (mainLifeRatio - 0.5) * 2, particle.color);\n }\n }\n }\n };\n\n particleSystem.start();\n return particleSystem;\n}\n\n// Creates a custom emitter to ensure particles are distributed evenly across a mesh surface.\nfunction createUniformMeshParticleEmitter(mesh: BABYLON.Mesh): BABYLON.CustomParticleEmitter {\n const positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);\n const indices = mesh.getIndices();\n const totalFaces = indices.length / 3;\n const cumulativeTriangleAreas: number[] = [];\n let totalArea = 0;\n const vA = new BABYLON.Vector3(), vB = new BABYLON.Vector3(), vC = new BABYLON.Vector3();\n const edge1 = new BABYLON.Vector3(), edge2 = new BABYLON.Vector3();\n\n for (let i = 0; i < totalFaces; i++) {\n const indexA = indices[i * 3], indexB = indices[i * 3 + 1], indexC = indices[i * 3 + 2];\n BABYLON.Vector3.FromArrayToRef(positions, indexA * 3, vA);\n BABYLON.Vector3.FromArrayToRef(positions, indexB * 3, vB);\n BABYLON.Vector3.FromArrayToRef(positions, indexC * 3, vC);\n vB.subtractToRef(vA, edge1);\n vC.subtractToRef(vA, edge2);\n const area = BABYLON.Vector3.Cross(edge1, edge2).length() * 0.5;\n totalArea += area;\n cumulativeTriangleAreas.push(totalArea);\n }\n\n for (let i = 0; i < totalFaces; i++) cumulativeTriangleAreas[i] /= totalArea;\n\n const customEmitter = new BABYLON.CustomParticleEmitter();\n customEmitter.particlePositionGenerator = (index, particle, out) => {\n const random = Math.random();\n let triangleIndex = 0;\n for (let i = 0; i < totalFaces; i++) if (random < cumulativeTriangleAreas[i]) { triangleIndex = i; break; }\n const iA = indices[triangleIndex * 3], iB = indices[triangleIndex * 3 + 1], iC = indices[triangleIndex * 3 + 2];\n BABYLON.Vector3.FromArrayToRef(positions, iA * 3, vA);\n BABYLON.Vector3.FromArrayToRef(positions, iB * 3, vB);\n BABYLON.Vector3.FromArrayToRef(positions, iC * 3, vC);\n let r1 = Math.random(), r2 = Math.random();\n if (r1 + r2 > 1) { r1 = 1 - r1; r2 = 1 - r2; }\n vB.subtractToRef(vA, edge1);\n vC.subtractToRef(vA, edge2);\n out.copyFrom(vA).addInPlace(edge1.scaleInPlace(r1)).addInPlace(edge2.scaleInPlace(r2));\n };\n customEmitter.particleDestinationGenerator = (index, particle, out) => {\n out.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);\n };\n return customEmitter;\n}\n","version":"0.20.7","type":"typescript"}} + title="Simple flower" + /> + + \ No newline at end of file diff --git a/docs/learn/code/common/occt/shapes/compound/_category_.json b/docs/learn/code/common/occt/shapes/compound/_category_.json index 94df59f4..3cf1616d 100644 --- a/docs/learn/code/common/occt/shapes/compound/_category_.json +++ b/docs/learn/code/common/occt/shapes/compound/_category_.json @@ -1,6 +1,6 @@ { "label": "Compound", - "position": 6, + "position": 7, "link": { "type": "generated-index", "title": "Compound Shapes", diff --git a/docs/learn/code/common/occt/shapes/face/_category_.json b/docs/learn/code/common/occt/shapes/face/_category_.json new file mode 100644 index 00000000..f23df6d5 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/face/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Face", + "position": 4, + "link": { + "type": "generated-index", + "title": "Faces", + "description": "Faces are one of the most fundamental pieces of geometry that you can create inside OCCT kernel. These tutorials will help you understand how to create and manipulate faces in your 3D models.", + "slug": "/code/common/occt/shapes/face" + } +} \ No newline at end of file diff --git a/docs/learn/code/common/occt/shapes/face/face-basic-primitives.md b/docs/learn/code/common/occt/shapes/face/face-basic-primitives.md new file mode 100644 index 00000000..a4677480 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/face/face-basic-primitives.md @@ -0,0 +1,51 @@ +--- +sidebar_position: 2 +title: Face Basic Primitives +sidebar_label: Face Basic Primitives +description: Learn how to create basic face primitives that define surfaces and areas +tags: [code, occt, rete, blockly, typescript] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; + +OCCT category icon with a stylized logo representation + +# Introduction to OCCT Face Basic Primitives + +Faces are two-dimensional surfaces that fill geometric boundaries to create areas in 3D space. While wires define outlines and curves, faces create actual surfaces that can be visualized with materials, analyzed for properties, and used to build 3D solids. + +Think of wires as the frame of a window, and faces as the glass that fills the frame. This tutorial demonstrates creating circle, square, rectangle, and ellipse faces using different programming approaches. + + + + + + + circleFacesquareFacerectangleFaceellipseFacecircleFace4000010squareFace3700010rectangleFace37-700010ellipseFace00-901013circleFacesquareFacerectangleFaceellipseFace","version":"0.20.7","type":"blockly"}} + title="Creating basic face primitives" + /> + + + {\n // Create a circle face\n const circleOptions = new CircleDto();\n circleOptions.radius = 4;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circleFace = await face.createCircleFace(circleOptions);\n\n // Create a square face at a different position\n const squareOptions = new SquareDto();\n squareOptions.size = 3;\n squareOptions.center = [7, 0, 0] as Point3;\n squareOptions.direction = [0, 1, 0] as Vector3;\n\n const squareFace = await face.createSquareFace(squareOptions);\n\n // Create a rectangle face\n const rectangleOptions = new RectangleDto();\n rectangleOptions.width = 3;\n rectangleOptions.length = 7;\n rectangleOptions.center = [-7, 0, 0] as Point3;\n rectangleOptions.direction = [0, 1, 0] as Vector3;\n\n const rectangleFace = await face.createRectangleFace(rectangleOptions);\n\n // Create an ellipse face\n const ellipseOptions = new EllipseDto();\n ellipseOptions.center = [0, 0, -9] as Point3;\n ellipseOptions.direction = [0, 1, 0] as Vector3;\n ellipseOptions.radiusMinor = 1;\n ellipseOptions.radiusMajor = 3;\n\n const ellipseFace = await face.createEllipseFace(ellipseOptions);\n\n // Draw all the created faces\n bitbybit.draw.drawAnyAsync({ entity: circleFace });\n bitbybit.draw.drawAnyAsync({ entity: squareFace });\n bitbybit.draw.drawAnyAsync({ entity: rectangleFace });\n bitbybit.draw.drawAnyAsync({ entity: ellipseFace });\n}\n\n// Execute the function\nstart();","version":"0.20.7","type":"typescript"}} + title="Creating basic face primitives" + /> + + + +Each face primitive requires basic parameters: a center point for positioning, a direction vector for orientation (typically [0,1,0] for horizontal faces), and size parameters like radius for circles or width/length for rectangles. The examples create four different face types at separate positions so you can see them clearly. + +These face primitives serve as building blocks for more complex modeling operations. You can extrude them into 3D solids, combine them with boolean operations, or use them as surfaces for analysis and visualization. They're commonly used in architectural modeling, manufacturing design, and any application where you need precise geometric surfaces. diff --git a/docs/learn/code/common/occt/shapes/face/face-from-points.md b/docs/learn/code/common/occt/shapes/face/face-from-points.md new file mode 100644 index 00000000..e2dd4be0 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/face/face-from-points.md @@ -0,0 +1,59 @@ +--- +sidebar_position: 3 +title: Face From Points +sidebar_label: Face From Points +description: Learn how to create face from the list of points +tags: [code, occt, rete, blockly, typescript] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; + +OCCT category icon with a stylized logo representation + +# Creating Face From The List Of Points + +Creating a face from a list of points is essential for building custom polygonal shapes in 3D modeling. This method allows you to define a face by specifying its boundary vertices in order, which is particularly useful for creating irregular shapes, architectural elements, or custom geometric forms that can't be easily generated with standard primitives. + +The polygon face operation automatically connects your points in sequence and creates a planar face that fills the enclosed area. Point ordering is crucial - the sequence determines both the shape and the orientation of the resulting face. + +This example demonstrates creating a quadrilateral face from four strategically placed points to form an asymmetric shape. + + + + + + + point1point2point3point4pointsListpolygonFacepoint1-30-8point2-303point3303point4500pointsListpoint1point2point3point4polygonFacepointsListpolygonFacepointsList","version":"0.20.7","type":"blockly"}} + title="Creating face from points" + /> + + + {\n // Define the four points that will form the polygon face\n const point1: Point3 = [-3, 0, -8];\n const point2: Point3 = [-3, 0, 3];\n const point3: Point3 = [3, 0, 3];\n const point4: Point3 = [5, 0, 0];\n\n // Create a list of points in the correct order\n const points: Point3[] = [point1, point2, point3, point4];\n\n // Create the polygon face options\n const polygonOptions = new PolygonDto();\n polygonOptions.points = points;\n\n // Create the polygon face from the points\n const polygonFace = await face.createPolygonFace(polygonOptions);\n\n // Draw the polygon face\n bitbybit.draw.drawAnyAsync({ entity: polygonFace });\n\n // Optionally, draw the points to visualize the vertices\n bitbybit.draw.drawAnyAsync({ entity: points });\n}\n\n// Execute the function\nstart();","version":"0.20.7","type":"typescript"}} + title="Creating face from points" + /> + + + +## Key Concepts + +**Point Ordering**: The sequence of points determines both the shape and orientation of the polygon face. Points should be ordered consistently (either clockwise or counter-clockwise) to ensure predictable face normals and proper rendering. + +**Polygon Face Creation**: The `createPolygonFace` function automatically connects the points in sequence and fills the enclosed area with a planar face. This is ideal for creating custom shapes that can't be generated with standard geometric primitives. + +**Coplanar Points**: For best results, all points should lie in the same plane. While OCCT can handle slightly non-coplanar points by fitting a best-fit plane, significant deviations may produce unexpected results. + +**Practical Applications**: Polygon faces from points are commonly used for architectural elements (floor plans, wall sections), custom mechanical parts, artistic designs, and any situation where you need precise control over shape boundaries. diff --git a/docs/learn/code/common/occt/shapes/face/face-from-wire.md b/docs/learn/code/common/occt/shapes/face/face-from-wire.md new file mode 100644 index 00000000..32cf9a23 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/face/face-from-wire.md @@ -0,0 +1,65 @@ +--- +sidebar_position: 4 +title: Face From Wire +sidebar_label: Face From Wire +description: Learn how to create face from single closed wire +tags: [code, occt, rete, blockly, typescript] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; + +OCCT category icon with a stylized logo representation + +# Creating Face From Wire + +Creating a face from a wire is one of the most fundamental operations in 3D modeling. Any closed wire can be converted into a face that fills the area bounded by that wire. This process transforms a 1D boundary into a 2D surface, which can then be used for visualization, analysis, or further 3D operations like extrusion. + +## Understanding Wire Orientation and Face Normal + +The orientation of a wire determines the direction of the face's normal vector, which affects how the face appears and behaves in 3D operations. When you create a face from a wire, the face's "up" direction depends on whether the wire follows a clockwise or counter-clockwise path. + +Wire reversal is often necessary to ensure faces have the correct orientation for: +- Proper lighting and material rendering +- Consistent normal directions in complex models +- Correct behavior in boolean operations +- Proper face orientation for manufacturing applications + +This example demonstrates creating a star-shaped face from a reversed wire to control the face orientation. + + + + + + + starWirereversedWirefaceFromWirestarWire0000107730FALSEreversedWirestarWirefaceFromWirereversedWireTRUEfaceFromWire","version":"0.20.7","type":"blockly"}} + title="Creating face from wire" + /> + + + {\n // Create a star wire\n const starOptions = new StarDto();\n starOptions.center = [0, 0, 0] as Point3;\n starOptions.direction = [0, 1, 0] as Vector3;\n starOptions.numRays = 7;\n starOptions.outerRadius = 7;\n starOptions.innerRadius = 3;\n starOptions.offsetOuterEdges = 0;\n starOptions.half = false;\n\n const starWire = await wire.createStarWire(starOptions);\n\n // Reverse the wire orientation\n const reversedWire = await wire.reversedWire({ shape: starWire });\n\n // Create a face from the reversed wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = reversedWire;\n faceOptions.planar = true;\n\n const faceFromWire = await face.createFaceFromWire(faceOptions);\n\n // Draw the created face\n bitbybit.draw.drawAnyAsync({ entity: faceFromWire });\n}\n\n// Execute the function\nstart();","version":"0.20.7","type":"typescript"}} + title="Creating face from wire" + /> + + + +## Key Concepts + +**Wire Reversal**: The `reversedWire` operation changes the direction of the wire's traversal. This is crucial because the face's normal vector (which direction is "up") depends on the wire's orientation. A clockwise wire produces a face with the normal pointing in one direction, while a counter-clockwise wire produces the opposite. + +**Planar Faces**: Setting `planar: true` ensures that the face lies entirely in a single plane. This is essential for most 2D shapes and guarantees predictable behavior in subsequent 3D operations. + +**Face Orientation**: The orientation of the created face affects lighting, material rendering, and boolean operations. Wire reversal gives you control over which side of the face is considered the "front" or "outside" surface. diff --git a/docs/learn/code/common/occt/shapes/face/face-hex-grid-pattern.md b/docs/learn/code/common/occt/shapes/face/face-hex-grid-pattern.md new file mode 100644 index 00000000..478a0985 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/face/face-hex-grid-pattern.md @@ -0,0 +1,85 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; + +# Face Hexagons Grid + +OCCT category icon with a stylized logo representation + +Learn how to create hexagonal face patterns that respond to a control point. Hexagons closer to the control point become larger, while distant ones become smaller, creating organic wave-like effects. + +## Key Concepts + +- **Control Point**: A point that influences the hexagon sizes based on distance +- **Distance Scaling**: Hexagons scale up/down based on how far they are from the control point +- **UV Coordinates**: Position the control point using 0-1 values instead of exact coordinates +- **Pattern Filtering**: Selectively remove some hexagons to create openings + + + + + + + gridWidthgridHeighthexagonsInWidthhexagonsInHeightcontrolPointUcontrolPointVrectangleFacecontrolPointhexGridhexCentersdistancesminDistancemaxDistancescalePatterndistancescaledValueinclusionPatternhexagonFacesaffectorPointpingridWidth16.5gridHeight9hexagonsInWidth29hexagonsInHeight29controlPointU0controlPointV0.49rectangleFacegridWidthgridHeight000010controlPointrectangleFacecontrolPointUcontrolPointVhexGridgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSETRUETRUEhexCentershexGridcentersdistancescontrolPointhexCentersminDistancedistancesmaxDistancedistancesscalePatterndistancedistancesscaledValuedistanceminDistancemaxDistance0.2110.9INSERTLASTscalePatternscaledValueinclusionPattern[true, true, true, true, false]hexagonFacesgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSEscalePatternscalePatterninclusionPatternaffectorPointcontrolPoint030pincontrolPointaffectorPoint0010.1Affector0.30.1hexagonFacespincontrolPoint","version":"0.20.7","type":"blockly"}} + title="Creating face from wire" + /> + + + {\n // Define grid parameters\n const gridWidth = 16.5;\n const gridHeight = 9;\n const hexagonsInWidth = 29;\n const hexagonsInHeight = 29;\n const controlPointU = 0; // UV parameter for control point position\n const controlPointV = 0.49; // UV parameter for control point position\n\n // Create a reference rectangle face to define the control point\n const rectangleOptions = new RectangleDto();\n rectangleOptions.width = gridWidth;\n rectangleOptions.length = gridHeight;\n rectangleOptions.center = [0, 0, 0] as Point3;\n rectangleOptions.direction = [0, 1, 0] as Vector3;\n\n const rectangleFace = await face.createRectangleFace(rectangleOptions);\n\n // Create control point on the rectangle face using UV parameters\n const pointOnUVOptions = new DataOnUVDto();\n pointOnUVOptions.shape = rectangleFace;\n pointOnUVOptions.paramU = controlPointU;\n pointOnUVOptions.paramV = controlPointV;\n\n const controlPoint = await face.pointOnUV(pointOnUVOptions);\n\n // Generate hexagon grid centers for distance calculation\n const hexGridOptions = new HexGridScaledToFitDto();\n hexGridOptions.width = gridWidth;\n hexGridOptions.height = gridHeight;\n hexGridOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridOptions.flatTop = true;\n hexGridOptions.extendTop = false;\n hexGridOptions.extendBottom = false;\n hexGridOptions.extendLeft = false;\n hexGridOptions.extendRight = false;\n hexGridOptions.centerGrid = true;\n hexGridOptions.pointsOnGround = true;\n\n const hexGrid = point.hexGridScaledToFit(hexGridOptions);\n const hexCenters = hexGrid.centers;\n\n // Calculate distances from control point to all hexagon centers\n const distances = point.distancesToPoints({\n startPoint: controlPoint,\n endPoints: hexCenters\n });\n\n // Flatten the distance array and find min/max values\n const minDistance = vector.min({ vector: distances });\n const maxDistance = vector.max({ vector: distances });\n\n // Remap distances to scale factors (0.211 to 0.9)\n const remapOptions = new RemapNumberDto();\n remapOptions.fromLow = minDistance;\n remapOptions.fromHigh = maxDistance;\n remapOptions.toLow = 0.211;\n remapOptions.toHigh = 0.9;\n\n const scalePattern = distances.map(x => {\n remapOptions.number = x;\n return math.remap(remapOptions);\n })\n\n // Create inclusion pattern for selective hexagon removal\n const inclusionPattern = json.parse({ text: \"[true, true, true, true, false]\" });\n\n // Create the hexagon faces with advanced patterns\n const hexFaceOptions = new HexagonsInGridDto();\n hexFaceOptions.width = gridWidth;\n hexFaceOptions.height = gridHeight;\n hexFaceOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexFaceOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexFaceOptions.flatTop = true;\n hexFaceOptions.extendTop = false;\n hexFaceOptions.extendBottom = false;\n hexFaceOptions.extendLeft = false;\n hexFaceOptions.extendRight = false;\n hexFaceOptions.scalePatternWidth = scalePattern;\n hexFaceOptions.scalePatternHeight = scalePattern;\n hexFaceOptions.inclusionPattern = inclusionPattern;\n\n const hexagonFaces = await face.hexagonsInGrid(hexFaceOptions);\n\n // Create affector point visualization (control point elevated)\n const affectorPoint = vector.add({\n first: controlPoint,\n second: [0, 3, 0] as Vector3\n }) as Point3;\n\n // Create pin with label to show the affector point\n const pinOptions = new PinWithLabelDto();\n pinOptions.startPoint = controlPoint;\n pinOptions.endPoint = affectorPoint;\n pinOptions.direction = [0, 0, 1] as Vector3;\n pinOptions.offsetFromStart = 0.1;\n pinOptions.label = \"Affector\";\n pinOptions.labelOffset = 0.3;\n pinOptions.labelSize = 0.1;\n\n const pin = await dimensions.pinWithLabel(pinOptions);\n\n // Draw the hexagon faces and affector pin\n bitbybit.draw.drawAnyAsync({ entity: hexagonFaces });\n bitbybit.draw.drawAnyAsync({ entity: pin });\n\n // Optional: Draw the control point for reference\n bitbybit.draw.drawAnyAsync({\n entity: controlPoint,\n });\n}\n\n// Execute the function\nstart();","version":"0.20.7","type":"typescript"}} + title="Creating face from wire" + /> + + + +## How It Works + +This example creates hexagonal faces that vary in size based on their distance from a control point, creating natural-looking patterns. + +### Step-by-Step Process + +1. **Set up the grid parameters** - Define width, height, and number of hexagons +2. **Create a control point** - Use UV coordinates (0 to 1) to position it on the grid +3. **Calculate distances** - Measure how far each hexagon center is from the control point +4. **Scale the hexagons** - Closer hexagons become larger, distant ones become smaller +5. **Apply patterns** - Optionally remove some hexagons to create openings + +### The Math Behind It + +The key is **remapping distances to scale values**: +- Find the shortest and longest distances +- Convert these to scale factors between 0.211 and 0.9 +- Apply these scales to make hexagons bigger or smaller + +### Why Use UV Coordinates? + +UV coordinates (from 0 to 1) are better than exact positions because: +- **U=0** means left edge, **U=1** means right edge +- **V=0** means bottom edge, **V=1** means top edge +- Works the same regardless of grid size +- Easy to understand and adjust + +### Pattern Control + +The `inclusionPattern` lets you remove hexagons in a repeating pattern: +- `[true, true, false]` = show 2, skip 1, repeat +- `[true, true, true, true, false]` = show 4, skip 1, repeat + +## Real-World Uses + +- **Architecture**: Building facades that respond to sunlight or wind +- **Product Design**: Grip patterns, ventilation holes, decorative surfaces +- **Interior Design**: Ceiling panels, wall textures, lighting patterns + +The beauty of this approach is that simple distance calculations create complex, organic-looking patterns that feel natural rather than mechanical. diff --git a/docs/learn/code/common/occt/shapes/face/intro-face.md b/docs/learn/code/common/occt/shapes/face/intro-face.md new file mode 100644 index 00000000..1adc2704 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/face/intro-face.md @@ -0,0 +1,53 @@ +--- +sidebar_position: 1 +title: "Working with Faces in OCCT" +sidebar_label: Intro Faces +description: Learn about OCCT faces, how to create them from wires, their role in constructing solids, and how faces can have multiple wires (e.g., for holes). +tags: [occt] +--- + +# OCCT Shapes: The Face + +OCCT category icon with a stylized logo representation + +## Introduction to Faces + +In OpenCascade Technology (OCCT), a **Face** is a fundamental 2D shape that represents a surface bounded by one or more **Wires**. Faces are crucial as they form the surfaces used to construct **Shells** and **Solids**. + +Key characteristics of faces: +* **Composition:** Made from one or more closed wires that define boundaries. The outer wire defines the face's perimeter, while additional inner wires create holes. +* **Surface Types:** Faces can represent various surface types, including planes, cylinders, spheres, and other complex surfaces, depending on the underlying geometry. +* **Building Blocks:** Faces serve as the fundamental 2D elements for constructing 3D solids through operations like extrusion, revolution, or boolean combinations. + +## Creating Face Primitives + +Bitbybit offers many straightforward ways to create common face shapes (primitives) directly. These include: +* **Squares and Rectangles** +* **Circles and Ellipses** + +These face primitives serve as excellent starting points for creating shells and solids, or can be used directly for surface analysis and visualization. + +## Creating Faces from Wires + +The most versatile method for creating faces is by using wires as boundaries: + +* **Simple Face:** Created from a single closed wire that defines the outer boundary. +* **Face with Holes:** + * **Outer Boundary:** One closed wire defines the face's perimeter. + * **Inner Boundaries (Holes):** Additional closed wires create holes within the face. +* **Complex Surfaces:** Faces can be created on various underlying surfaces (planes, cylinders, etc.) with wire boundaries projected onto those surfaces. + +## Faces as Building Blocks for Solids + +Faces are fundamental for constructing 3D geometry: +* **Shells:** Collections of connected faces that form the boundary of a volume. +* **Solids:** Closed shells that define complete 3D objects with interior volume. +* **Extrusion/Revolution:** Faces can be extruded along paths or revolved around axes to create 3D solids. + +Understanding the relationship between wires and faces is essential for 3D modeling. When you deconstruct a face with holes, you'll get multiple wires - one for the outer boundary and additional wires for each hole. + +The "Face" category in Bitbybit editors provides comprehensive tools for creating these essential 2D surfaces, bridging the gap between 1D wires and 3D solids, forming the foundation of surface and solid modeling operations. diff --git a/docs/learn/code/common/occt/shapes/shells/_category_.json b/docs/learn/code/common/occt/shapes/shells/_category_.json index 5d45b5d0..4bd66d25 100644 --- a/docs/learn/code/common/occt/shapes/shells/_category_.json +++ b/docs/learn/code/common/occt/shapes/shells/_category_.json @@ -1,6 +1,6 @@ { "label": "Shells", - "position": 4, + "position": 5, "link": { "type": "generated-index", "title": "Shells", diff --git a/docs/learn/code/common/occt/shapes/solids/_category_.json b/docs/learn/code/common/occt/shapes/solids/_category_.json index 551a212e..24e0a2ed 100644 --- a/docs/learn/code/common/occt/shapes/solids/_category_.json +++ b/docs/learn/code/common/occt/shapes/solids/_category_.json @@ -1,6 +1,6 @@ { "label": "Solids", - "position": 5, + "position": 6, "link": { "type": "generated-index", "title": "Solids", diff --git a/docs/learn/code/common/occt/shapes/wire/wire-bezier-weights.md b/docs/learn/code/common/occt/shapes/wire/wire-bezier-weights.md new file mode 100644 index 00000000..bec525a8 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/wire/wire-bezier-weights.md @@ -0,0 +1,55 @@ +--- +sidebar_position: 4 +title: Wire Bezier Weights +sidebar_label: Wire Bezier Weights +description: Learn how to create bezier wire with weights +tags: [code, occt, rete, blockly, typescript] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; + +OCCT category icon with a stylized logo representation + +# Creating Weighted Bezier Curves + +Weighted Bezier curves give you precise control over how strongly each control point influences the curve's shape. Think of weights as invisible magnets attached to each control point - the heavier the weight, the more the curve gets pulled toward that point. + +This example demonstrates the dramatic effect weights can have on curve shape. We start with five control points arranged in 3D space, then assign different weight values to each point. The middle control point receives the heaviest weight of 200, while the others get lighter weights ranging from 10 to 60. + +The visual result shows three key elements that help you understand how weights work. The bright yellow curve represents the final weighted Bezier shape, dramatically pulled toward the middle point due to its high weight. A thin white polyline connects all the control points in order, showing you the basic path without any curve smoothing. Green spheres appear at each control point, with their sizes corresponding to the weight values - the larger the sphere, the stronger that point's influence on the curve. + +## Understanding the Weight Effect + +When you run this example, you'll see how the curve bends strongly toward the middle control point. This happens because its weight of 200 is much higher than the surrounding points. The curve still passes through the general area of all control points, but it gets pulled most strongly toward the heavily weighted middle point, creating a distinctive bulge in that direction. + +This technique is invaluable for design work where you need the curve to emphasize certain areas while maintaining smooth transitions. Automotive designers use weighted Bezier curves to ensure body panels flow correctly through critical styling points, while maintaining smooth surfaces elsewhere. + + + + + + + point1point2point3point4point5pointsListweightsbezierWirepolylineRefminWeightmaxWeightspheressphere1sphere2sphere3sphere4sphere5point1-1000point2-503point307-5point4503point51000pointsListpoint1point2point3point4point5weights10602003010bezierWirepointsListweightsFALSEbezierWire0.01FALSETRUE#e4ff1a10polylineRefpointsListFALSEpolylineRefminWeightweightsmaxWeightweightssphere1point110minWeightmaxWeight0.30.9sphere2point260minWeightmaxWeight0.30.9sphere3point3200minWeightmaxWeight0.30.9sphere4point430minWeightmaxWeight0.30.9sphere5point510minWeightmaxWeight0.30.9spheressphere1sphere2sphere3sphere4sphere5spheres0.05TRUEFALSE#37ff00","version":"0.20.7","type":"blockly"}} + title="Bezier curves with different weight distributions" + /> + + + {\n // Define control points for the Bezier curve\n const controlPoints: Point3[] = [\n [-10, 0, 0],\n [-5, 0, 3],\n [0, 7, -5],\n [5, 0, 3],\n [10, 0, 0]\n ];\n\n // Weight values - higher weights pull the curve closer to that point\n const weights: number[] = [10, 60, 200, 30, 10];\n \n // Create weighted Bezier curve\n const bezierOptions = new BezierWeightsDto();\n bezierOptions.points = controlPoints;\n bezierOptions.weights = weights;\n bezierOptions.closed = false;\n const bezierWire = await wire.createBezierWeights(bezierOptions);\n\n // Draw the Bezier curve with thick yellow line\n const wireDrawOptions = new DrawOcctShapeSimpleOptions();\n wireDrawOptions.precision = 0.01;\n wireDrawOptions.drawFaces = false;\n wireDrawOptions.drawEdges = true;\n wireDrawOptions.edgeColour = \"#e4ff1a\";\n wireDrawOptions.edgeWidth = 10;\n\n await draw.drawAnyAsync({ entity: bezierWire, options: wireDrawOptions });\n\n // Draw reference polyline connecting control points\n const plnOptions = new PolylineCreateDto();\n plnOptions.points = controlPoints;\n const pln = polyline.create(plnOptions) as Bit.Inputs.Draw.Entity;\n await draw.drawAnyAsync({ entity: pln });\n\n // Calculate weight range for sphere scaling\n const minWeight = vector.min({ vector: weights });\n const maxWeight = vector.max({ vector: weights });\n\n // Create spheres at control points, sized by their weights\n const spherePromises = [];\n for (let i = 0; i < controlPoints.length; i++) {\n // Remap weight to sphere radius (0.3 to 0.9)\n const remapOptions = new RemapNumberDto();\n remapOptions.number = weights[i];\n remapOptions.fromLow = minWeight;\n remapOptions.fromHigh = maxWeight;\n remapOptions.toLow = 0.3;\n remapOptions.toHigh = 0.9;\n\n const sphereOptions = new SphereDto();\n sphereOptions.center = controlPoints[i];\n sphereOptions.radius = bitbybit.math.remap(remapOptions);\n\n spherePromises.push(shapes.solid.createSphere(sphereOptions));\n }\n\n const spheres = await Promise.all(spherePromises);\n\n // Draw spheres in green\n const sphereDrawOptions = new DrawOcctShapeSimpleOptions();\n sphereDrawOptions.precision = 0.05;\n sphereDrawOptions.drawFaces = true;\n sphereDrawOptions.drawEdges = false;\n sphereDrawOptions.faceColour = \"#37ff00\";\n \n draw.drawAnyAsync({ entity: spheres, options: sphereDrawOptions });\n};\n\nstart();","version":"0.20.7","type":"typescript"}} + title="Bezier curves with different weight distributions" + /> + + diff --git a/docs/learn/code/common/occt/shapes/wire/wire-hexagons-advanced-pattern.md b/docs/learn/code/common/occt/shapes/wire/wire-hexagons-advanced-pattern.md index 7836848b..54238d81 100644 --- a/docs/learn/code/common/occt/shapes/wire/wire-hexagons-advanced-pattern.md +++ b/docs/learn/code/common/occt/shapes/wire/wire-hexagons-advanced-pattern.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 7 title: Hexagons Advanced Pattern sidebar_label: Hexagons Advanced Pattern description: Learn how to use advanced hexagon pattern parameters to create intricate designs. diff --git a/docs/learn/code/common/occt/shapes/wire/wire-hexagons-in-grid.md b/docs/learn/code/common/occt/shapes/wire/wire-hexagons-in-grid.md index b64d38ed..9bcca828 100644 --- a/docs/learn/code/common/occt/shapes/wire/wire-hexagons-in-grid.md +++ b/docs/learn/code/common/occt/shapes/wire/wire-hexagons-in-grid.md @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 6 title: Hexagons Grid sidebar_label: Hexagons Grid description: Learn how to create wire based hexagon grids diff --git a/docs/learn/code/common/occt/shapes/wire/wire-via-points.md b/docs/learn/code/common/occt/shapes/wire/wire-via-points.md new file mode 100644 index 00000000..24aabc83 --- /dev/null +++ b/docs/learn/code/common/occt/shapes/wire/wire-via-points.md @@ -0,0 +1,143 @@ +--- +sidebar_position: 3 +title: Wire Via Points +sidebar_label: Wire Via Points +description: Learn how to create various wires passing via or being controlled by collections of points +tags: [code, occt, rete, blockly, typescript] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; + +OCCT category icon with a stylized logo representation + +# Creating Wires Via Points + +Imagine you're sketching a path through a landscape, connecting landmarks along the way. In 3D modeling, creating wires from points follows the same principle - you define key locations in space and let OCCT connect them in different ways to create the exact path you need. + +## When Would You Use Point-Based Wires? + +Think of real-world scenarios where you need to connect specific locations: + +- **Tracing a coastline** from GPS coordinates collected during a survey +- **Designing a race track** that must pass through mandatory checkpoints +- **Creating a roller coaster path** with smooth curves through scenic viewpoints +- **Plotting a robot's movement** through precise manufacturing positions +- **Sketching a building outline** from surveyed property corners + +## The Different Ways to Connect Your Points + +OCCT offers several methods to connect points, each creating a different type of path. Most of these methods create **open-ended wires** - paths with distinct start and end points. To create closed shapes, you'll typically combine these wires with additional segments or use specialized closing operations. + +### Straight-Line Connections +These create angular, precise paths perfect for architectural or mechanical designs: + +- **Line Wire**: The simplest connection - imagine drawing a ruler-straight line between two distant points +- **Polyline Wire**: Like connecting dots with straight lines, creating sharp turns at each point +- **Polygon Wire**: Similar to polyline, but automatically closes the shape by connecting back to the start + +### Smooth Curved Connections +These create flowing, organic paths ideal for natural forms or aesthetic designs: + +- **Interpolated Wire**: Like threading a flexible cable through each point - the curve passes exactly through every location +- **Bezier Wire**: Points act like magnets pulling on a rubber band - the curve flows toward them but may not touch each one +- **BSpline Wire**: The most sophisticated option, offering mathematical precision for complex industrial curves + +## A Story of Six Paths + +Picture yourself as a landscape architect designing a garden path system. You have five scenic viewpoints that visitors should experience. Here's how each wire method would create a different journey: + +### Polylines and Polygons +You want visitors to walk directly between viewpoints with clear sight lines. **Polylines** create this direct, no-nonsense path with sharp turns at each viewpoint. **Polygons** add a final segment back to the starting point, creating a complete loop tour. + +*Perfect for: Property boundaries, building floor plans, mechanical part outlines* + +### Interpolated Curves +You want a gentle, flowing path that guides visitors smoothly through each viewpoint without jarring direction changes. The path bends gracefully, ensuring visitors experience every scenic spot while maintaining a natural walking rhythm. + +*Perfect for: Natural landscape features, aerodynamic surfaces, smooth motion paths* + +### Bezier and BSpline Curves +You want artistic control over the path's character. The viewpoints influence the route's direction and feel, but the path may curve away from some points to create a more aesthetically pleasing or functionally optimal journey. + +*Perfect for: Automotive body panels, artistic sculptures, complex industrial surfaces* + +## Example: Comprehensive Point-Based Wire Creation + +This example demonstrates all major methods for creating wires from points, showing how the same set of points can produce dramatically different geometric results depending on the chosen method. + + + + + + + point1point2point3point4point5pointsListlineWirepolylineWirepolygonWireinterpolatedWireinterpolatedPeriodicWirebezierWirebsplineWirepoint1-400point2-201point300-1point4202point5400pointsListpoint1point2point3point4point5lineWirepoint1point5polylineWirepointsList010polygonWirepointsList020interpolatedWirepointsListFALSE1e-7030interpolatedPeriodicWirepointsListTRUE1e-7040bezierWirepointsList050bsplineWirepointsListFALSE3060lineWirepolylineWirepolygonWireinterpolatedWireinterpolatedPeriodicWirebezierWirebsplineWire","version":"0.20.7","type":"blockly"}} + title="Comprehensive wire creation from points" + /> + + + {\n // Define a set of control points for wire creation\n const points: Point3[] = [\n [-4, 0, 0], // Start point\n [-2, 0, 1], // First control point\n [0, 0, -1], // Second control point\n [2, 0, 2], // Third control point\n [4, 0, 0] // End point\n ];\n\n // 1. Create a simple line wire between first and last points\n const lineOptions = new LineDto();\n lineOptions.start = points[0];\n lineOptions.end = points[4];\n const lineWire = await wire.createLineWire(lineOptions);\n\n // 2. Create a polyline wire connecting all points with straight segments\n const polylineOptions = new PolylineDto();\n polylineOptions.points = points;\n const polylineWire = await wire.createPolylineWire(polylineOptions);\n\n // 3. Create a polygon wire (closed shape) from the points\n const polygonOptions = new PolygonDto();\n polygonOptions.points = points;\n const polygonWire = await wire.createPolygonWire(polygonOptions);\n\n // 4. Create an interpolated wire that passes smoothly through all points\n const interpolationOptions = new InterpolationDto();\n interpolationOptions.points = points;\n interpolationOptions.periodic = false;\n interpolationOptions.tolerance = 1e-7;\n const interpolatedWire = await wire.interpolatePoints(interpolationOptions);\n\n // 5. Create an interpolated periodic wire that creates a closed smooth curve\n const interpolationPeriodicOptions = new InterpolationDto();\n interpolationPeriodicOptions.points = points;\n interpolationPeriodicOptions.periodic = true;\n interpolationPeriodicOptions.tolerance = 1e-7;\n const interpolatedPeriodicWire = await wire.interpolatePoints(interpolationPeriodicOptions);\n\n // 6. Create a Bezier curve using points as control points\n const bezierOptions = new BezierDto();\n bezierOptions.points = points;\n bezierOptions.closed = false;\n const bezierWire = await wire.createBezier(bezierOptions);\n\n // 7. Create a BSpline curve with specified degree\n const bsplineOptions = new BSplineDto();\n bsplineOptions.points = points;\n bsplineOptions.closed = false;\n const bsplineWire = await wire.createBSpline(bsplineOptions);\n\n // Translate wires to different Y positions for better visualization\n const translateOptions = new TranslateDto();\n\n // Translate polyline\n translateOptions.shape = polylineWire;\n translateOptions.translation = [0, 1, 0] as Vector3;\n const translatedPolyline = await transforms.translate(translateOptions);\n\n // Translate polygon\n translateOptions.shape = polygonWire;\n translateOptions.translation = [0, 2, 0] as Vector3;\n const translatedPolygon = await transforms.translate(translateOptions);\n\n // Translate interpolated wire\n translateOptions.shape = interpolatedWire;\n translateOptions.translation = [0, 3, 0] as Vector3;\n const translatedInterpolated = await transforms.translate(translateOptions);\n\n // Translate interpolated periodic wire\n translateOptions.shape = interpolatedPeriodicWire;\n translateOptions.translation = [0, 4, 0] as Vector3;\n const translatedInterpolatedPeriodic = await transforms.translate(translateOptions);\n\n // Translate Bezier wire\n translateOptions.shape = bezierWire;\n translateOptions.translation = [0, 5, 0] as Vector3;\n const translatedBezier = await transforms.translate(translateOptions);\n\n // Translate BSpline wire\n translateOptions.shape = bsplineWire;\n translateOptions.translation = [0, 6, 0] as Vector3;\n const translatedBSpline = await transforms.translate(translateOptions);\n\n // Draw all the created wires\n bitbybit.draw.drawAnyAsync({ entity: lineWire });\n bitbybit.draw.drawAnyAsync({ entity: translatedPolyline });\n bitbybit.draw.drawAnyAsync({ entity: translatedPolygon });\n bitbybit.draw.drawAnyAsync({ entity: translatedInterpolated });\n bitbybit.draw.drawAnyAsync({ entity: translatedInterpolatedPeriodic });\n bitbybit.draw.drawAnyAsync({ entity: translatedBezier });\n bitbybit.draw.drawAnyAsync({ entity: translatedBSpline });\n}\n\n// Execute the function\nstart();","version":"0.20.7","type":"typescript"}} + title="Comprehensive wire creation from points" + /> + + + +## Understanding the Results + +When you run this example, you'll see six different paths connecting the same five points. Each represents a different approach to the journey: + +### Line Wire (Bottom) +The most direct route - ignores the scenic stops and goes straight from start to finish. + +### Polyline Wire (Second from bottom) +The methodical tourist route - visits every viewpoint with efficient straight-line segments between them. + +### Polygon Wire (Third from bottom) +The complete circuit tour - same as polyline but returns to the starting point to complete the loop. + +### Interpolated Wire (Fourth from bottom) +The scenic route - flows smoothly through every viewpoint like a gentle river following its course. + +### Interpolated Periodic Wire (Fifth from bottom) +The endless garden path - creates a smooth, closed loop that flows through all points and seamlessly connects back to itself. + +### Bezier Wire (Sixth from bottom) +The artistic interpretation - the viewpoints inspire the path's direction, creating an elegant curve that captures the essence of the journey. + +### BSpline Wire (Top) +The engineer's precision path - mathematically optimized for specific curve properties while responding to the viewpoint influences. + +## Choosing Your Path + +### When You Need Precise Control +**Line and Polyline wires** are your friends when every point must be exactly where you specify - think building corners, property boundaries, or mechanical part edges where precision trumps aesthetics. + +### When You Need Natural Flow +**Interpolated wires** create organic, flowing shapes perfect for landscapes, water features, or any design where you want smooth transitions through specific locations. + +### When You Need Artistic Freedom +**Bezier and BSpline wires** give you creative control over the overall shape while using your points as guides rather than absolute requirements - ideal for automotive styling, product design, or architectural features. + +## Important Note: Open vs. Closed Wires + +Most point-based wire methods create **open-ended paths** with distinct start and end points. While polygon wires and periodic interpolated wires automatically close themselves, other methods produce open wires that you can later: + +- **Combine with additional wire segments** to create complex closed shapes +- **Use as building blocks** for more sophisticated geometries +- **Connect to other wires** using OCCT's wire joining operations +- **Transform into faces** when combined with other wires to form closed boundaries + +Understanding point-based wire creation opens the door to more advanced OCCT operations like face creation, surface modeling, and complex 3D shape development. These wires become the foundation elements that you'll combine and transform to build sophisticated geometries. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2f9c03af..945a2373 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,433 @@ { "name": "bitbybit", - "version": "0.20.5", + "version": "0.20.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitbybit", - "version": "0.20.5", + "version": "0.20.7", "license": "MIT", "devDependencies": { + "rimraf": "^5.0.5", "typescript": "^4.3.5" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -24,6 +440,120 @@ "engines": { "node": ">=4.2.0" } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } } } } diff --git a/package.json b/package.json index df35230b..e76e8c8b 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,16 @@ "ci-manifold-worker": "cd packages/dev/manifold-worker && npm ci", "ci-manifold": "cd packages/dev/manifold && npm ci", "ci-packages": "npm run ci-base && npm run ci-core && npm run ci-threejs && npm run ci-babylonjs && npm run ci-occt-worker && npm run ci-occt && npm run ci-jscad-worker && npm run ci-jscad && npm run ci-manifold-worker && npm run ci-manifold", - "rm-dist-base-if-exists": "rm -rf packages/dev/base/dist || true", - "rm-dist-core-if-exists": "rm -rf packages/dev/core/dist || true", - "rm-dist-threejs-if-exists": "rm -rf packages/dev/threejs/dist || true", - "rm-dist-babylonjs-if-exists": "rm -rf packages/dev/babylonjs/dist || true", - "rm-dist-occt-worker-if-exists": "rm -rf packages/dev/occt-worker/dist || true", - "rm-dist-occt-if-exists": "rm -rf packages/dev/occt/dist || true", - "rm-dist-jscad-worker-if-exists": "rm -rf packages/dev/jscad-worker/dist || true", - "rm-dist-jscad-if-exists": "rm -rf packages/dev/jscad/dist || true", - "rm-dist-manifold-worker-if-exists": "rm -rf packages/dev/manifold-worker/dist || true", - "rm-dist-manifold-if-exists": "rm -rf packages/dev/manifold/dist || true", + "rm-dist-base-if-exists": "rimraf packages/dev/base/dist", + "rm-dist-core-if-exists": "rimraf packages/dev/core/dist", + "rm-dist-threejs-if-exists": "rimraf packages/dev/threejs/dist", + "rm-dist-babylonjs-if-exists": "rimraf packages/dev/babylonjs/dist", + "rm-dist-occt-worker-if-exists": "rimraf packages/dev/occt-worker/dist", + "rm-dist-occt-if-exists": "rimraf packages/dev/occt/dist", + "rm-dist-jscad-worker-if-exists": "rimraf packages/dev/jscad-worker/dist", + "rm-dist-jscad-if-exists": "rimraf packages/dev/jscad/dist", + "rm-dist-manifold-worker-if-exists": "rimraf packages/dev/manifold-worker/dist", + "rm-dist-manifold-if-exists": "rimraf packages/dev/manifold/dist", "rm-dist-packages": "npm run rm-dist-base-if-exists && npm run rm-dist-core-if-exists && npm run rm-dist-threejs-if-exists && npm run rm-dist-babylonjs-if-exists && npm run rm-dist-occt-worker-if-exists && npm run rm-dist-occt-if-exists && npm run rm-dist-jscad-worker-if-exists && npm run rm-dist-jscad-if-exists && npm run rm-dist-manifold-worker-if-exists && npm run rm-dist-manifold-if-exists", "build-base": "cd packages/dev/base && npm run build-p", "build-core": "cd packages/dev/core && npm run build-p", @@ -45,7 +45,10 @@ "test-jscad": "cd packages/dev/jscad && npm run test-c", "test-manifold": "cd packages/dev/manifold && npm run test-c", "test-threejs": "cd packages/dev/threejs && npm run test-c", - "test": "npm run test-occt && npm run test-base && npm run test-core && npm run test-jscad && npm run test-manifold && npm run test-threejs" + "test": "npm run test-occt && npm run test-base && npm run test-core && npm run test-jscad && npm run test-manifold && npm run test-threejs", + "setup": "npm run install-rebuild-all-packages", + "setup-and-test": "npm run setup && npm run test", + "first-time-setup": "npm install && npm run setup-and-test" }, "repository": { "type": "git", @@ -70,6 +73,7 @@ }, "homepage": "https://bitbybit.dev", "devDependencies": { - "typescript": "^4.3.5" + "typescript": "^4.3.5", + "rimraf": "^5.0.5" } } \ No newline at end of file