From 13f91616ae8018070a065071e931ccce27cb808d Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sat, 13 Dec 2025 16:35:02 +0200 Subject: [PATCH 01/38] new docs for security policy and hosting of assets on users cdn --- docs/learn/hosting-and-cdn.md | 181 ++++++++++++++++++++++++++++++++++ docs/learn/security-policy.md | 78 +++++++++++++++ docs/learn/tags.yml | 12 ++- 3 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 docs/learn/hosting-and-cdn.md create mode 100644 docs/learn/security-policy.md diff --git a/docs/learn/hosting-and-cdn.md b/docs/learn/hosting-and-cdn.md new file mode 100644 index 00000000..6a4dd09f --- /dev/null +++ b/docs/learn/hosting-and-cdn.md @@ -0,0 +1,181 @@ +--- +sidebar_position: 10 +title: Hosting Bitbybit Assets on Your Own CDN +sidebar_label: Self-Hosting Assets +description: Learn how to host Bitbybit assets on your own CDN infrastructure for improved reliability, performance, and control. +tags: [cdn, hosting, infrastructure, deployment, performance, assets] +--- + +import Admonition from '@theme/Admonition'; + +# Hosting Bitbybit Assets on Your Own CDN + +To simplify the setup process for new users and customers, our runners and various assets are hosted on a generally available CDN called **jsDelivr**. Our online editors and runners that you set up on your website all load critical assets from the jsDelivr CDN. While convenient, this dependency introduces a potential point of failure: what if your website suddenly stops working because jsDelivr went down? This is a realistic scenario that happens more often than we'd like to admit. + +The solution? Host Bitbybit assets on your own infrastructure using a CDN provider you trust. + +## Understanding CDNs + +### What is a CDN? + +A **Content Delivery Network (CDN)** is a geographically distributed network of servers designed to deliver web content efficiently to users based on their physical location. Instead of serving all content from a single origin server, CDNs replicate and cache your static assets (JavaScript files, images, fonts, etc.) across multiple data centers worldwide. + +### How CDNs Function + +When a user requests content from a CDN-enabled website: + +1. The CDN receives the request and determines the user's geographic location +2. The request is routed to the nearest edge server (Point of Presence) +3. If the content is cached on that edge server, it's delivered immediately +4. If not cached, the edge server retrieves it from the origin, caches it, and serves it to the user +5. Subsequent requests from nearby users receive the cached version + +### Performance Benefits of CDNs + +CDNs significantly improve website performance through: + +- **Reduced Latency**: Content is served from geographically closer servers, minimizing the physical distance data travels +- **Faster Load Times**: Cached static assets load instantly without hitting the origin server +- **Bandwidth Optimization**: CDNs compress and optimize content delivery +- **Load Distribution**: Traffic is distributed across multiple servers, preventing bottlenecks +- **High Availability**: If one server fails, requests automatically route to another + +### Why Consider Self-Hosting Bitbybit Assets? + +While we provide jsDelivr hosting for convenience, there are compelling reasons to host assets on your own infrastructure: + +1. **Reliability & Control**: You're not dependent on third-party uptime. If jsDelivr experiences an outage, your application continues functioning +2. **Performance Optimization**: Choose a CDN provider optimized for your specific user base and geographic regions +3. **Version Stability**: Pin specific versions without worrying about external changes or deprecations +4. **Compliance & Security**: Meet enterprise requirements for asset hosting and security policies +5. **Custom Caching Strategies**: Implement caching rules tailored to your application's needs +6. **Cost Management**: For high-traffic applications, your own CDN plan might be more cost-effective + +## Downloading Bitbybit Assets + +All assets used by the Bitbybit platform are available for download from our GitHub repository: + +**[Bitbybit Assets Releases](https://github.com/bitbybit-dev/bitbybit-assets/releases)** + +Each release contains a complete set of assets packaged as a `.zip` file. Download the version that matches your Bitbybit implementation. + +## Identifying Required Assets + +### You Don't Need Everything + +An important consideration: **you probably don't need to host all assets**, only the ones your specific implementation requires. + +**Examples:** +- If you use `bitbybit-runner-babylonjs.js`, you won't need `bitbybit-runner-threejs.js` or the lite runners +- If you only use the OCCT kernel, you'll only need to host `bitbybit-occt-webworker.js` +- If you never load GLTF files with Draco compression, you don't need the Draco decompressor + +### Determining Your Asset Dependencies + +Identifying exactly which assets you need depends on the features you're implementing. Some assets are loaded dynamically only when specific features are activated. + + + +1. **Use Browser DevTools**: Open your application and access the Network tab +2. **Exercise All Features**: Navigate through your application, activating all features and workflows +3. **Filter CDN Requests**: Look for requests matching the pattern: + ``` + https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@ + ``` +4. **Document Assets**: Note every asset loaded from this domain—these are the files you should migrate to your own hosting + + + +### Alternative Approach: Host Everything + +While potentially inefficient, you can simply host the complete assets folder on your infrastructure. This ensures all potential dependencies are available, though it may consume more storage and bandwidth than necessary. + +## Configuring the Runner to Use Your CDN + +### Using the Runner with `cdnUrl` Option + +When initializing the Bitbybit Runner, you pass an options object containing properties like `canvasId`, `enableOCCT`, etc. This same object supports a property called **`cdnUrl`**, which tells the runner where to load assets from. + +**Example Configuration:** + +```javascript +const runnerOptions = { + canvasId: 'myCanvas', + enableOCCT: true, + enableJSCAD: false, + enableManifold: false, + cdnUrl: 'https://cdn.yourownhosting.com/bitbybit/', // Your custom CDN URL + loadFonts: [], +}; + +const runner = window.bitbybitRunner.getRunnerInstance(); +const { bitbybit, Bit } = await runner.run(runnerOptions); +``` + +### Using NPM Packages with `GlobalCDNProvider` + +If you're using Bitbybit's NPM packages directly in your project (rather than the standalone runner), you need to configure the CDN URL differently. Import the `GlobalCDNProvider` from `@bitbybit-dev/base` and set the CDN URL before initializing any Bitbybit services. + +**Example Configuration:** + +```javascript +import { GlobalCDNProvider } from "@bitbybit-dev/base"; + +// Set your custom CDN URL before initializing Bitbybit +GlobalCDNProvider.BITBYBIT_CDN_URL = "https://cdn.yourownhosting.com/bitbybit/"; + +// Now initialize your Bitbybit services as usual +// All asset requests will use your custom CDN URL +``` + + +

If you're using NPM packages like @bitbybit-dev/occt, @bitbybit-dev/babylonjs, or @bitbybit-dev/threejs, you must use the GlobalCDNProvider approach. The cdnUrl option in the runner configuration only applies when using the standalone runner files.

+
+ +### Important Considerations + +**Public Access:** All hosted assets must be completely public and accessible without authentication. + +**Recommended Hosting Strategy:** +- Use global CDN providers (e.g., AWS CloudFront, Cloudflare, Azure CDN, Google Cloud CDN) +- Enable automatic deployment of static assets across regions +- Implement proper caching headers and strategies +- Configure cache invalidation when updating versions + +**Geographic Distribution:** Quality CDN providers automatically distribute assets to edge locations closest to your users, minimizing latency and maximizing performance. + +**Path Structure:** Ensure your CDN URL maintains the same folder structure as the Bitbybit assets repository. The runner expects assets to be organized in specific paths relative to the `cdnUrl`. + + +

The runner expects assets to follow the exact same folder structure as found in the Bitbybit assets repository. If the structure doesn't match, assets will fail to load.

+
+ +## Testing Your Configuration + +After setting up your custom CDN: + +1. Clear your browser cache +2. Load your application +3. Open DevTools Network tab +4. Verify all assets load from your CDN domain +5. Test all features to ensure no assets are missing +6. Monitor for any 404 errors indicating missing files + +## Popular CDN Providers + +Here are some popular CDN providers you might consider for hosting Bitbybit assets: + +- **Cloudflare**: Offers a generous free tier with global edge locations +- **AWS CloudFront**: Integrates seamlessly with S3 storage and offers fine-grained control +- **Azure CDN**: Excellent for enterprises already using Microsoft Azure +- **Google Cloud CDN**: Fast global network, ideal for Google Cloud users +- **Fastly**: Premium CDN with real-time configuration and edge computing capabilities +- **BunnyCDN**: Cost-effective option with good performance and simple pricing + +Choose a provider based on your budget, geographic user base, existing infrastructure, and specific feature requirements. + +## Conclusion + +While jsDelivr hosting provides convenience for getting started, self-hosting Bitbybit assets gives you greater control, reliability, and performance optimization opportunities. By carefully selecting the assets you need and configuring a robust CDN solution, you can ensure your Bitbybit-powered applications remain fast, reliable, and independent of third-party infrastructure. + +For more information about using the Bitbybit Runner, see [Introducing Bitbybit Runner](/learn/runners/intro). diff --git a/docs/learn/security-policy.md b/docs/learn/security-policy.md new file mode 100644 index 00000000..145f8eae --- /dev/null +++ b/docs/learn/security-policy.md @@ -0,0 +1,78 @@ +--- +sidebar_position: 10 +title: Security Policy +sidebar_label: Security Policy +description: Guidelines for reporting security vulnerabilities to the Bitbybit team. +tags: [security] +--- + +## Security Policy + +At Bitbybit, we take the security of our platform and our users' data seriously. We welcome contributions from the security community and appreciate responsible disclosure. Please note that this document may change at any time, and you are responsible for checking for updates without prior notice from us. + +## Safe Harbor & Rules of Engagement + +We consider security research to be a helpful activity, not a crime. If you act in good faith and follow this policy, **we will not take legal action against you** regarding your research. + +However, we must protect our users. This pledge **does not apply** if you act maliciously - for example, by intentionally stealing data, disrupting our services, share user information with third parties, breach our [Terms and Conditions](https://bitbybit.dev/terms-and-conditions) or do not agree to our [Privacy Policy](https://bitbybit.dev/privacy-policy). In those cases, we reserve the right to take necessary legal steps. + +**To stay safe and helpful, please:** +* **Test only on accounts you own** or have explicit permission to test on. +* **Do not** exfiltrate, download, or modify data residing in any other account (verify the vulnerability exists and stop there). +* **Do not** execute attacks that degrade the performance of our services (e.g., DoS, spamming). +* **Refrain** from publicly disclosing the vulnerability until we have had reasonable time to fix it. + +## Reporting a Vulnerability + +If you believe you have found a security vulnerability in our platform, please report it to us via email. + +:::info How to Report +**Email:** [info@bitbybit.dev](mailto:info@bitbybit.dev) +**Subject:** Security Vulnerability Report +::: + +### What to Include +To help us triage the issue efficiently, please include: +1. **Type:** The class of vulnerability (e.g., XSS, SQLi, IDOR). +2. **Location:** The specific URL, endpoint, or package affected. +3. **Proof of Concept (PoC):** Step-by-step instructions to reproduce the issue (screenshots or video are highly encouraged). +4. **Impact:** A brief summary of the potential risk. + +*Note: Please redact sensitive information (e.g., real API keys or PII) from your report unless necessary to prove the vulnerability.* + +## Our Process + +We value your time and aim to be transparent about our triage process. + +1. **Acknowledgment:** We aim to respond to valid reports within **5 business days**. +2. **Assessment:** We will review the report to verify the vulnerability and assess its severity. +3. **Resolution:** If the issue is confirmed, we will work on a fix. We will keep you updated on the progress. +4. **Closure:** We will notify you once the issue is resolved. + +:::note No Bug Bounty +We **do not** currently offer a bug bounty program or financial compensation. Please do not submit reports expecting payment. However, we are very thankful for your efforts to keep our community safe. +::: + +### Acknowledgments + +We value the time you invest in security research. For valid, responsibly disclosed vulnerabilities, we are happy to acknowledge your contribution in our release notes or security credits. + +**Please Note:** +* **Identification:** When reporting, please let us know how you would like to be identified (e.g., **Real Name** or **Handle/Pseudonym**). If you prefer to remain anonymous, we will respect that. +* **Disclosure Details:** To protect our users and infrastructure, we reserve the right to limit the technical details shared in public acknowledgments. We generally describe the issue category (e.g., "Discovered and helped mitigate XSS vulnerability") rather than providing specific exploit steps. + +## Scope + +### In Scope +* [bitbybit.dev](https://bitbybit.dev) website and platform +* Bitbybit open-source repositories and NPM packages + +### Out of Scope +The following are generally considered out of scope and will not be acknowledged unless they present a severe risk: +* Social engineering (phishing) of Bitbybit staff or contractors. +* Denial of Service (DoS) attacks. +* **Automated scanner reports** without a reproducible PoC. +* Missing security headers (e.g., HSTS, CSP) without a clear exploitation scenario. +* Self-XSS (manually entered by the user). +* Vulnerabilities in third-party dependencies (unless directly caused by our implementation). +* Login/Logout CSRF. \ No newline at end of file diff --git a/docs/learn/tags.yml b/docs/learn/tags.yml index bf648e88..3786e58b 100644 --- a/docs/learn/tags.yml +++ b/docs/learn/tags.yml @@ -796,4 +796,14 @@ simple-matching: product-options: label: Product Options permalink: /product-options - description: Product options and configurations. \ No newline at end of file + description: Product options and configurations. + +cdn: + label: CDN + permalink: /cdn + description: Content Delivery Network and caching. + +hosting: + label: Hosting + permalink: /hosting + description: Web hosting and server management. \ No newline at end of file From 790525a6c2635343b40a3b6d37f43a0c2ad7c865 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 11:22:18 +0200 Subject: [PATCH 02/38] fixed wire splitting bug --- .../occt/lib/services/base/wires.service.ts | 9 +- .../dev/occt/lib/services/shapes/wire.test.ts | 297 +++++++++++++++++- 2 files changed, 301 insertions(+), 5 deletions(-) diff --git a/packages/dev/occt/lib/services/base/wires.service.ts b/packages/dev/occt/lib/services/base/wires.service.ts index ed6f6172..f72a9f34 100644 --- a/packages/dev/occt/lib/services/base/wires.service.ts +++ b/packages/dev/occt/lib/services/base/wires.service.ts @@ -582,10 +582,11 @@ export class WiresService { splitOnPoints(inputs: Inputs.OCCT.SplitWireOnPointsDto): TopoDS_Wire[] { const wire = inputs.shape; - const splitPoints = inputs.points; + // Remove duplicate points to avoid incorrect behavior + const splitPoints = this.vecHelper.removeAllDuplicateVectors(inputs.points, 1e-7); - // 1. Get the list of edges from the wire - const edges = this.shapeGettersService.getEdges({ shape: wire }); + // 1. Get the list of edges from the wire in correct order along the wire + const edges = this.edgesService.getEdgesAlongWire({ shape: wire }); if (edges.length === 0) return []; // 2. Collect split locations as {edgeIndex, parameter} @@ -611,7 +612,7 @@ export class WiresService { const firstVal = first.current; const lastVal = last.current; - const gpPnt = this.entitiesService.gpPnt(pt); + const gpPnt = this.entitiesService.gpPnt(pt as Base.Point3); const projector = new this.occ.GeomAPI_ProjectPointOnCurve_2(gpPnt, curve); if (projector.NbPoints() > 0) { const param = projector.LowerDistanceParameter(); diff --git a/packages/dev/occt/lib/services/shapes/wire.test.ts b/packages/dev/occt/lib/services/shapes/wire.test.ts index 2a1e1797..d8507879 100644 --- a/packages/dev/occt/lib/services/shapes/wire.test.ts +++ b/packages/dev/occt/lib/services/shapes/wire.test.ts @@ -1103,7 +1103,7 @@ describe("OCCT wire unit tests", () => { }); // TODO this test is failing because theres a bug in splitting on points method - xit("should split filleted triangle by points", () => { + it("should split filleted triangle by points", () => { const triangle = wire.createPolygonWire({ points: [ [-0.5, 0, -0.28867513459], @@ -1126,6 +1126,301 @@ describe("OCCT wire unit tests", () => { }); }); + it("should split filleted square by points", () => { + const square = wire.createSquareWire({ + size: 2, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const filletedSquare = fillets.fillet2d({ shape: square, radius: 0.2 }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: filletedSquare, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 8 + }); + const split = wire.splitOnPoints({ shape: filletedSquare, points: pts }); + expect(split.length).toBe(8); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const totalLength = segmentLengths.reduce((a, b) => a + b, 0); + const expectedTotalLength = wire.getWireLength({ shape: filletedSquare }); + // Verify total length is preserved + expect(totalLength).toBeCloseTo(expectedTotalLength, 2); + }); + + it("should split filleted rectangle by points with many divisions", () => { + const rectangle = wire.createRectangleWire({ + width: 3, + length: 2, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const filletedRect = fillets.fillet2d({ shape: rectangle, radius: 0.3 }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: filletedRect, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 20 + }); + const split = wire.splitOnPoints({ shape: filletedRect, points: pts }); + expect(split.length).toBe(20); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const totalLength = segmentLengths.reduce((a, b) => a + b, 0); + const expectedTotalLength = wire.getWireLength({ shape: filletedRect }); + // Verify total length is preserved + expect(totalLength).toBeCloseTo(expectedTotalLength, 2); + }); + + it("should split ellipse wire by points", () => { + const ellipse = wire.createEllipseWire({ + radiusMajor: 2, + radiusMinor: 1, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: ellipse, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 12 + }); + const split = wire.splitOnPoints({ shape: ellipse, points: pts }); + expect(split.length).toBe(12); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const totalLength = segmentLengths.reduce((a, b) => a + b, 0); + const expectedTotalLength = wire.getWireLength({ shape: ellipse }); + // Verify total length is preserved (tolerance for curve parameterization differences) + expect(totalLength).toBeCloseTo(expectedTotalLength, 1); + }); + + it("should split bspline wire by points", () => { + const bspline = wire.createBSpline({ + points: [[0, 0, 0], [1, 2, 0], [2, 0, 0], [3, 2, 0], [4, 0, 0]], + closed: false + }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: bspline, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 15 + }); + const split = wire.splitOnPoints({ shape: bspline, points: pts }); + expect(split.length).toBe(15); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const totalLength = segmentLengths.reduce((a, b) => a + b, 0); + const expectedTotalLength = wire.getWireLength({ shape: bspline }); + // Verify total length is preserved (small tolerance for curve reconstruction differences) + expect(totalLength).toBeCloseTo(expectedTotalLength, 0); + }); + + it("should split closed bspline wire by points", () => { + const bspline = wire.createBSpline({ + points: [[0, 0, 0], [1, 2, 0], [2, 0, 0], [3, 2, 0], [2, 3, 0], [0, 2, 0]], + closed: true + }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: bspline, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 10 + }); + const split = wire.splitOnPoints({ shape: bspline, points: pts }); + expect(split.length).toBe(10); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const totalLength = segmentLengths.reduce((a, b) => a + b, 0); + const expectedTotalLength = wire.getWireLength({ shape: bspline }); + // Verify total length is preserved for closed bspline (small tolerance for curve reconstruction) + expect(totalLength).toBeCloseTo(expectedTotalLength, 0); + }); + + it("should split bezier wire by points", () => { + const bezier = wire.createBezier({ + points: [[0, 0, 0], [1, 3, 0], [2, -1, 0], [3, 2, 0]], + closed: false + }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: bezier, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 8 + }); + const split = wire.splitOnPoints({ shape: bezier, points: pts }); + expect(split.length).toBe(8); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const expectedLength = wire.getWireLength({ shape: bezier }) / 8; + // Allow slightly looser tolerance for bezier curve parameterization + segmentLengths.forEach(len => { + expect(len).toBeCloseTo(expectedLength, 3); + }); + }); + + it("should split filleted pentagon (ngon) by points", () => { + const pentagon = wire.createNGonWire({ + nrCorners: 5, + radius: 2, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const filletedPentagon = fillets.fillet2d({ shape: pentagon, radius: 0.2 }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: filletedPentagon, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 10 + }); + const split = wire.splitOnPoints({ shape: filletedPentagon, points: pts }); + expect(split.length).toBe(10); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const totalLength = segmentLengths.reduce((a, b) => a + b, 0); + const expectedTotalLength = wire.getWireLength({ shape: filletedPentagon }); + // Verify total length is preserved + expect(totalLength).toBeCloseTo(expectedTotalLength, 2); + }); + + it("should split filleted hexagon by points with odd number of divisions", () => { + const hexagon = wire.createNGonWire({ + nrCorners: 6, + radius: 1.5, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const filletedHexagon = fillets.fillet2d({ shape: hexagon, radius: 0.15 }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: filletedHexagon, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 7 + }); + const split = wire.splitOnPoints({ shape: filletedHexagon, points: pts }); + expect(split.length).toBe(7); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const expectedLength = wire.getWireLength({ shape: filletedHexagon }) / 7; + segmentLengths.forEach(len => { + expect(len).toBeCloseTo(expectedLength, 4); + }); + }); + + it("should split L-polygon wire by points", () => { + const lPolygon = wire.createLPolygonWire({ + widthFirst: 2, + lengthFirst: 3, + widthSecond: 1, + lengthSecond: 1.5, + align: Inputs.OCCT.directionEnum.outside, + rotation: 0, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: lPolygon, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 12 + }); + const split = wire.splitOnPoints({ shape: lPolygon, points: pts }); + expect(split.length).toBe(12); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const expectedLength = wire.getWireLength({ shape: lPolygon }) / 12; + segmentLengths.forEach(len => { + expect(len).toBeCloseTo(expectedLength, 4); + }); + }); + + it("should split parallelogram wire by points", () => { + const parallelogram = wire.createParallelogramWire({ + width: 3, + height: 2, + angle: 30, + aroundCenter: true, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: parallelogram, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 8 + }); + const split = wire.splitOnPoints({ shape: parallelogram, points: pts }); + expect(split.length).toBe(8); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const expectedLength = wire.getWireLength({ shape: parallelogram }) / 8; + segmentLengths.forEach(len => { + expect(len).toBeCloseTo(expectedLength, 4); + }); + }); + + it("should split combined wire with mixed edge types by points", () => { + // Create a wire that combines straight edges and a curved edge + const line1 = wire.createLineWire({ start: [0, 0, 0], end: [2, 0, 0] }); + const arc = edge.arcThroughThreePoints({ start: [2, 0, 0], middle: [2.5, 0, 0.5], end: [2, 0, 1] }); + const arcWire = wire.createWireFromEdge({ shape: arc }); + const line2 = wire.createLineWire({ start: [2, 0, 1], end: [0, 0, 1] }); + const combinedWire = wire.combineEdgesAndWiresIntoAWire({ shapes: [line1, arcWire, line2] }); + + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: combinedWire, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 10 + }); + const split = wire.splitOnPoints({ shape: combinedWire, points: pts }); + expect(split.length).toBe(10); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const expectedLength = wire.getWireLength({ shape: combinedWire }) / 10; + segmentLengths.forEach(len => { + expect(len).toBeCloseTo(expectedLength, 4); + }); + }); + + it("should split filleted L-polygon wire by points", () => { + const lPolygon = wire.createLPolygonWire({ + widthFirst: 2, + lengthFirst: 3, + widthSecond: 1, + lengthSecond: 1.5, + align: Inputs.OCCT.directionEnum.middle, + rotation: 0, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const filletedL = fillets.fillet2d({ shape: lPolygon, radius: 0.15 }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: filletedL, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 16 + }); + const split = wire.splitOnPoints({ shape: filletedL, points: pts }); + expect(split.length).toBe(16); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const expectedLength = wire.getWireLength({ shape: filletedL }) / 16; + segmentLengths.forEach(len => { + expect(len).toBeCloseTo(expectedLength, 4); + }); + }); + + it("should split periodic interpolated wire by points", () => { + const periodicWire = wire.interpolatePoints({ + points: [[0, 0, 0], [2, 1, 0], [3, 0, 0], [2, -1, 0]], + periodic: true, + tolerance: 1e-7 + }); + const pts = wire.divideWireByEqualDistanceToPoints({ + shape: periodicWire, + removeEndPoint: true, + removeStartPoint: false, + nrOfDivisions: 12 + }); + const split = wire.splitOnPoints({ shape: periodicWire, points: pts }); + expect(split.length).toBe(12); + const segmentLengths = split.map((s) => wire.getWireLength({ shape: s })); + const expectedLength = wire.getWireLength({ shape: periodicWire }) / 12; + segmentLengths.forEach(len => { + expect(len).toBeCloseTo(expectedLength, 4); + }); + }); + it("should split square wire by points", () => { const square = wire.createSquareWire({ size: 1, From a79c9f460b7c43c6b8aad6efbadc3bd5282af2fa Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 11:35:31 +0200 Subject: [PATCH 03/38] mesh mesh intersection unit tests --- .../dev/occt/lib/services/booleans.test.ts | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/packages/dev/occt/lib/services/booleans.test.ts b/packages/dev/occt/lib/services/booleans.test.ts index 949c48ef..467f4aaf 100644 --- a/packages/dev/occt/lib/services/booleans.test.ts +++ b/packages/dev/occt/lib/services/booleans.test.ts @@ -4,10 +4,12 @@ import { VectorHelperService } from "../api/vector-helper.service"; import { ShapesHelperService } from "../api/shapes-helper.service"; import { OCCTBooleans } from "./booleans"; import { OCCTSolid } from "./shapes/solid"; +import { OCCTWire } from "./shapes/wire"; describe("OCCT booleans unit tests", () => { let occt: OpenCascadeInstance; let solid: OCCTSolid; + let wire: OCCTWire; let booleans: OCCTBooleans; let occHelper: OccHelper; @@ -18,6 +20,7 @@ describe("OCCT booleans unit tests", () => { occHelper = new OccHelper(vec, s, occt); solid = new OCCTSolid(occt, occHelper); + wire = new OCCTWire(occt, occHelper); booleans = new OCCTBooleans(occt, occHelper); }); @@ -50,5 +53,109 @@ describe("OCCT booleans unit tests", () => { expect(() => booleans.difference({ shape: box1, shapes: [], keepEdges: false })).toThrowError("Shape is not a compound or is null."); }); + it("should compute mesh mesh intersection wires of two intersecting boxes", async () => { + const box1 = solid.createBox({ width: 2, height: 2, length: 2, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 2, height: 2, length: 2, center: [1, 1, 1] }); + const wires = booleans.meshMeshIntersectionWires({ shape1: box1, shape2: box2, precision1: 0.01, precision2: 0.01 }); + expect(wires.length).toBe(1); + const totalLength = wires.reduce((acc, w) => acc + wire.getWireLength({ shape: w }), 0); + expect(totalLength).toBe(6); + box1.delete(); + box2.delete(); + wires.forEach(w => w.delete()); + }); + + it("should compute mesh mesh intersection points of two intersecting boxes", async () => { + const box1 = solid.createBox({ width: 2, height: 2, length: 2, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 2, height: 2, length: 2, center: [1, 1, 1] }); + const points = booleans.meshMeshIntersectionPoints({ shape1: box1, shape2: box2, precision1: 0.01, precision2: 0.01 }); + expect(points.length).toBe(1); + expect(points[0].length).toBe(7); + expect(points[0][0].length).toBe(3); + box1.delete(); + box2.delete(); + }); + + it("should return empty wires for non-intersecting boxes", async () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 5, 5] }); + const wires = booleans.meshMeshIntersectionWires({ shape1: box1, shape2: box2, precision1: 0.01, precision2: 0.01 }); + expect(wires.length).toBe(0); + box1.delete(); + box2.delete(); + }); + + it("should return empty points for non-intersecting boxes", async () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 5, 5] }); + const points = booleans.meshMeshIntersectionPoints({ shape1: box1, shape2: box2, precision1: 0.01, precision2: 0.01 }); + expect(points.length).toBe(0); + box1.delete(); + box2.delete(); + }); + + it("should compute mesh mesh intersection of shapes wires", async () => { + const box1 = solid.createBox({ width: 2, height: 2, length: 2, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 2, height: 2, length: 2, center: [1, 1, 1] }); + const box3 = solid.createBox({ width: 2, height: 2, length: 2, center: [-1, -1, -1] }); + const wires = booleans.meshMeshIntersectionOfShapesWires({ shape: box1, shapes: [box2, box3], precision: 0.01 }); + expect(wires.length).toBe(2); + const totalLength = wires.reduce((acc, w) => acc + wire.getWireLength({ shape: w }), 0); + expect(totalLength).toBe(12); + box1.delete(); + box2.delete(); + box3.delete(); + wires.forEach(w => w.delete()); + }); + + it("should compute mesh mesh intersection of shapes points", async () => { + const box1 = solid.createBox({ width: 2, height: 2, length: 2, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 2, height: 2, length: 2, center: [1, 1, 1] }); + const box3 = solid.createBox({ width: 2, height: 2, length: 2, center: [-1, -1, -1] }); + const points = booleans.meshMeshIntersectionOfShapesPoints({ shape: box1, shapes: [box2, box3], precision: 0.01 }); + expect(points.length).toBe(2); + expect(points[0].length).toBe(7); + expect(points[0][0].length).toBe(3); + box1.delete(); + box2.delete(); + box3.delete(); + }); + + it("should compute mesh mesh intersection of shapes with custom precisions", async () => { + const box1 = solid.createBox({ width: 2, height: 2, length: 2, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 2, height: 2, length: 2, center: [1, 1, 1] }); + const box3 = solid.createBox({ width: 2, height: 2, length: 2, center: [-1, -1, -1] }); + const wires = booleans.meshMeshIntersectionOfShapesWires({ shape: box1, shapes: [box2, box3], precision: 0.05, precisionShapes: [0.02, 0.03] }); + expect(wires.length).toBe(2); + box1.delete(); + box2.delete(); + box3.delete(); + wires.forEach(w => w.delete()); + }); + + it("should return empty wires for mesh mesh intersection of shapes with no intersections", async () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [10, 10, 10] }); + const box3 = solid.createBox({ width: 1, height: 1, length: 1, center: [-10, -10, -10] }); + const wires = booleans.meshMeshIntersectionOfShapesWires({ shape: box1, shapes: [box2, box3], precision: 0.01 }); + expect(wires.length).toBe(0); + box1.delete(); + box2.delete(); + box3.delete(); + }); + + it("should compute mesh mesh intersection with sphere and box", async () => { + const sphere = solid.createSphere({ radius: 1, center: [0, 0, 0] }); + const box = solid.createBox({ width: 1, height: 1, length: 1, center: [0.5, 0.5, 0.5] }); + const wires = booleans.meshMeshIntersectionWires({ shape1: sphere, shape2: box, precision1: 0.01, precision2: 0.01 }); + expect(wires.length).toBe(12); + const points = booleans.meshMeshIntersectionPoints({ shape1: sphere, shape2: box, precision1: 0.01, precision2: 0.01 }); + expect(points.length).toBe(12); + sphere.delete(); + box.delete(); + wires.forEach(w => w.delete()); + }); + }); + From 156427ed9796d448de15daf208bb8176c657506b Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 11:38:14 +0200 Subject: [PATCH 04/38] remove io-assembly from coverage --- packages/dev/occt/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dev/occt/package.json b/packages/dev/occt/package.json index 10e00ebe..7d417f83 100644 --- a/packages/dev/occt/package.json +++ b/packages/dev/occt/package.json @@ -71,7 +71,8 @@ "lib/occ-service.ts", "lib/api/shapes-helper.service.ts", "lib/api/vector-helper.service.ts", - "!**/index.ts" + "!**/index.ts", + "!**/io-assembly.ts" ] } } \ No newline at end of file From 146909c29401b2d789b31dea420868ea5bf82bdc Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 11:50:37 +0200 Subject: [PATCH 05/38] additional solid unit tests --- .../occt/lib/services/shapes/solid.test.ts | 294 +++++++++++++++++- 1 file changed, 281 insertions(+), 13 deletions(-) diff --git a/packages/dev/occt/lib/services/shapes/solid.test.ts b/packages/dev/occt/lib/services/shapes/solid.test.ts index a6f3484b..d58f93f4 100644 --- a/packages/dev/occt/lib/services/shapes/solid.test.ts +++ b/packages/dev/occt/lib/services/shapes/solid.test.ts @@ -163,13 +163,13 @@ describe("OCCT solid unit tests", () => { const filteredPoints = solid.filterSolidPoints(filterOptions); expect(filteredPoints.length).toBe(13); expect(filteredPoints).toEqual([ - [ 0, 0, -1 ], [ -0.5, 0, -0.5 ], - [ 0, 0, -0.5 ], [ 0.5, 0, -0.5 ], - [ -1, 0, 0 ], [ -0.5, 0, 0 ], - [ 0, 0, 0 ], [ 0.5, 0, 0 ], - [ 1, 0, 0 ], [ -0.5, 0, 0.5 ], - [ 0, 0, 0.5 ], [ 0.5, 0, 0.5 ], - [ 0, 0, 1 ] + [0, 0, -1], [-0.5, 0, -0.5], + [0, 0, -0.5], [0.5, 0, -0.5], + [-1, 0, 0], [-0.5, 0, 0], + [0, 0, 0], [0.5, 0, 0], + [1, 0, 0], [-0.5, 0, 0.5], + [0, 0, 0.5], [0.5, 0, 0.5], + [0, 0, 1] ]); sphere.delete(); f1.delete(); @@ -190,12 +190,12 @@ describe("OCCT solid unit tests", () => { const filteredPoints = solid.filterSolidPoints(filterOptions); expect(filteredPoints.length).toBe(12); expect(filteredPoints).toEqual([ - [ -1, 0, -1 ], [ -0.5, 0, -1 ], - [ 0.5, 0, -1 ], [ 1, 0, -1 ], - [ -1, 0, -0.5 ], [ 1, 0, -0.5 ], - [ -1, 0, 0.5 ], [ 1, 0, 0.5 ], - [ -1, 0, 1 ], [ -0.5, 0, 1 ], - [ 0.5, 0, 1 ], [ 1, 0, 1 ] + [-1, 0, -1], [-0.5, 0, -1], + [0.5, 0, -1], [1, 0, -1], + [-1, 0, -0.5], [1, 0, -0.5], + [-1, 0, 0.5], [1, 0, 0.5], + [-1, 0, 1], [-0.5, 0, 1], + [0.5, 0, 1], [1, 0, 1] ]); sphere.delete(); f1.delete(); @@ -229,4 +229,272 @@ describe("OCCT solid unit tests", () => { expect(filteredPoints.length).toBe(0); sphere.delete(); }); + + // I-Beam profile solid tests + it("should create an I-beam profile solid with default values", async () => { + // I-beam area: 2 flanges (width * flangeThickness) + web ((height - 2*flangeThickness) * webThickness) + // width=2, height=3, webThickness=0.2, flangeThickness=0.3, extrusionLengthFront=1 + // Area = 2 * (2 * 0.3) + (3 - 2*0.3) * 0.2 = 1.2 + 2.4 * 0.2 = 1.2 + 0.48 = 1.68 + // Volume = 1.68 * 1 = 1.68 + const opt = new Inputs.OCCT.IBeamProfileSolidDto(2, 3, 0.2, 0.3); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const ibeam = solid.createIBeamProfileSolid(opt); + const volume = solid.getSolidVolume({ shape: ibeam }); + expect(volume).toBeCloseTo(1.68); + ibeam.delete(); + }); + + it("should create an I-beam profile solid with bidirectional extrusion", async () => { + // Same area as above: 1.68 + // Volume = 1.68 * (1 + 0.5) = 1.68 * 1.5 = 2.52 + const opt = new Inputs.OCCT.IBeamProfileSolidDto(2, 3, 0.2, 0.3); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0.5; + const ibeam = solid.createIBeamProfileSolid(opt); + const volume = solid.getSolidVolume({ shape: ibeam }); + expect(volume).toBeCloseTo(2.52); + ibeam.delete(); + }); + + // H-Beam profile solid tests + it("should create an H-beam profile solid with default values", async () => { + // H-beam area: 2 flanges (height * flangeThickness) + web ((width - 2*flangeThickness) * webThickness) + // width=2, height=3, webThickness=0.2, flangeThickness=0.3, extrusionLengthFront=1 + // Area = 2 * (3 * 0.3) + (2 - 2*0.3) * 0.2 = 1.8 + 1.4 * 0.2 = 1.8 + 0.28 = 2.08 + // Volume = 2.08 * 1 = 2.08 + const opt = new Inputs.OCCT.HBeamProfileSolidDto(2, 3, 0.2, 0.3); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const hbeam = solid.createHBeamProfileSolid(opt); + const volume = solid.getSolidVolume({ shape: hbeam }); + expect(volume).toBeCloseTo(2.08); + hbeam.delete(); + }); + + it("should create an H-beam profile solid with backward extrusion only", async () => { + // Volume = 2.08 * 2 = 4.16 + const opt = new Inputs.OCCT.HBeamProfileSolidDto(2, 3, 0.2, 0.3); + opt.extrusionLengthFront = 0; + opt.extrusionLengthBack = 2; + const hbeam = solid.createHBeamProfileSolid(opt); + const volume = solid.getSolidVolume({ shape: hbeam }); + expect(volume).toBeCloseTo(4.16); + hbeam.delete(); + }); + + // T-Beam profile solid tests + it("should create a T-beam profile solid with default values", async () => { + // T-beam area: 1 flange (width * flangeThickness) + web ((height - flangeThickness) * webThickness) + // width=2, height=2, webThickness=0.2, flangeThickness=0.3, extrusionLengthFront=1 + // Area = (2 * 0.3) + (2 - 0.3) * 0.2 = 0.6 + 1.7 * 0.2 = 0.6 + 0.34 = 0.94 + // Volume = 0.94 * 1 = 0.94 + const opt = new Inputs.OCCT.TBeamProfileSolidDto(2, 2, 0.2, 0.3); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const tbeam = solid.createTBeamProfileSolid(opt); + const volume = solid.getSolidVolume({ shape: tbeam }); + expect(volume).toBeCloseTo(0.94); + tbeam.delete(); + }); + + // U-Beam profile solid tests + it("should create a U-beam profile solid with default values", async () => { + // U-beam area: 2 vertical flanges + bottom web + // width=2, height=3, webThickness=0.2, flangeThickness=0.3, flangeWidth=0.5, extrusionLengthFront=1 + const opt = new Inputs.OCCT.UBeamProfileSolidDto(2, 3, 0.2, 0.3, 0.5); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const ubeam = solid.createUBeamProfileSolid(opt); + const volume = solid.getSolidVolume({ shape: ubeam }); + // Actual computed volume is 2.25 + expect(volume).toBeCloseTo(2.25); + ubeam.delete(); + }); + + // Star solid tests + it("should create a star solid with default values", async () => { + const opt = new Inputs.OCCT.StarSolidDto(2, 1, 5); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const star = solid.createStarSolid(opt); + const volume = solid.getSolidVolume({ shape: star }); + // 5-pointed star area ≈ 5 * (0.5 * outerRadius * innerRadius * sin(2π/5)) = 5 * 0.5 * 2 * 1 * sin(72°) + // For a 5-pointed star with outer=2, inner=1: + // Area can be calculated as n * r1 * r2 * sin(π/n) where n=5 + // = 5 * 2 * 1 * sin(36°) ≈ 5 * 2 * 0.588 ≈ 5.878 + // Volume ≈ 5.878 + expect(volume).toBeCloseTo(5.877, 2); + + star.delete(); + }); + + it("should create a star solid and verify volume is positive", async () => { + const opt = new Inputs.OCCT.StarSolidDto(2, 1, 6); + opt.half = false; + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const star = solid.createStarSolid(opt); + const volume = solid.getSolidVolume({ shape: star }); + // 6-pointed star with outer=2, inner=1 + expect(volume).toBe(6); + star.delete(); + }); + + // NGon solid tests + it("should create a hexagon (6-gon) solid", async () => { + // Hexagon area = (3 * sqrt(3) / 2) * r^2 where r is the radius + // For radius = 1: Area = (3 * 1.732 / 2) * 1 = 2.598 + // Volume = 2.598 * 1 = 2.598 + const opt = new Inputs.OCCT.NGonSolidDto([0, 0, 0], [0, 1, 0], 6, 1); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const hexagon = solid.createNGonSolid(opt); + const volume = solid.getSolidVolume({ shape: hexagon }); + expect(volume).toBeCloseTo(2.598, 2); + hexagon.delete(); + }); + + it("should create a pentagon (5-gon) solid", async () => { + // Pentagon area = (5/4) * r^2 * sqrt(10 + 2*sqrt(5)) / sqrt(5) + // Simplified: (5/2) * r^2 * sin(72°) = 2.5 * 1 * 0.951 = 2.378 + const opt = new Inputs.OCCT.NGonSolidDto([0, 0, 0], [0, 1, 0], 5, 1); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const pentagon = solid.createNGonSolid(opt); + const volume = solid.getSolidVolume({ shape: pentagon }); + expect(volume).toBeCloseTo(2.378, 2); + pentagon.delete(); + }); + + it("should create a triangle (3-gon) solid", async () => { + // Equilateral triangle inscribed in circle of radius r + // Area = (3 * sqrt(3) / 4) * (r * sqrt(3))^2 = (3 * sqrt(3) / 4) * 3 * r^2 = (9 * sqrt(3) / 4) * r^2 + // For r = 1: Area = 1.299 + const opt = new Inputs.OCCT.NGonSolidDto([0, 0, 0], [0, 1, 0], 3, 1); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const triangle = solid.createNGonSolid(opt); + const volume = solid.getSolidVolume({ shape: triangle }); + expect(volume).toBeCloseTo(1.299, 2); + triangle.delete(); + }); + + // Parallelogram solid tests + it("should create a parallelogram solid", async () => { + // Parallelogram area = width * height (base * height for the slanted shape) + // width=2, height=1, angle=15 degrees + // Area = 2 * 1 = 2 + // Volume = 2 * 1 = 2 + const opt = new Inputs.OCCT.ParallelogramSolidDto([0, 0, 0], [0, 1, 0], true, 2, 1, 15); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const parallelogram = solid.createParallelogramSolid(opt); + const volume = solid.getSolidVolume({ shape: parallelogram }); + expect(volume).toBeCloseTo(2); + parallelogram.delete(); + }); + + it("should create a parallelogram solid with bidirectional extrusion", async () => { + const opt = new Inputs.OCCT.ParallelogramSolidDto([0, 0, 0], [0, 1, 0], true, 2, 1, 30); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 1; + const parallelogram = solid.createParallelogramSolid(opt); + const volume = solid.getSolidVolume({ shape: parallelogram }); + // Volume = 2 * 2 = 4 + expect(volume).toBeCloseTo(4); + parallelogram.delete(); + }); + + // Heart solid tests + it("should create a heart solid", async () => { + const opt = new Inputs.OCCT.HeartSolidDto([0, 0, 0], [0, 1, 0], 0, 2); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const heart = solid.createHeartSolid(opt); + const volume = solid.getSolidVolume({ shape: heart }); + // Heart shape volume depends on the parametric curve + expect(volume).toBeCloseTo(2.732, 2); + heart.delete(); + }); + + it("should create a rotated heart solid with same volume", async () => { + const opt1 = new Inputs.OCCT.HeartSolidDto([0, 0, 0], [0, 1, 0], 0, 2); + opt1.extrusionLengthFront = 1; + opt1.extrusionLengthBack = 0; + const heart1 = solid.createHeartSolid(opt1); + + const opt2 = new Inputs.OCCT.HeartSolidDto([0, 0, 0], [0, 1, 0], 45, 2); + opt2.extrusionLengthFront = 1; + opt2.extrusionLengthBack = 0; + const heart2 = solid.createHeartSolid(opt2); + + const volume1 = solid.getSolidVolume({ shape: heart1 }); + const volume2 = solid.getSolidVolume({ shape: heart2 }); + expect(volume1).toBeCloseTo(volume2); + heart1.delete(); + heart2.delete(); + }); + + // Christmas tree solid tests + it("should create a christmas tree solid", async () => { + const opt = new Inputs.OCCT.ChristmasTreeSolidDto(6, 1.5, 3, 5, 1, 1, false, 0, [0, 0, 0], [0, 1, 0]); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const tree = solid.createChristmasTreeSolid(opt); + const volume = solid.getSolidVolume({ shape: tree }); + expect(volume).toBeCloseTo(15.687, 2); + tree.delete(); + }); + + it("should create christmas tree solid with bidirectional extrusion", async () => { + const opt = new Inputs.OCCT.ChristmasTreeSolidDto(6, 1.5, 3, 5, 1, 1, false, 0, [0, 0, 0], [0, 1, 0]); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 1; + const tree = solid.createChristmasTreeSolid(opt); + const volume = solid.getSolidVolume({ shape: tree }); + // Bidirectional extrusion should double the volume + expect(volume).toBeCloseTo(31.375, 2); + tree.delete(); + }); + + // L-Polygon solid tests + it("should create an L-polygon solid with default values", async () => { + // L-polygon area depends on alignment mode + // widthFirst=1, lengthFirst=2, widthSecond=0.5, lengthSecond=2 + const opt = new Inputs.OCCT.LPolygonSolidDto(1, 2, 0.5, 2); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 0; + const lpolygon = solid.createLPolygonSolid(opt); + const volume = solid.getSolidVolume({ shape: lpolygon }); + // Actual computed volume is 3.5 + expect(volume).toBeCloseTo(3.5); + lpolygon.delete(); + }); + + it("should create an L-polygon solid with bidirectional extrusion", async () => { + const opt = new Inputs.OCCT.LPolygonSolidDto(1, 2, 0.5, 2); + opt.extrusionLengthFront = 1; + opt.extrusionLengthBack = 1; + const lpolygon = solid.createLPolygonSolid(opt); + const volume = solid.getSolidVolume({ shape: lpolygon }); + // Volume = 3.5 * 2 = 7 + expect(volume).toBeCloseTo(7); + lpolygon.delete(); + }); + + // Error case tests + it("should throw error when both extrusion lengths are zero", async () => { + const opt = new Inputs.OCCT.IBeamProfileSolidDto(2, 3, 0.2, 0.3); + opt.extrusionLengthFront = 0; + opt.extrusionLengthBack = 0; + expect(() => solid.createIBeamProfileSolid(opt)).toThrow("Cannot create solid: both extrusionLengthFront and extrusionLengthBack are 0"); + }); + + it("should throw error for NGon solid when both extrusion lengths are zero", async () => { + const opt = new Inputs.OCCT.NGonSolidDto([0, 0, 0], [0, 1, 0], 6, 1); + opt.extrusionLengthFront = 0; + opt.extrusionLengthBack = 0; + expect(() => solid.createNGonSolid(opt)).toThrow("Cannot create solid: both extrusionLengthFront and extrusionLengthBack are 0"); + }); }); From d05173d97d75aa1c4bab2ba40080d7af8f8646e2 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 11:57:34 +0200 Subject: [PATCH 06/38] additional edge unit tests --- .../dev/occt/lib/services/shapes/edge.test.ts | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) diff --git a/packages/dev/occt/lib/services/shapes/edge.test.ts b/packages/dev/occt/lib/services/shapes/edge.test.ts index eeee5a75..e2254fb1 100644 --- a/packages/dev/occt/lib/services/shapes/edge.test.ts +++ b/packages/dev/occt/lib/services/shapes/edge.test.ts @@ -1259,4 +1259,310 @@ describe("OCCT edge unit tests", () => { circle.delete(); edges.forEach(e => e.delete()); }; + + // fromBaseLine tests + it("should create an edge from base line", async () => { + const line: Inputs.Base.Line3 = { start: [0, 0, 0], end: [1, 1, 1] }; + const e = edge.fromBaseLine({ line }); + const length = edge.getEdgeLength({ shape: e }); + expect(length).toBeCloseTo(Math.sqrt(3), closeToNr); + e.delete(); + }); + + // fromBaseLines tests + it("should create edges from base lines", async () => { + const lines: Inputs.Base.Line3[] = [ + { start: [0, 0, 0], end: [1, 0, 0] }, + { start: [0, 0, 0], end: [0, 2, 0] }, + { start: [0, 0, 0], end: [0, 0, 3] } + ]; + const edges = edge.fromBaseLines({ lines }); + expect(edges.length).toBe(3); + const lengths = edge.getEdgesLengths({ shapes: edges }); + expect(lengths[0]).toBeCloseTo(1, closeToNr); + expect(lengths[1]).toBeCloseTo(2, closeToNr); + expect(lengths[2]).toBeCloseTo(3, closeToNr); + edges.forEach(e => e.delete()); + }); + + // fromBaseSegment tests + it("should create an edge from base segment", async () => { + const segment: Inputs.Base.Segment3 = [[0, 0, 0], [2, 2, 2]]; + const e = edge.fromBaseSegment({ segment }); + const length = edge.getEdgeLength({ shape: e }); + expect(length).toBeCloseTo(Math.sqrt(12), closeToNr); + e.delete(); + }); + + // fromBaseSegments tests + it("should create edges from base segments", async () => { + const segments: Inputs.Base.Segment3[] = [ + [[0, 0, 0], [1, 0, 0]], + [[0, 0, 0], [0, 2, 0]], + [[0, 0, 0], [0, 0, 3]] + ]; + const edges = edge.fromBaseSegments({ segments }); + expect(edges.length).toBe(3); + const lengths = edge.getEdgesLengths({ shapes: edges }); + expect(lengths[0]).toBeCloseTo(1, closeToNr); + expect(lengths[1]).toBeCloseTo(2, closeToNr); + expect(lengths[2]).toBeCloseTo(3, closeToNr); + edges.forEach(e => e.delete()); + }); + + // fromPoints tests + it("should create edges from points", async () => { + const points: Inputs.Base.Point3[] = [ + [0, 0, 0], + [1, 0, 0], + [1, 1, 0], + [0, 1, 0] + ]; + const edges = edge.fromPoints({ points }); + expect(edges.length).toBe(3); + const lengths = edge.getEdgesLengths({ shapes: edges }); + expect(lengths[0]).toBeCloseTo(1, closeToNr); + expect(lengths[1]).toBeCloseTo(1, closeToNr); + expect(lengths[2]).toBeCloseTo(1, closeToNr); + edges.forEach(e => e.delete()); + }); + + it("should create no edges from single point", async () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0]]; + const edges = edge.fromPoints({ points }); + expect(edges.length).toBe(0); + }); + + // fromBasePolyline tests + it("should create edges from open polyline", async () => { + const polyline: Inputs.Base.Polyline3 = { + points: [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], + isClosed: false + }; + const edges = edge.fromBasePolyline({ polyline }); + expect(edges.length).toBe(3); + edges.forEach(e => e.delete()); + }); + + it("should create edges from closed polyline", async () => { + const polyline: Inputs.Base.Polyline3 = { + points: [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], + isClosed: true + }; + const edges = edge.fromBasePolyline({ polyline }); + expect(edges.length).toBe(4); + const totalLength = edge.getEdgesLengths({ shapes: edges }).reduce((a, b) => a + b, 0); + expect(totalLength).toBeCloseTo(4, closeToNr); + edges.forEach(e => e.delete()); + }); + + // fromBaseTriangle tests + it("should create edges from triangle", async () => { + const triangle: Inputs.Base.Triangle3 = [ + [0, 0, 0], + [1, 0, 0], + [0.5, 1, 0] + ]; + const edges = edge.fromBaseTriangle({ triangle }); + expect(edges.length).toBe(3); + edges.forEach(e => e.delete()); + }); + + // fromBaseMesh tests + it("should create edges from mesh", async () => { + const mesh: Inputs.Base.Mesh3 = [ + [[0, 0, 0], [1, 0, 0], [0.5, 1, 0]], + [[1, 0, 0], [2, 0, 0], [1.5, 1, 0]] + ]; + const edges = edge.fromBaseMesh({ mesh }); + expect(edges.length).toBe(6); // 2 triangles * 3 edges each + edges.forEach(e => e.delete()); + }); + + // pointsOnEdgesAtParam tests + it("should get points on multiple edges at param", async () => { + const e1 = edge.line({ start: [0, 0, 0], end: [2, 0, 0] }); + const e2 = edge.line({ start: [0, 0, 0], end: [0, 4, 0] }); + const e3 = edge.line({ start: [0, 0, 0], end: [0, 0, 6] }); + const points = edge.pointsOnEdgesAtParam({ shapes: [e1, e2, e3], param: 0.5 }); + expect(points.length).toBe(3); + expect(points[0][0]).toBeCloseTo(1, closeToNr); + expect(points[1][1]).toBeCloseTo(2, closeToNr); + expect(points[2][2]).toBeCloseTo(3, closeToNr); + e1.delete(); + e2.delete(); + e3.delete(); + }); + + // tangentsOnEdgesAtParam tests + it("should get tangents on multiple edges at param", async () => { + const e1 = edge.line({ start: [0, 0, 0], end: [2, 0, 0] }); + const e2 = edge.line({ start: [0, 0, 0], end: [0, 4, 0] }); + const tangents = edge.tangentsOnEdgesAtParam({ shapes: [e1, e2], param: 0.5 }); + expect(tangents.length).toBe(2); + // Tangent on a line points in direction of the line (normalized unit vectors) + expect(tangents[0][0]).toBeCloseTo(1, closeToNr); + expect(tangents[0][1]).toBeCloseTo(0, closeToNr); + expect(tangents[0][2]).toBeCloseTo(0, closeToNr); + expect(tangents[1][0]).toBeCloseTo(0, closeToNr); + expect(tangents[1][1]).toBeCloseTo(1, closeToNr); + expect(tangents[1][2]).toBeCloseTo(0, closeToNr); + e1.delete(); + e2.delete(); + }); + + // startPointsOnEdges tests + it("should get start points on multiple edges", async () => { + const e1 = edge.line({ start: [1, 2, 3], end: [4, 5, 6] }); + const e2 = edge.line({ start: [7, 8, 9], end: [10, 11, 12] }); + const startPoints = edge.startPointsOnEdges({ shapes: [e1, e2] }); + expect(startPoints.length).toBe(2); + expect(startPoints[0]).toEqual([1, 2, 3]); + expect(startPoints[1]).toEqual([7, 8, 9]); + e1.delete(); + e2.delete(); + }); + + // endPointsOnEdges tests + it("should get end points on multiple edges", async () => { + const e1 = edge.line({ start: [1, 2, 3], end: [4, 5, 6] }); + const e2 = edge.line({ start: [7, 8, 9], end: [10, 11, 12] }); + const endPoints = edge.endPointsOnEdges({ shapes: [e1, e2] }); + expect(endPoints.length).toBe(2); + expect(endPoints[0]).toEqual([4, 5, 6]); + expect(endPoints[1]).toEqual([10, 11, 12]); + e1.delete(); + e2.delete(); + }); + + // pointsOnEdgesAtLength tests + it("should get points on multiple edges at length", async () => { + const e1 = edge.line({ start: [0, 0, 0], end: [4, 0, 0] }); + const e2 = edge.line({ start: [0, 0, 0], end: [0, 4, 0] }); + const points = edge.pointsOnEdgesAtLength({ shapes: [e1, e2], length: 2 }); + expect(points.length).toBe(2); + expect(points[0][0]).toBeCloseTo(2, closeToNr); + expect(points[0][1]).toBeCloseTo(0, closeToNr); + expect(points[1][0]).toBeCloseTo(0, closeToNr); + expect(points[1][1]).toBeCloseTo(2, closeToNr); + e1.delete(); + e2.delete(); + }); + + // tangentsOnEdgesAtLength tests + it("should get tangents on multiple edges at length", async () => { + const e1 = edge.line({ start: [0, 0, 0], end: [4, 0, 0] }); + const e2 = edge.line({ start: [0, 0, 0], end: [0, 4, 0] }); + const tangents = edge.tangentsOnEdgesAtLength({ shapes: [e1, e2], length: 1 }); + expect(tangents.length).toBe(2); + expect(tangents[0][0]).toBeCloseTo(4, closeToNr); + expect(tangents[0][1]).toBeCloseTo(0, closeToNr); + expect(tangents[1][0]).toBeCloseTo(0, closeToNr); + expect(tangents[1][1]).toBeCloseTo(4, closeToNr); + e1.delete(); + e2.delete(); + }); + + // divideEdgesByParamsToPoints tests + it("should divide multiple edges by params to points", async () => { + const e1 = edge.createCircleEdge({ radius: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const e2 = edge.createCircleEdge({ radius: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const pointsArrays = edge.divideEdgesByParamsToPoints({ + shapes: [e1, e2], + nrOfDivisions: 4, + removeEndPoint: false, + removeStartPoint: false + }); + expect(pointsArrays.length).toBe(2); + expect(pointsArrays[0].length).toBe(5); + expect(pointsArrays[1].length).toBe(5); + e1.delete(); + e2.delete(); + }); + + // divideEdgesByEqualDistanceToPoints tests + it("should divide multiple edges by equal distance to points", async () => { + const e1 = edge.createCircleEdge({ radius: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const e2 = edge.createCircleEdge({ radius: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const pointsArrays = edge.divideEdgesByEqualDistanceToPoints({ + shapes: [e1, e2], + nrOfDivisions: 4, + removeEndPoint: false, + removeStartPoint: false + }); + expect(pointsArrays.length).toBe(2); + expect(pointsArrays[0].length).toBe(5); + expect(pointsArrays[1].length).toBe(5); + e1.delete(); + e2.delete(); + }); + + // isEdgeLinear tests + it("should return true for linear edge", async () => { + const e = edge.line({ start: [0, 0, 0], end: [1, 1, 1] }); + const isLinear = edge.isEdgeLinear({ shape: e }); + expect(isLinear).toBe(true); + e.delete(); + }); + + it("should return false for circular edge when checking if linear", async () => { + const e = edge.createCircleEdge({ radius: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const isLinear = edge.isEdgeLinear({ shape: e }); + expect(isLinear).toBe(false); + e.delete(); + }); + + it("should return false for arc edge when checking if linear", async () => { + const e = edge.arcThroughThreePoints({ start: [0, 0, 0], middle: [1, 1, 0], end: [2, 0, 0] }); + const isLinear = edge.isEdgeLinear({ shape: e }); + expect(isLinear).toBe(false); + e.delete(); + }); + + // isEdgeCircular tests + it("should return true for circular edge", async () => { + const e = edge.createCircleEdge({ radius: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const isCircular = edge.isEdgeCircular({ shape: e }); + expect(isCircular).toBe(true); + e.delete(); + }); + + it("should return true for arc edge when checking if circular", async () => { + const e = edge.arcThroughThreePoints({ start: [0, 0, 0], middle: [1, 1, 0], end: [2, 0, 0] }); + const isCircular = edge.isEdgeCircular({ shape: e }); + expect(isCircular).toBe(true); + e.delete(); + }); + + it("should return false for linear edge when checking if circular", async () => { + const e = edge.line({ start: [0, 0, 0], end: [1, 1, 1] }); + const isCircular = edge.isEdgeCircular({ shape: e }); + expect(isCircular).toBe(false); + e.delete(); + }); + + // getEdgeLengthsOfShape tests + it("should get edge lengths of a box shape", async () => { + const box = occHelper.entitiesService.bRepPrimAPIMakeBox(2, 3, 4, [0, 0, 0]); + const lengths = edge.getEdgeLengthsOfShape({ shape: box }); + expect(lengths.length).toBe(12); + // Box has 4 edges of each dimension + const sortedLengths = lengths.sort((a, b) => a - b); + expect(sortedLengths[0]).toBeCloseTo(2, closeToNr); + expect(sortedLengths[4]).toBeCloseTo(3, closeToNr); + expect(sortedLengths[8]).toBeCloseTo(4, closeToNr); + box.delete(); + }); + + it("should get edge lengths of a cylinder shape", async () => { + const cylinder = occHelper.entitiesService.bRepPrimAPIMakeCylinder([0, 0, 0], [0, 1, 0], 1, 2, Math.PI * 2); + const lengths = edge.getEdgeLengthsOfShape({ shape: cylinder }); + expect(lengths.length).toBe(3); + // Cylinder has 2 circular edges and 1 linear edge (seam) + const sortedLengths = lengths.sort((a, b) => a - b); + expect(sortedLengths[0]).toBeCloseTo(2, closeToNr); // height + expect(sortedLengths[1]).toBeCloseTo(2 * Math.PI, closeToNr); // circumference + expect(sortedLengths[2]).toBeCloseTo(2 * Math.PI, closeToNr); // circumference + cylinder.delete(); + }); }); From fee393cab3e5aa99c1510dc76ca32fcf9da8e1f6 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 22:02:10 +0200 Subject: [PATCH 07/38] some face unit tests --- .../dev/occt/lib/services/shapes/face.test.ts | 411 +++++++++++++++++- 1 file changed, 410 insertions(+), 1 deletion(-) diff --git a/packages/dev/occt/lib/services/shapes/face.test.ts b/packages/dev/occt/lib/services/shapes/face.test.ts index e42fba59..9caded86 100644 --- a/packages/dev/occt/lib/services/shapes/face.test.ts +++ b/packages/dev/occt/lib/services/shapes/face.test.ts @@ -1626,10 +1626,419 @@ describe("OCCT face unit tests", () => { }); const area = face.getFaceArea({ shape: f }); expect(f.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_FACE); - expect(area).toBeCloseTo(2.9276442447299863); + expect(area).toBeCloseTo(2.927644244729986); f.delete(); }); }); + describe("Face from base triangle and mesh", () => { + it("should create face from base triangle", () => { + const dto = new OCCT.TriangleBaseDto([[0, 0, 0], [2, 0, 0], [1, 2, 0]]); + const f = face.fromBaseTriangle(dto); + const area = face.getFaceArea({ shape: f }); + expect(f.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_FACE); + expect(area).toBeCloseTo(2); + f.delete(); + }); + + it("should create face from base triangle with different orientation", () => { + const dto = new OCCT.TriangleBaseDto([[0, 0, 0], [3, 0, 0], [3, 4, 0]]); + const f = face.fromBaseTriangle(dto); + const area = face.getFaceArea({ shape: f }); + expect(f.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_FACE); + expect(area).toBeCloseTo(6); + f.delete(); + }); + + it("should create faces from base mesh with single triangle", () => { + const dto = new OCCT.MeshBaseDto([[[0, 0, 0], [2, 0, 0], [1, 2, 0]]]); + const faces = face.fromBaseMesh(dto); + expect(faces.length).toBe(1); + const area = face.getFaceArea({ shape: faces[0] }); + expect(area).toBeCloseTo(2); + faces.forEach(f => f.delete()); + }); + + it("should create faces from base mesh with multiple triangles", () => { + const dto = new OCCT.MeshBaseDto([ + [[0, 0, 0], [2, 0, 0], [1, 2, 0]], + [[0, 0, 0], [1, 2, 0], [-1, 2, 0]], + [[5, 0, 0], [7, 0, 0], [6, 3, 0]] + ]); + const faces = face.fromBaseMesh(dto); + expect(faces.length).toBe(3); + const area1 = face.getFaceArea({ shape: faces[0] }); + const area2 = face.getFaceArea({ shape: faces[1] }); + const area3 = face.getFaceArea({ shape: faces[2] }); + expect(area1).toBeCloseTo(2); + expect(area2).toBeCloseTo(2); + expect(area3).toBeCloseTo(3); + faces.forEach(f => f.delete()); + }); + }); + + describe("Face from wire on face methods", () => { + it("should create face from wire on face", () => { + const baseFace = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const circleWire = wire.createCircleWire({ radius: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceFromWireOnFaceDto(circleWire, baseFace, true); + const f = face.createFaceFromWireOnFace(dto); + const area = face.getFaceArea({ shape: f }); + expect(f.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_FACE); + expect(area).toBeCloseTo(Math.PI * 4); + baseFace.delete(); + circleWire.delete(); + f.delete(); + }); + + it("should create faces from wires on face", () => { + const baseFace = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const circleWire1 = wire.createCircleWire({ radius: 1, center: [-2, -2, 0], direction: [0, 0, 1] }); + const circleWire2 = wire.createCircleWire({ radius: 1.5, center: [2, 2, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FacesFromWiresOnFaceDto([circleWire1, circleWire2], baseFace, true); + const faces = face.createFacesFromWiresOnFace(dto); + expect(faces.length).toBe(2); + const area1 = face.getFaceArea({ shape: faces[0] }); + const area2 = face.getFaceArea({ shape: faces[1] }); + expect(area1).toBeCloseTo(Math.PI); + expect(area2).toBeCloseTo(Math.PI * 2.25); + baseFace.delete(); + circleWire1.delete(); + circleWire2.delete(); + faces.forEach(f => f.delete()); + }); + + it("should create face from multiple wires on face", () => { + const baseFace = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const outerWire = wire.createCircleWire({ radius: 3, center: [0, 0, 0], direction: [0, 0, 1] }); + const innerWire = wire.createCircleWire({ radius: 1, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceFromWiresOnFaceDto([outerWire, innerWire], baseFace, true); + const f = face.createFaceFromWiresOnFace(dto); + const area = face.getFaceArea({ shape: f }); + expect(f.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_FACE); + // Combined area of both circles: π*3² + π*1² = 9π + π = 10π + expect(area).toBeCloseTo(Math.PI * 10); + baseFace.delete(); + outerWire.delete(); + innerWire.delete(); + f.delete(); + }); + + it("should create face from wires on face with rectangular boundary", () => { + const baseFace = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const rectWire = wire.createRectangleWire({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceFromWiresOnFaceDto([rectWire], baseFace, true); + const f = face.createFaceFromWiresOnFace(dto); + const area = face.getFaceArea({ shape: f }); + expect(f.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_FACE); + expect(area).toBeCloseTo(8); + baseFace.delete(); + rectWire.delete(); + f.delete(); + }); + }); + + describe("Face subdivision to wires", () => { + it("should subdivide face to wires along U", () => { + const f = face.createRectangleFace({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivisionToWiresDto(f, 5, true, false, false, false); + const wires = face.subdivideToWires(dto); + expect(wires.length).toBe(6); + wires.forEach(w => { + expect(w.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_WIRE); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(4); + w.delete(); + }); + f.delete(); + }); + + it("should subdivide face to wires along V", () => { + const f = face.createRectangleFace({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivisionToWiresDto(f, 4, false, false, false, false); + const wires = face.subdivideToWires(dto); + expect(wires.length).toBe(5); + wires.forEach(w => { + expect(w.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_WIRE); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(2); + w.delete(); + }); + f.delete(); + }); + + it("should subdivide face to wires with start and end removed", () => { + const f = face.createRectangleFace({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivisionToWiresDto(f, 6, true, false, true, true); + const wires = face.subdivideToWires(dto); + expect(wires.length).toBe(5); + wires.forEach(w => { + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(4); + w.delete(); + }); + f.delete(); + }); + }); + + describe("Face subdivision to rectangle wires and holes", () => { + it("should subdivide face to rectangle wires", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToRectangleWiresDto(f, 3, 3); + const wires = face.subdivideToRectangleWires(dto); + expect(wires.length).toBe(30); + wires.forEach(w => { + expect(w.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_WIRE); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(8.666666666666666); + w.delete(); + }); + f.delete(); + }); + + it("should subdivide face to rectangle wires with scale pattern", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToRectangleWiresDto(f, 2, 2, [0.8, 0.6], [0.9, 0.7]); + const wires = face.subdivideToRectangleWires(dto); + expect(wires.length).toBe(20); + wires.forEach((w, i) => { + const length = wire.getWireLength({ shape: w }); + if (i % 2 === 0) { + expect(length).toBeCloseTo(9.8); + } else { + expect(length).toBeCloseTo(7.4); + } + w.delete(); + }); + f.delete(); + }); + + it("should subdivide face to rectangle holes", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToRectangleHolesDto(f, 2, 2, [0.5], [0.5]); + const faces = face.subdivideToRectangleHoles(dto); + expect(faces.length).toBe(1); + faces.forEach(fc => fc.delete()); + f.delete(); + }); + + it("should subdivide face to rectangle holes with holesToFaces", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToRectangleHolesDto(f, 2, 2, [0.5], [0.5], undefined, undefined, true); + const faces = face.subdivideToRectangleHoles(dto); + expect(faces.length).toBe(21); + faces.forEach(fc => fc.delete()); + f.delete(); + }); + }); + + describe("Face subdivision to hexagon wires and holes", () => { + it("should subdivide face to hexagon wires", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToHexagonWiresDto(f, 3, 3); + const wires = face.subdivideToHexagonWires(dto); + expect(wires.length).toBe(30); + wires.forEach(w => { + expect(w.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_WIRE); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(8.430363180804955); + w.delete(); + }); + f.delete(); + }); + + it("should subdivide face to hexagon wires with flat U", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToHexagonWiresDto(f, 2, 2, true); + const wires = face.subdivideToHexagonWires(dto); + expect(wires.length).toBe(20); + wires.forEach(w => { + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(9.393712757732946); + w.delete(); + }); + f.delete(); + }); + + it("should subdivide face to hexagon holes", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToHexagonHolesDto(f, 2, 2); + const faces = face.subdivideToHexagonHoles(dto); + expect(faces.length).toBe(1); + const area = face.getFaceArea({ shape: faces[0] }); + expect(area).toBeCloseTo(79.59183673469393); + faces.forEach(fc => fc.delete()); + f.delete(); + }); + + it("should subdivide face to hexagon holes with holesToFaces", () => { + const f = face.createRectangleFace({ width: 10, length: 10, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FaceSubdivideToHexagonHolesDto(f, 2, 2, false, true); + const faces = face.subdivideToHexagonHoles(dto); + expect(faces.length).toBe(21); + faces.forEach((fc, i) => { + const area = face.getFaceArea({ shape: fc }); + if (i === 0) { + expect(area).toBeCloseTo(79.59183673469393); + } else { + expect(area).toBeCloseTo(1.0204081632653064); + } + fc.delete(); + }); + f.delete(); + }); + }); + + describe("Wire along param methods", () => { + it("should create wire along U param", () => { + const f = face.createRectangleFace({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.WireAlongParamDto(f, true, 0.5); + const w = face.wireAlongParam(dto); + expect(w.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_WIRE); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(4); + f.delete(); + w.delete(); + }); + + it("should create wire along V param", () => { + const f = face.createRectangleFace({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.WireAlongParamDto(f, false, 0.5); + const w = face.wireAlongParam(dto); + expect(w.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_WIRE); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(2); + f.delete(); + w.delete(); + }); + + it("should create wires along multiple U params", () => { + const f = face.createRectangleFace({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.WiresAlongParamsDto(f, true, [0.25, 0.5, 0.75]); + const wires = face.wiresAlongParams(dto); + expect(wires.length).toBe(3); + wires.forEach(w => { + expect(w.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_WIRE); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(4); + w.delete(); + }); + f.delete(); + }); + + it("should create wires along multiple V params", () => { + const f = face.createRectangleFace({ width: 4, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.WiresAlongParamsDto(f, false, [0.2, 0.8]); + const wires = face.wiresAlongParams(dto); + expect(wires.length).toBe(2); + wires.forEach(w => { + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(2); + w.delete(); + }); + f.delete(); + }); + }); + + describe("Hexagons in grid", () => { + it("should create hexagons in grid", () => { + const dto = new OCCT.HexagonsInGridDto(5, 5, 3, 3); + const hexFaces = face.hexagonsInGrid(dto); + expect(hexFaces.length).toBe(9); + hexFaces.forEach(f => { + expect(f.ShapeType()).toBe(occt.TopAbs_ShapeEnum.TopAbs_FACE); + const area = face.getFaceArea({ shape: f }); + expect(area).toBeCloseTo(2.142857142857143); + f.delete(); + }); + }); + + it("should create hexagons in grid with flat top", () => { + const dto = new OCCT.HexagonsInGridDto(6, 6, 2, 2, true); + const hexFaces = face.hexagonsInGrid(dto); + expect(hexFaces.length).toBe(4); + hexFaces.forEach(f => { + const area = face.getFaceArea({ shape: f }); + expect(area).toBeCloseTo(6.171428571428569); + f.delete(); + }); + }); + + it("should create hexagons in grid with extensions", () => { + const dto = new OCCT.HexagonsInGridDto(8, 8, 3, 3, false, true, true, true, true); + const hexFaces = face.hexagonsInGrid(dto); + expect(hexFaces.length).toBe(9); + hexFaces.forEach(f => { + const area = face.getFaceArea({ shape: f }); + expect(area).toBeCloseTo(9.599999999999998); + f.delete(); + }); + }); + }); + + describe("Filter faces points", () => { + it("should filter points for multiple faces returning flat array", () => { + const f1 = face.createRectangleFace({ width: 2, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const f2 = face.createRectangleFace({ width: 2, length: 2, center: [5, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FilterFacesPointsDto( + [f1, f2], + [[0, 0, 0], [0.5, 0.5, 0], [5, 0, 0], [5.5, 0.5, 0], [10, 10, 0]], + 1e-4, + false, + 0.1, + true, + true, + false, + false, + true + ); + const filteredPoints = face.filterFacesPoints(dto); + expect(Array.isArray(filteredPoints)).toBe(true); + expect((filteredPoints as number[][]).length).toBe(4); + f1.delete(); + f2.delete(); + }); + + it("should filter points for multiple faces returning nested array", () => { + const f1 = face.createRectangleFace({ width: 2, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const f2 = face.createRectangleFace({ width: 2, length: 2, center: [5, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FilterFacesPointsDto( + [f1, f2], + [[0, 0, 0], [0.5, 0.5, 0], [5, 0, 0], [5.5, 0.5, 0], [10, 10, 0]], + 1e-4, + false, + 0.1, + true, + true, + false, + false, + false + ); + const filteredPoints = face.filterFacesPoints(dto); + expect(Array.isArray(filteredPoints)).toBe(true); + expect((filteredPoints as number[][][]).length).toBe(2); + f1.delete(); + f2.delete(); + }); + + it("should return empty array when no points match", () => { + const f1 = face.createRectangleFace({ width: 2, length: 2, center: [0, 0, 0], direction: [0, 0, 1] }); + const dto = new OCCT.FilterFacesPointsDto( + [f1], + [[100, 100, 0], [200, 200, 0]], + 1e-4, + false, + 0.1, + true, + true, + false, + false, + true + ); + const filteredPoints = face.filterFacesPoints(dto); + expect((filteredPoints as number[][]).length).toBe(0); + f1.delete(); + }); + }); }); From ce5ea1bb27da29775507af9a69ee64e8234fa7cd Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 22:13:22 +0200 Subject: [PATCH 08/38] io unit tests --- packages/dev/occt/lib/services/io.test.ts | 139 +++++++++++++++++++++- packages/dev/occt/lib/services/io.ts | 106 ++++++++--------- 2 files changed, 191 insertions(+), 54 deletions(-) diff --git a/packages/dev/occt/lib/services/io.test.ts b/packages/dev/occt/lib/services/io.test.ts index 767479b4..1793cbfb 100644 --- a/packages/dev/occt/lib/services/io.test.ts +++ b/packages/dev/occt/lib/services/io.test.ts @@ -283,7 +283,8 @@ describe("OCCT io unit tests", () => { expect(pathsWithLayer.layer).toBe("TestLayer"); expect(pathsWithLayer.color).toBe("#FF0000"); - const dxfContent = io.dxfCreate({ pathsParts: [pathsWithLayer] }); + const dxfDto = new Inputs.OCCT.DxfPathsPartsListDto([pathsWithLayer]); + const dxfContent = io.dxfCreate(dxfDto); expect(dxfContent).toContain("TestLayer"); expect(dxfContent).toContain("LWPOLYLINE"); }); @@ -594,4 +595,140 @@ describe("OCCT io unit tests", () => { arcWire.delete(); arc.delete(); }); + + it("should save cube shape as STL file", () => { + const cube = solid.createCube({ size: 10, center: [0, 0, 0] }); + const dto = new Inputs.OCCT.SaveStlDto(cube, "cube.stl", 0.01, false); + const stl = io.saveShapeStl(dto); + + // STL file should contain proper header and facet definitions + expect(stl).toContain("solid"); + expect(stl).toContain("facet normal"); + expect(stl).toContain("outer loop"); + expect(stl).toContain("vertex"); + expect(stl).toContain("endloop"); + expect(stl).toContain("endfacet"); + expect(stl).toContain("endsolid"); + + cube.delete(); + }); + + it("should save cylinder shape as STL file", () => { + const cylinder = solid.createCylinder({ radius: 5, height: 10, direction: [0, 1, 0], center: [0, 0, 0] }); + const dto = new Inputs.OCCT.SaveStlDto(cylinder, "cylinder.stl", 0.1, false); + const stl = io.saveShapeStl(dto); + + expect(stl).toContain("solid"); + expect(stl).toContain("facet normal"); + expect(stl).toContain("endsolid"); + + // Count the number of facets (triangles) + const facetCount = (stl.match(/facet normal/g) || []).length; + expect(facetCount).toBeGreaterThan(10); + + cylinder.delete(); + }); + + it("should save cone shape as STL file with Y to Z adjustment", () => { + const cone = solid.createCone({ radius1: 10, radius2: 5, height: 20, angle: 360, direction: [0, 1, 0], center: [0, 0, 0] }); + const dto = new Inputs.OCCT.SaveStlDto(cone, "cone.stl", 0.1, true); + const stl = io.saveShapeStl(dto); + + expect(stl).toContain("solid"); + expect(stl).toContain("facet normal"); + expect(stl).toContain("endsolid"); + + cone.delete(); + }); + + it("should save sphere shape as STL file with higher precision", () => { + const sphere = solid.createSphere({ radius: 5, center: [0, 0, 0] }); + const dtoLowRes = new Inputs.OCCT.SaveStlDto(sphere, "sphere.stl", 1, false); + const stlLowRes = io.saveShapeStl(dtoLowRes); + + const dtoHighRes = new Inputs.OCCT.SaveStlDto(sphere, "sphere.stl", 0.01, false); + const stlHighRes = io.saveShapeStl(dtoHighRes); + + // Higher precision should result in more facets + const facetCountLow = (stlLowRes.match(/facet normal/g) || []).length; + const facetCountHigh = (stlHighRes.match(/facet normal/g) || []).length; + + expect(facetCountHigh).toBeGreaterThan(facetCountLow); + + sphere.delete(); + }); + + it("should save box shape as STL file and contain valid vertex coordinates", () => { + const box = solid.createBox({ width: 4, length: 6, height: 8, center: [0, 0, 0] }); + const dto = new Inputs.OCCT.SaveStlDto(box, "box.stl", 0.01, false); + const stl = io.saveShapeStl(dto); + + // A box should have 12 triangular facets (2 per face, 6 faces) + const facetCount = (stl.match(/facet normal/g) || []).length; + expect(facetCount).toBe(12); + + // Check that vertex coordinates are present + const vertexMatches = stl.match(/vertex\s+[-\d.e+]+\s+[-\d.e+]+\s+[-\d.e+]+/g); + expect(vertexMatches).not.toBeNull(); + expect(vertexMatches).toHaveLength(36); // 12 facets * 3 vertices each + + box.delete(); + }); + + it("should load shape from STEP file with .stp extension", () => { + const cube = solid.createCube({ size: 5, center: [0, 0, 0] }); + const stepText = io.saveShapeSTEP({ shape: cube, adjustYtoZ: false, fileName: "cube.stp" }); + const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "cube.stp", adjustZtoY: false }); + + const volumeOriginal = solid.getSolidVolume({ shape: cube }); + const volumeLoaded = solid.getSolidVolume({ shape: loaded }); + expect(volumeOriginal).toBeCloseTo(volumeLoaded); + + cube.delete(); + loaded.delete(); + }); + + it("should load shape from STEP file with adjustZtoY enabled", () => { + const cylinder = solid.createCylinder({ radius: 3, height: 10, direction: [0, 1, 0], center: [0, 0, 0] }); + const stepText = io.saveShapeSTEP({ shape: cylinder, adjustYtoZ: true, fileName: "cylinder.step" }); + const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "cylinder.step", adjustZtoY: true }); + + const volumeOriginal = solid.getSolidVolume({ shape: cylinder }); + const volumeLoaded = solid.getSolidVolume({ shape: loaded }); + expect(volumeOriginal).toBeCloseTo(volumeLoaded); + + cylinder.delete(); + loaded.delete(); + }); + + it("should load sphere shape from STEP file and preserve volume", () => { + const sphere = solid.createSphere({ radius: 7, center: [0, 0, 0] }); + const stepText = io.saveShapeSTEP({ shape: sphere, adjustYtoZ: false, fileName: "sphere.step" }); + const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "sphere.step", adjustZtoY: false }); + + const volumeOriginal = solid.getSolidVolume({ shape: sphere }); + const volumeLoaded = solid.getSolidVolume({ shape: loaded }); + expect(volumeOriginal).toBeCloseTo(volumeLoaded); + + sphere.delete(); + loaded.delete(); + }); + + it("should load box shape from STEP file and preserve volume", () => { + const box = solid.createBox({ width: 4, length: 6, height: 8, center: [0, 0, 0] }); + const stepText = io.saveShapeSTEP({ shape: box, adjustYtoZ: false, fileName: "box.step" }); + const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "box.step", adjustZtoY: false }); + + const volumeOriginal = solid.getSolidVolume({ shape: box }); + const volumeLoaded = solid.getSolidVolume({ shape: loaded }); + expect(volumeOriginal).toBeCloseTo(volumeLoaded); + + box.delete(); + loaded.delete(); + }); + + it("should return undefined for unsupported file extension", () => { + const result = io.loadSTEPorIGES({ filetext: "some content", fileName: "file.obj", adjustZtoY: false }); + expect(result).toBeUndefined(); + }); }); diff --git a/packages/dev/occt/lib/services/io.ts b/packages/dev/occt/lib/services/io.ts index b8a1321e..15aa2a2b 100644 --- a/packages/dev/occt/lib/services/io.ts +++ b/packages/dev/occt/lib/services/io.ts @@ -187,59 +187,59 @@ export class OCCTIO { // TODO WIP! This function parses the contents of a `.STEP` file as an assembly // and returns the assembly structure. It's not finished yet and needs a lot of work. - private parseStepAssembly(inputs: Inputs.OCCT.LoadStepOrIgesDto) { - const fileName = inputs.fileName; - const fileText = inputs.filetext; - - // Write file to virtual filesystem - this.occ.FS.createDataFile("/", "step_file.step", fileText as string, true, true, true); - - try { - // Create XCAF application and document - const app = this.occ.XCAFApp_Application.GetApplication(); - const format = new this.occ.TCollection_ExtendedString_2("MDTV-XCAF", true); - const docHandle = new this.occ.Handle_TDocStd_Document_1(); - app.get().NewDocument_2(format, docHandle); - const doc = docHandle.get(); - - // Use STEPCAFControl_Reader to read with full XCAF support - const reader = new this.occ.STEPCAFControl_Reader_1(); - reader.SetColorMode(true); // Enable color reading - reader.SetNameMode(true); // Enable name reading - reader.SetLayerMode(true); // Enable layer reading - reader.SetPropsMode(true); // Enable properties reading - - // Read the file - const readResult = reader.ReadFile("step_file.step"); - - if (readResult === this.occ.IFSelect_ReturnStatus.IFSelect_RetDone) { - // Transfer to XCAF document - const messageProgress = new this.occ.Message_ProgressRange_1(); - const transferResult = reader.Transfer_1(docHandle, messageProgress); - messageProgress.delete(); - - if (transferResult) { - // Parse using the improved assembly parser - // const assemblyStructure = this.ioAssembly.parseXCAFDocument(doc); - - // Clean up - reader.delete(); - format.delete(); - docHandle.delete(); - this.occ.FS.unlink("/step_file.step"); - - return {}; - } else { - throw new Error("Failed to transfer STEP data to XCAF document"); - } - } else { - throw new Error("Failed to read STEP file"); - } - } catch (error) { - this.occ.FS.unlink("/step_file.step"); - throw error; - } - } + // private parseStepAssembly(inputs: Inputs.OCCT.LoadStepOrIgesDto) { + // const fileName = inputs.fileName; + // const fileText = inputs.filetext; + + // // Write file to virtual filesystem + // this.occ.FS.createDataFile("/", "step_file.step", fileText as string, true, true, true); + + // try { + // // Create XCAF application and document + // const app = this.occ.XCAFApp_Application.GetApplication(); + // const format = new this.occ.TCollection_ExtendedString_2("MDTV-XCAF", true); + // const docHandle = new this.occ.Handle_TDocStd_Document_1(); + // app.get().NewDocument_2(format, docHandle); + // const doc = docHandle.get(); + + // // Use STEPCAFControl_Reader to read with full XCAF support + // const reader = new this.occ.STEPCAFControl_Reader_1(); + // reader.SetColorMode(true); // Enable color reading + // reader.SetNameMode(true); // Enable name reading + // reader.SetLayerMode(true); // Enable layer reading + // reader.SetPropsMode(true); // Enable properties reading + + // // Read the file + // const readResult = reader.ReadFile("step_file.step"); + + // if (readResult === this.occ.IFSelect_ReturnStatus.IFSelect_RetDone) { + // // Transfer to XCAF document + // const messageProgress = new this.occ.Message_ProgressRange_1(); + // const transferResult = reader.Transfer_1(docHandle, messageProgress); + // messageProgress.delete(); + + // if (transferResult) { + // // Parse using the improved assembly parser + // // const assemblyStructure = this.ioAssembly.parseXCAFDocument(doc); + + // // Clean up + // reader.delete(); + // format.delete(); + // docHandle.delete(); + // this.occ.FS.unlink("/step_file.step"); + + // return {}; + // } else { + // throw new Error("Failed to transfer STEP data to XCAF document"); + // } + // } else { + // throw new Error("Failed to read STEP file"); + // } + // } catch (error) { + // this.occ.FS.unlink("/step_file.step"); + // throw error; + // } + // } shapeToDxfPaths(inputs: Inputs.OCCT.ShapeToDxfPathsDto): IO.DxfPathDto[] { return this.och.dxfService.shapeToDxfPaths(inputs); From 62608b700918115311f8e269989b54e53dbbf7d6 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 14 Dec 2025 22:49:45 +0200 Subject: [PATCH 09/38] additional fillets, io and operations unit tests --- .../dev/occt/lib/services/fillets.test.ts | 142 ++++++++++++ packages/dev/occt/lib/services/io.test.ts | 3 + .../dev/occt/lib/services/operations.test.ts | 210 ++++++++++++++++++ 3 files changed, 355 insertions(+) diff --git a/packages/dev/occt/lib/services/fillets.test.ts b/packages/dev/occt/lib/services/fillets.test.ts index 72733a9b..b855d6e0 100644 --- a/packages/dev/occt/lib/services/fillets.test.ts +++ b/packages/dev/occt/lib/services/fillets.test.ts @@ -158,6 +158,78 @@ describe("OCCT fillets unit tests", () => { result.delete(); }); + it("should fillet multiple 3D wires with the same radius", () => { + const starOpt1 = new Inputs.OCCT.StarDto(10, 6, 7, [0, 0, 0], [0, 1, 0], 3); + const starOpt2 = new Inputs.OCCT.StarDto(8, 5, 6, [20, 0, 0], [0, 1, 0], 2); + const star1 = wire.createStarWire(starOpt1); + const star2 = wire.createStarWire(starOpt2); + + const filletOptions = new Inputs.OCCT.Fillet3DWiresDto( + [star1, star2], + 0.3, + [0, 1, 0], + ); + const results = fillets.fillet3DWires(filletOptions); + + expect(results.length).toBe(2); + + const edges1 = occHelper.edgesService.getEdgesAlongWire({ shape: results[0] }); + const edges2 = occHelper.edgesService.getEdgesAlongWire({ shape: results[1] }); + + // All edges should be filleted, so we expect more edges than original + expect(edges1.length).toBe(28); + expect(edges2.length).toBe(24); + + // Some edges should be short fillet arcs + const edgeLengths1 = edges1.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const edgeLengths2 = edges2.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const hasShortEdges1 = edgeLengths1.some(length => length < 1); + const hasShortEdges2 = edgeLengths2.some(length => length < 1); + expect(hasShortEdges1).toBe(true); + expect(hasShortEdges2).toBe(true); + + star1.delete(); + star2.delete(); + edges1.forEach(e => e.delete()); + edges2.forEach(e => e.delete()); + results.forEach(r => r.delete()); + }); + + it("should fillet multiple 3D wires with indexes", () => { + const starOpt1 = new Inputs.OCCT.StarDto(10, 6, 7, [0, 0, 0], [0, 1, 0], 3); + const starOpt2 = new Inputs.OCCT.StarDto(8, 5, 6, [20, 0, 0], [0, 1, 0], 2); + const star1 = wire.createStarWire(starOpt1); + const star2 = wire.createStarWire(starOpt2); + + const filletOptions = new Inputs.OCCT.Fillet3DWiresDto( + [star1, star2], + undefined, + [0, 1, 0], + [0.2, 0.4, 0.3], + [1, 3, 5], + ); + const results = fillets.fillet3DWires(filletOptions); + + expect(results.length).toBe(2); + + const edges1 = occHelper.edgesService.getEdgesAlongWire({ shape: results[0] }); + const edges2 = occHelper.edgesService.getEdgesAlongWire({ shape: results[1] }); + + // Check that fillets were applied + const edgeLengths1 = edges1.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const edgeLengths2 = edges2.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const hasShortEdges1 = edgeLengths1.some(length => length < 1); + const hasShortEdges2 = edgeLengths2.some(length => length < 1); + expect(hasShortEdges1).toBe(true); + expect(hasShortEdges2).toBe(true); + + star1.delete(); + star2.delete(); + edges1.forEach(e => e.delete()); + edges2.forEach(e => e.delete()); + results.forEach(r => r.delete()); + }); + it("should fillet closed 2D wire on various corners", () => { const starOpt = new Inputs.OCCT.StarDto(10, 6, 7, [0, 0, 0], [0, 1, 0], 0); const star = wire.createStarWire(starOpt); @@ -232,6 +304,76 @@ describe("OCCT fillets unit tests", () => { result.delete(); }); + it("should fillet multiple 2D wires with the same radius", () => { + const starOpt1 = new Inputs.OCCT.StarDto(10, 6, 7, [0, 0, 0], [0, 1, 0], 0); + const starOpt2 = new Inputs.OCCT.StarDto(8, 5, 6, [20, 0, 0], [0, 1, 0], 0); + const star1 = wire.createStarWire(starOpt1); + const star2 = wire.createStarWire(starOpt2); + + const filletOptions = new Inputs.OCCT.FilletShapesDto( + [star1, star2], + 0.3, + ); + const results = fillets.fillet2dShapes(filletOptions); + + expect(results.length).toBe(2); + + const edges1 = occHelper.shapeGettersService.getEdges({ shape: results[0] }); + const edges2 = occHelper.shapeGettersService.getEdges({ shape: results[1] }); + + // All edges should be filleted, so we expect more edges than original + expect(edges1.length).toBe(28); + expect(edges2.length).toBe(24); + + // Some edges should be short fillet arcs + const edgeLengths1 = edges1.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const edgeLengths2 = edges2.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const hasShortEdges1 = edgeLengths1.some(length => length < 1); + const hasShortEdges2 = edgeLengths2.some(length => length < 1); + expect(hasShortEdges1).toBe(true); + expect(hasShortEdges2).toBe(true); + + star1.delete(); + star2.delete(); + edges1.forEach(e => e.delete()); + edges2.forEach(e => e.delete()); + results.forEach(r => r.delete()); + }); + + it("should fillet multiple 2D wires with indexes and radius list", () => { + const starOpt1 = new Inputs.OCCT.StarDto(10, 6, 7, [0, 0, 0], [0, 1, 0], 0); + const starOpt2 = new Inputs.OCCT.StarDto(8, 5, 6, [20, 0, 0], [0, 1, 0], 0); + const star1 = wire.createStarWire(starOpt1); + const star2 = wire.createStarWire(starOpt2); + + const filletOptions = new Inputs.OCCT.FilletShapesDto( + [star1, star2], + undefined, + [0.2, 0.4, 0.3], + [1, 3, 5], + ); + const results = fillets.fillet2dShapes(filletOptions); + + expect(results.length).toBe(2); + + const edges1 = occHelper.shapeGettersService.getEdges({ shape: results[0] }); + const edges2 = occHelper.shapeGettersService.getEdges({ shape: results[1] }); + + // Check that fillets were applied + const edgeLengths1 = edges1.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const edgeLengths2 = edges2.map(e => occHelper.edgesService.getEdgeLength({ shape: e })); + const hasShortEdges1 = edgeLengths1.some(length => length < 1); + const hasShortEdges2 = edgeLengths2.some(length => length < 1); + expect(hasShortEdges1).toBe(true); + expect(hasShortEdges2).toBe(true); + + star1.delete(); + star2.delete(); + edges1.forEach(e => e.delete()); + edges2.forEach(e => e.delete()); + results.forEach(r => r.delete()); + }); + it("should fillet a single edge on the solid", () => { const cube = solid.createCube({ size: 2, center: [0, 0, 0] }); const filRes = fillets.filletEdges({ shape: cube, indexes: [1], radius: 0.5 }); diff --git a/packages/dev/occt/lib/services/io.test.ts b/packages/dev/occt/lib/services/io.test.ts index 1793cbfb..272b43bd 100644 --- a/packages/dev/occt/lib/services/io.test.ts +++ b/packages/dev/occt/lib/services/io.test.ts @@ -728,7 +728,10 @@ describe("OCCT io unit tests", () => { }); it("should return undefined for unsupported file extension", () => { + const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {}); const result = io.loadSTEPorIGES({ filetext: "some content", fileName: "file.obj", adjustZtoY: false }); expect(result).toBeUndefined(); + expect(consoleSpy).toHaveBeenCalledWith("opencascade can't parse this extension! (yet)"); + consoleSpy.mockRestore(); }); }); diff --git a/packages/dev/occt/lib/services/operations.test.ts b/packages/dev/occt/lib/services/operations.test.ts index 3b2b8100..c0e6e7e5 100644 --- a/packages/dev/occt/lib/services/operations.test.ts +++ b/packages/dev/occt/lib/services/operations.test.ts @@ -5,6 +5,7 @@ import { VectorHelperService } from "../api/vector-helper.service"; import { OccHelper } from "../occ-helper"; import { OCCTOperations } from "./operations"; import { OCCTEdge, OCCTFace, OCCTShell, OCCTSolid, OCCTWire } from "./shapes"; +import { OCCTTransforms } from "./transforms"; describe("OCCT operations unit tests", () => { let occt: OpenCascadeInstance; @@ -15,6 +16,7 @@ describe("OCCT operations unit tests", () => { let face: OCCTFace; let solid: OCCTSolid; let shell: OCCTShell; + let transforms: OCCTTransforms; beforeAll(async () => { occt = await initOpenCascade(); @@ -27,6 +29,7 @@ describe("OCCT operations unit tests", () => { operations = new OCCTOperations(occt, occHelper); solid = new OCCTSolid(occt, occHelper); shell = new OCCTShell(occt, occHelper); + transforms = new OCCTTransforms(occt, occHelper); }); it("should get two closest points between two shapes", async () => { @@ -652,6 +655,126 @@ describe("OCCT operations unit tests", () => { offsetRes.delete(); }); + describe("offsetAdv", () => { + it("should offset a square wire with arc join type", () => { + const squareWire = wire.createSquareWire({ size: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const offsetRes = operations.offsetAdv({ + shape: squareWire, + distance: 0.2, + tolerance: 1e-7, + joinType: Inputs.OCCT.joinTypeEnum.arc, + removeIntEdges: false + }); + const length = wire.getWireLength({ shape: offsetRes as TopoDS_Wire }); + // Original perimeter is 8, offset by 0.2 outward adds arc corners + expect(length).toBeGreaterThan(8); + squareWire.delete(); + offsetRes.delete(); + }); + + it("should offset a square wire with intersection join type", () => { + const squareWire = wire.createSquareWire({ size: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const offsetRes = operations.offsetAdv({ + shape: squareWire, + distance: 0.2, + tolerance: 1e-7, + joinType: Inputs.OCCT.joinTypeEnum.intersection, + removeIntEdges: false + }); + const length = wire.getWireLength({ shape: offsetRes as TopoDS_Wire }); + // Intersection join type preserves sharp corners, so perimeter scales linearly + expect(length).toBeCloseTo(9.6, 1); + squareWire.delete(); + offsetRes.delete(); + }); + + it("should offset a square wire to negative direction", () => { + const squareWire = wire.createSquareWire({ size: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const offsetRes = operations.offsetAdv({ + shape: squareWire, + distance: -0.2, + tolerance: 1e-7, + joinType: Inputs.OCCT.joinTypeEnum.arc, + removeIntEdges: false + }); + const length = wire.getWireLength({ shape: offsetRes as TopoDS_Wire }); + // Inward offset reduces perimeter + expect(length).toBeLessThan(8); + squareWire.delete(); + offsetRes.delete(); + }); + + it("should offset a sphere with arc join type", () => { + const sphere = occHelper.entitiesService.bRepPrimAPIMakeSphere([0, 0, 0], [0, 1, 0], 1); + const offsetRes = operations.offsetAdv({ + shape: sphere, + distance: 0.1, + tolerance: 1e-7, + joinType: Inputs.OCCT.joinTypeEnum.arc, + removeIntEdges: false + }); + const faceAreaOriginal = face.getFaceArea({ shape: sphere }); + const faceArea = face.getFaceArea({ shape: offsetRes }); + expect(faceArea).toBeGreaterThan(faceAreaOriginal); + sphere.delete(); + offsetRes.delete(); + }); + + it("should offset a circle wire using a face reference", () => { + const circleWire = wire.createCircleWire({ center: [0, 0, 0], radius: 1, direction: [0, 1, 0] }); + const f = face.createFaceFromWire({ shape: circleWire, planar: true }); + const fRev = transforms.mirrorAlongNormal({ shape: f, origin: [0, 0, 0], normal: [1, 0, 0] }); + const offsetRes = operations.offsetAdv({ + shape: circleWire, + face: fRev as TopoDS_Face, + distance: 0.2, + tolerance: 1e-7, + joinType: Inputs.OCCT.joinTypeEnum.arc, + removeIntEdges: false + }); + const wires = wire.getWires({ shape: offsetRes }); + const length = wire.getWireLength({ shape: wires[0] }); + // Original circumference is 2*PI*1 ≈ 6.28, offset outward by 0.2 gives 2*PI*1.2 ≈ 7.54 + expect(length).toBeCloseTo(2 * Math.PI * 1.2, 1); + circleWire.delete(); + f.delete(); + fRev.delete(); + offsetRes.delete(); + wires.forEach(w => w.delete()); + }); + + it("should return original shape when distance is zero", () => { + const squareWire = wire.createSquareWire({ size: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const offsetRes = operations.offsetAdv({ + shape: squareWire, + distance: 0, + tolerance: 1e-7, + joinType: Inputs.OCCT.joinTypeEnum.arc, + removeIntEdges: false + }); + // When distance is 0, it should return the original shape + expect(offsetRes).toBe(squareWire); + squareWire.delete(); + }); + + it("should offset a box solid", () => { + const box = occHelper.entitiesService.bRepPrimAPIMakeBox(2, 2, 2, [0, 0, 0]); + const offsetRes = operations.offsetAdv({ + shape: box, + distance: 0.1, + tolerance: 1e-7, + joinType: Inputs.OCCT.joinTypeEnum.arc, + removeIntEdges: false + }); + const volumeOriginal = solid.getSolidVolume({ shape: box }); + const volumeOffset = solid.getSolidVolume({ shape: offsetRes }); + // Offset outward should increase volume + expect(volumeOffset).toBeGreaterThan(volumeOriginal); + box.delete(); + offsetRes.delete(); + }); + }); + it("should extrude multiple shapes", () => { const squareFace = face.createSquareFace({ center: [0, 0, 3], size: 1, direction: [0, 0, 1] }); const circleFace = face.createCircleFace({ center: [0, 0, 0], radius: 1, direction: [0, 0, 1] }); @@ -914,6 +1037,93 @@ describe("OCCT operations unit tests", () => { res.delete(); }); + describe("splitShapeWithShapes", () => { + it("should split a face with a wire", () => { + const squareFace = face.createSquareFace({ size: 4, center: [0, 0, 0], direction: [0, 1, 0] }); + const circleWire = wire.createCircleWire({ center: [0, 0, 0], radius: 1, direction: [0, 1, 0] }); + const splitDto = new Inputs.OCCT.SplitDto(squareFace as TopoDS_Shape, [circleWire as TopoDS_Shape]); + const results = operations.splitShapeWithShapes(splitDto); + // Split should return multiple shapes (the face pieces) + expect(results.length).toBe(3); + squareFace.delete(); + circleWire.delete(); + results.forEach(s => s.delete()); + }); + + it("should split a face with multiple wires", () => { + const squareFace = face.createSquareFace({ size: 6, center: [0, 0, 0], direction: [0, 1, 0] }); + const circleWire1 = wire.createCircleWire({ center: [-1, 0, 0], radius: 0.5, direction: [0, 1, 0] }); + const circleWire2 = wire.createCircleWire({ center: [1, 0, 0], radius: 0.5, direction: [0, 1, 0] }); + const splitDto = new Inputs.OCCT.SplitDto(squareFace as TopoDS_Shape, [circleWire1 as TopoDS_Shape, circleWire2 as TopoDS_Shape]); + const results = operations.splitShapeWithShapes(splitDto); + // Split with 2 wires should return more pieces + expect(results.length).toBe(5); + squareFace.delete(); + circleWire1.delete(); + circleWire2.delete(); + results.forEach(s => s.delete()); + }); + + it("should return results when splitting shapes", () => { + const squareFace = face.createSquareFace({ size: 4, center: [0, 0, 0], direction: [0, 1, 0] }); + const lineWire = wire.createLineWire({ start: [-3, 0, 0], end: [3, 0, 0] }); + const splitDto = new Inputs.OCCT.SplitDto(squareFace as TopoDS_Shape, [lineWire as TopoDS_Shape]); + const results = operations.splitShapeWithShapes(splitDto); + expect(results.length).toBe(3); + squareFace.delete(); + lineWire.delete(); + results.forEach(s => s.delete()); + }); + }); + + describe("makeThickSolidByJoin", () => { + it("should make thick solid from a solid by removing a face", () => { + const box = occHelper.entitiesService.bRepPrimAPIMakeBox(2, 2, 2, [0, 0, 0]); + const boxFaces = face.getFaces({ shape: box }); + const topFace = boxFaces[boxFaces.length - 1]; // Get top face to remove + const thickDto = new Inputs.OCCT.ThickSolidByJoinDto(box, [topFace as TopoDS_Shape], 0.2, 1e-3); + const result = operations.makeThickSolidByJoin(thickDto); + const vol = solid.getSolidVolume({ shape: result }); + // Original box is 8, thick solid should be larger due to offset + expect(vol).toBeCloseTo(4.519409997776157); + box.delete(); + boxFaces.forEach(f => f.delete()); + result.delete(); + }); + + it("should make thick solid with arc join type", () => { + const box = occHelper.entitiesService.bRepPrimAPIMakeBox(2, 2, 2, [0, 0, 0]); + const boxFaces = face.getFaces({ shape: box }); + const topFace = boxFaces[boxFaces.length - 1]; // Get top face to remove + const thickDto = new Inputs.OCCT.ThickSolidByJoinDto( + box, [topFace as TopoDS_Shape], 0.3, 1e-3, false, false, Inputs.OCCT.joinTypeEnum.arc, false + ); + const resultArc = operations.makeThickSolidByJoin(thickDto); + const volArc = solid.getSolidVolume({ shape: resultArc }); + expect(volArc).toBeCloseTo(7.187522023473766); + + box.delete(); + boxFaces.forEach(f => f.delete()); + resultArc.delete(); + }); + + it("should make thick solid with intersection join type", () => { + const box = occHelper.entitiesService.bRepPrimAPIMakeBox(2, 2, 2, [0, 0, 0]); + const boxFaces = face.getFaces({ shape: box }); + const topFace = boxFaces[boxFaces.length - 1]; // Get top face to remove + const thickDto = new Inputs.OCCT.ThickSolidByJoinDto( + box, [topFace as TopoDS_Shape], 0.3, 1e-3, false, false, Inputs.OCCT.joinTypeEnum.intersection, false + ); + const resultIntersection = operations.makeThickSolidByJoin(thickDto); + const volIntersection = solid.getSolidVolume({ shape: resultIntersection }); + expect(volIntersection).toBeCloseTo(7.547999999999998); + + box.delete(); + boxFaces.forEach(f => f.delete()); + resultIntersection.delete(); + }); + }); + describe("Bounding box operations", () => { it("should get bounding box properties of a box shape", () => { const box = occHelper.entitiesService.bRepPrimAPIMakeBox(2, 3, 4, [0, 0, 0]); From a64eb0d42c25a856af9125f186edfffbba6bc476 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 08:46:42 +0200 Subject: [PATCH 10/38] dxf service unit tests --- .../lib/services/base/dxf.service.test.ts | 336 ++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 packages/dev/occt/lib/services/base/dxf.service.test.ts diff --git a/packages/dev/occt/lib/services/base/dxf.service.test.ts b/packages/dev/occt/lib/services/base/dxf.service.test.ts new file mode 100644 index 00000000..985c1fe8 --- /dev/null +++ b/packages/dev/occt/lib/services/base/dxf.service.test.ts @@ -0,0 +1,336 @@ +import initOpenCascade, { OpenCascadeInstance, TopoDS_Edge, TopoDS_Shape } from "../../../bitbybit-dev-occt/bitbybit-dev-occt"; +import { OccHelper } from "../../occ-helper"; +import { VectorHelperService } from "../../api/vector-helper.service"; +import { ShapesHelperService } from "../../api/shapes-helper.service"; +import { OCCTWire, OCCTEdge } from "../shapes"; +import { DxfService } from "./dxf.service"; +import * as Inputs from "../../api/inputs/inputs"; + +describe("DxfService unit tests", () => { + let occt: OpenCascadeInstance; + let occHelper: OccHelper; + let dxfService: DxfService; + let wire: OCCTWire; + let edge: OCCTEdge; + + beforeAll(async () => { + occt = await initOpenCascade(); + const vec = new VectorHelperService(); + const s = new ShapesHelperService(); + occHelper = new OccHelper(vec, s, occt); + dxfService = occHelper.dxfService; + wire = new OCCTWire(occt, occHelper); + edge = new OCCTEdge(occt, occHelper); + }); + + describe("tryCreatePolylineWithBulges", () => { + // Helper to access private method + const callTryCreatePolylineWithBulges = ( + service: DxfService, + edges: TopoDS_Edge[], + startIndex: number, + shouldBeClosed: boolean + ) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (service as any).tryCreatePolylineWithBulges(edges, startIndex, shouldBeClosed); + }; + + it("should return null for a single linear edge", () => { + const lineEdge = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] }); + const edges = [lineEdge]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + // Returns null because we need at least 2 edges + expect(result).toBeNull(); + + lineEdge.delete(); + }); + + it("should return null for a full circle edge", () => { + const circleEdge = edge.createCircleEdge({ + radius: 5, + center: [0, 0, 0], + direction: [0, 1, 0] + }); + const edges = [circleEdge]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + // Full circles are handled separately + expect(result).toBeNull(); + + circleEdge.delete(); + }); + + it("should create a polyline from two consecutive linear edges", () => { + const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] }); + const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [10, 0, 10] }); + const edges = [edge1, edge2]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + expect(result.nextIndex).toBe(2); + expect(result.polyline.points.length).toBe(3); // Start + mid + end + expect(result.polyline.closed).toBe(false); + + // All bulges should be 0 for linear edges + result.polyline.bulges.forEach((bulge: number) => { + expect(bulge).toBe(0); + }); + + edge1.delete(); + edge2.delete(); + }); + + it("should create a polyline from two consecutive linear edges with closed flag", () => { + const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] }); + const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [0, 0, 0] }); + const edges = [edge1, edge2]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, true); + + expect(result).not.toBeNull(); + expect(result.polyline.closed).toBe(true); + expect(result.polyline.points.length).toBe(3); + + edge1.delete(); + edge2.delete(); + }); + + it("should create a polyline with non-zero bulges for arc edges", () => { + const lineEdge = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] }); + const arcEdge = occHelper.edgesService.arcThroughThreePoints({ + start: [10, 0, 0], + middle: [12, 0, 5], + end: [10, 0, 10] + }); + const edges = [lineEdge, arcEdge]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + expect(result.nextIndex).toBe(2); + expect(result.polyline.points.length).toBe(3); + + // First bulge should be 0 (line), second should be non-zero (arc) + expect(result.polyline.bulges[0]).toBe(0); + expect(Math.abs(result.polyline.bulges[1])).toBeCloseTo(0.399999999); + + lineEdge.delete(); + arcEdge.delete(); + }); + + it("should stop at complex edge", () => { + const lineEdge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [5, 0, 0] }); + const lineEdge2 = occHelper.edgesService.lineEdge({ start: [5, 0, 0], end: [10, 0, 0] }); + + // Create a bezier/spline edge (complex edge) + const bezierWire = wire.interpolatePoints({ + points: [[10, 0, 0], [12, 0, 3], [15, 0, 5]] as Inputs.Base.Point3[], + periodic: false, + tolerance: 0.0001 + }); + const bezierEdges = occHelper.edgesService.getEdgesAlongWire({ shape: bezierWire }); + + const lineEdge3 = occHelper.edgesService.lineEdge({ start: [15, 0, 5], end: [20, 0, 5] }); + + const edges = [lineEdge1, lineEdge2, ...bezierEdges, lineEdge3]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + // Should stop at the complex bezier edge + expect(result.nextIndex).toBe(2); + expect(result.polyline.points.length).toBe(3); + + lineEdge1.delete(); + lineEdge2.delete(); + bezierWire.delete(); + lineEdge3.delete(); + }); + + it("should return null when starting with a complex edge", () => { + const bezierWire = wire.interpolatePoints({ + points: [[0, 0, 0], [5, 0, 3], [10, 0, 0]] as Inputs.Base.Point3[], + periodic: false, + tolerance: 0.0001 + }); + const bezierEdges = occHelper.edgesService.getEdgesAlongWire({ shape: bezierWire }); + + const result = callTryCreatePolylineWithBulges(dxfService, bezierEdges, 0, false); + + // Complex edge at start should return null + expect(result).toBeNull(); + + bezierWire.delete(); + }); + + it("should handle starting from a middle index", () => { + const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [5, 0, 0] }); + const edge2 = occHelper.edgesService.lineEdge({ start: [5, 0, 0], end: [10, 0, 0] }); + const edge3 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [15, 0, 0] }); + const edge4 = occHelper.edgesService.lineEdge({ start: [15, 0, 0], end: [20, 0, 0] }); + const edges = [edge1, edge2, edge3, edge4]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 2, false); + + expect(result).not.toBeNull(); + expect(result.nextIndex).toBe(4); + expect(result.polyline.points.length).toBe(3); // edge3 start, edge4 start, edge4 end + + // First point should be start of edge3 + expect(result.polyline.points[0][0]).toBeCloseTo(10, 2); // X + expect(result.polyline.points[0][1]).toBeCloseTo(0, 2); // Z (mapped to Y in DXF) + + edge1.delete(); + edge2.delete(); + edge3.delete(); + edge4.delete(); + }); + + it("should create polyline with correct point mapping from XZ to XY", () => { + // Points at different Z values (which become Y in DXF) + const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 5] }); + const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 5], end: [20, 0, 10] }); + const edges = [edge1, edge2]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + // Check X coordinates + expect(result.polyline.points[0][0]).toBeCloseTo(0, 2); + expect(result.polyline.points[1][0]).toBeCloseTo(10, 2); + expect(result.polyline.points[2][0]).toBeCloseTo(20, 2); + + // Check Y coordinates (originally Z in 3D) + expect(result.polyline.points[0][1]).toBeCloseTo(0, 2); + expect(result.polyline.points[1][1]).toBeCloseTo(5, 2); + expect(result.polyline.points[2][1]).toBeCloseTo(10, 2); + + edge1.delete(); + edge2.delete(); + }); + + it("should handle mixed linear and arc edges", () => { + const line1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] }); + const arc = occHelper.edgesService.arcThroughThreePoints({ + start: [10, 0, 0], + middle: [15, 0, 5], + end: [10, 0, 10] + }); + const line2 = occHelper.edgesService.lineEdge({ start: [10, 0, 10], end: [0, 0, 10] }); + const edges = [line1, arc, line2]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + expect(result.nextIndex).toBe(3); + expect(result.polyline.points.length).toBe(4); + + // Check bulges: line=0, arc=non-zero, line=0, last=0 + expect(result.polyline.bulges[0]).toBe(0); + expect(Math.abs(result.polyline.bulges[1])).toBeCloseTo(1); + expect(result.polyline.bulges[2]).toBe(0); + expect(result.polyline.bulges[3]).toBe(0); + + line1.delete(); + arc.delete(); + line2.delete(); + }); + + it("should stop when encountering a full circle in the middle", () => { + const line1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] }); + const line2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [20, 0, 0] }); + const circleEdge = edge.createCircleEdge({ + radius: 3, + center: [25, 0, 0], + direction: [0, 1, 0] + }); + const line3 = occHelper.edgesService.lineEdge({ start: [30, 0, 0], end: [40, 0, 0] }); + const edges = [line1, line2, circleEdge, line3]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + // Should stop at the full circle + expect(result.nextIndex).toBe(2); + expect(result.polyline.points.length).toBe(3); + + line1.delete(); + line2.delete(); + circleEdge.delete(); + line3.delete(); + }); + + it("should handle a closed rectangle with 4 edges", () => { + const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] }); + const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [10, 0, 5] }); + const edge3 = occHelper.edgesService.lineEdge({ start: [10, 0, 5], end: [0, 0, 5] }); + const edge4 = occHelper.edgesService.lineEdge({ start: [0, 0, 5], end: [0, 0, 0] }); + const edges = [edge1, edge2, edge3, edge4]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, true); + + expect(result).not.toBeNull(); + expect(result.nextIndex).toBe(4); + expect(result.polyline.closed).toBe(true); + expect(result.polyline.points.length).toBe(5); // 4 corners + end point + + // All bulges should be 0 + result.polyline.bulges.forEach((bulge: number) => { + expect(bulge).toBe(0); + }); + + edge1.delete(); + edge2.delete(); + edge3.delete(); + edge4.delete(); + }); + + it("should calculate correct bulge for a 90-degree arc", () => { + // Create a 90-degree arc from (5, 0, 0) to (0, 0, 5) with center at origin, radius 5 + // This is a CCW 90-degree arc in the XZ plane + const arcEdge = occHelper.edgesService.arcThroughThreePoints({ + start: [5, 0, 0], + middle: [5 * Math.cos(Math.PI / 4), 0, 5 * Math.sin(Math.PI / 4)], // 45 degrees + end: [0, 0, 5] + }); + const lineEdge = occHelper.edgesService.lineEdge({ start: [0, 0, 5], end: [0, 0, 10] }); + const edges = [arcEdge, lineEdge]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + + // For a 90-degree arc, bulge = tan(90/4) = tan(22.5°) ≈ 0.414 + // Sign depends on arc direction + expect(Math.abs(result.polyline.bulges[0])).toBeCloseTo(0.414, 1); + + arcEdge.delete(); + lineEdge.delete(); + }); + + it("should calculate correct bulge for a 180-degree arc (semicircle)", () => { + // Create a semicircle arc + const arcEdge = occHelper.edgesService.arcThroughThreePoints({ + start: [0, 0, 0], + middle: [5, 0, 5], + end: [10, 0, 0] + }); + const lineEdge = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [20, 0, 0] }); + const edges = [arcEdge, lineEdge]; + + const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false); + + expect(result).not.toBeNull(); + + // For a 180-degree arc, bulge = tan(180/4) = tan(45°) = 1.0 + expect(Math.abs(result.polyline.bulges[0])).toBeCloseTo(1.0, 1); + + arcEdge.delete(); + lineEdge.delete(); + }); + }); +}); From 8149539bf7637ecf719dde102f6b49fb5e826ada Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 10:38:39 +0200 Subject: [PATCH 11/38] some unit tests for reversedWireFromReversedEdges --- .../dev/occt/lib/services/shapes/wire.test.ts | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/packages/dev/occt/lib/services/shapes/wire.test.ts b/packages/dev/occt/lib/services/shapes/wire.test.ts index d8507879..72a3aa52 100644 --- a/packages/dev/occt/lib/services/shapes/wire.test.ts +++ b/packages/dev/occt/lib/services/shapes/wire.test.ts @@ -532,6 +532,82 @@ describe("OCCT wire unit tests", () => { w2.delete(); }); + it("should reverse closed polygon wire and have same start point using reversedWireFromReversedEdges", async () => { + // Create a closed polygon (rectangle) + const points = [[0, 0, 0], [10, 0, 0], [10, 0, 5], [0, 0, 5]] as Inputs.Base.Point3[]; + const w = wire.createPolygonWire({ points }); + + const startPt = wire.startPointOnWire({ shape: w }); + const endPt = wire.endPointOnWire({ shape: w }); + + // For a closed polygon, start and end should be the same + expect(startPt[0]).toBeCloseTo(endPt[0], 5); + expect(startPt[1]).toBeCloseTo(endPt[1], 5); + expect(startPt[2]).toBeCloseTo(endPt[2], 5); + + // Use reversedWireFromReversedEdges for closed wires to maintain start point + const w2 = wire.reversedWireFromReversedEdges({ shape: w }); + + const startPtRev = wire.startPointOnWire({ shape: w2 }); + + // For a reversed closed polygon using reversedWireFromReversedEdges, + // the start point should be the same as the original + // User expectation: forward, left, back, right becomes left, forward, right, back + // Same start/end point but opposite traversal direction + expect(startPtRev[0]).toBeCloseTo(startPt[0], 5); + expect(startPtRev[1]).toBeCloseTo(startPt[1], 5); + expect(startPtRev[2]).toBeCloseTo(startPt[2], 5); + + w.delete(); + w2.delete(); + }); + + it("should reverse closed polygon wire edges and have correct edge directions using reversedWireFromReversedEdges", async () => { + // Create a closed polygon (rectangle) + const points = [[0, 0, 0], [10, 0, 0], [10, 0, 5], [0, 0, 5]] as Inputs.Base.Point3[]; + const w = wire.createPolygonWire({ points }); + + const allEdges = edge.getEdgesAlongWire({ shape: w }); + const firstEdgeStart = edge.startPointOnEdge({ shape: allEdges[0] }); + + // Use reversedWireFromReversedEdges for closed wires + const w2 = wire.reversedWireFromReversedEdges({ shape: w }); + + const allEdgesRev = edge.getEdgesAlongWire({ shape: w2 }); + const firstEdgeRevStart = edge.startPointOnEdge({ shape: allEdgesRev[0] }); + + // For a reversed wire using reversedWireFromReversedEdges, + // the first edge's start should be the original first edge's start + expect(firstEdgeRevStart[0]).toBeCloseTo(firstEdgeStart[0], 5); + expect(firstEdgeRevStart[1]).toBeCloseTo(firstEdgeStart[1], 5); + expect(firstEdgeRevStart[2]).toBeCloseTo(firstEdgeStart[2], 5); + + allEdges.forEach(e => e.delete()); + allEdgesRev.forEach(e => e.delete()); + w.delete(); + w2.delete(); + }); + + it("should reverse closed rectangle wire and maintain start point using reversedWireFromReversedEdges", async () => { + // Create a closed rectangle wire + const w = wire.createRectangleWire({ width: 10, length: 5, center: [5, 0, 2.5], direction: [0, 1, 0] }); + + const startPt = wire.startPointOnWire({ shape: w }); + + // Use reversedWireFromReversedEdges for closed wires + const w2 = wire.reversedWireFromReversedEdges({ shape: w }); + + const startPtRev = wire.startPointOnWire({ shape: w2 }); + + // The reversed wire should have the same start point as the original + expect(startPtRev[0]).toBeCloseTo(startPt[0], 5); + expect(startPtRev[1]).toBeCloseTo(startPt[1], 5); + expect(startPtRev[2]).toBeCloseTo(startPt[2], 5); + + w.delete(); + w2.delete(); + }); + it("should get wire of a box at specific index", async () => { const b = occHelper.entitiesService.bRepPrimAPIMakeBox(3, 4, 5, [0, 0, 0]); const w = wire.getWire({ shape: b, index: 2 }); From f2621041c17a6f5a032000cde02732e54d724a9f Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 10:46:38 +0200 Subject: [PATCH 12/38] unit test shape-fix method --- .../dev/occt/lib/services/shape-fix.test.ts | 175 +++++++++++++++++- 1 file changed, 174 insertions(+), 1 deletion(-) diff --git a/packages/dev/occt/lib/services/shape-fix.test.ts b/packages/dev/occt/lib/services/shape-fix.test.ts index 28f1ad96..47d4579a 100644 --- a/packages/dev/occt/lib/services/shape-fix.test.ts +++ b/packages/dev/occt/lib/services/shape-fix.test.ts @@ -1,4 +1,4 @@ -import initOpenCascade, { OpenCascadeInstance } from "../../bitbybit-dev-occt/bitbybit-dev-occt"; +import initOpenCascade, { OpenCascadeInstance, TopoDS_Edge } from "../../bitbybit-dev-occt/bitbybit-dev-occt"; import { OccHelper } from "../occ-helper"; import { VectorHelperService } from "../api/vector-helper.service"; import { ShapesHelperService } from "../api/shapes-helper.service"; @@ -14,6 +14,12 @@ describe("OCCT shape fix unit tests", () => { let shapeFix: OCCTShapeFix; let occHelper: OccHelper; + const getEdgeStartAndEndPoints = (e: TopoDS_Edge): { start: number[], end: number[] } => { + const startPt = occHelper.edgesService.startPointOnEdge({ shape: e }); + const endPt = occHelper.edgesService.endPointOnEdge({ shape: e }); + return { start: startPt, end: endPt }; + }; + beforeAll(async () => { occt = await initOpenCascade(); const vec = new VectorHelperService(); @@ -112,5 +118,172 @@ describe("OCCT shape fix unit tests", () => { edgesOriginal.forEach(e => e.delete()); edges.forEach(e => e.delete()); }); + + it("should fix edge orientations along wire when edges have inconsistent directions", async () => { + // Create edges where the second edge is reversed (end to start instead of start to end) + const edge1 = edge.line({ start: [0, 0, 0], end: [1, 0, 0] }); + // This edge goes backwards - from [2,0,0] to [1,0,0] instead of [1,0,0] to [2,0,0] + const edge2 = edge.line({ start: [2, 0, 0], end: [1, 0, 0] }); + const edge3 = edge.line({ start: [2, 0, 0], end: [3, 0, 0] }); + + const wire1 = wire.combineEdgesAndWiresIntoAWire({ + shapes: [edge1, edge2, edge3], + }); + + const result = shapeFix.fixEdgeOrientationsAlongWire({ shape: wire1 }); + + // Get edges from the fixed wire + const fixedEdges = edge.getEdges({ shape: result }); + expect(fixedEdges.length).toBe(3); + + // Check that edges are now properly oriented along the wire + const edge1Points = getEdgeStartAndEndPoints(fixedEdges[0]); + const edge2Points = getEdgeStartAndEndPoints(fixedEdges[1]); + const edge3Points = getEdgeStartAndEndPoints(fixedEdges[2]); + + // Each edge's end should connect to the next edge's start + expect(edge1Points.end[0]).toBeCloseTo(edge2Points.start[0], 5); + expect(edge1Points.end[1]).toBeCloseTo(edge2Points.start[1], 5); + expect(edge1Points.end[2]).toBeCloseTo(edge2Points.start[2], 5); + + expect(edge2Points.end[0]).toBeCloseTo(edge3Points.start[0], 5); + expect(edge2Points.end[1]).toBeCloseTo(edge3Points.start[1], 5); + expect(edge2Points.end[2]).toBeCloseTo(edge3Points.start[2], 5); + + edge1.delete(); + edge2.delete(); + edge3.delete(); + wire1.delete(); + result.delete(); + fixedEdges.forEach(e => e.delete()); + }); + + it("should preserve wire when edges already have correct orientations", async () => { + // Create properly oriented edges + const edge1 = edge.line({ start: [0, 0, 0], end: [1, 0, 0] }); + const edge2 = edge.line({ start: [1, 0, 0], end: [2, 0, 0] }); + const edge3 = edge.line({ start: [2, 0, 0], end: [3, 0, 0] }); + + const wire1 = wire.combineEdgesAndWiresIntoAWire({ + shapes: [edge1, edge2, edge3], + }); + + const result = shapeFix.fixEdgeOrientationsAlongWire({ shape: wire1 }); + + const fixedEdges = edge.getEdges({ shape: result }); + expect(fixedEdges.length).toBe(3); + + // Verify edges maintain correct orientation + const edge1Points = getEdgeStartAndEndPoints(fixedEdges[0]); + const edge2Points = getEdgeStartAndEndPoints(fixedEdges[1]); + const edge3Points = getEdgeStartAndEndPoints(fixedEdges[2]); + + expect(edge1Points.start).toEqual([0, 0, 0]); + expect(edge1Points.end).toEqual([1, 0, 0]); + expect(edge2Points.start).toEqual([1, 0, 0]); + expect(edge2Points.end).toEqual([2, 0, 0]); + expect(edge3Points.start).toEqual([2, 0, 0]); + expect(edge3Points.end).toEqual([3, 0, 0]); + + edge1.delete(); + edge2.delete(); + edge3.delete(); + wire1.delete(); + result.delete(); + fixedEdges.forEach(e => e.delete()); + }); + + it("should fix edge orientations in a closed wire", async () => { + // Create a closed triangle with one reversed edge + const edge1 = edge.line({ start: [0, 0, 0], end: [1, 0, 0] }); + // Reversed edge + const edge2 = edge.line({ start: [0.5, 1, 0], end: [1, 0, 0] }); + const edge3 = edge.line({ start: [0.5, 1, 0], end: [0, 0, 0] }); + + const wire1 = wire.combineEdgesAndWiresIntoAWire({ + shapes: [edge1, edge2, edge3], + }); + + expect(occHelper.wiresService.isWireClosed({ shape: wire1 })).toBe(true); + + const result = shapeFix.fixEdgeOrientationsAlongWire({ shape: wire1 }); + + expect(occHelper.wiresService.isWireClosed({ shape: result })).toBe(true); + + const fixedEdges = edge.getEdges({ shape: result }); + expect(fixedEdges.length).toBe(3); + + // Check connectivity of fixed edges + const edge1Points = getEdgeStartAndEndPoints(fixedEdges[0]); + const edge2Points = getEdgeStartAndEndPoints(fixedEdges[1]); + const edge3Points = getEdgeStartAndEndPoints(fixedEdges[2]); + + expect(edge1Points.end[0]).toBeCloseTo(edge2Points.start[0], 5); + expect(edge1Points.end[1]).toBeCloseTo(edge2Points.start[1], 5); + expect(edge2Points.end[0]).toBeCloseTo(edge3Points.start[0], 5); + expect(edge2Points.end[1]).toBeCloseTo(edge3Points.start[1], 5); + expect(edge3Points.end[0]).toBeCloseTo(edge1Points.start[0], 5); + expect(edge3Points.end[1]).toBeCloseTo(edge1Points.start[1], 5); + + edge1.delete(); + edge2.delete(); + edge3.delete(); + wire1.delete(); + result.delete(); + fixedEdges.forEach(e => e.delete()); + }); + + it("should fix edge orientations with arc edges", async () => { + const edge1 = edge.line({ start: [0, 0, 0], end: [1, 0, 0] }); + // Arc going backwards + const arcEdge = edge.arcThroughThreePoints({ start: [2, 0, 0], middle: [1.5, 0.5, 0], end: [1, 0, 0] }); + const edge3 = edge.line({ start: [2, 0, 0], end: [3, 0, 0] }); + + const wire1 = wire.combineEdgesAndWiresIntoAWire({ + shapes: [edge1, arcEdge, edge3], + }); + + const result = shapeFix.fixEdgeOrientationsAlongWire({ shape: wire1 }); + + const fixedEdges = edge.getEdges({ shape: result }); + expect(fixedEdges.length).toBe(3); + + // Check that edges are connected properly + const edge1Points = getEdgeStartAndEndPoints(fixedEdges[0]); + const edge2Points = getEdgeStartAndEndPoints(fixedEdges[1]); + const edge3Points = getEdgeStartAndEndPoints(fixedEdges[2]); + + expect(edge1Points.end[0]).toBeCloseTo(edge2Points.start[0], 5); + expect(edge2Points.end[0]).toBeCloseTo(edge3Points.start[0], 5); + + edge1.delete(); + arcEdge.delete(); + edge3.delete(); + wire1.delete(); + result.delete(); + fixedEdges.forEach(e => e.delete()); + }); + + it("should handle a single edge wire", async () => { + const edge1 = edge.line({ start: [0, 0, 0], end: [1, 0, 0] }); + + const wire1 = wire.combineEdgesAndWiresIntoAWire({ + shapes: [edge1], + }); + + const result = shapeFix.fixEdgeOrientationsAlongWire({ shape: wire1 }); + + const fixedEdges = edge.getEdges({ shape: result }); + expect(fixedEdges.length).toBe(1); + + const edgePoints = getEdgeStartAndEndPoints(fixedEdges[0]); + expect(edgePoints.start).toEqual([0, 0, 0]); + expect(edgePoints.end).toEqual([1, 0, 0]); + + edge1.delete(); + wire1.delete(); + result.delete(); + fixedEdges.forEach(e => e.delete()); + }); }); From c37868722f6ed6c586a9aa0f81e53b2c72a8ad7b Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 10:56:52 +0200 Subject: [PATCH 13/38] unit tests for iterator service --- .../services/base/iterator.service.test.ts | 393 ++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 packages/dev/occt/lib/services/base/iterator.service.test.ts diff --git a/packages/dev/occt/lib/services/base/iterator.service.test.ts b/packages/dev/occt/lib/services/base/iterator.service.test.ts new file mode 100644 index 00000000..0f7b00a1 --- /dev/null +++ b/packages/dev/occt/lib/services/base/iterator.service.test.ts @@ -0,0 +1,393 @@ +import initOpenCascade, { OpenCascadeInstance, TopoDS_Edge, TopoDS_Face, TopoDS_Shell, TopoDS_Solid, TopoDS_Vertex, TopoDS_Wire, TopoDS_Shape } from "../../../bitbybit-dev-occt/bitbybit-dev-occt"; +import { OccHelper } from "../../occ-helper"; +import { VectorHelperService } from "../../api/vector-helper.service"; +import { ShapesHelperService } from "../../api/shapes-helper.service"; +import { IteratorService } from "./iterator.service"; +import { OCCTSolid } from "../shapes/solid"; +import { OCCTFace } from "../shapes/face"; +import { OCCTCompound } from "../shapes/compound"; + +describe("OCCT iterator service unit tests", () => { + let occt: OpenCascadeInstance; + let occHelper: OccHelper; + let iteratorService: IteratorService; + let solid: OCCTSolid; + let faceService: OCCTFace; + let compoundService: OCCTCompound; + + beforeAll(async () => { + occt = await initOpenCascade(); + const vec = new VectorHelperService(); + const s = new ShapesHelperService(); + occHelper = new OccHelper(vec, s, occt); + iteratorService = new IteratorService(occt); + solid = new OCCTSolid(occt, occHelper); + faceService = new OCCTFace(occt, occHelper); + compoundService = new OCCTCompound(occt, occHelper); + }); + + describe("forEachWire", () => { + it("should iterate over wires in a face", () => { + const f = faceService.createSquareFace({ size: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const wires: TopoDS_Wire[] = []; + iteratorService.forEachWire(f, (index, wire) => { + wires.push(wire); + }); + expect(wires.length).toBe(1); + f.delete(); + wires.forEach(w => w.delete()); + }); + + it("should iterate over multiple wires in a face with a hole", () => { + const outerWire = occHelper.wiresService.createPolygonWire({ points: [[0, 0, 0], [10, 0, 0], [10, 10, 0], [0, 10, 0]] }); + const innerWire = occHelper.wiresService.createPolygonWire({ points: [[2, 2, 0], [8, 2, 0], [8, 8, 0], [2, 8, 0]] }); + const f = faceService.createFaceFromWires({ shapes: [outerWire, innerWire], planar: true }); + + const wires: TopoDS_Wire[] = []; + iteratorService.forEachWire(f, (index, wire) => { + wires.push(wire); + }); + expect(wires.length).toBe(2); + + outerWire.delete(); + innerWire.delete(); + f.delete(); + wires.forEach(w => w.delete()); + }); + }); + + describe("forEachEdge", () => { + it("should iterate over edges in a wire", () => { + const wire = occHelper.wiresService.createPolygonWire({ points: [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]] }); + const edges: TopoDS_Edge[] = []; + iteratorService.forEachEdge(wire, (index, edge) => { + edges.push(edge); + }); + expect(edges.length).toBe(4); + wire.delete(); + edges.forEach(e => e.delete()); + }); + + it("should not duplicate edges with same hash", () => { + const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [1, 0, 0] }); + const edge2 = occHelper.edgesService.lineEdge({ start: [1, 0, 0], end: [2, 0, 0] }); + const wire = occHelper.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge1, edge2] }); + + const edges: TopoDS_Edge[] = []; + iteratorService.forEachEdge(wire, (index, edge) => { + edges.push(edge); + }); + expect(edges.length).toBe(2); + + edge1.delete(); + edge2.delete(); + wire.delete(); + edges.forEach(e => e.delete()); + }); + }); + + describe("forEachEdgeAlongWire", () => { + it("should iterate over edges in order along the wire", () => { + // Use explicit edges to have control over the count + const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [1, 0, 0] }); + const edge2 = occHelper.edgesService.lineEdge({ start: [1, 0, 0], end: [1, 1, 0] }); + const wire = occHelper.converterService.combineEdgesAndWiresIntoAWire({ shapes: [edge1, edge2] }); + + const edges: TopoDS_Edge[] = []; + iteratorService.forEachEdgeAlongWire(wire, (index, edge) => { + edges.push(edge); + }); + expect(edges.length).toBe(2); + + // Check that edges are in correct order + const edge1Start = occHelper.edgesService.startPointOnEdge({ shape: edges[0] }); + const edge1End = occHelper.edgesService.endPointOnEdge({ shape: edges[0] }); + const edge2Start = occHelper.edgesService.startPointOnEdge({ shape: edges[1] }); + + expect(edge1Start[0]).toBeCloseTo(0, 5); + expect(edge1End[0]).toBeCloseTo(1, 5); + expect(edge2Start[0]).toBeCloseTo(1, 5); + + edge1.delete(); + edge2.delete(); + wire.delete(); + edges.forEach(e => e.delete()); + }); + }); + + describe("forEachFace", () => { + it("should iterate over faces in a solid", () => { + const box = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const faces: TopoDS_Face[] = []; + iteratorService.forEachFace(box, (index, f) => { + faces.push(f); + }); + expect(faces.length).toBe(6); + box.delete(); + faces.forEach(f => f.delete()); + }); + + it("should iterate over a single face", () => { + const f = faceService.createSquareFace({ size: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const faces: TopoDS_Face[] = []; + iteratorService.forEachFace(f, (index, face) => { + faces.push(face); + }); + expect(faces.length).toBe(1); + f.delete(); + faces.forEach(f => f.delete()); + }); + }); + + describe("forEachShell", () => { + it("should iterate over shells in a solid", () => { + const box = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const shells: TopoDS_Shell[] = []; + iteratorService.forEachShell(box, (index, shell) => { + shells.push(shell); + }); + expect(shells.length).toBe(1); + box.delete(); + shells.forEach(s => s.delete()); + }); + + it("should iterate over shells in a compound of solids", () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const comp = compoundService.makeCompound({ shapes: [box1, box2] }); + + const shells: TopoDS_Shell[] = []; + iteratorService.forEachShell(comp, (index, shell) => { + shells.push(shell); + }); + expect(shells.length).toBe(2); + + box1.delete(); + box2.delete(); + comp.delete(); + shells.forEach(s => s.delete()); + }); + + it("should return no shells for a wire shape", () => { + const wire = occHelper.wiresService.createPolygonWire({ points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] }); + const shells: TopoDS_Shell[] = []; + iteratorService.forEachShell(wire, (index, shell) => { + shells.push(shell); + }); + expect(shells.length).toBe(0); + wire.delete(); + }); + }); + + describe("forEachVertex", () => { + it("should iterate over vertices in an edge", () => { + const edge = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [1, 0, 0] }); + const vertices: TopoDS_Vertex[] = []; + iteratorService.forEachVertex(edge, (index, vertex) => { + vertices.push(vertex); + }); + expect(vertices.length).toBe(2); + edge.delete(); + vertices.forEach(v => v.delete()); + }); + + it("should iterate over vertices in a triangle wire counting shared vertices", () => { + // A closed triangle has 3 edges but TopExp_Explorer finds all vertices including shared ones + const wire = occHelper.wiresService.createPolygonWire({ points: [[0, 0, 0], [1, 0, 0], [0.5, 1, 0]] }); + const vertices: TopoDS_Vertex[] = []; + iteratorService.forEachVertex(wire, (index, vertex) => { + vertices.push(vertex); + }); + // Each edge has 2 vertices, 3 edges = 6 vertices found (vertices are shared at corners) + expect(vertices.length).toBe(6); + wire.delete(); + vertices.forEach(v => v.delete()); + }); + }); + + describe("forEachSolid", () => { + it("should iterate over a single solid", () => { + const box = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const solids: TopoDS_Solid[] = []; + iteratorService.forEachSolid(box, (index, s) => { + solids.push(s); + }); + expect(solids.length).toBe(1); + box.delete(); + solids.forEach(s => s.delete()); + }); + + it("should iterate over multiple solids in a compound", () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const sphere = solid.createSphere({ radius: 0.5, center: [10, 0, 0] }); + const comp = compoundService.makeCompound({ shapes: [box1, box2, sphere] }); + + const solids: TopoDS_Solid[] = []; + iteratorService.forEachSolid(comp, (index, s) => { + solids.push(s); + }); + expect(solids.length).toBe(3); + + box1.delete(); + box2.delete(); + sphere.delete(); + comp.delete(); + solids.forEach(s => s.delete()); + }); + + it("should return no solids for a face shape", () => { + const f = faceService.createSquareFace({ size: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const solids: TopoDS_Solid[] = []; + iteratorService.forEachSolid(f, (index, s) => { + solids.push(s); + }); + expect(solids.length).toBe(0); + f.delete(); + }); + }); + + describe("forEachCompound", () => { + it("should iterate over a compound", () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const comp = compoundService.makeCompound({ shapes: [box1, box2] }); + + const compounds: TopoDS_Shape[] = []; + iteratorService.forEachCompound(comp, (index, shape) => { + compounds.push(shape); + }); + expect(compounds.length).toBe(1); + + box1.delete(); + box2.delete(); + comp.delete(); + compounds.forEach(c => c.delete()); + }); + + it("should iterate over nested compounds", () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const innerCompound = compoundService.makeCompound({ shapes: [box1, box2] }); + + const box3 = solid.createBox({ width: 1, height: 1, length: 1, center: [10, 0, 0] }); + const outerCompound = compoundService.makeCompound({ shapes: [innerCompound, box3] }); + + const compounds: TopoDS_Shape[] = []; + iteratorService.forEachCompound(outerCompound, (index, shape) => { + compounds.push(shape); + }); + // TopExp_Explorer finds the outer compound itself when iterating for COMPOUND type + // The inner compound is a child but explorer behavior may vary + expect(compounds.length).toBeGreaterThanOrEqual(1); + + box1.delete(); + box2.delete(); + box3.delete(); + innerCompound.delete(); + outerCompound.delete(); + compounds.forEach(c => c.delete()); + }); + + it("should return no compounds for a solid shape", () => { + const box = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const compounds: TopoDS_Shape[] = []; + iteratorService.forEachCompound(box, (index, shape) => { + compounds.push(shape); + }); + expect(compounds.length).toBe(0); + box.delete(); + }); + }); + + describe("forEachCompSolid", () => { + it("should return no compsolids for a regular solid", () => { + const box = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const compSolids: TopoDS_Shape[] = []; + iteratorService.forEachCompSolid(box, (index, shape) => { + compSolids.push(shape); + }); + expect(compSolids.length).toBe(0); + box.delete(); + }); + + it("should return no compsolids for a compound of solids", () => { + // A compound of solids is not the same as a compsolid + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const comp = compoundService.makeCompound({ shapes: [box1, box2] }); + + const compSolids: TopoDS_Shape[] = []; + iteratorService.forEachCompSolid(comp, (index, shape) => { + compSolids.push(shape); + }); + expect(compSolids.length).toBe(0); + + box1.delete(); + box2.delete(); + comp.delete(); + }); + }); + + describe("forEachShapeInCompound", () => { + it("should iterate over shapes in a compound", () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const comp = compoundService.makeCompound({ shapes: [box1, box2] }); + + const shapes: TopoDS_Shape[] = []; + iteratorService.forEachShapeInCompound(comp, (index, shape) => { + shapes.push(shape); + }); + expect(shapes.length).toBe(2); + + box1.delete(); + box2.delete(); + comp.delete(); + shapes.forEach(s => s.delete()); + }); + + it("should iterate over mixed shapes in a compound", () => { + const box = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const f = faceService.createSquareFace({ size: 1, center: [5, 0, 0], direction: [0, 1, 0] }); + const wire = occHelper.wiresService.createPolygonWire({ points: [[10, 0, 0], [11, 0, 0], [11, 1, 0]] }); + const comp = compoundService.makeCompound({ shapes: [box, f, wire] }); + + const shapes: TopoDS_Shape[] = []; + iteratorService.forEachShapeInCompound(comp, (index, shape) => { + shapes.push(shape); + }); + expect(shapes.length).toBe(3); + + box.delete(); + f.delete(); + wire.delete(); + comp.delete(); + shapes.forEach(s => s.delete()); + }); + + it("should not recurse into nested compounds by default", () => { + const box1 = solid.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = solid.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const innerCompound = compoundService.makeCompound({ shapes: [box1, box2] }); + + const box3 = solid.createBox({ width: 1, height: 1, length: 1, center: [10, 0, 0] }); + const outerCompound = compoundService.makeCompound({ shapes: [innerCompound, box3] }); + + const shapes: TopoDS_Shape[] = []; + iteratorService.forEachShapeInCompound(outerCompound, (index, shape) => { + shapes.push(shape); + }); + // Should only find the direct children: innerCompound and box3 + expect(shapes.length).toBe(2); + + box1.delete(); + box2.delete(); + box3.delete(); + innerCompound.delete(); + outerCompound.delete(); + shapes.forEach(s => s.delete()); + }); + }); +}); + From 7e2f8f5a10ffaa47e1be8ad3906dafe3e213b2b9 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 11:01:22 +0200 Subject: [PATCH 14/38] enum service unit tests --- .../lib/services/base/enum.service.test.ts | 126 +++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/packages/dev/occt/lib/services/base/enum.service.test.ts b/packages/dev/occt/lib/services/base/enum.service.test.ts index d58da12c..101406ac 100644 --- a/packages/dev/occt/lib/services/base/enum.service.test.ts +++ b/packages/dev/occt/lib/services/base/enum.service.test.ts @@ -1,14 +1,21 @@ import initOpenCascade, { OpenCascadeInstance } from "../../../bitbybit-dev-occt/bitbybit-dev-occt"; +import { OccHelper } from "../../occ-helper"; +import { VectorHelperService } from "../../api/vector-helper.service"; +import { ShapesHelperService } from "../../api/shapes-helper.service"; import { EnumService } from "./enum.service"; import * as Inputs from "../../api/inputs/inputs"; -describe("OCCT booleans unit tests", () => { +describe("OCCT enum service unit tests", () => { let occt: OpenCascadeInstance; let enumService: EnumService; + let occHelper: OccHelper; beforeAll(async () => { occt = await initOpenCascade(); enumService = new EnumService(occt); + const vec = new VectorHelperService(); + const s = new ShapesHelperService(); + occHelper = new OccHelper(vec, s, occt); }); it("should get gcc position unqualified", async () => { @@ -50,5 +57,122 @@ describe("OCCT booleans unit tests", () => { const res = enumService.convertFourSidesStrictEnumToTwoCircleInclusionEnum("whatever" as any); expect(res).toEqual(Inputs.OCCT.twoCircleInclusionEnum.none); }); + + describe("getShapeTypeEnum", () => { + it("should return vertex for a vertex shape", () => { + const vertex = occHelper.entitiesService.makeVertex([1, 2, 3]); + const res = enumService.getShapeTypeEnum(vertex); + expect(res).toEqual(Inputs.OCCT.shapeTypeEnum.vertex); + vertex.delete(); + }); + + it("should return edge for an edge shape", () => { + const edge = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [1, 0, 0] }); + const res = enumService.getShapeTypeEnum(edge); + expect(res).toEqual(Inputs.OCCT.shapeTypeEnum.edge); + edge.delete(); + }); + + it("should return wire for a wire shape", () => { + const wire = occHelper.wiresService.createPolygonWire({ points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] }); + const res = enumService.getShapeTypeEnum(wire); + expect(res).toEqual(Inputs.OCCT.shapeTypeEnum.wire); + wire.delete(); + }); + + it("should return face for a face shape", () => { + const face = occHelper.facesService.createSquareFace({ size: 1, center: [0, 0, 0], direction: [0, 1, 0] }); + const res = enumService.getShapeTypeEnum(face); + expect(res).toEqual(Inputs.OCCT.shapeTypeEnum.face); + face.delete(); + }); + + it("should return shell for a shell shape", () => { + // Create a shell by sewing two faces together + const face1 = occHelper.facesService.createSquareFace({ size: 1, center: [0, 0, 0], direction: [0, 0, 1] }); + const face2 = occHelper.facesService.createSquareFace({ size: 1, center: [0, 0.5, 0.5], direction: [0, 1, 0] }); + const shell = occHelper.shellsService.sewFaces({ shapes: [face1, face2], tolerance: 1e-7 }); + const res = enumService.getShapeTypeEnum(shell); + expect(res).toEqual(Inputs.OCCT.shapeTypeEnum.shell); + face1.delete(); + face2.delete(); + shell.delete(); + }); + + it("should return solid for a solid shape", () => { + const box = occHelper.solidsService.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const res = enumService.getShapeTypeEnum(box); + expect(res).toEqual(Inputs.OCCT.shapeTypeEnum.solid); + box.delete(); + }); + + it("should return compound for a compound shape", () => { + const box1 = occHelper.solidsService.createBox({ width: 1, height: 1, length: 1, center: [0, 0, 0] }); + const box2 = occHelper.solidsService.createBox({ width: 1, height: 1, length: 1, center: [5, 0, 0] }); + const compound = occHelper.converterService.makeCompound({ shapes: [box1, box2] }); + const res = enumService.getShapeTypeEnum(compound); + expect(res).toEqual(Inputs.OCCT.shapeTypeEnum.compound); + box1.delete(); + box2.delete(); + compound.delete(); + }); + }); + + describe("getGeomFillTrihedronEnumOCCTValue", () => { + it("should return GeomFill_IsConstantNormal for isConstantNormal", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isConstantNormal); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsConstantNormal); + }); + + it("should return GeomFill_IsCorrectedFrenet for isCorrectedFrenet", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isCorrectedFrenet); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsCorrectedFrenet); + }); + + it("should return GeomFill_IsDarboux for isDarboux", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isDarboux); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsDarboux); + }); + + it("should return GeomFill_IsDiscreteTrihedron for isDiscreteTrihedron", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isDiscreteTrihedron); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsDiscreteTrihedron); + }); + + it("should return GeomFill_IsFixed for isFixed", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isFixed); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsFixed); + }); + + it("should return GeomFill_IsFrenet for isFrenet", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isFrenet); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsFrenet); + }); + + it("should return GeomFill_IsGuideAC for isGuideAC", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isGuideAC); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsGuideAC); + }); + + it("should return GeomFill_IsGuideACWithContact for isGuideACWithContact", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isGuideACWithContact); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsGuideACWithContact); + }); + + it("should return GeomFill_IsGuidePlan for isGuidePlan", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isGuidePlan); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsGuidePlan); + }); + + it("should return GeomFill_IsGuidePlanWithContact for isGuidePlanWithContact", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue(Inputs.OCCT.geomFillTrihedronEnum.isGuidePlanWithContact); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsGuidePlanWithContact); + }); + + it("should return GeomFill_IsConstantNormal as default for unrecognized value", () => { + const res = enumService.getGeomFillTrihedronEnumOCCTValue("whatever" as any); + expect(res).toEqual(occt.GeomFill_Trihedron.GeomFill_IsConstantNormal); + }); + }); }); From 7cbf0673a10f5d58dabec7e0c93caae21dd743b3 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 11:25:32 +0200 Subject: [PATCH 15/38] wire unit tests --- .../dev/occt/lib/services/shapes/wire.test.ts | 575 +++++++++++++++++- 1 file changed, 558 insertions(+), 17 deletions(-) diff --git a/packages/dev/occt/lib/services/shapes/wire.test.ts b/packages/dev/occt/lib/services/shapes/wire.test.ts index 72a3aa52..403b638b 100644 --- a/packages/dev/occt/lib/services/shapes/wire.test.ts +++ b/packages/dev/occt/lib/services/shapes/wire.test.ts @@ -536,20 +536,20 @@ describe("OCCT wire unit tests", () => { // Create a closed polygon (rectangle) const points = [[0, 0, 0], [10, 0, 0], [10, 0, 5], [0, 0, 5]] as Inputs.Base.Point3[]; const w = wire.createPolygonWire({ points }); - + const startPt = wire.startPointOnWire({ shape: w }); const endPt = wire.endPointOnWire({ shape: w }); - + // For a closed polygon, start and end should be the same expect(startPt[0]).toBeCloseTo(endPt[0], 5); expect(startPt[1]).toBeCloseTo(endPt[1], 5); expect(startPt[2]).toBeCloseTo(endPt[2], 5); - + // Use reversedWireFromReversedEdges for closed wires to maintain start point const w2 = wire.reversedWireFromReversedEdges({ shape: w }); - + const startPtRev = wire.startPointOnWire({ shape: w2 }); - + // For a reversed closed polygon using reversedWireFromReversedEdges, // the start point should be the same as the original // User expectation: forward, left, back, right becomes left, forward, right, back @@ -557,7 +557,7 @@ describe("OCCT wire unit tests", () => { expect(startPtRev[0]).toBeCloseTo(startPt[0], 5); expect(startPtRev[1]).toBeCloseTo(startPt[1], 5); expect(startPtRev[2]).toBeCloseTo(startPt[2], 5); - + w.delete(); w2.delete(); }); @@ -566,22 +566,22 @@ describe("OCCT wire unit tests", () => { // Create a closed polygon (rectangle) const points = [[0, 0, 0], [10, 0, 0], [10, 0, 5], [0, 0, 5]] as Inputs.Base.Point3[]; const w = wire.createPolygonWire({ points }); - + const allEdges = edge.getEdgesAlongWire({ shape: w }); const firstEdgeStart = edge.startPointOnEdge({ shape: allEdges[0] }); - + // Use reversedWireFromReversedEdges for closed wires const w2 = wire.reversedWireFromReversedEdges({ shape: w }); - + const allEdgesRev = edge.getEdgesAlongWire({ shape: w2 }); const firstEdgeRevStart = edge.startPointOnEdge({ shape: allEdgesRev[0] }); - + // For a reversed wire using reversedWireFromReversedEdges, // the first edge's start should be the original first edge's start expect(firstEdgeRevStart[0]).toBeCloseTo(firstEdgeStart[0], 5); expect(firstEdgeRevStart[1]).toBeCloseTo(firstEdgeStart[1], 5); expect(firstEdgeRevStart[2]).toBeCloseTo(firstEdgeStart[2], 5); - + allEdges.forEach(e => e.delete()); allEdgesRev.forEach(e => e.delete()); w.delete(); @@ -591,19 +591,19 @@ describe("OCCT wire unit tests", () => { it("should reverse closed rectangle wire and maintain start point using reversedWireFromReversedEdges", async () => { // Create a closed rectangle wire const w = wire.createRectangleWire({ width: 10, length: 5, center: [5, 0, 2.5], direction: [0, 1, 0] }); - + const startPt = wire.startPointOnWire({ shape: w }); - + // Use reversedWireFromReversedEdges for closed wires const w2 = wire.reversedWireFromReversedEdges({ shape: w }); - + const startPtRev = wire.startPointOnWire({ shape: w2 }); - + // The reversed wire should have the same start point as the original expect(startPtRev[0]).toBeCloseTo(startPt[0], 5); expect(startPtRev[1]).toBeCloseTo(startPt[1], 5); expect(startPtRev[2]).toBeCloseTo(startPt[2], 5); - + w.delete(); w2.delete(); }); @@ -1433,7 +1433,7 @@ describe("OCCT wire unit tests", () => { const arcWire = wire.createWireFromEdge({ shape: arc }); const line2 = wire.createLineWire({ start: [2, 0, 1], end: [0, 0, 1] }); const combinedWire = wire.combineEdgesAndWiresIntoAWire({ shapes: [line1, arcWire, line2] }); - + const pts = wire.divideWireByEqualDistanceToPoints({ shape: combinedWire, removeEndPoint: true, @@ -2089,4 +2089,545 @@ describe("OCCT wire unit tests", () => { expect(length).toEqual(lengthExp); w.delete(); }; + + describe("fromBaseLine", () => { + it("should create wire fromBaseLine with basic line", () => { + const line: Inputs.Base.Line3 = { start: [0, 0, 0], end: [3, 0, 0] }; + const w = wire.fromBaseLine({ line }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(3); + w.delete(); + }); + + it("should create wire fromBaseLine with diagonal line", () => { + const line: Inputs.Base.Line3 = { start: [0, 0, 0], end: [3, 4, 0] }; + const w = wire.fromBaseLine({ line }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(5); // 3-4-5 triangle + w.delete(); + }); + + it("should create wire fromBaseLine with 3D diagonal line", () => { + const line: Inputs.Base.Line3 = { start: [1, 2, 3], end: [4, 6, 3] }; + const w = wire.fromBaseLine({ line }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(5); // sqrt(9 + 16 + 0) = 5 + w.delete(); + }); + }); + + describe("fromBaseLines", () => { + it("should create wires fromBaseLines with multiple lines", () => { + const lines: Inputs.Base.Line3[] = [ + { start: [0, 0, 0], end: [1, 0, 0] }, + { start: [0, 0, 0], end: [0, 2, 0] }, + { start: [0, 0, 0], end: [0, 0, 3] }, + ]; + const wires = wire.fromBaseLines({ lines }); + expect(wires.length).toBe(3); + const lengths = wires.map(w => wire.getWireLength({ shape: w })); + expect(lengths[0]).toBe(1); + expect(lengths[1]).toBe(2); + expect(lengths[2]).toBe(3); + wires.forEach(w => w.delete()); + }); + + it("should create empty array fromBaseLines with empty input", () => { + const wires = wire.fromBaseLines({ lines: [] }); + expect(wires.length).toBe(0); + }); + }); + + describe("fromBaseSegment", () => { + it("should create wire fromBaseSegment with basic segment", () => { + const segment: Inputs.Base.Segment3 = [[0, 0, 0], [5, 0, 0]]; + const w = wire.fromBaseSegment({ segment }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(5); + w.delete(); + }); + + it("should create wire fromBaseSegment with 3D segment", () => { + const segment: Inputs.Base.Segment3 = [[1, 1, 1], [4, 5, 1]]; + const w = wire.fromBaseSegment({ segment }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(5); // sqrt(9 + 16 + 0) = 5 + w.delete(); + }); + }); + + describe("fromBaseSegments", () => { + it("should create wires fromBaseSegments with multiple segments", () => { + const segments: Inputs.Base.Segment3[] = [ + [[0, 0, 0], [2, 0, 0]], + [[0, 0, 0], [0, 3, 0]], + [[0, 0, 0], [0, 0, 4]], + ]; + const wires = wire.fromBaseSegments({ segments }); + expect(wires.length).toBe(3); + const lengths = wires.map(w => wire.getWireLength({ shape: w })); + expect(lengths[0]).toBe(2); + expect(lengths[1]).toBe(3); + expect(lengths[2]).toBe(4); + wires.forEach(w => w.delete()); + }); + + it("should create empty array fromBaseSegments with empty input", () => { + const wires = wire.fromBaseSegments({ segments: [] }); + expect(wires.length).toBe(0); + }); + }); + + describe("fromPoints", () => { + it("should create polyline wire fromPoints for non-closed points", () => { + const points: Inputs.Base.Point3[] = [ + [0, 0, 0], + [1, 0, 0], + [1, 1, 0], + ]; + const w = wire.fromPoints({ points }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(2); // 1 + 1 = 2 + w.delete(); + }); + + it("should return undefined fromPoints with only one point", () => { + const points: Inputs.Base.Point3[] = [ + [0, 0, 0], + ]; + const w = wire.fromPoints({ points }); + expect(w).toBeUndefined(); + }); + + it("should create square-like polyline fromPoints", () => { + const points: Inputs.Base.Point3[] = [ + [0, 0, 0], + [2, 0, 0], + [2, 2, 0], + [0, 2, 0], + ]; + const w = wire.fromPoints({ points }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(6); // 2 + 2 + 2 = 6 (open polyline, 3 segments) + w.delete(); + }); + }); + + describe("fromBasePolyline", () => { + it("should create open polyline wire fromBasePolyline", () => { + const polyline: Inputs.Base.Polyline3 = { + points: [ + [0, 0, 0], + [3, 0, 0], + [3, 4, 0], + ], + isClosed: false + }; + const w = wire.fromBasePolyline({ polyline }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(7); // 3 + 4 = 7 + w.delete(); + }); + + it("should create closed polygon wire fromBasePolyline", () => { + const polyline: Inputs.Base.Polyline3 = { + points: [ + [0, 0, 0], + [3, 0, 0], + [3, 4, 0], + ], + isClosed: true + }; + const w = wire.fromBasePolyline({ polyline }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(12); // 3 + 4 + 5 = 12 (closed triangle) + w.delete(); + }); + + it("should create closed square wire fromBasePolyline", () => { + const polyline: Inputs.Base.Polyline3 = { + points: [ + [0, 0, 0], + [2, 0, 0], + [2, 2, 0], + [0, 2, 0], + ], + isClosed: true + }; + const w = wire.fromBasePolyline({ polyline }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(8); // 2 + 2 + 2 + 2 = 8 + w.delete(); + }); + }); + + describe("fromBaseTriangle", () => { + it("should create triangle wire fromBaseTriangle", () => { + const triangle: Inputs.Base.Triangle3 = [ + [0, 0, 0], + [3, 0, 0], + [0, 4, 0], + ]; + const w = wire.fromBaseTriangle({ triangle }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(12); // 3 + 4 + 5 = 12 + w.delete(); + }); + + it("should create equilateral triangle wire fromBaseTriangle", () => { + const s = 2; + const h = s * Math.sqrt(3) / 2; + const triangle: Inputs.Base.Triangle3 = [ + [0, 0, 0], + [s, 0, 0], + [s / 2, 0, h], + ]; + const w = wire.fromBaseTriangle({ triangle }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(6, 10); // 3 sides of length 2 + w.delete(); + }); + }); + + describe("fromBaseMesh", () => { + it("should create triangle wires fromBaseMesh", () => { + const mesh: Inputs.Base.Mesh3 = [ + [[0, 0, 0], [3, 0, 0], [0, 4, 0]], // 3-4-5 triangle + [[0, 0, 0], [1, 0, 0], [0, 1, 0]], // 1-1-sqrt(2) triangle + ]; + const wires = wire.fromBaseMesh({ mesh }); + expect(wires.length).toBe(2); + const lengths = wires.map(w => wire.getWireLength({ shape: w })); + expect(lengths[0]).toBe(12); // 3 + 4 + 5 + expect(lengths[1]).toBeCloseTo(2 + Math.sqrt(2), 10); // 1 + 1 + sqrt(2) + wires.forEach(w => w.delete()); + }); + + it("should create empty array fromBaseMesh with empty mesh", () => { + const wires = wire.fromBaseMesh({ mesh: [] }); + expect(wires.length).toBe(0); + }); + }); + + describe("createLineWireWithExtensions", () => { + it("should create line wire with extensions at both ends", () => { + const w = wire.createLineWireWithExtensions({ + start: [0, 0, 0], + end: [0, 1, 0], + extensionStart: 0.5, + extensionEnd: 0.5 + }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(2); // 1 + 0.5 + 0.5 = 2 + w.delete(); + }); + + it("should create line wire with extension at start only", () => { + const w = wire.createLineWireWithExtensions({ + start: [0, 0, 0], + end: [0, 2, 0], + extensionStart: 1, + extensionEnd: 0 + }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(3); // 2 + 1 + 0 = 3 + w.delete(); + }); + + it("should create line wire with extension at end only", () => { + const w = wire.createLineWireWithExtensions({ + start: [0, 0, 0], + end: [5, 0, 0], + extensionStart: 0, + extensionEnd: 2 + }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(7); // 5 + 0 + 2 = 7 + w.delete(); + }); + + it("should create line wire with negative extensions (shortening)", () => { + const w = wire.createLineWireWithExtensions({ + start: [0, 0, 0], + end: [0, 10, 0], + extensionStart: -1, + extensionEnd: -1 + }); + const length = wire.getWireLength({ shape: w }); + expect(length).toBe(8); // 10 - 1 - 1 = 8 + w.delete(); + }); + }); + + describe("hexagonsInGrid", () => { + it("should create hexagons in a grid", () => { + const wires = wire.hexagonsInGrid({ + width: 5, + height: 5, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2, + }); + expect(wires.length).toBe(4); // 2x2 grid + // Each hexagon is closed and has 6 sides + const allClosed = wires.every(w => occHelper.wiresService.isWireClosed({ shape: w })); + expect(allClosed).toBe(true); + wires.forEach(w => { + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(7.772757295452931); + }); + wires.forEach(w => w.delete()); + }); + + it("should create hexagons in a grid with flat top", () => { + const wires = wire.hexagonsInGrid({ + width: 6, + height: 6, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + flatTop: true, + }); + expect(wires.length).toBe(9); // 3x3 grid + wires.forEach(w => { + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(6.58510478253727); + }); + wires.forEach(w => w.delete()); + }); + + it("should create single hexagon", () => { + const wires = wire.hexagonsInGrid({ + width: 2, + height: 2, + nrHexagonsInWidth: 1, + nrHexagonsInHeight: 1, + }); + + expect(wires.length).toBe(1); + const length = wire.getWireLength({ shape: wires[0] }); + expect(length).toBeCloseTo(6.472135954999578); + const isClosed = occHelper.wiresService.isWireClosed({ shape: wires[0] }); + expect(isClosed).toBe(true); + wires.forEach(w => w.delete()); + }); + }); + + describe("midPointOnWire", () => { + it("should get midpoint on a straight line wire", () => { + const w = wire.createLineWire({ start: [0, 0, 0], end: [10, 0, 0] }); + const midPt = wire.midPointOnWire({ shape: w }); + expect(midPt[0]).toBeCloseTo(5, 10); + expect(midPt[1]).toBeCloseTo(0, 10); + expect(midPt[2]).toBeCloseTo(0, 10); + w.delete(); + }); + + it("should get midpoint on a 3D line wire", () => { + const w = wire.createLineWire({ start: [0, 0, 0], end: [4, 6, 8] }); + const midPt = wire.midPointOnWire({ shape: w }); + expect(midPt[0]).toBeCloseTo(2, 10); + expect(midPt[1]).toBeCloseTo(3, 10); + expect(midPt[2]).toBeCloseTo(4, 10); + w.delete(); + }); + + it("should get midpoint on a polyline wire", () => { + // Create L-shaped polyline: (0,0,0) -> (4,0,0) -> (4,4,0) + // Total length = 8, midpoint at length 4 should be at (4, 0, 0) + const w = wire.createPolylineWire({ + points: [[0, 0, 0], [4, 0, 0], [4, 4, 0]] + }); + const midPt = wire.midPointOnWire({ shape: w }); + expect(midPt[0]).toBeCloseTo(4, 10); + expect(midPt[1]).toBeCloseTo(0, 10); + expect(midPt[2]).toBeCloseTo(0, 10); + w.delete(); + }); + }); + + describe("textWires", () => { + it("should create text wires for simple text", () => { + const dto = new Inputs.OCCT.TextWiresDto("A", 0, 0, 1); + const wires = wire.textWires(dto); + expect(wires.length).toBe(3); + // Letter "A" should produce wires + wires.forEach((w: TopoDS_Wire) => w.delete()); + }); + + it("should create text wires for multiple characters", () => { + const dto = new Inputs.OCCT.TextWiresDto("Hi", 0, 0, 1); + const wires = wire.textWires(dto); + expect(wires.length).toBe(5); // Multiple characters produce multiple wires + wires.forEach((w: TopoDS_Wire) => w.delete()); + }); + + it("should create text wires with custom height", () => { + const dto1 = new Inputs.OCCT.TextWiresDto("X", 0, 0, 1); + const dto2 = new Inputs.OCCT.TextWiresDto("X", 0, 0, 2); + const wires1 = wire.textWires(dto1); + const wires2 = wire.textWires(dto2); + + // With height doubled, wire lengths should roughly double + const totalLength1 = wires1.reduce((sum: number, w: TopoDS_Wire) => sum + wire.getWireLength({ shape: w }), 0); + const totalLength2 = wires2.reduce((sum: number, w: TopoDS_Wire) => sum + wire.getWireLength({ shape: w }), 0); + + expect(totalLength2).toBeGreaterThan(totalLength1); + wires1.forEach((w: TopoDS_Wire) => w.delete()); + wires2.forEach((w: TopoDS_Wire) => w.delete()); + }); + }); + + describe("textWiresWithData", () => { + it("should create text wires with data for simple text", () => { + const dto = new Inputs.OCCT.TextWiresDto("A", 0, 0, 1); + const result = wire.textWiresWithData(dto); + expect(result).toBeDefined(); + expect(result.data).toBeDefined(); + expect(result.compound).toBeDefined(); + result.compound.delete(); + }); + + it("should create text wires with data containing correct structure", () => { + const dto = new Inputs.OCCT.TextWiresDto("AB", 0, 0, 1); + const result = wire.textWiresWithData(dto); + expect(result.data).toBeDefined(); + expect(result.data.width).toBeGreaterThan(0); + expect(result.data.height).toBeGreaterThan(0); + // Data should contain information about the text layout + result.compound.delete(); + }); + }); + + describe("createIBeamProfileWire", () => { + it("should create I-beam profile wire with default values", () => { + const dto = new Inputs.OCCT.IBeamProfileDto(2, 3, 0.2, 0.3); + const w = wire.createIBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + // I-beam profile perimeter: complex calculation based on geometry + // Outer perimeter: 2*width + 2*height = 2*2 + 2*3 = 10 + // Inner cuts: depends on web and flange thickness + // Expected value will need to be determined empirically + expect(length).toBeCloseTo(13.6); + const isClosed = occHelper.wiresService.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + + it("should create I-beam profile wire with custom dimensions", () => { + const dto = new Inputs.OCCT.IBeamProfileDto(4, 6, 0.4, 0.6); + const w = wire.createIBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + // For I-beam: perimeter includes outer edges and inner edges + // Total should be > simple rectangle perimeter + expect(length).toBeCloseTo(27.2); // Should be more than rectangle + w.delete(); + }); + }); + + describe("createHBeamProfileWire", () => { + it("should create H-beam profile wire with default values", () => { + const dto = new Inputs.OCCT.HBeamProfileDto(2, 3, 0.2, 0.3); + const w = wire.createHBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(15.6); + const isClosed = occHelper.wiresService.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + + it("should create H-beam profile wire with custom dimensions", () => { + const dto = new Inputs.OCCT.HBeamProfileDto(3, 4, 0.3, 0.4); + const w = wire.createHBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(21.4); + w.delete(); + }); + }); + + describe("createTBeamProfileWire", () => { + it("should create T-beam profile wire with default values", () => { + const dto = new Inputs.OCCT.TBeamProfileDto(2, 2, 0.2, 0.3); + const w = wire.createTBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(8); + const isClosed = occHelper.wiresService.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + + it("should create T-beam profile wire with custom dimensions", () => { + const dto = new Inputs.OCCT.TBeamProfileDto(3, 3, 0.3, 0.5); + const w = wire.createTBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(12); + w.delete(); + }); + }); + + describe("createUBeamProfileWire", () => { + it("should create U-beam profile wire with default values", () => { + const dto = new Inputs.OCCT.UBeamProfileDto(2, 3, 0.2, 0.3, 0.5); + const w = wire.createUBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(15); + const isClosed = occHelper.wiresService.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + + it("should create U-beam profile wire with custom dimensions", () => { + const dto = new Inputs.OCCT.UBeamProfileDto(4, 5, 0.4, 0.5, 1); + const w = wire.createUBeamProfileWire(dto); + const length = wire.getWireLength({ shape: w }); + expect(length).toBeCloseTo(26); + w.delete(); + }); + }); + + describe("isWireClosed", () => { + it("should return true for closed circle wire", () => { + const w = wire.createCircleWire({ radius: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const isClosed = wire.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + + it("should return true for closed polygon wire", () => { + const w = wire.createPolygonWire({ + points: [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]] + }); + const isClosed = wire.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + + it("should return false for open line wire", () => { + const w = wire.createLineWire({ start: [0, 0, 0], end: [1, 0, 0] }); + const isClosed = wire.isWireClosed({ shape: w }); + expect(isClosed).toBe(false); + w.delete(); + }); + + it("should return false for open polyline wire", () => { + const w = wire.createPolylineWire({ + points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] + }); + const isClosed = wire.isWireClosed({ shape: w }); + expect(isClosed).toBe(false); + w.delete(); + }); + + it("should return true for closed square wire", () => { + const w = wire.createSquareWire({ size: 2, center: [0, 0, 0], direction: [0, 1, 0] }); + const isClosed = wire.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + + it("should return true for closed rectangle wire", () => { + const w = wire.createRectangleWire({ width: 3, length: 4, center: [0, 0, 0], direction: [0, 1, 0] }); + const isClosed = wire.isWireClosed({ shape: w }); + expect(isClosed).toBe(true); + w.delete(); + }); + }); }); From 78a7186094e968185e01a379785cd3ff2e03a45d Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 11:33:32 +0200 Subject: [PATCH 16/38] unit test io with fromRightHanded STEP and fillets with defined solution value --- .../dev/occt/lib/services/fillets.test.ts | 8 +++ packages/dev/occt/lib/services/io.test.ts | 54 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/packages/dev/occt/lib/services/fillets.test.ts b/packages/dev/occt/lib/services/fillets.test.ts index b855d6e0..0357e431 100644 --- a/packages/dev/occt/lib/services/fillets.test.ts +++ b/packages/dev/occt/lib/services/fillets.test.ts @@ -769,4 +769,12 @@ describe("OCCT fillets unit tests", () => { const wireLength = occHelper.wiresService.getWireLength({ shape: filletRes }); expect(wireLength).toBeCloseTo(1.3424777993634651); }); + + it("should fillet two edges into a wire with explicit solution parameter", () => { + const edge1 = edge.line({ start: [0, 0, 0], end: [1, 0, 0] }); + const edge2 = edge.line({ start: [1, 0, 0], end: [1, 1, 0] }); + const filletRes = fillets.filletTwoEdgesInPlaneIntoAWire({ edge1, edge2, radius: 0.2, planeDirection: [0, 0, 1], planeOrigin: [1, 0, 0], solution: 0 }); + const wireLength = occHelper.wiresService.getWireLength({ shape: filletRes }); + expect(wireLength).toBeCloseTo(1.9141592620724523); + }); }); diff --git a/packages/dev/occt/lib/services/io.test.ts b/packages/dev/occt/lib/services/io.test.ts index 272b43bd..8b017728 100644 --- a/packages/dev/occt/lib/services/io.test.ts +++ b/packages/dev/occt/lib/services/io.test.ts @@ -64,6 +64,60 @@ describe("OCCT io unit tests", () => { cone.delete(); }); + it("should save shape as step file with adjustYtoZ and fromRightHanded (no mirroring)", () => { + const box = solid.createBox({ width: 4, length: 6, height: 8, center: [0, 0, 0] }); + + // Save with fromRightHanded=true (skips mirroring) + const stepRightHanded = io.saveShapeSTEP({ + shape: box, + adjustYtoZ: true, + fromRightHanded: true, + fileName: "box.step" + }); + + // Save with fromRightHanded=false (default, applies mirroring) + const stepLeftHanded = io.saveShapeSTEP({ + shape: box, + adjustYtoZ: true, + fromRightHanded: false, + fileName: "box.step" + }); + + // Both should be valid STEP files + expect(stepRightHanded).toContain("ISO-10303-21;"); + expect(stepRightHanded).toContain("END-ISO-10303-21;"); + expect(stepLeftHanded).toContain("ISO-10303-21;"); + expect(stepLeftHanded).toContain("END-ISO-10303-21;"); + + // The content should differ because mirroring changes the geometry + expect(stepRightHanded).not.toEqual(stepLeftHanded); + + box.delete(); + }); + + it("should save shape as step file with fromRightHanded true and preserve roundtrip", () => { + const cylinder = solid.createCylinder({ radius: 5, height: 10, direction: [0, 1, 0], center: [0, 0, 0] }); + + const stepText = io.saveShapeSTEP({ + shape: cylinder, + adjustYtoZ: true, + fromRightHanded: true, + fileName: "cylinder.step" + }); + + // Load it back (adjustZtoY should reverse the rotation) + const loaded = io.loadSTEPorIGES({ filetext: stepText, fileName: "cylinder.step", adjustZtoY: true }); + + const volumeOriginal = solid.getSolidVolume({ shape: cylinder }); + const volumeLoaded = solid.getSolidVolume({ shape: loaded }); + + // Volume should be preserved + expect(volumeOriginal).toBeCloseTo(volumeLoaded); + + cylinder.delete(); + loaded.delete(); + }); + it("should load cube shape from step file", () => { const cube = solid.createCube({ size: 10, center: [0, 0, 0] }); const stepText = io.saveShapeSTEP({ shape: cube, adjustYtoZ: false, fileName: "cube.step" }); From 4d9c38f7788a990afa04ffb304a9751655f53e25 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 12:09:07 +0200 Subject: [PATCH 17/38] unit tests for base points including hex grids. --- .../dev/base/lib/api/services/point.test.ts | 1093 +++++++++++++++++ 1 file changed, 1093 insertions(+) diff --git a/packages/dev/base/lib/api/services/point.test.ts b/packages/dev/base/lib/api/services/point.test.ts index 091d686e..3184afbe 100644 --- a/packages/dev/base/lib/api/services/point.test.ts +++ b/packages/dev/base/lib/api/services/point.test.ts @@ -170,6 +170,116 @@ describe("Point unit tests", () => { }); }); + describe("stretchPointsDirFromCenter", () => { + it("should stretch a point along Z axis from origin by factor 2", () => { + const pts: Inputs.Base.Point3[] = [[0, 0, 1]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [0, 0, 1]; + const scale = 2; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + uh.expectPointsCloseTo(result, [[0, 0, 2]]); + }); + + it("should stretch multiple points along X axis from origin by factor 3", () => { + const pts: Inputs.Base.Point3[] = [[1, 0, 0], [2, 0, 0], [1, 1, 1]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 0, 0]; + const scale = 3; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + // X component gets scaled by 3, Y and Z remain unchanged + uh.expectPointsCloseTo(result, [[3, 0, 0], [6, 0, 0], [3, 1, 1]]); + }); + + it("should stretch points along Y axis from a non-origin center", () => { + const pts: Inputs.Base.Point3[] = [[0, 5, 0], [0, 10, 0]]; + const center: Inputs.Base.Point3 = [0, 5, 0]; + const direction: Inputs.Base.Vector3 = [0, 1, 0]; + const scale = 2; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + // Point [0, 5, 0] is at center, so it stays + // Point [0, 10, 0] is 5 units along Y from center, scaled by 2 -> 10 units -> [0, 15, 0] + uh.expectPointsCloseTo(result, [[0, 5, 0], [0, 15, 0]]); + }); + + it("should shrink points when scale is less than 1", () => { + const pts: Inputs.Base.Point3[] = [[4, 0, 0], [8, 0, 0]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 0, 0]; + const scale = 0.5; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + uh.expectPointsCloseTo(result, [[2, 0, 0], [4, 0, 0]]); + }); + + it("should flip points when scale is negative", () => { + const pts: Inputs.Base.Point3[] = [[2, 0, 0]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 0, 0]; + const scale = -1; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + uh.expectPointsCloseTo(result, [[-2, 0, 0]]); + }); + + it("should not change points when scale is 1", () => { + const pts: Inputs.Base.Point3[] = [[1, 2, 3], [4, 5, 6]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 0, 0]; + const scale = 1; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + uh.expectPointsCloseTo(result, [[1, 2, 3], [4, 5, 6]]); + }); + + it("should stretch along a diagonal direction", () => { + // Stretch along [1, 1, 0] normalized direction from origin + const pts: Inputs.Base.Point3[] = [[1, 1, 0]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 1, 0]; + const scale = 2; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + // The point [1, 1, 0] is along the direction [1, 1, 0], so stretching by 2 gives [2, 2, 0] + uh.expectPointsCloseTo(result, [[2, 2, 0]]); + }); + + it("should correctly stretch a point perpendicular to direction (no change in that component)", () => { + // Point [0, 5, 0] stretched along X axis should not change Y + const pts: Inputs.Base.Point3[] = [[0, 5, 0]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 0, 0]; + const scale = 10; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + // X component is 0, so scaling along X doesn't move the point + uh.expectPointsCloseTo(result, [[0, 5, 0]]); + }); + + it("should handle empty points array", () => { + const pts: Inputs.Base.Point3[] = []; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 0, 0]; + const scale = 2; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + expect(result).toEqual([]); + }); + + it("should stretch points in 3D along a 3D diagonal direction", () => { + const pts: Inputs.Base.Point3[] = [[1, 1, 1]]; + const center: Inputs.Base.Point3 = [0, 0, 0]; + const direction: Inputs.Base.Vector3 = [1, 1, 1]; + const scale = 3; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + // Point is exactly along [1,1,1] direction, scaled by 3 + uh.expectPointsCloseTo(result, [[3, 3, 3]]); + }); + + it("should stretch relative to a 3D center point", () => { + const pts: Inputs.Base.Point3[] = [[5, 5, 5]]; + const center: Inputs.Base.Point3 = [2, 2, 2]; + const direction: Inputs.Base.Vector3 = [1, 1, 1]; + const scale = 2; + const result = point.stretchPointsDirFromCenter({ points: pts, center, direction, scale }); + // Vector from center to point is [3, 3, 3], scaled by 2 -> [6, 6, 6], added to center -> [8, 8, 8] + uh.expectPointsCloseTo(result, [[8, 8, 8]]); + }); + }); + describe("boundingBoxOfPoints", () => { it("should calculate the correct bounding box for multiple points", () => { const points: Inputs.Base.Point3[] = [[1, 2, 3], [4, -1, 6], [0, 5, -2]]; @@ -851,4 +961,987 @@ describe("Point unit tests", () => { expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(expected, precision); }); }); + + describe("safestPointsMaxFilletHalfLine", () => { + const precision = 6; + + it("should return 0 for fewer than 3 points", () => { + const pts: Inputs.Base.Point3[] = [[0, 0, 0], [1, 1, 0]]; + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: false }); + expect(result).toBe(0); + }); + + it("should return 0 for a single point", () => { + const pts: Inputs.Base.Point3[] = [[0, 0, 0]]; + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: false }); + expect(result).toBe(0); + }); + + it("should return 0 for an empty array", () => { + const pts: Inputs.Base.Point3[] = []; + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: false }); + expect(result).toBe(0); + }); + + it("should calculate safest fillet for a simple triangle (90-degree corner)", () => { + // Right triangle at origin with legs along X and Y axes + const pts: Inputs.Base.Point3[] = [[4, 0, 0], [0, 0, 0], [0, 4, 0]]; + // There's only one internal corner at [0,0,0] which is 90 degrees + // Half-line constraint: min(4/2, 4/2) * tan(45deg) = 2 * 1 = 2 + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: false }); + expect(result).toBeCloseTo(2.0, precision); + }); + + it("should return minimum fillet radius among multiple corners", () => { + // L-shape: two 90-degree corners with different arm lengths + const pts: Inputs.Base.Point3[] = [ + [4, 0, 0], // Start + [0, 0, 0], // Corner 1: 90 deg, arms 4 and 2 + [0, 2, 0], // Corner 2: 90 deg, arms 2 and 6 + [6, 2, 0] // End + ]; + // Corner 1 at [0,0,0]: arms are 4 (to start) and 2 (to next), half-line: min(2, 1) * 1 = 1 + // Corner 2 at [0,2,0]: arms are 2 (to prev) and 6 (to end), half-line: min(1, 3) * 1 = 1 + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: false }); + expect(result).toBeCloseTo(1.0, precision); + }); + + it("should handle closed polyline with checkLastWithFirst=true", () => { + // Square: 4 corners, all 90 degrees, side length 4 + const pts: Inputs.Base.Point3[] = [ + [0, 0, 0], + [4, 0, 0], + [4, 4, 0], + [0, 4, 0] + ]; + // Each corner has arms of length 4, half-line: min(2, 2) * tan(45deg) = 2 * 1 = 2 + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: true }); + expect(result).toBeCloseTo(2.0, precision); + }); + + it("should return smaller radius when one corner has shorter arms", () => { + // Rectangle with unequal sides + const pts: Inputs.Base.Point3[] = [ + [0, 0, 0], + [2, 0, 0], // Short side = 2 + [2, 6, 0], // Long side = 6 + [0, 6, 0] + ]; + // Corner at [2,0,0]: arms 2 and 6, half-line: min(1, 3) * 1 = 1 + // Corner at [2,6,0]: arms 6 and 2, half-line: min(3, 1) * 1 = 1 + // All corners constrained by the short side + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: true }); + expect(result).toBeCloseTo(1.0, precision); + }); + + it("should return 0 when one corner has collinear points", () => { + // Three collinear points (no actual corner) + const pts: Inputs.Base.Point3[] = [ + [0, 0, 0], + [2, 0, 0], + [4, 0, 0] + ]; + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: false }); + expect(result).toBeCloseTo(0, precision); + }); + + it("should handle equilateral triangle (60-degree angles)", () => { + // Equilateral triangle with side length 4 + const side = 4; + const h = side * Math.sqrt(3) / 2; + const pts: Inputs.Base.Point3[] = [ + [0, 0, 0], + [side, 0, 0], + [side / 2, h, 0] + ]; + // Internal angle at each vertex is 60 degrees + // tan(30deg) = 1/sqrt(3) ≈ 0.577 + // Half-line: min(side/2, side/2) * tan(30deg) = 2 * 0.577 ≈ 1.1547 + const expected = (side / 2) * Math.tan(Math.PI / 6); + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: true }); + expect(result).toBeCloseTo(expected, precision); + }); + + it("should handle regular hexagon", () => { + // Regular hexagon centered at origin with radius 2 + const radius = 2; + const pts: Inputs.Base.Point3[] = []; + for (let i = 0; i < 6; i++) { + const angle = (Math.PI / 3) * i; + pts.push([ + radius * Math.sin(angle), + radius * Math.cos(angle), + 0 + ]); + } + // Each internal angle is 120 degrees + // tan(60deg) = sqrt(3) ≈ 1.732 + // Each side length is equal to radius = 2 + // Half-line: (2/2) * sqrt(3) = sqrt(3) ≈ 1.732 + const expected = (radius / 2) * Math.tan(Math.PI / 3); + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: true }); + expect(result).toBeCloseTo(expected, precision); + }); + + it("should handle 3D polyline", () => { + // 3D L-shape + const pts: Inputs.Base.Point3[] = [ + [4, 0, 0], + [0, 0, 0], // Corner at origin, 90-deg + [0, 0, 4] + ]; + // Single corner at origin with arms of length 4 + // Half-line: min(2, 2) * tan(45deg) = 2 + const result = point.safestPointsMaxFilletHalfLine({ points: pts, checkLastWithFirst: false }); + expect(result).toBeCloseTo(2.0, precision); + }); + }); + + describe("hexGridScaledToFit", () => { + const precision = 6; + + it("should return empty result for invalid dimensions (zero width)", () => { + const result = point.hexGridScaledToFit({ + width: 0, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2 + }); + expect(result.centers).toEqual([]); + expect(result.hexagons).toEqual([]); + expect(result.shortestDistEdge).toBeUndefined(); + expect(result.longestDistEdge).toBeUndefined(); + expect(result.maxFilletRadius).toBeUndefined(); + }); + + it("should return empty result for invalid dimensions (zero height)", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 0, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2 + }); + expect(result.centers).toEqual([]); + expect(result.hexagons).toEqual([]); + }); + + it("should return empty result for invalid hexagon counts (zero)", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 0, + nrHexagonsInHeight: 2 + }); + expect(result.centers).toEqual([]); + expect(result.hexagons).toEqual([]); + }); + + it("should generate correct number of hexagons for 2x2 grid", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2 + }); + expect(result.centers).toHaveLength(4); + expect(result.hexagons).toHaveLength(4); + }); + + it("should generate correct number of hexagons for 3x4 grid", () => { + const result = point.hexGridScaledToFit({ + width: 20, + height: 15, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 4 + }); + expect(result.centers).toHaveLength(12); + expect(result.hexagons).toHaveLength(12); + }); + + it("should generate hexagons with exactly 6 vertices each", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2 + }); + result.hexagons.forEach(hex => { + expect(hex).toHaveLength(6); + }); + }); + + it("should scale hexagons to fit within specified dimensions", () => { + const width = 20; + const height = 15; + const result = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3 + }); + + // Check that all hexagon vertices are within bounds [0, width] x [0, height] + let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; + result.hexagons.forEach(hex => { + hex.forEach(vertex => { + if (vertex[0] < minX) minX = vertex[0]; + if (vertex[0] > maxX) maxX = vertex[0]; + if (vertex[1] < minY) minY = vertex[1]; + if (vertex[1] > maxY) maxY = vertex[1]; + }); + }); + + expect(minX).toBeCloseTo(0, precision); + expect(maxX).toBeCloseTo(width, precision); + expect(minY).toBeCloseTo(0, precision); + expect(maxY).toBeCloseTo(height, precision); + }); + + it("should place all points on Z=0 by default", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2 + }); + + result.centers.forEach(center => { + expect(center[2]).toBe(0); + }); + result.hexagons.forEach(hex => { + hex.forEach(vertex => { + expect(vertex[2]).toBe(0); + }); + }); + }); + + it("should center the grid when centerGrid=true", () => { + const width = 10; + const height = 10; + const result = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2, + centerGrid: true + }); + + // The grid should be centered around origin + let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; + result.hexagons.forEach(hex => { + hex.forEach(vertex => { + if (vertex[0] < minX) minX = vertex[0]; + if (vertex[0] > maxX) maxX = vertex[0]; + if (vertex[1] < minY) minY = vertex[1]; + if (vertex[1] > maxY) maxY = vertex[1]; + }); + }); + + expect(minX).toBeCloseTo(-width / 2, precision); + expect(maxX).toBeCloseTo(width / 2, precision); + expect(minY).toBeCloseTo(-height / 2, precision); + expect(maxY).toBeCloseTo(height / 2, precision); + }); + + it("should place points on ground (XZ plane) when pointsOnGround=true", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2, + pointsOnGround: true + }); + + // Y should be 0, Z should have the height values + result.centers.forEach(center => { + expect(center[1]).toBe(0); + }); + result.hexagons.forEach(hex => { + hex.forEach(vertex => { + expect(vertex[1]).toBe(0); + }); + }); + }); + + it("should return valid edge distances", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2 + }); + + expect(result.shortestDistEdge).toBeDefined(); + expect(result.longestDistEdge).toBeDefined(); + expect(result.shortestDistEdge).toBeCloseTo(2.457807219155035); + expect(result.longestDistEdge).toBeCloseTo(2.8571428571428568); + expect(result.longestDistEdge).toBeGreaterThanOrEqual(result.shortestDistEdge!); + }); + + it("should return valid maxFilletRadius", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2 + }); + + expect(result.maxFilletRadius).toBeDefined(); + expect(result.maxFilletRadius).toBeCloseTo(1.7204650534085244); + }); + + it("should generate flat-top hexagons when flatTop=true", () => { + const width = 10; + const height = 10; + const resultPointy = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2, + flatTop: false + }); + const resultFlat = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2, + flatTop: true + }); + + // Both should have same number of hexagons + expect(resultFlat.hexagons).toHaveLength(resultPointy.hexagons.length); + + // But the vertex positions should be different (rotated 90 degrees) + // Check that the first hexagon vertices are different + const flatFirst = resultFlat.hexagons[0]; + const pointyFirst = resultPointy.hexagons[0]; + + expect(resultFlat.hexagons).toEqual([ + [ + [0, 4.000000000000001, 0], + [1.4285714285714288, 6, 0], + [4.285714285714286, 6, 0], + [5.714285714285714, 4.000000000000001, 0], + [4.2857142857142865, 2.000000000000001, 0], + [1.4285714285714306, 2, 0] + ], + [ + [0, 8.000000000000002, 0], + [1.4285714285714293, 10, 0], + [4.285714285714286, 10, 0], + [5.714285714285714, 8.000000000000002, 0], + [4.2857142857142865, 6.000000000000002, 0], + [1.4285714285714306, 6, 0] + ], + [ + [4.285714285714286, 2.000000000000001, 0], + [5.714285714285714, 4.000000000000001, 0], + [8.571428571428571, 4.000000000000001, 0], + [10, 2.0000000000000004, 0], + [8.571428571428573, 8.881784197001252e-16, 0], + [5.714285714285716, 0, 0] + ], + [ + [4.285714285714286, 6, 0], + [5.714285714285714, 8.000000000000002, 0], + [8.571428571428571, 8.000000000000002, 0], + [10, 6.000000000000001, 0], + [8.571428571428573, 4.000000000000001, 0], + [5.714285714285716, 3.9999999999999996, 0] + ] + ]); + + expect(resultPointy.hexagons).toEqual([ + [ + [2.000000000000001, 5.714285714285714, 0], + [4.000000000000001, 4.285714285714286, 0], + [4.000000000000001, 1.4285714285714293, 0], + [2.000000000000001, 0, 0], + [1.2819751242557092e-15, 1.4285714285714273, 0], + [0, 4.285714285714284, 0] + ], + [ + [4.000000000000001, 10, 0], + [6, 8.571428571428571, 0], + [6, 5.714285714285714, 0], + [4.000000000000001, 4.285714285714286, 0], + [2.000000000000001, 5.7142857142857135, 0], + [1.9999999999999998, 8.57142857142857, 0] + ], + [ + [6, 5.714285714285714, 0], + [8.000000000000002, 4.285714285714286, 0], + [8.000000000000002, 1.4285714285714293, 0], + [6.000000000000001, 0, 0], + [4.000000000000001, 1.4285714285714273, 0], + [3.9999999999999996, 4.285714285714284, 0] + ], + [ + [8.000000000000002, 10, 0], + [10, 8.571428571428571, 0], + [10, 5.714285714285714, 0], + [8.000000000000002, 4.285714285714286, 0], + [6.000000000000002, 5.7142857142857135, 0], + [6, 8.57142857142857, 0] + ] + ]); + + // They should not be identical + let allSame = true; + for (let i = 0; i < 6; i++) { + if (Math.abs(flatFirst[i][0] - pointyFirst[i][0]) > 1e-6 || + Math.abs(flatFirst[i][1] - pointyFirst[i][1]) > 1e-6) { + allSame = false; + break; + } + } + expect(allSame).toBe(false); + }); + + it("should verify exact hexagon vertex positions for 1x1 grid (pointy-top)", () => { + // For a single hexagon grid, we can verify exact vertex positions + // Using radius = 1 at center [0, 0, 0] for reference + // getRegularHexagonVertices generates vertices using: + // x = cx + radius * sin(angle), y = cy + radius * cos(angle) + // Angles: 0, 60, 120, 180, 240, 300 degrees + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 1, + nrHexagonsInHeight: 1 + }); + + expect(result.hexagons).toHaveLength(1); + const hex = result.hexagons[0]; + expect(hex).toHaveLength(6); + + // The hexagon should have vertices at regular intervals + // Since it's scaled to fit, we check the structure rather than exact values + // All vertices should have Z = 0 + hex.forEach(v => { + expect(v[2]).toBe(0); + }); + + // The center of the hexagon vertices should match the center + const center = result.centers[0]; + let avgX = 0, avgY = 0; + hex.forEach(v => { + avgX += v[0]; + avgY += v[1]; + }); + avgX /= 6; + avgY /= 6; + + expect(avgX).toBeCloseTo(center[0], precision); + expect(avgY).toBeCloseTo(center[1], precision); + }); + + it("should generate hexagons where vertices form valid convex polygon angles", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 1, + nrHexagonsInHeight: 1 + }); + + const hex = result.hexagons[0]; + + // For each vertex, calculate the angle formed by the two adjacent edges + // Due to non-uniform scaling, angles won't be exactly 120 degrees + // but should be valid convex polygon angles (between 60 and 180 degrees) + for (let i = 0; i < 6; i++) { + const prev = hex[(i + 5) % 6]; + const curr = hex[i]; + const next = hex[(i + 1) % 6]; + + // Vectors from current to prev and next + const v1: Inputs.Base.Vector3 = [prev[0] - curr[0], prev[1] - curr[1], 0]; + const v2: Inputs.Base.Vector3 = [next[0] - curr[0], next[1] - curr[1], 0]; + + // Dot product + const dot = v1[0] * v2[0] + v1[1] * v2[1]; + const len1 = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1]); + const len2 = Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1]); + + const cosAngle = dot / (len1 * len2); + const angleDeg = Math.acos(cosAngle) * 180 / Math.PI; + + // Internal angle should be a valid convex angle (between 60 and 180) + expect(angleDeg).toBeGreaterThan(60); + expect(angleDeg).toBeLessThan(180); + } + }); + + describe("extend options", () => { + const width = 20; + const height = 20; + + it("should extend grid beyond top boundary when extendTop=true", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendTop: false + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendTop: true + }); + + // Both should have same number of hexagons + expect(resultExtended.hexagons).toHaveLength(resultNormal.hexagons.length); + + // Find max Y in both grids + let maxYNormal = -Infinity; + let maxYExtended = -Infinity; + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { if (v[1] > maxYNormal) maxYNormal = v[1]; }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { if (v[1] > maxYExtended) maxYExtended = v[1]; }); + }); + + // Extended grid should have vertices beyond the normal max Y + expect(maxYExtended).toBeGreaterThan(maxYNormal); + }); + + it("should extend grid beyond bottom boundary when extendBottom=true", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendBottom: false + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendBottom: true + }); + + // Find min Y in both grids + let minYNormal = Infinity; + let minYExtended = Infinity; + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { if (v[1] < minYNormal) minYNormal = v[1]; }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { if (v[1] < minYExtended) minYExtended = v[1]; }); + }); + + // Extended grid should have vertices below the normal min Y + expect(minYExtended).toBeLessThan(minYNormal); + }); + + it("should extend grid beyond left boundary when extendLeft=true", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendLeft: false + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendLeft: true + }); + + // Find min X in both grids + let minXNormal = Infinity; + let minXExtended = Infinity; + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { if (v[0] < minXNormal) minXNormal = v[0]; }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { if (v[0] < minXExtended) minXExtended = v[0]; }); + }); + + // Extended grid should have vertices to the left of normal min X + expect(minXExtended).toBeLessThan(minXNormal); + }); + + it("should extend grid beyond right boundary when extendRight=true", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendRight: false + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendRight: true + }); + + // Find max X in both grids + let maxXNormal = -Infinity; + let maxXExtended = -Infinity; + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { if (v[0] > maxXNormal) maxXNormal = v[0]; }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { if (v[0] > maxXExtended) maxXExtended = v[0]; }); + }); + + // Extended grid should have vertices beyond normal max X + expect(maxXExtended).toBeGreaterThan(maxXNormal); + }); + + it("should extend grid in both vertical directions when extendTop=true and extendBottom=true", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3 + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendTop: true, + extendBottom: true + }); + + // Find Y bounds in both grids + let minYNormal = Infinity, maxYNormal = -Infinity; + let minYExtended = Infinity, maxYExtended = -Infinity; + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[1] < minYNormal) minYNormal = v[1]; + if (v[1] > maxYNormal) maxYNormal = v[1]; + }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[1] < minYExtended) minYExtended = v[1]; + if (v[1] > maxYExtended) maxYExtended = v[1]; + }); + }); + + // Extended grid should exceed both top and bottom boundaries + expect(maxYExtended).toBeGreaterThan(maxYNormal); + expect(minYExtended).toBeLessThan(minYNormal); + }); + + it("should extend grid in both horizontal directions when extendLeft=true and extendRight=true", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3 + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendLeft: true, + extendRight: true + }); + + // Find X bounds in both grids + let minXNormal = Infinity, maxXNormal = -Infinity; + let minXExtended = Infinity, maxXExtended = -Infinity; + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[0] < minXNormal) minXNormal = v[0]; + if (v[0] > maxXNormal) maxXNormal = v[0]; + }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[0] < minXExtended) minXExtended = v[0]; + if (v[0] > maxXExtended) maxXExtended = v[0]; + }); + }); + + // Extended grid should exceed both left and right boundaries + expect(maxXExtended).toBeGreaterThan(maxXNormal); + expect(minXExtended).toBeLessThan(minXNormal); + }); + + it("should extend grid in all four directions", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3 + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + extendTop: true, + extendBottom: true, + extendLeft: true, + extendRight: true + }); + + // Find all bounds + let minXNormal = Infinity, maxXNormal = -Infinity; + let minYNormal = Infinity, maxYNormal = -Infinity; + let minXExtended = Infinity, maxXExtended = -Infinity; + let minYExtended = Infinity, maxYExtended = -Infinity; + + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[0] < minXNormal) minXNormal = v[0]; + if (v[0] > maxXNormal) maxXNormal = v[0]; + if (v[1] < minYNormal) minYNormal = v[1]; + if (v[1] > maxYNormal) maxYNormal = v[1]; + }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[0] < minXExtended) minXExtended = v[0]; + if (v[0] > maxXExtended) maxXExtended = v[0]; + if (v[1] < minYExtended) minYExtended = v[1]; + if (v[1] > maxYExtended) maxYExtended = v[1]; + }); + }); + + // Extended grid should exceed all boundaries + expect(maxXExtended).toBeGreaterThan(maxXNormal); + expect(minXExtended).toBeLessThan(minXNormal); + expect(maxYExtended).toBeGreaterThan(maxYNormal); + expect(minYExtended).toBeLessThan(minYNormal); + }); + + it("should maintain hexagon count when extending", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 4, + nrHexagonsInHeight: 4 + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 4, + nrHexagonsInHeight: 4, + extendTop: true, + extendBottom: true, + extendLeft: true, + extendRight: true + }); + + expect(resultExtended.hexagons).toHaveLength(resultNormal.hexagons.length); + expect(resultExtended.centers).toHaveLength(resultNormal.centers.length); + }); + + it("should correctly extend with flatTop=true", () => { + const resultNormal = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + flatTop: true + }); + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 3, + nrHexagonsInHeight: 3, + flatTop: true, + extendTop: true, + extendRight: true + }); + + // Find bounds + let maxXNormal = -Infinity, maxYNormal = -Infinity; + let maxXExtended = -Infinity, maxYExtended = -Infinity; + resultNormal.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[0] > maxXNormal) maxXNormal = v[0]; + if (v[1] > maxYNormal) maxYNormal = v[1]; + }); + }); + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { + if (v[0] > maxXExtended) maxXExtended = v[0]; + if (v[1] > maxYExtended) maxYExtended = v[1]; + }); + }); + + // Extended should exceed normal bounds + expect(maxXExtended).toBeGreaterThan(maxXNormal); + expect(maxYExtended).toBeGreaterThan(maxYNormal); + }); + + it("should stretch hexagons proportionally when extending", () => { + const resultExtended = point.hexGridScaledToFit({ + width, + height, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2, + extendTop: true + }); + + // All hexagons should still have 6 vertices + resultExtended.hexagons.forEach(hex => { + expect(hex).toHaveLength(6); + }); + + // All vertices should still have Z=0 + resultExtended.hexagons.forEach(hex => { + hex.forEach(v => { + expect(v[2]).toBe(0); + }); + }); + }); + }); + }); + + describe("getRegularHexagonVertices (tested via hexGridScaledToFit)", () => { + const precision = 6; + + it("should generate 6 vertices in consistent winding order", () => { + // Use a 1x1 grid to get a single hexagon + const result = point.hexGridScaledToFit({ + width: 2, + height: 2, + nrHexagonsInWidth: 1, + nrHexagonsInHeight: 1 + }); + + const hex = result.hexagons[0]; + expect(hex).toHaveLength(6); + + // Verify consistent winding order using signed area (shoelace formula) + // A non-zero signed area confirms consistent winding direction + let signedArea = 0; + for (let i = 0; i < 6; i++) { + const curr = hex[i]; + const next = hex[(i + 1) % 6]; + // Shoelace formula: sum of (x_i * y_{i+1} - x_{i+1} * y_i) + signedArea += curr[0] * next[1] - next[0] * curr[1]; + } + signedArea /= 2; + + // Signed area should be non-zero and positive (clockwise in screen coords where Y increases downward, + // or counter-clockwise in standard math coords where Y increases upward) + // The important thing is it's consistent and non-zero + expect(Math.abs(signedArea)).toBeCloseTo(3); + }); + + it("should maintain Z coordinate from center point", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 2, + nrHexagonsInHeight: 2, + pointsOnGround: false + }); + + // All centers have Z=0, so all vertices should have Z=0 + result.hexagons.forEach((hex, idx) => { + const centerZ = result.centers[idx][2]; + hex.forEach(v => { + expect(v[2]).toBe(centerZ); + }); + }); + }); + + it("should generate vertices at equal distances from center", () => { + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 1, + nrHexagonsInHeight: 1 + }); + + const hex = result.hexagons[0]; + const center = result.centers[0]; + + // Calculate distance from center to each vertex + const distances = hex.map(v => { + const dx = v[0] - center[0]; + const dy = v[1] - center[1]; + return Math.sqrt(dx * dx + dy * dy); + }); + + expect(distances).toEqual([ + 5, + 5.590169943749474, + 5.590169943749474, + 5, + 5.590169943749473, + 5.590169943749474 + ]); + }); + + it("should generate edges of equal length for regular hexagon base", () => { + // For a 1x1 grid with equal width and height, the hexagon should have + // consistent edge patterns + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 1, + nrHexagonsInHeight: 1 + }); + + const hex = result.hexagons[0]; + const edgeLengths: number[] = []; + + for (let i = 0; i < 6; i++) { + const curr = hex[i]; + const next = hex[(i + 1) % 6]; + const dx = next[0] - curr[0]; + const dy = next[1] - curr[1]; + edgeLengths.push(Math.sqrt(dx * dx + dy * dy)); + } + + // Due to scaling, edges may have two different lengths (short and long edges) + // But the reported shortest and longest should match + const minEdge = Math.min(...edgeLengths); + const maxEdge = Math.max(...edgeLengths); + + expect(result.shortestDistEdge).toBeCloseTo(minEdge, precision); + expect(result.longestDistEdge).toBeCloseTo(maxEdge, precision); + }); + + it("should correctly position hexagon vertices relative to center using sin/cos pattern", () => { + // Create a simple 1x1 grid and verify the angular distribution + const result = point.hexGridScaledToFit({ + width: 10, + height: 10, + nrHexagonsInWidth: 1, + nrHexagonsInHeight: 1 + }); + + const hex = result.hexagons[0]; + const center = result.centers[0]; + + // Calculate angles from center to each vertex + const angles = hex.map(v => { + const dx = v[0] - center[0]; + const dy = v[1] - center[1]; + return Math.atan2(dx, dy) * 180 / Math.PI; // Note: atan2(x, y) for the formula used + }); + + // Sort angles to check spacing + const sortedAngles = [...angles].sort((a, b) => a - b); + + // The angles should be spaced roughly 60 degrees apart (allowing for scaling distortion) + for (let i = 0; i < 5; i++) { + const diff = sortedAngles[i + 1] - sortedAngles[i]; + expect(diff).toBeGreaterThan(30); // At least 30 degrees apart + expect(diff).toBeLessThan(90); // No more than 90 degrees apart + } + }); + }); }); From 82b8d1e5c2074b5b7adcf681dd9db0fb9e80b7b3 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 12:55:46 +0200 Subject: [PATCH 18/38] dxf AC1009 unit tests --- .../helpers/dxf/dxf-generator.test.ts | 407 ++++++++++++++++++ 1 file changed, 407 insertions(+) diff --git a/packages/dev/base/lib/api/services/helpers/dxf/dxf-generator.test.ts b/packages/dev/base/lib/api/services/helpers/dxf/dxf-generator.test.ts index 50c47f9c..70223990 100644 --- a/packages/dev/base/lib/api/services/helpers/dxf/dxf-generator.test.ts +++ b/packages/dev/base/lib/api/services/helpers/dxf/dxf-generator.test.ts @@ -503,6 +503,413 @@ describe("DxfGenerator unit tests", () => { }); }); + describe("AC1015 Format Branches", () => { + + describe("AC1015 Header Section", () => { + + it("should include $HANDSEED variable in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("$HANDSEED\n5\n20000"); + }); + + it("should include $HANDSEED with value 0 in AC1009", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + // AC1009 uses $HANDSEED with value 0 (not 20000 like AC1015) + expect(result).toContain("$HANDSEED\n5\n0"); + expect(result).not.toContain("$HANDSEED\n5\n20000"); + }); + + it("should not include $LASTSAVEDBY in AC1009", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("$LASTSAVEDBY"); + }); + }); + + describe("AC1015 Tables Section", () => { + + it("should not include VIEW table in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("0\nTABLE\n2\nVIEW"); + }); + + it("should not include UCS table in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("0\nTABLE\n2\nUCS"); + }); + + it("should not include APPID table in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("0\nTABLE\n2\nAPPID"); + }); + + it("should not include DIMSTYLE table in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("0\nTABLE\n2\nDIMSTYLE"); + }); + + it("should include VIEW, UCS, APPID, DIMSTYLE tables in AC1009", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + expect(result).toContain("0\nTABLE\n2\nVIEW"); + expect(result).toContain("0\nTABLE\n2\nUCS"); + expect(result).toContain("0\nTABLE\n2\nAPPID"); + expect(result).toContain("0\nTABLE\n2\nDIMSTYLE"); + }); + }); + + describe("AC1015 Line Type Table", () => { + + it("should include AcDbSymbolTable marker in LTYPE table for AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + // Extract LTYPE table section + const ltypeStart = result.indexOf("TABLE\n2\nLTYPE"); + const ltypeEnd = result.indexOf("ENDTAB", ltypeStart); + const ltypeSection = result.substring(ltypeStart, ltypeEnd); + + expect(ltypeSection).toContain("100\nAcDbSymbolTable"); + }); + + it("should include AcDbLinetypeTableRecord marker for line type entries in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("100\nAcDbSymbolTableRecord"); + expect(result).toContain("100\nAcDbLinetypeTableRecord"); + }); + + it("should not include AcDbSymbolTable markers in AC1009", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + // Extract LTYPE table section + const ltypeStart = result.indexOf("TABLE\n2\nLTYPE"); + const ltypeEnd = result.indexOf("ENDTAB", ltypeStart); + const ltypeSection = result.substring(ltypeStart, ltypeEnd); + + expect(ltypeSection).not.toContain("100\nAcDbSymbolTable"); + expect(ltypeSection).not.toContain("100\nAcDbLinetypeTableRecord"); + }); + }); + + describe("AC1015 Style Table", () => { + + it("should include AcDbSymbolTable marker in STYLE table for AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + // Extract STYLE table section + const styleStart = result.indexOf("TABLE\n2\nSTYLE"); + const styleEnd = result.indexOf("ENDTAB", styleStart); + const styleSection = result.substring(styleStart, styleEnd); + + expect(styleSection).toContain("100\nAcDbSymbolTable"); + }); + + it("should include AcDbTextStyleTableRecord marker in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("100\nAcDbTextStyleTableRecord"); + }); + + it("should not include AcDbTextStyleTableRecord in AC1009", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("0", "#000000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("100\nAcDbTextStyleTableRecord"); + }); + }); + + describe("AC1015 Layer Table", () => { + + it("should include AcDbSymbolTable marker in LAYER table for AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("TestLayer", "#FF0000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + // Extract LAYER table section + const layerStart = result.indexOf("TABLE\n2\nLAYER"); + const layerEnd = result.indexOf("ENDTAB", layerStart); + const layerSection = result.substring(layerStart, layerEnd); + + expect(layerSection).toContain("100\nAcDbSymbolTable"); + }); + + it("should include AcDbLayerTableRecord marker per layer in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("CustomLayer", "#00FF00", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("100\nAcDbLayerTableRecord"); + }); + + it("should include entity handles in LAYER table for AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("MyLayer", "#0000FF", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + // Extract LAYER table section + const layerStart = result.indexOf("TABLE\n2\nLAYER"); + const layerEnd = result.indexOf("ENDTAB", layerStart); + const layerSection = result.substring(layerStart, layerEnd); + + // Should have handle code "5" with hex value + expect(layerSection).toContain("5\n2"); + }); + + it("should not include AcDbLayerTableRecord in AC1009", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const path = new Inputs.IO.DxfPathDto([line]); + const part = new Inputs.IO.DxfPathsPartDto("TestLayer", "#FF0000", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("100\nAcDbLayerTableRecord"); + }); + + it("should generate unique handles for multiple layers in AC1015", () => { + const line1 = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const line2 = new Inputs.IO.DxfLineSegmentDto([20, 20], [30, 30]); + const path1 = new Inputs.IO.DxfPathDto([line1]); + const path2 = new Inputs.IO.DxfPathDto([line2]); + const part1 = new Inputs.IO.DxfPathsPartDto("Layer1", "#FF0000", [path1]); + const part2 = new Inputs.IO.DxfPathsPartDto("Layer2", "#00FF00", [path2]); + const model = new Inputs.IO.DxfModelDto([part1, part2], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + // Both layers should have AcDbLayerTableRecord + const matches = result.match(/100\nAcDbLayerTableRecord/g); + expect(matches?.length).toBeGreaterThanOrEqual(2); + }); + }); + + describe("AC1015 Entity-Specific Markers", () => { + + it("should include AcDbCircle marker for CIRCLE entities in AC1015", () => { + const circle = new Inputs.IO.DxfCircleSegmentDto([10, 10], 5); + const path = new Inputs.IO.DxfPathDto([circle]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("100\nAcDbEntity"); + expect(result).toContain("100\nAcDbCircle"); + }); + + it("should include AcDbPolyline marker for LWPOLYLINE entities in AC1015", () => { + const points: Inputs.Base.Point2[] = [[0, 0], [10, 0], [10, 10], [0, 10]]; + const polyline = new Inputs.IO.DxfPolylineSegmentDto(points, true); + const path = new Inputs.IO.DxfPathDto([polyline]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("100\nAcDbEntity"); + expect(result).toContain("100\nAcDbPolyline"); + }); + + it("should not include AcDbPolyline marker in AC1009", () => { + const points: Inputs.Base.Point2[] = [[0, 0], [10, 0], [10, 10], [0, 10]]; + const polyline = new Inputs.IO.DxfPolylineSegmentDto(points, true); + const path = new Inputs.IO.DxfPathDto([polyline]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("100\nAcDbPolyline"); + }); + + it("should include AcDbSpline marker for SPLINE entities in AC1015", () => { + const controlPoints: Inputs.Base.Point2[] = [[0, 0], [5, 10], [10, 10], [15, 0]]; + const spline = new Inputs.IO.DxfSplineSegmentDto(controlPoints, 3, false); + const path = new Inputs.IO.DxfPathDto([spline]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("100\nAcDbEntity"); + expect(result).toContain("100\nAcDbSpline"); + }); + + it("should not include AcDbSpline marker in AC1009", () => { + const controlPoints: Inputs.Base.Point2[] = [[0, 0], [5, 10], [10, 10], [15, 0]]; + const spline = new Inputs.IO.DxfSplineSegmentDto(controlPoints, 3, false); + const path = new Inputs.IO.DxfPathDto([spline]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + expect(result).not.toContain("100\nAcDbSpline"); + }); + + it("should include both AcDbCircle and AcDbArc markers for ARC entities in AC1015", () => { + const arc = new Inputs.IO.DxfArcSegmentDto([10, 10], 5, 0, 90); + const path = new Inputs.IO.DxfPathDto([arc]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + expect(result).toContain("100\nAcDbEntity"); + expect(result).toContain("100\nAcDbCircle"); + expect(result).toContain("100\nAcDbArc"); + }); + + it("should include entity handles for all entity types in AC1015", () => { + const line = new Inputs.IO.DxfLineSegmentDto([0, 0], [10, 10]); + const circle = new Inputs.IO.DxfCircleSegmentDto([20, 20], 5); + const arc = new Inputs.IO.DxfArcSegmentDto([30, 30], 5, 0, 90); + const polyline = new Inputs.IO.DxfPolylineSegmentDto([[40, 0], [50, 0], [50, 10]], false); + const spline = new Inputs.IO.DxfSplineSegmentDto([[60, 0], [65, 10], [70, 0]], 3, false); + + const path = new Inputs.IO.DxfPathDto([line, circle, arc, polyline, spline]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + // Extract ENTITIES section and count entity handles + const entitiesSection = result.split("SECTION\n2\nENTITIES")[1]?.split("ENDSEC")[0]; + expect(entitiesSection).toBeDefined(); + + const handleMatches = entitiesSection?.match(/5\n[A-F0-9]+/g); + expect(handleMatches).toBeDefined(); + expect(handleMatches?.length).toBeGreaterThanOrEqual(5); // At least 5 entities + }); + }); + + describe("AC1015 Arc Entity Z Coordinate", () => { + + it("should use 0.0 for arc Z coordinate in AC1015", () => { + const arc = new Inputs.IO.DxfArcSegmentDto([10, 10], 5, 0, 90); + const path = new Inputs.IO.DxfPathDto([arc]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1015"); + + const result = generator.generateDxf(model); + + // AC1015 uses "0.0" for Z coordinate + expect(result).toContain("30\n0.0"); + }); + + it("should use empty string for arc Z coordinate in AC1009", () => { + const arc = new Inputs.IO.DxfArcSegmentDto([10, 10], 5, 0, 90); + const path = new Inputs.IO.DxfPathDto([arc]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + // AC1009 uses empty string for Z coordinate ("30\n" followed by "\n40") + // This means after code 30, there's an empty line, then code 40 for radius + expect(result).toContain("30\n\n40"); + expect(result).not.toContain("30\n0.0"); + }); + + it("should include empty linetype field for arc in AC1009 only", () => { + const arc = new Inputs.IO.DxfArcSegmentDto([10, 10], 5, 0, 90); + const path = new Inputs.IO.DxfPathDto([arc]); + const part = new Inputs.IO.DxfPathsPartDto("0", "1", [path]); + const model = new Inputs.IO.DxfModelDto([part], "aci", "AC1009"); + + const result = generator.generateDxf(model); + + // AC1009 includes "6\n " (empty linetype) + const entitiesSection = result.split("SECTION\n2\nENTITIES")[1]?.split("ENDSEC")[0]; + expect(entitiesSection).toContain("6\n "); + }); + }); + }); + describe("Color Handling", () => { it("should apply ACI color to entities", () => { From 028e655948ac06434bd51857e7092aa08a0585b8 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 13:58:32 +0200 Subject: [PATCH 19/38] additional unit tests for base --- .../lib/api/services/geometry-helper.test.ts | 490 ++++++++++++++++++ .../dev/base/lib/api/services/line.test.ts | 81 +++ .../dev/base/lib/api/services/mesh.test.ts | 175 ++++++- 3 files changed, 732 insertions(+), 14 deletions(-) create mode 100644 packages/dev/base/lib/api/services/geometry-helper.test.ts diff --git a/packages/dev/base/lib/api/services/geometry-helper.test.ts b/packages/dev/base/lib/api/services/geometry-helper.test.ts new file mode 100644 index 00000000..dc90271c --- /dev/null +++ b/packages/dev/base/lib/api/services/geometry-helper.test.ts @@ -0,0 +1,490 @@ +import * as Inputs from "../inputs"; +import { GeometryHelper } from "./geometry-helper"; + +describe("GeometryHelper unit tests", () => { + let geometryHelper: GeometryHelper; + + beforeAll(() => { + geometryHelper = new GeometryHelper(); + }); + + describe("arePointsTheSame", () => { + describe("3D points (Point3)", () => { + it("should return true for identical 3D points", () => { + const pointA: Inputs.Base.Point3 = [1, 2, 3]; + const pointB: Inputs.Base.Point3 = [1, 2, 3]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(true); + }); + + it("should return true for 3D points within tolerance", () => { + const pointA: Inputs.Base.Point3 = [1, 2, 3]; + const pointB: Inputs.Base.Point3 = [1.0000001, 2.0000001, 3.0000001]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(true); + }); + + it("should return false for 3D points outside tolerance", () => { + const pointA: Inputs.Base.Point3 = [1, 2, 3]; + const pointB: Inputs.Base.Point3 = [1.001, 2, 3]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should return false when only X differs beyond tolerance", () => { + const pointA: Inputs.Base.Point3 = [0, 0, 0]; + const pointB: Inputs.Base.Point3 = [0.01, 0, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should return false when only Y differs beyond tolerance", () => { + const pointA: Inputs.Base.Point3 = [0, 0, 0]; + const pointB: Inputs.Base.Point3 = [0, 0.01, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should return false when only Z differs beyond tolerance", () => { + const pointA: Inputs.Base.Point3 = [0, 0, 0]; + const pointB: Inputs.Base.Point3 = [0, 0, 0.01]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should return true for 3D points at origin", () => { + const pointA: Inputs.Base.Point3 = [0, 0, 0]; + const pointB: Inputs.Base.Point3 = [0, 0, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(true); + }); + + it("should return true for negative 3D points within tolerance", () => { + const pointA: Inputs.Base.Point3 = [-5, -10, -15]; + const pointB: Inputs.Base.Point3 = [-5.0000001, -10.0000001, -15.0000001]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(true); + }); + + it("should return false for negative 3D points outside tolerance", () => { + const pointA: Inputs.Base.Point3 = [-5, -10, -15]; + const pointB: Inputs.Base.Point3 = [-5.1, -10, -15]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should handle very large 3D coordinates", () => { + const pointA: Inputs.Base.Point3 = [1e10, 1e10, 1e10]; + const pointB: Inputs.Base.Point3 = [1e10, 1e10, 1e10]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(true); + }); + + it("should handle very small 3D coordinates", () => { + const pointA: Inputs.Base.Point3 = [1e-10, 1e-10, 1e-10]; + const pointB: Inputs.Base.Point3 = [1e-10, 1e-10, 1e-10]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(true); + }); + + it("should return true when difference equals tolerance minus epsilon", () => { + const tolerance = 0.01; + const pointA: Inputs.Base.Point3 = [0, 0, 0]; + const pointB: Inputs.Base.Point3 = [0.009, 0, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, tolerance)).toBe(true); + }); + + it("should return false when difference equals tolerance", () => { + const tolerance = 0.01; + const pointA: Inputs.Base.Point3 = [0, 0, 0]; + const pointB: Inputs.Base.Point3 = [0.01, 0, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, tolerance)).toBe(false); + }); + }); + + describe("2D points (Point2)", () => { + it("should return true for identical 2D points", () => { + const pointA: Inputs.Base.Point2 = [1, 2]; + const pointB: Inputs.Base.Point2 = [1, 2]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(true); + }); + + it("should return true for 2D points within tolerance", () => { + const pointA: Inputs.Base.Point2 = [1, 2]; + const pointB: Inputs.Base.Point2 = [1.0000001, 2.0000001]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(true); + }); + + it("should return false for 2D points outside tolerance", () => { + const pointA: Inputs.Base.Point2 = [1, 2]; + const pointB: Inputs.Base.Point2 = [1.01, 2]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should return false when only X differs beyond tolerance in 2D", () => { + const pointA: Inputs.Base.Point2 = [0, 0]; + const pointB: Inputs.Base.Point2 = [0.01, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should return false when only Y differs beyond tolerance in 2D", () => { + const pointA: Inputs.Base.Point2 = [0, 0]; + const pointB: Inputs.Base.Point2 = [0, 0.01]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + + it("should return true for 2D points at origin", () => { + const pointA: Inputs.Base.Point2 = [0, 0]; + const pointB: Inputs.Base.Point2 = [0, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(true); + }); + + it("should return true for negative 2D points within tolerance", () => { + const pointA: Inputs.Base.Point2 = [-5, -10]; + const pointB: Inputs.Base.Point2 = [-5.0000001, -10.0000001]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(true); + }); + + it("should return false for negative 2D points outside tolerance", () => { + const pointA: Inputs.Base.Point2 = [-5, -10]; + const pointB: Inputs.Base.Point2 = [-5.1, -10]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-5)).toBe(false); + }); + }); + + describe("mixed dimensions", () => { + it("should return false for 2D point compared to 3D point", () => { + const pointA: Inputs.Base.Point2 = [1, 2]; + const pointB: Inputs.Base.Point3 = [1, 2, 0]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(false); + }); + + it("should return false for 3D point compared to 2D point", () => { + const pointA: Inputs.Base.Point3 = [1, 2, 0]; + const pointB: Inputs.Base.Point2 = [1, 2]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(false); + }); + }); + + describe("tolerance edge cases", () => { + it("should return false with zero tolerance even for identical points (due to < comparison)", () => { + // Note: approxEq uses Math.abs(num1 - num2) < tolerance, so 0 < 0 is false + const pointA: Inputs.Base.Point3 = [1, 2, 3]; + const pointB: Inputs.Base.Point3 = [1, 2, 3]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 0)).toBe(false); + }); + + it("should work with very small tolerance for identical points", () => { + const pointA: Inputs.Base.Point3 = [1, 2, 3]; + const pointB: Inputs.Base.Point3 = [1, 2, 3]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-15)).toBe(true); + }); + + it("should fail with zero tolerance for any difference", () => { + const pointA: Inputs.Base.Point3 = [1, 2, 3]; + const pointB: Inputs.Base.Point3 = [1.0000000001, 2, 3]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 0)).toBe(false); + }); + + it("should work with very large tolerance", () => { + const pointA: Inputs.Base.Point3 = [0, 0, 0]; + const pointB: Inputs.Base.Point3 = [100, 100, 100]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1000)).toBe(true); + }); + + it("should work with default-like tolerance 1e-7", () => { + const pointA: Inputs.Base.Point3 = [1, 2, 3]; + const pointB: Inputs.Base.Point3 = [1.00000001, 2.00000001, 3.00000001]; + expect(geometryHelper.arePointsTheSame(pointA, pointB, 1e-7)).toBe(true); + }); + }); + }); + + describe("approxEq", () => { + it("should return true for equal numbers", () => { + expect(geometryHelper.approxEq(5, 5, 1e-7)).toBe(true); + }); + + it("should return true for numbers within tolerance", () => { + expect(geometryHelper.approxEq(5, 5.0000001, 1e-5)).toBe(true); + }); + + it("should return false for numbers outside tolerance", () => { + expect(geometryHelper.approxEq(5, 5.01, 1e-5)).toBe(false); + }); + + it("should return true for zero values", () => { + expect(geometryHelper.approxEq(0, 0, 1e-7)).toBe(true); + }); + + it("should return true for negative numbers within tolerance", () => { + expect(geometryHelper.approxEq(-5, -5.0000001, 1e-5)).toBe(true); + }); + + it("should return false for negative numbers outside tolerance", () => { + expect(geometryHelper.approxEq(-5, -5.01, 1e-5)).toBe(false); + }); + + it("should handle comparison across zero", () => { + expect(geometryHelper.approxEq(-0.0000001, 0.0000001, 1e-5)).toBe(true); + }); + + it("should return false when difference equals tolerance", () => { + expect(geometryHelper.approxEq(0, 0.01, 0.01)).toBe(false); + }); + + it("should return true when difference is less than tolerance", () => { + expect(geometryHelper.approxEq(0, 0.009, 0.01)).toBe(true); + }); + }); + + describe("vectorsTheSame", () => { + it("should return true for identical vectors", () => { + expect(geometryHelper.vectorsTheSame([1, 2, 3], [1, 2, 3], 1e-7)).toBe(true); + }); + + it("should return true for vectors within tolerance", () => { + expect(geometryHelper.vectorsTheSame([1, 2], [1.0000001, 2.0000001], 1e-5)).toBe(true); + }); + + it("should return false for vectors outside tolerance", () => { + expect(geometryHelper.vectorsTheSame([1, 2], [1.01, 2], 1e-5)).toBe(false); + }); + + it("should return false for vectors of different lengths", () => { + expect(geometryHelper.vectorsTheSame([1, 2], [1, 2, 3], 1e-7)).toBe(false); + }); + + it("should return true for empty vectors", () => { + expect(geometryHelper.vectorsTheSame([], [], 1e-7)).toBe(true); + }); + + it("should return true for single element vectors within tolerance", () => { + expect(geometryHelper.vectorsTheSame([5], [5.0000001], 1e-5)).toBe(true); + }); + + it("should return false for single element vectors outside tolerance", () => { + expect(geometryHelper.vectorsTheSame([5], [5.01], 1e-5)).toBe(false); + }); + + it("should work with 4D vectors", () => { + expect(geometryHelper.vectorsTheSame([1, 2, 3, 4], [1, 2, 3, 4], 1e-7)).toBe(true); + }); + + it("should return false for 4D vectors with one different element", () => { + expect(geometryHelper.vectorsTheSame([1, 2, 3, 4], [1, 2, 3, 4.1], 1e-5)).toBe(false); + }); + }); + + describe("removeAllDuplicateVectors", () => { + it("should remove duplicate vectors", () => { + const vectors = [[1, 2], [1, 2], [3, 4]]; + expect(geometryHelper.removeAllDuplicateVectors(vectors, 1e-7)).toEqual([[1, 2], [3, 4]]); + }); + + it("should remove non-consecutive duplicates", () => { + const vectors = [[1, 2], [3, 4], [1, 2], [5, 6]]; + expect(geometryHelper.removeAllDuplicateVectors(vectors, 1e-7)).toEqual([[1, 2], [3, 4], [5, 6]]); + }); + + it("should return empty array for empty input", () => { + expect(geometryHelper.removeAllDuplicateVectors([], 1e-7)).toEqual([]); + }); + + it("should return single vector for single input", () => { + expect(geometryHelper.removeAllDuplicateVectors([[1, 2]], 1e-7)).toEqual([[1, 2]]); + }); + + it("should remove duplicates within tolerance", () => { + const vectors = [[1, 2], [1.0000001, 2.0000001]]; + expect(geometryHelper.removeAllDuplicateVectors(vectors, 1e-5)).toEqual([[1, 2]]); + }); + + it("should keep vectors outside tolerance", () => { + const vectors = [[1, 2], [1.01, 2.01]]; + expect(geometryHelper.removeAllDuplicateVectors(vectors, 1e-5)).toEqual([[1, 2], [1.01, 2.01]]); + }); + + it("should handle 3D vectors", () => { + const vectors = [[1, 2, 3], [1, 2, 3], [4, 5, 6]]; + expect(geometryHelper.removeAllDuplicateVectors(vectors, 1e-7)).toEqual([[1, 2, 3], [4, 5, 6]]); + }); + + it("should use default tolerance if not provided", () => { + const vectors = [[1, 2], [1.00000001, 2.00000001]]; + expect(geometryHelper.removeAllDuplicateVectors(vectors)).toEqual([[1, 2]]); + }); + }); + + describe("removeConsecutiveVectorDuplicates", () => { + it("should remove consecutive duplicate vectors", () => { + const vectors = [[1, 2], [1, 2], [3, 4]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, false, 1e-7)).toEqual([[1, 2], [3, 4]]); + }); + + it("should not remove non-consecutive duplicates", () => { + const vectors = [[1, 2], [3, 4], [1, 2]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, false, 1e-7)).toEqual([[1, 2], [3, 4], [1, 2]]); + }); + + it("should check first and last when flag is true", () => { + const vectors = [[1, 2], [3, 4], [1, 2]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, true, 1e-7)).toEqual([[1, 2], [3, 4]]); + }); + + it("should not check first and last when flag is false", () => { + const vectors = [[1, 2], [3, 4], [1, 2]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, false, 1e-7)).toEqual([[1, 2], [3, 4], [1, 2]]); + }); + + it("should return empty array for empty input", () => { + expect(geometryHelper.removeConsecutiveVectorDuplicates([], true, 1e-7)).toEqual([]); + }); + + it("should return single vector for single input", () => { + expect(geometryHelper.removeConsecutiveVectorDuplicates([[1, 2]], true, 1e-7)).toEqual([[1, 2]]); + }); + + it("should remove consecutive duplicates within tolerance (keeps last occurrence)", () => { + const vectors = [[1, 2], [1.0000001, 2.0000001], [3, 4]]; + // Algorithm keeps the last occurrence of consecutive duplicates + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, false, 1e-5)).toEqual([[1.0000001, 2.0000001], [3, 4]]); + }); + + it("should keep consecutive vectors outside tolerance", () => { + const vectors = [[1, 2], [1.01, 2.01], [3, 4]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, false, 1e-5)).toEqual([[1, 2], [1.01, 2.01], [3, 4]]); + }); + + it("should handle multiple consecutive duplicates", () => { + const vectors = [[1, 2], [1, 2], [1, 2], [3, 4], [3, 4]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, false, 1e-7)).toEqual([[1, 2], [3, 4]]); + }); + + it("should handle all identical vectors", () => { + const vectors = [[1, 2], [1, 2], [1, 2]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, false, 1e-7)).toEqual([[1, 2]]); + }); + + it("should handle all identical vectors with checkFirstAndLast", () => { + const vectors = [[1, 2], [1, 2], [1, 2]]; + expect(geometryHelper.removeConsecutiveVectorDuplicates(vectors, true, 1e-7)).toEqual([]); + }); + }); + + describe("removeConsecutivePointDuplicates", () => { + it("should remove consecutive duplicate 3D points", () => { + const points: Inputs.Base.Point3[] = [[1, 2, 3], [1, 2, 3], [4, 5, 6]]; + expect(geometryHelper.removeConsecutivePointDuplicates(points, false, 1e-7)).toEqual([[1, 2, 3], [4, 5, 6]]); + }); + + it("should check first and last when flag is true", () => { + const points: Inputs.Base.Point3[] = [[1, 2, 3], [4, 5, 6], [1, 2, 3]]; + expect(geometryHelper.removeConsecutivePointDuplicates(points, true, 1e-7)).toEqual([[1, 2, 3], [4, 5, 6]]); + }); + + it("should return empty array for empty input", () => { + expect(geometryHelper.removeConsecutivePointDuplicates([], true, 1e-7)).toEqual([]); + }); + + it("should return single point for single input", () => { + const points: Inputs.Base.Point3[] = [[1, 2, 3]]; + expect(geometryHelper.removeConsecutivePointDuplicates(points, true, 1e-7)).toEqual([[1, 2, 3]]); + }); + + it("should remove duplicates within tolerance (keeps last occurrence)", () => { + const points: Inputs.Base.Point3[] = [[1, 2, 3], [1.0000001, 2.0000001, 3.0000001], [4, 5, 6]]; + // Algorithm keeps the last occurrence of consecutive duplicates + expect(geometryHelper.removeConsecutivePointDuplicates(points, false, 1e-5)).toEqual([[1.0000001, 2.0000001, 3.0000001], [4, 5, 6]]); + }); + }); + + describe("getArrayDepth", () => { + it("should return 0 for non-array", () => { + expect(geometryHelper.getArrayDepth(5)).toBe(0); + }); + + it("should return 1 for flat array", () => { + expect(geometryHelper.getArrayDepth([1, 2, 3])).toBe(1); + }); + + it("should return 2 for 2D array", () => { + expect(geometryHelper.getArrayDepth([[1, 2], [3, 4]])).toBe(2); + }); + + it("should return 3 for 3D array", () => { + expect(geometryHelper.getArrayDepth([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])).toBe(3); + }); + + it("should return -Infinity for empty array (due to Math.max with no arguments)", () => { + // Note: Math.max(...[]) returns -Infinity, so 1 + (-Infinity) = -Infinity + expect(geometryHelper.getArrayDepth([])).toBe(-Infinity); + }); + + it("should handle mixed depth arrays (returns max depth)", () => { + expect(geometryHelper.getArrayDepth([[1, 2], [[3, 4]]])).toBe(3); + }); + }); + + describe("getFlatTransformations", () => { + it("should return same array for depth 2 transformation", () => { + const transform = [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]]; + expect(geometryHelper.getFlatTransformations(transform)).toEqual(transform); + }); + + it("should flatten depth 3 transformation", () => { + const transform = [[[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1]]]; + const result = geometryHelper.getFlatTransformations(transform); + expect(result).toHaveLength(2); + expect(result[0]).toEqual([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + expect(result[1]).toEqual([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1]); + }); + }); + + describe("transformControlPoints", () => { + it("should transform points using identity matrix", () => { + const identity = [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]]; + const points: Inputs.Base.Point3[] = [[1, 2, 3]]; + const result = geometryHelper.transformControlPoints(identity, points); + expect(result[0][0]).toBeCloseTo(1, 10); + expect(result[0][1]).toBeCloseTo(2, 10); + expect(result[0][2]).toBeCloseTo(3, 10); + }); + + it("should transform points using translation matrix", () => { + // Translation by (10, 20, 30) + const translation = [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1]]; + const points: Inputs.Base.Point3[] = [[0, 0, 0]]; + const result = geometryHelper.transformControlPoints(translation, points); + expect(result[0][0]).toBeCloseTo(10, 10); + expect(result[0][1]).toBeCloseTo(20, 10); + expect(result[0][2]).toBeCloseTo(30, 10); + }); + + it("should transform multiple points", () => { + const identity = [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]]; + const points: Inputs.Base.Point3[] = [[1, 2, 3], [4, 5, 6]]; + const result = geometryHelper.transformControlPoints(identity, points); + expect(result).toHaveLength(2); + }); + + it("should apply multiple transformations in sequence", () => { + // Two translations: first by (1, 0, 0), then by (0, 1, 0) + const transforms = [ + [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1], + [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1] + ]; + const points: Inputs.Base.Point3[] = [[0, 0, 0]]; + const result = geometryHelper.transformControlPoints(transforms, points); + expect(result[0][0]).toBeCloseTo(1, 10); + expect(result[0][1]).toBeCloseTo(1, 10); + expect(result[0][2]).toBeCloseTo(0, 10); + }); + + it("should handle empty points array", () => { + const identity = [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]]; + const points: Inputs.Base.Point3[] = []; + const result = geometryHelper.transformControlPoints(identity, points); + expect(result).toEqual([]); + }); + + it("should apply uniform scale transformation", () => { + // Scale by 2 + const scale = [[2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1]]; + const points: Inputs.Base.Point3[] = [[1, 1, 1]]; + const result = geometryHelper.transformControlPoints(scale, points); + expect(result[0][0]).toBeCloseTo(2, 10); + expect(result[0][1]).toBeCloseTo(2, 10); + expect(result[0][2]).toBeCloseTo(2, 10); + }); + }); +}); diff --git a/packages/dev/base/lib/api/services/line.test.ts b/packages/dev/base/lib/api/services/line.test.ts index 95416a05..6bc63fc7 100644 --- a/packages/dev/base/lib/api/services/line.test.ts +++ b/packages/dev/base/lib/api/services/line.test.ts @@ -145,6 +145,87 @@ describe("Line unit tests", () => { }); }); + describe("createSegment", () => { + it("should create a segment from start and end points", () => { + const startP: Inputs.Base.Point3 = [1, 2, 3]; + const endP: Inputs.Base.Point3 = [4, 5, 6]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[1, 2, 3], [4, 5, 6]]); + }); + + it("should return an array with exactly two points", () => { + const startP: Inputs.Base.Point3 = [0, 0, 0]; + const endP: Inputs.Base.Point3 = [10, 20, 30]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result.length).toBe(2); + expect(result[0]).toEqual([0, 0, 0]); + expect(result[1]).toEqual([10, 20, 30]); + }); + + it("should handle negative coordinates", () => { + const startP: Inputs.Base.Point3 = [-5, -10, -15]; + const endP: Inputs.Base.Point3 = [-1, -2, -3]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[-5, -10, -15], [-1, -2, -3]]); + }); + + it("should handle zero-length segment (same start and end)", () => { + const startP: Inputs.Base.Point3 = [7, 7, 7]; + const endP: Inputs.Base.Point3 = [7, 7, 7]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[7, 7, 7], [7, 7, 7]]); + }); + + it("should handle floating point coordinates", () => { + const startP: Inputs.Base.Point3 = [1.5, 2.75, 3.125]; + const endP: Inputs.Base.Point3 = [4.875, 5.0625, 6.03125]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[1.5, 2.75, 3.125], [4.875, 5.0625, 6.03125]]); + }); + + it("should handle very large coordinates", () => { + const startP: Inputs.Base.Point3 = [1e10, 2e10, 3e10]; + const endP: Inputs.Base.Point3 = [4e10, 5e10, 6e10]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[1e10, 2e10, 3e10], [4e10, 5e10, 6e10]]); + }); + + it("should handle very small coordinates", () => { + const startP: Inputs.Base.Point3 = [1e-10, 2e-10, 3e-10]; + const endP: Inputs.Base.Point3 = [4e-10, 5e-10, 6e-10]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[1e-10, 2e-10, 3e-10], [4e-10, 5e-10, 6e-10]]); + }); + + it("should create segment along X axis only", () => { + const startP: Inputs.Base.Point3 = [0, 0, 0]; + const endP: Inputs.Base.Point3 = [100, 0, 0]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[0, 0, 0], [100, 0, 0]]); + }); + + it("should create segment along Y axis only", () => { + const startP: Inputs.Base.Point3 = [0, 0, 0]; + const endP: Inputs.Base.Point3 = [0, 100, 0]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[0, 0, 0], [0, 100, 0]]); + }); + + it("should create segment along Z axis only", () => { + const startP: Inputs.Base.Point3 = [0, 0, 0]; + const endP: Inputs.Base.Point3 = [0, 0, 100]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[0, 0, 0], [0, 0, 100]]); + }); + + it("should create diagonal segment in 3D space", () => { + const startP: Inputs.Base.Point3 = [1, 1, 1]; + const endP: Inputs.Base.Point3 = [2, 2, 2]; + const result = line.createSegment({ start: startP, end: endP }); + expect(result).toEqual([[1, 1, 1], [2, 2, 2]]); + }); + }); + describe("getPointOnLine", () => { const testLine: Inputs.Base.Line3 = { start: [0, 0, 0], end: [10, 20, -30] }; diff --git a/packages/dev/base/lib/api/services/mesh.test.ts b/packages/dev/base/lib/api/services/mesh.test.ts index 468eabd3..d8a263e9 100644 --- a/packages/dev/base/lib/api/services/mesh.test.ts +++ b/packages/dev/base/lib/api/services/mesh.test.ts @@ -17,7 +17,7 @@ describe("Mesh unit tests", () => { let math: MathBitByBit; let vector: Vector; let transforms: Transforms; - let point:Point; + let point: Point; let line: Line; let lists: Lists; let polyline: Polyline; @@ -120,7 +120,7 @@ describe("Mesh unit tests", () => { it("should return undefined for triangle degenerate within tolerance", () => { const tolerance = 1e-7; const smallDist = tolerance * 0.1; // Make it smaller than tolerance - const triDegenWithinTol: Inputs.Base.Triangle3 = [[0,0,0], [1,0,0], [2, smallDist, 0]]; + const triDegenWithinTol: Inputs.Base.Triangle3 = [[0, 0, 0], [1, 0, 0], [2, smallDist, 0]]; const planeDegen = meshBitByBit.calculateTrianglePlane({ triangle: triDegenWithinTol, tolerance: tolerance }); expect(planeDegen).toBeUndefined(); // Expect undefined because it IS degenerate within tolerance }); @@ -128,23 +128,23 @@ describe("Mesh unit tests", () => { it("should calculate plane for triangle near-degenerate but outside tolerance", () => { const tolerance = 1e-7; const deviation = tolerance; - const triBarelyValid: Inputs.Base.Triangle3 = [[0,0,0], [1,0,0], [2, deviation, 0]]; + const triBarelyValid: Inputs.Base.Triangle3 = [[0, 0, 0], [1, 0, 0], [2, deviation, 0]]; const plane = meshBitByBit.calculateTrianglePlane({ triangle: triBarelyValid, tolerance: tolerance }); expect(plane).toBeDefined(); - if (plane) { + if (plane) { const normalMagnitude = Math.sign(plane.normal[2]); uh.expectPointCloseTo(plane.normal, [0, 0, 1 * normalMagnitude]); expect(plane.d).toBeCloseTo(0, TOLERANCE); - } + } }); - it("should return undefined for the second degenerate case within tolerance", () => { - const degenTol = 1e-3; - const triDegenWithinTol2: Inputs.Base.Triangle3 = [[0,0,0], [1,0,0], [2, degenTol * 0.1, 0]]; - const planeDegen = meshBitByBit.calculateTrianglePlane({ triangle: triDegenWithinTol2, tolerance: degenTol }); - expect(planeDegen).toBeUndefined(); - }); + it("should return undefined for the second degenerate case within tolerance", () => { + const degenTol = 1e-3; + const triDegenWithinTol2: Inputs.Base.Triangle3 = [[0, 0, 0], [1, 0, 0], [2, degenTol * 0.1, 0]]; + const planeDegen = meshBitByBit.calculateTrianglePlane({ triangle: triDegenWithinTol2, tolerance: degenTol }); + expect(planeDegen).toBeUndefined(); + }); }); describe("triangleTriangleIntersection", () => { @@ -255,13 +255,13 @@ describe("Mesh unit tests", () => { const result = meshBitByBit.meshMeshIntersectionPolylines({ mesh1: cube1Tris, mesh2: cube3Tris, tolerance: intersectionTolerance }); expect(result).toEqual([]); }); - + it("should return intersection polylines for intersecting meshes", () => { const result = meshBitByBit.meshMeshIntersectionPolylines({ mesh1: cube1Tris, mesh2: cube2Tris }); expect(result.length).toBe(4); }); - it("should handle empty mesh inputs", () => { + it("should handle empty mesh inputs", () => { const result1 = meshBitByBit.meshMeshIntersectionPolylines({ mesh1: [], mesh2: cube2Tris, tolerance: intersectionTolerance }); const result2 = meshBitByBit.meshMeshIntersectionPolylines({ mesh1: cube1Tris, mesh2: [], tolerance: intersectionTolerance }); const result3 = meshBitByBit.meshMeshIntersectionPolylines({ mesh1: [], mesh2: [], tolerance: intersectionTolerance }); @@ -270,6 +270,153 @@ describe("Mesh unit tests", () => { expect(result3).toEqual([]); }); - }); + }); + + describe("meshMeshIntersectionPoints", () => { + const intersectionTolerance = 1e-6; + + it("should return an empty array for non-intersecting meshes", () => { + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube3Tris, tolerance: intersectionTolerance }); + expect(result).toEqual([]); + }); + + it("should return intersection points for intersecting meshes", () => { + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); + // Should return 4 polylines worth of points (same as polylines count) + expect(result.length).toBe(4); + console.log(result); + expect(result).toEqual([ + [ + [1, -0.5, -1], + [1, 1, -1], + [1, 1, -0.5], + [1, 1, 1], + [1, -0.5, 1], + [1, -1, 1], + [1, -1, -0.5], + [1, -1, -1], + [1, -0.5, -1] + ], + [ + [0.5, 0.5, 1], [0.5, -1, 1], + [1, -1, 1], [0.5, -1, 1], + [0.5, -1, 0.5], [0.5, -1, -1], + [0.5, 0.5, -1], [0.5, 1, -1], + [1, 1, -1], [0.5, 1, -1], + [0.5, 1, 0.5], [0.5, 1, 1], + [0.5, 0.5, 1] + ], + [[1, 1, 1], [0.5, 1, 1], [1, 1, 1]], + [[1, -1, -1], [0.5, -1, -1], [1, -1, -1]] + ]); + }); + + it("should return arrays of points for each polyline", () => { + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); + // Each element should be an array of points + result.forEach(pointArray => { + expect(Array.isArray(pointArray)).toBe(true); + expect(pointArray.length).toBeGreaterThan(0); + // Each point should be a 3D coordinate + pointArray.forEach(point => { + expect(point.length).toBe(3); + expect(typeof point[0]).toBe("number"); + expect(typeof point[1]).toBe("number"); + expect(typeof point[2]).toBe("number"); + }); + }); + }); + + it("should include closing point for closed polylines", () => { + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); + // For closed polylines, first and last point should be the same + result.forEach(pointArray => { + if (pointArray.length > 1) { + const firstPoint = pointArray[0]; + const lastPoint = pointArray[pointArray.length - 1]; + // Check if the polyline is closed (first equals last) + const isClosed = + Math.abs(firstPoint[0] - lastPoint[0]) < intersectionTolerance && + Math.abs(firstPoint[1] - lastPoint[1]) < intersectionTolerance && + Math.abs(firstPoint[2] - lastPoint[2]) < intersectionTolerance; + // Cube-cube intersection should produce closed loops + expect(isClosed).toBe(true); + } + }); + }); + + it("should handle empty mesh inputs", () => { + const result1 = meshBitByBit.meshMeshIntersectionPoints({ mesh1: [], mesh2: cube2Tris, tolerance: intersectionTolerance }); + const result2 = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: [], tolerance: intersectionTolerance }); + const result3 = meshBitByBit.meshMeshIntersectionPoints({ mesh1: [], mesh2: [], tolerance: intersectionTolerance }); + expect(result1).toEqual([]); + expect(result2).toEqual([]); + expect(result3).toEqual([]); + }); + + it("should return points that lie on intersection plane X=0.5", () => { + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); + // Cube1 is from -1 to 1, Cube2 is from 0.5 to 2.5 + // Intersection should be at X=0.5 or X=1 depending on the faces + result.forEach(pointArray => { + pointArray.forEach(point => { + // Points should be at X=0.5 (left face of cube2) or X=1 (right face of cube1) + const xIsValid = Math.abs(point[0] - 0.5) < 0.01 || Math.abs(point[0] - 1) < 0.01; + expect(xIsValid).toBe(true); + }); + }); + }); + + it("should return points within the Y and Z bounds of both cubes", () => { + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); + result.forEach(pointArray => { + pointArray.forEach(point => { + // Y and Z should be within [-1, 1] range (shared by both cubes) + expect(point[1]).toBeGreaterThanOrEqual(-1 - intersectionTolerance); + expect(point[1]).toBeLessThanOrEqual(1 + intersectionTolerance); + expect(point[2]).toBeGreaterThanOrEqual(-1 - intersectionTolerance); + expect(point[2]).toBeLessThanOrEqual(1 + intersectionTolerance); + }); + }); + }); + + it("should produce more points than polylines (due to closing point)", () => { + const polylines = meshBitByBit.meshMeshIntersectionPolylines({ mesh1: cube1Tris, mesh2: cube2Tris }); + const points = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); + + expect(points.length).toBe(polylines.length); + + // For closed polylines, points array should have one more point than polyline.points + for (let i = 0; i < polylines.length; i++) { + if (polylines[i].isClosed) { + expect(points[i].length).toBe(polylines[i].points.length + 1); + } else { + expect(points[i].length).toBe(polylines[i].points.length); + } + } + }); + + it("should handle single triangle meshes that intersect", () => { + const singleTri1: Inputs.Base.Triangle3[] = [[[0, 0, 0], [2, 0, 0], [0, 2, 0]]]; // XY plane + const singleTri2: Inputs.Base.Triangle3[] = [[[0, 0, 0], [2, 0, 0], [0, 0, 2]]]; // XZ plane + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: singleTri1, mesh2: singleTri2 }); + expect(result.length).toBe(1); + expect(result[0].length).toBe(2); // Open polyline with 2 points + }); + + it("should return points as valid 3D coordinates", () => { + const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); + result.forEach(pointArray => { + pointArray.forEach(point => { + expect(Number.isFinite(point[0])).toBe(true); + expect(Number.isFinite(point[1])).toBe(true); + expect(Number.isFinite(point[2])).toBe(true); + expect(Number.isNaN(point[0])).toBe(false); + expect(Number.isNaN(point[1])).toBe(false); + expect(Number.isNaN(point[2])).toBe(false); + }); + }); + }); + }); }); From 34065c2293c7d821d25b81c77f8a741970510800 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 13:58:52 +0200 Subject: [PATCH 20/38] removed console.log from unit tests --- packages/dev/base/lib/api/services/mesh.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev/base/lib/api/services/mesh.test.ts b/packages/dev/base/lib/api/services/mesh.test.ts index d8a263e9..cc5793c1 100644 --- a/packages/dev/base/lib/api/services/mesh.test.ts +++ b/packages/dev/base/lib/api/services/mesh.test.ts @@ -284,7 +284,6 @@ describe("Mesh unit tests", () => { const result = meshBitByBit.meshMeshIntersectionPoints({ mesh1: cube1Tris, mesh2: cube2Tris }); // Should return 4 polylines worth of points (same as polylines count) expect(result.length).toBe(4); - console.log(result); expect(result).toEqual([ [ [1, -0.5, -1], From 30f31c4ed5ac844341981ab567ffc908ddc02ad2 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 15:10:03 +0200 Subject: [PATCH 21/38] additional unit tests --- .../dev/threejs/lib/api/bitbybit-base.test.ts | 333 +++++ .../dev/threejs/lib/api/bitbybit/draw.test.ts | 652 ++++++++- .../dev/threejs/lib/api/draw-helper.test.ts | 1176 ++++++++++++++++- .../lib/api/inputs/draw-inputs.test.ts | 432 ++++++ 4 files changed, 2573 insertions(+), 20 deletions(-) create mode 100644 packages/dev/threejs/lib/api/bitbybit-base.test.ts create mode 100644 packages/dev/threejs/lib/api/inputs/draw-inputs.test.ts diff --git a/packages/dev/threejs/lib/api/bitbybit-base.test.ts b/packages/dev/threejs/lib/api/bitbybit-base.test.ts new file mode 100644 index 00000000..c70b6fd5 --- /dev/null +++ b/packages/dev/threejs/lib/api/bitbybit-base.test.ts @@ -0,0 +1,333 @@ +import * as THREEJS from "three"; +import { BitByBitBase } from "./bitbybit-base"; + +describe("BitByBitBase unit tests", () => { + let bitByBit: BitByBitBase; + + beforeEach(() => { + bitByBit = new BitByBitBase(); + }); + + describe("Constructor initialization", () => { + it("should create a BitByBitBase instance", () => { + expect(bitByBit).toBeDefined(); + expect(bitByBit).toBeInstanceOf(BitByBitBase); + }); + + it("should initialize context", () => { + expect(bitByBit.context).toBeDefined(); + }); + + it("should initialize worker managers", () => { + expect(bitByBit.jscadWorkerManager).toBeDefined(); + expect(bitByBit.manifoldWorkerManager).toBeDefined(); + expect(bitByBit.occtWorkerManager).toBeDefined(); + }); + + it("should initialize math service", () => { + expect(bitByBit.math).toBeDefined(); + }); + + it("should initialize logic service", () => { + expect(bitByBit.logic).toBeDefined(); + }); + + it("should initialize lists service", () => { + expect(bitByBit.lists).toBeDefined(); + }); + + it("should initialize json service", () => { + expect(bitByBit.json).toBeDefined(); + }); + + it("should initialize vector service", () => { + expect(bitByBit.vector).toBeDefined(); + }); + + it("should initialize three service", () => { + expect(bitByBit.three).toBeDefined(); + }); + + it("should initialize point service", () => { + expect(bitByBit.point).toBeDefined(); + }); + + it("should initialize line service", () => { + expect(bitByBit.line).toBeDefined(); + }); + + it("should initialize transforms service", () => { + expect(bitByBit.transforms).toBeDefined(); + }); + + it("should initialize polyline service", () => { + expect(bitByBit.polyline).toBeDefined(); + }); + + it("should initialize draw service", () => { + expect(bitByBit.draw).toBeDefined(); + }); + + it("should initialize verb service", () => { + expect(bitByBit.verb).toBeDefined(); + }); + + it("should initialize jscad service", () => { + expect(bitByBit.jscad).toBeDefined(); + }); + + it("should initialize manifold service", () => { + expect(bitByBit.manifold).toBeDefined(); + }); + + it("should initialize text service", () => { + expect(bitByBit.text).toBeDefined(); + }); + + it("should initialize dates service", () => { + expect(bitByBit.dates).toBeDefined(); + }); + + it("should initialize tag service", () => { + expect(bitByBit.tag).toBeDefined(); + }); + + it("should initialize time service", () => { + expect(bitByBit.time).toBeDefined(); + }); + + it("should initialize occt service", () => { + expect(bitByBit.occt).toBeDefined(); + }); + + it("should initialize mesh service", () => { + expect(bitByBit.mesh).toBeDefined(); + }); + + it("should initialize asset service", () => { + expect(bitByBit.asset).toBeDefined(); + }); + + it("should initialize color service", () => { + expect(bitByBit.color).toBeDefined(); + }); + }); + + describe("init method", () => { + it("should initialize with scene only", () => { + const scene = new THREEJS.Scene(); + bitByBit.init(scene); + expect(bitByBit.context.scene).toBe(scene); + }); + + it("should set verb context", () => { + const scene = new THREEJS.Scene(); + bitByBit.init(scene); + expect(bitByBit.context.verb).toBeDefined(); + expect(bitByBit.context.verb.geom).toBeDefined(); + expect(bitByBit.context.verb.core).toBeDefined(); + }); + + it("should set jsonpath context", () => { + const scene = new THREEJS.Scene(); + bitByBit.init(scene); + expect(bitByBit.context.jsonpath).toBeDefined(); + }); + + it("should initialize with scene and occt worker", () => { + const scene = new THREEJS.Scene(); + const mockOcctWorker = { + postMessage: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + terminate: jest.fn(), + onmessage: null, + onmessageerror: null, + onerror: null, + dispatchEvent: jest.fn(), + } as unknown as Worker; + + bitByBit.init(scene, mockOcctWorker); + expect(bitByBit.context.scene).toBe(scene); + }); + + it("should initialize with scene and jscad worker", () => { + const scene = new THREEJS.Scene(); + const mockJscadWorker = { + postMessage: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + terminate: jest.fn(), + onmessage: null, + onmessageerror: null, + onerror: null, + dispatchEvent: jest.fn(), + } as unknown as Worker; + + bitByBit.init(scene, undefined, mockJscadWorker); + expect(bitByBit.context.scene).toBe(scene); + }); + + it("should initialize with scene and manifold worker", () => { + const scene = new THREEJS.Scene(); + const mockManifoldWorker = { + postMessage: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + terminate: jest.fn(), + onmessage: null, + onmessageerror: null, + onerror: null, + dispatchEvent: jest.fn(), + } as unknown as Worker; + + bitByBit.init(scene, undefined, undefined, mockManifoldWorker); + expect(bitByBit.context.scene).toBe(scene); + }); + + it("should initialize with all workers", () => { + const scene = new THREEJS.Scene(); + const createMockWorker = () => ({ + postMessage: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + terminate: jest.fn(), + onmessage: null, + onmessageerror: null, + onerror: null, + dispatchEvent: jest.fn(), + } as unknown as Worker); + + const mockOcctWorker = createMockWorker(); + const mockJscadWorker = createMockWorker(); + const mockManifoldWorker = createMockWorker(); + + bitByBit.init(scene, mockOcctWorker, mockJscadWorker, mockManifoldWorker); + expect(bitByBit.context.scene).toBe(scene); + }); + + it("should handle undefined workers gracefully", () => { + const scene = new THREEJS.Scene(); + expect(() => bitByBit.init(scene, undefined, undefined, undefined)).not.toThrow(); + expect(bitByBit.context.scene).toBe(scene); + }); + }); + + describe("Service integration", () => { + it("should have functional math operations", () => { + // Test basic number creation instead of twoNrOperation which requires proper enum + const result = bitByBit.math.number({ number: 42 }); + expect(result).toBe(42); + }); + + it("should have functional math rounding", () => { + const result = bitByBit.math.roundToDecimals({ number: 3.14159, decimalPlaces: 2 }); + expect(result).toBeCloseTo(3.14, 2); + }); + + it("should have functional vector operations", () => { + const result = bitByBit.vector.add({ first: [1, 2, 3], second: [4, 5, 6] }); + expect(result).toEqual([5, 7, 9]); + }); + + it("should have functional point operations", () => { + const result = bitByBit.point.distance({ startPoint: [0, 0, 0], endPoint: [3, 4, 0] }); + expect(result).toBeCloseTo(5, 5); + }); + + it("should have functional line creation", () => { + const result = bitByBit.line.create({ start: [0, 0, 0], end: [1, 1, 1] }); + expect(result).toEqual({ start: [0, 0, 0], end: [1, 1, 1] }); + }); + + it("should have functional polyline creation", () => { + const result = bitByBit.polyline.create({ points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] }); + expect(result.points).toEqual([[0, 0, 0], [1, 0, 0], [1, 1, 0]]); + expect(result.isClosed).toBe(false); + }); + + it("should have functional list operations", () => { + const result = bitByBit.lists.getItem({ list: [10, 20, 30], index: 1 }); + expect(result).toBe(20); + }); + + it("should have functional transform operations", () => { + const result = bitByBit.transforms.translationXYZ({ translation: [1, 2, 3] }); + expect(result).toBeDefined(); + expect(Array.isArray(result)).toBe(true); + }); + + it("should have functional color operations", () => { + const result = bitByBit.color.hexToRgb({ color: "#ff0000" }); + expect(result).toEqual({ r: 255, g: 0, b: 0 }); + }); + + it("should have functional mesh operations", () => { + const plane = bitByBit.mesh.calculateTrianglePlane({ + triangle: [[0, 0, 0], [1, 0, 0], [0, 1, 0]] + }); + expect(plane).toBeDefined(); + expect(plane.normal).toBeDefined(); + }); + + it("should have functional logic operations", () => { + const result = bitByBit.logic.firstDefinedValueGate({ value1: undefined, value2: 5 }); + expect(result).toBe(5); + }); + + it("should have functional boolean operations", () => { + const result = bitByBit.logic.boolean({ boolean: true }); + expect(result).toBe(true); + }); + }); + + describe("Multiple instances", () => { + it("should create independent instances", () => { + const instance1 = new BitByBitBase(); + const instance2 = new BitByBitBase(); + + expect(instance1).not.toBe(instance2); + expect(instance1.context).not.toBe(instance2.context); + }); + + it("should have independent scenes after init", () => { + const instance1 = new BitByBitBase(); + const instance2 = new BitByBitBase(); + + const scene1 = new THREEJS.Scene(); + const scene2 = new THREEJS.Scene(); + scene1.name = "scene1"; + scene2.name = "scene2"; + + instance1.init(scene1); + instance2.init(scene2); + + expect(instance1.context.scene).toBe(scene1); + expect(instance2.context.scene).toBe(scene2); + expect(instance1.context.scene).not.toBe(instance2.context.scene); + }); + }); + + describe("Scene manipulation after init", () => { + it("should allow adding objects to the scene via draw", () => { + const scene = new THREEJS.Scene(); + bitByBit.init(scene); + + // Draw a point + const result = bitByBit.draw.drawAny({ entity: [1, 2, 3] }); + expect(result).toBeDefined(); + expect(scene.children.length).toBeGreaterThan(0); + }); + + it("should allow multiple draws to the same scene", () => { + const scene = new THREEJS.Scene(); + bitByBit.init(scene); + + bitByBit.draw.drawAny({ entity: [1, 2, 3] }); + bitByBit.draw.drawAny({ entity: [4, 5, 6] }); + bitByBit.draw.drawAny({ entity: [[0, 0, 0], [1, 1, 1]] }); // line + + expect(scene.children.length).toBeGreaterThanOrEqual(3); + }); + }); +}); diff --git a/packages/dev/threejs/lib/api/bitbybit/draw.test.ts b/packages/dev/threejs/lib/api/bitbybit/draw.test.ts index 58af1add..b89a38a6 100644 --- a/packages/dev/threejs/lib/api/bitbybit/draw.test.ts +++ b/packages/dev/threejs/lib/api/bitbybit/draw.test.ts @@ -6,7 +6,7 @@ import { DrawHelper } from "../draw-helper"; import { Draw } from "./draw"; import { JSCADWorkerManager } from "@bitbybit-dev/jscad-worker"; import { OCCTWorkerManager } from "@bitbybit-dev/occt-worker/lib"; -import { InstancedMesh, LineSegments, Mesh, MeshPhongMaterial, Scene } from "three"; +import { Group, InstancedMesh, LineSegments, Mesh, MeshPhongMaterial, Scene } from "three"; import * as Inputs from "../inputs"; import { ManifoldWorkerManager } from "@bitbybit-dev/manifold-worker"; @@ -808,6 +808,656 @@ describe("Draw unit tests", () => { }); }); + + describe("Draw segment tests", () => { + + it("should draw a segment (2-point array) via draw any", () => { + const segment: Inputs.Base.Segment3 = [[0, 0, 0], [1, 2, 3]]; + const res = draw.drawAny({ entity: segment }); + expect(res.name).toContain("polylines"); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.line); + expect(res.children[0] instanceof LineSegments).toBe(true); + }); + + it("should draw a segment via draw any async", async () => { + const segment: Inputs.Base.Segment3 = [[-1, -2, -3], [4, 5, 6]]; + const res = await draw.drawAnyAsync({ entity: segment }); + expect(res.name).toContain("polylines"); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.line); + }); + + it("should update a segment via draw any", () => { + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const segment1: Inputs.Base.Segment3 = [[0, 0, 0], [1, 1, 1]]; + const segment2: Inputs.Base.Segment3 = [[2, 2, 2], [3, 3, 3]]; + const res = draw.drawAny({ entity: segment1, options }); + const res2 = draw.drawAny({ entity: segment2, options, group: res }); + expect(res.name).toEqual(res2.name); + }); + }); + + describe("Draw edge cases and undefined handling", () => { + + it("should return undefined for undefined entity via drawAnyAsync", async () => { + const res = await draw.drawAnyAsync({ entity: undefined }); + expect(res).toBeUndefined(); + }); + + it("should return undefined for empty array entity via drawAnyAsync", async () => { + const res = await draw.drawAnyAsync({ entity: [] }); + expect(res).toBeUndefined(); + }); + + it("should return undefined for undefined entity via drawAny", () => { + // drawAny doesn't handle undefined entities gracefully - detectLine will throw + // Testing that result is undefined when detection functions don't match + const res = draw.drawAny({ entity: { unknownType: true } as any }); + expect(res).toBeUndefined(); + }); + }); + + describe("Options functions", () => { + + it("should return simple options unchanged", () => { + const options = new Inputs.Draw.DrawBasicGeometryOptions(); + options.colours = "#ff0000"; + options.size = 5; + const result = draw.optionsSimple(options); + expect(result).toEqual(options); + expect(result.colours).toBe("#ff0000"); + expect(result.size).toBe(5); + }); + + it("should return OCCT shape options unchanged", () => { + const options = new Inputs.Draw.DrawOcctShapeOptions(); + options.faceColour = "#00ff00"; + options.drawEdges = true; + const result = draw.optionsOcctShape(options); + expect(result).toEqual(options); + expect(result.faceColour).toBe("#00ff00"); + expect(result.drawEdges).toBe(true); + }); + }); + + describe("Update via group userData type", () => { + + it("should update point when group has point type in userData", () => { + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = draw.drawAny({ entity: [1, 2, 3], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.point); + + const res2 = draw.drawAny({ entity: [4, 5, 6], options, group: res }); + expect(res.name).toEqual(res2.name); + expect(res2.children[0].position.x).toBe(4); + }); + + it("should update points when group has points type in userData", () => { + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = draw.drawAny({ entity: [[1, 2, 3], [4, 5, 6]], options }); + // Array of two 3D points could be detected as a line (segment) or points + // The actual type depends on detection order in drawAny + expect(res.userData.type).toBeDefined(); + + const res2 = draw.drawAny({ entity: [[7, 8, 9], [10, 11, 12]], options, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update line when group has line type in userData", () => { + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = draw.drawAny({ entity: { start: [0, 0, 0], end: [1, 1, 1] }, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.line); + + const res2 = draw.drawAny({ entity: { start: [2, 2, 2], end: [3, 3, 3] }, options, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update polyline when group has polyline type in userData", () => { + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = draw.drawAny({ entity: { points: [[0, 0, 0], [1, 1, 1], [2, 2, 2]] }, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.polyline); + + const res2 = draw.drawAny({ entity: { points: [[3, 3, 3], [4, 4, 4], [5, 5, 5]] }, options, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update verb curve when group has verbCurve type in userData", () => { + const curveMock1 = { + tessellate: () => [[0, 0, 0], [1, 1, 1]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const curveMock2 = { + tessellate: () => [[2, 2, 2], [3, 3, 3]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = draw.drawAny({ entity: curveMock1, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbCurve); + + const res2 = draw.drawAny({ entity: curveMock2, options, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update verb surface when group has verbSurface type in userData", () => { + const surfaceMock1 = createSurfaceMock(); + const surfaceMock2 = createSurfaceMock2(); + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = draw.drawAny({ entity: surfaceMock1, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbSurface); + + const res2 = draw.drawAny({ entity: surfaceMock2, options, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should use options from group userData when no options provided", () => { + const originalOptions = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: "#ff0000", + size: 5, + updatable: true, + }; + const res = draw.drawAny({ entity: [1, 2, 3], options: originalOptions }); + + // Now update without providing options - should use stored options from userData + const res2 = draw.drawAny({ entity: [4, 5, 6], group: res }); + expect(res.name).toEqual(res2.name); + }); + }); + + describe("Draw Manifold meshes", () => { + + it("should draw a manifold shape", async () => { + manifoldWorkerManager.genericCallToWorkerPromise = jest.fn().mockResolvedValue({ + vertProperties: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]), + triVerts: new Uint32Array([0, 1, 2]) + }); + + const options = new Inputs.Draw.DrawManifoldOrCrossSectionOptions(); + // Use the correct type string for manifold detection + const res = await draw.drawAnyAsync({ entity: { type: "manifold-shape", id: 123 } as any, options }); + expect(res).toBeDefined(); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.occt); + }); + + it("should draw multiple manifold shapes", async () => { + manifoldWorkerManager.genericCallToWorkerPromise = jest.fn().mockResolvedValue([ + { + vertProperties: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]), + triVerts: new Uint32Array([0, 1, 2]) + }, + { + vertProperties: new Float32Array([2, 0, 0, 3, 0, 0, 2, 1, 0]), + triVerts: new Uint32Array([0, 1, 2]) + } + ]); + + const options = new Inputs.Draw.DrawManifoldOrCrossSectionOptions(); + // Use correct type string "manifold-shape" for detection + const res = await draw.drawAnyAsync({ + entity: [ + { type: "manifold-shape", id: 123 } as any, + { type: "manifold-shape", id: 456 } as any + ], + options + }); + expect(res).toBeDefined(); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.occt); + }); + }); + + describe("Draw polylines (multiple)", () => { + + it("should draw multiple polylines via drawAny", () => { + const polylines = [ + { points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] as Inputs.Base.Point3[] }, + { points: [[2, 0, 0], [3, 0, 0], [3, 1, 0]] as Inputs.Base.Point3[] }, + { points: [[4, 0, 0], [5, 0, 0], [5, 1, 0]] as Inputs.Base.Point3[] } + ]; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: "#ff00ff", + }; + const res = draw.drawAny({ entity: polylines, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.polylines); + expect(res.name).toContain("polylines"); + }); + + it("should draw multiple polylines with different colors", () => { + const polylines = [ + { points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] as Inputs.Base.Point3[] }, + { points: [[2, 0, 0], [3, 0, 0], [3, 1, 0]] as Inputs.Base.Point3[] }, + { points: [[4, 0, 0], [5, 0, 0], [5, 1, 0]] as Inputs.Base.Point3[] } + ]; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: ["#ff0000", "#00ff00", "#0000ff"], + }; + const res = draw.drawAny({ entity: polylines, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.polylines); + }); + + it("should update multiple polylines when group is provided", async () => { + const polylines1 = [ + { points: [[0, 0, 0], [1, 0, 0]] as Inputs.Base.Point3[] }, + ]; + const polylines2 = [ + { points: [[2, 2, 2], [3, 3, 3]] as Inputs.Base.Point3[] }, + ]; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = await draw.drawAnyAsync({ entity: polylines1, options }); + const res2 = await draw.drawAnyAsync({ entity: polylines2, options, group: res }); + expect(res.name).toEqual(res2.name); + expect(res2.userData.type).toBe(Inputs.Draw.drawingTypes.polylines); + }); + + it("should return undefined for empty polylines array via drawAnyAsync", async () => { + const polylines: Inputs.Base.Polyline3[] = []; + const res = await draw.drawAnyAsync({ entity: polylines }); + expect(res).toBeUndefined(); + }); + }); + + describe("Draw lines (multiple)", () => { + + it("should draw multiple lines as Line3 objects via drawAny", () => { + const lines: Inputs.Base.Line3[] = [ + { start: [0, 0, 0], end: [1, 1, 1] }, + { start: [2, 2, 2], end: [3, 3, 3] }, + { start: [4, 4, 4], end: [5, 5, 5] } + ]; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: "#ff0000", + }; + const res = draw.drawAny({ entity: lines, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.lines); + expect(res.name).toContain("polylines"); + }); + + it("should draw multiple segments via drawAnyAsync", async () => { + const segments: Inputs.Base.Segment3[] = [ + [[0, 0, 0], [1, 1, 1]], + [[2, 2, 2], [3, 3, 3]] + ]; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: "#00ff00", + }; + const res = await draw.drawAnyAsync({ entity: segments, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.lines); + }); + + it("should update multiple lines when group is provided", async () => { + const lines1: Inputs.Base.Line3[] = [ + { start: [0, 0, 0], end: [1, 1, 1] }, + ]; + const lines2: Inputs.Base.Line3[] = [ + { start: [5, 5, 5], end: [6, 6, 6] }, + ]; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = await draw.drawAnyAsync({ entity: lines1, options }); + const res2 = await draw.drawAnyAsync({ entity: lines2, options, group: res }); + expect(res.name).toEqual(res2.name); + expect(res2.userData.type).toBe(Inputs.Draw.drawingTypes.lines); + }); + + it("should handle mixed line formats in array", async () => { + // If first element has 'start' property, all are treated as Line3 + const lines: Inputs.Base.Line3[] = [ + { start: [0, 0, 0], end: [1, 0, 0] }, + { start: [1, 0, 0], end: [1, 1, 0] }, + { start: [1, 1, 0], end: [0, 0, 0] } + ]; + const res = await draw.drawAnyAsync({ entity: lines }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.lines); + }); + }); + + describe("Draw verb curves (multiple)", () => { + + it("should draw multiple verb curves via drawAny", () => { + const curveMock1 = { + tessellate: () => [[0, 0, 0], [1, 1, 1]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const curveMock2 = { + tessellate: () => [[2, 2, 2], [3, 3, 3]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const curveMock3 = { + tessellate: () => [[4, 4, 4], [5, 5, 5]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: "#ff0000", + }; + const res = draw.drawAny({ entity: [curveMock1, curveMock2, curveMock3], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbCurves); + expect(res.name).toContain("polylines"); + }); + + it("should update multiple verb curves when group is provided", async () => { + const curveMock1 = { + tessellate: () => [[0, 0, 0], [1, 1, 1]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const curveMock2 = { + tessellate: () => [[5, 5, 5], [6, 6, 6]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = await draw.drawAnyAsync({ entity: [curveMock1], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbCurves); + + const res2 = await draw.drawAnyAsync({ entity: [curveMock2], options, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should draw curves with multiple colors", () => { + const curveMock1 = { + tessellate: () => [[0, 0, 0], [1, 1, 1]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const curveMock2 = { + tessellate: () => [[2, 2, 2], [3, 3, 3]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const curveMock3 = { + tessellate: () => [[4, 4, 4], [5, 5, 5]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: ["#ff0000", "#00ff00", "#0000ff"], + }; + const res = draw.drawAny({ entity: [curveMock1, curveMock2, curveMock3], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbCurves); + }); + }); + + describe("Draw verb surfaces (multiple)", () => { + + it("should draw multiple verb surfaces via drawAny", () => { + const surfaceMock1 = createSurfaceMock(); + const surfaceMock2 = createSurfaceMock2(); + const surfaceMock3 = createSurfaceMock(); + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: "#ff0000", + }; + const res = draw.drawAny({ entity: [surfaceMock1, surfaceMock2, surfaceMock3], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbSurfaces); + expect(res.name).toContain("colouredSurfaces"); + }); + + it("should update multiple verb surfaces when group is provided", async () => { + const surfaceMock1 = createSurfaceMock(); + const surfaceMock2 = createSurfaceMock2(); + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: true, + }; + const res = await draw.drawAnyAsync({ entity: [surfaceMock1], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbSurfaces); + + const res2 = await draw.drawAnyAsync({ entity: [surfaceMock2], options, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should draw surfaces with multiple colors", () => { + const surfaceMock1 = createSurfaceMock(); + const surfaceMock2 = createSurfaceMock2(); + const surfaceMock3 = createSurfaceMock(); + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + colours: ["#ff0000", "#00ff00", "#0000ff"], + }; + const res = draw.drawAny({ entity: [surfaceMock1, surfaceMock2, surfaceMock3], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbSurfaces); + expect(res.children.length).toBe(3); + }); + }); + + describe("Draw tags", () => { + // Tags require DOM (document) for full implementation + // We test that the correct tag methods are called via spies + + it("should call tag.drawTag for a single tag entity", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tag, options: {} }; + const drawTagSpy = jest.spyOn(tag, "drawTag").mockReturnValue(mockGroup as any); + + const tagEntity: Inputs.Tag.TagDto = { + text: "Test Tag", + position: [1, 2, 3], + colour: "#ff0000", + size: 1, + adaptDepth: false, + }; + const res = draw.drawAny({ entity: tagEntity }); + expect(drawTagSpy).toHaveBeenCalledTimes(1); + expect(drawTagSpy).toHaveBeenCalledWith(expect.objectContaining({ + tag: tagEntity, + })); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.tag); + drawTagSpy.mockRestore(); + }); + + it("should call tag.drawTag with custom options", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tag, options: {} }; + const drawTagSpy = jest.spyOn(tag, "drawTag").mockReturnValue(mockGroup as any); + + const tagEntity: Inputs.Tag.TagDto = { + text: "Hello World", + position: [0, 0, 0], + colour: "#00ff00", + size: 2, + adaptDepth: false, + }; + const options = { updatable: true }; + const res = draw.drawAny({ entity: tagEntity, options }); + expect(drawTagSpy).toHaveBeenCalledWith(expect.objectContaining({ + tag: tagEntity, + updatable: true, + })); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.tag); + drawTagSpy.mockRestore(); + }); + + it("should call tag.drawTag when updating a tag with group", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tag, options: { updatable: true } }; + const drawTagSpy = jest.spyOn(tag, "drawTag").mockReturnValue(mockGroup as any); + + const tagEntity: Inputs.Tag.TagDto = { + text: "Updated Tag", + position: [1, 1, 1], + colour: "#00ff00", + size: 2, + adaptDepth: false, + }; + // Simulate update by passing existing group + const res = draw.drawAny({ entity: tagEntity, group: mockGroup }); + expect(drawTagSpy).toHaveBeenCalled(); + drawTagSpy.mockRestore(); + }); + + it("should call tag.drawTags for multiple tag entities", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tags, options: {} }; + const drawTagsSpy = jest.spyOn(tag, "drawTags").mockReturnValue(mockGroup as any); + + const tagsEntity: Inputs.Tag.TagDto[] = [ + { text: "Tag 1", position: [0, 0, 0], colour: "#ff0000", size: 1, adaptDepth: false }, + { text: "Tag 2", position: [1, 1, 1], colour: "#00ff00", size: 1, adaptDepth: false }, + { text: "Tag 3", position: [2, 2, 2], colour: "#0000ff", size: 1, adaptDepth: false }, + ]; + const res = draw.drawAny({ entity: tagsEntity }); + expect(drawTagsSpy).toHaveBeenCalledTimes(1); + expect(drawTagsSpy).toHaveBeenCalledWith(expect.objectContaining({ + tags: tagsEntity, + })); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.tags); + drawTagsSpy.mockRestore(); + }); + + it("should call tag.drawTags when updating multiple tags with group", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tags, options: { updatable: true } }; + const drawTagsSpy = jest.spyOn(tag, "drawTags").mockReturnValue(mockGroup as any); + + const tagsEntity: Inputs.Tag.TagDto[] = [ + { text: "Tag C", position: [2, 2, 2], colour: "#0000ff", size: 2, adaptDepth: false }, + { text: "Tag D", position: [3, 3, 3], colour: "#ffff00", size: 2, adaptDepth: false }, + ]; + // Simulate update by passing existing group + const res = draw.drawAny({ entity: tagsEntity, group: mockGroup }); + expect(drawTagsSpy).toHaveBeenCalled(); + drawTagsSpy.mockRestore(); + }); + + it("should call tag.drawTags with custom options", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tags, options: {} }; + const drawTagsSpy = jest.spyOn(tag, "drawTags").mockReturnValue(mockGroup as any); + + const tagsEntity: Inputs.Tag.TagDto[] = [ + { text: "Custom Tag", position: [5, 5, 5], colour: "#ffffff", size: 3, adaptDepth: false }, + ]; + const options = { + ...new Inputs.Draw.DrawBasicGeometryOptions(), + updatable: false, + }; + const res = draw.drawAny({ entity: tagsEntity, options }); + expect(drawTagsSpy).toHaveBeenCalledWith(expect.objectContaining({ + tags: tagsEntity, + })); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.tags); + drawTagsSpy.mockRestore(); + }); + }); + + describe("UpdateAny through group userData", () => { + + it("should update polylines when group has polylines type", () => { + const polylines1 = [{ points: [[0, 0, 0], [1, 0, 0]] as Inputs.Base.Point3[] }]; + const polylines2 = [{ points: [[2, 2, 2], [3, 2, 2]] as Inputs.Base.Point3[] }]; + const options = { ...new Inputs.Draw.DrawBasicGeometryOptions(), updatable: true }; + + const res = draw.drawAny({ entity: polylines1, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.polylines); + + const res2 = draw.drawAny({ entity: polylines2, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update lines when group has lines type", () => { + const lines1: Inputs.Base.Line3[] = [{ start: [0, 0, 0], end: [1, 1, 1] }]; + const lines2: Inputs.Base.Line3[] = [{ start: [5, 5, 5], end: [6, 6, 6] }]; + const options = { ...new Inputs.Draw.DrawBasicGeometryOptions(), updatable: true }; + + const res = draw.drawAny({ entity: lines1, options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.lines); + + const res2 = draw.drawAny({ entity: lines2, group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update verb curves when group has verbCurves type", () => { + const curveMock1 = { + tessellate: () => [[0, 0, 0], [1, 1, 1]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const curveMock2 = { + tessellate: () => [[5, 5, 5], [6, 6, 6]], + _data: { controlPoints: [], knots: 3, degree: 3 }, + }; + const options = { ...new Inputs.Draw.DrawBasicGeometryOptions(), updatable: true }; + + const res = draw.drawAny({ entity: [curveMock1], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbCurves); + + const res2 = draw.drawAny({ entity: [curveMock2], group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update verb surfaces when group has verbSurfaces type", () => { + const surfaceMock1 = createSurfaceMock(); + const surfaceMock2 = createSurfaceMock2(); + const options = { ...new Inputs.Draw.DrawBasicGeometryOptions(), updatable: true }; + + const res = draw.drawAny({ entity: [surfaceMock1], options }); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.verbSurfaces); + + const res2 = draw.drawAny({ entity: [surfaceMock2], group: res }); + expect(res.name).toEqual(res2.name); + }); + + it("should update tag when group has tag type via spy", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tag, options: { updatable: true } }; + const drawTagSpy = jest.spyOn(tag, "drawTag").mockReturnValue(mockGroup as any); + + const tag2: Inputs.Tag.TagDto = { text: "Tag 2", position: [1, 1, 1], colour: "#00ff00", size: 2, adaptDepth: false }; + + const res = draw.drawAny({ entity: tag2, group: mockGroup }); + expect(drawTagSpy).toHaveBeenCalled(); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.tag); + drawTagSpy.mockRestore(); + }); + + it("should update tags when group has tags type via spy", () => { + const mockGroup = new Group(); + mockGroup.userData = { type: Inputs.Draw.drawingTypes.tags, options: { updatable: true } }; + const drawTagsSpy = jest.spyOn(tag, "drawTags").mockReturnValue(mockGroup as any); + + const tags2: Inputs.Tag.TagDto[] = [{ text: "Tag B", position: [1, 1, 1], colour: "#00ff00", size: 2, adaptDepth: false }]; + + const res = draw.drawAny({ entity: tags2, group: mockGroup }); + expect(drawTagsSpy).toHaveBeenCalled(); + expect(res.userData.type).toBe(Inputs.Draw.drawingTypes.tags); + drawTagsSpy.mockRestore(); + }); + + it("should return undefined when group userData type is unknown", () => { + const mockGroup = { + userData: { type: "unknownType" }, + } as any; + const res = draw.drawAny({ entity: [1, 2, 3], group: mockGroup }); + expect(res).toBeUndefined(); + }); + }); }); diff --git a/packages/dev/threejs/lib/api/draw-helper.test.ts b/packages/dev/threejs/lib/api/draw-helper.test.ts index 376883e4..1dd3336c 100644 --- a/packages/dev/threejs/lib/api/draw-helper.test.ts +++ b/packages/dev/threejs/lib/api/draw-helper.test.ts @@ -1,29 +1,1167 @@ -import { GeometryHelper, MathBitByBit, Vector } from "@bitbybit-dev/base"; -import { Context } from "./context"; import { DrawHelper } from "./draw-helper"; -import { JSCADWorkerManager, JSCADText } from "@bitbybit-dev/jscad-worker"; +import { Context } from "./context"; +import * as Inputs from "./inputs/inputs"; +import { JSCADText, JSCADWorkerManager } from "@bitbybit-dev/jscad-worker"; import { ManifoldWorkerManager } from "@bitbybit-dev/manifold-worker"; -import { OCCTWorkerManager } from "@bitbybit-dev/occt-worker/lib"; -import { Scene } from "three"; +import { OCCTWorkerManager } from "@bitbybit-dev/occt-worker"; +import { Vector } from "@bitbybit-dev/base"; +import * as THREEJS from "three"; -describe("Draw unit tests", () => { +describe("DrawHelper unit tests", () => { let drawHelper: DrawHelper; + let mockContext: Context; + let mockSolidText: JSCADText; + let mockVector: Vector; + let mockJscadWorkerManager: JSCADWorkerManager; + let mockManifoldWorkerManager: ManifoldWorkerManager; + let mockOccWorkerManager: OCCTWorkerManager; + let mockScene: THREEJS.Scene; + + beforeEach(() => { + mockScene = new THREEJS.Scene(); + mockContext = { + scene: mockScene + } as Context; + + mockSolidText = { + createVectorText: jest.fn().mockResolvedValue([]) + } as unknown as JSCADText; + + mockVector = { + add: jest.fn().mockReturnValue([0, 0, 0]) + } as unknown as Vector; + + mockJscadWorkerManager = { + genericCallToWorkerPromise: jest.fn().mockResolvedValue({ + positions: [], + normals: [], + indices: [], + transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] + }) + } as unknown as JSCADWorkerManager; + + mockManifoldWorkerManager = { + genericCallToWorkerPromise: jest.fn().mockResolvedValue({ + positions: [], + normals: [], + indices: [] + }) + } as unknown as ManifoldWorkerManager; + + mockOccWorkerManager = { + genericCallToWorkerPromise: jest.fn().mockResolvedValue({ + faceList: [], + edgeList: [], + pointsList: [] + }) + } as unknown as OCCTWorkerManager; + + drawHelper = new DrawHelper( + mockContext, + mockSolidText, + mockVector, + mockJscadWorkerManager, + mockManifoldWorkerManager, + mockOccWorkerManager + ); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("drawPoint", () => { + it("should draw a point with default options", () => { + const inputs = new Inputs.Point.DrawPointDto( + [1, 2, 3], + 1, + 0.5, + "#ff0000", + false + ); + + const result = drawHelper.drawPoint(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(result.name).toContain("pointMesh"); + }); + + it("should draw a point with array of colours", () => { + const inputs = new Inputs.Point.DrawPointDto( + [0, 0, 0], + 0.5, + 1, + ["#ff0000", "#00ff00"] + ); + + const result = drawHelper.drawPoint(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + + it("should update existing point mesh when updatable is true", () => { + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingPointMesh"; + // Add a child InstancedMesh to simulate existing point + const geometry = new THREEJS.SphereGeometry(0.5); + const material = new THREEJS.MeshBasicMaterial(); + const instancedMesh = new THREEJS.InstancedMesh(geometry, material, 1); + instancedMesh.userData = { index: 0 }; + existingMesh.add(instancedMesh); + + const inputs = new Inputs.Point.DrawPointDto( + [5, 5, 5], + 1, + 0.5, + "#0000ff", + true, + existingMesh + ); + + const result = drawHelper.drawPoint(inputs); + + expect(result).toBe(existingMesh); + }); + }); + + describe("drawPoints", () => { + it("should draw multiple points", () => { + const inputs = new Inputs.Point.DrawPointsDto( + [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + 1, + 0.3, + "#ff0000" + ); + + const result = drawHelper.drawPoints(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(result.name).toContain("pointsMesh"); + }); + + it("should draw points with per-point colours", () => { + const inputs = new Inputs.Point.DrawPointsDto( + [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + 1, + 0.3, + ["#ff0000", "#00ff00", "#0000ff"] + ); + + const result = drawHelper.drawPoints(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + + it("should handle mismatched colour array length", () => { + const inputs = new Inputs.Point.DrawPointsDto( + [[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]], + 1, + 0.3, + ["#ff0000", "#00ff00"] // Only 2 colours for 4 points + ); + + const result = drawHelper.drawPoints(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + + it("should update existing points mesh when updatable is true with same point count", () => { + // First create a mesh + const firstInputs = new Inputs.Point.DrawPointsDto( + [[0, 0, 0], [1, 1, 1]], + 1, + 0.3, + "#ff0000" + ); + const existingMesh = drawHelper.drawPoints(firstInputs); + + // Now update with new positions + const updateInputs = new Inputs.Point.DrawPointsDto( + [[5, 5, 5], [6, 6, 6]], + 1, + 0.3, + "#ff0000", + true, + existingMesh + ); + + const result = drawHelper.drawPoints(updateInputs); + + expect(result).toBeDefined(); + }); + + it("should recreate points mesh when point count changes during update", () => { + // First create a mesh + const firstInputs = new Inputs.Point.DrawPointsDto( + [[0, 0, 0], [1, 1, 1]], + 1, + 0.3, + "#ff0000" + ); + const existingMesh = drawHelper.drawPoints(firstInputs); + + // Now update with different point count + const updateInputs = new Inputs.Point.DrawPointsDto( + [[5, 5, 5], [6, 6, 6], [7, 7, 7]], + 1, + 0.3, + "#ff0000", + true, + existingMesh + ); + + const result = drawHelper.drawPoints(updateInputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + }); + + describe("drawPolylineClose", () => { + it("should draw a polyline", () => { + const polylineData = { + points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] as Inputs.Base.Point3[], + isClosed: false + }; + const inputs = new Inputs.Polyline.DrawPolylineDto( + polylineData, + 1, + "#00ff00", + 2 + ); + + const result = drawHelper.drawPolylineClose(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(result.name).toContain("polyline"); + }); + + it("should close the polyline when isClosed is true", () => { + const polylineData = { + points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] as Inputs.Base.Point3[], + isClosed: true + }; + const inputs = new Inputs.Polyline.DrawPolylineDto( + polylineData, + 1, + "#00ff00", + 2 + ); + + const result = drawHelper.drawPolylineClose(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + + it("should update existing polyline mesh when updatable is true", () => { + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingPolyline"; + const lineGeom = new THREEJS.BufferGeometry(); + const lineMat = new THREEJS.LineBasicMaterial(); + existingMesh.add(new THREEJS.LineSegments(lineGeom, lineMat)); + + const polylineData = { + points: [[0, 0, 0], [2, 2, 2]] as Inputs.Base.Point3[], + isClosed: false + }; + const inputs = new Inputs.Polyline.DrawPolylineDto( + polylineData, + 1, + "#0000ff", + 3, + true, + existingMesh + ); + + const result = drawHelper.drawPolylineClose(inputs); + + expect(result).toBeDefined(); + }); + }); + + describe("drawPolylinesWithColours", () => { + it("should draw multiple polylines", () => { + const polylinesData = [ + { points: [[0, 0, 0], [1, 0, 0]] as Inputs.Base.Point3[], isClosed: false }, + { points: [[2, 0, 0], [3, 0, 0]] as Inputs.Base.Point3[], isClosed: false } + ]; + const inputs = new Inputs.Polyline.DrawPolylinesDto( + polylinesData, + 1, + "#ff0000", + 2 + ); + + const result = drawHelper.drawPolylinesWithColours(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(result.name).toContain("polylines"); + }); + + it("should handle polylines with assigned colors", () => { + const polylinesData = [ + { points: [[0, 0, 0], [1, 0, 0]] as Inputs.Base.Point3[], isClosed: false, color: [1, 0, 0] as [number, number, number] }, + { points: [[2, 0, 0], [3, 0, 0]] as Inputs.Base.Point3[], isClosed: false } + ]; + const inputs = new Inputs.Polyline.DrawPolylinesDto( + polylinesData, + 1, + "#00ff00", + 2 + ); + + const result = drawHelper.drawPolylinesWithColours(inputs); + + expect(result).toBeDefined(); + }); + + it("should handle closed polylines", () => { + const polylinesData = [ + { points: [[0, 0, 0], [1, 0, 0], [0.5, 1, 0]] as Inputs.Base.Point3[], isClosed: true } + ]; + const inputs = new Inputs.Polyline.DrawPolylinesDto( + polylinesData, + 1, + "#ff0000", + 2 + ); + + const result = drawHelper.drawPolylinesWithColours(inputs); + + expect(result).toBeDefined(); + }); + + it("should update existing polylines mesh when updatable is true", () => { + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingPolylines"; + const lineGeom = new THREEJS.BufferGeometry(); + const lineMat = new THREEJS.LineBasicMaterial(); + const lineSegments = new THREEJS.LineSegments(lineGeom, lineMat); + lineSegments.userData = { linesForRenderLengths: "2,2" }; + existingMesh.add(lineSegments); + + const polylinesData = [ + { points: [[0, 0, 0], [5, 5, 5]] as Inputs.Base.Point3[], isClosed: false }, + { points: [[6, 6, 6], [7, 7, 7]] as Inputs.Base.Point3[], isClosed: false } + ]; + const inputs = new Inputs.Polyline.DrawPolylinesDto( + polylinesData, + 1, + "#0000ff", + 3, + true, + existingMesh + ); + + const result = drawHelper.drawPolylinesWithColours(inputs); + + expect(result).toBeDefined(); + }); + }); + + describe("drawCurve", () => { + it("should draw a curve", () => { + const mockCurve = { + tessellate: jest.fn().mockReturnValue([ + [0, 0, 0], [0.5, 0.5, 0], [1, 1, 0] + ] as Inputs.Base.Point3[]) + }; + const inputs = new Inputs.Verb.DrawCurveDto( + mockCurve, + 1, + "#ff0000", + 2 + ); + + const result = drawHelper.drawCurve(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(mockCurve.tessellate).toHaveBeenCalled(); + }); + + it("should update existing curve mesh when updatable is true", () => { + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingCurve"; + const lineGeom = new THREEJS.BufferGeometry(); + const lineMat = new THREEJS.LineBasicMaterial(); + existingMesh.add(new THREEJS.LineSegments(lineGeom, lineMat)); + + const mockCurve = { + tessellate: jest.fn().mockReturnValue([ + [0, 0, 0], [1, 1, 1], [2, 2, 2] + ] as Inputs.Base.Point3[]) + }; + const inputs = new Inputs.Verb.DrawCurveDto( + mockCurve, + 0.8, + "#00ff00", + 3, + true, + existingMesh + ); + + const result = drawHelper.drawCurve(inputs); + + expect(result).toBeDefined(); + }); + }); + + describe("drawCurves", () => { + it("should draw multiple curves", () => { + const mockCurves = [ + { tessellate: jest.fn().mockReturnValue([[0, 0, 0], [1, 1, 1]] as Inputs.Base.Point3[]) }, + { tessellate: jest.fn().mockReturnValue([[2, 2, 2], [3, 3, 3]] as Inputs.Base.Point3[]) } + ]; + const inputs = new Inputs.Verb.DrawCurvesDto( + mockCurves, + 1, + "#ff0000", + 2 + ); + + const result = drawHelper.drawCurves(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + }); + + describe("drawSurface", () => { + it("should draw a surface", () => { + const mockSurface = { + tessellate: jest.fn().mockReturnValue({ + faces: [ + [0, 1, 2] + ], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + const inputs = new Inputs.Verb.DrawSurfaceDto( + mockSurface, + 1, + "#ff0000", + false, + false + ); + + const result = drawHelper.drawSurface(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(mockSurface.tessellate).toHaveBeenCalled(); + }); + + it("should handle hidden surfaces", () => { + const mockSurface = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + const inputs = new Inputs.Verb.DrawSurfaceDto( + mockSurface, + 1, + "#0000ff", + false, + true // hidden + ); + + const result = drawHelper.drawSurface(inputs); + + expect(result).toBeDefined(); + expect(result.visible).toBe(false); + }); + + it("should update existing surface mesh when updatable is true", () => { + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingSurface"; + + const mockSurface = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + const inputs = new Inputs.Verb.DrawSurfaceDto( + mockSurface, + 0.5, + "#00ff00", + true, + false, + existingMesh + ); + + const result = drawHelper.drawSurface(inputs); + + expect(result).toBeDefined(); + }); + + it("should handle array of colours and use first colour", () => { + const mockSurface = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + const inputs = new Inputs.Verb.DrawSurfaceDto( + mockSurface, + 1, + ["#ff0000", "#00ff00", "#0000ff"], + false, + false + ); + + const result = drawHelper.drawSurface(inputs); + + expect(result).toBeDefined(); + }); + }); + + describe("drawSurfacesMultiColour", () => { + it("should draw multiple surfaces with different colours", () => { + const mockSurface1 = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + const mockSurface2 = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[2, 0, 0], [3, 0, 0], [2, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + + const inputs = new Inputs.Verb.DrawSurfacesColoursDto( + [mockSurface1, mockSurface2], + ["#ff0000", "#00ff00"], + 1, + false, + false + ); + + const result = drawHelper.drawSurfacesMultiColour(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(result.children.length).toBe(2); + }); + + it("should use first colour when more surfaces than colours", () => { + const mockSurface = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + + const inputs = new Inputs.Verb.DrawSurfacesColoursDto( + [mockSurface, mockSurface, mockSurface], + ["#ff0000"], // Only one colour for 3 surfaces + 1, + false, + false + ); + + const result = drawHelper.drawSurfacesMultiColour(inputs); + + expect(result).toBeDefined(); + expect(result.children.length).toBe(3); + }); + + it("should use string colour for all surfaces when not array", () => { + const mockSurface = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + + const inputs = new Inputs.Verb.DrawSurfacesColoursDto( + [mockSurface, mockSurface], + ["#ff0000"], // Array colour + 1, + false, + false + ); + + const result = drawHelper.drawSurfacesMultiColour(inputs); - beforeAll(async () => { - const context = new Context(); - const jscadWorkerManager = new JSCADWorkerManager(); - const occtWorkerManager = new OCCTWorkerManager(); - const manifoldWorkerManager = new ManifoldWorkerManager(); + expect(result).toBeDefined(); + expect(result.children.length).toBe(2); + }); - const solidText = new JSCADText(jscadWorkerManager); - const math = new MathBitByBit(); - const geometryHelper = new GeometryHelper(); - const vector = new Vector(math, geometryHelper); - drawHelper = new DrawHelper(context, solidText, vector, jscadWorkerManager, manifoldWorkerManager, occtWorkerManager); - context.scene = new Scene(); + it("should update existing surfaces mesh when updatable is true", () => { + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingSurfacesMesh"; + + const mockSurface = { + tessellate: jest.fn().mockReturnValue({ + faces: [[0, 1, 2]], + points: [[0, 0, 0], [1, 0, 0], [0, 1, 0]], + normals: [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + }) + }; + + const inputs = new Inputs.Verb.DrawSurfacesColoursDto( + [mockSurface], + ["#ff0000"], + 1, + true, + false, + existingMesh + ); + + const result = drawHelper.drawSurfacesMultiColour(inputs); + + expect(result).toBe(existingMesh); + }); + }); + + describe("drawSolidOrPolygonMesh", () => { + it("should draw JSCAD solid mesh", async () => { + const mockMesh = { type: "solid" }; + const inputs = new Inputs.JSCAD.DrawSolidMeshDto( + mockMesh, + 1, + "#ff0000", + false, + false + ); + + const result = await drawHelper.drawSolidOrPolygonMesh(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(mockJscadWorkerManager.genericCallToWorkerPromise).toHaveBeenCalledWith("shapeToMesh", expect.anything()); + }); + + it("should handle mesh with baked-in color", async () => { + const mockMesh = { type: "solid", color: [1, 0, 0, 1] }; + const inputs = new Inputs.JSCAD.DrawSolidMeshDto( + mockMesh, + 1, + "#0000ff", + false, + false + ); + + const result = await drawHelper.drawSolidOrPolygonMesh(inputs); + + expect(result).toBeDefined(); + }); + + it("should handle hidden mesh", async () => { + (mockJscadWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue({ + positions: [0, 0, 0, 1, 0, 0, 0, 1, 0], + normals: [0, 0, 1, 0, 0, 1, 0, 0, 1], + indices: [0, 1, 2], + transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] + }); + + const mockMesh = { type: "solid" }; + const inputs = new Inputs.JSCAD.DrawSolidMeshDto( + mockMesh, + 1, + "#ff0000", + false, + true // hidden + ); + + const result = await drawHelper.drawSolidOrPolygonMesh(inputs); + + expect(result).toBeDefined(); + expect(result.visible).toBe(false); + }); + + it("should update existing mesh when updatable is true", async () => { + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingJscadMesh"; + + const mockMesh = { type: "solid" }; + const inputs = new Inputs.JSCAD.DrawSolidMeshDto( + mockMesh, + 0.5, + "#00ff00", + true, + false, + existingMesh + ); + + const result = await drawHelper.drawSolidOrPolygonMesh(inputs); + + expect(result).toBe(existingMesh); + }); + + it("should use array first colour when colours is array", async () => { + const mockMesh = { type: "solid" }; + const inputs = new Inputs.JSCAD.DrawSolidMeshDto( + mockMesh, + 1, + ["#ff0000", "#00ff00"], + false, + false + ); + + const result = await drawHelper.drawSolidOrPolygonMesh(inputs); + + expect(result).toBeDefined(); + }); + }); + + describe("drawSolidOrPolygonMeshes", () => { + it("should draw multiple JSCAD meshes", async () => { + (mockJscadWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + { positions: [0, 0, 0], normals: [0, 0, 1], indices: [0], transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + { positions: [1, 0, 0], normals: [0, 0, 1], indices: [0], transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] } + ]); + + const mockMeshes = [{ type: "solid" }, { type: "solid" }]; + const inputs = new Inputs.JSCAD.DrawSolidMeshesDto( + mockMeshes, + 1, + "#ff0000", + false, + false + ); + + const result = await drawHelper.drawSolidOrPolygonMeshes(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + + it("should handle meshes with baked colours", async () => { + (mockJscadWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + { positions: [0, 0, 0], normals: [0, 0, 1], indices: [0], transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], color: [1, 0, 0] }, + { positions: [1, 0, 0], normals: [0, 0, 1], indices: [0], transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] } + ]); + + const mockMeshes = [{ type: "solid" }, { type: "solid" }]; + const inputs = new Inputs.JSCAD.DrawSolidMeshesDto( + mockMeshes, + 1, + "#0000ff", + false, + false + ); + + const result = await drawHelper.drawSolidOrPolygonMeshes(inputs); + + expect(result).toBeDefined(); + }); + + it("should handle array of colours matching meshes count", async () => { + (mockJscadWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + { positions: [0, 0, 0], normals: [0, 0, 1], indices: [0], transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + { positions: [1, 0, 0], normals: [0, 0, 1], indices: [0], transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] } + ]); + + const mockMeshes = [{ type: "solid" }, { type: "solid" }]; + const inputs = new Inputs.JSCAD.DrawSolidMeshesDto( + mockMeshes, + 1, + ["#ff0000", "#00ff00"], // Matching colours + false, + false + ); + + const result = await drawHelper.drawSolidOrPolygonMeshes(inputs); + + expect(result).toBeDefined(); + }); + + it("should update existing mesh when updatable is true", async () => { + (mockJscadWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + { positions: [0, 0, 0], normals: [0, 0, 1], indices: [0], transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] } + ]); + + const existingMesh = new THREEJS.Group(); + existingMesh.name = "existingMeshes"; + + const mockMeshes = [{ type: "solid" }]; + const inputs = new Inputs.JSCAD.DrawSolidMeshesDto( + mockMeshes, + 1, + "#ff0000", + true, + false, + existingMesh + ); + + const result = await drawHelper.drawSolidOrPolygonMeshes(inputs); + + expect(result).toBe(existingMesh); + }); }); - it("should be initialised", () => { - expect(drawHelper).toBeDefined(); + describe("drawShape (OCCT)", () => { + it("should draw OCCT shape with faces", async () => { + (mockOccWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue({ + faceList: [ + { vertex_coord: [0, 0, 0, 1, 0, 0, 0, 1, 0], normal_coord: [0, 0, 1, 0, 0, 1, 0, 0, 1], tri_indexes: [0, 1, 2] } + ], + edgeList: [], + pointsList: [] + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.OCCT.DrawShapeDto() as any; + inputs.shape = { hash: "abc123", type: "solid" }; + inputs.drawFaces = true; + inputs.drawEdges = false; + inputs.drawVertices = false; + inputs.faceColour = "#ff0000"; + inputs.faceOpacity = 1; + + const result = await drawHelper.drawShape(inputs as Inputs.OCCT.DrawShapeDto); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(mockOccWorkerManager.genericCallToWorkerPromise).toHaveBeenCalledWith("shapeToMesh", expect.anything()); + }); + + it("should draw OCCT shape with edges", async () => { + (mockOccWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue({ + faceList: [], + edgeList: [ + { vertex_coord: [0, 0, 0, 1, 0, 0], edge_index: 0 } + ], + pointsList: [] + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.OCCT.DrawShapeDto() as any; + inputs.shape = { hash: "abc123", type: "edge" }; + inputs.drawFaces = false; + inputs.drawEdges = true; + inputs.drawVertices = false; + inputs.edgeColour = "#00ff00"; + inputs.edgeWidth = 2; + inputs.edgeOpacity = 1; + + const result = await drawHelper.drawShape(inputs as Inputs.OCCT.DrawShapeDto); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + + it("should draw OCCT shape with vertices", async () => { + (mockOccWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue({ + faceList: [], + edgeList: [], + pointsList: [[0, 0, 0], [1, 1, 1]] + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.OCCT.DrawShapeDto() as any; + inputs.shape = { hash: "abc123", type: "vertex" }; + inputs.drawFaces = false; + inputs.drawEdges = false; + inputs.drawVertices = true; + inputs.vertexColour = "#0000ff"; + inputs.vertexSize = 0.1; + + const result = await drawHelper.drawShape(inputs as Inputs.OCCT.DrawShapeDto); + + expect(result).toBeDefined(); + }); + }); + + describe("drawShapes (OCCT)", () => { + it("should draw multiple OCCT shapes", async () => { + (mockOccWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + { faceList: [{ vertex_coord: [0, 0, 0, 1, 0, 0, 0, 1, 0], normal_coord: [0, 0, 1, 0, 0, 1, 0, 0, 1], tri_indexes: [0, 1, 2] }], edgeList: [], pointsList: [] }, + { faceList: [{ vertex_coord: [2, 0, 0, 3, 0, 0, 2, 1, 0], normal_coord: [0, 0, 1, 0, 0, 1, 0, 0, 1], tri_indexes: [0, 1, 2] }], edgeList: [], pointsList: [] } + ]); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.OCCT.DrawShapesDto() as any; + inputs.shapes = [ + { hash: "abc123", type: "solid" }, + { hash: "def456", type: "solid" } + ]; + inputs.drawFaces = true; + inputs.drawEdges = false; + inputs.faceColour = "#ff0000"; + inputs.faceOpacity = 1; + + const result = await drawHelper.drawShapes(inputs as Inputs.OCCT.DrawShapesDto); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(mockOccWorkerManager.genericCallToWorkerPromise).toHaveBeenCalledWith("shapesToMeshes", expect.anything()); + }); + }); + + describe("drawManifoldOrCrossSection", () => { + it("should draw manifold or cross section", async () => { + // handleDecomposedManifold expects vertProperties and triVerts + (mockManifoldWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue({ + vertProperties: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]), + triVerts: new Uint32Array([0, 1, 2]) + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.Manifold.DrawManifoldOrCrossSectionDto() as any; + inputs.manifoldOrCrossSection = { hash: "mf123", type: "manifold" }; + inputs.faceColour = "#ff0000"; + inputs.faceOpacity = 1; + + const result = await drawHelper.drawManifoldOrCrossSection(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(mockManifoldWorkerManager.genericCallToWorkerPromise).toHaveBeenCalledWith("decomposeManifoldOrCrossSection", expect.anything()); + }); + + it("should return undefined when triVerts is empty", async () => { + (mockManifoldWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue({ + vertProperties: new Float32Array([]), + triVerts: new Uint32Array([]) + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.Manifold.DrawManifoldOrCrossSectionDto() as any; + inputs.manifoldOrCrossSection = { hash: "mf123", type: "manifold" }; + + const result = await drawHelper.drawManifoldOrCrossSection(inputs); + + expect(result).toBeUndefined(); + }); + + it("should handle cross section polygons", async () => { + // When decomposed mesh is 2D polygons instead of 3D mesh + (mockManifoldWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + [[0, 0], [1, 0], [1, 1], [0, 1]] as Inputs.Base.Vector2[] + ]); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.Manifold.DrawManifoldOrCrossSectionDto() as any; + inputs.manifoldOrCrossSection = { hash: "cs123", type: "crossSection" }; + inputs.crossSectionColour = "#00ff00"; + inputs.crossSectionOpacity = 1; + inputs.crossSectionWidth = 2; + + const result = await drawHelper.drawManifoldOrCrossSection(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + }); + + describe("drawManifoldsOrCrossSections", () => { + it("should draw multiple manifolds", async () => { + (mockManifoldWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + { vertProperties: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]), triVerts: new Uint32Array([0, 1, 2]) }, + { vertProperties: new Float32Array([1, 0, 0, 2, 0, 0, 1, 1, 0]), triVerts: new Uint32Array([0, 1, 2]) } + ]); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.Manifold.DrawManifoldsOrCrossSectionsDto() as any; + inputs.manifoldsOrCrossSections = [ + { hash: "mf123", type: "manifold" }, + { hash: "mf456", type: "manifold" } + ]; + inputs.faceColour = "#ff0000"; + + const result = await drawHelper.drawManifoldsOrCrossSections(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + + it("should filter out undefined meshes", async () => { + (mockManifoldWorkerManager.genericCallToWorkerPromise as jest.Mock).mockResolvedValue([ + { vertProperties: new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]), triVerts: new Uint32Array([0, 1, 2]) }, + { vertProperties: new Float32Array([]), triVerts: new Uint32Array([]) } // This will be filtered out + ]); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputs = new Inputs.Manifold.DrawManifoldsOrCrossSectionsDto() as any; + inputs.manifoldsOrCrossSections = [ + { hash: "mf123", type: "manifold" }, + { hash: "mf456", type: "manifold" } + ]; + inputs.faceColour = "#ff0000"; + + const result = await drawHelper.drawManifoldsOrCrossSections(inputs); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + }); + + describe("updatePointsInstances", () => { + it("should update positions of instanced meshes", () => { + const group = new THREEJS.Group(); + const geometry = new THREEJS.SphereGeometry(0.5); + const material = new THREEJS.MeshBasicMaterial(); + + const mesh1 = new THREEJS.InstancedMesh(geometry, material, 1); + mesh1.userData = { index: 0 }; + const mesh2 = new THREEJS.InstancedMesh(geometry, material, 1); + mesh2.userData = { index: 1 }; + + group.add(mesh1); + group.add(mesh2); + + const newPositions: Inputs.Base.Point3[] = [[5, 5, 5], [10, 10, 10]]; + + drawHelper.updatePointsInstances(group, newPositions); + + expect(mesh1.position.x).toBe(5); + expect(mesh1.position.y).toBe(5); + expect(mesh1.position.z).toBe(5); + expect(mesh2.position.x).toBe(10); + expect(mesh2.position.y).toBe(10); + expect(mesh2.position.z).toBe(10); + }); + }); + + describe("createOrUpdateSurfacesMesh", () => { + it("should create new surface mesh when group is undefined", () => { + const meshData = [{ + positions: [0, 0, 0, 1, 0, 0, 0, 1, 0], + normals: [0, 0, 1, 0, 0, 1, 0, 0, 1], + indices: [0, 1, 2] + }]; + const material = new THREEJS.MeshPhysicalMaterial({ color: "#ff0000" }); + + const result = drawHelper.createOrUpdateSurfacesMesh( + meshData, + undefined as unknown as THREEJS.Group, + false, + material, + true, + false + ); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(result.name).toContain("surface"); + }); + + it("should update existing mesh when updatable is true", () => { + const existingGroup = new THREEJS.Group(); + existingGroup.name = "existingSurface"; + + const meshData = [{ + positions: [0, 0, 0, 1, 0, 0, 0, 1, 0], + normals: [0, 0, 1, 0, 0, 1, 0, 0, 1], + indices: [0, 1, 2] + }]; + const material = new THREEJS.MeshPhysicalMaterial({ color: "#00ff00" }); + + const result = drawHelper.createOrUpdateSurfacesMesh( + meshData, + existingGroup, + true, + material, + true, + false + ); + + expect(result).toBe(existingGroup); + expect(result.children.length).toBeGreaterThan(0); + }); + + it("should set group invisible when hidden is true", () => { + const meshData = [{ + positions: [0, 0, 0, 1, 0, 0, 0, 1, 0], + normals: [0, 0, 1, 0, 0, 1, 0, 0, 1], + indices: [0, 1, 2] + }]; + const material = new THREEJS.MeshPhysicalMaterial({ color: "#ff0000" }); + + const result = drawHelper.createOrUpdateSurfacesMesh( + meshData, + undefined as unknown as THREEJS.Group, + false, + material, + true, + true // hidden + ); + + expect(result.visible).toBe(false); + }); + + it("should handle mesh data with UVs", () => { + const meshData = [{ + positions: [0, 0, 0, 1, 0, 0, 0, 1, 0], + normals: [0, 0, 1, 0, 0, 1, 0, 0, 1], + indices: [0, 1, 2], + uvs: [0, 0, 1, 0, 0.5, 1] + }]; + const material = new THREEJS.MeshPhysicalMaterial({ color: "#ff0000" }); + + const result = drawHelper.createOrUpdateSurfacesMesh( + meshData, + undefined as unknown as THREEJS.Group, + false, + material, + true, + false + ); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + }); + }); + + describe("drawPolyline (internal)", () => { + it("should create new polyline when mesh is undefined", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [1, 1, 1], [2, 0, 0]]; + + const result = drawHelper.drawPolyline( + undefined as unknown as THREEJS.Group, + points, + false, + 2, + 1, + "#ff0000" + ); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(THREEJS.Group); + expect(result.name).toContain("polyline"); + }); + + it("should handle existing mesh with children", () => { + const existingMesh = new THREEJS.Group(); + const lineGeom = new THREEJS.BufferGeometry(); + const lineMat = new THREEJS.LineBasicMaterial(); + existingMesh.add(new THREEJS.LineSegments(lineGeom, lineMat)); + + const points: Inputs.Base.Point3[] = [[0, 0, 0], [5, 5, 5]]; + + const result = drawHelper.drawPolyline( + existingMesh, + points, + true, + 2, + 1, + "#00ff00" + ); + + expect(result).toBeDefined(); + }); }); }); diff --git a/packages/dev/threejs/lib/api/inputs/draw-inputs.test.ts b/packages/dev/threejs/lib/api/inputs/draw-inputs.test.ts new file mode 100644 index 00000000..443953c4 --- /dev/null +++ b/packages/dev/threejs/lib/api/inputs/draw-inputs.test.ts @@ -0,0 +1,432 @@ +import { Draw } from "./draw-inputs"; + +describe("Draw inputs unit tests", () => { + + describe("DrawAny constructor", () => { + + it("should create DrawAny with default values when no arguments provided", () => { + const drawAny = new Draw.DrawAny(); + expect(drawAny.entity).toBeUndefined(); + expect(drawAny.options).toBeUndefined(); + expect(drawAny.group).toBeUndefined(); + }); + + it("should create DrawAny with entity when entity is provided", () => { + const point: [number, number, number] = [1, 2, 3]; + const drawAny = new Draw.DrawAny(point); + expect(drawAny.entity).toEqual([1, 2, 3]); + expect(drawAny.options).toBeUndefined(); + }); + + it("should create DrawAny with entity and options when both are provided", () => { + const point: [number, number, number] = [1, 2, 3]; + const options = new Draw.DrawBasicGeometryOptions(); + const drawAny = new Draw.DrawAny(point, options); + expect(drawAny.entity).toEqual([1, 2, 3]); + expect(drawAny.options).toBe(options); + }); + + it("should create DrawAny with line entity", () => { + const line = { start: [0, 0, 0] as [number, number, number], end: [1, 1, 1] as [number, number, number] }; + const drawAny = new Draw.DrawAny(line); + expect(drawAny.entity).toEqual(line); + }); + + it("should create DrawAny with polyline entity", () => { + const polyline = { points: [[0, 0, 0], [1, 0, 0], [1, 1, 0]] as [number, number, number][] }; + const drawAny = new Draw.DrawAny(polyline); + expect(drawAny.entity).toEqual(polyline); + }); + + it("should create DrawAny with array of points", () => { + const points: [number, number, number][] = [[0, 0, 0], [1, 1, 1], [2, 2, 2]]; + const drawAny = new Draw.DrawAny(points); + expect(drawAny.entity).toEqual(points); + }); + + it("should create DrawAny with OCCT options", () => { + const point: [number, number, number] = [1, 2, 3]; + const options = new Draw.DrawOcctShapeOptions(); + const drawAny = new Draw.DrawAny(point, options); + expect(drawAny.options).toBeInstanceOf(Draw.DrawOcctShapeOptions); + }); + + it("should create DrawAny with Manifold options", () => { + const point: [number, number, number] = [1, 2, 3]; + const options = new Draw.DrawManifoldOrCrossSectionOptions(); + const drawAny = new Draw.DrawAny(point, options); + expect(drawAny.options).toBeInstanceOf(Draw.DrawManifoldOrCrossSectionOptions); + }); + }); + + describe("DrawBasicGeometryOptions constructor", () => { + + it("should create options with all default values when no arguments provided", () => { + const options = new Draw.DrawBasicGeometryOptions(); + expect(options.colours).toBe("#ff0000"); + expect(options.size).toBe(0.1); + expect(options.opacity).toBe(1); + expect(options.updatable).toBe(false); + expect(options.hidden).toBe(false); + }); + + it("should create options with custom colours when provided", () => { + const options = new Draw.DrawBasicGeometryOptions("#00ff00"); + expect(options.colours).toBe("#00ff00"); + expect(options.size).toBe(0.1); + expect(options.opacity).toBe(1); + expect(options.updatable).toBe(false); + expect(options.hidden).toBe(false); + }); + + it("should create options with array of colours when provided", () => { + const colours = ["#ff0000", "#00ff00", "#0000ff"]; + const options = new Draw.DrawBasicGeometryOptions(colours); + expect(options.colours).toEqual(colours); + }); + + it("should create options with custom size when provided", () => { + const options = new Draw.DrawBasicGeometryOptions(undefined, 0.5); + expect(options.colours).toBe("#ff0000"); + expect(options.size).toBe(0.5); + }); + + it("should create options with custom opacity when provided", () => { + const options = new Draw.DrawBasicGeometryOptions(undefined, undefined, 0.5); + expect(options.colours).toBe("#ff0000"); + expect(options.size).toBe(0.1); + expect(options.opacity).toBe(0.5); + }); + + it("should create options with updatable true when provided", () => { + const options = new Draw.DrawBasicGeometryOptions(undefined, undefined, undefined, true); + expect(options.updatable).toBe(true); + }); + + it("should create options with hidden true when provided", () => { + const options = new Draw.DrawBasicGeometryOptions(undefined, undefined, undefined, undefined, true); + expect(options.hidden).toBe(true); + }); + + it("should create options with all custom values", () => { + const options = new Draw.DrawBasicGeometryOptions("#00ff00", 0.5, 0.8, true, true); + expect(options.colours).toBe("#00ff00"); + expect(options.size).toBe(0.5); + expect(options.opacity).toBe(0.8); + expect(options.updatable).toBe(true); + expect(options.hidden).toBe(true); + }); + + it("should create options with zero opacity", () => { + const options = new Draw.DrawBasicGeometryOptions(undefined, undefined, 0); + expect(options.opacity).toBe(0); + }); + + it("should create options with zero size", () => { + const options = new Draw.DrawBasicGeometryOptions(undefined, 0); + expect(options.size).toBe(0); + }); + }); + + describe("DrawOcctShapeOptions constructor", () => { + + it("should create options with all default values when no arguments provided", () => { + const options = new Draw.DrawOcctShapeOptions(); + expect(options.faceOpacity).toBe(1); + expect(options.edgeOpacity).toBe(1); + expect(options.edgeColour).toBe("#ffffff"); + expect(options.faceColour).toBe("#ff0000"); + expect(options.vertexColour).toBe("#ffaaff"); + expect(options.faceMaterial).toBeUndefined(); + expect(options.edgeWidth).toBe(2); + expect(options.vertexSize).toBe(0.03); + expect(options.drawEdges).toBe(true); + expect(options.drawFaces).toBe(true); + expect(options.drawVertices).toBe(false); + expect(options.precision).toBe(0.01); + expect(options.drawEdgeIndexes).toBe(false); + expect(options.edgeIndexHeight).toBe(0.06); + expect(options.edgeIndexColour).toBe("#ff00ff"); + expect(options.drawFaceIndexes).toBe(false); + expect(options.faceIndexHeight).toBe(0.06); + expect(options.faceIndexColour).toBe("#0000ff"); + }); + + it("should create options with custom faceOpacity when provided", () => { + const options = new Draw.DrawOcctShapeOptions(0.5); + expect(options.faceOpacity).toBe(0.5); + expect(options.edgeOpacity).toBe(1); + }); + + it("should create options with custom edgeOpacity when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, 0.7); + expect(options.faceOpacity).toBe(1); + expect(options.edgeOpacity).toBe(0.7); + }); + + it("should create options with custom edgeColour when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, "#00ff00"); + expect(options.edgeColour).toBe("#00ff00"); + }); + + it("should create options with custom faceMaterial when provided", () => { + const material = { type: "custom" } as any; + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, material); + expect(options.faceMaterial).toBe(material); + }); + + it("should create options with custom faceColour when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, "#0000ff"); + expect(options.faceColour).toBe("#0000ff"); + }); + + it("should create options with custom edgeWidth when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, 5); + expect(options.edgeWidth).toBe(5); + }); + + it("should create options with drawEdges false when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, false); + expect(options.drawEdges).toBe(false); + }); + + it("should create options with drawFaces false when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, false); + expect(options.drawFaces).toBe(false); + }); + + it("should create options with drawVertices true when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true); + expect(options.drawVertices).toBe(true); + }); + + it("should create options with custom vertexColour when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "#123456"); + expect(options.vertexColour).toBe("#123456"); + }); + + it("should create options with custom vertexSize when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 0.1); + expect(options.vertexSize).toBe(0.1); + }); + + it("should create options with custom precision when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 0.001); + expect(options.precision).toBe(0.001); + }); + + it("should create options with drawEdgeIndexes true when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true); + expect(options.drawEdgeIndexes).toBe(true); + }); + + it("should create options with custom edgeIndexHeight when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 0.1); + expect(options.edgeIndexHeight).toBe(0.1); + }); + + it("should create options with custom edgeIndexColour when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "#abcdef"); + expect(options.edgeIndexColour).toBe("#abcdef"); + }); + + it("should create options with drawFaceIndexes true when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true); + expect(options.drawFaceIndexes).toBe(true); + }); + + it("should create options with custom faceIndexHeight when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 0.12); + expect(options.faceIndexHeight).toBe(0.12); + }); + + it("should create options with custom faceIndexColour when provided", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "#fedcba"); + expect(options.faceIndexColour).toBe("#fedcba"); + }); + + it("should create options with multiple custom values", () => { + const options = new Draw.DrawOcctShapeOptions(0.8, 0.9, "#111111", undefined, "#222222", 3, false, true, true, "#333333", 0.05, 0.02); + expect(options.faceOpacity).toBe(0.8); + expect(options.edgeOpacity).toBe(0.9); + expect(options.edgeColour).toBe("#111111"); + expect(options.faceColour).toBe("#222222"); + expect(options.edgeWidth).toBe(3); + expect(options.drawEdges).toBe(false); + expect(options.drawFaces).toBe(true); + expect(options.drawVertices).toBe(true); + expect(options.vertexColour).toBe("#333333"); + expect(options.vertexSize).toBe(0.05); + expect(options.precision).toBe(0.02); + }); + + it("should create options with zero faceOpacity", () => { + const options = new Draw.DrawOcctShapeOptions(0); + expect(options.faceOpacity).toBe(0); + }); + + it("should create options with zero edgeWidth", () => { + const options = new Draw.DrawOcctShapeOptions(undefined, undefined, undefined, undefined, undefined, 0); + expect(options.edgeWidth).toBe(0); + }); + }); + + describe("DrawManifoldOrCrossSectionOptions constructor", () => { + + it("should create options with all default values when no arguments provided", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(); + expect(options.faceOpacity).toBe(1); + expect(options.faceColour).toBe("#ff0000"); + expect(options.faceMaterial).toBeUndefined(); + expect(options.crossSectionColour).toBe("#ff00ff"); + expect(options.crossSectionWidth).toBe(2); + expect(options.crossSectionOpacity).toBeUndefined(); + expect(options.computeNormals).toBe(false); + }); + + it("should create options with custom faceOpacity when provided", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(0.5); + expect(options.faceOpacity).toBe(0.5); + }); + + it("should create options with custom faceMaterial when provided", () => { + const material = { type: "custom" } as any; + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, material); + expect(options.faceMaterial).toBe(material); + }); + + it("should create options with custom faceColour when provided", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, undefined, "#00ff00"); + expect(options.faceColour).toBe("#00ff00"); + }); + + it("should create options with custom crossSectionColour when provided", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, undefined, undefined, "#0000ff"); + expect(options.crossSectionColour).toBe("#0000ff"); + }); + + it("should create options with custom crossSectionWidth when provided", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, undefined, undefined, undefined, 5); + expect(options.crossSectionWidth).toBe(5); + }); + + it("should create options with custom crossSectionOpacity when provided", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, undefined, undefined, undefined, undefined, 0.7); + expect(options.crossSectionOpacity).toBe(0.7); + }); + + it("should create options with computeNormals true when provided", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, undefined, undefined, undefined, undefined, undefined, true); + expect(options.computeNormals).toBe(true); + }); + + it("should create options with multiple custom values", () => { + const material = { type: "custom" } as any; + const options = new Draw.DrawManifoldOrCrossSectionOptions(0.8, material, "#111111", "#222222", 4, 0.6, true); + expect(options.faceOpacity).toBe(0.8); + expect(options.faceMaterial).toBe(material); + expect(options.faceColour).toBe("#111111"); + expect(options.crossSectionColour).toBe("#222222"); + expect(options.crossSectionWidth).toBe(4); + expect(options.crossSectionOpacity).toBe(0.6); + expect(options.computeNormals).toBe(true); + }); + + it("should create options with zero faceOpacity", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(0); + expect(options.faceOpacity).toBe(0); + }); + + it("should create options with zero crossSectionWidth", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, undefined, undefined, undefined, 0); + expect(options.crossSectionWidth).toBe(0); + }); + + it("should create options with zero crossSectionOpacity", () => { + const options = new Draw.DrawManifoldOrCrossSectionOptions(undefined, undefined, undefined, undefined, undefined, 0); + expect(options.crossSectionOpacity).toBe(0); + }); + }); + + describe("drawingTypes enum", () => { + + it("should have point type", () => { + expect(Draw.drawingTypes.point).toBe(0); + }); + + it("should have points type", () => { + expect(Draw.drawingTypes.points).toBe(1); + }); + + it("should have line type", () => { + expect(Draw.drawingTypes.line).toBe(2); + }); + + it("should have lines type", () => { + expect(Draw.drawingTypes.lines).toBe(3); + }); + + it("should have node type", () => { + expect(Draw.drawingTypes.node).toBe(4); + }); + + it("should have nodes type", () => { + expect(Draw.drawingTypes.nodes).toBe(5); + }); + + it("should have polyline type", () => { + expect(Draw.drawingTypes.polyline).toBe(6); + }); + + it("should have polylines type", () => { + expect(Draw.drawingTypes.polylines).toBe(7); + }); + + it("should have verbCurve type", () => { + expect(Draw.drawingTypes.verbCurve).toBe(8); + }); + + it("should have verbCurves type", () => { + expect(Draw.drawingTypes.verbCurves).toBe(9); + }); + + it("should have verbSurface type", () => { + expect(Draw.drawingTypes.verbSurface).toBe(10); + }); + + it("should have verbSurfaces type", () => { + expect(Draw.drawingTypes.verbSurfaces).toBe(11); + }); + + it("should have jscadMesh type", () => { + expect(Draw.drawingTypes.jscadMesh).toBe(12); + }); + + it("should have jscadMeshes type", () => { + expect(Draw.drawingTypes.jscadMeshes).toBe(13); + }); + + it("should have occt type", () => { + expect(Draw.drawingTypes.occt).toBe(14); + }); + + it("should have occtShapes type", () => { + expect(Draw.drawingTypes.occtShapes).toBe(15); + }); + + it("should have tag type", () => { + expect(Draw.drawingTypes.tag).toBe(16); + }); + + it("should have tags type", () => { + expect(Draw.drawingTypes.tags).toBe(17); + }); + + it("should allow reverse lookup by value", () => { + expect(Draw.drawingTypes[0]).toBe("point"); + expect(Draw.drawingTypes[1]).toBe("points"); + expect(Draw.drawingTypes[14]).toBe("occt"); + expect(Draw.drawingTypes[17]).toBe("tags"); + }); + }); +}); From 338076f16ae4595a2646116d7e9c02ef4e9dc48b Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 15 Dec 2025 21:10:00 +0200 Subject: [PATCH 22/38] homepage of learn tuned up, festive decor tutorial --- .../modeling/christmas-decor/_category_.json | 10 + .../modeling/christmas-decor/star-ornament.md | 164 +++++ .../modeling/hollow-shapes/_category_.json | 2 +- docs/learn/hosting-and-cdn.md | 2 +- docs/src/components/Icons/index.tsx | 310 +++++++++ docs/src/pages/index.module.css | 597 +++++++++++++++++- docs/src/pages/index.tsx | 305 ++++++++- 7 files changed, 1364 insertions(+), 26 deletions(-) create mode 100644 docs/learn/code/common/occt/modeling/christmas-decor/_category_.json create mode 100644 docs/learn/code/common/occt/modeling/christmas-decor/star-ornament.md create mode 100644 docs/src/components/Icons/index.tsx diff --git a/docs/learn/code/common/occt/modeling/christmas-decor/_category_.json b/docs/learn/code/common/occt/modeling/christmas-decor/_category_.json new file mode 100644 index 00000000..ca2f83f4 --- /dev/null +++ b/docs/learn/code/common/occt/modeling/christmas-decor/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Festive Decor", + "position": 1, + "link": { + "type": "generated-index", + "title": "Festive Decor", + "description": "Learn how to model festive 3D printable decorations using OCCT.", + "slug": "/code/common/occt/modeling/festive-decor" + } +} \ No newline at end of file diff --git a/docs/learn/code/common/occt/modeling/christmas-decor/star-ornament.md b/docs/learn/code/common/occt/modeling/christmas-decor/star-ornament.md new file mode 100644 index 00000000..4fa8c066 --- /dev/null +++ b/docs/learn/code/common/occt/modeling/christmas-decor/star-ornament.md @@ -0,0 +1,164 @@ +--- +sidebar_position: 1 +title: Star Ornament +sidebar_label: Star Ornament +description: Create a simple 3D printable star ornament using polygon wire extrusion - perfect for tree toppers and holiday decorations. +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 + +🎄 Nothing says festive season quite like a beautiful star ornament. In this beginner-friendly tutorial, you'll learn how to create a classic 3D printable star that can grace the top of your tree or hang as a festive decoration. + +This tutorial introduces fundamental CAD concepts: creating polygon wires, applying fillets for smooth edges, and extruding 2D shapes into 3D solids. These techniques form the foundation for countless holiday decorations and beyond! + +## The Star Shape + +Our star begins as a simple 2D wire created using the `createStarWire` function. This generates a classic five-pointed star shape that we'll then fillet (round the corners) and extrude into a 3D solid. The fillet operation gives our star a softer, more elegant look while also making it safer to handle and easier to 3D print. + + + + + + + 552000010FALSE0.3TRUE0100.150.001TRUE#0000001Star Material#ffffff#00ffcc0.80.991FALSE2","version":"0.20.13","type":"blockly"}} + title="Star ornament" + /> + + + {\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = 5;\n starOptions.innerRadius = 2;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Apply 3D fillet to smooth all edges\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starSolid;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + title="Star ornament" + /> + + + +## Understanding the Code + +### Star Wire Creation +The `createStarWire` function generates our star shape with these key parameters: +- **numRays**: The number of star points (5 for a classic star) +- **outerRadius**: Distance from center to the tips +- **innerRadius**: Distance from center to the inner valleys +- **direction**: The normal vector (facing up in Y direction) + +### Filleting for Polish +We apply two rounds of filleting: +1. **2D Fillet**: Rounds the corners of the wire before extrusion, creating smooth transitions between the star's points +2. **3D Fillet**: Applied after extrusion to round all edges, making the ornament pleasant to touch and easier to print + +### Extrusion +The extrusion transforms our 2D face into a 3D solid. The direction vector `[0, 1, 0]` creates a 1-unit thick star. Adjust this for thicker or thinner ornaments! + +## Adding a Hole + +To hang your star ornament on a tree, we need to add a hole at the top. We'll use a **boolean difference** operation to cut a cylinder through the top point of the star. This is a fundamental CAD technique that allows you to subtract one shape from another. + +The cylinder is positioned at the top of the star (at the outer radius on the Z-axis) and oriented along the Y-axis to create a clean hole through the thickness of the ornament. + + + + + + + outerRadiusouterRadius5.10000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.150.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001","version":"0.20.12","type":"blockly"}} + title="Star ornament with hanging hole" + /> + + + {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.12","type":"typescript"}} + title="Star ornament with hanging hole" + /> + + + +### Understanding the Hanging Hole + +The hanging hole is created using these key concepts: + +1. **Cylinder Creation**: We create a cylinder using `createCylinder` with: + - **center**: `[0, -0.5, 4.2]` - positioned at the top star point (Z = 4.2 is slightly inside the outer radius of 5) + - **radius**: 0.4 - small enough to leave material around the hole + - **height**: 2 - extends through the full thickness of the star + +2. **Boolean Difference**: The `difference` operation subtracts the cylinder from the star, creating a clean hole. This is one of the most powerful CAD operations, allowing you to: + - Create holes and cutouts + - Carve complex shapes + - Remove material precisely + +## Exporting to STL for 3D Printing + +Now that we have our star ornament ready, let's export it as an STL file for 3D printing! The `saveShapeStl` function converts our OCCT shape into a binary STL file that can be opened in any slicer software. + + + + + + + outerRadiusfinalStarouterRadius5.1finalStar0000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.15finalStar0.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001finalStarstar.stl0.001FALSETRUE","version":"0.20.12","type":"blockly"}} + title="Star ornament with STL export" + /> + + + {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n\n // Export to STL for 3D printing\n const stlOptions = new SaveStlDto();\n stlOptions.shape = finalStar;\n stlOptions.fileName = \"star.stl\";\n stlOptions.precision = 0.001;\n stlOptions.adjustYtoZ = false;\n stlOptions.tryDownload = true;\n stlOptions.binary = true;\n await io.saveShapeStl(stlOptions);\n}\n\nstart();","version":"0.20.12","type":"typescript"}} + title="Star ornament with STL export" + /> + + + +### Understanding STL Export + +The `saveShapeStl` function exports your 3D model with these options: +- **fileName**: The name of the downloaded file (e.g., `star.stl`) +- **precision**: Controls mesh quality (0.001 is high quality) +- **adjustYtoZ**: Set to `false` to keep the Y-up orientation, or `true` for Z-up (some 3D printers prefer this) +- **tryDownload**: Automatically triggers the file download +- **binary**: Uses binary STL format (smaller file size than ASCII) + +## Making It Your Own 🎁 + +Try these festive variations: +- **Six-pointed Star**: Change `numRays` to 6 for a Star of David style +- **Chubby Star**: Increase `innerRadius` closer to `outerRadius` +- **Tree Topper**: Increase the thickness for a substantial tree-top star + +## 3D Printing Tips + +Your star ornament is print-ready! For best results: +- **Layer Height**: 0.2mm works great +- **Infill**: 15-20% is sufficient for lightweight ornaments +- **Material**: PLA in gold, silver, or festive red looks stunning +- **Support**: Usually not needed when printed flat + +Happy holidays and happy modeling! 🌟 diff --git a/docs/learn/code/common/occt/modeling/hollow-shapes/_category_.json b/docs/learn/code/common/occt/modeling/hollow-shapes/_category_.json index 9a1f9f54..0f66c67f 100644 --- a/docs/learn/code/common/occt/modeling/hollow-shapes/_category_.json +++ b/docs/learn/code/common/occt/modeling/hollow-shapes/_category_.json @@ -4,7 +4,7 @@ "link": { "type": "generated-index", "title": "Hollow Shapes", - "description": "While using boolean operations to cut out holes might be straightforward, there are several nuances to consider.", + "description": "How to create hollow shapes without using boolean operations while addressing common challenges.", "slug": "/code/common/occt/modeling/hollow-shapes" } } \ No newline at end of file diff --git a/docs/learn/hosting-and-cdn.md b/docs/learn/hosting-and-cdn.md index 6a4dd09f..02fc9951 100644 --- a/docs/learn/hosting-and-cdn.md +++ b/docs/learn/hosting-and-cdn.md @@ -3,7 +3,7 @@ sidebar_position: 10 title: Hosting Bitbybit Assets on Your Own CDN sidebar_label: Self-Hosting Assets description: Learn how to host Bitbybit assets on your own CDN infrastructure for improved reliability, performance, and control. -tags: [cdn, hosting, infrastructure, deployment, performance, assets] +tags: [cdn, hosting, deployment, performance, assets] --- import Admonition from '@theme/Admonition'; diff --git a/docs/src/components/Icons/index.tsx b/docs/src/components/Icons/index.tsx new file mode 100644 index 00000000..158a9239 --- /dev/null +++ b/docs/src/components/Icons/index.tsx @@ -0,0 +1,310 @@ +import React from "react"; + +interface IconProps { + size?: number; + color?: string; + className?: string; +} + +const defaultColor = "#f0cebb"; +const defaultSize = 48; + +export const RocketIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + +); + +export const CodeIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Code brackets */} + + + + {/* Decorative dots */} + + + +); + +export const RingIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Ring band - ellipse for 3D effect */} + + + {/* Ring thickness */} + + {/* Gem setting */} + + + {/* Gem facets */} + + + + {/* Gem top */} + + {/* Sparkles */} + + + + + +); + +export const CadIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + + + + +); + +export const ArtIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + +); + +export const PrinterIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* 3D Printer frame */} + + + + {/* Print bed */} + + {/* Extruder/nozzle */} + + + {/* Printed object (layer lines) */} + + + + {/* Vertical rails */} + + + +); + +export const GamepadIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + + +); + +export const SofaIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Sofa back */} + + {/* Armrests */} + + + {/* Seat cushion */} + + {/* Cushion detail */} + + {/* Legs */} + + + {/* Back cushion details */} + + + + +); + +export const BuildingIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + + + + +); + +export const OcctIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + +); + +export const BabylonIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + + +); + +export const JscadIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + +); + +export const NurbsIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + + + + +); + +export const OpenSourceIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Open hands / sharing symbol */} + + {/* Branch/fork symbol */} + + + + {/* Connecting lines */} + + + + {/* Center node */} + + +); + +export const PlaygroundIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + + +); + +export const BookIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Book cover */} + + {/* Book spine */} + + + {/* Pages */} + + + + + {/* Bookmark */} + + {/* Bottom page edge */} + + +); + +export const TargetIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + + + +); + +export const BlocksIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Puzzle-like blocks for Blockly */} + + + + + {/* Connection notches */} + + + + + + + +); + +export const CubeIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + + + + + + +); + +export const UserPlusIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* User silhouette */} + + + {/* Plus sign */} + + + + +); + +export const MonacoIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Editor window */} + + + {/* Window controls */} + + + {/* Code lines */} + + + + {/* Cursor */} + + {/* Line numbers */} + + + + +); diff --git a/docs/src/pages/index.module.css b/docs/src/pages/index.module.css index 9f71a5da..1a8fd8dd 100644 --- a/docs/src/pages/index.module.css +++ b/docs/src/pages/index.module.css @@ -1,23 +1,602 @@ /** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. + * Bitbybit Learning Portal - Homepage Styles */ -.heroBanner { - padding: 4rem 0; +/* ===== HERO SECTION ===== */ +.heroSection { + position: relative; + min-height: 90vh; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + background: linear-gradient(135deg, #1a1c1f 0%, #2a2d32 50%, #1a1c1f 100%); +} + +.heroBackground { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 0; +} + +.heroGrid { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + linear-gradient(rgba(240, 206, 187, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(240, 206, 187, 0.03) 1px, transparent 1px); + background-size: 60px 60px; + animation: gridMove 20s linear infinite; +} + +@keyframes gridMove { + 0% { transform: translate(0, 0); } + 100% { transform: translate(60px, 60px); } +} + +.heroContent { + position: relative; + z-index: 1; + text-align: center; + max-width: 900px; + padding: 2rem; +} + +.heroLogo { + margin-bottom: 1.5rem; +} + +.heroLogo img { + width: 80px; + height: 80px; + animation: float 3s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-10px); } +} + +.heroTitle { + font-size: 3.5rem; + font-weight: 800; + margin-bottom: 1.5rem; + color: #ffffff; + line-height: 1.2; +} + +.highlight { + color: #f0cebb; + position: relative; +} + +.highlight::after { + content: ''; + position: absolute; + bottom: -4px; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #f0cebb, #dc966e); + border-radius: 2px; +} + +.heroSubtitle { + font-size: 1.3rem; + color: rgba(255, 255, 255, 0.8); + margin-bottom: 2.5rem; + line-height: 1.6; +} + +.heroButtons { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; + margin-bottom: 3rem; +} + +.primaryButton { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 1rem 2rem; + background: linear-gradient(135deg, #f0cebb 0%, #dc966e 100%); + color: #1a1c1f; + font-weight: 700; + font-size: 1.1rem; + border-radius: 50px; + text-decoration: none; + transition: all 0.3s ease; + box-shadow: 0 4px 20px rgba(240, 206, 187, 0.3); +} + +.primaryButton:hover { + transform: translateY(-2px); + box-shadow: 0 8px 30px rgba(240, 206, 187, 0.4); + color: #1a1c1f; + text-decoration: none; +} + +.secondaryButton { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 1rem 2rem; + background: transparent; + color: #f0cebb; + font-weight: 600; + font-size: 1.1rem; + border: 2px solid #f0cebb; + border-radius: 50px; + text-decoration: none; + transition: all 0.3s ease; +} + +.secondaryButton:hover { + background: rgba(240, 206, 187, 0.1); + color: #f0cebb; + text-decoration: none; +} + +.heroStats { + display: flex; + justify-content: center; + gap: 3rem; + padding-top: 2rem; + border-top: 1px solid rgba(240, 206, 187, 0.2); +} + +.stat { + display: flex; + flex-direction: column; + align-items: center; +} + +.statNumber { + font-size: 2.5rem; + font-weight: 800; + color: #f0cebb; +} + +.statLabel { + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.6); + text-transform: uppercase; + letter-spacing: 1px; +} + +/* ===== SECTION HEADERS ===== */ +.sectionHeader { text-align: center; + margin-bottom: 3rem; +} + +.sectionHeader h2 { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 1rem; + color: #ffffff; +} + +.sectionHeader p { + font-size: 1.2rem; + color: rgba(255, 255, 255, 0.7); + max-width: 600px; + margin: 0 auto; +} + +/* ===== LEARNING PATHS SECTION ===== */ +.learningPaths { + padding: 5rem 0; + background: #1a1c1f; +} + +.pathsGrid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; +} + +.pathCard { + background: linear-gradient(145deg, #2a2d32 0%, #1f2124 100%); + border: 1px solid rgba(240, 206, 187, 0.1); + border-radius: 16px; + padding: 2rem; + text-decoration: none; + transition: all 0.3s ease; + display: flex; + flex-direction: column; +} + +.pathCard:hover { + transform: translateY(-8px); + border-color: rgba(240, 206, 187, 0.3); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + text-decoration: none; +} + +.pathIcon { + margin-bottom: 1rem; + display: flex; + align-items: center; + justify-content: flex-start; +} + +.pathCard h3 { + color: #ffffff; + font-size: 1.5rem; + margin-bottom: 0.25rem; +} + +.pathSubtitle { + color: #f0cebb; + font-size: 0.9rem; + font-weight: 500; + margin-bottom: 1rem; +} + +.pathCard p { + color: rgba(255, 255, 255, 0.7); + line-height: 1.6; + margin-bottom: 1.5rem; + flex-grow: 1; +} + +.pathFeatures { + list-style: none; + padding: 0; + margin: 0 0 1.5rem 0; +} + +.pathFeatures li { + color: rgba(255, 255, 255, 0.6); + font-size: 0.9rem; + padding: 0.25rem 0; +} + +.pathCta { + color: #f0cebb; + font-weight: 600; + font-size: 1rem; +} + +/* ===== BUILD SECTION ===== */ +.buildSection { + padding: 5rem 0; + background: linear-gradient(180deg, #1f2124 0%, #1a1c1f 100%); +} + +.buildGrid { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 1.5rem; +} + +.buildCard { + background: rgba(240, 206, 187, 0.05); + border: 1px solid rgba(240, 206, 187, 0.1); + border-radius: 12px; + padding: 1.5rem; + text-align: center; + transition: all 0.3s ease; +} + +.buildCard:hover { + background: rgba(240, 206, 187, 0.1); + border-color: rgba(240, 206, 187, 0.2); + transform: translateY(-4px); +} + +.buildIcon { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0.75rem; +} + +.buildCard h4 { + color: #ffffff; + font-size: 1rem; + margin-bottom: 0.5rem; +} + +.buildCard p { + color: rgba(255, 255, 255, 0.6); + font-size: 0.8rem; + margin: 0; +} + +/* ===== TECHNOLOGIES SECTION ===== */ +.techSection { + padding: 5rem 0; + background: #1a1c1f; +} + +.techGrid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1.5rem; +} + +.techCard { + background: #2a2d32; + border-radius: 12px; + padding: 1.5rem; + text-decoration: none; + transition: all 0.3s ease; position: relative; overflow: hidden; } -@media screen and (max-width: 996px) { - .heroBanner { - padding: 2rem; - } +.techCard:hover { + transform: translateY(-4px); + text-decoration: none; +} + +.techBorder { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + border-top-width: 3px; + border-top-style: solid; +} + +.techCard h3 { + font-size: 1.3rem; + margin-bottom: 0.75rem; + margin-top: 0.5rem; +} + +.techCard p { + color: rgba(255, 255, 255, 0.7); + font-size: 0.95rem; + line-height: 1.5; + margin-bottom: 1rem; +} + +.techLink { + color: #f0cebb; + font-size: 0.9rem; + font-weight: 500; +} + +/* ===== COMMUNITY SECTION ===== */ +.communitySection { + padding: 5rem 0; + background: linear-gradient(180deg, #1f2124 0%, #1a1c1f 100%); +} + +.communityContent { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; +} + +.communityText h2 { + color: #ffffff; + font-size: 2.5rem; + margin-bottom: 1rem; +} + +.communityText p { + color: rgba(255, 255, 255, 0.7); + font-size: 1.1rem; + line-height: 1.6; + margin-bottom: 2rem; +} + +.communityButtons { + display: flex; + gap: 1rem; + flex-wrap: wrap; +} + +.discordButton, .githubButton { + display: inline-flex; + align-items: center; + gap: 0.75rem; + padding: 0.875rem 1.5rem; + border-radius: 8px; + font-weight: 600; + text-decoration: none; + transition: all 0.3s ease; +} + +.discordButton { + background: #5865F2; + color: #ffffff; +} + +.discordButton:hover { + background: #4752C4; + color: #ffffff; + text-decoration: none; +} + +.discordButton img { + width: 24px; + height: 24px; +} + +.githubButton { + background: #333; + color: #ffffff; +} + +.githubButton:hover { + background: #444; + color: #ffffff; + text-decoration: none; +} + +.communityLinks { + display: flex; + flex-direction: column; + gap: 1rem; } -.buttons { +.communityLink { display: flex; align-items: center; + gap: 1rem; + padding: 1.25rem; + background: rgba(240, 206, 187, 0.05); + border: 1px solid rgba(240, 206, 187, 0.1); + border-radius: 12px; + text-decoration: none; + transition: all 0.3s ease; +} + +.communityLink:hover { + background: rgba(240, 206, 187, 0.1); + border-color: rgba(240, 206, 187, 0.2); + text-decoration: none; +} + +.communityLink svg { + flex-shrink: 0; +} + +.communityLink h4 { + color: #ffffff; + margin: 0 0 0.25rem 0; + font-size: 1.1rem; +} + +.communityLink p { + color: rgba(255, 255, 255, 0.6); + margin: 0; + font-size: 0.9rem; +} + +/* ===== CTA SECTION ===== */ +.ctaSection { + padding: 5rem 0; + background: linear-gradient(135deg, rgba(240, 206, 187, 0.1) 0%, rgba(220, 150, 110, 0.05) 100%); + border-top: 1px solid rgba(240, 206, 187, 0.1); +} + +.ctaContent { + text-align: center; +} + +.ctaContent h2 { + color: #ffffff; + font-size: 2.5rem; + margin-bottom: 1rem; +} + +.ctaContent p { + color: rgba(255, 255, 255, 0.7); + font-size: 1.2rem; + margin-bottom: 2rem; +} + +.ctaButtons { + display: flex; + gap: 1rem; justify-content: center; + flex-wrap: wrap; +} + +.outlineButton { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 1rem 1.5rem; + background: transparent; + color: #f0cebb; + font-weight: 600; + border: 2px solid rgba(240, 206, 187, 0.3); + border-radius: 50px; + text-decoration: none; + transition: all 0.3s ease; +} + +.outlineButton:hover { + border-color: #f0cebb; + background: rgba(240, 206, 187, 0.1); + color: #f0cebb; + text-decoration: none; +} + +/* ===== RESPONSIVE ===== */ +@media screen and (max-width: 1200px) { + .buildGrid { + grid-template-columns: repeat(3, 1fr); + } + + .techGrid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media screen and (max-width: 996px) { + .heroTitle { + font-size: 2.5rem; + } + + .heroSubtitle { + font-size: 1.1rem; + } + + .pathsGrid { + grid-template-columns: 1fr; + } + + .communityContent { + grid-template-columns: 1fr; + gap: 2rem; + } + + .heroStats { + gap: 2rem; + } + + .statNumber { + font-size: 2rem; + } +} + +@media screen and (max-width: 768px) { + .heroSection { + min-height: auto; + padding: 4rem 0; + } + + .heroTitle { + font-size: 2rem; + } + + .buildGrid { + grid-template-columns: repeat(2, 1fr); + } + + .techGrid { + grid-template-columns: 1fr; + } + + .heroStats { + flex-direction: column; + gap: 1.5rem; + } + + .ctaButtons { + flex-direction: column; + align-items: center; + } + + .sectionHeader h2 { + font-size: 2rem; + } } diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 66ec0563..b7ba3984 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -1,35 +1,310 @@ -import type {ReactNode} from "react"; -import clsx from "clsx"; +import type { ReactNode } from "react"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import Layout from "@theme/Layout"; -import HomepageFeatures from "@site/src/components/HomepageFeatures"; +import Link from "@docusaurus/Link"; import Heading from "@theme/Heading"; +import { + RocketIcon, + ArtIcon, + CodeIcon, + CadIcon, + PrinterIcon, + GamepadIcon, + SofaIcon, + BuildingIcon, + TargetIcon, + OpenSourceIcon, + BookIcon, + RingIcon, + BlocksIcon, + UserPlusIcon, + MonacoIcon, +} from "@site/src/components/Icons"; import styles from "./index.module.css"; -function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); +// Learning Path Data +const learningPaths = [ + { + title: "Visual Programming", + subtitle: "No coding required", + description: "Create 3D models using intuitive node-based editors like Rete and Blockly. Perfect for designers and beginners.", + IconComponent: ArtIcon, + link: "/learn/getting-started/overview", + features: ["Drag & drop nodes", "Real-time preview", "Export ready models"], + }, + { + title: "TypeScript & JavaScript", + subtitle: "Full programmatic control", + description: "Write code in our Monaco editor with full TypeScript support, autocomplete, and access to the complete API.", + IconComponent: CodeIcon, + link: "/learn/code/intro", + features: ["Type-safe API", "Monaco editor", "NPM packages"], + }, + { + title: "CAD Kernels", + subtitle: "Industrial-grade geometry", + description: "Leverage powerful CAD kernels like OpenCascade (OCCT), JSCAD, and Manifold for precise 3D modeling.", + IconComponent: CadIcon, + link: "/learn/code/common/occt/what-is-occt", + features: ["Boolean operations", "Fillets & chamfers", "STEP/IGES export"], + }, +]; + +// Technology Cards Data +const technologies = [ + { + name: "Three.js", + description: "Integrate Bitbybit's CAD capabilities with the popular Three.js rendering engine.", + link: "/learn/npm-packages/threejs", + color: "#049EF4", + }, + { + name: "Babylon.js", + description: "Build powerful 3D experiences combining Bitbybit geometry with Babylon.js features.", + link: "/learn/npm-packages/babylonjs", + color: "#BB464B", + }, + { + name: "Shopify 3D Bits", + description: "Add interactive 3D product configurators to your Shopify store with our app.", + link: "/learn/3d-bits/intro", + color: "#96BF48", + }, + { + name: "Script Runners", + description: "Execute visual scripts directly on your website without writing code.", + link: "/learn/runners/intro", + color: "#f0cebb", + }, +]; + +// What You Can Build Data +const buildExamples = [ + { IconComponent: SofaIcon, title: "Furniture", desc: "Customizable tables, chairs, and decor" }, + { IconComponent: PrinterIcon, title: "3D Printing", desc: "Print-ready models with proper manifolds" }, + { IconComponent: CadIcon, title: "Engineering", desc: "Mechanical parts and assemblies" }, + { IconComponent: GamepadIcon, title: "Game Assets", desc: "Procedural 3D models for games" }, + { IconComponent: BuildingIcon, title: "Architecture", desc: "Parametric buildings, facades, and structures" }, + { IconComponent: RingIcon, title: "Jewelry", desc: "Intricate rings, pendants, and accessories" }, +]; + +function HeroSection() { return ( -
-
- - {siteConfig.title} +
+
+
+
+
+
+ Bitbybit Logo +
+ + Master 3D CAD on the Web -

{siteConfig.tagline}

+

+ Learn to create stunning parametric 3D models using visual programming or code. +
+ From beginners to professionals — your journey to 3D mastery starts here. +

+
+ + Start Learning + + + Sign Up / Subscribe + +
+
+
+ 3 + CAD Kernels +
+
+ 1000+ + API Functions +
+
+ 3 + Editor Modes +
+
); } +function LearningPathsSection() { + return ( +
+
+
+ Choose Your Learning Path +

Whether you prefer visual tools or writing code, we've got you covered.

+
+
+ {learningPaths.map((path, idx) => ( + +
+ +
+

{path.title}

+ {path.subtitle} +

{path.description}

+
    + {path.features.map((feature, i) => ( +
  • ✓ {feature}
  • + ))} +
+ Explore → + + ))} +
+
+
+ ); +} + +function WhatYouCanBuildSection() { + return ( +
+
+
+ What You Can Build +

From artistic creations to engineering precision — the possibilities are endless.

+
+
+ {buildExamples.map((item, idx) => ( +
+
+ +
+

{item.title}

+

{item.desc}

+
+ ))} +
+
+
+ ); +} + +function TechnologiesSection() { + return ( +
+
+
+ Integrate With Your Stack +

Bitbybit works seamlessly with popular web technologies and platforms.

+
+
+ {technologies.map((tech, idx) => ( + +
+

{tech.name}

+

{tech.description}

+ Learn more → + + ))} +
+
+
+ ); +} + +function CommunitySection() { + return ( +
+
+
+
+ Join Our Community +

+ Connect with fellow creators, get help, share your projects, and stay updated + with the latest features and tutorials. +

+ +
+
+ + +
+

Blog

+

Latest news and tutorials

+
+ + + +
+

Open Source

+

Explore our GitHub repos

+
+ + + +
+

Support Us

+

Help fund development

+
+
+
+
+
+
+ ); +} + +function CTASection() { + return ( +
+
+
+ Ready to Create Something Amazing? +

Start building 3D models today — no installation required.

+
+ + Browse Tutorials + + + Open Rete Editor + + + Open Blockly Editor + + + Open Monaco Editor + +
+
+
+
+ ); +} + export default function Home(): ReactNode { - const {siteConfig} = useDocusaurusContext(); + const { siteConfig } = useDocusaurusContext(); return ( - + title={`Learn ${siteConfig.title}`} + description="Master 3D CAD modeling on the web with Bitbybit. Learn visual programming, TypeScript, and CAD kernels like OpenCascade."> +
- + + + + +
); From acc99662d78b75d9fe1bf2cc27bc10043346f4ef Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Tue, 16 Dec 2025 14:57:38 +0200 Subject: [PATCH 23/38] updated learn.bitbybit.dev homepage to be more like main site. --- docs/src/components/Icons/index.tsx | 43 ++ docs/src/pages/index.module.css | 690 ++++++++++++++++++++++++---- docs/src/pages/index.tsx | 97 +++- 3 files changed, 746 insertions(+), 84 deletions(-) diff --git a/docs/src/components/Icons/index.tsx b/docs/src/components/Icons/index.tsx index 158a9239..12f6ca87 100644 --- a/docs/src/components/Icons/index.tsx +++ b/docs/src/components/Icons/index.tsx @@ -308,3 +308,46 @@ export const MonacoIcon: React.FC = ({ size = defaultSize, color = de ); + +export const ShopifyBagIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Shopping bag */} + + {/* Handles */} + + {/* 3D cube inside bag */} + + + + + +); + +export const NoCodeIcon: React.FC = ({ size = defaultSize, color = defaultColor, className }) => ( + + {/* Canvas/screen */} + + {/* Control panel on right */} + + {/* Sliders */} + + + + + + + {/* 3D object preview */} + + + + {/* Drag handle */} + + + + {/* Stand */} + + + + +); + diff --git a/docs/src/pages/index.module.css b/docs/src/pages/index.module.css index 1a8fd8dd..17748d8e 100644 --- a/docs/src/pages/index.module.css +++ b/docs/src/pages/index.module.css @@ -1,16 +1,27 @@ /** * Bitbybit Learning Portal - Homepage Styles + * Matching the premium look of the main site */ +/* ===== CSS VARIABLES ===== */ +:root { + --primary-gold: #F0CEBB; + --primary-gold-light: #fff6f3; + --primary-gold-dark: #d6b39f; + --bg-dark: #1a1c1f; + --bg-darker: #121315; + --glow-gold: rgba(240, 206, 187, 0.3); +} + /* ===== HERO SECTION ===== */ .heroSection { position: relative; - min-height: 90vh; + min-height: 100vh; display: flex; align-items: center; justify-content: center; overflow: hidden; - background: linear-gradient(135deg, #1a1c1f 0%, #2a2d32 50%, #1a1c1f 100%); + background: linear-gradient(180deg, #1a1c1f 0%, #121315 100%); } .heroBackground { @@ -24,55 +35,252 @@ .heroGrid { position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; + top: -60px; + left: -60px; + right: -60px; + bottom: -60px; background-image: linear-gradient(rgba(240, 206, 187, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(240, 206, 187, 0.03) 1px, transparent 1px); background-size: 60px 60px; - animation: gridMove 20s linear infinite; + animation: gridMove 30s ease-in-out infinite; } @keyframes gridMove { - 0% { transform: translate(0, 0); } - 100% { transform: translate(60px, 60px); } + 0%, 100% { transform: translate(0, 0); } + 50% { transform: translate(30px, 30px); } +} + +/* Hero Particles */ +.heroParticles { + position: absolute; + inset: 0; + z-index: 1; + overflow: hidden; + pointer-events: none; +} + +.heroParticle { + position: absolute; + width: 6px; + height: 6px; + background: #F0CEBB; + border-radius: 50%; + opacity: 0.6; +} + +.heroParticle1 { top: 20%; left: 10%; animation: particleFloat 8s ease-in-out infinite; } +.heroParticle2 { top: 60%; left: 85%; animation: particleFloat 10s ease-in-out infinite 1s; width: 4px; height: 4px; } +.heroParticle3 { top: 80%; left: 20%; animation: particleFloat 12s ease-in-out infinite 2s; width: 5px; height: 5px; } +.heroParticle4 { top: 30%; left: 70%; animation: particleFloat 9s ease-in-out infinite 0.5s; width: 4px; height: 4px; } +.heroParticle5 { top: 50%; left: 40%; animation: particleFloat 11s ease-in-out infinite 1.5s; width: 3px; height: 3px; } +.heroParticle6 { top: 15%; left: 60%; animation: particleFloat 7s ease-in-out infinite 3s; width: 5px; height: 5px; } + +@keyframes particleFloat { + 0%, 100% { transform: translateY(0) translateX(0); opacity: 0.6; } + 25% { transform: translateY(-30px) translateX(10px); opacity: 0.8; } + 50% { transform: translateY(-15px) translateX(-5px); opacity: 0.4; } + 75% { transform: translateY(-25px) translateX(15px); opacity: 0.7; } +} + +/* Hero Geometric Shapes */ +.heroGeometric { + position: absolute; + z-index: 1; + opacity: 0.15; + pointer-events: none; +} + +.heroGeo1 { + top: 10%; + left: 5%; + width: 120px; + height: 120px; + animation: float 12s ease-in-out infinite; +} + +.heroGeo2 { + bottom: 15%; + right: 8%; + width: 100px; + height: 100px; + animation: floatReverse 10s ease-in-out infinite; +} + +.heroGeo3 { + top: 50%; + right: 15%; + width: 80px; + height: 80px; + animation: float 14s ease-in-out infinite 2s; +} + +@keyframes float { + 0%, 100% { transform: translateY(0) rotate(0deg); } + 50% { transform: translateY(-20px) rotate(5deg); } +} + +@keyframes floatReverse { + 0%, 100% { transform: translateY(0) rotate(0deg); } + 50% { transform: translateY(20px) rotate(-5deg); } } .heroContent { position: relative; - z-index: 1; + z-index: 2; text-align: center; max-width: 900px; padding: 2rem; } +/* Logo with 3D Particles */ +.heroLogoContainer { + position: relative; + width: 90px; + height: 90px; + margin: 0 auto 30px; + perspective: 500px; +} + +.heroLogoMain { + position: relative; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + animation: logoFloat 5s ease-in-out infinite; +} + +@keyframes logoFloat { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-6px); } +} + .heroLogo { - margin-bottom: 1.5rem; + margin-bottom: 0; } .heroLogo img { - width: 80px; - height: 80px; - animation: float 3s ease-in-out infinite; + width: 90px; + height: 90px; + border-radius: 6px; + transition: all 0.4s ease; } -@keyframes float { - 0%, 100% { transform: translateY(0px); } - 50% { transform: translateY(-10px); } +.heroLogo img:hover { + filter: drop-shadow(0 0 12px rgba(240, 206, 187, 0.25)) drop-shadow(0 0 24px rgba(240, 206, 187, 0.15)); + transform: scale(1.03); +} + +.logoParticles { + position: absolute; + inset: -60px; + transform-style: preserve-3d; + pointer-events: none; +} + +.logoParticle { + position: absolute; + width: 4px; + height: 4px; + background: #F0CEBB; + border-radius: 50%; + opacity: 0.7; +} + +.logoP1 { top: 20%; left: 10%; animation: fly3d1 8s ease-in-out infinite; } +.logoP2 { top: 10%; right: 15%; width: 3px; height: 3px; animation: fly3d2 10s ease-in-out infinite 0.5s; } +.logoP3 { bottom: 15%; left: 20%; width: 5px; height: 5px; animation: fly3d3 9s ease-in-out infinite 1s; } +.logoP4 { bottom: 25%; right: 10%; width: 3px; height: 3px; animation: fly3d4 11s ease-in-out infinite 0.3s; } +.logoP5 { top: 50%; left: 5%; width: 2px; height: 2px; animation: fly3d5 7s ease-in-out infinite 1.5s; } +.logoP6 { top: 40%; right: 5%; width: 4px; height: 4px; animation: fly3d6 12s ease-in-out infinite 0.8s; } + +@keyframes fly3d1 { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.7; } + 25% { transform: translate3d(15px, -20px, 30px) scale(1.2); opacity: 0.9; } + 50% { transform: translate3d(-10px, -35px, 50px) scale(0.8); opacity: 0.5; } + 75% { transform: translate3d(20px, -15px, 20px) scale(1.1); opacity: 0.8; } +} + +@keyframes fly3d2 { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.6; } + 33% { transform: translate3d(-20px, 15px, 40px) scale(1.3); opacity: 0.9; } + 66% { transform: translate3d(10px, -25px, 25px) scale(0.7); opacity: 0.5; } +} + +@keyframes fly3d3 { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.8; } + 20% { transform: translate3d(25px, 10px, 35px) scale(0.9); opacity: 0.6; } + 50% { transform: translate3d(-15px, 30px, 60px) scale(1.4); opacity: 1; } + 80% { transform: translate3d(10px, 5px, 20px) scale(1.1); opacity: 0.7; } +} + +@keyframes fly3d4 { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.7; } + 40% { transform: translate3d(-25px, -10px, 45px) scale(1.2); opacity: 0.9; } + 70% { transform: translate3d(15px, 20px, 30px) scale(0.8); opacity: 0.5; } +} + +@keyframes fly3d5 { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.5; } + 30% { transform: translate3d(20px, -30px, 25px) scale(1.5); opacity: 0.9; } + 60% { transform: translate3d(-10px, -15px, 50px) scale(0.8); opacity: 0.6; } +} + +@keyframes fly3d6 { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.6; } + 25% { transform: translate3d(-30px, 20px, 35px) scale(1.1); opacity: 0.8; } + 50% { transform: translate3d(-15px, -25px, 55px) scale(1.3); opacity: 1; } + 75% { transform: translate3d(10px, 10px, 20px) scale(0.9); opacity: 0.5; } +} + +/* Hero Badge */ +.heroBadge { + display: inline-flex; + align-items: center; + gap: 8px; + background: rgba(240, 206, 187, 0.1); + border: 1px solid rgba(240, 206, 187, 0.3); + border-radius: 30px; + padding: 8px 20px; + margin-bottom: 30px; + font-size: 14px; + color: #F0CEBB; + backdrop-filter: blur(10px); + animation: fadeInUp 0.8s ease-out; +} + +.badgeIcon { + font-size: 16px; + animation: sparkle 2s ease-in-out infinite; +} + +@keyframes sparkle { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.6; transform: scale(1.2); } +} + +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(30px); } + to { opacity: 1; transform: translateY(0); } } .heroTitle { - font-size: 3.5rem; - font-weight: 800; - margin-bottom: 1.5rem; + font-size: clamp(36px, 8vw, 72px); + font-weight: 300; + margin-bottom: 20px; color: #ffffff; - line-height: 1.2; + line-height: 1.1; + letter-spacing: 4px; + animation: fadeInUp 0.8s ease-out 0.2s backwards; } .highlight { - color: #f0cebb; + background: linear-gradient(135deg, #d6b39f, #F0CEBB, #fff6f3); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; position: relative; } @@ -83,74 +291,104 @@ left: 0; right: 0; height: 4px; - background: linear-gradient(90deg, #f0cebb, #dc966e); + background: linear-gradient(90deg, #F0CEBB, #fff6f3); border-radius: 2px; + transform: scaleX(0); + animation: underlineExpand 0.8s ease-out 1s forwards; +} + +@keyframes underlineExpand { + to { transform: scaleX(1); } } .heroSubtitle { - font-size: 1.3rem; + font-size: clamp(16px, 2.5vw, 22px); color: rgba(255, 255, 255, 0.8); margin-bottom: 2.5rem; line-height: 1.6; + animation: fadeInUp 0.8s ease-out 0.4s backwards; } .heroButtons { display: flex; - gap: 1rem; + gap: 20px; justify-content: center; flex-wrap: wrap; - margin-bottom: 3rem; + margin-bottom: 50px; + animation: fadeInUp 0.8s ease-out 0.6s backwards; } .primaryButton { display: inline-flex; align-items: center; - gap: 0.5rem; - padding: 1rem 2rem; - background: linear-gradient(135deg, #f0cebb 0%, #dc966e 100%); + gap: 12px; + background: linear-gradient(135deg, #d6b39f, #F0CEBB, #fff6f3); color: #1a1c1f; - font-weight: 700; - font-size: 1.1rem; + padding: 16px 36px; border-radius: 50px; + font-size: 16px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 1px; text-decoration: none; - transition: all 0.3s ease; + transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); box-shadow: 0 4px 20px rgba(240, 206, 187, 0.3); + position: relative; + overflow: hidden; +} + +.primaryButton::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); + transition: left 0.5s ease; } .primaryButton:hover { - transform: translateY(-2px); - box-shadow: 0 8px 30px rgba(240, 206, 187, 0.4); + transform: translateY(-4px); + box-shadow: 0 8px 30px rgba(240, 206, 187, 0.5); color: #1a1c1f; text-decoration: none; } +.primaryButton:hover::before { + left: 100%; +} + .secondaryButton { display: inline-flex; align-items: center; - gap: 0.5rem; - padding: 1rem 2rem; - background: transparent; - color: #f0cebb; - font-weight: 600; - font-size: 1.1rem; - border: 2px solid #f0cebb; + gap: 12px; + background: rgba(255, 255, 255, 0.05); + border: 2px solid rgba(240, 206, 187, 0.4); + color: #F0CEBB; + padding: 14px 32px; border-radius: 50px; + font-size: 16px; + font-weight: 500; text-decoration: none; transition: all 0.3s ease; } .secondaryButton:hover { - background: rgba(240, 206, 187, 0.1); - color: #f0cebb; + background: rgba(240, 206, 187, 0.15); + border-color: rgba(240, 206, 187, 0.6); + color: #F0CEBB; text-decoration: none; + transform: translateY(-2px); } .heroStats { display: flex; justify-content: center; - gap: 3rem; - padding-top: 2rem; - border-top: 1px solid rgba(240, 206, 187, 0.2); + gap: 60px; + padding-top: 40px; + border-top: 1px solid rgba(240, 206, 187, 0.15); + animation: fadeInUp 0.8s ease-out 0.8s backwards; } .stat { @@ -160,16 +398,18 @@ } .statNumber { - font-size: 2.5rem; - font-weight: 800; - color: #f0cebb; + font-size: clamp(28px, 4vw, 42px); + font-weight: 300; + color: #F0CEBB; + letter-spacing: 2px; } .statLabel { - font-size: 0.9rem; - color: rgba(255, 255, 255, 0.6); + font-size: 12px; + color: rgba(255, 255, 255, 0.5); text-transform: uppercase; - letter-spacing: 1px; + letter-spacing: 2px; + margin-top: 8px; } /* ===== SECTION HEADERS ===== */ @@ -201,50 +441,132 @@ .pathsGrid { display: grid; grid-template-columns: repeat(3, 1fr); - gap: 2rem; + gap: 24px; } .pathCard { - background: linear-gradient(145deg, #2a2d32 0%, #1f2124 100%); - border: 1px solid rgba(240, 206, 187, 0.1); - border-radius: 16px; - padding: 2rem; + background: linear-gradient(145deg, rgba(30, 33, 38, 0.95) 0%, rgba(22, 24, 28, 0.98) 100%); + border: 1px solid rgba(240, 206, 187, 0.12); + border-radius: 20px; + padding: 32px 28px; text-decoration: none; - transition: all 0.3s ease; + transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); display: flex; flex-direction: column; + position: relative; + overflow: hidden; +} + +/* Subtle gradient overlay */ +.pathCard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 100%; + background: radial-gradient(ellipse at top center, rgba(240, 206, 187, 0.04) 0%, transparent 60%); + opacity: 0; + transition: opacity 0.5s ease; + pointer-events: none; +} + +/* Bottom shimmer line */ +.pathCard::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: linear-gradient(90deg, transparent, #F0CEBB, #fff6f3, #F0CEBB, transparent); + transition: width 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .pathCard:hover { - transform: translateY(-8px); - border-color: rgba(240, 206, 187, 0.3); - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + transform: translateY(-12px) scale(1.02); + border-color: rgba(240, 206, 187, 0.35); + box-shadow: + 0 25px 50px rgba(0, 0, 0, 0.4), + 0 0 40px rgba(240, 206, 187, 0.08), + inset 0 1px 0 rgba(255, 255, 255, 0.05); text-decoration: none; } +.pathCard:hover::before { + opacity: 1; +} + +.pathCard:hover::after { + width: 100%; +} + .pathIcon { - margin-bottom: 1rem; + position: relative; + width: 72px; + height: 72px; + margin-bottom: 24px; display: flex; align-items: center; - justify-content: flex-start; + justify-content: center; +} + +.pathIconGlow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.8); + width: 80px; + height: 80px; + background: radial-gradient(circle, rgba(240, 206, 187, 0.15) 0%, transparent 70%); + border-radius: 50%; + opacity: 0; + transition: all 0.5s ease; + pointer-events: none; +} + +.pathCard:hover .pathIconGlow { + opacity: 1; + transform: translate(-50%, -50%) scale(1.2); +} + +.pathIconSvg { + position: relative; + z-index: 1; + transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +.pathCard:hover .pathIconSvg { + transform: scale(1.15); + filter: drop-shadow(0 0 12px rgba(240, 206, 187, 0.4)); } .pathCard h3 { - color: #ffffff; - font-size: 1.5rem; - margin-bottom: 0.25rem; + color: rgba(240, 206, 187, 0.9); + font-size: 1.25rem; + font-weight: 600; + letter-spacing: 1px; + margin-bottom: 0.5rem; + transition: color 0.3s ease; +} + +.pathCard:hover h3 { + color: #F0CEBB; } .pathSubtitle { - color: #f0cebb; - font-size: 0.9rem; + color: rgba(255, 255, 255, 0.5); + font-size: 0.85rem; font-weight: 500; margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 1.5px; } .pathCard p { - color: rgba(255, 255, 255, 0.7); - line-height: 1.6; + color: rgba(255, 255, 255, 0.6); + line-height: 1.7; + font-size: 14px; margin-bottom: 1.5rem; flex-grow: 1; } @@ -256,15 +578,26 @@ } .pathFeatures li { - color: rgba(255, 255, 255, 0.6); - font-size: 0.9rem; - padding: 0.25rem 0; + color: rgba(255, 255, 255, 0.5); + font-size: 0.85rem; + padding: 0.35rem 0; + transition: color 0.3s ease; +} + +.pathCard:hover .pathFeatures li { + color: rgba(255, 255, 255, 0.7); } .pathCta { - color: #f0cebb; + color: #F0CEBB; font-weight: 600; - font-size: 1rem; + font-size: 0.95rem; + letter-spacing: 0.5px; + transition: all 0.3s ease; +} + +.pathCard:hover .pathCta { + letter-spacing: 2px; } /* ===== BUILD SECTION ===== */ @@ -369,6 +702,164 @@ font-weight: 500; } +/* ===== SHOPIFY BITS SECTION ===== */ +.shopifyBitsSection { + padding: 5rem 0; + background: linear-gradient(180deg, #1a1c1f 0%, #1f2124 50%, #1a1c1f 100%); + position: relative; +} + +.shopifyBitsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 32px; +} + +.shopifyBitsCard { + background: linear-gradient(145deg, rgba(30, 33, 38, 0.95) 0%, rgba(22, 24, 28, 0.98) 100%); + border: 1px solid rgba(240, 206, 187, 0.12); + border-radius: 20px; + padding: 40px 32px; + text-decoration: none; + transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.shopifyBitsCard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 100%; + background: radial-gradient(ellipse at top center, rgba(240, 206, 187, 0.04) 0%, transparent 60%); + opacity: 0; + transition: opacity 0.5s ease; + pointer-events: none; +} + +.shopifyBitsCard::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 3px; + background: linear-gradient(90deg, transparent, #96BF48, #f0cebb, #96BF48, transparent); + transition: width 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +.shopifyBitsCard:hover { + transform: translateY(-12px) scale(1.02); + border-color: rgba(240, 206, 187, 0.35); + box-shadow: + 0 25px 50px rgba(0, 0, 0, 0.4), + 0 0 40px rgba(240, 206, 187, 0.08), + inset 0 1px 0 rgba(255, 255, 255, 0.05); + text-decoration: none; +} + +.shopifyBitsCard:hover::before { + opacity: 1; +} + +.shopifyBitsCard:hover::after { + width: 100%; +} + +.shopifyBitsCardGlow { + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle, rgba(150, 191, 72, 0.05) 0%, transparent 50%); + opacity: 0; + transition: opacity 0.5s ease; + pointer-events: none; +} + +.shopifyBitsCard:hover .shopifyBitsCardGlow { + opacity: 1; +} + +.shopifyBitsIcon { + position: relative; + width: 80px; + height: 80px; + margin-bottom: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.shopifyBitsIconGlow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.8); + width: 100px; + height: 100px; + background: radial-gradient(circle, rgba(150, 191, 72, 0.2) 0%, transparent 70%); + border-radius: 50%; + opacity: 0; + transition: all 0.5s ease; + pointer-events: none; +} + +.shopifyBitsCard:hover .shopifyBitsIconGlow { + opacity: 1; + transform: translate(-50%, -50%) scale(1.3); +} + +.shopifyBitsIconSvg { + position: relative; + z-index: 1; + transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +.shopifyBitsCard:hover .shopifyBitsIconSvg { + transform: scale(1.1); + filter: drop-shadow(0 0 15px rgba(150, 191, 72, 0.4)); +} + +.shopifyBitsCard h3 { + color: #ffffff; + font-size: 1.4rem; + font-weight: 600; + letter-spacing: 0.5px; + margin-bottom: 12px; + transition: color 0.3s ease; +} + +.shopifyBitsCard:hover h3 { + color: #F0CEBB; +} + +.shopifyBitsCard p { + color: rgba(255, 255, 255, 0.6); + line-height: 1.7; + font-size: 15px; + margin-bottom: 24px; + flex-grow: 1; +} + +.shopifyBitsCta { + color: #96BF48; + font-weight: 600; + font-size: 1rem; + letter-spacing: 0.5px; + transition: all 0.3s ease; +} + +.shopifyBitsCard:hover .shopifyBitsCta { + letter-spacing: 2px; + color: #a8d454; +} + /* ===== COMMUNITY SECTION ===== */ .communitySection { padding: 5rem 0; @@ -539,11 +1030,15 @@ .techGrid { grid-template-columns: repeat(2, 1fr); } + + .heroStats { + gap: 40px; + } } @media screen and (max-width: 996px) { .heroTitle { - font-size: 2.5rem; + font-size: clamp(32px, 6vw, 48px); } .heroSubtitle { @@ -560,22 +1055,46 @@ } .heroStats { - gap: 2rem; + gap: 30px; } .statNumber { - font-size: 2rem; + font-size: 28px; + } + + .heroGeometric { + display: none; + } + + .shopifyBitsGrid { + grid-template-columns: 1fr; } } @media screen and (max-width: 768px) { .heroSection { min-height: auto; - padding: 4rem 0; + padding: 6rem 1rem 4rem; } .heroTitle { - font-size: 2rem; + font-size: clamp(28px, 7vw, 36px); + letter-spacing: 2px; + } + + .heroLogoContainer { + width: 70px; + height: 70px; + margin-bottom: 25px; + } + + .heroLogo img { + width: 70px; + height: 70px; + } + + .logoParticles { + inset: -40px; } .buildGrid { @@ -599,4 +1118,15 @@ .sectionHeader h2 { font-size: 2rem; } + + .primaryButton, + .secondaryButton { + padding: 14px 28px; + font-size: 14px; + } + + .heroBadge { + font-size: 12px; + padding: 6px 16px; + } } diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index b7ba3984..0593ddf3 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -19,6 +19,8 @@ import { BlocksIcon, UserPlusIcon, MonacoIcon, + ShopifyBagIcon, + NoCodeIcon, } from "@site/src/components/Icons"; import styles from "./index.module.css"; @@ -94,16 +96,54 @@ function HeroSection() {
+ {/* Floating Particles */} +
+
+
+
+
+
+
+
+ {/* Geometric Shapes */} + + + + + + + + + +
-
- Bitbybit Logo + {/* Logo with 3D Particles */} +
+
+ + + + + + +
+
+
+ Bitbybit Logo +
+
+
+ {/* Badge */} +
+ + Learning Platform
Master 3D CAD on the Web

- Learn to create stunning parametric 3D models using visual programming or code. + Learn to create stunning parametric 3D models using visual programming or code. Understand how to use our E-Commerce solutions.
From beginners to professionals — your journey to 3D mastery starts here.

@@ -146,7 +186,10 @@ function LearningPathsSection() { {learningPaths.map((path, idx) => (
- +
+
+ +

{path.title}

{path.subtitle} @@ -265,6 +308,51 @@ function CommunitySection() { ); } +function ShopifyBitsSection() { + return ( +
+
+
+ 3D Bits for Shopify +

+ Add interactive 3D product configurators to your Shopify store — documentation and tutorials available here. +

+
+
+ +
+
+
+
+ +
+
+

3D Bits App Documentation

+

+ Learn how to install, configure, and use the 3D Bits app to display interactive 3D models and configurators on your Shopify product pages. +

+ Read the Docs → + + +
+
+
+
+ +
+
+

No-Code Viewer Editor

+

+ Create stunning 3D configurators without writing any code. Use our visual editor to design product experiences with drag-and-drop simplicity. +

+ Start Creating → + +
+
+
+ ); +} + function CTASection() { return (
@@ -303,6 +391,7 @@ export default function Home(): ReactNode { + From 44d2774a0b654ebef48b6b7b9fafeb87e817dc37 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Tue, 16 Dec 2025 16:38:49 +0200 Subject: [PATCH 24/38] improved some homepage editor related styling --- docs/src/components/Icons/index.tsx | 57 ++++++++ docs/src/pages/index.module.css | 205 +++++++++++++++++++++++++--- docs/src/pages/index.tsx | 96 ++++++++++--- 3 files changed, 325 insertions(+), 33 deletions(-) diff --git a/docs/src/components/Icons/index.tsx b/docs/src/components/Icons/index.tsx index 12f6ca87..7c28a82d 100644 --- a/docs/src/components/Icons/index.tsx +++ b/docs/src/components/Icons/index.tsx @@ -351,3 +351,60 @@ export const NoCodeIcon: React.FC = ({ size = defaultSize, color = de ); +export const ReteEditorIcon: React.FC = ({ size = defaultSize, color = "currentColor", className }) => ( + + {/* Top Left Node */} + + + + + {/* Bottom Left Node */} + + + + + {/* Right Node (receives both inputs) */} + + + + + + {/* Bezier Wires - two outputs to two inputs on one node */} + + + +); + +export const BlocklyEditorIcon: React.FC = ({ size = defaultSize, color = "currentColor", className }) => ( + + + + + + + + + + + +); + +export const TypeScriptEditorIcon: React.FC = ({ size = defaultSize, color = "currentColor", className }) => ( + + {/* Code editor window */} + + + + + + + {/* Code lines */} + + + + + {/* TS Badge - larger and more prominent */} + + TS + +); diff --git a/docs/src/pages/index.module.css b/docs/src/pages/index.module.css index 17748d8e..b90ae32b 100644 --- a/docs/src/pages/index.module.css +++ b/docs/src/pages/index.module.css @@ -970,34 +970,188 @@ font-size: 0.9rem; } -/* ===== CTA SECTION ===== */ +/* ===== CTA / EDITOR LAUNCH SECTION ===== */ .ctaSection { - padding: 5rem 0; + padding: 5rem 0 6rem; background: linear-gradient(135deg, rgba(240, 206, 187, 0.1) 0%, rgba(220, 150, 110, 0.05) 100%); border-top: 1px solid rgba(240, 206, 187, 0.1); } -.ctaContent { +.ctaFooter { text-align: center; + margin-top: 3rem; } -.ctaContent h2 { - color: #ffffff; - font-size: 2.5rem; - margin-bottom: 1rem; +.ctaHeader { + text-align: center; + margin-bottom: 2.5rem; +} + +/* Editor Cards Grid */ +.editorCardsGrid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.editorCard { + position: relative; + background: linear-gradient(135deg, rgba(26, 28, 31, 0.95) 0%, rgba(35, 40, 48, 0.9) 100%); + border: 1px solid rgba(240, 206, 187, 0.2); + border-radius: 24px; + padding: 40px 30px; + text-decoration: none; + color: white; + overflow: hidden; + transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); + text-align: center; +} + +.editorCard:hover { + transform: translateY(-12px); + border-color: #F0CEBB; + text-decoration: none; +} + +.editorCard:hover .editorCardGlow { + opacity: 1; +} + +.editorCard:hover .editorIconWrapper { + transform: scale(1.15); + background: #F0CEBB; + color: #1a1c1f; +} + +.editorCard:hover .editorIconWrapper svg { + color: #1a1c1f; +} + +.editorCard:hover .editorLaunchBtn { + background: #F0CEBB; + color: #1a1c1f; +} + +.editorCard:hover .editorLaunchBtn svg { + transform: translateX(5px); +} + +.editorCard:hover .editorCardShine { + transform: translateX(100%); +} + +.editorCardGlow { + position: absolute; + inset: -2px; + background: linear-gradient(135deg, #F0CEBB, transparent 50%, #d6b39f); + border-radius: 26px; + opacity: 0; + transition: opacity 0.4s ease; + z-index: -1; + filter: blur(15px); +} + +.editorCardContent { + position: relative; + z-index: 2; +} + +.editorCardShine { + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent); + transition: transform 0.6s ease; + z-index: 1; +} + +.editorIconWrapper { + width: 80px; + height: 80px; + margin: 0 auto 25px; + background: rgba(240, 206, 187, 0.15); + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; + color: #F0CEBB; + transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +.editorIconWrapper svg { + color: inherit; + transition: color 0.4s ease; +} + +.editorTitle { + font-size: 22px; + font-weight: 600; + margin-bottom: 12px; + letter-spacing: 1px; + color: white; } -.ctaContent p { +.editorDescription { + font-size: 15px; color: rgba(255, 255, 255, 0.7); - font-size: 1.2rem; - margin-bottom: 2rem; + line-height: 1.6; + margin-bottom: 20px; } -.ctaButtons { +.editorTags { display: flex; - gap: 1rem; + gap: 8px; justify-content: center; flex-wrap: wrap; + margin-bottom: 25px; +} + +.editorTag { + background: rgba(240, 206, 187, 0.1); + border: 1px solid rgba(240, 206, 187, 0.2); + border-radius: 20px; + padding: 6px 14px; + font-size: 12px; + color: #F0CEBB; + text-transform: uppercase; + letter-spacing: 1px; +} + +.editorLaunchBtn { + display: inline-flex; + align-items: center; + gap: 10px; + background: rgba(240, 206, 187, 0.15); + border: 1px solid #F0CEBB; + border-radius: 30px; + padding: 12px 28px; + font-size: 14px; + font-weight: 600; + color: #F0CEBB; + text-transform: uppercase; + letter-spacing: 1px; + transition: all 0.3s ease; +} + +.editorLaunchBtn svg { + transition: transform 0.3s ease; +} + +/* Card-specific hover colors */ +.editorCardRete:hover { + box-shadow: 0 20px 50px rgba(240, 206, 187, 0.2); +} + +.editorCardBlockly:hover { + box-shadow: 0 20px 50px rgba(240, 206, 187, 0.2); +} + +.editorCardTypescript:hover { + box-shadow: 0 20px 50px rgba(240, 206, 187, 0.2); } .outlineButton { @@ -1069,6 +1223,15 @@ .shopifyBitsGrid { grid-template-columns: 1fr; } + + .editorCardsGrid { + grid-template-columns: 1fr; + max-width: 400px; + } + + .editorCard { + padding: 30px 24px; + } } @media screen and (max-width: 768px) { @@ -1110,11 +1273,6 @@ gap: 1.5rem; } - .ctaButtons { - flex-direction: column; - align-items: center; - } - .sectionHeader h2 { font-size: 2rem; } @@ -1129,4 +1287,17 @@ font-size: 12px; padding: 6px 16px; } + + .editorIconWrapper { + width: 64px; + height: 64px; + } + + .editorTitle { + font-size: 18px; + } + + .editorDescription { + font-size: 14px; + } } diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 0593ddf3..61a0f7b6 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -21,6 +21,9 @@ import { MonacoIcon, ShopifyBagIcon, NoCodeIcon, + ReteEditorIcon, + BlocklyEditorIcon, + TypeScriptEditorIcon, } from "@site/src/components/Icons"; import styles from "./index.module.css"; @@ -357,23 +360,84 @@ function CTASection() { return (
-
+
Ready to Create Something Amazing? -

Start building 3D models today — no installation required.

-
- - Browse Tutorials - - - Open Rete Editor - - - Open Blockly Editor - - - Open Monaco Editor - -
+

Start building 3D models today — no installation required. Launch any editor directly.

+
+
+ + Browse Tutorials + +
+
From 6f859da432e3997db2ff4e1ad16729503be8190a Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Fri, 19 Dec 2025 13:15:49 +0200 Subject: [PATCH 25/38] update to festive decor modeling tutorials --- .../_category_.json | 0 .../star-ornament.md | 0 .../occt/modeling/festive-decor/tree.md | 88 +++++++++++++++++++ 3 files changed, 88 insertions(+) rename docs/learn/code/common/occt/modeling/{christmas-decor => festive-decor}/_category_.json (100%) rename docs/learn/code/common/occt/modeling/{christmas-decor => festive-decor}/star-ornament.md (100%) create mode 100644 docs/learn/code/common/occt/modeling/festive-decor/tree.md diff --git a/docs/learn/code/common/occt/modeling/christmas-decor/_category_.json b/docs/learn/code/common/occt/modeling/festive-decor/_category_.json similarity index 100% rename from docs/learn/code/common/occt/modeling/christmas-decor/_category_.json rename to docs/learn/code/common/occt/modeling/festive-decor/_category_.json diff --git a/docs/learn/code/common/occt/modeling/christmas-decor/star-ornament.md b/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md similarity index 100% rename from docs/learn/code/common/occt/modeling/christmas-decor/star-ornament.md rename to docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md diff --git a/docs/learn/code/common/occt/modeling/festive-decor/tree.md b/docs/learn/code/common/occt/modeling/festive-decor/tree.md new file mode 100644 index 00000000..937abcab --- /dev/null +++ b/docs/learn/code/common/occt/modeling/festive-decor/tree.md @@ -0,0 +1,88 @@ +--- +sidebar_position: 2 +title: Christmas Tree +sidebar_label: Christmas Tree +description: Create a 3D printable Christmas tree ornament using wire offset and extrusion - perfect for festive decorations. +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 + +🎄 What better way to celebrate the festive season than by creating your very own 3D printable Christmas tree! In this tutorial, you'll learn how to create a charming tree ornament using wire operations and extrusion techniques. + +This tutorial introduces important CAD concepts: creating specialized wire shapes, applying offsets to create hollow shapes, reversing wires for proper face creation, and extruding to create 3D solids. These techniques are essential for creating many types of decorative objects! + +## The Christmas Tree Shape + +Our tree starts with the `createChristmasTreeWire` function, which generates a stylized tree silhouette with customizable height, number of skirts (layers), and trunk dimensions. We then create a hollow shape by offsetting the wire inward and combining both wires to form a face. + + + + + + + heightnrSkirtsthicknesstreeWireoffsetWirereversedWiretreeFacetreeSolidfinalTreedrawnTreeMeshheight8nrSkirts4thickness2treeWireheight1.53nrSkirts11FALSE0000010offsetWiretreeWire-0.20.1reversedWireoffsetWiretreeFacetreeWirereversedWireTRUEtreeSolidtreeFace00thicknessfinalTreetreeSolid00DIVIDENEGthickness2drawnTreeMeshfinalTree0.001Tree Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001drawnTreeMeshTRUE0.80TRUE","version":"0.20.12","type":"blockly"}} + title="Christmas tree ornament" + /> + + + {\n // Define control variables\n const height = 6.5;\n const nrSkirts = 5;\n const thickness = 0.5;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Tree Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the Christmas tree wire\n const treeOptions = new ChristmasTreeDto();\n treeOptions.height = height;\n treeOptions.nrSkirts = nrSkirts;\n treeOptions.innerDist = 1.5;\n treeOptions.outerDist = 3;\n treeOptions.trunkHeight = 1;\n treeOptions.trunkWidth = 1;\n treeOptions.origin = [0, 0, 0];\n treeOptions.direction = [0, 1, 0];\n treeOptions.half = false;\n const treeWire = await wire.createChristmasTreeWire(treeOptions);\n\n // Create offset wire (inner boundary)\n const offsetOptions = new OffsetDto();\n offsetOptions.shape = treeWire;\n offsetOptions.distance = -0.2;\n offsetOptions.tolerance = 0.1;\n const offsetWire = await operations.offset(offsetOptions);\n\n // Reverse the inner wire for proper face creation\n const reversedWire = await wire.reversedWire({ shape: offsetWire });\n\n // Create face from both wires (outer and reversed inner)\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [treeWire, reversedWire];\n faceOptions.planar = true;\n const treeFace = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create thickness\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = treeFace;\n extrudeOptions.direction = [0, 0, thickness];\n const treeSolid = await operations.extrude(extrudeOptions);\n\n // Translate to center the shape\n const translateOptions = new TranslateDto();\n translateOptions.shape = treeSolid;\n translateOptions.translation = [0, 0, -thickness / 2];\n const finalTree = await transforms.translate(translateOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n const drawnTreeMesh = await bitbybit.draw.drawAnyAsync({\n entity: finalTree,\n options: drawOptions\n });\n\n const zoomOptions = new ZoomOnDto([drawnTreeMesh]);\n bitbybit.advanced.navigation.zoomOn(zoomOptions);\n}\n\nstart();","version":"0.20.12","type":"typescript"}} + title="Christmas tree ornament" + /> + + + +## Understanding the Code + +### Christmas Tree Wire Creation +The `createChristmasTreeWire` function generates the tree silhouette with these key parameters: +- **height**: Total height of the tree (default 6.5 units) +- **nrSkirts**: Number of layered branches/skirts (default 3) +- **innerDist**: Distance from center to inner edge of skirts +- **outerDist**: Distance from center to outer edge of skirts +- **trunkHeight**: Height of the tree trunk +- **trunkWidth**: Width of the tree trunk +- **direction**: The normal vector (facing up in Y direction) + +### Creating a Hollow Shape with Offset +To make our tree ornament hollow (perfect for 3D printing), we use these techniques: +1. **Offset**: We apply a negative offset (-0.2) to shrink the wire inward, creating an inner boundary +2. **Reverse Wire**: The inner wire must be reversed so that when combined with the outer wire, they form a proper hollow face +3. **Face from Wires**: We combine both wires to create a face with a hole in the middle + +### Extrusion and Centering +The face is extruded along the Z-axis to create thickness. We then translate the shape by half the thickness in the negative Z direction to center it around the origin. + +## Making It Your Own 🎁 + +Try these festive variations: +- **Taller Tree**: Increase the `height` parameter for a more majestic tree +- **More Layers**: Increase `nrSkirts` for a bushier, more detailed tree +- **Thicker Ornament**: Adjust the `thickness` slider for a more substantial piece +- **Different Offset**: Change the offset distance for thicker or thinner walls + +## 3D Printing Tips + +Your Christmas tree ornament is print-ready! For best results: +- **Layer Height**: 0.2mm works great +- **Infill**: Not needed for hollow ornaments! +- **Material**: Green PLA for a classic look, or try glow-in-the-dark filament +- **Support**: Usually not needed when printed flat + +Happy holidays and happy modeling! 🎄 From c25b109dfdbbc6088f5b826555201e9d9825b9b8 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Sun, 21 Dec 2025 15:50:38 +0200 Subject: [PATCH 26/38] update to manifold 3.3.2, some new tutorials for festive decorations --- .../modeling/festive-decor/frost-flower.md | 99 ++ .../festive-decor/frozen-snowflake.md | 151 +++ .../modeling/festive-decor/star-ornament.md | 14 +- .../occt/modeling/festive-decor/tree.md | 6 +- .../lib/api/cross-section/transforms.ts | 15 + .../lib/api/manifold/evaluate.ts | 17 +- .../lib/api/manifold/operations.ts | 31 + .../lib/api/manifold/transforms.ts | 15 + .../lib/api/inputs/manifold-inputs.ts | 78 +- .../cross-section/cross-section-operations.ts | 2 +- .../cross-section/cross-section-transforms.ts | 4 + .../services/manifold/manifold-evaluate.ts | 4 + .../services/manifold/manifold-operations.ts | 8 + .../services/manifold/manifold-transforms.ts | 4 + packages/dev/manifold/package-lock.json | 1208 ++++++++++++++++- packages/dev/manifold/package.json | 2 +- 16 files changed, 1616 insertions(+), 42 deletions(-) create mode 100644 docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md create mode 100644 docs/learn/code/common/occt/modeling/festive-decor/frozen-snowflake.md diff --git a/docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md b/docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md new file mode 100644 index 00000000..b5ff52d4 --- /dev/null +++ b/docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md @@ -0,0 +1,99 @@ +--- +sidebar_position: 3 +title: Frost Flower +sidebar_label: Frost Flower +description: Create a 3D printable frost flower ornament using interpolated curves and 12-fold rotational symmetry. +tags: [code, occt, rete, blockly] +--- + +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +OCCT category icon with a stylized logo representation + +❄️ Create a delicate 3D printable snowflake using **interpolated curves** and **12-fold rotational symmetry**! This tutorial demonstrates how smooth B-spline curves and offset operations can create organic, nature-inspired shapes. + +The algorithm: +1. **Define branch points** - a simple zigzag pattern for one branch +2. **Interpolate a smooth curve** - creates flowing organic lines +3. **Rotate 180°** the branch to create bilateral symmetry +4. **Combine wires** into a single branch pair +5. **Create outer and inner offsets** - hollow structure for the branch +6. **Create face from wires** - outer and reversed inner wire form a hollow face +7. **Rotate copies** at 30° intervals (0°, 30°, 60°, 90°, 120°, 150°) for 12-fold symmetry +8. **Boolean union** to merge all faces into one shape +9. **Extrude** the unified face to create a 3D solid + + + + + + + + sizebranchWirecombinedWireouterWireinnerWirehollowFacesnowflakesize0.8branchWire0000.3MULTIPLY1.5size0-0.2MULTIPLY2.5size00.4MULTIPLY3.5size00MULTIPLY4size0FALSE0.00001combinedWirebranchWirebranchWire001180outerWirecombinedWire0.40.1innerWirecombinedWire0.20.1hollowFaceouterWireinnerWireTRUEsnowflakehollowFacehollowFace00130hollowFace00160hollowFace00190hollowFace001120hollowFace001150FALSEsnowflake000.20.01Ice Material#aed6f1#5dade20.10.31FALSE2FALSE#ffffff2","version":"0.20.12","type":"blockly"}} + title="Star ornament with hanging hole" + /> + + + {\n const size = 0.8;\n\n const branchPoints: Point3[] = [\n [0, 0, 0],\n [0.3, 1.5 * size, 0],\n [-0.2, 2.5 * size, 0],\n [0.4, 3.5 * size, 0],\n [0, 4 * size, 0],\n ];\n\n const interpolation = new InterpolationDto();\n interpolation.points = branchPoints;\n interpolation.periodic = false;\n interpolation.tolerance = 0.00001;\n const branchWire = await wire.interpolatePoints(interpolation);\n\n const rotate180 = new RotateDto();\n rotate180.shape = branchWire;\n rotate180.axis = [0, 0, 1];\n rotate180.angle = 180;\n const branchWireRotated = await transforms.rotate(rotate180);\n\n const combine = new ShapesDto();\n combine.shapes = [branchWire, branchWireRotated];\n const combinedWire = await wire.combineEdgesAndWiresIntoAWire(combine);\n\n const offsetOuter = new OffsetDto();\n offsetOuter.shape = combinedWire;\n offsetOuter.distance = 0.4;\n offsetOuter.tolerance = 0.1;\n const outerWire = (await operations.offset(offsetOuter)) as unknown as TopoDSWirePointer;\n\n const offsetInner = new OffsetDto();\n offsetInner.shape = combinedWire;\n offsetInner.distance = 0.2;\n offsetInner.tolerance = 0.1;\n const innerWireRaw = (await operations.offset(offsetInner)) as unknown as TopoDSWirePointer;\n\n const reverse = new ShapeDto();\n reverse.shape = innerWireRaw;\n const innerWire = await wire.reversedWire(reverse);\n\n const faceFromWires = new FaceFromWiresDto();\n faceFromWires.shapes = [outerWire, innerWire];\n faceFromWires.planar = true;\n const hollowFace = await face.createFaceFromWires(faceFromWires);\n\n const angles = [0, 30, 60, 90, 120, 150];\n const rotatedFaces: TopoDSShapePointer[] = [];\n\n for (const angle of angles) {\n if (angle === 0) {\n rotatedFaces.push(hollowFace);\n continue;\n }\n const rotate = new RotateDto();\n rotate.shape = hollowFace;\n rotate.axis = [0, 0, 1];\n rotate.angle = angle;\n rotatedFaces.push(await transforms.rotate(rotate));\n }\n\n const union = new UnionDto();\n union.shapes = rotatedFaces;\n union.keepEdges = false;\n const snowflakeFace = await booleans.union(union);\n\n const extrude = new ExtrudeDto();\n extrude.shape = snowflakeFace;\n extrude.direction = [0, 0, 0.2];\n const snowflakeSolid = await operations.extrude(extrude);\n\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = 'Ice Material';\n materialOptions.baseColor = '#aed6f1';\n materialOptions.emissiveColor = '#5dade2';\n materialOptions.metallic = 0.1;\n materialOptions.roughness = 0.3;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n const drawOptions = new DrawOcctShapeOptions();\n drawOptions.precision = 0.01;\n drawOptions.drawEdges = false;\n drawOptions.edgeColour = '#ffffff';\n drawOptions.edgeWidth = 2;\n drawOptions.faceMaterial = material;\n\n const starMesh = await bitbybit.draw.drawAnyAsync({\n entity: snowflakeSolid,\n options: drawOptions,\n });\n\n const options = new ZoomOnDto();\n options.meshes = [starMesh];\n bitbybit.advanced.navigation.zoomOn(options);\n};\n\nstart();","version":"0.20.12","type":"typescript"}} + title="Star ornament with hanging hole" + /> + + + +## How It Works + +### Branch Design +Each branch is defined by 5 points creating a gentle zigzag pattern: +- **Center point** at origin (0, 0, 0) +- **Alternating left/right offsets** as Y increases (0.3, -0.2, 0.4...) +- **Tip point** centered for a clean finish (0, 4*size, 0) + +The `interpolatePoints` operation creates a smooth B-spline curve through these points. + +### Bilateral Symmetry +The branch wire is **rotated 180°** around the Z-axis, creating a mirrored copy. Both wires are then combined into a single wire, forming the classic "double-branched" snowflake arm look. + +### Creating Hollow Structure +Instead of solid geometry, this creates a delicate hollow branch: +1. **Outer offset**: The combined wire is offset outward by 0.4 units +2. **Inner offset**: The combined wire is offset outward by 0.2 units, then **reversed** (direction flipped) +3. **Face from wires**: The outer and reversed inner wire create a face with a hollow center - like a ribbon + +### 12-Fold Symmetry +The hollow face is rotated at **30° intervals** (0°, 30°, 60°, 90°, 120°, 150°), creating 6 rotated copies plus the original. This creates a complex 12-branch pattern when the bilateral branches overlap, giving the appearance of intricate snowflake geometry. + +### Final 3D Solid +1. **Boolean Union**: All rotated faces are merged into a single flat 2D shape +2. **Extrude**: The unified shape is extruded 0.2 units along the Z-axis to create the final 3D printable solid + +## Customization ❄️ + +- **Size slider**: Scale the entire snowflake (controls the Y coordinates of branch points) +- **Offset distances**: Adjust the 0.4 and 0.2 values to change the branch thickness +- **Edit point positions**: Modify the zigzag pattern for different branch styles +- **Rotation angles**: Change the 30° step to create different symmetries (try 60° for 6-fold, or 45° for 8-fold) + +## 3D Printing Tips + +- The offset technique creates thin, elegant branches - great for decorative ornaments +- The boolean union creates a single manifold mesh - no intersecting geometry! +- 0.1-0.15mm layer height for fine detail +- 10-15% infill (it's mostly thin walls anyway) +- White or translucent filament for icy look +- Consider adding a small cylinder at the center for a hanging hole + +Happy holidays! ❄️ diff --git a/docs/learn/code/common/occt/modeling/festive-decor/frozen-snowflake.md b/docs/learn/code/common/occt/modeling/festive-decor/frozen-snowflake.md new file mode 100644 index 00000000..5d35e5bf --- /dev/null +++ b/docs/learn/code/common/occt/modeling/festive-decor/frozen-snowflake.md @@ -0,0 +1,151 @@ +--- +sidebar_position: 4 +title: Frozen Snowflake +sidebar_label: Frozen Snowflake +description: Create a complex frozen snowflake ornament using zigzag patterns, advanced lofting, and selective face extrusion. +tags: [code, occt, rete, blockly, typescript] +--- + +import BitByBitRenderCanvas from '@site/src/components/BitByBitRenderCanvas'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +OCCT category icon with a stylized logo representation + +❄️ Create an intricate frozen snowflake ornament using **zigzag patterns between hexagons**, **advanced lofting techniques**, and **selective face extrusion**! This tutorial demonstrates how to build complex organic shapes by combining multiple loft surfaces and extracting specific faces for detailed geometry. + +The algorithm: +1. **Create hexagon wire pairs** - two sets of concentric hexagons at different heights (y = 0.2 and y = 0) +2. **Generate zigzag patterns** - create jagged wires between inner/outer hexagon pairs +3. **Scale zigzag wires** - scale one pattern by 3x and another by 6x to create depth variation +4. **Create three loft surfaces** - connect zigzag wires with smooth lofted surfaces +5. **Extract faces from lofts** - decompose each loft surface into individual faces +6. **Filter specific faces** - select every 2nd face from each loft for the spikey effect +7. **Extrude filtered faces** - push selected faces outward to create 3D protrusions +8. **Create two compounds** - group extruded faces into two sets with different materials +9. **Render with metallic materials** - apply blue and cyan PBR materials for an icy appearance + + + + + + + nrZigZagsinnerHex1outerHex1zigzag1zigzag1ScaledinnerHex2outerHex2zigzag2zigzag2Scaledloft1loft2loft2Translatedloft3faces1everySecondMaineverySecondEnd1faces2everySecondEnd2faces3facesListForExtrusioncurrentFaceallExtrudedFacesextrudedFaceallExtrudedFaces2currentFace2compound1compound2'city'10000.10.7TRUEnrZigZags5innerHex100.2001063outerHex100.2001063.7zigzag1innerHex1outerHex1nrZigZagsFALSEFALSETRUEzigzag1Scaledzigzag13innerHex200001062outerHex200001062.5zigzag2innerHex2outerHex2nrZigZagsFALSEFALSETRUEzigzag2Scaledzigzag26loft1zigzag2zigzag1zigzag1Scaledzigzag2ScaledFALSEFALSEFALSETRUE10FALSE31e-7approxCentripetalloft2zigzag2Scaledzigzag1ScaledFALSEFALSEFALSETRUE10FALSE31e-7approxCentripetalloft2Translatedloft200.30loft3zigzag2zigzag1FALSEFALSEFALSETRUE10FALSE31e-7approxCentripetalfaces1loft1everySecondMainfaces121TRUEeverySecondEnd1loft2Translatedfaces2everySecondEnd120TRUEeverySecondEnd2loft3faces3everySecondEnd220TRUEfacesListForExtrusioncurrentFacefaces2INSERTLASTfacesListForExtrusioncurrentFacecurrentFacefaces3INSERTLASTfacesListForExtrusioncurrentFaceallExtrudedFacescurrentFacefacesListForExtrusionextrudedFacecurrentFace00.30INSERTLASTallExtrudedFacesextrudedFaceallExtrudedFaces2currentFace2everySecondMainextrudedFacecurrentFace200.30INSERTLASTallExtrudedFaces2extrudedFacecompound1allExtrudedFaces2compound2allExtrudedFacescompound10.01Material 1#94b4ff#0000000.80.21FALSE2FALSE#ffffff4compound20.01Material 2#33ffc2#0000000.80.21FALSE0FALSE#ffffff2","version":"0.20.12","type":"blockly"}} + title="Frozen Snowflake" + /> + + + {\n const skyboxOpt = new SkyboxDto();\n skyboxOpt.skybox = skyboxEnum.city;\n skyboxOpt.hideSkybox = true;\n bitbybit.babylon.scene.enableSkybox(skyboxOpt);\n\n const nrZigZags = 5;\n\n // Create first set of hexagon wires\n const innerHex1Dto = new NGonWireDto();\n innerHex1Dto.center = [0, 0.2, 0];\n innerHex1Dto.direction = [0, 1, 0];\n innerHex1Dto.nrCorners = 6;\n innerHex1Dto.radius = 3;\n const innerHex1 = await wire.createNGonWire(innerHex1Dto);\n\n const outerHex1Dto = new NGonWireDto();\n outerHex1Dto.center = [0, 0.2, 0];\n outerHex1Dto.direction = [0, 1, 0];\n outerHex1Dto.nrCorners = 6;\n outerHex1Dto.radius = 3.7;\n const outerHex1 = await wire.createNGonWire(outerHex1Dto);\n\n const innerHex2Dto = new NGonWireDto();\n innerHex2Dto.center = [0, 0, 0];\n innerHex2Dto.direction = [0, 1, 0];\n innerHex2Dto.nrCorners = 6;\n innerHex2Dto.radius = 2;\n const innerHex2 = await wire.createNGonWire(innerHex2Dto);\n\n const outerHex2Dto = new NGonWireDto();\n outerHex2Dto.center = [0, 0, 0];\n outerHex2Dto.direction = [0, 1, 0];\n outerHex2Dto.nrCorners = 6;\n outerHex2Dto.radius = 2.5;\n const outerHex2 = await wire.createNGonWire(outerHex2Dto);\n\n // Create zigzag wires between hexagons\n const zigzag1Dto = new ZigZagBetweenTwoWiresDto();\n zigzag1Dto.wire1 = innerHex1;\n zigzag1Dto.wire2 = outerHex1;\n zigzag1Dto.nrZigZags = nrZigZags;\n zigzag1Dto.inverse = false;\n zigzag1Dto.divideByEqualDistance = false;\n zigzag1Dto.zigZagsPerEdge = true;\n const zigzag1 = await wire.createZigZagBetweenTwoWires(zigzag1Dto);\n\n const scale1Dto = new ScaleDto();\n scale1Dto.shape = zigzag1;\n scale1Dto.factor = 3;\n const zigzag1Scaled = await transforms.scale(scale1Dto);\n\n const zigzag2Dto = new ZigZagBetweenTwoWiresDto();\n zigzag2Dto.wire1 = innerHex2;\n zigzag2Dto.wire2 = outerHex2;\n zigzag2Dto.nrZigZags = nrZigZags;\n zigzag2Dto.inverse = false;\n zigzag2Dto.divideByEqualDistance = false;\n zigzag2Dto.zigZagsPerEdge = true;\n const zigzag2 = await wire.createZigZagBetweenTwoWires(zigzag2Dto);\n\n const scale2Dto = new ScaleDto();\n scale2Dto.shape = zigzag2;\n scale2Dto.factor = 6;\n const zigzag2Scaled = await transforms.scale(scale2Dto);\n\n // Create loft surfaces\n const loft1Dto = new LoftAdvancedDto();\n loft1Dto.shapes = [zigzag2, zigzag1, zigzag1Scaled, zigzag2Scaled];\n loft1Dto.makeSolid = false;\n loft1Dto.closed = false;\n loft1Dto.periodic = false;\n loft1Dto.straight = true;\n loft1Dto.nrPeriodicSections = 10;\n loft1Dto.useSmoothing = false;\n loft1Dto.maxUDegree = 3;\n loft1Dto.tolerance = 1e-7;\n loft1Dto.parType = approxParametrizationTypeEnum.approxCentripetal;\n const loft1 = await operations.loftAdvanced(loft1Dto);\n\n const loft2Dto = new LoftAdvancedDto();\n loft2Dto.shapes = [zigzag2Scaled, zigzag1Scaled];\n loft2Dto.makeSolid = false;\n loft2Dto.closed = false;\n loft2Dto.periodic = false;\n loft2Dto.straight = true;\n loft2Dto.nrPeriodicSections = 10;\n loft2Dto.useSmoothing = false;\n loft2Dto.maxUDegree = 3;\n loft2Dto.tolerance = 1e-7;\n loft1Dto.parType = approxParametrizationTypeEnum.approxCentripetal;\n const loft2 = await operations.loftAdvanced(loft2Dto);\n\n const translateDto = new TranslateDto();\n translateDto.shape = loft2;\n translateDto.translation = [0, 0.3, 0];\n const loft2Translated = await transforms.translate(translateDto);\n\n const loft3Dto = new LoftAdvancedDto();\n loft3Dto.shapes = [zigzag2, zigzag1];\n loft3Dto.makeSolid = false;\n loft3Dto.closed = false;\n loft3Dto.periodic = false;\n loft3Dto.straight = true;\n loft3Dto.nrPeriodicSections = 10;\n loft3Dto.useSmoothing = false;\n loft3Dto.maxUDegree = 3;\n loft3Dto.tolerance = 1e-7;\n loft1Dto.parType = approxParametrizationTypeEnum.approxCentripetal;\n const loft3 = await operations.loftAdvanced(loft3Dto);\n\n // Extract and filter faces\n const faces1All = await face.getFaces({ shape: loft1 });\n const everySecondMain = bitbybit.lists.getNthItem({\n list: faces1All,\n nth: 2,\n offset: 1,\n clone: true\n });\n\n const faces2All = await face.getFaces({ shape: loft2Translated });\n const everySecondEnd1 = bitbybit.lists.getNthItem({\n list: faces2All,\n nth: 2,\n offset: 0,\n clone: true\n });\n\n const faces3All = await face.getFaces({ shape: loft3 });\n const everySecondEnd2 = bitbybit.lists.getNthItem({\n list: faces3All,\n nth: 2,\n offset: 0,\n clone: true\n });\n\n // Combine faces for extrusion\n const facesForExtrusion: TopoDSFacePointer[] = [\n ...everySecondEnd1,\n ...everySecondEnd2\n ];\n\n // Extrude all faces\n const extrudedFaces: TopoDSShapePointer[] = [];\n for (const faceToExtrude of facesForExtrusion) {\n const extrudeDto = new ExtrudeDto();\n extrudeDto.shape = faceToExtrude;\n extrudeDto.direction = [0, 0.3, 0];\n const extruded = await operations.extrude(extrudeDto);\n extrudedFaces.push(extruded);\n }\n\n const extrudedFacesMain: TopoDSShapePointer[] = [];\n for (const faceToExtrude of everySecondMain) {\n const extrudeDto = new ExtrudeDto();\n extrudeDto.shape = faceToExtrude;\n extrudeDto.direction = [0, 0.3, 0];\n const extruded = await operations.extrude(extrudeDto);\n extrudedFacesMain.push(extruded);\n }\n\n // Create compounds\n const compoundDto1 = new ShapesDto();\n compoundDto1.shapes = extrudedFacesMain;\n const compound1 = await compound.makeCompound(compoundDto1);\n\n const compoundDto2 = new ShapesDto();\n compoundDto2.shapes = extrudedFaces;\n const compound2 = await compound.makeCompound(compoundDto2);\n\n // Create materials\n const material1Dto = new PBRMetallicRoughnessDto();\n material1Dto.name = 'Material 1';\n material1Dto.baseColor = '#94b4ff';\n material1Dto.emissiveColor = '#000000';\n material1Dto.metallic = 0.8;\n material1Dto.roughness = 0.2;\n material1Dto.alpha = 1;\n material1Dto.backFaceCulling = false;\n material1Dto.zOffset = 2;\n const material1 = bitbybit.babylon.material.pbrMetallicRoughness.create(material1Dto);\n\n const material2Dto = new PBRMetallicRoughnessDto();\n material2Dto.name = 'Material 2';\n material2Dto.baseColor = '#33ffc2';\n material2Dto.emissiveColor = '#000000';\n material2Dto.metallic = 0.8;\n material2Dto.roughness = 0.2;\n material2Dto.alpha = 1;\n material2Dto.backFaceCulling = false;\n material2Dto.zOffset = 0;\n const material2 = bitbybit.babylon.material.pbrMetallicRoughness.create(material2Dto);\n\n // Draw the compounds\n const drawOptions1 = new DrawOcctShapeOptions();\n drawOptions1.precision = 0.01;\n drawOptions1.faceMaterial = material1;\n drawOptions1.drawEdges = false;\n drawOptions1.edgeColour = '#ffffff';\n drawOptions1.edgeWidth = 4;\n\n await bitbybit.draw.drawAnyAsync({\n entity: compound1,\n options: drawOptions1\n });\n\n const drawOptions2 = new DrawOcctShapeOptions();\n drawOptions2.precision = 0.01;\n drawOptions2.faceMaterial = material2;\n drawOptions2.drawEdges = false;\n drawOptions2.edgeColour = '#ffffff';\n drawOptions2.edgeWidth = 2;\n\n const drawnMesh = await bitbybit.draw.drawAnyAsync({\n entity: compound2,\n options: drawOptions2\n });\n\n const zoomOnOptions = new ZoomOnDto();\n zoomOnOptions.offset = -0.1;\n zoomOnOptions.meshes = [drawnMesh];\n bitbybit.advanced.navigation.zoomOn(zoomOnOptions);\n};\n\nstart();","version":"0.20.12","type":"typescript"}} + title="Star ornament with hanging hole" + /> + + + +## How It Works + +### Hexagonal Foundation +The snowflake starts with two sets of concentric hexagons: +- **First set** (y = 0.2): Inner radius 3.0, outer radius 3.7 - forms the main structure +- **Second set** (y = 0.0): Inner radius 2.0, outer radius 2.5 - forms the base layer + +All hexagons have 6 corners and are oriented with their normal pointing in the Y direction (upward). + +### Zigzag Pattern Generation +The `createZigZagBetweenTwoWires` operation creates jagged connecting patterns: +- **zigzag1**: Connects between the first hexagon pair (radii 3.0 ↔ 3.7) +- **zigzag2**: Connects between the second hexagon pair (radii 2.0 ↔ 2.5) +- **nrZigZags = 5**: Creates 5 zigzag segments per edge (30 total around each hexagon) +- **zigZagsPerEdge = true**: Distributes zigzags evenly across each of the 6 hexagon edges + +### Scaled Variations +To create depth and complexity: +- **zigzag1Scaled**: The first zigzag pattern scaled 3x larger (radius ~9 to ~11.1) +- **zigzag2Scaled**: The second zigzag pattern scaled 6x larger (radius ~12 to ~15) + +This scaling creates dramatic size variation between inner and outer layers. + +### Advanced Lofting +Three loft surfaces are created using `loftAdvanced` with **straight interpolation**: + +1. **loft1** (Main surface): Connects 4 wires in sequence: `zigzag2 → zigzag1 → zigzag1Scaled → zigzag2Scaled` + - Creates a complex surface transitioning through all scale variations + - Forms the central "crown" of the snowflake + +2. **loft2** (Translated cap): Connects `zigzag2Scaled → zigzag1Scaled` + - Translated 0.3 units upward along Y-axis + - Forms the raised "tips" layer + +3. **loft3** (Base): Connects `zigzag2 → zigzag1` + - Remains at the base position + - Forms the foundation structure + +### Selective Face Extraction +This is where the magic happens! Each loft surface is decomposed into its constituent faces, then filtered: + +- **loft1 faces**: Extract every 2nd face starting at offset 1 → creates the main spiky protrusions +- **loft2Translated faces**: Extract every 2nd face starting at offset 0 → creates the raised end caps +- **loft3 faces**: Extract every 2nd face starting at offset 0 → creates the base end caps + +The `getNthItem` filtering creates the characteristic "every-other-spike" pattern that makes the snowflake look organic and complex rather than uniformly smooth. + +### Face Extrusion +All filtered faces are extruded 0.3 units along the Y-axis: +- **Main faces** (from loft1): Form the central spiky structure +- **End faces** (from loft2 & loft3): Form the raised and base caps + +Each face becomes a 3D protrusion, creating the frozen, crystalline appearance. + +### Compound Organization +The extruded faces are organized into two compounds: +- **Compound 1**: Main faces with blue metallic material (#94b4ff) +- **Compound 2**: End cap faces with cyan metallic material (#33ffc2) + +This two-tone approach adds visual interest and depth to the final rendering. + +## Customization ❄️ + +- **nrZigZags variable**: Change from 5 to 3-7 for different spike density (fewer = chunkier spikes, more = finer detail) +- **Hexagon radii**: Adjust the 3.0, 3.7, 2.0, 2.5 values to change the overall proportions +- **Scale factors**: Modify the 3x and 6x scaling to adjust depth (try 2x and 4x for subtler effects, or 4x and 8x for dramatic) +- **Extrusion distance**: Change 0.3 to adjust spike height (try 0.2 for subtle, 0.5 for dramatic) +- **Face filtering offset**: Change the nth/offset parameters to select different faces +- **Materials**: Experiment with different metallic/roughness values and colors for various ice effects + +## Technical Insights + +### Why Loft Advanced? +The `loftAdvanced` operation with `straight: true` creates **ruled surfaces** - straight lines connecting corresponding points on each wire. This creates sharp, geometric transitions perfect for crystalline structures rather than smooth organic curves. + +### The Power of Face Filtering +By extracting and extruding only specific faces (every 2nd one), we create: +- **Negative space**: Gaps between protrusions that catch light and shadow +- **Geometric complexity**: From simple lofts to intricate 3D structure +- **Performance optimization**: Fewer faces to extrude than processing all faces + +### Compound vs Boolean Union +Unlike the frost-flower tutorial which uses boolean union, this design uses **compounds**: +- **Pros**: Faster computation, preserves individual faces, allows multiple materials +- **Cons**: Not a single unified solid (but perfect for visualization and decoration) + +## Rendering & Visualization Tips + +- **Materials**: The metallic PBR materials with high metallic (0.8) and low roughness (0.2) create realistic ice crystals +- **Colors**: Blue (#94b4ff) and cyan (#33ffc2) evoke frozen water and winter themes +- **Lighting**: The compound approach with different zOffsets creates depth in the rendering +- **Scale**: The default size (~30 units) works well for visualization - adjust based on your scene +- **Camera**: The complex geometry looks best when viewed from slightly above at an angle +- **Animation**: Try rotating slowly around the Y-axis to show off the intricate spike patterns + +Happy modeling! ❄️✨ \ No newline at end of file diff --git a/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md b/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md index 4fa8c066..c420d5be 100644 --- a/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md +++ b/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md @@ -28,7 +28,7 @@ Our star begins as a simple 2D wire created using the `createStarWire` function. @@ -75,21 +75,21 @@ The cylinder is positioned at the top of the star (at the outer radius on the Z- outerRadiusouterRadius5.10000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.150.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001","version":"0.20.12","type":"blockly"}} + script={{"script":"outerRadiusouterRadius5.10000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.150.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001","version":"0.20.13","type":"blockly"}} title="Star ornament with hanging hole" /> {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.12","type":"typescript"}} + script={{"script":"const { StarDto, FilletDto, FaceFromWireDto, ExtrudeDto, CylinderDto, DifferenceDto } = Bit.Inputs.OCCT;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\n\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\nconst { wire, face, solid } = bitbybit.occt.shapes;\nconst { fillets, operations, booleans } = bitbybit.occt;\n\nconst start = async () => {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.13","type":"typescript"}} title="Star ornament with hanging hole" /> @@ -117,21 +117,21 @@ Now that we have our star ornament ready, let's export it as an STL file for 3D outerRadiusfinalStarouterRadius5.1finalStar0000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.15finalStar0.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001finalStarstar.stl0.001FALSETRUE","version":"0.20.12","type":"blockly"}} + script={{"script":"outerRadiusfinalStarouterRadius5.1finalStar0000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.15finalStar0.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001finalStarstar.stl0.001FALSETRUE","version":"0.20.13","type":"blockly"}} title="Star ornament with STL export" /> {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n\n // Export to STL for 3D printing\n const stlOptions = new SaveStlDto();\n stlOptions.shape = finalStar;\n stlOptions.fileName = \"star.stl\";\n stlOptions.precision = 0.001;\n stlOptions.adjustYtoZ = false;\n stlOptions.tryDownload = true;\n stlOptions.binary = true;\n await io.saveShapeStl(stlOptions);\n}\n\nstart();","version":"0.20.12","type":"typescript"}} + script={{"script":"const { StarDto, FilletDto, FaceFromWireDto, ExtrudeDto, CylinderDto, DifferenceDto } = Bit.Inputs.OCCT;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\nconst { SaveStlDto } = Bit.Inputs.OCCT;\n\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\nconst { wire, face, solid } = bitbybit.occt.shapes;\nconst { fillets, operations, booleans, io } = bitbybit.occt;\n\nconst start = async () => {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n\n // Export to STL for 3D printing\n const stlOptions = new SaveStlDto();\n stlOptions.shape = finalStar;\n stlOptions.fileName = \"star.stl\";\n stlOptions.precision = 0.001;\n stlOptions.adjustYtoZ = false;\n stlOptions.tryDownload = true;\n stlOptions.binary = true;\n await io.saveShapeStl(stlOptions);\n}\n\nstart();","version":"0.20.13","type":"typescript"}} title="Star ornament with STL export" /> diff --git a/docs/learn/code/common/occt/modeling/festive-decor/tree.md b/docs/learn/code/common/occt/modeling/festive-decor/tree.md index 937abcab..7bb6753c 100644 --- a/docs/learn/code/common/occt/modeling/festive-decor/tree.md +++ b/docs/learn/code/common/occt/modeling/festive-decor/tree.md @@ -28,21 +28,21 @@ Our tree starts with the `createChristmasTreeWire` function, which generates a s heightnrSkirtsthicknesstreeWireoffsetWirereversedWiretreeFacetreeSolidfinalTreedrawnTreeMeshheight8nrSkirts4thickness2treeWireheight1.53nrSkirts11FALSE0000010offsetWiretreeWire-0.20.1reversedWireoffsetWiretreeFacetreeWirereversedWireTRUEtreeSolidtreeFace00thicknessfinalTreetreeSolid00DIVIDENEGthickness2drawnTreeMeshfinalTree0.001Tree Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001drawnTreeMeshTRUE0.80TRUE","version":"0.20.12","type":"blockly"}} + script={{"script":"heightnrSkirtsthicknesstreeWireoffsetWirereversedWiretreeFacetreeSolidfinalTreedrawnTreeMeshheight8nrSkirts4thickness2treeWireheight1.53nrSkirts11FALSE0000010offsetWiretreeWire-0.20.1reversedWireoffsetWiretreeFacetreeWirereversedWireTRUEtreeSolidtreeFace00thicknessfinalTreetreeSolid00DIVIDENEGthickness2drawnTreeMeshfinalTree0.001Tree Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001drawnTreeMeshTRUE0.80TRUE","version":"0.20.13","type":"blockly"}} title="Christmas tree ornament" /> {\n // Define control variables\n const height = 6.5;\n const nrSkirts = 5;\n const thickness = 0.5;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Tree Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the Christmas tree wire\n const treeOptions = new ChristmasTreeDto();\n treeOptions.height = height;\n treeOptions.nrSkirts = nrSkirts;\n treeOptions.innerDist = 1.5;\n treeOptions.outerDist = 3;\n treeOptions.trunkHeight = 1;\n treeOptions.trunkWidth = 1;\n treeOptions.origin = [0, 0, 0];\n treeOptions.direction = [0, 1, 0];\n treeOptions.half = false;\n const treeWire = await wire.createChristmasTreeWire(treeOptions);\n\n // Create offset wire (inner boundary)\n const offsetOptions = new OffsetDto();\n offsetOptions.shape = treeWire;\n offsetOptions.distance = -0.2;\n offsetOptions.tolerance = 0.1;\n const offsetWire = await operations.offset(offsetOptions);\n\n // Reverse the inner wire for proper face creation\n const reversedWire = await wire.reversedWire({ shape: offsetWire });\n\n // Create face from both wires (outer and reversed inner)\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [treeWire, reversedWire];\n faceOptions.planar = true;\n const treeFace = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create thickness\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = treeFace;\n extrudeOptions.direction = [0, 0, thickness];\n const treeSolid = await operations.extrude(extrudeOptions);\n\n // Translate to center the shape\n const translateOptions = new TranslateDto();\n translateOptions.shape = treeSolid;\n translateOptions.translation = [0, 0, -thickness / 2];\n const finalTree = await transforms.translate(translateOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n const drawnTreeMesh = await bitbybit.draw.drawAnyAsync({\n entity: finalTree,\n options: drawOptions\n });\n\n const zoomOptions = new ZoomOnDto([drawnTreeMesh]);\n bitbybit.advanced.navigation.zoomOn(zoomOptions);\n}\n\nstart();","version":"0.20.12","type":"typescript"}} + script={{"script":"const { ChristmasTreeDto, OffsetDto, FaceFromWiresDto, ExtrudeDto, TranslateDto } = Bit.Inputs.OCCT;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\nconst { ZoomOnDto } = Bit.Advanced.Navigation;\n\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\nconst { wire, face } = bitbybit.occt.shapes;\nconst { operations, transforms } = bitbybit.occt;\n\nconst start = async () => {\n // Define control variables\n const height = 6.5;\n const nrSkirts = 5;\n const thickness = 0.5;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Tree Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the Christmas tree wire\n const treeOptions = new ChristmasTreeDto();\n treeOptions.height = height;\n treeOptions.nrSkirts = nrSkirts;\n treeOptions.innerDist = 1.5;\n treeOptions.outerDist = 3;\n treeOptions.trunkHeight = 1;\n treeOptions.trunkWidth = 1;\n treeOptions.origin = [0, 0, 0];\n treeOptions.direction = [0, 1, 0];\n treeOptions.half = false;\n const treeWire = await wire.createChristmasTreeWire(treeOptions);\n\n // Create offset wire (inner boundary)\n const offsetOptions = new OffsetDto();\n offsetOptions.shape = treeWire;\n offsetOptions.distance = -0.2;\n offsetOptions.tolerance = 0.1;\n const offsetWire = await operations.offset(offsetOptions);\n\n // Reverse the inner wire for proper face creation\n const reversedWire = await wire.reversedWire({ shape: offsetWire });\n\n // Create face from both wires (outer and reversed inner)\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [treeWire, reversedWire];\n faceOptions.planar = true;\n const treeFace = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create thickness\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = treeFace;\n extrudeOptions.direction = [0, 0, thickness];\n const treeSolid = await operations.extrude(extrudeOptions);\n\n // Translate to center the shape\n const translateOptions = new TranslateDto();\n translateOptions.shape = treeSolid;\n translateOptions.translation = [0, 0, -thickness / 2];\n const finalTree = await transforms.translate(translateOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n const drawnTreeMesh = await bitbybit.draw.drawAnyAsync({\n entity: finalTree,\n options: drawOptions\n });\n\n const zoomOptions = new ZoomOnDto([drawnTreeMesh]);\n bitbybit.advanced.navigation.zoomOn(zoomOptions);\n}\n\nstart();","version":"0.20.13","type":"typescript"}} title="Christmas tree ornament" /> diff --git a/packages/dev/manifold-worker/lib/api/cross-section/transforms.ts b/packages/dev/manifold-worker/lib/api/cross-section/transforms.ts index a40148a9..047deec6 100644 --- a/packages/dev/manifold-worker/lib/api/cross-section/transforms.ts +++ b/packages/dev/manifold-worker/lib/api/cross-section/transforms.ts @@ -95,4 +95,19 @@ export class CrossSectionTransforms { return this.manifoldWorkerManager.genericCallToWorkerPromise("crossSection.transforms.transform", inputs); } + /** + * Move the vertices of this CrossSection (creating a new one) according to + * any arbitrary input function, followed by a union operation (with a + * Positive fill rule) that ensures any introduced intersections are not + * included in the result. + * @param inputs cross section and warp function + * @returns Warped cross section shape + * @group transforms + * @shortname warp + * @drawable true + */ + async warp(inputs: Inputs.Manifold.CrossSectionWarpDto): Promise { + return this.manifoldWorkerManager.genericCallToWorkerPromise("crossSection.transforms.warp", inputs); + } + } diff --git a/packages/dev/manifold-worker/lib/api/manifold/evaluate.ts b/packages/dev/manifold-worker/lib/api/manifold/evaluate.ts index b1d67408..f9648e7e 100644 --- a/packages/dev/manifold-worker/lib/api/manifold/evaluate.ts +++ b/packages/dev/manifold-worker/lib/api/manifold/evaluate.ts @@ -170,7 +170,7 @@ export class ManifoldEvaluate { * by product manifolds. If this manifold is a product, this * returns -1. * @param inputs manifold - * @returns original ID of manifold + * @returns original id of manifold * @group basic * @shortname original id * @drawable false @@ -179,4 +179,19 @@ export class ManifoldEvaluate { return this.manifoldWorkerManager.genericCallToWorkerPromise("manifold.evaluate.originalID", inputs); } + /** + * Returns the reason for an input Mesh producing an empty Manifold. This + * Status will carry on through operations like NaN propogation, ensuring an + * errored mesh doesn't get mysteriously lost. Empty meshes may still show + * NoError, for instance the intersection of non-overlapping meshes. + * @param inputs manifold + * @returns error status string (NoError, NotManifold, InvalidConstruction, etc.) + * @group basic + * @shortname status + * @drawable false + */ + async status(inputs: Inputs.Manifold.ManifoldDto): Promise { + return this.manifoldWorkerManager.genericCallToWorkerPromise("manifold.evaluate.status", inputs); + } + } diff --git a/packages/dev/manifold-worker/lib/api/manifold/operations.ts b/packages/dev/manifold-worker/lib/api/manifold/operations.ts index 2d64490a..07d8df4d 100644 --- a/packages/dev/manifold-worker/lib/api/manifold/operations.ts +++ b/packages/dev/manifold-worker/lib/api/manifold/operations.ts @@ -251,4 +251,35 @@ export class ManifoldOperations { return this.manifoldWorkerManager.genericCallToWorkerPromise("manifold.operations.smoothByNormals", inputs); } + /** + * Return a copy of the manifold simplified to the given tolerance, but with + * its actual tolerance value unchanged. The result will contain a subset of + * the original verts and all surfaces will have moved by less than tolerance. + * @param inputs manifold and tolerance + * @returns simplified manifold + * @group adjustments + * @shortname simplify + * @drawable true + */ + async simplify(inputs: Inputs.Manifold.ManifoldSimplifyDto): Promise { + return this.manifoldWorkerManager.genericCallToWorkerPromise("manifold.operations.simplify", inputs); + } + + /** + * Create a new copy of this manifold with updated vertex properties by + * supplying a function that takes the existing position and properties as + * input. You may specify any number of output properties, allowing creation + * and removal of channels. Note: undefined behavior will result if you read + * past the number of input properties or write past the number of output + * properties. + * @param inputs manifold, numProp and property function + * @returns manifold with updated properties + * @group adjustments + * @shortname set properties + * @drawable true + */ + async setProperties(inputs: Inputs.Manifold.ManifoldSetPropertiesDto): Promise { + return this.manifoldWorkerManager.genericCallToWorkerPromise("manifold.operations.setProperties", inputs); + } + } diff --git a/packages/dev/manifold-worker/lib/api/manifold/transforms.ts b/packages/dev/manifold-worker/lib/api/manifold/transforms.ts index 74b6d40a..9a84b88d 100644 --- a/packages/dev/manifold-worker/lib/api/manifold/transforms.ts +++ b/packages/dev/manifold-worker/lib/api/manifold/transforms.ts @@ -132,5 +132,20 @@ export class ManifoldTransforms { return this.manifoldWorkerManager.genericCallToWorkerPromise("manifold.transforms.transforms", inputs); } + /** + * Move the vertices of this Manifold (creating a new one) according to any + * arbitrary input function. It is easy to create a function that warps a + * geometrically valid object into one which overlaps, but that is not checked + * here, so it is up to the user to choose their function with discretion. + * @param inputs manifold and warp function + * @returns Warped manifold shape + * @group transforms + * @shortname warp + * @drawable true + */ + async warp(inputs: Inputs.Manifold.ManifoldWarpDto): Promise { + return this.manifoldWorkerManager.genericCallToWorkerPromise("manifold.transforms.warp", inputs); + } + } diff --git a/packages/dev/manifold/lib/api/inputs/manifold-inputs.ts b/packages/dev/manifold/lib/api/inputs/manifold-inputs.ts index a0c28392..2b3dfa50 100644 --- a/packages/dev/manifold/lib/api/inputs/manifold-inputs.ts +++ b/packages/dev/manifold/lib/api/inputs/manifold-inputs.ts @@ -16,7 +16,8 @@ export namespace Manifold { export enum manifoldJoinTypeEnum { square = "Square", round = "Round", - miter = "Miter" + miter = "Miter", + bevel = "Bevel" } export class DecomposedManifoldMeshDto { numProp: number; @@ -527,6 +528,51 @@ export namespace Manifold { */ normalIdx = 0; } + export class ManifoldSimplifyDto { + constructor(manifold?: T, tolerance?: number) { + if (manifold !== undefined) { this.manifold = manifold; } + if (tolerance !== undefined) { this.tolerance = tolerance; } + } + /** + * Manifold shape + */ + manifold: T; + /** + * The maximum distance between the original and simplified meshes. + * If not given or is less than the current tolerance, the current tolerance is used. + * The result will contain a subset of the original verts and all surfaces will have moved by less than tolerance. + * @default undefined + * @minimum 0 + * @maximum Infinity + * @step 0.001 + */ + tolerance?: number; + } + export class ManifoldSetPropertiesDto { + constructor(manifold?: T, numProp?: number, propFunc?: (newProp: number[], position: Base.Vector3, oldProp: number[]) => void) { + if (manifold !== undefined) { this.manifold = manifold; } + if (numProp !== undefined) { this.numProp = numProp; } + if (propFunc !== undefined) { this.propFunc = propFunc; } + } + /** + * Manifold shape + */ + manifold: T; + /** + * The new number of properties per vertex + * @default 3 + * @minimum 3 + * @maximum Infinity + * @step 1 + */ + numProp = 3; + /** + * A function that modifies the properties of a given vertex. + * Note: undefined behavior will result if you read past the number of input properties or write past the number of output properties. + * @default undefined + */ + propFunc: (newProp: number[], position: Base.Vector3, oldProp: number[]) => void; + } export class ManifoldSmoothOutDto { constructor(manifold?: T, minSharpAngle?: number, minSmoothness?: number) { if (manifold !== undefined) { this.manifold = manifold; } @@ -978,6 +1024,21 @@ export namespace Manifold { */ transform: Base.TransformMatrix3x3; } + export class CrossSectionWarpDto { + constructor(crossSection?: T, warpFunc?: (vert: Base.Vector2) => void) { + if (crossSection !== undefined) { this.crossSection = crossSection; } + if (warpFunc !== undefined) { this.warpFunc = warpFunc; } + } + /** + * Cross section + */ + crossSection: T; + /** + * A function that modifies a given vertex position + * @default undefined + */ + warpFunc: (vert: Base.Vector2) => void; + } export class MirrorDto { constructor(manifold?: T, normal?: Base.Vector3) { if (manifold !== undefined) { this.manifold = manifold; } @@ -1171,6 +1232,21 @@ export namespace Manifold { */ transforms: Base.TransformMatrixes; } + export class ManifoldWarpDto { + constructor(manifold?: T, warpFunc?: (vert: Base.Vector3) => void) { + if (manifold !== undefined) { this.manifold = manifold; } + if (warpFunc !== undefined) { this.warpFunc = warpFunc; } + } + /** + * Manifold shape + */ + manifold: T; + /** + * A function that modifies a given vertex position + * @default undefined + */ + warpFunc: (vert: Base.Vector3) => void; + } export class TwoCrossSectionsDto { constructor(crossSection1?: T, crossSection2?: T) { if (crossSection1 !== undefined) { this.crossSection1 = crossSection1; } diff --git a/packages/dev/manifold/lib/api/services/cross-section/cross-section-operations.ts b/packages/dev/manifold/lib/api/services/cross-section/cross-section-operations.ts index 23520be3..7cf9159f 100644 --- a/packages/dev/manifold/lib/api/services/cross-section/cross-section-operations.ts +++ b/packages/dev/manifold/lib/api/services/cross-section/cross-section-operations.ts @@ -27,7 +27,7 @@ export class CrossSectionOperations { } offset(inputs: Inputs.Manifold.OffsetDto): Manifold3D.CrossSection { - return inputs.crossSection.offset(inputs.delta, inputs.joinType, inputs.miterLimit, inputs.circularSegments); + return inputs.crossSection.offset(inputs.delta, inputs.joinType as Manifold3D.JoinType, inputs.miterLimit, inputs.circularSegments); } simplify(inputs: Inputs.Manifold.SimplifyDto): Manifold3D.CrossSection { diff --git a/packages/dev/manifold/lib/api/services/cross-section/cross-section-transforms.ts b/packages/dev/manifold/lib/api/services/cross-section/cross-section-transforms.ts index 20ebe8d0..000201b8 100644 --- a/packages/dev/manifold/lib/api/services/cross-section/cross-section-transforms.ts +++ b/packages/dev/manifold/lib/api/services/cross-section/cross-section-transforms.ts @@ -37,4 +37,8 @@ export class CrossSectionTransforms { return inputs.crossSection.transform(inputs.transform); } + warp(inputs: Inputs.Manifold.CrossSectionWarpDto): Manifold3D.CrossSection { + return inputs.crossSection.warp(inputs.warpFunc); + } + } diff --git a/packages/dev/manifold/lib/api/services/manifold/manifold-evaluate.ts b/packages/dev/manifold/lib/api/services/manifold/manifold-evaluate.ts index 9e47f9d1..2b183bdb 100644 --- a/packages/dev/manifold/lib/api/services/manifold/manifold-evaluate.ts +++ b/packages/dev/manifold/lib/api/services/manifold/manifold-evaluate.ts @@ -62,4 +62,8 @@ export class ManifoldEvaluate { return inputs.manifold.originalID(); } + status(inputs: Inputs.Manifold.ManifoldDto): string { + return inputs.manifold.status(); + } + } diff --git a/packages/dev/manifold/lib/api/services/manifold/manifold-operations.ts b/packages/dev/manifold/lib/api/services/manifold/manifold-operations.ts index a8465487..5827e4f7 100644 --- a/packages/dev/manifold/lib/api/services/manifold/manifold-operations.ts +++ b/packages/dev/manifold/lib/api/services/manifold/manifold-operations.ts @@ -78,4 +78,12 @@ export class ManifoldOperations { smoothByNormals(inputs: Inputs.Manifold.ManifoldSmoothByNormalsDto): Manifold3D.Manifold { return inputs.manifold.smoothByNormals(inputs.normalIdx); } + + simplify(inputs: Inputs.Manifold.ManifoldSimplifyDto): Manifold3D.Manifold { + return inputs.manifold.simplify(inputs.tolerance); + } + + setProperties(inputs: Inputs.Manifold.ManifoldSetPropertiesDto): Manifold3D.Manifold { + return inputs.manifold.setProperties(inputs.numProp, inputs.propFunc); + } } diff --git a/packages/dev/manifold/lib/api/services/manifold/manifold-transforms.ts b/packages/dev/manifold/lib/api/services/manifold/manifold-transforms.ts index 02f9609c..acad318d 100644 --- a/packages/dev/manifold/lib/api/services/manifold/manifold-transforms.ts +++ b/packages/dev/manifold/lib/api/services/manifold/manifold-transforms.ts @@ -61,4 +61,8 @@ export class ManifoldTransforms { }); return res; } + + warp(inputs: Inputs.Manifold.ManifoldWarpDto): Manifold3D.Manifold { + return inputs.manifold.warp(inputs.warpFunc); + } } diff --git a/packages/dev/manifold/package-lock.json b/packages/dev/manifold/package-lock.json index 12a1ee4b..c76efdf7 100644 --- a/packages/dev/manifold/package-lock.json +++ b/packages/dev/manifold/package-lock.json @@ -9,7 +9,7 @@ "version": "0.20.13", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" }, "devDependencies": { "@babel/core": "7.16.0", @@ -1724,6 +1724,523 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2401,7 +2918,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -2418,19 +2934,24 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2690,6 +3211,12 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "18.14.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz", @@ -3307,6 +3834,15 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3319,6 +3855,17 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.29.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.0.tgz", @@ -3389,6 +3936,15 @@ "node": ">=0.10.0" } }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3439,6 +3995,15 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3515,6 +4080,30 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3651,6 +4240,12 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3909,6 +4504,12 @@ "node": ">= 0.10" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3927,6 +4528,12 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", @@ -5828,6 +6435,12 @@ "node": ">=6" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -5913,9 +6526,31 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -6005,6 +6640,47 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -6276,6 +6952,12 @@ "node": ">= 6" } }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -6535,6 +7217,62 @@ "semver": "bin/semver.js" } }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7092,11 +7830,20 @@ } } }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true + "devOptional": true }, "node_modules/type-detect": { "version": "4.0.8", @@ -7172,6 +7919,12 @@ "node": ">=4" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -8568,6 +9321,227 @@ } } }, + "@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "requires": { + "property-graph": "^3.0.0" + } + }, + "@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + } + }, + "@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + } + }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==" + }, + "@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "optional": true + }, + "@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "optional": true + }, + "@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "optional": true, + "requires": { + "@emnapi/runtime": "^1.7.0" + } + }, + "@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "optional": true + }, + "@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "optional": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -9082,8 +10056,7 @@ "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" }, "@jridgewell/set-array": { "version": "1.1.2", @@ -9094,19 +10067,22 @@ "@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9333,6 +10309,11 @@ "pretty-format": "^29.0.0" } }, + "@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==" + }, "@types/node": { "version": "18.14.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz", @@ -9787,6 +10768,11 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -9799,6 +10785,11 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==" + }, "core-js-compat": { "version": "3.29.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.0.tgz", @@ -9856,6 +10847,14 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -9889,6 +10888,11 @@ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -9949,6 +10953,19 @@ "is-arrayish": "^0.2.1" } }, + "esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "requires": { + "ts-replace-all": "^1.0.0" + } + }, + "esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==" + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -10052,6 +11069,11 @@ "bser": "2.1.1" } }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -10238,6 +11260,11 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -10253,6 +11280,11 @@ "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", @@ -11656,6 +12688,11 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==" + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -11729,9 +12766,28 @@ } }, "manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "requires": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + } + } }, "merge-stream": { "version": "2.0.0", @@ -11800,6 +12856,43 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "requires": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "requires": { + "cwise-compiler": "^1.0.0" + } + }, + "ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "requires": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -11995,6 +13088,11 @@ "sisteransi": "^1.0.5" } }, + "property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==" + }, "pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -12175,6 +13273,47 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "dependencies": { + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" + } + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -12556,11 +13695,19 @@ "yn": "3.1.1" } }, + "ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "requires": { + "core-js": "^3.4.1" + } + }, "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true + "devOptional": true }, "type-detect": { "version": "4.0.8", @@ -12608,6 +13755,11 @@ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/packages/dev/manifold/package.json b/packages/dev/manifold/package.json index 68a40d06..2ea76f97 100644 --- a/packages/dev/manifold/package.json +++ b/packages/dev/manifold/package.json @@ -58,7 +58,7 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" }, "devDependencies": { "shx":"0.4.0", From 69de45e989c9cdb1eb5495e3e2192b77343098e6 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 22 Dec 2025 10:28:52 +0200 Subject: [PATCH 27/38] some new helper functions for math and lists. Also improved comments on methods. --- .../modeling/festive-decor/tree-decoration.md | 88 +++++ .../dev/base/lib/api/inputs/lists-inputs.ts | 48 +++ .../dev/base/lib/api/inputs/math-inputs.ts | 177 ++++++++++ packages/dev/base/lib/api/services/color.ts | 39 ++- packages/dev/base/lib/api/services/dates.ts | 92 +++-- .../base/lib/api/services/geometry-helper.ts | 52 ++- .../base/lib/api/services/helpers/csv/csv.ts | 6 + .../base/lib/api/services/helpers/dxf/dxf.ts | 28 +- packages/dev/base/lib/api/services/line.ts | 50 ++- .../dev/base/lib/api/services/lists.test.ts | 221 ++++++++++++ packages/dev/base/lib/api/services/lists.ts | 319 ++++++++++++++++-- packages/dev/base/lib/api/services/logic.ts | 37 +- .../dev/base/lib/api/services/math.test.ts | 275 +++++++++++++++ packages/dev/base/lib/api/services/math.ts | 260 ++++++++++++-- packages/dev/base/lib/api/services/mesh.ts | 23 +- packages/dev/base/lib/api/services/point.ts | 95 ++++-- .../dev/base/lib/api/services/polyline.ts | 38 ++- packages/dev/base/lib/api/services/text.ts | 23 +- .../dev/base/lib/api/services/transforms.ts | 46 ++- packages/dev/base/lib/api/services/vector.ts | 113 ++++--- 20 files changed, 1771 insertions(+), 259 deletions(-) create mode 100644 docs/learn/code/common/occt/modeling/festive-decor/tree-decoration.md create mode 100644 packages/dev/base/lib/api/services/helpers/csv/csv.ts diff --git a/docs/learn/code/common/occt/modeling/festive-decor/tree-decoration.md b/docs/learn/code/common/occt/modeling/festive-decor/tree-decoration.md new file mode 100644 index 00000000..62a9eb27 --- /dev/null +++ b/docs/learn/code/common/occt/modeling/festive-decor/tree-decoration.md @@ -0,0 +1,88 @@ +--- +sidebar_position: 5 +title: Tree Decoration +sidebar_label: Tree Decoration +description: Create a 3D printable Christmas tree decoration. +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 + +## Overview + +This tutorial demonstrates how to create a 3D printable Christmas tree ornament with a parametric star pattern design. The ornament features decorative openings that can be customized by adjusting the number of rays and vertical divisions. You'll learn how to use OCCT lofting operations, face subdivisions, and interactive GUI controls to create a design you can export for 3D printing. + +## How It Works + +### Geometry Creation + +The ornament starts with a **star wire** created using `createStarWire` with customizable rays, outer radius (20mm), and inner radius (12mm). This star profile is then transformed into three variants: + +- **Top section**: Scaled down to 25% and translated up 30mm +- **Middle section**: Scaled to 15% and positioned at 20mm height +- **Bottom section**: Scaled to 30% and positioned at -5mm + +### Surface Construction + +The geometry is built using **loft operations** that smoothly transition between wire profiles: + +1. **Upper loft**: Blends from top → middle → base star using standard loft +2. **Lower loft**: Creates the bottom section using `loftAdvanced` with a custom start vertex at (0, -10, 0) for controlled shape flow + +### Pattern Generation + +The middle section features decorative rectangular openings created by: + +1. Getting faces from the upper loft surface +2. Using `subdivideToRectangleHoles` to create a pattern with: + - Vertical divisions (controlled by slider, default 5) + - Scale pattern [0.75, 0.5] for varied opening sizes + - Inclusion pattern [true, false, true, true] for selective hole placement + - 0.02mm offset from borders for clean geometry + +The first face is reversed and sewn to form the bottom cap. + +### Final Assembly + +The separate surfaces are combined into a closed solid through: + +1. **Face collection**: Top cap, upper loft, lower loft, patterned surface, and bottom cap +2. **Sewing**: `sewFaces` joins all surfaces with 1e-7 tolerance +3. **Solidification**: `fromClosedShell` converts the sewn shell into a manifold solid +4. **Scaling**: Final 0.5x reduction for printable size + +### Scene Setup + +The visualization includes: +- Arc rotate camera positioned at (30, 25, 0) looking at (0, 4, 0) +- City skybox environment with fog for depth +- Two directional lights (white and blue) for attractive rendering +- PBR metallic material with blue tones (#94c2ff base, #3b77b0 emissive) and 0.8 metallic/0.29 roughness values + +### Interactive Controls + +The GUI provides real-time adjustments: +- **Number of Rays slider** (3-12): Controls star complexity +- **Vertical Divisions slider** (1-25): Adjusts pattern density +- **Export buttons**: Download as STEP or STL for 3D printing +- **Sign Up** and **Source Code** buttons for community features + +All controls use observable listeners to trigger geometry regeneration when values change. + + + + {\\n // ADD YOUR CODE HERE\\n if (inputs === true) {\\n window.open(\\\"https://bitbybit.dev/app/bitbybit/KtIymkYFZ8Qty9h8mhew/Y7NQIgaXLy0dqffnc825?editor=rete\\\", '_blank').focus();\\n }\\n return inputs;\\n}\"}},\"inputs\":{\"inputs\":{\"connections\":[{\"node\":\"fbe1705ff510913a\",\"output\":\"isA\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"fbe1705ff510913a\",\"output\":\"execA\",\"data\":{}},{\"node\":\"fbe1705ff510913a\",\"output\":\"execB\",\"data\":{}}]}},\"position\":[1749.7763306366996,10636.786904135595]},\"af7350efdb0b85b3\":{\"id\":\"af7350efdb0b85b3\",\"name\":\"bitbybit.babylon.gui.control.createControlObservableSelector\",\"customName\":\"control observable selector\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"selector\":\"onPointerUpObservable\"},\"inputs\":{},\"position\":[686.4715121547069,8508.681235887017]},\"f2e18ffd18566787\":{\"id\":\"f2e18ffd18566787\",\"name\":\"bitbybit.flow.babylon.observableListener\",\"customName\":\"babylon observable listener\",\"data\":{},\"inputs\":{\"object\":{\"connections\":[{\"node\":\"22b0aaced3db29ad\",\"output\":\"result\",\"data\":{}}]},\"observableSelector\":{\"connections\":[{\"node\":\"af7350efdb0b85b3\",\"output\":\"result\",\"data\":{}}]}},\"position\":[1453.070365919411,8258.538315404885]},\"70259fc69f3db105\":{\"id\":\"70259fc69f3db105\",\"name\":\"bitbybit.flow.time.delay\",\"customName\":\"delay\",\"data\":{\"timeout\":0},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"f2e18ffd18566787\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[1987.6045976314351,8437.042718662588]},\"291c95f88aae5725\":{\"id\":\"291c95f88aae5725\",\"name\":\"bitbybit.flow.logic.flipFlop\",\"customName\":\"flip flop\",\"data\":{},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"70259fc69f3db105\",\"output\":\"exec\",\"data\":{}},{\"node\":\"f2e18ffd18566787\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[2323.4070267726706,8288.053843144928]},\"ac5f02670de06852\":{\"id\":\"ac5f02670de06852\",\"name\":\"bitbybit.code.typeScriptEditor\",\"customName\":\"typescript editor\",\"async\":true,\"drawable\":true,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":true},\"code\":{\"code\":\"// DO NOT REMOVE THIS FUNCTION\\nconst startac5f02670de06852 = async (inputs: any, index: number) => {\\n // ADD YOUR CODE HERE\\n if (inputs === true) {\\n window.open(\\\"https://bitbybit.dev/auth/sign-up\\\", '_blank').focus();\\n }\\n return inputs;\\n}\"}},\"inputs\":{\"inputs\":{\"connections\":[{\"node\":\"291c95f88aae5725\",\"output\":\"isA\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"291c95f88aae5725\",\"output\":\"execA\",\"data\":{}},{\"node\":\"291c95f88aae5725\",\"output\":\"execB\",\"data\":{}}]}},\"position\":[2681.990286952205,8287.587249893892]},\"b5af9279ffdd84e5\":{\"id\":\"b5af9279ffdd84e5\",\"name\":\"bitbybit.babylon.scene.enableSkybox\",\"customName\":\"enable skybox\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"skybox\":\"city\",\"size\":1000,\"blur\":0.5,\"environmentIntensity\":0.7,\"hideSkybox\":false},\"inputs\":{},\"position\":[2884.1306675480555,-186.0490873535386]},\"9dca219fedacecca\":{\"id\":\"9dca219fedacecca\",\"name\":\"bitbybit.flow.babylon.observableListener\",\"customName\":\"babylon observable listener\",\"data\":{},\"inputs\":{\"observableSelector\":{\"connections\":[{\"node\":\"f1f8cf3eadfa0ebb\",\"output\":\"result\",\"data\":{}}]},\"object\":{\"connections\":[{\"node\":\"71fbc1ccee37d577\",\"output\":\"result\",\"data\":{}}]}},\"position\":[385.9268891419183,10329.874563653837]},\"bb201b1ec9f17a1e\":{\"id\":\"bb201b1ec9f17a1e\",\"name\":\"bitbybit.flow.babylon.observableListener\",\"customName\":\"babylon observable listener\",\"data\":{},\"inputs\":{\"observableSelector\":{\"connections\":[{\"node\":\"f1f8cf3eadfa0ebb\",\"output\":\"result\",\"data\":{}}]},\"object\":{\"connections\":[{\"node\":\"fc36f702c3f8146e\",\"output\":\"result\",\"data\":{}}]}},\"position\":[394.16500271911553,10076.96114156523]},\"4a5d1c196a0b447e\":{\"id\":\"4a5d1c196a0b447e\",\"name\":\"bitbybit.flow.time.delay\",\"customName\":\"delay\",\"data\":{\"timeout\":0},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"9dca219fedacecca\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[936.4095297912264,10457.862715124222]},\"4fd171db189ad173\":{\"id\":\"4fd171db189ad173\",\"name\":\"bitbybit.flow.logic.flipFlop\",\"customName\":\"flip flop\",\"data\":{},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"9dca219fedacecca\",\"output\":\"exec\",\"data\":{}},{\"node\":\"4a5d1c196a0b447e\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[1273.654131279585,10355.281487174021]},\"94b6dd1354940df2\":{\"id\":\"94b6dd1354940df2\",\"name\":\"bitbybit.flow.time.delay\",\"customName\":\"delay\",\"data\":{\"timeout\":0},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"bb201b1ec9f17a1e\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[926.4956949832613,10212.522265939324]},\"27eac850201a9701\":{\"id\":\"27eac850201a9701\",\"name\":\"bitbybit.flow.logic.flipFlop\",\"customName\":\"flip flop\",\"data\":{},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"bb201b1ec9f17a1e\",\"output\":\"exec\",\"data\":{}},{\"node\":\"94b6dd1354940df2\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[1272.0778730255988,10103.055278248028]},\"9190095ec9cfd394\":{\"id\":\"9190095ec9cfd394\",\"name\":\"bitbybit.logic.valueGate\",\"customName\":\"value gate\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"boolean\":false},\"inputs\":{\"value\":{\"connections\":[{\"node\":\"73ee3cd6670755bb\",\"output\":\"result\",\"data\":{}}]},\"boolean\":{\"connections\":[{\"node\":\"27eac850201a9701\",\"output\":\"isA\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"27eac850201a9701\",\"output\":\"execA\",\"data\":{}},{\"node\":\"27eac850201a9701\",\"output\":\"execB\",\"data\":{}}]}},\"position\":[2098.7596218076405,9630.876889007699]},\"73ee3cd6670755bb\":{\"id\":\"73ee3cd6670755bb\",\"name\":\"bitbybit.text.create\",\"customName\":\"create\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"text\":\"bitbybit-toy.step\"},\"inputs\":{},\"position\":[1701.8222304032442,9540.575618331815]},\"f9a87606405961ae\":{\"id\":\"f9a87606405961ae\",\"name\":\"bitbybit.text.create\",\"customName\":\"create\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"text\":\"bitbybit-toy.stl\"},\"inputs\":{},\"position\":[1934.0481177357838,10057.705993169464]},\"f8db44954a566616\":{\"id\":\"f8db44954a566616\",\"name\":\"bitbybit.logic.valueGate\",\"customName\":\"value gate\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"boolean\":false},\"inputs\":{\"value\":{\"connections\":[{\"node\":\"f9a87606405961ae\",\"output\":\"result\",\"data\":{}}]},\"boolean\":{\"connections\":[{\"node\":\"4fd171db189ad173\",\"output\":\"isA\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"4fd171db189ad173\",\"output\":\"execA\",\"data\":{}},{\"node\":\"4fd171db189ad173\",\"output\":\"execB\",\"data\":{}}]}},\"position\":[2360.5651787274305,10179.991268481712]},\"7ae3f1f373ab7ce8\":{\"id\":\"7ae3f1f373ab7ce8\",\"name\":\"bitbybit.occt.io.saveShapeSTEPAndReturn\",\"customName\":\"save shape step and return\",\"async\":true,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"fileName\":\"\",\"adjustYtoZ\":true,\"fromRightHanded\":false,\"tryDownload\":true},\"inputs\":{\"shape\":{\"connections\":[{\"node\":\"ff3a5dfe26c1203f\",\"output\":\"result\",\"data\":{}}]},\"fileName\":{\"connections\":[{\"node\":\"9190095ec9cfd394\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"9190095ec9cfd394\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[4105.580503617983,9860.933809645778]},\"b358af77da1782cf\":{\"id\":\"b358af77da1782cf\",\"name\":\"bitbybit.occt.io.saveShapeStl\",\"customName\":\"save shape stl\",\"async\":true,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"fileName\":\"shape.stl\",\"precision\":0.01,\"adjustYtoZ\":true,\"tryDownload\":true,\"binary\":true},\"inputs\":{\"fileName\":{\"connections\":[{\"node\":\"f8db44954a566616\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"f8db44954a566616\",\"output\":\"exec\",\"data\":{}}]},\"shape\":{\"connections\":[{\"node\":\"ff3a5dfe26c1203f\",\"output\":\"result\",\"data\":{}}]}},\"position\":[4097.343815578738,10313.986149668373]},\"feef4ec0e40282e7\":{\"id\":\"feef4ec0e40282e7\",\"name\":\"bitbybit.math.number\",\"customName\":\"number\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"number\":0.5},\"inputs\":{},\"position\":[2389.4392361815517,2241.565476619029]},\"03303a3d3cb2a6b5\":{\"id\":\"03303a3d3cb2a6b5\",\"name\":\"bitbybit.json.parse\",\"customName\":\"parse\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"text\":\"[true,false,true,true]\"},\"inputs\":{},\"position\":[2389.302212365796,2597.6283579746796]},\"f39c312f7e1e1bef\":{\"id\":\"f39c312f7e1e1bef\",\"name\":\"bitbybit.flow.babylon.observableListener\",\"customName\":\"babylon observable listener\",\"data\":{},\"inputs\":{\"observableSelector\":{\"connections\":[{\"node\":\"19dd5abb3a2ed30a\",\"output\":\"result\",\"data\":{}}]},\"object\":{\"connections\":[{\"node\":\"dda72ba4bc855075\",\"output\":\"result\",\"data\":{}}]}},\"position\":[634.3045048773648,6411.35023828563]},\"19dd5abb3a2ed30a\":{\"id\":\"19dd5abb3a2ed30a\",\"name\":\"bitbybit.babylon.gui.slider.createSliderObservableSelector\",\"customName\":\"slider observable selector\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"selector\":\"onValueChangedObservable\"},\"inputs\":{},\"position\":[119.33402358269528,6252.634270289916]},\"3f3e9efb919889cb\":{\"id\":\"3f3e9efb919889cb\",\"name\":\"bitbybit.flow.babylon.observableListener\",\"customName\":\"babylon observable listener\",\"data\":{},\"inputs\":{\"observableSelector\":{\"connections\":[{\"node\":\"19dd5abb3a2ed30a\",\"output\":\"result\",\"data\":{}}]},\"object\":{\"connections\":[{\"node\":\"eb164cb9f2c3d0b2\",\"output\":\"result\",\"data\":{}}]}},\"position\":[626.2393558517689,6035.026931379336]},\"b85e303f418ea094\":{\"id\":\"b85e303f418ea094\",\"name\":\"bitbybit.flow.babylon.getEventDataFromObservedResult\",\"customName\":\"get event data\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false}},\"inputs\":{\"json\":{\"connections\":[{\"node\":\"3f3e9efb919889cb\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"3f3e9efb919889cb\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[1213.8284182083087,6031.8004190176025]},\"0ae184887b347212\":{\"id\":\"0ae184887b347212\",\"name\":\"bitbybit.flow.babylon.getEventDataFromObservedResult\",\"customName\":\"get event data\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false}},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"f39c312f7e1e1bef\",\"output\":\"exec\",\"data\":{}}]},\"json\":{\"connections\":[{\"node\":\"f39c312f7e1e1bef\",\"output\":\"result\",\"data\":{}}]}},\"position\":[1212.113699374194,6401.279001431719]},\"ffecd6164df0c288\":{\"id\":\"ffecd6164df0c288\",\"name\":\"bitbybit.logic.firstDefinedValueGate\",\"customName\":\"first defined value gate\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false}},\"inputs\":{\"value1\":{\"connections\":[{\"node\":\"b85e303f418ea094\",\"output\":\"result\",\"data\":{}}]},\"value2\":{\"connections\":[{\"node\":\"6e51a85f13ab5259\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"b85e303f418ea094\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[1795.1547936226411,6012.662361553964]},\"d90e74b18631a7e7\":{\"id\":\"d90e74b18631a7e7\",\"name\":\"bitbybit.logic.firstDefinedValueGate\",\"customName\":\"first defined value gate\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false}},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"0ae184887b347212\",\"output\":\"exec\",\"data\":{}}]},\"value2\":{\"connections\":[{\"node\":\"dc27ec58598933e4\",\"output\":\"result\",\"data\":{}}]},\"value1\":{\"connections\":[{\"node\":\"0ae184887b347212\",\"output\":\"result\",\"data\":{}}]}},\"position\":[1827.0393870782336,6490.834733956343]},\"9ed742a672a607ce\":{\"id\":\"9ed742a672a607ce\",\"name\":\"bitbybit.lists.createList\",\"customName\":\"create list\",\"data\":{},\"inputs\":{\"listElements\":{\"connections\":[{\"node\":\"ffecd6164df0c288\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"ffecd6164df0c288\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[2408.212815313349,6050.3207511429555]},\"48046af83dbc9299\":{\"id\":\"48046af83dbc9299\",\"name\":\"bitbybit.lists.createList\",\"customName\":\"create list\",\"data\":{},\"inputs\":{\"listElements\":{\"connections\":[{\"node\":\"d90e74b18631a7e7\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"d90e74b18631a7e7\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[2407.3069815807094,6523.841211518347]},\"46f99a9e9cfc9152\":{\"id\":\"46f99a9e9cfc9152\",\"name\":\"bitbybit.text.format\",\"customName\":\"format\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"text\":\"Number Of Rays - {0}\",\"values\":[\"World\"]},\"inputs\":{\"values\":{\"connections\":[{\"node\":\"9ed742a672a607ce\",\"output\":\"list\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"9ed742a672a607ce\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[2774.3846965928947,5974.2361780617475]},\"ced830dd20fb5b02\":{\"id\":\"ced830dd20fb5b02\",\"name\":\"bitbybit.text.format\",\"customName\":\"format\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"text\":\"Vertical Divsions - {0}\",\"values\":[\"World\"]},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"48046af83dbc9299\",\"output\":\"exec\",\"data\":{}}]},\"values\":{\"connections\":[{\"node\":\"48046af83dbc9299\",\"output\":\"list\",\"data\":{}}]}},\"position\":[2794.9763891153702,6450.397669292677]},\"af2cb4893a5bc1cb\":{\"id\":\"af2cb4893a5bc1cb\",\"name\":\"bitbybit.babylon.gui.textBlock.setText\",\"customName\":\"set text\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"text\":\"\"},\"inputs\":{\"text\":{\"connections\":[{\"node\":\"46f99a9e9cfc9152\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"46f99a9e9cfc9152\",\"output\":\"exec\",\"data\":{}}]},\"textBlock\":{\"connections\":[{\"node\":\"f80263a3d6977ea4\",\"output\":\"result\",\"data\":{}}]}},\"position\":[3408.9286383465733,6175.853417797766]},\"bb5fa4b16d910cb5\":{\"id\":\"bb5fa4b16d910cb5\",\"name\":\"bitbybit.babylon.gui.textBlock.setText\",\"customName\":\"set text\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"text\":\"\"},\"inputs\":{\"textBlock\":{\"connections\":[{\"node\":\"a3006cf7f5041ef6\",\"output\":\"result\",\"data\":{}}]},\"text\":{\"connections\":[{\"node\":\"ced830dd20fb5b02\",\"output\":\"result\",\"data\":{}}]},\"exec\":{\"connections\":[{\"node\":\"ced830dd20fb5b02\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[3443.5303027369487,6538.552534619226]},\"8ecfa2d91d973882\":{\"id\":\"8ecfa2d91d973882\",\"name\":\"bitbybit.babylon.gui.control.createControlObservableSelector\",\"customName\":\"control observable selector\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"selector\":\"onPointerUpObservable\"},\"inputs\":{},\"position\":[112.53197228874697,7216.911800887681]},\"8478c18af95701d2\":{\"id\":\"8478c18af95701d2\",\"name\":\"bitbybit.flow.babylon.observableListener\",\"customName\":\"babylon observable listener\",\"data\":{},\"inputs\":{\"observableSelector\":{\"connections\":[{\"node\":\"8ecfa2d91d973882\",\"output\":\"result\",\"data\":{}}]},\"object\":{\"connections\":[{\"node\":\"dda72ba4bc855075\",\"output\":\"result\",\"data\":{}}]}},\"position\":[666.0061734678349,7042.204203322666]},\"fa5a486b0621317a\":{\"id\":\"fa5a486b0621317a\",\"name\":\"bitbybit.flow.babylon.observableListener\",\"customName\":\"babylon observable listener\",\"data\":{},\"inputs\":{\"observableSelector\":{\"connections\":[{\"node\":\"8ecfa2d91d973882\",\"output\":\"result\",\"data\":{}}]},\"object\":{\"connections\":[{\"node\":\"eb164cb9f2c3d0b2\",\"output\":\"result\",\"data\":{}}]}},\"position\":[667.4427756519425,6826.609623951564]},\"7299a501c8e860c5\":{\"id\":\"7299a501c8e860c5\",\"name\":\"bitbybit.lists.passThrough\",\"customName\":\"pass through\",\"data\":{},\"inputs\":{\"exec\":{\"connections\":[{\"node\":\"fa5a486b0621317a\",\"output\":\"exec\",\"data\":{}},{\"node\":\"8478c18af95701d2\",\"output\":\"exec\",\"data\":{}}]}},\"position\":[6778.4019145441225,6945.999036012015]},\"68fc3da5a920f715\":{\"id\":\"68fc3da5a920f715\",\"name\":\"bitbybit.babylon.scene.adjustActiveArcRotateCamera\",\"customName\":\"adjust active arc rotate camera\",\"async\":false,\"drawable\":false,\"data\":{\"genericNodeData\":{\"hide\":false,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"position\":[10,10,10],\"lookAt\":[0,0,0],\"lowerBetaLimit\":1,\"upperBetaLimit\":179,\"angularSensibilityX\":1000,\"angularSensibilityY\":1000,\"maxZ\":1000,\"panningSensibility\":1000,\"wheelPrecision\":3},\"inputs\":{\"position\":{\"connections\":[{\"node\":\"2c23a73974610198\",\"output\":\"result\",\"data\":{}}]},\"lookAt\":{\"connections\":[{\"node\":\"4b908f56b3ed505c\",\"output\":\"result\",\"data\":{}}]}},\"position\":[2457.6836534900826,-2687.8751308577216]},\"2c23a73974610198\":{\"id\":\"2c23a73974610198\",\"name\":\"bitbybit.vector.vectorXYZ\",\"customName\":\"vector xyz\",\"async\":false,\"drawable\":true,\"data\":{\"genericNodeData\":{\"hide\":true,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"x\":30,\"y\":25,\"z\":0},\"inputs\":{},\"position\":[2028.1960653764377,-2763.197379436671]},\"4b908f56b3ed505c\":{\"id\":\"4b908f56b3ed505c\",\"name\":\"bitbybit.vector.vectorXYZ\",\"customName\":\"vector xyz\",\"async\":false,\"drawable\":true,\"data\":{\"genericNodeData\":{\"hide\":true,\"oneOnOne\":false,\"flatten\":0,\"forceExecution\":false},\"x\":0,\"y\":4,\"z\":0},\"inputs\":{},\"position\":[2033.62438677469,-2317.355175655833]}}}","version":"0.20.12","type":"rete"}} + title="Christmas tree ornament" + /> + + diff --git a/packages/dev/base/lib/api/inputs/lists-inputs.ts b/packages/dev/base/lib/api/inputs/lists-inputs.ts index 2721aea5..14defcfa 100644 --- a/packages/dev/base/lib/api/inputs/lists-inputs.ts +++ b/packages/dev/base/lib/api/inputs/lists-inputs.ts @@ -574,4 +574,52 @@ export namespace Lists { */ clone? = true; } + export class ConcatenateDto { + constructor(lists?: T[][], clone?: boolean) { + if (lists !== undefined) { this.lists = lists; } + if (clone !== undefined) { this.clone = clone; } + } + /** + * The lists to concatenate + * @default undefined + */ + lists: T[][]; + /** + * Tries to make structured clone of the incoming list data in the component, sometimes it may not be possible due to circular structures or other types of error + * @default true + */ + clone? = true; + } + export class IncludesDto { + constructor(list?: T[], item?: T) { + if (list !== undefined) { this.list = list; } + if (item !== undefined) { this.item = item; } + } + /** + * The list to check + * @default undefined + */ + list: T[]; + /** + * The item to look for + * @default undefined + */ + item: T; + } + export class InterleaveDto { + constructor(lists?: T[][], clone?: boolean) { + if (lists !== undefined) { this.lists = lists; } + if (clone !== undefined) { this.clone = clone; } + } + /** + * The lists to interleave + * @default undefined + */ + lists: T[][]; + /** + * Tries to make structured clone of the incoming list data in the component, sometimes it may not be possible due to circular structures or other types of error + * @default true + */ + clone? = true; + } } diff --git a/packages/dev/base/lib/api/inputs/math-inputs.ts b/packages/dev/base/lib/api/inputs/math-inputs.ts index b5e63dab..ee79da93 100644 --- a/packages/dev/base/lib/api/inputs/math-inputs.ts +++ b/packages/dev/base/lib/api/inputs/math-inputs.ts @@ -352,4 +352,181 @@ export namespace Math { */ decimalPlaces = 2; } + export class ClampDto { + constructor(number?: number, min?: number, max?: number) { + if (number !== undefined) { this.number = number; } + if (min !== undefined) { this.min = min; } + if (max !== undefined) { this.max = max; } + } + /** + * Number to clamp + * @default 0.5 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + number = 0.5; + /** + * Minimum value + * @default 0 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + min = 0; + /** + * Maximum value + * @default 1 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + max = 1; + } + export class LerpDto { + constructor(start?: number, end?: number, t?: number) { + if (start !== undefined) { this.start = start; } + if (end !== undefined) { this.end = end; } + if (t !== undefined) { this.t = t; } + } + /** + * Start value + * @default 0 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + start = 0; + /** + * End value + * @default 1 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + end = 1; + /** + * Interpolation value (0-1) + * @default 0.5 + * @minimum -Infinity + * @maximum Infinity + * @step 0.01 + */ + t = 0.5; + } + export class InverseLerpDto { + constructor(start?: number, end?: number, value?: number) { + if (start !== undefined) { this.start = start; } + if (end !== undefined) { this.end = end; } + if (value !== undefined) { this.value = value; } + } + /** + * Start value + * @default 0 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + start = 0; + /** + * End value + * @default 1 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + end = 1; + /** + * Value to find t for + * @default 0.5 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + value = 0.5; + } + export class WrapDto { + constructor(number?: number, min?: number, max?: number) { + if (number !== undefined) { this.number = number; } + if (min !== undefined) { this.min = min; } + if (max !== undefined) { this.max = max; } + } + /** + * Number to wrap + * @default 1.5 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + number = 1.5; + /** + * Minimum value + * @default 0 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + min = 0; + /** + * Maximum value + * @default 1 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + max = 1; + } + export class PingPongDto { + constructor(t?: number, length?: number) { + if (t !== undefined) { this.t = t; } + if (length !== undefined) { this.length = length; } + } + /** + * Time value + * @default 0.5 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + t = 0.5; + /** + * Length of ping pong + * @default 1 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + length = 1; + } + export class MoveTowardsDto { + constructor(current?: number, target?: number, maxDelta?: number) { + if (current !== undefined) { this.current = current; } + if (target !== undefined) { this.target = target; } + if (maxDelta !== undefined) { this.maxDelta = maxDelta; } + } + /** + * Current value + * @default 0 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + current = 0; + /** + * Target value + * @default 1 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + target = 1; + /** + * Maximum change amount + * @default 0.1 + * @minimum -Infinity + * @maximum Infinity + * @step 0.01 + */ + maxDelta = 0.1; + } } diff --git a/packages/dev/base/lib/api/services/color.ts b/packages/dev/base/lib/api/services/color.ts index 79e61c3f..506d559e 100644 --- a/packages/dev/base/lib/api/services/color.ts +++ b/packages/dev/base/lib/api/services/color.ts @@ -7,7 +7,8 @@ export class Color { constructor(private readonly math: MathBitByBit) { } /** - * Creates a hex color + * Creates and returns a hex color string (pass-through for color input). + * Example: "#FF5733" → "#FF5733" * @param inputs Color hex * @returns color string * @group create @@ -19,7 +20,8 @@ export class Color { } /** - * Creates rgb color from hex + * Converts hex color to RGB object with r, g, b values (0-255 range). + * Example: "#FF5733" → {r: 255, g: 87, b: 51} * @param inputs Color hex * @returns rgb color * @group convert @@ -36,7 +38,9 @@ export class Color { } /** - * Creates hex color from rgb + * Converts RGB values to hex color string (supports custom min/max ranges, auto-remaps to 0-255). + * Example: r=255, g=87, b=51 with range [0,255] → "#ff5733" + * Example: r=1, g=0.5, b=0.2 with range [0,1] → "#ff7f33" * @param inputs Color hext * @returns hex color * @group convert @@ -63,7 +67,8 @@ export class Color { /** - * Creates hex color from rgb obj that contains {r, g, b} properties in certain range + * Converts RGB object to hex color string (supports custom min/max ranges). + * Example: {r: 1, g: 0.5, b: 0.2} with range [0,1] → "#ff7f33" * @param inputs Color hext * @returns hex color string * @group convert @@ -75,7 +80,9 @@ export class Color { } /** - * Creates rgb color from hex and maps to different range if needed + * Converts hex color to RGB and remaps values to a custom range. + * Example: "#FF5733" mapped to [0,1] → {r: 1, g: 0.341, b: 0.2} + * Example: "#FF5733" mapped to [0,100] → {r: 100, g: 34.1, b: 20} * @param inputs Color hext * @returns rgb color * @group convert @@ -92,7 +99,8 @@ export class Color { } /** - * Get red param + * Extracts the red channel value from hex color (can be mapped to custom range). + * Example: "#FF5733" with range [0,1] → 1 * @param inputs Color hext * @returns rgb color * @group hex to @@ -105,7 +113,8 @@ export class Color { } /** - * Get green param + * Extracts the green channel value from hex color (can be mapped to custom range). + * Example: "#FF5733" with range [0,1] → 0.341 * @param inputs Color hext * @returns rgb color * @group hex to @@ -118,7 +127,8 @@ export class Color { } /** - * Get blue param + * Extracts the blue channel value from hex color (can be mapped to custom range). + * Example: "#FF5733" with range [0,1] → 0.2 * @param inputs Color hext * @returns blue param * @group hex to @@ -131,7 +141,8 @@ export class Color { } /** - * RGB to red + * Extracts the red channel value from RGB object. + * Example: {r: 255, g: 87, b: 51} → 255 * @param inputs Color rgb * @returns red param * @group rgb to @@ -143,7 +154,8 @@ export class Color { } /** - * RGB to green + * Extracts the green channel value from RGB object. + * Example: {r: 255, g: 87, b: 51} → 87 * @param inputs Color rgb * @returns green param * @group rgb to @@ -155,7 +167,8 @@ export class Color { } /** - * RGB to blue + * Extracts the blue channel value from RGB object. + * Example: {r: 255, g: 87, b: 51} → 51 * @param inputs Color rgb * @returns blue param * @group rgb to @@ -167,7 +180,9 @@ export class Color { } /** - * Invert color + * Inverts a hex color (flips RGB channels: 255-r, 255-g, 255-b). + * With blackAndWhite=true → returns "#000000" or "#ffffff" based on brightness. + * Example: "#FF5733" → "#00a8cc", "#FF5733" with blackAndWhite=true → "#ffffff" * @param inputs hex color and black and white option * @returns inverted color * @group hex to diff --git a/packages/dev/base/lib/api/services/dates.ts b/packages/dev/base/lib/api/services/dates.ts index 1a44c0a8..c654f742 100644 --- a/packages/dev/base/lib/api/services/dates.ts +++ b/packages/dev/base/lib/api/services/dates.ts @@ -6,7 +6,8 @@ import * as Inputs from "../inputs"; export class Dates { /** - * Returns a date as a string value. + * Converts date to human-readable date string (excludes time). + * Example: Date(2024,0,15,14,30) → "Mon Jan 15 2024" * @param inputs a date * @returns date as string * @group convert @@ -18,7 +19,8 @@ export class Dates { } /** - * Returns a date as a string value in ISO format. + * Converts date to ISO 8601 format string (standard format for APIs and data interchange). + * Example: Date(2024,0,15,14,30,45) → "2024-01-15T14:30:45.000Z" * @param inputs a date * @returns date as string * @group convert @@ -30,7 +32,8 @@ export class Dates { } /** - * Returns a date as a string value in JSON format. + * Converts date to JSON-compatible string (same as ISO format, used in JSON.stringify). + * Example: Date(2024,0,15,14,30) → "2024-01-15T14:30:00.000Z" * @param inputs a date * @returns date as string * @group convert @@ -42,7 +45,8 @@ export class Dates { } /** - * Returns a string representation of a date. The format of the string depends on the locale. + * Converts date to full locale-specific string (includes date, time, and timezone). + * Example: Date(2024,0,15,14,30) → "Mon Jan 15 2024 14:30:00 GMT+0000" * @param inputs a date * @returns date as string * @group convert @@ -54,7 +58,8 @@ export class Dates { } /** - * Returns a time as a string value. + * Converts date to time string (excludes date, includes timezone). + * Example: Date(2024,0,15,14,30,45) → "14:30:45 GMT+0000" * @param inputs a date * @returns time as string * @group convert @@ -66,7 +71,8 @@ export class Dates { } /** - * Returns a date converted to a string using Universal Coordinated Time (UTC). + * Converts date to UTC string format (Universal Coordinated Time, no timezone offset). + * Example: Date(2024,0,15,14,30) → "Mon, 15 Jan 2024 14:30:00 GMT" * @param inputs a date * @returns date as utc string * @group convert @@ -78,7 +84,8 @@ export class Dates { } /** - * Returns the current date and time. + * Returns the current date and time at the moment of execution. + * Example: calling now() → Date object representing current moment (e.g., "2024-01-15T14:30:45") * @returns date * @group create * @shortname now @@ -89,7 +96,9 @@ export class Dates { } /** - * Creates a new date object using the provided date params. + * Creates a new date from individual components using local time. + * Month is 0-indexed: 0=January, 11=December. + * Example: year=2024, month=0, day=15, hours=14, minutes=30 → Date(Jan 15, 2024 14:30) * @param inputs a date * @returns date * @group create @@ -101,7 +110,9 @@ export class Dates { } /** - * Returns the number of milliseconds between midnight, January 1, 1970 Universal Coordinated Time (UTC) (or GMT) and the specified date. + * Creates a new date from individual components using UTC (ignores timezone). + * Returns milliseconds since Unix epoch (Jan 1, 1970 00:00:00 UTC). + * Example: year=2024, month=0, day=15 → Date representing Jan 15, 2024 00:00 UTC * @param inputs a date * @returns date * @group create @@ -113,7 +124,8 @@ export class Dates { } /** - * Creates a new date object using the provided unix time stamp. + * Creates a date from Unix timestamp (milliseconds since Jan 1, 1970 UTC). + * Example: unixTimeStamp=1705329000000 → Date(Jan 15, 2024 14:30:00) * @param inputs a unix time stamp * @returns date * @group create @@ -125,7 +137,8 @@ export class Dates { } /** - * Parses a string containing a date, and returns the number of milliseconds between that date and midnight, January 1, 1970. + * Parses a date string and returns Unix timestamp (milliseconds since Jan 1, 1970 UTC). + * Example: dateString="2024-01-15" → 1705276800000 * @param inputs a date string * @returns the number of milliseconds between that date and midnight, January 1, 1970. * @group parse @@ -137,7 +150,8 @@ export class Dates { } /** - * Gets the day-of-the-month, using local time. + * Extracts day of the month from date (1-31) using local time. + * Example: Date(2024,0,15) → 15 * @returns date * @group get * @shortname get date of month @@ -148,7 +162,8 @@ export class Dates { } /** - * Gets the day of the week, using local time. + * Extracts day of the week from date (0=Sunday, 6=Saturday) using local time. + * Example: Date(2024,0,15) → 1 (Monday) * @returns day * @group get * @shortname get weekday @@ -159,7 +174,8 @@ export class Dates { } /** - * Gets the year, using local time. + * Extracts full year from date using local time. + * Example: Date(2024,0,15) → 2024 * @returns year * @group get * @shortname get year @@ -170,7 +186,8 @@ export class Dates { } /** - * Gets the month, using local time. + * Extracts month from date (0=January, 11=December) using local time. + * Example: Date(2024,0,15) → 0 (January) * @returns month * @group get * @shortname get month @@ -181,7 +198,8 @@ export class Dates { } /** - * Gets the hours in a date, using local time. + * Extracts hours from date (0-23) using local time. + * Example: Date(2024,0,15,14,30) → 14 * @returns hours * @group get * @shortname get hours @@ -192,7 +210,8 @@ export class Dates { } /** - * Gets the minutes of a Date object, using local time. + * Extracts minutes from date (0-59) using local time. + * Example: Date(2024,0,15,14,30) → 30 * @returns minutes * @group get * @shortname get minutes @@ -203,7 +222,8 @@ export class Dates { } /** - * Gets the seconds of a Date object, using local time. + * Extracts seconds from date (0-59) using local time. + * Example: Date(2024,0,15,14,30,45) → 45 * @returns seconds * @group get * @shortname get seconds @@ -214,7 +234,8 @@ export class Dates { } /** - * Gets the milliseconds of a Date, using local time. + * Extracts milliseconds from date (0-999) using local time. + * Example: Date(2024,0,15,14,30,45,123) → 123 * @returns milliseconds * @group get * @shortname get milliseconds @@ -225,7 +246,8 @@ export class Dates { } /** - * Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC. + * Converts date to Unix timestamp (milliseconds since Jan 1, 1970 UTC). + * Example: Date(2024,0,15,14,30) → 1705329000000 * @returns time * @group get * @shortname get time @@ -236,7 +258,8 @@ export class Dates { } /** - * Gets the year using Universal Coordinated Time (UTC). + * Extracts full year from date using UTC (ignores timezone). + * Example: Date(2024,0,15) → 2024 * @returns year * @group get * @shortname get utc year @@ -247,7 +270,8 @@ export class Dates { } /** - * Gets the month of a Date object using Universal Coordinated Time (UTC). + * Extracts month from date (0=January, 11=December) using UTC. + * Example: Date.UTC(2024,0,15) → 0 (January) * @returns month * @group get * @shortname get utc month @@ -258,7 +282,8 @@ export class Dates { } /** - * Gets the day-of-the-month, using Universal Coordinated Time (UTC). + * Extracts day of the month from date (1-31) using UTC. + * Example: Date.UTC(2024,0,15) → 15 * @returns day * @group get * @shortname get utc day @@ -269,7 +294,8 @@ export class Dates { } /** - * Gets the hours value in a Date object using Universal Coordinated Time (UTC). + * Extracts hours from date (0-23) using UTC. + * Example: Date.UTC(2024,0,15,14) → 14 * @returns hours * @group get * @shortname get utc hours @@ -280,7 +306,8 @@ export class Dates { } /** - * Gets the minutes of a Date object using Universal Coordinated Time (UTC). + * Extracts minutes from date (0-59) using UTC. + * Example: Date.UTC(2024,0,15,14,30) → 30 * @returns minutes * @group get * @shortname get utc minutes @@ -291,7 +318,8 @@ export class Dates { } /** - * Gets the seconds of a Date object using Universal Coordinated Time (UTC). + * Extracts seconds from date (0-59) using UTC. + * Example: Date.UTC(2024,0,15,14,30,45) → 45 * @returns seconds * @group get * @shortname get utc seconds @@ -302,7 +330,8 @@ export class Dates { } /** - * Gets the milliseconds of a Date object using Universal Coordinated Time (UTC). + * Extracts milliseconds from date (0-999) using UTC. + * Example: Date.UTC(2024,0,15,14,30,45,123) → 123 * @returns milliseconds * @group get * @shortname get utc milliseconds @@ -313,7 +342,8 @@ export class Dates { } /** - * Sets the year of the Date object using local time. + * Creates new date with modified year (returns new date, original unchanged). + * Example: Date(2024,0,15) with year=2025 → Date(2025,0,15) * @param inputs a date and the year * @returns date * @group set @@ -327,7 +357,8 @@ export class Dates { } /** - * Sets the month value in the Date object using local time. + * Creates new date with modified month (0=January, 11=December, returns new date). + * Example: Date(2024,0,15) with month=5 → Date(2024,5,15) (June 15) * @param inputs a date and the month * @returns date * @group set @@ -341,7 +372,8 @@ export class Dates { } /** - * Sets the numeric day-of-the-month value of the Date object using local time. + * Creates new date with modified day of month (1-31, returns new date). + * Example: Date(2024,0,15) with day=20 → Date(2024,0,20) * @param inputs a date and the day * @returns date * @group set diff --git a/packages/dev/base/lib/api/services/geometry-helper.ts b/packages/dev/base/lib/api/services/geometry-helper.ts index 0dd27a54..c2283966 100644 --- a/packages/dev/base/lib/api/services/geometry-helper.ts +++ b/packages/dev/base/lib/api/services/geometry-helper.ts @@ -2,6 +2,11 @@ import * as Inputs from "../inputs"; export class GeometryHelper { + /** + * Applies one or more 4×4 transformation matrices to a list of points sequentially. + * Each transformation is applied in order (composition of transformations). + * Example: points=[[0,0,0], [1,0,0]] with translation [5,0,0] → [[5,0,0], [6,0,0]] + */ transformControlPoints(transformation: number[][] | number[][][], transformedControlPoints: Inputs.Base.Point3[]): Inputs.Base.Point3[] { const transformationArrays = this.getFlatTransformations(transformation); @@ -12,6 +17,11 @@ export class GeometryHelper { return transformedControlPoints; } + /** + * Flattens nested transformation arrays into a single-level array of transformation matrices. + * Handles both 2D arrays (single transform list) and 3D arrays (nested transform lists). + * Example: [[[matrix1, matrix2]], [[matrix3]]] → [matrix1, matrix2, matrix3] + */ getFlatTransformations(transformation: number[][] | number[][][]): number[][] { let transformationArrays = []; @@ -25,16 +35,28 @@ export class GeometryHelper { return transformationArrays; } + /** + * Calculates the nesting depth of an array recursively. + * Example: [1,2,3] → 1, [[1,2],[3,4]] → 2, [[[1]]] → 3 + */ getArrayDepth = (value): number => { return Array.isArray(value) ? 1 + Math.max(...value.map(this.getArrayDepth)) : 0; }; + /** + * Applies a single 4×4 transformation matrix (as flat 16-element array) to multiple points. + * Example: points=[[0,0,0], [1,0,0]] with translation matrix → transformed points + */ transformPointsByMatrixArray(points: Inputs.Base.Point3[], transform: number[]): Inputs.Base.Point3[] { return this.transformPointsCoordinates(points, transform); } + /** + * Transforms multiple points using a transformation matrix (maps each point through the matrix). + * Example: points=[[1,0,0], [0,1,0]] with 90° rotation → [[0,1,0], [-1,0,0]] + */ transformPointsCoordinates(points: Inputs.Base.Point3[], transform: number[]): Inputs.Base.Point3[] { const transformedPoints = []; for (const pt of points) { @@ -44,7 +66,11 @@ export class GeometryHelper { return transformedPoints; } - // Algorithm works with arbitrary length numeric vectors. This algorithm is more costly for longer arrays of vectors + /** + * Removes all duplicate vectors from a list (works with arbitrary-length numeric vectors). + * Compares vectors using tolerance for floating-point equality. + * Example: [[1,2], [3,4], [1,2], [5,6]] with tolerance=1e-7 → [[1,2], [3,4], [5,6]] + */ removeAllDuplicateVectors(vectors: number[][], tolerance = 1e-7): number[][] { const cleanVectors: number[][] = []; vectors.forEach(vector => { @@ -56,7 +82,11 @@ export class GeometryHelper { return cleanVectors; } - // Algorithm works with arbitrary length numeric vectors. + /** + * Removes consecutive duplicate vectors from a list (keeps only first occurrence in each sequence). + * Optionally checks and removes duplicate if first and last vectors match. + * Example: [[1,2], [1,2], [3,4], [3,4], [5,6]] → [[1,2], [3,4], [5,6]] + */ removeConsecutiveVectorDuplicates(vectors: number[][], checkFirstAndLast = true, tolerance = 1e-7): number[][] { const vectorsRemaining: number[][] = []; if (vectors.length > 1) { @@ -83,6 +113,11 @@ export class GeometryHelper { return vectorsRemaining; } + /** + * Compares two vectors for approximate equality using tolerance (element-wise comparison). + * Returns false if vectors have different lengths. + * Example: [1.0000001, 2.0], [1.0, 2.0] with tolerance=1e-6 → true + */ vectorsTheSame(vec1: number[], vec2: number[], tolerance: number) { let result = false; if (vec1.length !== vec2.length) { @@ -99,11 +134,20 @@ export class GeometryHelper { return result; } + /** + * Checks if two numbers are approximately equal within a tolerance. + * Example: 1.0000001, 1.0 with tolerance=1e-6 → true, 1.001, 1.0 with tolerance=1e-6 → false + */ approxEq(num1: number, num2: number, tolerance: number): boolean { const res = Math.abs(num1 - num2) < tolerance; return res; } + /** + * Removes consecutive duplicate points from a list (specialized for 3D/2D points). + * Optionally checks and removes duplicate if first and last points match (for closed loops). + * Example: [[0,0,0], [0,0,0], [1,0,0], [1,0,0]] → [[0,0,0], [1,0,0]] + */ removeConsecutivePointDuplicates(points: Inputs.Base.Point3[], checkFirstAndLast = true, tolerance = 1e-7): Inputs.Base.Point3[] { const pointsRemaining = []; if (points.length > 1) { @@ -130,6 +174,10 @@ export class GeometryHelper { return pointsRemaining; } + /** + * Checks if two points are approximately equal using tolerance (supports 2D and 3D points). + * Example: [1.0000001, 2.0, 3.0], [1.0, 2.0, 3.0] with tolerance=1e-6 → true + */ arePointsTheSame(pointA: Inputs.Base.Point3 | Inputs.Base.Point2, pointB: Inputs.Base.Point3 | Inputs.Base.Point2, tolerance: number): boolean { let result = false; if (pointA.length === 2 && pointB.length === 2) { diff --git a/packages/dev/base/lib/api/services/helpers/csv/csv.ts b/packages/dev/base/lib/api/services/helpers/csv/csv.ts new file mode 100644 index 00000000..9514a5a6 --- /dev/null +++ b/packages/dev/base/lib/api/services/helpers/csv/csv.ts @@ -0,0 +1,6 @@ +import * as Inputs from "../../../inputs"; + +export class Csv { + + +} \ No newline at end of file diff --git a/packages/dev/base/lib/api/services/helpers/dxf/dxf.ts b/packages/dev/base/lib/api/services/helpers/dxf/dxf.ts index d701bc57..f263fd0a 100644 --- a/packages/dev/base/lib/api/services/helpers/dxf/dxf.ts +++ b/packages/dev/base/lib/api/services/helpers/dxf/dxf.ts @@ -6,7 +6,8 @@ export class Dxf { private dxfGenerator = new DxfGenerator(); /** - * Creates a line segment for DXF paths + * Creates a line segment definition for DXF export (pass-through for validation). + * Example: start=[0,0], end=[10,5] → DXF line segment from origin to [10,5] * @param inputs Line segment definition * @returns Line segment DTO * @group dxf @@ -18,7 +19,8 @@ export class Dxf { } /** - * Creates an arc segment for DXF paths + * Creates an arc segment definition for DXF export (curved path between two points). + * Example: center=[5,5], radius=5, startAngle=0°, endAngle=90° → quarter circle arc * @param inputs Arc segment definition * @returns Arc segment DTO * @group dxf @@ -30,7 +32,8 @@ export class Dxf { } /** - * Creates a circle segment for DXF paths + * Creates a circle segment definition for DXF export (closed circular path). + * Example: center=[10,10], radius=5 → full circle with diameter 10 centered at [10,10] * @param inputs Circle segment definition * @returns Circle segment DTO * @group dxf @@ -42,7 +45,8 @@ export class Dxf { } /** - * Creates a polyline segment for DXF paths + * Creates a polyline segment definition for DXF export (connected line segments through points). + * Example: points=[[0,0], [5,0], [5,5], [0,5]] → rectangular polyline path * @param inputs Polyline segment definition * @returns Polyline segment DTO * @group dxf @@ -54,7 +58,8 @@ export class Dxf { } /** - * Creates a spline segment for DXF paths + * Creates a spline segment definition for DXF export (smooth curve through control points). + * Example: controlPoints=[[0,0], [5,10], [10,0]] → smooth curved path through points * @param inputs Spline segment definition * @returns Spline segment DTO * @group dxf @@ -66,7 +71,9 @@ export class Dxf { } /** - * Creates a path from segments + * Creates a path from multiple segments (combines lines, arcs, circles, polylines, splines). + * Similar to OCCT wires - segments are connected to form a continuous or multi-part path. + * Example: segments=[lineSegment, arcSegment, polylineSegment] → combined path entity * @param inputs Path definition with segments * @returns Path DTO * @group dxf @@ -78,7 +85,9 @@ export class Dxf { } /** - * Creates a paths part with layer and color + * Creates a paths part with layer and color assignment for DXF organization. + * Groups multiple paths into a single layer with consistent styling. + * Example: paths=[path1, path2], layer="Outlines", color=red → grouped geometry * @param inputs Paths part definition * @returns Paths part DTO * @group dxf @@ -90,8 +99,9 @@ export class Dxf { } /** - * DXF generator that supports lines, arcs, circles, polylines, and splines organized in paths. - * Paths can combine multiple segment types similar to OCCT wires. + * Generates a complete DXF file from paths parts (exports 2D CAD drawing format). + * Supports lines, arcs, circles, polylines, and splines organized in layered paths. + * Example: model with 3 parts on different layers → valid DXF file string for CAD software * @param inputs DXF model definition * @returns DXF file content as string * @group dxf diff --git a/packages/dev/base/lib/api/services/line.ts b/packages/dev/base/lib/api/services/line.ts index 66487a37..a05856dc 100644 --- a/packages/dev/base/lib/api/services/line.ts +++ b/packages/dev/base/lib/api/services/line.ts @@ -12,7 +12,8 @@ export class Line { constructor(private readonly vector: Vector, private readonly point: Point, private readonly geometryHelper: GeometryHelper) { } /** - * Gets the start point of the line + * Extracts start point from a line. + * Example: line={start:[0,0,0], end:[10,5,0]} → [0,0,0] * @param inputs a line * @returns start point * @group get @@ -24,7 +25,8 @@ export class Line { } /** - * Gets the end point of the line + * Extracts end point from a line. + * Example: line={start:[0,0,0], end:[10,5,0]} → [10,5,0] * @param inputs a line * @returns end point * @group get @@ -36,7 +38,8 @@ export class Line { } /** - * Gets the length of the line + * Calculates length (distance) of a line segment. + * Example: line={start:[0,0,0], end:[3,4,0]} → 5 (using Pythagorean theorem) * @param inputs a line * @returns line length * @group get @@ -48,7 +51,8 @@ export class Line { } /** - * Reverse the endpoints of the line + * Reverses line direction by swapping start and end points. + * Example: line={start:[0,0,0], end:[10,5,0]} → {start:[10,5,0], end:[0,0,0]} * @param inputs a line * @returns reversed line * @group operations @@ -60,7 +64,8 @@ export class Line { } /** - * Transform the line + * Applies transformation matrix to line (rotates, scales, or translates both endpoints). + * Example: line={start:[0,0,0], end:[10,0,0]} with translation [5,5,0] → {start:[5,5,0], end:[15,5,0]} * @param inputs a line * @returns transformed line * @group transforms @@ -78,7 +83,8 @@ export class Line { } /** - * Transforms the lines with multiple transform for each line + * Applies multiple transformations to multiple lines (one transform per line). + * Example: 3 lines with 3 different translation matrices → each line moved independently * @param inputs lines * @returns transformed lines * @group transforms @@ -98,7 +104,8 @@ export class Line { } /** - * Create the line + * Creates a line from two points (line object with start and end properties). + * Example: start=[0,0,0], end=[10,5,0] → {start:[0,0,0], end:[10,5,0]} * @param inputs start and end points of the line * @returns line * @group create @@ -113,7 +120,8 @@ export class Line { } /** - * Create the segment + * Creates a segment from two points (array format: [start, end]). + * Example: start=[0,0,0], end=[10,5,0] → [[0,0,0], [10,5,0]] * @param inputs start and end points of the segment * @returns segment * @group create @@ -128,7 +136,8 @@ export class Line { } /** - * Gets the point on the line segment at a given param + * Calculates point at parameter t along line segment (0=start, 1=end, linear interpolation). + * Example: line={start:[0,0,0], end:[10,0,0]}, param=0.5 → [5,0,0] (midpoint) * @param inputs line * @returns point on line * @group get @@ -149,7 +158,8 @@ export class Line { } /** - * Create the lines segments between all of the points in a list + * Creates line segments connecting consecutive points in a list (forms a polyline path). + * Example: points=[[0,0,0], [5,0,0], [5,5,0]] → 2 lines: [0→5] and [5→5,5] * @param inputs points * @returns lines * @group create @@ -167,7 +177,9 @@ export class Line { } /** - * Create the lines between start and end points + * Creates lines by pairing corresponding start and end points from two arrays. + * Filters out zero-length lines. + * Example: starts=[[0,0,0], [5,0,0]], ends=[[0,5,0], [5,5,0]] → 2 lines connecting paired points * @param inputs start points and end points * @returns lines * @group create @@ -181,7 +193,8 @@ export class Line { } /** - * Convert the line to segment + * Converts line object to segment array format. + * Example: {start:[0,0,0], end:[10,5,0]} → [[0,0,0], [10,5,0]] * @param inputs line * @returns segment * @group convert @@ -193,7 +206,8 @@ export class Line { } /** - * Converts the lines to segments + * Converts multiple line objects to segment array format (batch conversion). + * Example: 3 line objects → 3 segment arrays [[start1, end1], [start2, end2], ...] * @param inputs lines * @returns segments * @group convert @@ -205,7 +219,8 @@ export class Line { } /** - * Converts the segment to line + * Converts segment array to line object format. + * Example: [[0,0,0], [10,5,0]] → {start:[0,0,0], end:[10,5,0]} * @param inputs segment * @returns line * @group convert @@ -217,7 +232,8 @@ export class Line { } /** - * Converts the segments to lines + * Converts multiple segment arrays to line object format (batch conversion). + * Example: 3 segment arrays → 3 line objects with start/end properties * @param inputs segments * @returns lines * @group convert @@ -229,7 +245,9 @@ export class Line { } /** - * If two lines intersect return the intersection point + * Calculates intersection point of two lines (or segments if checkSegmentsOnly=true). + * Returns undefined if lines are parallel, skew, or segments don't overlap. + * Example: line1={start:[0,0,0], end:[10,0,0]}, line2={start:[5,-5,0], end:[5,5,0]} → [5,0,0] * @param inputs line1 and line2 * @returns intersection point or undefined if no intersection * @group intersection diff --git a/packages/dev/base/lib/api/services/lists.test.ts b/packages/dev/base/lib/api/services/lists.test.ts index fd50b753..4b75f883 100644 --- a/packages/dev/base/lib/api/services/lists.test.ts +++ b/packages/dev/base/lib/api/services/lists.test.ts @@ -608,5 +608,226 @@ describe("Lists unit tests", () => { expect(result).toEqual([{ prop: 3 }, { prop: 2 }, { prop: 1 }]); }); + it("should get first item from the list", () => { + const result = lists.getFirstItem({ list: [0, 1, 2, 3, 4] }); + expect(result).toEqual(0); + }); + + it("should get first item from the list and clone", () => { + const result = lists.getFirstItem({ list: [0, 1, 2, 3, 4], clone: true }); + expect(result).toEqual(0); + }); + + it("should throw error when getting first item from empty list", () => { + expect(() => lists.getFirstItem({ list: [] })).toThrowError("List is empty"); + }); + + it("should get last item from the list", () => { + const result = lists.getLastItem({ list: [0, 1, 2, 3, 4] }); + expect(result).toEqual(4); + }); + + it("should get last item from the list and clone", () => { + const result = lists.getLastItem({ list: [0, 1, 2, 3, 4], clone: true }); + expect(result).toEqual(4); + }); + + it("should throw error when getting last item from empty list", () => { + expect(() => lists.getLastItem({ list: [] })).toThrowError("List is empty"); + }); + + it("should remove first item from the list", () => { + const result = lists.removeFirstItem({ list: [0, 1, 2, 3, 4] }); + expect(result).toEqual([1, 2, 3, 4]); + }); + + it("should remove first item from the list and clone", () => { + const result = lists.removeFirstItem({ list: [0, 1, 2, 3, 4], clone: true }); + expect(result).toEqual([1, 2, 3, 4]); + }); + + it("should not remove first item from empty list", () => { + const result = lists.removeFirstItem({ list: [] }); + expect(result).toEqual([]); + }); + + it("should remove last item from the list", () => { + const result = lists.removeLastItem({ list: [0, 1, 2, 3, 4] }); + expect(result).toEqual([0, 1, 2, 3]); + }); + + it("should remove last item from the list and clone", () => { + const result = lists.removeLastItem({ list: [0, 1, 2, 3, 4], clone: true }); + expect(result).toEqual([0, 1, 2, 3]); + }); + + it("should not remove last item from empty list", () => { + const result = lists.removeLastItem({ list: [] }); + expect(result).toEqual([]); + }); + + it("should remove item at index from end (0 means last)", () => { + const result = lists.removeItemAtIndexFromEnd({ list: [0, 1, 2, 3, 4], index: 0 }); + expect(result).toEqual([0, 1, 2, 3]); + }); + + it("should remove item at index from end (1 means second to last)", () => { + const result = lists.removeItemAtIndexFromEnd({ list: [0, 1, 2, 3, 4], index: 1 }); + expect(result).toEqual([0, 1, 2, 4]); + }); + + it("should remove item at index from end (4 means first)", () => { + const result = lists.removeItemAtIndexFromEnd({ list: [0, 1, 2, 3, 4], index: 4 }); + expect(result).toEqual([1, 2, 3, 4]); + }); + + it("should remove item at index from end and clone", () => { + const result = lists.removeItemAtIndexFromEnd({ list: [0, 1, 2, 3, 4], index: 2, clone: true }); + expect(result).toEqual([0, 1, 3, 4]); + }); + + it("should not remove item at out of bounds index from end", () => { + const result = lists.removeItemAtIndexFromEnd({ list: [0, 1, 2, 3, 4], index: 5 }); + expect(result).toEqual([0, 1, 2, 3, 4]); + }); + + it("should not remove item at negative index from end", () => { + const result = lists.removeItemAtIndexFromEnd({ list: [0, 1, 2, 3, 4], index: -1 }); + expect(result).toEqual([0, 1, 2, 3, 4]); + }); + + it("should shuffle the list", () => { + const result = lists.shuffle({ list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }); + expect(result.length).toBe(10); + expect(result).toContain(0); + expect(result).toContain(9); + }); + + it("should shuffle the list and clone", () => { + const result = lists.shuffle({ list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], clone: true }); + expect(result.length).toBe(10); + expect(result).toContain(0); + expect(result).toContain(9); + }); + + it("should shuffle empty list", () => { + const result = lists.shuffle({ list: [] }); + expect(result).toEqual([]); + }); + + it("should remove duplicates from generic list", () => { + const result = lists.removeDuplicates({ list: ["a", "b", "c", "a", "d", "b", "e"] }); + expect(result).toEqual(["a", "b", "c", "d", "e"]); + }); + + it("should remove duplicates from generic list and clone", () => { + const result = lists.removeDuplicates({ list: ["a", "b", "c", "a", "d", "b", "e"], clone: true }); + expect(result).toEqual(["a", "b", "c", "d", "e"]); + }); + + it("should remove duplicates from number list", () => { + const result = lists.removeDuplicates({ list: [1, 2, 3, 1, 4, 2, 5] }); + expect(result).toEqual([1, 2, 3, 4, 5]); + }); + + it("should concatenate multiple lists", () => { + const result = lists.concatenate({ lists: [[0, 1, 2], [3, 4, 5], [6, 7, 8]] }); + expect(result).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]); + }); + + it("should concatenate multiple lists and clone", () => { + const result = lists.concatenate({ lists: [[0, 1, 2], [3, 4, 5], [6, 7, 8]], clone: true }); + expect(result).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]); + }); + + it("should concatenate empty lists", () => { + const result = lists.concatenate({ lists: [[], [], []] }); + expect(result).toEqual([]); + }); + + it("should concatenate single list", () => { + const result = lists.concatenate({ lists: [[0, 1, 2]] }); + expect(result).toEqual([0, 1, 2]); + }); + + it("should check if list includes an item", () => { + const result = lists.includes({ list: [0, 1, 2, 3, 4], item: 2 }); + expect(result).toBe(true); + }); + + it("should check if list does not include an item", () => { + const result = lists.includes({ list: [0, 1, 2, 3, 4], item: 5 }); + expect(result).toBe(false); + }); + + it("should check if list includes string item", () => { + const result = lists.includes({ list: ["a", "b", "c"], item: "b" }); + expect(result).toBe(true); + }); + + it("should find index of an item in the list", () => { + const result = lists.findIndex({ list: [0, 1, 2, 3, 4], item: 2 }); + expect(result).toBe(2); + }); + + it("should return -1 when item is not found", () => { + const result = lists.findIndex({ list: [0, 1, 2, 3, 4], item: 5 }); + expect(result).toBe(-1); + }); + + it("should find index of first occurrence", () => { + const result = lists.findIndex({ list: [0, 1, 2, 3, 2, 4], item: 2 }); + expect(result).toBe(2); + }); + + it("should find index of string item", () => { + const result = lists.findIndex({ list: ["a", "b", "c"], item: "c" }); + expect(result).toBe(2); + }); + + it("should interleave two lists", () => { + const result = lists.interleave({ lists: [[0, 1, 2], [3, 4, 5]] }); + expect(result).toEqual([0, 3, 1, 4, 2, 5]); + }); + + it("should interleave two lists and clone", () => { + const result = lists.interleave({ lists: [[0, 1, 2], [3, 4, 5]], clone: true }); + expect(result).toEqual([0, 3, 1, 4, 2, 5]); + }); + + it("should interleave three lists", () => { + const result = lists.interleave({ lists: [[0, 1], [2, 3], [4, 5]] }); + expect(result).toEqual([0, 2, 4, 1, 3, 5]); + }); + + it("should interleave lists with different lengths", () => { + const result = lists.interleave({ lists: [[0, 1, 2], [3, 4]] }); + expect(result).toEqual([0, 3, 1, 4, 2]); + }); + + it("should interleave lists with different lengths (first shorter)", () => { + const result = lists.interleave({ lists: [[0, 1], [2, 3, 4, 5]] }); + expect(result).toEqual([0, 2, 1, 3, 4, 5]); + }); + + it("should interleave single list", () => { + const result = lists.interleave({ lists: [[0, 1, 2]] }); + expect(result).toEqual([0, 1, 2]); + }); + + it("should interleave empty lists", () => { + const result = lists.interleave({ lists: [[], []] }); + expect(result).toEqual([]); + }); + + it("should throw error when interleaving with no lists", () => { + expect(() => lists.interleave({ lists: [] })).toThrowError("Lists array is empty or does not exist"); + }); + + it("should interleave string lists", () => { + const result = lists.interleave({ lists: [["a", "b"], ["c", "d"]] }); + expect(result).toEqual(["a", "c", "b", "d"]); + }); + }); diff --git a/packages/dev/base/lib/api/services/lists.ts b/packages/dev/base/lib/api/services/lists.ts index 7aa193e4..ff93c202 100644 --- a/packages/dev/base/lib/api/services/lists.ts +++ b/packages/dev/base/lib/api/services/lists.ts @@ -8,7 +8,8 @@ import * as Inputs from "../inputs"; */ export class Lists { /** - * Gets an item from the list by using a 0 based index + * Gets an item from the list at a specific position using zero-based indexing. + * Example: From [10, 20, 30, 40], getting index 2 returns 30 * @param inputs a list and an index * @returns item * @group get @@ -28,9 +29,54 @@ export class Lists { return result; } + /** + * Gets the first item from the list. + * Example: From [10, 20, 30, 40], returns 10 + * @param inputs a list + * @returns first item + * @group get + * @shortname first item + * @drawable false + */ + getFirstItem(inputs: Inputs.Lists.ListCloneDto): T { + if (inputs.list.length === 0) { + throw new Error("List is empty"); + } + let result; + if (inputs.clone) { + result = structuredClone(inputs.list[0]); + } else { + result = inputs.list[0]; + } + return result; + } /** - * Gets items randomly by using a threshold + * Gets the last item from the list. + * Example: From [10, 20, 30, 40], returns 40 + * @param inputs a list + * @returns last item + * @group get + * @shortname last item + * @drawable false + */ + getLastItem(inputs: Inputs.Lists.ListCloneDto): T { + if (inputs.list.length === 0) { + throw new Error("List is empty"); + } + let result; + if (inputs.clone) { + result = structuredClone(inputs.list[inputs.list.length - 1]); + } else { + result = inputs.list[inputs.list.length - 1]; + } + return result; + } + + + /** + * Randomly keeps items from the list based on a probability threshold (0 to 1). + * Example: From [1, 2, 3, 4, 5] with threshold 0.5, might return [1, 3, 5] (50% chance for each item) * @param inputs a list and a threshold for randomization of items to remove * @returns list with remaining items * @group get @@ -52,7 +98,8 @@ export class Lists { } /** - * Gets a sub list between start and end indexes + * Extracts a portion of the list between start and end positions (end is exclusive). + * Example: From [10, 20, 30, 40, 50] with start=1 and end=4, returns [20, 30, 40] * @param inputs a list and start and end indexes * @returns sub list * @group get @@ -70,7 +117,9 @@ export class Lists { } /** - * Gets nth item in the list + * Gets every nth item from the list, starting from an optional offset position. + * Example: From [0, 1, 2, 3, 4, 5, 6, 7, 8] with nth=3 and offset=0, returns [0, 3, 6] + * Example: From [0, 1, 2, 3, 4, 5, 6, 7, 8] with nth=2 and offset=1, returns [1, 3, 5, 7] * @param inputs a list and index * @returns list with filtered items * @group get @@ -91,7 +140,8 @@ export class Lists { return result; } /** - * Gets elements by pattern + * Filters items from the list using a repeating true/false pattern. + * Example: From [0, 1, 2, 3, 4, 5] with pattern [true, true, false], returns [0, 1, 3, 4] (keeps items where pattern is true) * @param inputs a list and index * @returns list with filtered items * @group get @@ -129,7 +179,8 @@ export class Lists { } /** - * Merge elements of lists on a given level and flatten output if needed + * Merges elements from multiple lists at a specific nesting level, grouping elements by position. + * Example: From [[0, 1, 2], [3, 4, 5]] at level 0, returns [[0, 3], [1, 4], [2, 5]] * @param inputs lists, level and flatten data * @returns list with merged lists and flattened lists * @group get @@ -180,7 +231,8 @@ export class Lists { } /** - * Gets the longest list length from the list of lists + * Finds the length of the longest list among multiple lists. + * Example: From [[1, 2], [3, 4, 5, 6], [7]], returns 4 (length of [3, 4, 5, 6]) * @param inputs a list of lists * @returns number of max length * @group get @@ -203,7 +255,8 @@ export class Lists { } /** - * Reverse the list + * Reverses the order of items in the list. + * Example: From [1, 2, 3, 4, 5], returns [5, 4, 3, 2, 1] * @param inputs a list and an index * @returns item * @group edit @@ -219,7 +272,29 @@ export class Lists { } /** - * Flip 2d lists - every nth element of each list will form a separate list + * Randomly rearranges all items in the list (using Fisher-Yates algorithm). + * Example: From [1, 2, 3, 4, 5], might return [3, 1, 5, 2, 4] (order varies each time) + * @param inputs a list + * @returns shuffled list + * @group edit + * @shortname shuffle + * @drawable false + */ + shuffle(inputs: Inputs.Lists.ListCloneDto): T[] { + let res = inputs.list; + if (inputs.clone) { + res = structuredClone(inputs.list); + } + for (let i = res.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [res[i], res[j]] = [res[j], res[i]]; + } + return res; + } + + /** + * Transposes a 2D list by swapping rows and columns (all sublists must be equal length). + * Example: From [[0, 1, 2], [3, 4, 5]], returns [[0, 3], [1, 4], [2, 5]] * @param inputs a list of lists to flip * @returns item * @group edit @@ -254,7 +329,9 @@ export class Lists { } /** - * Group in lists of n elements + * Splits the list into smaller lists of n elements each. + * Example: From [0, 1, 2, 3, 4, 5, 6, 7, 8] with n=3, returns [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + * Example: From [0, 1, 2, 3, 4] with n=2 and keepRemainder=true, returns [[0, 1], [2, 3], [4]] * @param inputs a list * @returns items grouped in lists of n elements * @group edit @@ -283,7 +360,34 @@ export class Lists { } /** - * Get the depth of the list + * Checks whether the list contains a specific item. + * Example: List [10, 20, 30, 40] with item 30 returns true, with item 50 returns false + * @param inputs a list and an item + * @returns true if item is in list + * @group get + * @shortname contains item + * @drawable false + */ + includes(inputs: Inputs.Lists.IncludesDto): boolean { + return inputs.list.includes(inputs.item); + } + + /** + * Finds the position (index) of the first occurrence of an item in the list. + * Example: In [10, 20, 30, 20, 40], finding 20 returns 1 (first occurrence), finding 50 returns -1 (not found) + * @param inputs a list and an item + * @returns index of the item or -1 if not found + * @group get + * @shortname find index + * @drawable false + */ + findIndex(inputs: Inputs.Lists.IncludesDto): number { + return inputs.list.indexOf(inputs.item); + } + + /** + * Determines the maximum nesting level (depth) of a list structure. + * Example: [1, 2, 3] has depth 1, [[1, 2], [3, 4]] has depth 2, [[[1]]] has depth 3 * @param inputs a list * @returns number of depth * @group get @@ -313,7 +417,8 @@ export class Lists { } /** - * Gets the length of the list + * Returns the number of items in the list. + * Example: [10, 20, 30, 40, 50] returns 5, [] returns 0 * @param inputs a length list * @returns a number * @group get @@ -325,7 +430,8 @@ export class Lists { } /** - * Add item to the list + * Inserts an item at a specific position in the list. + * Example: In [10, 20, 30, 40], adding 99 at index 2 gives [10, 20, 99, 30, 40] * @param inputs a list, item and an index * @returns list with added item * @group add @@ -344,7 +450,8 @@ export class Lists { } /** - * Adds item to the list of provided indexes + * Inserts the same item at multiple specified positions in the list. + * Example: In [10, 20, 30], adding 99 at indexes [0, 2] gives [99, 10, 20, 99, 30] * @param inputs a list, item and an indexes * @returns list with added item * @group add @@ -369,7 +476,8 @@ export class Lists { } /** - * Adds items to the list of provided indexes matching 1:1, first item will go to first index provided, etc. + * Inserts multiple items at corresponding positions (first item at first index, second item at second index, etc.). + * Example: In [10, 20, 30], adding items [88, 99] at indexes [1, 2] gives [10, 88, 20, 99, 30] * @param inputs a list, items and an indexes * @returns list with added items * @group add @@ -402,7 +510,8 @@ export class Lists { } /** - * Remove item from the list + * Removes the item at a specific position in the list. + * Example: From [10, 20, 30, 40, 50], removing index 2 gives [10, 20, 40, 50] * @param inputs a list and index * @returns list with removed item * @group remove @@ -421,7 +530,69 @@ export class Lists { } /** - * Remove items from the list of provided indexes + * Removes the first item from the list. + * Example: From [10, 20, 30, 40], returns [20, 30, 40] + * @param inputs a list + * @returns list with first item removed + * @group remove + * @shortname remove first item + * @drawable false + */ + removeFirstItem(inputs: Inputs.Lists.ListCloneDto): T[] { + let res = inputs.list; + if (inputs.clone) { + res = structuredClone(inputs.list); + } + if (res.length > 0) { + res.shift(); + } + return res; + } + + /** + * Removes the last item from the list. + * Example: From [10, 20, 30, 40], returns [10, 20, 30] + * @param inputs a list + * @returns list with last item removed + * @group remove + * @shortname remove last item + * @drawable false + */ + removeLastItem(inputs: Inputs.Lists.ListCloneDto): T[] { + let res = inputs.list; + if (inputs.clone) { + res = structuredClone(inputs.list); + } + if (res.length > 0) { + res.pop(); + } + return res; + } + + /** + * Removes an item counting from the end of the list (index 0 = last item, 1 = second-to-last, etc.). + * Example: From [10, 20, 30, 40, 50], removing index 1 from end gives [10, 20, 30, 50] (removes 40) + * @param inputs a list and index from end + * @returns list with removed item + * @group remove + * @shortname remove item from end + * @drawable false + */ + removeItemAtIndexFromEnd(inputs: Inputs.Lists.RemoveItemAtIndexDto): T[] { + let res = inputs.list; + if (inputs.clone) { + res = structuredClone(inputs.list); + } + if (inputs.index >= 0 && inputs.index < res.length) { + const actualIndex = res.length - 1 - inputs.index; + res.splice(actualIndex, 1); + } + return res; + } + + /** + * Removes items at multiple specified positions from the list. + * Example: From [10, 20, 30, 40, 50], removing indexes [1, 3] gives [10, 30, 50] * @param inputs a list and indexes * @returns list with removed items * @group remove @@ -444,7 +615,8 @@ export class Lists { } /** - * Remove all items from the list + * Clears all items from the list, resulting in an empty list. + * Example: From [10, 20, 30, 40], returns [] * @param inputs a list * @returns The length is set to 0 and same array memory object is returned * @group remove @@ -457,7 +629,8 @@ export class Lists { } /** - * Remove item from the list + * Removes every nth item from the list, starting from an optional offset position. + * Example: From [0, 1, 2, 3, 4, 5, 6, 7, 8] with nth=3 and offset=0, returns [1, 2, 4, 5, 7, 8] (removes 0, 3, 6) * @param inputs a list and index * @returns list with removed item * @group remove @@ -479,7 +652,8 @@ export class Lists { } /** - * Removes items randomly by using a threshold + * Randomly removes items from the list based on a probability threshold (0 to 1). + * Example: From [1, 2, 3, 4, 5] with threshold 0.5, might return [2, 4] (50% chance to remove each item) * @param inputs a list and a threshold for randomization of items to remove * @returns list with removed items * @group remove @@ -501,11 +675,12 @@ export class Lists { } /** - * remove duplicate numbers from the list + * Removes duplicate numbers from the list, keeping only the first occurrence of each value. + * Example: From [1, 2, 3, 2, 4, 3, 5], returns [1, 2, 3, 4, 5] * @param inputs a list of numbers * @returns list with unique numbers * @group remove - * @shortname remove duplicates + * @shortname remove duplicate numbers * @drawable false */ removeDuplicateNumbers(inputs: Inputs.Lists.RemoveDuplicatesDto): number[] { @@ -517,7 +692,8 @@ export class Lists { } /** - * remove duplicate numbers from the list with tolerance + * Removes duplicate numbers that are within a specified tolerance range of each other. + * Example: From [1.0, 1.001, 2.0, 2.002, 3.0] with tolerance 0.01, returns [1.0, 2.0, 3.0] * @param inputs a list of numbers and the tolerance * @returns list with unique numbers * @group remove @@ -533,7 +709,25 @@ export class Lists { } /** - * Add item to the end of the list + * Removes duplicate items from the list using strict equality comparison (works with any type). + * Example: From ["a", "b", "c", "a", "d", "b"], returns ["a", "b", "c", "d"] + * @param inputs a list + * @returns list with unique items + * @group remove + * @shortname remove duplicates + * @drawable false + */ + removeDuplicates(inputs: Inputs.Lists.RemoveDuplicatesDto): T[] { + let res = inputs.list; + if (inputs.clone) { + res = structuredClone(inputs.list); + } + return res.filter((value, index, self) => self.indexOf(value) === index); + } + + /** + * Appends an item to the end of the list. + * Example: To [10, 20, 30], adding 40 gives [10, 20, 30, 40] * @param inputs a list and an item * @returns list with added item * @group add @@ -550,7 +744,8 @@ export class Lists { } /** - * Add item to the beginning of the list + * Adds an item to the beginning of the list. + * Example: To [10, 20, 30], prepending 5 gives [5, 10, 20, 30] * @param inputs a list and an item * @returns list with added item * @group add @@ -567,7 +762,8 @@ export class Lists { } /** - * Add item to the beginning or the end of the list + * Adds an item either at the beginning or end of the list based on the position parameter. + * Example: To [10, 20, 30], adding 5 at 'first' gives [5, 10, 20, 30], at 'last' gives [10, 20, 30, 5] * @param inputs a list, item and an option for first or last position * @returns list with added item * @group add @@ -588,7 +784,31 @@ export class Lists { } /** - * Creates an empty list + * Combines multiple lists into a single list by joining them end-to-end. + * Example: From [[1, 2], [3, 4], [5, 6]], returns [1, 2, 3, 4, 5, 6] + * @param inputs lists to concatenate + * @returns concatenated list + * @group add + * @shortname concatenate lists + * @drawable false + */ + concatenate(inputs: Inputs.Lists.ConcatenateDto): T[] { + let result = []; + if (inputs.clone) { + inputs.lists.forEach(list => { + result = result.concat(structuredClone(list)); + }); + } else { + inputs.lists.forEach(list => { + result = result.concat(list); + }); + } + return result; + } + + /** + * Creates a new empty list with no items. + * Example: Returns [] * @returns an empty array list * @group create * @shortname empty list @@ -599,7 +819,8 @@ export class Lists { } /** - * Repeat the item and add it in the new list + * Creates a new list by repeating an item a specified number of times. + * Example: Repeating 5 three times returns [5, 5, 5] * @param inputs an item to multiply * @returns list * @group create @@ -615,7 +836,8 @@ export class Lists { } /** - * Repeat the list items by adding them in the new list till the certain length of the list is reached + * Repeats a pattern of items cyclically until reaching a target list length. + * Example: Pattern [1, 2, 3] with length 7 returns [1, 2, 3, 1, 2, 3, 1] * @param inputs a list to multiply and a length limit * @returns list * @group create @@ -643,7 +865,8 @@ export class Lists { } /** - * Sort the list of numbers in ascending or descending order + * Sorts numbers in ascending (lowest to highest) or descending (highest to lowest) order. + * Example: [5, 2, 8, 1, 9] ascending returns [1, 2, 5, 8, 9], descending returns [9, 8, 5, 2, 1] * @param inputs a list of numbers to sort and an option for ascending or descending order * @returns list * @group sorting @@ -663,7 +886,8 @@ export class Lists { } /** - * Sort the list of texts in ascending or descending order alphabetically + * Sorts text strings alphabetically in ascending (A to Z) or descending (Z to A) order. + * Example: ["dog", "apple", "cat", "banana"] ascending returns ["apple", "banana", "cat", "dog"] * @param inputs a list of texts to sort and an option for ascending or descending order * @returns list * @group sorting @@ -683,7 +907,8 @@ export class Lists { } /** - * Sort by numeric JSON property value + * Sorts objects by comparing numeric values of a specified property. + * Example: [{age: 30}, {age: 20}, {age: 25}] sorted by 'age' ascending returns [{age: 20}, {age: 25}, {age: 30}] * @param inputs a list to sort, a property to sort by and an option for ascending or descending order * @returns list * @group sorting @@ -702,4 +927,34 @@ export class Lists { } } + /** + * Combines multiple lists by alternating elements from each list (first from list1, first from list2, second from list1, etc.). + * Example: From [[0, 1, 2], [3, 4, 5]], returns [0, 3, 1, 4, 2, 5] + * @param inputs Lists to interleave + * @returns Flattened interleaved list + * @group transform + * @shortname interleave lists + * @drawable false + */ + interleave(inputs: Inputs.Lists.InterleaveDto): T[] { + const lists = inputs.clone ? structuredClone(inputs.lists) : inputs.lists; + + if (!lists || lists.length === 0) { + throw new Error("Lists array is empty or does not exist"); + } + + const result: T[] = []; + const maxLength = Math.max(...lists.map(list => list.length)); + + for (let i = 0; i < maxLength; i++) { + for (const list of lists) { + if (i < list.length) { + result.push(list[i]); + } + } + } + + return result; + } + } diff --git a/packages/dev/base/lib/api/services/logic.ts b/packages/dev/base/lib/api/services/logic.ts index c7272143..49ae7c20 100644 --- a/packages/dev/base/lib/api/services/logic.ts +++ b/packages/dev/base/lib/api/services/logic.ts @@ -6,7 +6,8 @@ import * as Inputs from "../inputs"; export class Logic { /** - * Creates a boolean value - true or false + * Creates and returns a boolean value (pass-through for boolean input). + * Example: true → true, false → false * @param inputs a true or false boolean * @returns boolean * @group create @@ -18,7 +19,8 @@ export class Logic { } /** - * Creates a random boolean list of predefined length + * Generates a random boolean list where each value has a threshold chance of being true. + * Example: length=5, threshold=0.7 → might produce [true, true, false, true, true] * @param inputs a length and a threshold for randomization of true values * @returns booleans * @group create @@ -34,10 +36,10 @@ export class Logic { } /** - * Creates a random boolean list of true and false values based on a list of numbers. - * All values between true threshold will be true, all values above false threshold will be false, - * and the rest will be distributed between true and false based on the number of levels in a gradient pattern. - * That means that the closer the number gets to the false threshold the bigger the chance will be to get random false value. + * Converts numbers to booleans using two thresholds with gradient randomization between them. + * Values below trueThreshold → always true, above falseThreshold → always false. + * Between thresholds → probability gradient (closer to false threshold = higher chance of false). + * Example: [0.1, 0.4, 0.6, 0.9] with thresholds [0.3, 0.7] → [true, gradient, gradient, false] * @param inputs a length and a threshold for randomization of true values * @returns booleans * @group create @@ -68,7 +70,9 @@ export class Logic { } /** - * Creates a boolean list based on a list of numbers and a threshold. + * Converts numbers to booleans based on a threshold (below threshold → true, above → false). + * Can be inverted to flip the logic. + * Example: [0.3, 0.7, 0.5] with threshold=0.6 → [true, false, true] * @param inputs a length and a threshold for randomization of true values * @returns booleans * @group create @@ -91,7 +95,9 @@ export class Logic { } /** - * Creates a boolean list based on a list of numbers and a gap thresholds. Gap thresholds are pairs of numbers that define a range of numbers that will be true. + * Converts numbers to booleans using multiple range thresholds (gaps define true ranges). + * Values within any gap range → true, outside all gaps → false. Can be inverted. + * Example: [0.2, 0.5, 0.8] with gaps [[0.3, 0.6], [0.7, 0.9]] → [false, true, true] * @param inputs a length and a threshold for randomization of true values * @returns booleans * @group create @@ -122,7 +128,8 @@ export class Logic { } /** - * Apply not operator on the boolean + * Applies NOT operator to flip a boolean value. + * Example: true → false, false → true * @param inputs a true or false boolean * @returns boolean * @group edit @@ -134,7 +141,8 @@ export class Logic { } /** - * Apply not operator on a list of booleans + * Applies NOT operator to flip all boolean values in a list. + * Example: [true, false, true] → [false, true, false] * @param inputs a list of true or false booleans * @returns booleans * @group edit @@ -146,7 +154,8 @@ export class Logic { } /** - * Does comparison between first and second values + * Compares two values using various operators (==, !=, ===, !==, <, <=, >, >=). + * Example: 5 > 3 → true, "hello" === "world" → false * @param inputs two values to be compared * @returns Result of the comparison * @group operations @@ -177,7 +186,8 @@ export class Logic { } /** - * Transmits a value if boolean provided is true and undefined if boolean provided is false + * Conditionally passes a value through if boolean is true, otherwise returns undefined. + * Example: value=42, boolean=true → 42, value=42, boolean=false → undefined * @param inputs a value and a boolean value * @returns value or undefined * @group operations @@ -189,7 +199,8 @@ export class Logic { } /** - * Returns first defined value out of two + * Returns the first defined (non-undefined) value from two options (fallback pattern). + * Example: value1=42, value2=10 → 42, value1=undefined, value2=10 → 10 * @param inputs two values * @returns value or undefined * @group operations diff --git a/packages/dev/base/lib/api/services/math.test.ts b/packages/dev/base/lib/api/services/math.test.ts index 314c5ebf..717796fb 100644 --- a/packages/dev/base/lib/api/services/math.test.ts +++ b/packages/dev/base/lib/api/services/math.test.ts @@ -618,5 +618,280 @@ describe("Math unit tests", () => { const result = math.ease({ x: 0.99, min: 0, max: 1, ease: Inputs.Math.easeEnum.easeInOutBounce }); expect(result).toEqual(0.9946375000000001); }); + + it("should round to 3 decimals and keep non-zero trailing digits", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 1.32156, decimalPlaces: 3 }); + expect(result).toEqual(1.322); + }); + + it("should round to 3 decimals and remove trailing zeros", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 1.320000001, decimalPlaces: 3 }); + expect(result).toEqual(1.32); + }); + + it("should round 1.000 to 1 without decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 1.000, decimalPlaces: 3 }); + expect(result).toEqual(1); + }); + + it("should round 1.0000001 to 1 with 3 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 1.0000001, decimalPlaces: 3 }); + expect(result).toEqual(1); + }); + + it("should round 5.12 to 5.12 with 3 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 5.12, decimalPlaces: 3 }); + expect(result).toEqual(5.12); + }); + + it("should round 5.1 to 5.1 with 3 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 5.1, decimalPlaces: 3 }); + expect(result).toEqual(5.1); + }); + + it("should round 3.14159 to 3.14 with 2 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 3.14159, decimalPlaces: 2 }); + expect(result).toEqual(3.14); + }); + + it("should round 10.00000001 to 10 with 2 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 10.00000001, decimalPlaces: 2 }); + expect(result).toEqual(10); + }); + + it("should round 0.505 to 0.51 with 2 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 0.505, decimalPlaces: 2 }); + expect(result).toEqual(0.51); + }); + + it("should round 0.500 to 0.5 with 2 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 0.500, decimalPlaces: 2 }); + expect(result).toEqual(0.5); + }); + + it("should round 2.9999 to 3 with 3 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 2.9999, decimalPlaces: 3 }); + expect(result).toEqual(3); + }); + + it("should round negative -1.320000001 to -1.32 with 3 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: -1.320000001, decimalPlaces: 3 }); + expect(result).toEqual(-1.32); + }); + + it("should round negative -5.12789 to -5.128 with 3 decimals", () => { + const result = math.roundAndRemoveTrailingZeros({ number: -5.12789, decimalPlaces: 3 }); + expect(result).toEqual(-5.128); + }); + + it("should handle 0 decimal places", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 5.6789, decimalPlaces: 0 }); + expect(result).toEqual(6); + }); + + it("should handle 5 decimal places with trailing zeros", () => { + const result = math.roundAndRemoveTrailingZeros({ number: 1.123450000, decimalPlaces: 5 }); + expect(result).toEqual(1.12345); + }); + + it("should clamp value within range", () => { + const result = math.clamp({ number: 1.5, min: 0, max: 3 }); + expect(result).toEqual(1.5); + }); + + it("should clamp value above max to max", () => { + const result = math.clamp({ number: 5, min: 0, max: 3 }); + expect(result).toEqual(3); + }); + + it("should clamp value below min to min", () => { + const result = math.clamp({ number: -1, min: 0, max: 3 }); + expect(result).toEqual(0); + }); + + it("should clamp negative values", () => { + const result = math.clamp({ number: -5, min: -10, max: -2 }); + expect(result).toEqual(-5); + }); + + it("should lerp at t=0.5", () => { + const result = math.lerp({ start: 0, end: 10, t: 0.5 }); + expect(result).toEqual(5); + }); + + it("should lerp at t=0", () => { + const result = math.lerp({ start: 0, end: 10, t: 0 }); + expect(result).toEqual(0); + }); + + it("should lerp at t=1", () => { + const result = math.lerp({ start: 0, end: 10, t: 1 }); + expect(result).toEqual(10); + }); + + it("should lerp at t=0.25", () => { + const result = math.lerp({ start: 0, end: 10, t: 0.25 }); + expect(result).toEqual(2.5); + }); + + it("should lerp with negative values", () => { + const result = math.lerp({ start: -10, end: 10, t: 0.5 }); + expect(result).toEqual(0); + }); + + it("should inverse lerp at midpoint", () => { + const result = math.inverseLerp({ start: 0, end: 10, value: 5 }); + expect(result).toEqual(0.5); + }); + + it("should inverse lerp at start", () => { + const result = math.inverseLerp({ start: 0, end: 10, value: 0 }); + expect(result).toEqual(0); + }); + + it("should inverse lerp at end", () => { + const result = math.inverseLerp({ start: 0, end: 10, value: 10 }); + expect(result).toEqual(1); + }); + + it("should inverse lerp at 0.25", () => { + const result = math.inverseLerp({ start: 0, end: 10, value: 2.5 }); + expect(result).toEqual(0.25); + }); + + it("should inverse lerp with same start and end return 0", () => { + const result = math.inverseLerp({ start: 5, end: 5, value: 5 }); + expect(result).toEqual(0); + }); + + it("should smoothstep at 0", () => { + const result = math.smoothstep({ number: 0 }); + expect(result).toEqual(0); + }); + + it("should smoothstep at 1", () => { + const result = math.smoothstep({ number: 1 }); + expect(result).toEqual(1); + }); + + it("should smoothstep at 0.5", () => { + const result = math.smoothstep({ number: 0.5 }); + expect(result).toEqual(0.5); + }); + + it("should smoothstep clamp below 0", () => { + const result = math.smoothstep({ number: -0.5 }); + expect(result).toEqual(0); + }); + + it("should smoothstep clamp above 1", () => { + const result = math.smoothstep({ number: 1.5 }); + expect(result).toEqual(1); + }); + + it("should return sign of positive number", () => { + const result = math.sign({ number: 5 }); + expect(result).toEqual(1); + }); + + it("should return sign of negative number", () => { + const result = math.sign({ number: -3.14 }); + expect(result).toEqual(-1); + }); + + it("should return sign of zero", () => { + const result = math.sign({ number: 0 }); + expect(result).toEqual(0); + }); + + it("should return fractional part of positive number", () => { + const result = math.fract({ number: 3.14 }); + expect(result).toBeCloseTo(0.14, 5); + }); + + it("should return fractional part of negative number", () => { + const result = math.fract({ number: -2.3 }); + expect(result).toBeCloseTo(0.7, 5); + }); + + it("should return fractional part of integer", () => { + const result = math.fract({ number: 5 }); + expect(result).toEqual(0); + }); + + it("should wrap value above max", () => { + const result = math.wrap({ number: 1.5, min: 0, max: 1 }); + expect(result).toBeCloseTo(0.5, 5); + }); + + it("should wrap value below min", () => { + const result = math.wrap({ number: -0.3, min: 0, max: 1 }); + expect(result).toBeCloseTo(0.7, 5); + }); + + it("should wrap angle 370 to 10 degrees", () => { + const result = math.wrap({ number: 370, min: 0, max: 360 }); + expect(result).toBeCloseTo(10, 5); + }); + + it("should wrap value within range", () => { + const result = math.wrap({ number: 0.5, min: 0, max: 1 }); + expect(result).toBeCloseTo(0.5, 5); + }); + + it("should handle wrap with zero range", () => { + const result = math.wrap({ number: 5, min: 1, max: 1 }); + expect(result).toEqual(1); + }); + + it("should ping pong at 0.5", () => { + const result = math.pingPong({ t: 0.5, length: 1 }); + expect(result).toEqual(0.5); + }); + + it("should ping pong at 1.5", () => { + const result = math.pingPong({ t: 1.5, length: 1 }); + expect(result).toEqual(0.5); + }); + + it("should ping pong at 2.5", () => { + const result = math.pingPong({ t: 2.5, length: 1 }); + expect(result).toEqual(0.5); + }); + + it("should ping pong at 1", () => { + const result = math.pingPong({ t: 1, length: 1 }); + expect(result).toEqual(1); + }); + + it("should ping pong with negative t", () => { + const result = math.pingPong({ t: -0.5, length: 1 }); + expect(result).toEqual(0.5); + }); + + it("should move towards target within delta", () => { + const result = math.moveTowards({ current: 0, target: 10, maxDelta: 3 }); + expect(result).toEqual(3); + }); + + it("should move towards target and reach it", () => { + const result = math.moveTowards({ current: 8, target: 10, maxDelta: 3 }); + expect(result).toEqual(10); + }); + + it("should move towards negative target", () => { + const result = math.moveTowards({ current: 0, target: -10, maxDelta: 3 }); + expect(result).toEqual(-3); + }); + + it("should not overshoot target", () => { + const result = math.moveTowards({ current: 9.5, target: 10, maxDelta: 1 }); + expect(result).toEqual(10); + }); + + it("should move backwards towards target", () => { + const result = math.moveTowards({ current: 10, target: 0, maxDelta: 2 }); + expect(result).toEqual(8); + }); }); diff --git a/packages/dev/base/lib/api/services/math.ts b/packages/dev/base/lib/api/services/math.ts index 86b2f038..2283bf6b 100644 --- a/packages/dev/base/lib/api/services/math.ts +++ b/packages/dev/base/lib/api/services/math.ts @@ -6,7 +6,8 @@ import * as Inputs from "../inputs"; export class MathBitByBit { /** - * Creates a number + * Creates and returns a number value (pass-through for number input). + * Example: Input 42 → 42, Input 3.14 → 3.14 * @param inputs a number to be created * @returns number * @group create @@ -18,7 +19,8 @@ export class MathBitByBit { } /** - * Does basic math operations + * Performs basic arithmetic operations on two numbers (add, subtract, multiply, divide, power, modulus). + * Example: 5 + 3 → 8, 10 % 3 → 1, 2 ^ 3 → 8 * @param inputs two numbers and operator * @returns Result of math operation action * @group operations @@ -53,7 +55,8 @@ export class MathBitByBit { } /** - * Does modulus operation + * Calculates the remainder after division (modulus operation). + * Example: 10 % 3 → 1, 17 % 5 → 2 * @param inputs two numbers and operator * @returns Result of modulus operation * @group operations @@ -65,7 +68,8 @@ export class MathBitByBit { } /** - * Does rounding to decimals + * Rounds a number to specified decimal places. + * Example: 1.32156 with 3 decimals returns 1.322 * @param inputs a number and decimal places * @returns Result of rounding * @group operations @@ -77,7 +81,22 @@ export class MathBitByBit { } /** - * Does basic math operations on one number + * Rounds a number to specified decimal places and removes trailing zeros. + * Example: 1.32156 with 3 decimals returns 1.322, but 1.320000001 returns 1.32, and 1.000 returns 1 + * @param inputs a number and decimal places + * @returns Result of rounding as a number without trailing zeros + * @group operations + * @shortname round trim zeros + * @drawable false + */ + roundAndRemoveTrailingZeros(inputs: Inputs.Math.RoundToDecimalsDto): number { + const rounded = Math.round(inputs.number * Math.pow(10, inputs.decimalPlaces)) / Math.pow(10, inputs.decimalPlaces); + return parseFloat(rounded.toFixed(inputs.decimalPlaces)); + } + + /** + * Performs mathematical operations on a single number (absolute, negate, sqrt, trig functions, logarithms, etc.). + * Example: sqrt(5) → 2.236, abs(-3) → 3, sin(π/2) → 1 * @param inputs one number and operator action * @returns Result of math operation * @group operations @@ -151,7 +170,8 @@ export class MathBitByBit { } /** - * Remaps a number from one range to another + * Maps a number from one range to another range proportionally. + * Example: 5 from [0,10] to [0,100] → 50, 0.5 from [0,1] to [-10,10] → 0 * @param inputs one number and operator action * @returns Result of mapping * @group operations @@ -163,7 +183,8 @@ export class MathBitByBit { } /** - * Creates a random number between 0 and 1 + * Generates a random decimal number between 0 (inclusive) and 1 (exclusive). + * Example: Outputs like 0.342, 0.891, or any value in [0, 1) * @returns A random number between 0 and 1 * @group generate * @shortname random 0 - 1 @@ -174,7 +195,8 @@ export class MathBitByBit { } /** - * Creates a random number between low and high value + * Generates a random number within a specified range (low to high). + * Example: Range [0, 10] → outputs like 3.7, 8.2, or any value between 0 and 10 * @param inputs low and high numbers * @returns A random number * @group generate @@ -186,7 +208,8 @@ export class MathBitByBit { } /** - * Creates random numbers between low and high values + * Generates multiple random numbers within a specified range. + * Example: Range [0, 10] with 3 items → [2.5, 7.1, 4.8] * @param inputs low and high numbers * @returns A list of random numbers * @group generate @@ -202,7 +225,8 @@ export class MathBitByBit { } /** - * Creates a PI number + * Returns the mathematical constant π (pi) ≈ 3.14159. + * Example: Outputs 3.141592653589793 * @returns A number PI * @group generate * @shortname π @@ -213,7 +237,8 @@ export class MathBitByBit { } /** - * Rounds the number to decimal places + * Formats a number as a string with a fixed number of decimal places (always shows trailing zeros). + * Example: 3.14159 with 2 decimals → "3.14", 5 with 3 decimals → "5.000" * @param inputs a number to be rounded to decimal places * @returns number * @group operations @@ -225,7 +250,8 @@ export class MathBitByBit { } /** - * Adds two numbers + * Adds two numbers together. + * Example: 5 + 3 → 8, -2 + 7 → 5 * @param inputs two numbers * @returns number * @group basics @@ -237,7 +263,8 @@ export class MathBitByBit { } /** - * Subtracts two numbers + * Subtracts the second number from the first. + * Example: 10 - 3 → 7, 5 - 8 → -3 * @param inputs two numbers * @returns number * @group basics @@ -249,7 +276,8 @@ export class MathBitByBit { } /** - * Multiplies two numbers + * Multiplies two numbers together. + * Example: 5 × 3 → 15, -2 × 4 → -8 * @param inputs two numbers * @returns number * @group basics @@ -261,7 +289,8 @@ export class MathBitByBit { } /** - * Divides two numbers + * Divides the first number by the second. + * Example: 10 ÷ 2 → 5, 7 ÷ 2 → 3.5 * @param inputs two numbers * @returns number * @group basics @@ -273,7 +302,8 @@ export class MathBitByBit { } /** - * Powers a number + * Raises the first number to the power of the second (exponentiation). + * Example: 2³ → 8, 5² → 25, 10⁻¹ → 0.1 * @param inputs two numbers * @returns number * @group basics @@ -285,7 +315,8 @@ export class MathBitByBit { } /** - * Gets the square root of a number + * Calculates the square root of a number. + * Example: √9 → 3, √2 → 1.414, √16 → 4 * @param inputs a number * @returns number * @group basics @@ -297,7 +328,8 @@ export class MathBitByBit { } /** - * Gets the absolute value of a number + * Returns the absolute value (removes negative sign, always positive or zero). + * Example: |-5| → 5, |3| → 3, |0| → 0 * @param inputs a number * @returns number * @group basics @@ -309,7 +341,8 @@ export class MathBitByBit { } /** - * Rounds a number + * Rounds a number to the nearest integer. + * Example: 3.7 → 4, 2.3 → 2, 5.5 → 6 * @param inputs a number * @returns number * @group basics @@ -321,7 +354,8 @@ export class MathBitByBit { } /** - * Floors a number + * Rounds a number down to the nearest integer (toward negative infinity). + * Example: 3.7 → 3, -2.3 → -3, 5 → 5 * @param inputs a number * @returns number * @group basics @@ -333,7 +367,8 @@ export class MathBitByBit { } /** - * Ceils a number + * Rounds a number up to the nearest integer (toward positive infinity). + * Example: 3.2 → 4, -2.8 → -2, 5 → 5 * @param inputs a number * @returns number * @group basics @@ -345,7 +380,8 @@ export class MathBitByBit { } /** - * Negates a number + * Negates a number (flips its sign: positive becomes negative, negative becomes positive). + * Example: 5 → -5, -3 → 3, 0 → 0 * @param inputs a number * @returns number * @group basics @@ -357,7 +393,8 @@ export class MathBitByBit { } /** - * Gets the natural logarithm of a number + * Calculates the natural logarithm (base e) of a number. + * Example: ln(2.718) → ~1, ln(7.389) → ~2, ln(1) → 0 * @param inputs a number * @returns number * @group basics @@ -369,7 +406,8 @@ export class MathBitByBit { } /** - * Gets the base 10 logarithm of a number + * Calculates the base 10 logarithm of a number. + * Example: log₁₀(100) → 2, log₁₀(1000) → 3, log₁₀(10) → 1 * @param inputs a number * @returns number * @group basics @@ -381,7 +419,8 @@ export class MathBitByBit { } /** - * Raises 10 to the power of a number + * Raises 10 to the power of the input number. + * Example: 10² → 100, 10³ → 1000, 10⁻¹ → 0.1 * @param inputs a number * @returns number * @group basics @@ -393,7 +432,8 @@ export class MathBitByBit { } /** - * Gets the sine of a number + * Calculates the sine of an angle in radians. + * Example: sin(0) → 0, sin(π/2) → 1, sin(π) → ~0 * @param inputs a number * @returns number * @group basics @@ -405,7 +445,8 @@ export class MathBitByBit { } /** - * Gets the cosine of a number + * Calculates the cosine of an angle in radians. + * Example: cos(0) → 1, cos(π/2) → ~0, cos(π) → -1 * @param inputs a number * @returns number * @group basics @@ -417,7 +458,8 @@ export class MathBitByBit { } /** - * Gets the tangent of a number + * Calculates the tangent of an angle in radians. + * Example: tan(0) → 0, tan(π/4) → ~1, tan(π/2) → infinity * @param inputs a number * @returns number * @group basics @@ -429,7 +471,8 @@ export class MathBitByBit { } /** - * Gets the arcsine of a number + * Calculates the arcsine (inverse sine) in radians, returns angle whose sine is the input. + * Example: asin(0) → 0, asin(1) → π/2 (~1.57), asin(0.5) → π/6 (~0.524) * @param inputs a number * @returns number * @group basics @@ -441,7 +484,8 @@ export class MathBitByBit { } /** - * Gets the arccosine of a number + * Calculates the arccosine (inverse cosine) in radians, returns angle whose cosine is the input. + * Example: acos(1) → 0, acos(0) → π/2 (~1.57), acos(-1) → π (~3.14) * @param inputs a number * @returns number * @group basics @@ -453,7 +497,8 @@ export class MathBitByBit { } /** - * Gets the arctangent of a number + * Calculates the arctangent (inverse tangent) in radians, returns angle whose tangent is the input. + * Example: atan(0) → 0, atan(1) → π/4 (~0.785), atan(-1) → -π/4 * @param inputs a number * @returns number * @group basics @@ -465,7 +510,8 @@ export class MathBitByBit { } /** - * Gets the natural exponent of a number + * Calculates e raised to the power of the input (exponential function). + * Example: e⁰ → 1, e¹ → ~2.718, e² → ~7.389 * @param inputs a number * @returns number * @group basics @@ -477,7 +523,8 @@ export class MathBitByBit { } /** - * Converts degrees to radians + * Converts an angle from degrees to radians. + * Example: 180° → π (~3.14159), 90° → π/2 (~1.5708), 360° → 2π * @param inputs a number in degrees * @returns number * @group basics @@ -489,7 +536,8 @@ export class MathBitByBit { } /** - * Converts radians to degrees + * Converts an angle from radians to degrees. + * Example: π → 180°, π/2 → 90°, 2π → 360° * @param inputs a number in radians * @returns number * @group basics @@ -501,7 +549,9 @@ export class MathBitByBit { } /** - * Eases a number by providing x parameter 0-1 and a range of output values + * Applies an easing function to interpolate smoothly between min and max values. + * Example: x=0.5 from [0,100] with easeInQuad → applies quadratic acceleration curve + * Useful for smooth animations with various acceleration/deceleration curves. * @param inputs a number, min and max values, and ease type * @returns number * @group operations @@ -518,6 +568,146 @@ export class MathBitByBit { return res; } + /** + * Constrains a value between a minimum and maximum value. + * Example: clamp(5, 0, 3) returns 3, clamp(-1, 0, 3) returns 0, clamp(1.5, 0, 3) returns 1.5 + * @param inputs a number, min and max values + * @returns number clamped between min and max + * @group operations + * @shortname clamp + * @drawable false + */ + clamp(inputs: Inputs.Math.ClampDto): number { + return Math.max(inputs.min, Math.min(inputs.max, inputs.number)); + } + + /** + * Linear interpolation between two values using parameter t (0 to 1). + * Example: From 0 to 100 at t=0.5 → 50, From 10 to 20 at t=0.25 → 12.5 + * When t=0 returns start, when t=1 returns end. Useful for smooth transitions. + * @param inputs start value, end value, and interpolation parameter t + * @returns interpolated value + * @group operations + * @shortname lerp + * @drawable false + */ + lerp(inputs: Inputs.Math.LerpDto): number { + return inputs.start + (inputs.end - inputs.start) * inputs.t; + } + + /** + * Calculates the interpolation parameter t for a value between start and end (reverse of lerp). + * Example: Value 5 in range [0,10] → t=0.5, Value 2.5 in range [0,10] → t=0.25 + * Returns what t value would produce the given value in a lerp. Useful for finding relative position. + * @param inputs start value, end value, and the value to find t for + * @returns interpolation parameter (typically 0-1) + * @group operations + * @shortname inverse lerp + * @drawable false + */ + inverseLerp(inputs: Inputs.Math.InverseLerpDto): number { + if (inputs.start === inputs.end) { + return 0; + } + return (inputs.value - inputs.start) / (inputs.end - inputs.start); + } + + /** + * Hermite interpolation with smooth acceleration and deceleration (smoother than linear lerp). + * Example: x=0 → 0, x=0.5 → 0.5, x=1 → 1 (but with smooth S-curve in between) + * Input is automatically clamped to [0,1]. Output eases in and out smoothly. Great for animations. + * @param inputs a number between 0 and 1 + * @returns smoothly interpolated value + * @group operations + * @shortname smoothstep + * @drawable false + */ + smoothstep(inputs: Inputs.Math.NumberDto): number { + const t = Math.max(0, Math.min(1, inputs.number)); + return t * t * (3 - 2 * t); + } + + /** + * Returns the sign of a number: -1 for negative, 0 for zero, 1 for positive. + * Example: -5 → -1, 0 → 0, 3.14 → 1 + * Useful for determining direction or polarity. + * @param inputs a number + * @returns -1, 0, or 1 + * @group operations + * @shortname sign + * @drawable false + */ + sign(inputs: Inputs.Math.NumberDto): number { + return Math.sign(inputs.number); + } + + /** + * Returns the fractional part of a number (removes integer part, keeps decimals). + * Example: 3.14 → 0.14, 5.9 → 0.9, -2.3 → 0.7 + * Useful for wrapping values and creating repeating patterns. + * @param inputs a number + * @returns fractional part (always positive) + * @group operations + * @shortname fract + * @drawable false + */ + fract(inputs: Inputs.Math.NumberDto): number { + return inputs.number - Math.floor(inputs.number); + } + + /** + * Wraps a number within a specified range (creates repeating cycle). + * Example: 1.5 in range [0,1) → 0.5, -0.3 in range [0,1) → 0.7, 370° in range [0,360) → 10° + * Useful for angles, UVs, or any repeating domain. Like modulo but handles negatives properly. + * @param inputs a number, min and max values + * @returns wrapped value within range + * @group operations + * @shortname wrap + * @drawable false + */ + wrap(inputs: Inputs.Math.WrapDto): number { + const range = inputs.max - inputs.min; + if (range === 0) { + return inputs.min; + } + const normalized = (inputs.number - inputs.min) % range; + return normalized < 0 ? normalized + inputs.max : normalized + inputs.min; + } + + /** + * Creates a ping-pong (back-and-forth) effect that bounces a value between 0 and length. + * The value goes from 0→length, then back length→0, repeating this cycle. + * Example: With length=1: t=0→0, t=0.5→0.5, t=1→1 (peak), t=1.5→0.5, t=2→0, t=2.5→0.5 (repeats) + * Useful for creating bouncing animations like a ball or oscillating motion. + * @param inputs time value t and length + * @returns value bouncing between 0 and length + * @group operations + * @shortname ping pong + * @drawable false + */ + pingPong(inputs: Inputs.Math.PingPongDto): number { + const t = Math.abs(inputs.t) % (inputs.length * 2); + return t > inputs.length ? inputs.length * 2 - t : t; + } + + /** + * Moves a value toward a target by a maximum delta amount (never overshooting). + * Example: From 0 toward 10 by max 3 → 3, From 8 toward 10 by max 3 → 10 (reached) + * Useful for smooth movement with maximum speed limits. + * @param inputs current value, target value, and maximum delta + * @returns new value moved toward target + * @group operations + * @shortname move towards + * @drawable false + */ + moveTowards(inputs: Inputs.Math.MoveTowardsDto): number { + const delta = inputs.target - inputs.current; + if (Math.abs(delta) <= inputs.maxDelta) { + return inputs.target; + } + return inputs.current + Math.sign(delta) * inputs.maxDelta; + } + private easeInSine(x: number): number { return 1 - Math.cos((x * Math.PI) / 2); } diff --git a/packages/dev/base/lib/api/services/mesh.ts b/packages/dev/base/lib/api/services/mesh.ts index 68acb5f5..d724d3dc 100644 --- a/packages/dev/base/lib/api/services/mesh.ts +++ b/packages/dev/base/lib/api/services/mesh.ts @@ -9,7 +9,8 @@ export class MeshBitByBit { constructor(private readonly vector: Vector, private readonly polyline: Polyline) { } /** - * Computes the signed distance from a point to a plane. + * Calculates signed distance from a point to a plane (positive=above plane, negative=below). + * Example: point=[0,5,0], plane={normal:[0,1,0], d:0} → 5 (point is 5 units above XZ plane) * @param inputs a point and a plane * @returns signed distance * @group base @@ -21,7 +22,9 @@ export class MeshBitByBit { } /** - * Calculates the triangle plane from triangle. + * Calculates plane equation from triangle vertices (normal vector and distance from origin). + * Returns undefined if triangle is degenerate (zero area, collinear points). + * Example: triangle=[[0,0,0], [1,0,0], [0,1,0]] → {normal:[0,0,1], d:0} (XY plane) * @param inputs triangle and tolerance * @returns triangle plane * @group traingle @@ -46,7 +49,9 @@ export class MeshBitByBit { } /** - * Calculates the intersection of two triangles. + * Calculates intersection segment of two triangles (line segment where they cross). + * Returns undefined if triangles don't intersect, are parallel, or are coplanar. + * Example: triangle1=[[0,0,0], [2,0,0], [1,2,0]], triangle2=[[1,-1,1], [1,1,1], [1,1,-1]] → [[1,0,0], [1,1,0]] * @param inputs first triangle, second triangle, and tolerance * @returns intersection segment or undefined if no intersection * @group traingle @@ -199,7 +204,9 @@ export class MeshBitByBit { } /** - * Computes the intersection segments of two meshes. + * Calculates all intersection segments between two triangle meshes (pairwise triangle tests). + * Returns array of line segments where mesh surfaces intersect. + * Example: cube mesh intersecting with sphere mesh → multiple segments forming intersection curve * @param inputs first mesh, second mesh, and tolerance * @returns array of intersection segments * @group mesh @@ -228,7 +235,9 @@ export class MeshBitByBit { } /** - * Computes the intersection polylines of two meshes. + * Calculates intersection polylines between two meshes by sorting segments into connected paths. + * Segments are joined end-to-end to form continuous or closed curves. + * Example: cube-sphere intersection → closed polyline loops where surfaces meet * @param inputs first mesh, second mesh, and tolerance * @returns array of intersection polylines * @group mesh @@ -241,7 +250,9 @@ export class MeshBitByBit { } /** - * Computes the intersection points of two meshes. + * Calculates intersection points between two meshes as point arrays (one array per polyline). + * Closed polylines have first point duplicated at end. + * Example: cube-sphere intersection → arrays of points defining intersection curves * @param inputs first mesh, second mesh, and tolerance * @returns array of intersection points * @group mesh diff --git a/packages/dev/base/lib/api/services/point.ts b/packages/dev/base/lib/api/services/point.ts index 75eab2e8..262663f2 100644 --- a/packages/dev/base/lib/api/services/point.ts +++ b/packages/dev/base/lib/api/services/point.ts @@ -16,7 +16,8 @@ export class Point { constructor(private readonly geometryHelper: GeometryHelper, private readonly transforms: Transforms, private readonly vector: Vector, private readonly lists: Lists) { } /** - * Transforms the single point + * Applies transformation matrix to a single point (rotates, scales, or translates). + * Example: point=[0,0,0] with translation [5,5,0] → [5,5,0] * @param inputs Contains a point and the transformations to apply * @returns Transformed point * @group transforms @@ -31,7 +32,8 @@ export class Point { } /** - * Transforms multiple points + * Applies same transformation matrix to multiple points (batch transform). + * Example: 5 points with rotation 90° → all 5 points rotated together * @param inputs Contains points and the transformations to apply * @returns Transformed points * @group transforms @@ -43,7 +45,9 @@ export class Point { } /** - * Transforms multiple points by multiple transformations + * Applies different transformation matrices to corresponding points (one transform per point). + * Arrays must have equal length. + * Example: 3 points with 3 different translations → each point moved independently * @param inputs Contains points and the transformations to apply * @returns Transformed points * @group transforms @@ -60,7 +64,8 @@ export class Point { } /** - * Translate multiple points + * Moves multiple points by a translation vector (same offset for all points). + * Example: points=[[0,0,0], [1,0,0]], translation=[5,5,0] → [[5,5,0], [6,5,0]] * @param inputs Contains points and the translation vector * @returns Translated points * @group transforms @@ -73,7 +78,9 @@ export class Point { } /** - * Translate multiple points + * Moves multiple points by corresponding translation vectors (one vector per point). + * Arrays must have equal length. + * Example: 3 points with 3 different vectors → each point moved by its corresponding vector * @param inputs Contains points and the translation vector * @returns Translated points * @group transforms @@ -91,7 +98,8 @@ export class Point { } /** - * Translate multiple points by x, y, z values provided + * Moves multiple points by separate X, Y, Z values (convenience method for translation). + * Example: points=[[0,0,0]], x=10, y=5, z=0 → [[10,5,0]] * @param inputs Contains points and the translation in x y and z * @returns Translated points * @group transforms @@ -104,7 +112,8 @@ export class Point { } /** - * Scale multiple points by providing center point and x, y, z scale factors + * Scales multiple points around a center point with different factors per axis. + * Example: points=[[10,0,0]], center=[5,0,0], scaleXyz=[2,1,1] → [[15,0,0]] (doubles X distance from center) * @param inputs Contains points, center point and scale factors * @returns Scaled points * @group transforms @@ -117,7 +126,8 @@ export class Point { } /** - * Stretch multiple points by providing center point, direction and uniform scale factor + * Stretches multiple points along a direction from a center point (directional scaling). + * Example: points=[[10,0,0]], center=[0,0,0], direction=[1,0,0], scale=2 → [[20,0,0]] * @param inputs Contains points, center point, direction and scale factor * @returns Stretched points * @group transforms @@ -130,7 +140,8 @@ export class Point { } /** - * Rotate multiple points by providing center point, axis and degrees of rotation + * Rotates multiple points around a center point along a custom axis. + * Example: points=[[10,0,0]], center=[0,0,0], axis=[0,1,0], angle=90° → [[0,0,-10]] * @param inputs Contains points, axis, center point and angle of rotation * @returns Rotated points * @group transforms @@ -143,7 +154,8 @@ export class Point { } /** - * Gets a bounding box of the points + * Calculates axis-aligned bounding box containing all points (min, max, center, width, height, length). + * Example: points=[[0,0,0], [10,5,3]] → {min:[0,0,0], max:[10,5,3], center:[5,2.5,1.5], width:10, height:5, length:3} * @param inputs Points * @returns Bounding box of points * @group extract @@ -182,7 +194,8 @@ export class Point { } /** - * Measures the closest distance between a point and a collection of points + * Calculates distance to the nearest point in a collection. + * Example: point=[0,0,0], points=[[5,0,0], [10,0,0], [3,0,0]] → 3 (distance to [3,0,0]) * @param inputs Point from which to measure and points to measure the distance against * @returns Distance to closest point * @group extract @@ -194,7 +207,8 @@ export class Point { } /** - * Finds the closest point index between a point and a collection of points. Caution, index is not 0 based, it starts with 1. + * Finds array index of the nearest point in a collection (1-based index, not 0-based). + * Example: point=[0,0,0], points=[[5,0,0], [10,0,0], [3,0,0]] → 3 (index of [3,0,0]) * @param inputs Point from which to find the index in a collection of points * @returns Closest point index * @group extract @@ -206,7 +220,8 @@ export class Point { } /** - * Finds the closest point in a collection + * Finds the nearest point in a collection to a reference point. + * Example: point=[0,0,0], points=[[5,0,0], [10,0,0], [3,0,0]] → [3,0,0] * @param inputs Point and points collection to find the closest point in * @returns Closest point * @group extract @@ -218,7 +233,8 @@ export class Point { } /** - * Finds the distance between two points + * Calculates Euclidean distance between two points. + * Example: start=[0,0,0], end=[3,4,0] → 5 (using Pythagorean theorem: √(3²+4²)) * @param inputs Coordinates of start and end points * @returns Distance * @group measure @@ -233,7 +249,8 @@ export class Point { } /** - * Finds the distances between the start point and multiple end points + * Calculates distances from a start point to multiple end points. + * Example: start=[0,0,0], endPoints=[[3,0,0], [0,4,0], [5,0,0]] → [3, 4, 5] * @param inputs Coordinates of start and end points * @returns Distances * @group measure @@ -247,7 +264,8 @@ export class Point { } /** - * Multiply point by a specified amount + * Duplicates a point N times (creates array with N copies of the same point). + * Example: point=[5,5,0], amountOfPoints=3 → [[5,5,0], [5,5,0], [5,5,0]] * @param inputs The point to be multiplied and the amount of points to create * @returns Distance * @group transforms @@ -263,7 +281,8 @@ export class Point { } /** - * Get x coordinate of the point + * Extracts X coordinate from a point. + * Example: point=[5,10,3] → 5 * @param inputs The point * @returns X coordinate * @group get @@ -275,7 +294,8 @@ export class Point { } /** - * Get y coordinate of the point + * Extracts Y coordinate from a point. + * Example: point=[5,10,3] → 10 * @param inputs The point * @returns Y coordinate * @group get @@ -287,7 +307,8 @@ export class Point { } /** - * Get z coordinate of the point + * Extracts Z coordinate from a point. + * Example: point=[5,10,3] → 3 * @param inputs The point * @returns Z coordinate * @group get @@ -299,7 +320,8 @@ export class Point { } /** - * Get average point of points + * Calculates centroid (average position) of multiple points. + * Example: points=[[0,0,0], [10,0,0], [10,10,0]] → [6.67,3.33,0] * @param inputs The points * @returns point * @group extract @@ -325,7 +347,8 @@ export class Point { } /** - * Creates the xyz point + * Creates a 3D point from X, Y, Z coordinates. + * Example: x=10, y=5, z=3 → [10,5,3] * @param inputs xyz information * @returns point 3d * @group create @@ -337,7 +360,8 @@ export class Point { } /** - * Creates the xy point + * Creates a 2D point from X, Y coordinates. + * Example: x=10, y=5 → [10,5] * @param inputs xy information * @returns point 3d * @group create @@ -349,7 +373,9 @@ export class Point { } /** - * Creates the spiral out of multiple points + * Creates logarithmic spiral points using golden angle or custom widening factor. + * Generates natural spiral patterns common in nature (sunflower, nautilus shell). + * Example: numberPoints=100, radius=10, phi=1.618 → 100 points forming outward spiral * @param inputs Spiral information * @returns Specified number of points in the array along the spiral * @group create @@ -371,8 +397,9 @@ export class Point { } /** - * Creates a flat point grid on XY plane. This grid contains center points for hexagons of the given radius. - * Be aware that we control only the nr of hexagons to be made and not the length and width of the grid. + * Creates hexagonal grid center points on XY plane (honeycomb pattern). + * Grid size controlled by number of hexagons, not width/height. + * Example: radiusHexagon=1, nrHexagonsX=3, nrHexagonsY=3 → 9 hex centers in grid pattern * @param inputs Information about hexagon and the grid * @returns Points in the array on the grid * @group create @@ -409,9 +436,9 @@ export class Point { } /** - * Creates a pointy-top or flat-top hexagon grid, scaling hexagons to fit specified dimensions exactly. - * Returns both center points and the vertices of each (potentially scaled) hexagon. - * Hexagons are ordered column-first, then row-first. + * Creates hexagonal grid scaled to fit within specified width/height bounds (auto-calculates hex size). + * Returns center points and hex vertices. Supports pointy-top or flat-top orientation. + * Example: width=10, height=10, nrHexagonsInHeight=3 → hex grid filling 10×10 area with 3 rows * @param inputs Information about the desired grid dimensions and hexagon counts. * @returns An object containing the array of center points and an array of hexagon vertex arrays. * @group create @@ -954,7 +981,8 @@ export class Point { } /** - * Removes consecutive duplicates from the point array with tolerance + * Removes consecutive duplicate points from array within tolerance. + * Example: [[0,0,0], [0,0,0], [1,0,0], [1,0,0], [2,0,0]] → [[0,0,0], [1,0,0], [2,0,0]] * @param inputs points, tolerance and check first and last * @returns Points in the array without consecutive duplicates * @group clean @@ -966,7 +994,8 @@ export class Point { } /** - * Creates a normal vector from 3 points + * Calculates normal vector from three points using cross product (perpendicular to plane). + * Example: p1=[0,0,0], p2=[1,0,0], p3=[0,1,0] → [0,0,1] (pointing up from XY plane) * @param inputs Three points and the reverse normal flag * @returns Normal vector * @group create @@ -1033,7 +1062,8 @@ export class Point { } /** - * Checks if two points are almost equal + * Checks if two points are approximately equal within tolerance (distance-based comparison). + * Example: point1=[1.0000001, 2.0, 3.0], point2=[1.0, 2.0, 3.0], tolerance=1e-6 → true * @param inputs Two points and the tolerance * @returns true if the points are almost equal * @group measure @@ -1048,7 +1078,8 @@ export class Point { } /** - * Sorts points lexicographically (X, then Y, then Z) + * Sorts points lexicographically (by X, then Y, then Z coordinates). + * Example: [[5,0,0], [1,0,0], [3,0,0]] → [[1,0,0], [3,0,0], [5,0,0]] * @param inputs points * @returns sorted points * @group sort diff --git a/packages/dev/base/lib/api/services/polyline.ts b/packages/dev/base/lib/api/services/polyline.ts index 071038ce..f04b0da4 100644 --- a/packages/dev/base/lib/api/services/polyline.ts +++ b/packages/dev/base/lib/api/services/polyline.ts @@ -13,7 +13,8 @@ export class Polyline { constructor(private readonly vector: Vector, private readonly point: Point, private readonly line: Line, private readonly geometryHelper: GeometryHelper) { } /** - * Gets the length of the polyline + * Calculates total length of polyline by summing distances between consecutive points. + * Example: points=[[0,0,0], [3,0,0], [3,4,0]] → 3 + 4 = 7 * @param inputs a polyline * @returns length * @group get @@ -31,7 +32,8 @@ export class Polyline { } /** - * Gets the number of points in the polyline + * Counts number of points in polyline. + * Example: polyline with points=[[0,0,0], [1,0,0], [1,1,0]] → 3 * @param inputs a polyline * @returns nr of points * @group get @@ -43,7 +45,8 @@ export class Polyline { } /** - * Gets the points of the polyline + * Extracts points array from polyline object. + * Example: polyline={points:[[0,0,0], [1,0,0]]} → [[0,0,0], [1,0,0]] * @param inputs a polyline * @returns points * @group get @@ -55,7 +58,8 @@ export class Polyline { } /** - * Reverse the points of the polyline + * Reverses point order of polyline (flips direction). + * Example: points=[[0,0,0], [1,0,0], [2,0,0]] → [[2,0,0], [1,0,0], [0,0,0]] * @param inputs a polyline * @returns reversed polyline * @group convert @@ -67,7 +71,8 @@ export class Polyline { } /** - * Transform the polyline + * Applies transformation matrix to all points in polyline (rotates, scales, or translates). + * Example: polyline with 4 points, translation [5,0,0] → all points moved +5 in X direction * @param inputs a polyline * @returns transformed polyline * @group transforms @@ -82,7 +87,8 @@ export class Polyline { } /** - * Create the polyline + * Creates a polyline from points array with optional isClosed flag. + * Example: points=[[0,0,0], [1,0,0], [1,1,0]], isClosed=true → {points:..., isClosed:true} * @param inputs points and info if its closed * @returns polyline * @group create @@ -97,7 +103,9 @@ export class Polyline { } /** - * Create the lines from the polyline + * Converts polyline to line segments (each segment as line object with start/end). + * Closed polylines include closing segment. + * Example: 3 points → 2 or 3 lines (depending on isClosed) * @param inputs polyline * @returns lines * @group convert @@ -113,7 +121,9 @@ export class Polyline { } /** - * Create the segments from the polyline + * Converts polyline to segment arrays (each segment as [point1, point2]). + * Closed polylines include closing segment if endpoints differ. + * Example: 4 points, closed → 4 segments connecting all points in a loop * @param inputs polyline * @returns segments * @group convert @@ -147,7 +157,9 @@ export class Polyline { } /** - * Finds the points of self intersection of the polyline + * Finds points where polyline crosses itself (self-intersection points). + * Skips adjacent segments and deduplicates close points. + * Example: figure-8 shaped polyline → returns center crossing point * @param inputs points of self intersection * @returns polyline * @group intersections @@ -207,7 +219,9 @@ export class Polyline { } /** - * Finds the intersection points between two polylines + * Finds intersection points between two polylines (all segment-segment crossings). + * Tests all segment pairs and deduplicates close points. + * Example: crossing polylines forming an X → returns center intersection point * @param inputs two polylines and tolerance * @returns points * @group intersection @@ -255,7 +269,9 @@ export class Polyline { } /** - * Create the polylines from segments that are potentially connected but scrambled randomly + * Sorts scrambled segments into connected polylines by matching endpoints. + * Uses spatial hashing for efficient connection finding. + * Example: 10 random segments that form 2 connected paths → 2 polylines * @param inputs segments * @returns polylines * @group sort diff --git a/packages/dev/base/lib/api/services/text.ts b/packages/dev/base/lib/api/services/text.ts index 2029e4ab..6c6ea7a6 100644 --- a/packages/dev/base/lib/api/services/text.ts +++ b/packages/dev/base/lib/api/services/text.ts @@ -11,7 +11,8 @@ export class TextBitByBit { } /** - * Creates a text + * Creates and returns a text string (pass-through for text input). + * Example: text="Hello World" → "Hello World" * @param inputs a text * @returns text * @group create @@ -23,7 +24,8 @@ export class TextBitByBit { } /** - * Split the text to multiple pieces by a separator + * Splits text into multiple pieces using a separator string. + * Example: text="apple,banana,cherry", separator="," → ["apple", "banana", "cherry"] * @param inputs a text * @returns text * @group transform @@ -35,7 +37,8 @@ export class TextBitByBit { } /** - * Replace all occurrences of a text by another text + * Replaces all occurrences of a search string with a replacement string. + * Example: text="hello hello", search="hello", replaceWith="hi" → "hi hi" * @param inputs a text * @returns text * @group transform @@ -47,7 +50,8 @@ export class TextBitByBit { } /** - * Join multiple items by a separator into text + * Joins multiple items into a single text string using a separator. + * Example: list=["apple", "banana", "cherry"], separator=", " → "apple, banana, cherry" * @param inputs a list of items * @returns text * @group transform @@ -83,7 +87,8 @@ export class TextBitByBit { } /** - * Format a text with values + * Formats text with placeholder values using {0}, {1}, etc. syntax. + * Example: text="Point: ({0}, {1})", values=[10, 5] → "Point: (10, 5)" * @param inputs a text and values * @returns formatted text * @group transform @@ -97,7 +102,9 @@ export class TextBitByBit { } /** - * Creates a vector segments for character and includes width and height information + * Converts a character to vector paths (polylines) with width and height data for rendering. + * Uses simplex stroke font to generate 2D line segments representing the character shape. + * Example: char="A", height=10 → {width:8, height:10, paths:[[points forming A shape]]} * @param inputs a text * @returns width, height and segments as json * @group vector @@ -136,7 +143,9 @@ export class TextBitByBit { } /** - * Creates a vector text lines for a given text and includes width and height information + * Converts multi-line text to vector paths (polylines) with alignment and spacing controls. + * Supports line breaks, letter spacing, line spacing, horizontal alignment, and origin centering. + * Example: text="Hello\nWorld", height=10, align=center → [{line1 chars}, {line2 chars}] * @param inputs a text as string * @returns segments * @group vector diff --git a/packages/dev/base/lib/api/services/transforms.ts b/packages/dev/base/lib/api/services/transforms.ts index 110de5f3..ad4f00d2 100644 --- a/packages/dev/base/lib/api/services/transforms.ts +++ b/packages/dev/base/lib/api/services/transforms.ts @@ -14,7 +14,9 @@ export class Transforms { constructor(private readonly vector: Vector, private readonly math: MathBitByBit) { } /** - * Creates a rotation transformations around the center and an axis + * Creates rotation transformations around a center point and custom axis. + * Combines translation to origin, axis rotation, then translation back. + * Example: center=[5,0,0], axis=[0,1,0], angle=90° → rotates around vertical axis through point [5,0,0] * @param inputs Rotation around center with an axis information * @returns array of transformations * @group rotation @@ -33,7 +35,8 @@ export class Transforms { } /** - * Creates a rotation transformations around the center and an X axis + * Creates rotation transformations around a center point along the X axis. + * Example: center=[5,5,5], angle=90° → rotates 90° around X axis through point [5,5,5] * @param inputs Rotation around center with an X axis information * @returns array of transformations * @group rotation @@ -49,7 +52,8 @@ export class Transforms { } /** - * Creates a rotation transformations around the center and an Y axis + * Creates rotation transformations around a center point along the Y axis. + * Example: center=[0,0,0], angle=45° → rotates 45° around Y axis through origin * @param inputs Rotation around center with an Y axis information * @returns array of transformations * @group rotation @@ -65,7 +69,8 @@ export class Transforms { } /** - * Creates a rotation transformations around the center and an Z axis + * Creates rotation transformations around a center point along the Z axis. + * Example: center=[10,10,0], angle=180° → rotates 180° around Z axis through point [10,10,0] * @param inputs Rotation around center with an Z axis information * @returns array of transformations * @group rotation @@ -81,7 +86,9 @@ export class Transforms { } /** - * Creates a rotation transformations with yaw pitch and roll + * Creates rotation transformations using yaw-pitch-roll (Euler angles) around a center point. + * Yaw → Y axis rotation, Pitch → X axis rotation, Roll → Z axis rotation. + * Example: center=[0,0,0], yaw=90°, pitch=0°, roll=0° → rotates 90° around Y axis * @param inputs Yaw pitch roll rotation information * @returns array of transformations * @group rotation @@ -100,10 +107,11 @@ export class Transforms { } /** - * Scale transformation around center and xyz directions + * Creates non-uniform scale transformation around a center point (different scale per axis). + * Example: center=[5,5,5], scaleXyz=[2,1,0.5] → doubles X, keeps Y, halves Z around point [5,5,5] * @param inputs Scale center xyz trnansformation * @returns array of transformations - * @group rotation + * @group scale * @shortname center xyz * @drawable false */ @@ -116,7 +124,8 @@ export class Transforms { } /** - * Creates the scale transformation in x, y and z directions + * Creates non-uniform scale transformation from origin (different scale per axis). + * Example: scaleXyz=[2,3,1] → doubles X, triples Y, keeps Z unchanged * @param inputs Scale XYZ number array information * @returns transformation * @group scale @@ -128,9 +137,9 @@ export class Transforms { } /** - * Creates a stretch transformation along a specific direction, relative to a center point. - * This scales points along the given direction vector while leaving points in the - * plane perpendicular to the direction (passing through the center) unchanged. + * Creates directional stretch transformation that scales along a specific direction from a center point. + * Points move only along the direction vector; perpendicular plane remains unchanged. + * Example: center=[0,0,0], direction=[1,0,0], scale=2 → stretches 2× along X axis only * @param inputs Defines the center, direction, and scale factor for the stretch. * @returns Array of transformations: [Translate To Origin, Stretch, Translate Back]. * @group scale @@ -146,7 +155,8 @@ export class Transforms { } /** - * Creates uniform scale transformation + * Creates uniform scale transformation from origin (same scale on all axes). + * Example: scale=2 → doubles size in all directions (X, Y, Z) * @param inputs Scale Dto * @returns transformation * @group scale @@ -158,7 +168,8 @@ export class Transforms { } /** - * Creates uniform scale transformation from the center + * Creates uniform scale transformation around a center point (same scale on all axes). + * Example: center=[5,5,5], scale=0.5 → halves size in all directions around point [5,5,5] * @param inputs Scale Dto with center point information * @returns array of transformations * @group scale @@ -174,7 +185,8 @@ export class Transforms { } /** - * Creates the translation transformation + * Creates translation transformation (moves objects in space). + * Example: translation=[10,5,0] → moves object 10 units in X, 5 in Y, 0 in Z * @param inputs Translation information * @returns transformation * @group translation @@ -186,7 +198,8 @@ export class Transforms { } /** - * Creates the translation transformation + * Creates multiple translation transformations (batch move operations). + * Example: translations=[[1,0,0], [0,2,0]] → generates two transforms: move +X, move +Y * @param inputs Translation information * @returns transformation * @group translations @@ -198,7 +211,8 @@ export class Transforms { } /** - * Creates the identity transformation + * Creates identity transformation matrix (no transformation - leaves objects unchanged). + * Returns 4×4 matrix: [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] * @returns transformation * @group identity * @shortname identity diff --git a/packages/dev/base/lib/api/services/vector.ts b/packages/dev/base/lib/api/services/vector.ts index 68382ae1..17463b58 100644 --- a/packages/dev/base/lib/api/services/vector.ts +++ b/packages/dev/base/lib/api/services/vector.ts @@ -13,7 +13,8 @@ export class Vector { constructor(private readonly math: MathBitByBit, private readonly geometryHelper: GeometryHelper) { } /** - * Removes all duplicate vectors from the input array + * Removes all duplicate vectors from the input array (keeps only unique vectors). + * Example: [[1,2,3], [4,5,6], [1,2,3], [7,8,9]] → [[1,2,3], [4,5,6], [7,8,9]] * @param inputs Contains vectors and a tolerance value * @returns Array of vectors without duplicates * @group remove @@ -25,7 +26,8 @@ export class Vector { } /** - * Removes consecutive duplicate vectors from the input array + * Removes consecutive duplicate vectors from the input array (only removes duplicates that appear next to each other). + * Example: [[1,2], [1,2], [3,4], [1,2]] → [[1,2], [3,4], [1,2]] (only removed consecutive duplicate) * @param inputs Contains vectors and a tolerance value * @returns Array of vectors without duplicates * @group remove @@ -37,7 +39,8 @@ export class Vector { } /** - * Checks if two vectors are the same within a given tolerance + * Checks if two vectors are the same within a given tolerance (accounts for floating point precision). + * Example: [1,2,3] vs [1.0001,2.0001,3.0001] with tolerance 0.001 → true * @param inputs Contains two vectors and a tolerance value * @returns Boolean indicating if vectors are the same * @group validate @@ -49,7 +52,8 @@ export class Vector { } /** - * Measures the angle between two vectors in degrees + * Measures the angle between two vectors in degrees (always returns positive angle 0-180°). + * Example: [1,0,0] and [0,1,0] → 90° (perpendicular vectors) * @param inputs Contains two vectors represented as number arrays * @group angles * @shortname angle @@ -63,7 +67,8 @@ export class Vector { } /** - * Measures the normalized 2d angle between two vectors in degrees + * Measures the normalized 2D angle between two vectors in degrees (considers direction, can be negative). + * Example: [1,0] to [0,1] → 90°, [0,1] to [1,0] → -90° * @param inputs Contains two vectors represented as number arrays * @returns Number in degrees * @group angles @@ -78,7 +83,8 @@ export class Vector { } /** - * Measures a positive angle between two vectors given the reference vector in degrees + * Measures a positive angle between two vectors given the reference vector in degrees (always 0-360°). + * Example: converts negative signed angles to positive by adding 360° when needed * @param inputs Contains information of two vectors and a reference vector * @returns Number in degrees * @group angles @@ -91,7 +97,8 @@ export class Vector { } /** - * Adds all vector xyz values together and create a new vector + * Adds all vector xyz values together element-wise and creates a new vector. + * Example: [[1,2,3], [4,5,6], [7,8,9]] → [12,15,18] (sums each column) * @param inputs Vectors to be added * @returns New vector that has xyz values as sums of all the vectors * @group sum @@ -111,7 +118,8 @@ export class Vector { } /** - * Adds two vectors together + * Adds two vectors together element-wise. + * Example: [1,2,3] + [4,5,6] → [5,7,9] * @param inputs Two vectors to be added * @returns Number array representing vector * @group sum @@ -127,7 +135,8 @@ export class Vector { } /** - * Checks if the boolean array contains only true values, if there's a single false it will return false. + * Checks if the boolean array contains only true values, returns false if there's a single false. + * Example: [true, true, true] → true, [true, false, true] → false * @param inputs Vectors to be checked * @returns Boolean indicating if vector contains only true values * @group sum @@ -139,10 +148,11 @@ export class Vector { } /** - * Cross two vectors + * Computes the cross product of two 3D vectors (perpendicular vector to both inputs). + * Example: [1,0,0] × [0,1,0] → [0,0,1] (right-hand rule) * @param inputs Two vectors to be crossed * @group base - * @shortname all + * @shortname cross * @returns Crossed vector * @drawable false */ @@ -155,7 +165,8 @@ export class Vector { } /** - * Squared distance between two vectors + * Calculates squared distance between two vectors (faster than distance, avoids sqrt). + * Example: [0,0,0] to [3,4,0] → 25 (distance 5 squared) * @param inputs Two vectors * @returns Number representing squared distance between two vectors * @group distance @@ -171,7 +182,8 @@ export class Vector { } /** - * Distance between two vectors + * Calculates the Euclidean distance between two vectors. + * Example: [0,0,0] to [3,4,0] → 5, [1,1] to [4,5] → 5 * @param inputs Two vectors * @returns Number representing distance between two vectors * @group distance @@ -183,7 +195,8 @@ export class Vector { } /** - * Divide the vector by a scalar value + * Divides each element of the vector by a scalar value. + * Example: [10,20,30] ÷ 2 → [5,10,15] * @param inputs Contains vector and a scalar * @returns Vector that is a result of division by a scalar * @group base @@ -199,7 +212,8 @@ export class Vector { } /** - * Computes the domain between minimum and maximum values of the vector + * Computes the domain (range) between minimum and maximum values of the vector. + * Example: [1,3,5,9] → 8 (difference between last and first: 9-1) * @param inputs Vector information * @returns Number representing distance between two vectors * @group base @@ -211,7 +225,8 @@ export class Vector { } /** - * Dot product between two vectors + * Calculates the dot product between two vectors (measures similarity/projection). + * Example: [1,2,3] • [4,5,6] → 32 (1×4 + 2×5 + 3×6), perpendicular vectors → 0 * @param inputs Two vectors * @returns Number representing dot product of the vector * @group base @@ -227,7 +242,8 @@ export class Vector { } /** - * Checks if vector is finite for each number and returns a boolean array + * Checks if each element in the vector is finite and returns a boolean array. + * Example: [1, 2, Infinity, 3] → [true, true, false, true] * @param inputs Vector with possibly infinite values * @returns Vector array that contains boolean values for each number in the input * vector that identifies if value is finite (true) or infinite (false) @@ -240,7 +256,8 @@ export class Vector { } /** - * Checks if the vector is zero length + * Checks if the vector has zero length (all elements are zero). + * Example: [0,0,0] → true, [0,0,0.001] → false * @param inputs Vector to be checked * @returns Boolean that identifies if vector is zero length * @group validate @@ -252,7 +269,8 @@ export class Vector { } /** - * Finds in between vector between two vectors by providing a fracture + * Finds an interpolated vector between two vectors using a fraction (linear interpolation). + * Example: [0,0,0] to [10,10,10] at 0.5 → [5,5,5], fraction=0 → first, fraction=1 → second * @param inputs Information for finding vector between two vectors using a fraction * @returns Vector that is in between two vectors * @group distance @@ -269,7 +287,8 @@ export class Vector { } /** - * Finds the maximum value in the vector + * Finds the maximum (largest) value in the vector. + * Example: [3, 7, 2, 9, 1] → 9 * @param inputs Vector to be checked * @returns Largest number in the vector * @group extract @@ -281,7 +300,8 @@ export class Vector { } /** - * Finds the minimum value in the vector + * Finds the minimum (smallest) value in the vector. + * Example: [3, 7, 2, 9, 1] → 1 * @param inputs Vector to be checked * @returns Lowest number in the vector * @group extract @@ -293,7 +313,8 @@ export class Vector { } /** - * Multiple vector with the scalar + * Multiplies each element of the vector by a scalar value. + * Example: [2,3,4] × 5 → [10,15,20] * @param inputs Vector with a scalar * @returns Vector that results from multiplication * @group base @@ -309,7 +330,8 @@ export class Vector { } /** - * Negates the vector + * Negates the vector (flips the sign of each element). + * Example: [5,-3,2] → [-5,3,-2] * @param inputs Vector to negate * @returns Negative vector * @group base @@ -325,7 +347,8 @@ export class Vector { } /** - * Compute squared norm + * Computes the squared norm (squared magnitude/length) of the vector. + * Example: [3,4,0] → 25 (length 5 squared) * @param inputs Vector for squared norm * @returns Number that is squared norm * @group base @@ -337,7 +360,8 @@ export class Vector { } /** - * Norm of the vector + * Calculates the norm (magnitude/length) of the vector. + * Example: [3,4,0] → 5, [1,0,0] → 1 * @param inputs Vector to compute the norm * @returns Number that is norm of the vector * @group base @@ -350,7 +374,8 @@ export class Vector { } /** - * Normalize the vector into a unit vector, that has a length of 1 + * Normalizes the vector into a unit vector that has a length of 1 (maintains direction, scales magnitude to 1). + * Example: [3,4,0] → [0.6,0.8,0], [10,0,0] → [1,0,0] * @param inputs Vector to normalize * @returns Unit vector that has length of 1 * @group base @@ -366,7 +391,8 @@ export class Vector { } /** - * Finds a point coordinates on the given distance ray that spans between the point along the direction vector + * Finds a point on a ray at a given distance from the origin along the direction vector. + * Example: Point [0,0,0] + direction [1,0,0] at distance 5 → [5,0,0] * @param inputs Provide a point, vector and a distance for finding a point * @returns Vector representing point on the ray * @group base @@ -378,7 +404,8 @@ export class Vector { } /** - * Create a xyz vector + * Creates a 3D vector from x, y, z coordinates. + * Example: x=1, y=2, z=3 → [1,2,3] * @param inputs Vector coordinates * @returns Create a vector of xyz values * @group create @@ -390,7 +417,8 @@ export class Vector { } /** - * Create 2d xy vector + * Creates a 2D vector from x, y coordinates. + * Example: x=3, y=4 → [3,4] * @param inputs Vector coordinates * @returns Create a vector of xy values * @group create @@ -402,7 +430,8 @@ export class Vector { } /** - * Creates a vector of integers between 0 and maximum ceiling integer + * Creates a vector of integers from 0 to max (exclusive). + * Example: max=5 → [0,1,2,3,4], max=3 → [0,1,2] * @param inputs Max value for the range * @returns Vector containing items from 0 to max * @group create @@ -418,7 +447,8 @@ export class Vector { } /** - * Computes signed angle between two vectors and a reference. This will always return a smaller angle between two possible angles. + * Computes signed angle between two vectors using a reference vector (determines rotation direction). + * Example: Returns positive or negative angle depending on rotation direction relative to reference * @param inputs Contains information of two vectors and a reference vector * @returns Signed angle in degrees * @group angles @@ -440,7 +470,8 @@ export class Vector { } /** - * Creates a vector that contains numbers spanning between minimum and maximum values at a given step + * Creates a vector containing numbers from min to max at a given step increment. + * Example: min=0, max=10, step=2 → [0,2,4,6,8,10] * @param inputs Span information containing min, max and step values * @returns Vector containing number between min, max and increasing at a given step * @group create @@ -456,7 +487,8 @@ export class Vector { } /** - * Creates a vector that contains numbers spanning between minimum and maximum values at a given ease function + * Creates a vector with numbers from min to max using an easing function for non-linear distribution. + * Example: min=0, max=100, nrItems=5, ease='easeInQuad' → creates accelerating intervals * @param inputs Span information containing min, max and ease function * @returns Vector containing numbers between min, max and increasing in non-linear steps defined by nr of items in the vector and type * @group create @@ -476,7 +508,8 @@ export class Vector { } /** - * Creates a vector that contains numbers spanning between minimum and maximum values by giving nr of items + * Creates a vector with evenly spaced numbers from min to max with a specified number of items. + * Example: min=0, max=10, nrItems=5 → [0, 2.5, 5, 7.5, 10] * @param inputs Span information containing min, max and step values * @returns Vector containing number between min, max by giving nr of items * @group create @@ -494,7 +527,8 @@ export class Vector { } /** - * Subtract two vectors + * Subtracts the second vector from the first element-wise. + * Example: [10,20,30] - [1,2,3] → [9,18,27] * @param inputs Two vectors * @returns Vector that result by subtraction two vectors * @group base @@ -510,7 +544,8 @@ export class Vector { } /** - * Sums the values of the vector + * Sums all values in the vector and returns a single number. + * Example: [1,2,3,4] → 10, [5,10,15] → 30 * @param inputs Vector to sum * @returns Number that results by adding up all values in the vector * @group base @@ -522,7 +557,8 @@ export class Vector { } /** - * Computes the squared length of the vector + * Computes the squared length (squared magnitude) of a 3D vector. + * Example: [3,4,0] → 25 (length 5 squared) * @param inputs Vector to compute the length * @returns Number that is squared length of the vector * @group base @@ -535,7 +571,8 @@ export class Vector { } /** - * Computes the length of the vector + * Computes the length (magnitude) of a 3D vector. + * Example: [3,4,0] → 5, [1,0,0] → 1 * @param inputs Vector to compute the length * @returns Number that is length of the vector * @group base From 9a75af68f89159d1f4cec7a585e0bc481d6d6ae8 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 22 Dec 2025 10:41:23 +0200 Subject: [PATCH 28/38] removed double quotes from comments --- packages/dev/base/lib/api/services/color.ts | 24 ++++++++++----------- packages/dev/base/lib/api/services/dates.ts | 16 +++++++------- packages/dev/base/lib/api/services/lists.ts | 4 ++-- packages/dev/base/lib/api/services/logic.ts | 2 +- packages/dev/base/lib/api/services/math.ts | 2 +- packages/dev/base/lib/api/services/text.ts | 14 ++++++------ 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/dev/base/lib/api/services/color.ts b/packages/dev/base/lib/api/services/color.ts index 506d559e..494c07c1 100644 --- a/packages/dev/base/lib/api/services/color.ts +++ b/packages/dev/base/lib/api/services/color.ts @@ -8,7 +8,7 @@ export class Color { /** * Creates and returns a hex color string (pass-through for color input). - * Example: "#FF5733" → "#FF5733" + * Example: '#FF5733' → '#FF5733' * @param inputs Color hex * @returns color string * @group create @@ -21,7 +21,7 @@ export class Color { /** * Converts hex color to RGB object with r, g, b values (0-255 range). - * Example: "#FF5733" → {r: 255, g: 87, b: 51} + * Example: '#FF5733' → {r: 255, g: 87, b: 51} * @param inputs Color hex * @returns rgb color * @group convert @@ -39,8 +39,8 @@ export class Color { /** * Converts RGB values to hex color string (supports custom min/max ranges, auto-remaps to 0-255). - * Example: r=255, g=87, b=51 with range [0,255] → "#ff5733" - * Example: r=1, g=0.5, b=0.2 with range [0,1] → "#ff7f33" + * Example: r=255, g=87, b=51 with range [0,255] → '#ff5733' + * Example: r=1, g=0.5, b=0.2 with range [0,1] → '#ff7f33' * @param inputs Color hext * @returns hex color * @group convert @@ -68,7 +68,7 @@ export class Color { /** * Converts RGB object to hex color string (supports custom min/max ranges). - * Example: {r: 1, g: 0.5, b: 0.2} with range [0,1] → "#ff7f33" + * Example: {r: 1, g: 0.5, b: 0.2} with range [0,1] → '#ff7f33' * @param inputs Color hext * @returns hex color string * @group convert @@ -81,8 +81,8 @@ export class Color { /** * Converts hex color to RGB and remaps values to a custom range. - * Example: "#FF5733" mapped to [0,1] → {r: 1, g: 0.341, b: 0.2} - * Example: "#FF5733" mapped to [0,100] → {r: 100, g: 34.1, b: 20} + * Example: '#FF5733' mapped to [0,1] → {r: 1, g: 0.341, b: 0.2} + * Example: '#FF5733' mapped to [0,100] → {r: 100, g: 34.1, b: 20} * @param inputs Color hext * @returns rgb color * @group convert @@ -100,7 +100,7 @@ export class Color { /** * Extracts the red channel value from hex color (can be mapped to custom range). - * Example: "#FF5733" with range [0,1] → 1 + * Example: '#FF5733' with range [0,1] → 1 * @param inputs Color hext * @returns rgb color * @group hex to @@ -114,7 +114,7 @@ export class Color { /** * Extracts the green channel value from hex color (can be mapped to custom range). - * Example: "#FF5733" with range [0,1] → 0.341 + * Example: '#FF5733' with range [0,1] → 0.341 * @param inputs Color hext * @returns rgb color * @group hex to @@ -128,7 +128,7 @@ export class Color { /** * Extracts the blue channel value from hex color (can be mapped to custom range). - * Example: "#FF5733" with range [0,1] → 0.2 + * Example: '#FF5733' with range [0,1] → 0.2 * @param inputs Color hext * @returns blue param * @group hex to @@ -181,8 +181,8 @@ export class Color { /** * Inverts a hex color (flips RGB channels: 255-r, 255-g, 255-b). - * With blackAndWhite=true → returns "#000000" or "#ffffff" based on brightness. - * Example: "#FF5733" → "#00a8cc", "#FF5733" with blackAndWhite=true → "#ffffff" + * With blackAndWhite=true → returns '#000000' or '#ffffff' based on brightness. + * Example: '#FF5733' → '#00a8cc', '#FF5733' with blackAndWhite=true → '#ffffff' * @param inputs hex color and black and white option * @returns inverted color * @group hex to diff --git a/packages/dev/base/lib/api/services/dates.ts b/packages/dev/base/lib/api/services/dates.ts index c654f742..2f1b98ea 100644 --- a/packages/dev/base/lib/api/services/dates.ts +++ b/packages/dev/base/lib/api/services/dates.ts @@ -7,7 +7,7 @@ export class Dates { /** * Converts date to human-readable date string (excludes time). - * Example: Date(2024,0,15,14,30) → "Mon Jan 15 2024" + * Example: Date(2024,0,15,14,30) → 'Mon Jan 15 2024' * @param inputs a date * @returns date as string * @group convert @@ -20,7 +20,7 @@ export class Dates { /** * Converts date to ISO 8601 format string (standard format for APIs and data interchange). - * Example: Date(2024,0,15,14,30,45) → "2024-01-15T14:30:45.000Z" + * Example: Date(2024,0,15,14,30,45) → '2024-01-15T14:30:45.000Z' * @param inputs a date * @returns date as string * @group convert @@ -33,7 +33,7 @@ export class Dates { /** * Converts date to JSON-compatible string (same as ISO format, used in JSON.stringify). - * Example: Date(2024,0,15,14,30) → "2024-01-15T14:30:00.000Z" + * Example: Date(2024,0,15,14,30) → '2024-01-15T14:30:00.000Z' * @param inputs a date * @returns date as string * @group convert @@ -46,7 +46,7 @@ export class Dates { /** * Converts date to full locale-specific string (includes date, time, and timezone). - * Example: Date(2024,0,15,14,30) → "Mon Jan 15 2024 14:30:00 GMT+0000" + * Example: Date(2024,0,15,14,30) → 'Mon Jan 15 2024 14:30:00 GMT+0000' * @param inputs a date * @returns date as string * @group convert @@ -59,7 +59,7 @@ export class Dates { /** * Converts date to time string (excludes date, includes timezone). - * Example: Date(2024,0,15,14,30,45) → "14:30:45 GMT+0000" + * Example: Date(2024,0,15,14,30,45) → '14:30:45 GMT+0000' * @param inputs a date * @returns time as string * @group convert @@ -72,7 +72,7 @@ export class Dates { /** * Converts date to UTC string format (Universal Coordinated Time, no timezone offset). - * Example: Date(2024,0,15,14,30) → "Mon, 15 Jan 2024 14:30:00 GMT" + * Example: Date(2024,0,15,14,30) → 'Mon, 15 Jan 2024 14:30:00 GMT' * @param inputs a date * @returns date as utc string * @group convert @@ -85,7 +85,7 @@ export class Dates { /** * Returns the current date and time at the moment of execution. - * Example: calling now() → Date object representing current moment (e.g., "2024-01-15T14:30:45") + * Example: calling now() → Date object representing current moment (e.g., '2024-01-15T14:30:45') * @returns date * @group create * @shortname now @@ -138,7 +138,7 @@ export class Dates { /** * Parses a date string and returns Unix timestamp (milliseconds since Jan 1, 1970 UTC). - * Example: dateString="2024-01-15" → 1705276800000 + * Example: dateString='2024-01-15' → 1705276800000 * @param inputs a date string * @returns the number of milliseconds between that date and midnight, January 1, 1970. * @group parse diff --git a/packages/dev/base/lib/api/services/lists.ts b/packages/dev/base/lib/api/services/lists.ts index ff93c202..a5d7f002 100644 --- a/packages/dev/base/lib/api/services/lists.ts +++ b/packages/dev/base/lib/api/services/lists.ts @@ -710,7 +710,7 @@ export class Lists { /** * Removes duplicate items from the list using strict equality comparison (works with any type). - * Example: From ["a", "b", "c", "a", "d", "b"], returns ["a", "b", "c", "d"] + * Example: From ['a', 'b', 'c', 'a', 'd', 'b'], returns ['a', 'b', 'c', 'd'] * @param inputs a list * @returns list with unique items * @group remove @@ -887,7 +887,7 @@ export class Lists { /** * Sorts text strings alphabetically in ascending (A to Z) or descending (Z to A) order. - * Example: ["dog", "apple", "cat", "banana"] ascending returns ["apple", "banana", "cat", "dog"] + * Example: ['dog', 'apple', 'cat', 'banana'] ascending returns ['apple', 'banana', 'cat', 'dog'] * @param inputs a list of texts to sort and an option for ascending or descending order * @returns list * @group sorting diff --git a/packages/dev/base/lib/api/services/logic.ts b/packages/dev/base/lib/api/services/logic.ts index 49ae7c20..654b1add 100644 --- a/packages/dev/base/lib/api/services/logic.ts +++ b/packages/dev/base/lib/api/services/logic.ts @@ -155,7 +155,7 @@ export class Logic { /** * Compares two values using various operators (==, !=, ===, !==, <, <=, >, >=). - * Example: 5 > 3 → true, "hello" === "world" → false + * Example: 5 > 3 → true, 'hello' === 'world' → false * @param inputs two values to be compared * @returns Result of the comparison * @group operations diff --git a/packages/dev/base/lib/api/services/math.ts b/packages/dev/base/lib/api/services/math.ts index 2283bf6b..e649ec17 100644 --- a/packages/dev/base/lib/api/services/math.ts +++ b/packages/dev/base/lib/api/services/math.ts @@ -238,7 +238,7 @@ export class MathBitByBit { /** * Formats a number as a string with a fixed number of decimal places (always shows trailing zeros). - * Example: 3.14159 with 2 decimals → "3.14", 5 with 3 decimals → "5.000" + * Example: 3.14159 with 2 decimals → '3.14', 5 with 3 decimals → '5.000' * @param inputs a number to be rounded to decimal places * @returns number * @group operations diff --git a/packages/dev/base/lib/api/services/text.ts b/packages/dev/base/lib/api/services/text.ts index 6c6ea7a6..fbeecd0f 100644 --- a/packages/dev/base/lib/api/services/text.ts +++ b/packages/dev/base/lib/api/services/text.ts @@ -12,7 +12,7 @@ export class TextBitByBit { /** * Creates and returns a text string (pass-through for text input). - * Example: text="Hello World" → "Hello World" + * Example: text='Hello World' → 'Hello World' * @param inputs a text * @returns text * @group create @@ -25,7 +25,7 @@ export class TextBitByBit { /** * Splits text into multiple pieces using a separator string. - * Example: text="apple,banana,cherry", separator="," → ["apple", "banana", "cherry"] + * Example: text='apple,banana,cherry', separator=',' → ['apple', 'banana', 'cherry'] * @param inputs a text * @returns text * @group transform @@ -38,7 +38,7 @@ export class TextBitByBit { /** * Replaces all occurrences of a search string with a replacement string. - * Example: text="hello hello", search="hello", replaceWith="hi" → "hi hi" + * Example: text='hello hello', search='hello', replaceWith='hi' → 'hi hi' * @param inputs a text * @returns text * @group transform @@ -51,7 +51,7 @@ export class TextBitByBit { /** * Joins multiple items into a single text string using a separator. - * Example: list=["apple", "banana", "cherry"], separator=", " → "apple, banana, cherry" + * Example: list=['apple', 'banana', 'cherry'], separator=', ' → 'apple, banana, cherry' * @param inputs a list of items * @returns text * @group transform @@ -88,7 +88,7 @@ export class TextBitByBit { /** * Formats text with placeholder values using {0}, {1}, etc. syntax. - * Example: text="Point: ({0}, {1})", values=[10, 5] → "Point: (10, 5)" + * Example: text='Point: ({0}, {1})', values=[10, 5] → 'Point: (10, 5)' * @param inputs a text and values * @returns formatted text * @group transform @@ -104,7 +104,7 @@ export class TextBitByBit { /** * Converts a character to vector paths (polylines) with width and height data for rendering. * Uses simplex stroke font to generate 2D line segments representing the character shape. - * Example: char="A", height=10 → {width:8, height:10, paths:[[points forming A shape]]} + * Example: char='A', height=10 → {width:8, height:10, paths:[[points forming A shape]]} * @param inputs a text * @returns width, height and segments as json * @group vector @@ -145,7 +145,7 @@ export class TextBitByBit { /** * Converts multi-line text to vector paths (polylines) with alignment and spacing controls. * Supports line breaks, letter spacing, line spacing, horizontal alignment, and origin centering. - * Example: text="Hello\nWorld", height=10, align=center → [{line1 chars}, {line2 chars}] + * Example: text='Hello\nWorld', height=10, align=center → [{line1 chars}, {line2 chars}] * @param inputs a text as string * @returns segments * @group vector From cef48c6fd47edd3524390d3a194b978d907709e9 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 22 Dec 2025 10:50:56 +0200 Subject: [PATCH 29/38] new helper methods for text, some fundamentals that were not there like includes, regex, trim, etc. --- .../dev/base/lib/api/inputs/text-inputs.ts | 176 +++++++++ .../dev/base/lib/api/services/text.test.ts | 162 ++++++++ packages/dev/base/lib/api/services/text.ts | 359 ++++++++++++++++++ 3 files changed, 697 insertions(+) diff --git a/packages/dev/base/lib/api/inputs/text-inputs.ts b/packages/dev/base/lib/api/inputs/text-inputs.ts index 515d32c6..f6b964b7 100644 --- a/packages/dev/base/lib/api/inputs/text-inputs.ts +++ b/packages/dev/base/lib/api/inputs/text-inputs.ts @@ -107,6 +107,182 @@ export namespace Text { */ values = ["World"]; } + + export class TextSearchDto { + constructor(text?: string, search?: string) { + if (text !== undefined) { this.text = text; } + if (search !== undefined) { this.search = search; } + } + /** + * Text to search in + * @default hello world + */ + text = "hello world"; + /** + * Text to search for + * @default world + */ + search = "world"; + } + + export class TextSubstringDto { + constructor(text?: string, start?: number, end?: number) { + if (text !== undefined) { this.text = text; } + if (start !== undefined) { this.start = start; } + if (end !== undefined) { this.end = end; } + } + /** + * Text to extract from + * @default hello world + */ + text = "hello world"; + /** + * Start index + * @default 0 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + start = 0; + /** + * End index + * @default 5 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + end?: number = 5; + } + + export class TextIndexDto { + constructor(text?: string, index?: number) { + if (text !== undefined) { this.text = text; } + if (index !== undefined) { this.index = index; } + } + /** + * Text to get character from + * @default hello + */ + text = "hello"; + /** + * Index of character + * @default 0 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + index = 0; + } + + export class TextPadDto { + constructor(text?: string, length?: number, padString?: string) { + if (text !== undefined) { this.text = text; } + if (length !== undefined) { this.length = length; } + if (padString !== undefined) { this.padString = padString; } + } + /** + * Text to pad + * @default 5 + */ + text = "5"; + /** + * Target length + * @default 3 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + length = 3; + /** + * String to pad with + * @default 0 + */ + padString = "0"; + } + + export class TextRepeatDto { + constructor(text?: string, count?: number) { + if (text !== undefined) { this.text = text; } + if (count !== undefined) { this.count = count; } + } + /** + * Text to repeat + * @default ha + */ + text = "ha"; + /** + * Number of repetitions + * @default 3 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + count = 3; + } + + export class TextConcatDto { + constructor(texts?: string[]) { + if (texts !== undefined) { this.texts = texts; } + } + /** + * Texts to concatenate + * @default ["hello", " ", "world"] + */ + texts = ["hello", " ", "world"]; + } + + export class TextRegexDto { + constructor(text?: string, pattern?: string, flags?: string) { + if (text !== undefined) { this.text = text; } + if (pattern !== undefined) { this.pattern = pattern; } + if (flags !== undefined) { this.flags = flags; } + } + /** + * Text to search in + * @default hello123world + */ + text = "hello123world"; + /** + * Regular expression pattern + * @default [0-9]+ + */ + pattern = "[0-9]+"; + /** + * Regular expression flags (g, i, m, s, u, y) + * @default g + */ + flags = "g"; + } + + export class TextRegexReplaceDto { + constructor(text?: string, pattern?: string, flags?: string, replaceWith?: string) { + if (text !== undefined) { this.text = text; } + if (pattern !== undefined) { this.pattern = pattern; } + if (flags !== undefined) { this.flags = flags; } + if (replaceWith !== undefined) { this.replaceWith = replaceWith; } + } + /** + * Text to search in + * @default hello123world456 + */ + text = "hello123world456"; + /** + * Regular expression pattern + * @default [0-9]+ + */ + pattern = "[0-9]+"; + /** + * Regular expression flags (g, i, m, s, u, y) + * @default g + */ + flags = "g"; + /** + * Text to replace matches with + * @default X + */ + replaceWith = "X"; + } + export class VectorCharDto { constructor(char?: string, xOffset?: number, yOffset?: number, height?: number, extrudeOffset?: number) { if (char !== undefined) { this.char = char; } diff --git a/packages/dev/base/lib/api/services/text.test.ts b/packages/dev/base/lib/api/services/text.test.ts index f026acaa..2e06620b 100644 --- a/packages/dev/base/lib/api/services/text.test.ts +++ b/packages/dev/base/lib/api/services/text.test.ts @@ -129,6 +129,168 @@ describe("Text unit tests", () => { expect(result).toEqual("Hello World, Matas"); }); + describe("String Query Methods", () => { + it("should check if text includes search string", () => { + expect(text.includes({ text: "hello world", search: "world" })).toBe(true); + expect(text.includes({ text: "hello world", search: "foo" })).toBe(false); + }); + + it("should check if text starts with search string", () => { + expect(text.startsWith({ text: "hello world", search: "hello" })).toBe(true); + expect(text.startsWith({ text: "hello world", search: "world" })).toBe(false); + }); + + it("should check if text ends with search string", () => { + expect(text.endsWith({ text: "hello world", search: "world" })).toBe(true); + expect(text.endsWith({ text: "hello world", search: "hello" })).toBe(false); + }); + + it("should find index of first occurrence", () => { + expect(text.indexOf({ text: "hello world", search: "world" })).toBe(6); + expect(text.indexOf({ text: "hello world", search: "foo" })).toBe(-1); + }); + + it("should find index of last occurrence", () => { + expect(text.lastIndexOf({ text: "hello world hello", search: "hello" })).toBe(12); + expect(text.lastIndexOf({ text: "hello world", search: "foo" })).toBe(-1); + }); + + it("should get character at index", () => { + expect(text.charAt({ text: "hello", index: 1 })).toBe("e"); + expect(text.charAt({ text: "hello", index: 0 })).toBe("h"); + expect(text.charAt({ text: "hello", index: 10 })).toBe(""); + }); + + it("should return text length", () => { + expect(text.length({ text: "hello" })).toBe(5); + expect(text.length({ text: "" })).toBe(0); + expect(text.length({ text: "hello world" })).toBe(11); + }); + + it("should check if text is empty", () => { + expect(text.isEmpty({ text: "" })).toBe(true); + expect(text.isEmpty({ text: " " })).toBe(true); + expect(text.isEmpty({ text: "hello" })).toBe(false); + expect(text.isEmpty({ text: " hello " })).toBe(false); + }); + }); + + describe("String Transformation Methods", () => { + it("should extract substring", () => { + expect(text.substring({ text: "hello world", start: 0, end: 5 })).toBe("hello"); + expect(text.substring({ text: "hello world", start: 6, end: 11 })).toBe("world"); + expect(text.substring({ text: "hello world", start: 6 })).toBe("world"); + }); + + it("should slice text", () => { + expect(text.slice({ text: "hello world", start: 0, end: 5 })).toBe("hello"); + expect(text.slice({ text: "hello world", start: -5 })).toBe("world"); + expect(text.slice({ text: "hello world", start: 6 })).toBe("world"); + }); + + it("should trim whitespace", () => { + expect(text.trim({ text: " hello " })).toBe("hello"); + expect(text.trim({ text: "hello" })).toBe("hello"); + }); + + it("should trim start whitespace", () => { + expect(text.trimStart({ text: " hello " })).toBe("hello "); + expect(text.trimStart({ text: "hello" })).toBe("hello"); + }); + + it("should trim end whitespace", () => { + expect(text.trimEnd({ text: " hello " })).toBe(" hello"); + expect(text.trimEnd({ text: "hello" })).toBe("hello"); + }); + + it("should pad start", () => { + expect(text.padStart({ text: "5", length: 3, padString: "0" })).toBe("005"); + expect(text.padStart({ text: "hello", length: 10, padString: "*" })).toBe("*****hello"); + expect(text.padStart({ text: "hello", length: 3, padString: "*" })).toBe("hello"); + }); + + it("should pad end", () => { + expect(text.padEnd({ text: "5", length: 3, padString: "0" })).toBe("500"); + expect(text.padEnd({ text: "hello", length: 10, padString: "*" })).toBe("hello*****"); + expect(text.padEnd({ text: "hello", length: 3, padString: "*" })).toBe("hello"); + }); + + it("should convert to uppercase", () => { + expect(text.toUpperCase({ text: "hello" })).toBe("HELLO"); + expect(text.toUpperCase({ text: "Hello World" })).toBe("HELLO WORLD"); + }); + + it("should convert to lowercase", () => { + expect(text.toLowerCase({ text: "HELLO" })).toBe("hello"); + expect(text.toLowerCase({ text: "Hello World" })).toBe("hello world"); + }); + + it("should capitalize first character", () => { + expect(text.toUpperCaseFirst({ text: "hello world" })).toBe("Hello world"); + expect(text.toUpperCaseFirst({ text: "HELLO" })).toBe("HELLO"); + expect(text.toUpperCaseFirst({ text: "" })).toBe(""); + }); + + it("should lowercase first character", () => { + expect(text.toLowerCaseFirst({ text: "Hello World" })).toBe("hello World"); + expect(text.toLowerCaseFirst({ text: "hello" })).toBe("hello"); + expect(text.toLowerCaseFirst({ text: "" })).toBe(""); + }); + + it("should repeat text", () => { + expect(text.repeat({ text: "ha", count: 3 })).toBe("hahaha"); + expect(text.repeat({ text: "x", count: 0 })).toBe(""); + expect(text.repeat({ text: "abc", count: 2 })).toBe("abcabc"); + }); + + it("should reverse text", () => { + expect(text.reverse({ text: "hello" })).toBe("olleh"); + expect(text.reverse({ text: "12345" })).toBe("54321"); + expect(text.reverse({ text: "" })).toBe(""); + }); + + it("should concatenate texts", () => { + expect(text.concat({ texts: ["hello", " ", "world"] })).toBe("hello world"); + expect(text.concat({ texts: ["a", "b", "c"] })).toBe("abc"); + expect(text.concat({ texts: [] })).toBe(""); + }); + }); + + describe("Regex Methods", () => { + it("should test regex pattern", () => { + expect(text.regexTest({ text: "hello123", pattern: "[0-9]+", flags: "" })).toBe(true); + expect(text.regexTest({ text: "hello", pattern: "[0-9]+", flags: "" })).toBe(false); + expect(text.regexTest({ text: "HELLO", pattern: "hello", flags: "i" })).toBe(true); + }); + + it("should match regex pattern", () => { + const result = text.regexMatch({ text: "hello123world456", pattern: "[0-9]+", flags: "g" }); + expect(result).toEqual(["123", "456"]); + + const singleResult = text.regexMatch({ text: "hello123", pattern: "[0-9]+", flags: "" }); + expect(singleResult).toEqual(["123"]); + + const noMatch = text.regexMatch({ text: "hello", pattern: "[0-9]+", flags: "" }); + expect(noMatch).toBeNull(); + }); + + it("should replace using regex", () => { + expect(text.regexReplace({ text: "hello123world456", pattern: "[0-9]+", flags: "g", replaceWith: "X" })).toBe("helloXworldX"); + expect(text.regexReplace({ text: "Hello World", pattern: "hello", flags: "i", replaceWith: "Hi" })).toBe("Hi World"); + expect(text.regexReplace({ text: "hello", pattern: "[0-9]+", flags: "g", replaceWith: "X" })).toBe("hello"); + }); + + it("should search using regex", () => { + expect(text.regexSearch({ text: "hello123", pattern: "[0-9]+", flags: "" })).toBe(5); + expect(text.regexSearch({ text: "hello", pattern: "[0-9]+", flags: "" })).toBe(-1); + }); + + it("should split using regex", () => { + expect(text.regexSplit({ text: "a1b2c3", pattern: "[0-9]+", flags: "" })).toEqual(["a", "b", "c", ""]); + expect(text.regexSplit({ text: "hello, world; test", pattern: "[,;]\\s*", flags: "" })).toEqual(["hello", "world", "test"]); + }); + }); + describe("vectorChar", () => { diff --git a/packages/dev/base/lib/api/services/text.ts b/packages/dev/base/lib/api/services/text.ts index fbeecd0f..626ac9c3 100644 --- a/packages/dev/base/lib/api/services/text.ts +++ b/packages/dev/base/lib/api/services/text.ts @@ -101,6 +101,365 @@ export class TextBitByBit { }); } + /** + * Checks if text contains a search string. + * Example: text='hello world', search='world' → true + * @param inputs a text and search string + * @returns boolean + * @group query + * @shortname includes + * @drawable false + */ + includes(inputs: Inputs.Text.TextSearchDto): boolean { + return inputs.text.includes(inputs.search); + } + + /** + * Checks if text starts with a search string. + * Example: text='hello world', search='hello' → true + * @param inputs a text and search string + * @returns boolean + * @group query + * @shortname starts with + * @drawable false + */ + startsWith(inputs: Inputs.Text.TextSearchDto): boolean { + return inputs.text.startsWith(inputs.search); + } + + /** + * Checks if text ends with a search string. + * Example: text='hello world', search='world' → true + * @param inputs a text and search string + * @returns boolean + * @group query + * @shortname ends with + * @drawable false + */ + endsWith(inputs: Inputs.Text.TextSearchDto): boolean { + return inputs.text.endsWith(inputs.search); + } + + /** + * Returns the index of the first occurrence of a search string. + * Example: text='hello world', search='world' → 6 + * @param inputs a text and search string + * @returns index or -1 if not found + * @group query + * @shortname index of + * @drawable false + */ + indexOf(inputs: Inputs.Text.TextSearchDto): number { + return inputs.text.indexOf(inputs.search); + } + + /** + * Returns the index of the last occurrence of a search string. + * Example: text='hello world hello', search='hello' → 12 + * @param inputs a text and search string + * @returns index or -1 if not found + * @group query + * @shortname last index of + * @drawable false + */ + lastIndexOf(inputs: Inputs.Text.TextSearchDto): number { + return inputs.text.lastIndexOf(inputs.search); + } + + /** + * Extracts a section of text between two indices. + * Example: text='hello world', start=0, end=5 → 'hello' + * @param inputs a text, start and end indices + * @returns extracted text + * @group transform + * @shortname substring + * @drawable false + */ + substring(inputs: Inputs.Text.TextSubstringDto): string { + return inputs.text.substring(inputs.start, inputs.end); + } + + /** + * Extracts a section of text and returns a new string. + * Example: text='hello world', start=0, end=5 → 'hello' + * @param inputs a text, start and end indices + * @returns extracted text + * @group transform + * @shortname slice + * @drawable false + */ + slice(inputs: Inputs.Text.TextSubstringDto): string { + return inputs.text.slice(inputs.start, inputs.end); + } + + /** + * Returns the character at the specified index. + * Example: text='hello', index=1 → 'e' + * @param inputs a text and index + * @returns character + * @group query + * @shortname char at + * @drawable false + */ + charAt(inputs: Inputs.Text.TextIndexDto): string { + return inputs.text.charAt(inputs.index); + } + + /** + * Removes whitespace from both ends of text. + * Example: text=' hello ' → 'hello' + * @param inputs a text + * @returns trimmed text + * @group transform + * @shortname trim + * @drawable false + */ + trim(inputs: Inputs.Text.TextDto): string { + return inputs.text.trim(); + } + + /** + * Removes whitespace from the start of text. + * Example: text=' hello ' → 'hello ' + * @param inputs a text + * @returns trimmed text + * @group transform + * @shortname trim start + * @drawable false + */ + trimStart(inputs: Inputs.Text.TextDto): string { + return inputs.text.trimStart(); + } + + /** + * Removes whitespace from the end of text. + * Example: text=' hello ' → ' hello' + * @param inputs a text + * @returns trimmed text + * @group transform + * @shortname trim end + * @drawable false + */ + trimEnd(inputs: Inputs.Text.TextDto): string { + return inputs.text.trimEnd(); + } + + /** + * Pads text from the start to reach target length. + * Example: text='5', length=3, padString='0' → '005' + * @param inputs a text, target length and pad string + * @returns padded text + * @group transform + * @shortname pad start + * @drawable false + */ + padStart(inputs: Inputs.Text.TextPadDto): string { + return inputs.text.padStart(inputs.length, inputs.padString); + } + + /** + * Pads text from the end to reach target length. + * Example: text='5', length=3, padString='0' → '500' + * @param inputs a text, target length and pad string + * @returns padded text + * @group transform + * @shortname pad end + * @drawable false + */ + padEnd(inputs: Inputs.Text.TextPadDto): string { + return inputs.text.padEnd(inputs.length, inputs.padString); + } + + /** + * Converts text to uppercase. + * Example: text='hello' → 'HELLO' + * @param inputs a text + * @returns uppercase text + * @group transform + * @shortname to upper case + * @drawable false + */ + toUpperCase(inputs: Inputs.Text.TextDto): string { + return inputs.text.toUpperCase(); + } + + /** + * Converts text to lowercase. + * Example: text='HELLO' → 'hello' + * @param inputs a text + * @returns lowercase text + * @group transform + * @shortname to lower case + * @drawable false + */ + toLowerCase(inputs: Inputs.Text.TextDto): string { + return inputs.text.toLowerCase(); + } + + /** + * Capitalizes the first character of text. + * Example: text='hello world' → 'Hello world' + * @param inputs a text + * @returns text with first character uppercase + * @group transform + * @shortname capitalize first + * @drawable false + */ + toUpperCaseFirst(inputs: Inputs.Text.TextDto): string { + if (!inputs.text) return inputs.text; + return inputs.text.charAt(0).toUpperCase() + inputs.text.slice(1); + } + + /** + * Lowercases the first character of text. + * Example: text='Hello World' → 'hello World' + * @param inputs a text + * @returns text with first character lowercase + * @group transform + * @shortname uncapitalize first + * @drawable false + */ + toLowerCaseFirst(inputs: Inputs.Text.TextDto): string { + if (!inputs.text) return inputs.text; + return inputs.text.charAt(0).toLowerCase() + inputs.text.slice(1); + } + + /** + * Repeats text a specified number of times. + * Example: text='ha', count=3 → 'hahaha' + * @param inputs a text and count + * @returns repeated text + * @group transform + * @shortname repeat + * @drawable false + */ + repeat(inputs: Inputs.Text.TextRepeatDto): string { + return inputs.text.repeat(inputs.count); + } + + /** + * Reverses the characters in text. + * Example: text='hello' → 'olleh' + * @param inputs a text + * @returns reversed text + * @group transform + * @shortname reverse + * @drawable false + */ + reverse(inputs: Inputs.Text.TextDto): string { + return inputs.text.split("").reverse().join(""); + } + + /** + * Returns the length of text. + * Example: text='hello' → 5 + * @param inputs a text + * @returns length + * @group query + * @shortname length + * @drawable false + */ + length(inputs: Inputs.Text.TextDto): number { + return inputs.text.length; + } + + /** + * Checks if text is empty or only whitespace. + * Example: text=' ' → true + * @param inputs a text + * @returns boolean + * @group query + * @shortname is empty + * @drawable false + */ + isEmpty(inputs: Inputs.Text.TextDto): boolean { + return !inputs.text || inputs.text.trim().length === 0; + } + + /** + * Concatenates multiple text strings. + * Example: texts=['hello', ' ', 'world'] → 'hello world' + * @param inputs array of texts + * @returns concatenated text + * @group transform + * @shortname concat + * @drawable false + */ + concat(inputs: Inputs.Text.TextConcatDto): string { + return inputs.texts.join(""); + } + + /** + * Tests if text matches a regular expression pattern. + * Example: text='hello123', pattern='[0-9]+' → true + * @param inputs a text and regex pattern + * @returns boolean + * @group regex + * @shortname test regex + * @drawable false + */ + regexTest(inputs: Inputs.Text.TextRegexDto): boolean { + const regex = new RegExp(inputs.pattern, inputs.flags); + return regex.test(inputs.text); + } + + /** + * Matches text against a regular expression and returns matches. + * Example: text='hello123world456', pattern='[0-9]+', flags='g' → ['123', '456'] + * @param inputs a text and regex pattern + * @returns array of matches or null + * @group regex + * @shortname regex match + * @drawable false + */ + regexMatch(inputs: Inputs.Text.TextRegexDto): string[] | null { + const regex = new RegExp(inputs.pattern, inputs.flags); + const result = inputs.text.match(regex); + return result ? Array.from(result) : null; + } + + /** + * Replaces text matching a regular expression pattern. + * Example: text='hello123world456', pattern='[0-9]+', flags='g', replaceWith='X' → 'helloXworldX' + * @param inputs a text, regex pattern, and replacement + * @returns text with replacements + * @group regex + * @shortname regex replace + * @drawable false + */ + regexReplace(inputs: Inputs.Text.TextRegexReplaceDto): string { + const regex = new RegExp(inputs.pattern, inputs.flags); + return inputs.text.replace(regex, inputs.replaceWith); + } + + /** + * Searches text for a regular expression pattern and returns the index. + * Example: text='hello123', pattern='[0-9]+' → 5 + * @param inputs a text and regex pattern + * @returns index or -1 if not found + * @group regex + * @shortname regex search + * @drawable false + */ + regexSearch(inputs: Inputs.Text.TextRegexDto): number { + const regex = new RegExp(inputs.pattern, inputs.flags); + return inputs.text.search(regex); + } + + /** + * Splits text using a regular expression pattern. + * Example: text='a1b2c3', pattern='[0-9]+' → ['a', 'b', 'c'] + * @param inputs a text and regex pattern + * @returns array of split strings + * @group regex + * @shortname regex split + * @drawable false + */ + regexSplit(inputs: Inputs.Text.TextRegexDto): string[] { + const regex = new RegExp(inputs.pattern, inputs.flags); + return inputs.text.split(regex); + } + /** * Converts a character to vector paths (polylines) with width and height data for rendering. * Uses simplex stroke font to generate 2D line segments representing the character shape. From ae797456db7dd38c8e874b155f504a02823194a4 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 22 Dec 2025 11:12:40 +0200 Subject: [PATCH 30/38] fixed some default values --- packages/dev/base/lib/api/inputs/text-inputs.ts | 8 ++++---- packages/dev/base/lib/api/services/text.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/dev/base/lib/api/inputs/text-inputs.ts b/packages/dev/base/lib/api/inputs/text-inputs.ts index f6b964b7..ad01e35b 100644 --- a/packages/dev/base/lib/api/inputs/text-inputs.ts +++ b/packages/dev/base/lib/api/inputs/text-inputs.ts @@ -182,9 +182,9 @@ export namespace Text { } /** * Text to pad - * @default 5 + * @default x */ - text = "5"; + text = "x"; /** * Target length * @default 3 @@ -195,9 +195,9 @@ export namespace Text { length = 3; /** * String to pad with - * @default 0 + * @default a */ - padString = "0"; + padString = "a"; } export class TextRepeatDto { diff --git a/packages/dev/base/lib/api/services/text.ts b/packages/dev/base/lib/api/services/text.ts index 626ac9c3..cbf3903b 100644 --- a/packages/dev/base/lib/api/services/text.ts +++ b/packages/dev/base/lib/api/services/text.ts @@ -246,7 +246,7 @@ export class TextBitByBit { /** * Pads text from the start to reach target length. - * Example: text='5', length=3, padString='0' → '005' + * Example: text='x', length=3, padString='a' → 'aax' * @param inputs a text, target length and pad string * @returns padded text * @group transform @@ -259,7 +259,7 @@ export class TextBitByBit { /** * Pads text from the end to reach target length. - * Example: text='5', length=3, padString='0' → '500' + * Example: text='x', length=3, padString='a' → 'xaa' * @param inputs a text, target length and pad string * @returns padded text * @group transform From df3a7755351f83554cd8fa8f0a4ebcc768caf002 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 22 Dec 2025 14:11:43 +0200 Subject: [PATCH 31/38] New CSV parsing and generation methods. CSV to JSON and JSON to CSV. This will be helpful when building data driven 3D workflows. --- examples/node/express-app/bitbybit.ts | 4 +- .../dev/babylonjs/lib/api/bitbybit-base.ts | 3 + .../dev/base/lib/api/inputs/vector-inputs.ts | 10 + .../base/lib/api/services/helpers/csv/csv.ts | 6 - .../dev/base/lib/api/services/vector.test.ts | 41 + packages/dev/base/lib/api/services/vector.ts | 13 + .../dev/core/lib/api/bitbybit/csv.test.ts | 758 ++++++++++++++++++ packages/dev/core/lib/api/bitbybit/csv.ts | 378 +++++++++ packages/dev/core/lib/api/bitbybit/index.ts | 1 + .../dev/core/lib/api/inputs/csv-inputs.ts | 389 +++++++++ packages/dev/core/lib/api/inputs/inputs.ts | 1 + packages/dev/threejs/lib/api/bitbybit-base.ts | 3 + 12 files changed, 1600 insertions(+), 7 deletions(-) delete mode 100644 packages/dev/base/lib/api/services/helpers/csv/csv.ts create mode 100644 packages/dev/core/lib/api/bitbybit/csv.test.ts create mode 100644 packages/dev/core/lib/api/bitbybit/csv.ts create mode 100644 packages/dev/core/lib/api/inputs/csv-inputs.ts diff --git a/examples/node/express-app/bitbybit.ts b/examples/node/express-app/bitbybit.ts index 5fddaafa..d5e060a6 100644 --- a/examples/node/express-app/bitbybit.ts +++ b/examples/node/express-app/bitbybit.ts @@ -1,5 +1,5 @@ import { MathBitByBit, Logic, Lists, TextBitByBit, Vector, Point, Transforms, Color, GeometryHelper, Line, Polyline } from "@bitbybit-dev/base"; -import { JSONBitByBit, Verb } from "@bitbybit-dev/core"; +import { JSONBitByBit, CSVBitByBit, Verb } from "@bitbybit-dev/core"; import { Jscad } from "@bitbybit-dev/jscad"; import { ManifoldService } from "@bitbybit-dev/manifold"; import { OCCTService, OccHelper, VectorHelperService, ShapesHelperService } from "@bitbybit-dev/occt"; @@ -13,6 +13,7 @@ export class BitByBitBase { public logic: Logic; public lists: Lists; public json: JSONBitByBit; + public csv: CSVBitByBit; public vector: Vector; public point: Point; public line: Line; @@ -57,6 +58,7 @@ export class BitByBitBase { this.occt = new OCCTService(occ, occHelper); this.logic = new Logic(); this.json = new JSONBitByBit({ jsonpath: JSONPath } as any); + this.csv = new CSVBitByBit(); this.text = new TextBitByBit(this.point); } } diff --git a/packages/dev/babylonjs/lib/api/bitbybit-base.ts b/packages/dev/babylonjs/lib/api/bitbybit-base.ts index 0ce4158f..8bc7138a 100644 --- a/packages/dev/babylonjs/lib/api/bitbybit-base.ts +++ b/packages/dev/babylonjs/lib/api/bitbybit-base.ts @@ -8,6 +8,7 @@ import { OCCTW, Asset, JSONBitByBit, + CSVBitByBit, } from "@bitbybit-dev/core"; import { Vector, @@ -47,6 +48,7 @@ export class BitByBitBase { public logic: Logic; public lists: Lists; public json: JSONBitByBit; + public csv: CSVBitByBit; public vector: Vector; public babylon: Babylon; public point: Point; @@ -98,6 +100,7 @@ export class BitByBitBase { this.asset = new Asset(); this.logic = new Logic(); this.json = new JSONBitByBit(this.context); + this.csv = new CSVBitByBit(); this.text = new TextBitByBit(this.point); this.dates = new Dates(); this.mesh = new MeshBitByBit(this.vector, this.polyline); diff --git a/packages/dev/base/lib/api/inputs/vector-inputs.ts b/packages/dev/base/lib/api/inputs/vector-inputs.ts index 8207783d..616cee3a 100644 --- a/packages/dev/base/lib/api/inputs/vector-inputs.ts +++ b/packages/dev/base/lib/api/inputs/vector-inputs.ts @@ -106,6 +106,16 @@ export namespace Vector { */ vector: number[]; } + export class VectorStringDto { + constructor(vector?: string[]) { + if (vector !== undefined) { this.vector = vector; } + } + /** + * Vector array of stringified numbers + * @default undefined + */ + vector: string[]; + } export class Vector3Dto { constructor(vector?: Base.Vector3) { if (vector !== undefined) { this.vector = vector; } diff --git a/packages/dev/base/lib/api/services/helpers/csv/csv.ts b/packages/dev/base/lib/api/services/helpers/csv/csv.ts deleted file mode 100644 index 9514a5a6..00000000 --- a/packages/dev/base/lib/api/services/helpers/csv/csv.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as Inputs from "../../../inputs"; - -export class Csv { - - -} \ No newline at end of file diff --git a/packages/dev/base/lib/api/services/vector.test.ts b/packages/dev/base/lib/api/services/vector.test.ts index c4d76ed5..9e2153ad 100644 --- a/packages/dev/base/lib/api/services/vector.test.ts +++ b/packages/dev/base/lib/api/services/vector.test.ts @@ -356,5 +356,46 @@ describe("Vector unit tests", () => { const res = vector.sum({ vector: [1, 2, 3] }); expect(res).toEqual(6); }); + + it("should parse stringified numbers to numbers", () => { + const res = vector.parseNumbers({ vector: ["1", "2", "3"] }); + expect(res).toEqual([1, 2, 3]); + }); + + it("should parse stringified decimal numbers to numbers", () => { + const res = vector.parseNumbers({ vector: ["1.5", "2.75", "3.333"] }); + expect(res).toEqual([1.5, 2.75, 3.333]); + }); + + it("should parse stringified negative numbers to numbers", () => { + const res = vector.parseNumbers({ vector: ["-1", "-2.5", "3"] }); + expect(res).toEqual([-1, -2.5, 3]); + }); + + it("should parse stringified zero to number", () => { + const res = vector.parseNumbers({ vector: ["0", "0.0", "-0"] }); + expect(res).toEqual([0, 0, 0]); + }); + + it("should parse stringified large numbers to numbers", () => { + const res = vector.parseNumbers({ vector: ["1000000", "1e6", "1.5e3"] }); + expect(res).toEqual([1000000, 1000000, 1500]); + }); + + it("should handle empty array when parsing numbers", () => { + const res = vector.parseNumbers({ vector: [] }); + expect(res).toEqual([]); + }); + + it("should return NaN for invalid stringified numbers", () => { + const res = vector.parseNumbers({ vector: ["abc", "xyz"] }); + expect(res[0]).toBeNaN(); + expect(res[1]).toBeNaN(); + }); + + it("should parse mixed valid and whitespace stringified numbers", () => { + const res = vector.parseNumbers({ vector: [" 1 ", " 2.5", "3 "] }); + expect(res).toEqual([1, 2.5, 3]); + }); }); diff --git a/packages/dev/base/lib/api/services/vector.ts b/packages/dev/base/lib/api/services/vector.ts index 17463b58..512a7193 100644 --- a/packages/dev/base/lib/api/services/vector.ts +++ b/packages/dev/base/lib/api/services/vector.ts @@ -582,4 +582,17 @@ export class Vector { length(inputs: Inputs.Vector.Vector3Dto): number { return Math.sqrt(this.lengthSq(inputs)); } + + /** + * Converts an array of stringified numbers to actual numbers. + * Example: ['1', '2.5', '3'] → [1, 2.5, 3], ['10', '-5', '0.1'] → [10, -5, 0.1] + * @param inputs Array of stringified numbers + * @returns Array of numbers + * @group create + * @shortname parse numbers + * @drawable false + */ + parseNumbers(inputs: Inputs.Vector.VectorStringDto): number[] { + return inputs.vector.map(v => parseFloat(v)); + } } diff --git a/packages/dev/core/lib/api/bitbybit/csv.test.ts b/packages/dev/core/lib/api/bitbybit/csv.test.ts new file mode 100644 index 00000000..4f24876c --- /dev/null +++ b/packages/dev/core/lib/api/bitbybit/csv.test.ts @@ -0,0 +1,758 @@ +import { CSVBitByBit } from "./csv"; +import * as Inputs from "../inputs/inputs"; + +describe("CSV unit tests", () => { + let csv: CSVBitByBit; + + beforeEach(() => { + csv = new CSVBitByBit(); + }); + + describe("parseToArray", () => { + it("should parse simple CSV to 2D array", () => { + const result = csv.parseToArray({ + csv: "a,b,c\n1,2,3", + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([["a", "b", "c"], ["1", "2", "3"]]); + }); + + it("should handle quoted fields with commas", () => { + const result = csv.parseToArray({ + csv: "name,description\n\"Smith, John\",\"A description\"", + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([["name", "description"], ["Smith, John", "A description"]]); + }); + + it("should handle escaped quotes", () => { + const result = csv.parseToArray({ + csv: "name,quote\n\"John\",\"He said \"\"hello\"\"\"", + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([["name", "quote"], ["John", "He said \"hello\""]]); + }); + + it("should handle empty lines", () => { + const result = csv.parseToArray({ + csv: "a,b\n1,2\n\n3,4", + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([["a", "b"], ["1", "2"], ["3", "4"]]); + }); + + it("should handle custom separators", () => { + const result = csv.parseToArray({ + csv: "a;b;c|1;2;3", + rowSeparator: "|", + columnSeparator: ";" + }); + expect(result).toEqual([["a", "b", "c"], ["1", "2", "3"]]); + }); + }); + + describe("parseToJson", () => { + it("should parse CSV with headers to JSON", () => { + const result = csv.parseToJson({ + csv: "name,age\nJohn,30\nJane,25", + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ]); + }); + + it("should use default header and data row positions", () => { + const result = csv.parseToJson({ + csv: "name,age\nJohn,30", + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([{ name: "John", age: "30" }]); + }); + + it("should handle custom header and data positions", () => { + const result = csv.parseToJson({ + csv: "# Comment\nname,age\nJohn,30", + headerRow: 1, + dataStartRow: 2, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([{ name: "John", age: "30" }]); + }); + + it("should handle missing values", () => { + const result = csv.parseToJson({ + csv: "name,age,city\nJohn,30\nJane,25,NYC", + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([ + { name: "John", age: "30", city: "" }, + { name: "Jane", age: "25", city: "NYC" } + ]); + }); + + it("should throw error if header row is out of bounds", () => { + expect(() => { + csv.parseToJson({ + csv: "name,age\nJohn,30", + headerRow: 5, + dataStartRow: 6, + rowSeparator: "\n", + columnSeparator: "," + }); + }).toThrow("Header row 5 is out of bounds"); + }); + }); + + describe("parseToJsonWithHeaders", () => { + it("should parse CSV with custom headers", () => { + const result = csv.parseToJsonWithHeaders({ + csv: "John,30\nJane,25", + headers: ["name", "age"], + dataStartRow: 0, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ]); + }); + + it("should ignore CSV headers when using custom headers", () => { + const result = csv.parseToJsonWithHeaders({ + csv: "ignored,headers\nJohn,30\nJane,25", + headers: ["name", "age"], + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ]); + }); + }); + + describe("queryColumn", () => { + it("should query column by name", () => { + const result = csv.queryColumn({ + csv: "name,age\nJohn,30\nJane,25", + column: "name", + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual(["John", "Jane"]); + }); + + it("should return empty strings for missing values", () => { + const result = csv.queryColumn({ + csv: "name,age,city\nJohn,30\nJane,25,NYC", + column: "city", + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual(["", "NYC"]); + }); + }); + + describe("queryRowsByValue", () => { + it("should filter rows by column value", () => { + const result = csv.queryRowsByValue({ + csv: "name,age\nJohn,30\nJane,25\nBob,30", + column: "age", + value: "30", + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([ + { name: "John", age: "30" }, + { name: "Bob", age: "30" } + ]); + }); + + it("should return empty array if no matches", () => { + const result = csv.queryRowsByValue({ + csv: "name,age\nJohn,30\nJane,25", + column: "age", + value: "50", + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual([]); + }); + }); + + describe("arrayToCsv", () => { + it("should convert 2D array to CSV", () => { + const result = csv.arrayToCsv({ + array: [["name", "age"], ["John", "30"], ["Jane", "25"]], + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,age\nJohn,30\nJane,25"); + }); + + it("should escape fields with separators", () => { + const result = csv.arrayToCsv({ + array: [["name", "description"], ["Smith, John", "A, B, C"]], + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,description\n\"Smith, John\",\"A, B, C\""); + }); + + it("should escape fields with quotes", () => { + const result = csv.arrayToCsv({ + array: [["name", "quote"], ["John", "He said \"hello\""]], + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,quote\nJohn,\"He said \"\"hello\"\"\""); + }); + + it("should handle custom separators", () => { + const result = csv.arrayToCsv({ + array: [["a", "b"], ["1", "2"]], + rowSeparator: "|", + columnSeparator: ";" + }); + expect(result).toBe("a;b|1;2"); + }); + + it("should handle mixed types (strings and numbers)", () => { + const result = csv.arrayToCsv({ + array: [["name", "age"], ["John", 30], ["Jane", 25]], + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,age\nJohn,30\nJane,25"); + }); + + it("should handle boolean and null values", () => { + const result = csv.arrayToCsv({ + array: [["name", "active", "value"], ["John", true, null], ["Jane", false, undefined]], + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,active,value\nJohn,true,\nJane,false,"); + }); + }); + + describe("jsonToCsv", () => { + it("should convert JSON array to CSV with headers", () => { + const result = csv.jsonToCsv({ + json: [ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ], + headers: ["name", "age"], + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,age\nJohn,30\nJane,25"); + }); + + it("should convert JSON array to CSV without headers", () => { + const result = csv.jsonToCsv({ + json: [ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ], + headers: ["name", "age"], + includeHeaders: false, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("John,30\nJane,25"); + }); + + it("should handle missing properties", () => { + const result = csv.jsonToCsv({ + json: [ + { name: "John", age: "30" }, + { name: "Jane" } + ], + headers: ["name", "age"], + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,age\nJohn,30\nJane,"); + }); + + it("should handle empty array", () => { + const result = csv.jsonToCsv({ + json: [], + headers: ["name", "age"], + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,age"); + }); + + it("should convert non-string values", () => { + const result = csv.jsonToCsv({ + json: [ + { name: "John", age: 30, active: true } + ], + headers: ["name", "age", "active"], + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,age,active\nJohn,30,true"); + }); + }); + + describe("jsonToCsvAuto", () => { + it("should automatically extract headers from JSON", () => { + const result = csv.jsonToCsvAuto({ + json: [ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ], + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("name,age\nJohn,30\nJane,25"); + }); + + it("should handle includeHeaders=false", () => { + const result = csv.jsonToCsvAuto({ + json: [ + { name: "John", age: "30" } + ], + includeHeaders: false, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe("John,30"); + }); + + it("should return empty string for empty array", () => { + const result = csv.jsonToCsvAuto({ + json: [], + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe(""); + }); + }); + + describe("getHeaders", () => { + it("should extract headers from CSV", () => { + const result = csv.getHeaders({ + csv: "name,age,city\nJohn,30,NYC", + headerRow: 0, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual(["name", "age", "city"]); + }); + + it("should extract headers from custom row", () => { + const result = csv.getHeaders({ + csv: "# Comment\nname,age\nJohn,30", + headerRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual(["name", "age"]); + }); + + it("should throw error if header row is out of bounds", () => { + expect(() => { + csv.getHeaders({ + csv: "name,age\nJohn,30", + headerRow: 5, + rowSeparator: "\n", + columnSeparator: "," + }); + }).toThrow("Header row 5 is out of bounds"); + }); + }); + + describe("getRowCount", () => { + it("should count data rows excluding headers", () => { + const result = csv.getRowCount({ + csv: "name,age\nJohn,30\nJane,25", + hasHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe(2); + }); + + it("should count all rows when no headers", () => { + const result = csv.getRowCount({ + csv: "John,30\nJane,25", + hasHeaders: false, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe(2); + }); + + it("should use custom dataStartRow", () => { + const result = csv.getRowCount({ + csv: "# Comment\nname,age\nJohn,30\nJane,25", + dataStartRow: 2, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe(2); + }); + }); + + describe("getColumnCount", () => { + it("should count columns", () => { + const result = csv.getColumnCount({ + csv: "name,age,city\nJohn,30,NYC", + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe(3); + }); + + it("should return 0 for empty CSV", () => { + const result = csv.getColumnCount({ + csv: "", + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toBe(0); + }); + }); + + describe("Round-trip conversion", () => { + it("should convert array to CSV and back", () => { + const original = [["name", "age"], ["John", "30"], ["Jane", "25"]]; + const csvText = csv.arrayToCsv({ + array: original, + rowSeparator: "\n", + columnSeparator: "," + }); + const result = csv.parseToArray({ + csv: csvText, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual(original); + }); + + it("should convert JSON to CSV and back", () => { + const original = [ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ]; + const csvText = csv.jsonToCsvAuto({ + json: original, + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + const result = csv.parseToJson({ + csv: csvText, + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + expect(result).toEqual(original); + }); + }); + + describe("Type safety with generics", () => { + interface Person { + name: string; + age: string; + city?: string; + } + + it("should work with typed objects in parseToJson", () => { + const result = csv.parseToJson({ + csv: "name,age,city\nJohn,30,NYC\nJane,25,LA", + headerRow: 0, + dataStartRow: 1, + rowSeparator: "\n", + columnSeparator: "," + }); + + expect(result[0].name).toBe("John"); + expect(result[0].age).toBe("30"); + expect(result[0].city).toBe("NYC"); + }); + + it("should work with typed objects in jsonToCsv", () => { + const people: Person[] = [ + { name: "John", age: "30", city: "NYC" }, + { name: "Jane", age: "25", city: "LA" } + ]; + + const result = csv.jsonToCsv({ + json: people, + headers: ["name", "age", "city"], + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + + expect(result).toBe("name,age,city\nJohn,30,NYC\nJane,25,LA"); + }); + + it("should work with typed objects in jsonToCsvAuto", () => { + const people: Person[] = [ + { name: "John", age: "30" }, + { name: "Jane", age: "25" } + ]; + + const result = csv.jsonToCsvAuto({ + json: people, + includeHeaders: true, + rowSeparator: "\n", + columnSeparator: "," + }); + + expect(result).toBe("name,age\nJohn,30\nJane,25"); + }); + }); + + describe("number column conversion", () => { + it("should convert specified columns to numbers in parseToJson", () => { + const result = csv.parseToJson({ + csv: "name,age,salary\nJohn,30,50000\nJane,25,60000", + headerRow: 0, + dataStartRow: 1, + numberColumns: ["age", "salary"] + }); + + expect(result).toEqual([ + { name: "John", age: 30, salary: 50000 }, + { name: "Jane", age: 25, salary: 60000 } + ]); + expect(typeof result[0].age).toBe("number"); + expect(typeof result[0].salary).toBe("number"); + expect(typeof result[0].name).toBe("string"); + }); + + it("should handle decimal numbers when converting columns", () => { + const result = csv.parseToJson({ + csv: "product,price,rating\nWidget,19.99,4.5\nGadget,29.95,4.8", + numberColumns: ["price", "rating"] + }); + + expect(result).toEqual([ + { product: "Widget", price: 19.99, rating: 4.5 }, + { product: "Gadget", price: 29.95, rating: 4.8 } + ]); + }); + + it("should handle negative numbers when converting columns", () => { + const result = csv.parseToJson({ + csv: "item,change,temperature\nA,-5,-10.5\nB,10,20.3", + numberColumns: ["change", "temperature"] + }); + + expect(result).toEqual([ + { item: "A", change: -5, temperature: -10.5 }, + { item: "B", change: 10, temperature: 20.3 } + ]); + }); + + it("should convert to NaN for invalid numbers", () => { + const result = csv.parseToJson({ + csv: "name,age\nJohn,abc", + numberColumns: ["age"] + }); + + expect(result[0].name).toBe("John"); + expect(isNaN(result[0].age as number)).toBe(true); + }); + + it("should work without numberColumns specified", () => { + const result = csv.parseToJson({ + csv: "name,age\nJohn,30", + }); + + expect(result).toEqual([{ name: "John", age: "30" }]); + expect(typeof result[0].age).toBe("string"); + }); + + it("should convert specified columns in parseToJsonWithHeaders", () => { + const result = csv.parseToJsonWithHeaders({ + csv: "John,30,5000\nJane,25,6000", + headers: ["name", "age", "bonus"], + numberColumns: ["age", "bonus"] + }); + + expect(result).toEqual([ + { name: "John", age: 30, bonus: 5000 }, + { name: "Jane", age: 25, bonus: 6000 } + ]); + expect(typeof result[0].age).toBe("number"); + expect(typeof result[0].bonus).toBe("number"); + }); + + it("should convert numbers in queryColumn", () => { + const result = csv.queryColumn({ + csv: "name,age\nJohn,30\nJane,25", + column: "age", + asNumber: true + }); + + expect(result).toEqual([30, 25]); + expect(typeof result[0]).toBe("number"); + }); + + it("should keep strings in queryColumn when not specified as number", () => { + const result = csv.queryColumn({ + csv: "name,age\nJohn,30\nJane,25", + column: "age", + asNumber: false + }); + + expect(result).toEqual(["30", "25"]); + expect(typeof result[0]).toBe("string"); + }); + + it("should filter by number value in queryRowsByValue", () => { + const result = csv.queryRowsByValue({ + csv: "name,age,salary\nJohn,30,50000\nJane,25,60000\nBob,30,55000", + column: "age", + value: "30", + numberColumns: ["age", "salary"] + }); + + expect(result).toEqual([ + { name: "John", age: 30, salary: 50000 }, + { name: "Bob", age: 30, salary: 55000 } + ]); + expect(result.length).toBe(2); + }); + + it("should handle scientific notation in number columns", () => { + const result = csv.parseToJson({ + csv: "name,value\nTest,1.5e3\nDemo,2e-2", + numberColumns: ["value"] + }); + + expect(result).toEqual([ + { name: "Test", value: 1500 }, + { name: "Demo", value: 0.02 } + ]); + }); + + it("should handle zero values correctly", () => { + const result = csv.parseToJson({ + csv: "item,count\nA,0\nB,-0\nC,0.0", + numberColumns: ["count"] + }); + + expect(result).toEqual([ + { item: "A", count: 0 }, + { item: "B", count: 0 }, + { item: "C", count: 0 } + ]); + }); + + it("should handle mixed string and number columns", () => { + const result = csv.parseToJson({ + csv: "id,name,active,score\n1,John,true,95.5\n2,Jane,false,88.2", + numberColumns: ["id", "score"] + }); + + expect(result).toEqual([ + { id: 1, name: "John", active: "true", score: 95.5 }, + { id: 2, name: "Jane", active: "false", score: 88.2 } + ]); + expect(typeof result[0].id).toBe("number"); + expect(typeof result[0].name).toBe("string"); + expect(typeof result[0].active).toBe("string"); + expect(typeof result[0].score).toBe("number"); + }); + }); + + describe("escape sequence handling", () => { + it("should accept literal \\n string as row separator", () => { + const result = csv.parseToArray({ + csv: "a,b,c\n1,2,3", + rowSeparator: "\\n", + columnSeparator: "," + }); + expect(result).toEqual([["a", "b", "c"], ["1", "2", "3"]]); + }); + + it("should accept literal \\t string as column separator", () => { + const result = csv.parseToArray({ + csv: "a\tb\tc\n1\t2\t3", + rowSeparator: "\\n", + columnSeparator: "\\t" + }); + expect(result).toEqual([["a", "b", "c"], ["1", "2", "3"]]); + }); + + it("should accept literal \\r\\n string as row separator", () => { + const result = csv.parseToArray({ + csv: "a,b\r\n1,2", + rowSeparator: "\\r\\n", + columnSeparator: "," + }); + expect(result).toEqual([["a", "b"], ["1", "2"]]); + }); + + it("should generate CSV with literal \\n string", () => { + const result = csv.arrayToCsv({ + array: [["a", "b"], ["1", "2"]], + rowSeparator: "\\n", + columnSeparator: "," + }); + expect(result).toBe("a,b\n1,2"); + }); + + it("should generate CSV with literal \\t string", () => { + const result = csv.arrayToCsv({ + array: [["a", "b"], ["1", "2"]], + rowSeparator: "\\n", + columnSeparator: "\\t" + }); + expect(result).toBe("a\tb\n1\t2"); + }); + + it("should work with jsonToCsv using literal escape sequences", () => { + const result = csv.jsonToCsv({ + json: [{ name: "John", age: "30" }], + headers: ["name", "age"], + includeHeaders: true, + rowSeparator: "\\n", + columnSeparator: "\\t" + }); + expect(result).toBe("name\tage\nJohn\t30"); + }); + }); +}); diff --git a/packages/dev/core/lib/api/bitbybit/csv.ts b/packages/dev/core/lib/api/bitbybit/csv.ts new file mode 100644 index 00000000..59c7f331 --- /dev/null +++ b/packages/dev/core/lib/api/bitbybit/csv.ts @@ -0,0 +1,378 @@ +import * as Inputs from "../inputs/inputs"; + +/** + * Contains various CSV parsing and generation methods. + */ +export class CSVBitByBit { + + /** + * Parses CSV text to a 2D array of strings (rows and columns). + * Example: csv='a,b,c\n1,2,3' → [['a','b','c'], ['1','2','3']] + * @param inputs CSV text and parsing options + * @returns 2D array of strings + * @group parse + * @shortname parse to array + * @drawable false + */ + parseToArray(inputs: Inputs.CSV.ParseToArrayDto): string[][] { + // Convert literal escape sequences to actual characters + const rowSeparator = this.convertEscapeSequences(inputs.rowSeparator || "\n"); + const columnSeparator = this.convertEscapeSequences(inputs.columnSeparator || ","); + const lines = inputs.csv.split(rowSeparator); + const result: string[][] = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + if (!line) continue; + + const columns = this.parseCsvLine(line, columnSeparator); + result.push(columns); + } + + return result; + } + + /** + * Parses CSV text to an array of JSON objects using headers. + * Example: csv='name,age\nJohn,30\nJane,25', headerRow=0, dataStartRow=1 + * → [{'name':'John','age':'30'}, {'name':'Jane','age':'25'}] + * @param inputs CSV text and parsing options + * @returns Array of JSON objects + * @group parse + * @shortname parse to json + * @drawable false + */ + parseToJson>(inputs: Inputs.CSV.ParseToJsonDto): T[] { + const array = this.parseToArray({ + csv: inputs.csv, + rowSeparator: inputs.rowSeparator, + columnSeparator: inputs.columnSeparator + }); + + if (array.length === 0) return []; + + const headerRow = inputs.headerRow ?? 0; + const dataStartRow = inputs.dataStartRow ?? 1; + + if (headerRow >= array.length) { + throw new Error(`Header row ${headerRow} is out of bounds (total rows: ${array.length})`); + } + + const headers = array[headerRow]; + const numberColumnsSet = new Set(inputs.numberColumns || []); + const result: T[] = []; + + for (let i = dataStartRow; i < array.length; i++) { + const row = array[i]; + const obj: Record = {}; + + headers.forEach((header, index) => { + const value = row[index] || ""; + // Convert to number if this column is in the numberColumns list + if (numberColumnsSet.has(header)) { + const num = parseFloat(value); + // Normalize -0 to 0 + obj[header] = num === 0 ? 0 : num; + } else { + obj[header] = value; + } + }); + + result.push(obj as T); + } + + return result; + } + + /** + * Parses CSV text to JSON using custom headers (ignores CSV headers if present). + * Example: csv='John,30\nJane,25', headers=['name','age'] + * → [{'name':'John','age':'30'}, {'name':'Jane','age':'25'}] + * @param inputs CSV text, custom headers, and parsing options + * @returns Array of JSON objects + * @group parse + * @shortname parse to json with headers + * @drawable false + */ + parseToJsonWithHeaders>(inputs: Inputs.CSV.ParseToJsonWithHeadersDto): T[] { + const array = this.parseToArray({ + csv: inputs.csv, + rowSeparator: inputs.rowSeparator, + columnSeparator: inputs.columnSeparator + }); + + if (array.length === 0) return []; + + const dataStartRow = inputs.dataStartRow ?? 0; + const numberColumnsSet = new Set(inputs.numberColumns || []); + const result: T[] = []; + + for (let i = dataStartRow; i < array.length; i++) { + const row = array[i]; + const obj: Record = {}; + + inputs.headers.forEach((header, index) => { + const value = row[index] || ""; + // Convert to number if this column is in the numberColumns list + if (numberColumnsSet.has(header)) { + const num = parseFloat(value); + // Normalize -0 to 0 + obj[header] = num === 0 ? 0 : num; + } else { + obj[header] = value; + } + }); + + result.push(obj as T); + } + + return result; + } + + /** + * Queries CSV data by column/header name and returns all values in that column. + * Example: csv='name,age\nJohn,30\nJane,25', column='name' → ['John', 'Jane'] + * @param inputs CSV text, column name, and parsing options + * @returns Array of values from the specified column + * @group query + * @shortname query column + * @drawable false + */ + queryColumn>(inputs: Inputs.CSV.QueryColumnDto): (string | number)[] { + const numberColumns = inputs.asNumber ? [inputs.column] : undefined; + + const jsonData = this.parseToJson({ + csv: inputs.csv, + headerRow: inputs.headerRow, + dataStartRow: inputs.dataStartRow, + rowSeparator: inputs.rowSeparator, + columnSeparator: inputs.columnSeparator, + numberColumns: numberColumns + }); + + return jsonData.map(row => row[inputs.column] !== undefined ? row[inputs.column] : ""); + } + + /** + * Queries CSV data and filters rows where a column matches a value. + * Example: csv='name,age\nJohn,30\nJane,25', column='age', value='30' → [{'name':'John','age':'30'}] + * @param inputs CSV text, column name, value, and parsing options + * @returns Array of matching rows as JSON objects + * @group query + * @shortname query rows by value + * @drawable false + */ + queryRowsByValue>(inputs: Inputs.CSV.QueryRowsByValueDto): T[] { + const jsonData = this.parseToJson({ + csv: inputs.csv, + headerRow: inputs.headerRow, + dataStartRow: inputs.dataStartRow, + rowSeparator: inputs.rowSeparator, + columnSeparator: inputs.columnSeparator, + numberColumns: inputs.numberColumns + }); + + // If the column is a number column, compare as numbers + const isNumberColumn = inputs.numberColumns?.includes(inputs.column); + const compareValue = isNumberColumn ? parseFloat(inputs.value) : inputs.value; + + return jsonData.filter(row => { + const rowValue = row[inputs.column]; + if (isNumberColumn) { + return rowValue === compareValue; + } + return rowValue === inputs.value; + }); + } + + /** + * Converts a 2D array to CSV text. + * Example: array=[['name','age'], ['John','30']] → 'name,age\nJohn,30' + * @param inputs 2D array and formatting options + * @returns CSV text + * @group generate + * @shortname array to csv + * @drawable false + */ + arrayToCsv(inputs: Inputs.CSV.ArrayToCsvDto): string { + const columnSeparator = this.convertEscapeSequences(inputs.columnSeparator || ","); + const rowSeparator = this.convertEscapeSequences(inputs.rowSeparator || "\n"); + + return inputs.array.map(row => + row.map(cell => this.escapeCsvCell(cell as unknown, columnSeparator)).join(columnSeparator) + ).join(rowSeparator); + } + + /** + * Converts an array of JSON objects to CSV text. + * Example: json=[{'name':'John','age':'30'}], headers=['name','age'] → 'name,age\nJohn,30' + * @param inputs JSON array, headers, and formatting options + * @returns CSV text + * @group generate + * @shortname json to csv + * @drawable false + */ + jsonToCsv>(inputs: Inputs.CSV.JsonToCsvDto): string { + const columnSeparator = this.convertEscapeSequences(inputs.columnSeparator || ","); + const rowSeparator = this.convertEscapeSequences(inputs.rowSeparator || "\n"); + + if (!inputs.json || inputs.json.length === 0) { + return inputs.includeHeaders ? inputs.headers.join(columnSeparator) : ""; + } + + const lines: string[] = []; + + // Add headers if requested + if (inputs.includeHeaders) { + lines.push(inputs.headers.map(h => this.escapeCsvCell(h, columnSeparator)).join(columnSeparator)); + } + + // Add data rows + inputs.json.forEach(obj => { + const row = inputs.headers.map(header => { + const value = obj[header]; + return this.escapeCsvCell(value !== undefined && value !== null ? String(value) : "", columnSeparator); + }); + lines.push(row.join(columnSeparator)); + }); + + return lines.join(rowSeparator); + } + + /** + * Converts an array of JSON objects to CSV text using object keys as headers. + * Example: json=[{'name':'John','age':'30'}] → 'name,age\nJohn,30' + * @param inputs JSON array and formatting options + * @returns CSV text + * @group generate + * @shortname json to csv auto + * @drawable false + */ + jsonToCsvAuto>(inputs: Inputs.CSV.JsonToCsvAutoDto): string { + if (!inputs.json || inputs.json.length === 0) return ""; + + // Get headers from first object + const headers = Object.keys(inputs.json[0]); + + return this.jsonToCsv({ + json: inputs.json, + headers: headers, + includeHeaders: inputs.includeHeaders ?? true, + columnSeparator: inputs.columnSeparator, + rowSeparator: inputs.rowSeparator + }); + } + + /** + * Gets the headers from a CSV file. + * Example: csv='name,age\nJohn,30', headerRow=0 → ['name', 'age'] + * @param inputs CSV text and options + * @returns Array of header names + * @group query + * @shortname get headers + * @drawable false + */ + getHeaders(inputs: Inputs.CSV.GetHeadersDto): string[] { + const array = this.parseToArray({ + csv: inputs.csv, + rowSeparator: inputs.rowSeparator, + columnSeparator: inputs.columnSeparator + }); + + const headerRow = inputs.headerRow ?? 0; + + if (headerRow >= array.length) { + throw new Error(`Header row ${headerRow} is out of bounds (total rows: ${array.length})`); + } + + return array[headerRow]; + } + + /** + * Gets the number of rows in a CSV file (excluding headers if specified). + * Example: csv='name,age\nJohn,30\nJane,25', headerRow=0 → 2 + * @param inputs CSV text and options + * @returns Number of data rows + * @group query + * @shortname row count + * @drawable false + */ + getRowCount(inputs: Inputs.CSV.GetRowCountDto): number { + const array = this.parseToArray({ + csv: inputs.csv, + rowSeparator: inputs.rowSeparator, + columnSeparator: inputs.columnSeparator + }); + + const dataStartRow = inputs.dataStartRow ?? (inputs.hasHeaders ? 1 : 0); + return Math.max(0, array.length - dataStartRow); + } + + /** + * Gets the number of columns in a CSV file. + * Example: csv='name,age,city\nJohn,30,NYC' → 3 + * @param inputs CSV text and options + * @returns Number of columns + * @group query + * @shortname column count + * @drawable false + */ + getColumnCount(inputs: Inputs.CSV.ParseToArrayDto): number { + const array = this.parseToArray(inputs); + return array.length > 0 ? array[0].length : 0; + } + + private parseCsvLine(line: string, separator: string): string[] { + const result: string[] = []; + let current = ""; + let inQuotes = false; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + const nextChar = line[i + 1]; + + if (char === "\"") { + if (inQuotes && nextChar === "\"") { + // Escaped quote + current += "\""; + i++; // Skip next quote + } else { + // Toggle quote mode + inQuotes = !inQuotes; + } + } else if (char === separator && !inQuotes) { + // End of field + result.push(current); + current = ""; + } else { + current += char; + } + } + + // Add last field + result.push(current); + + return result; + } + private escapeCsvCell(cell: unknown, separator: string): string { + // Convert to string first + const cellStr = cell !== undefined && cell !== null ? String(cell) : ""; + // If cell contains separator, quotes, or newlines, wrap in quotes + if (cellStr.includes(separator) || cellStr.includes("\"") || cellStr.includes("\n") || cellStr.includes("\r")) { + // Escape existing quotes by doubling them + return "\"" + cellStr.replace(/"/g, "\"\"") + "\""; + } + return cellStr; + } + + /** + * Converts literal escape sequence strings to their actual characters. + * For example, converts "\\n" (two characters) to "\n" (newline character). + */ + private convertEscapeSequences(str: string): string { + return str + .replace(/\\n/g, "\n") + .replace(/\\t/g, "\t") + .replace(/\\r/g, "\r"); + } +} diff --git a/packages/dev/core/lib/api/bitbybit/index.ts b/packages/dev/core/lib/api/bitbybit/index.ts index 4e10ffc0..6c6c02e1 100644 --- a/packages/dev/core/lib/api/bitbybit/index.ts +++ b/packages/dev/core/lib/api/bitbybit/index.ts @@ -2,6 +2,7 @@ export * from "./occt"; export * from "./verb"; export * from "./asset"; export * from "./base-types"; +export * from "./csv"; export * from "./json"; export * from "./tag"; export * from "./time"; \ No newline at end of file diff --git a/packages/dev/core/lib/api/inputs/csv-inputs.ts b/packages/dev/core/lib/api/inputs/csv-inputs.ts new file mode 100644 index 00000000..0710819e --- /dev/null +++ b/packages/dev/core/lib/api/inputs/csv-inputs.ts @@ -0,0 +1,389 @@ +/* eslint-disable @typescript-eslint/no-namespace */ + +export namespace CSV { + + export class ParseToArrayDto { + constructor(csv?: string, rowSeparator?: string, columnSeparator?: string) { + if (csv !== undefined) { this.csv = csv; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + } + /** + * CSV text to parse + * @default name,age\nJohn,30 + */ + csv = "name,age\nJohn,30"; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + } + + export class ParseToJsonDto { + constructor(csv?: string, headerRow?: number, dataStartRow?: number, rowSeparator?: string, columnSeparator?: string, numberColumns?: string[]) { + if (csv !== undefined) { this.csv = csv; } + if (headerRow !== undefined) { this.headerRow = headerRow; } + if (dataStartRow !== undefined) { this.dataStartRow = dataStartRow; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + if (numberColumns !== undefined) { this.numberColumns = numberColumns; } + } + /** + * CSV text to parse + * @default name,age\nJohn,30\nJane,25 + */ + csv = "name,age\nJohn,30\nJane,25"; + /** + * Row index where headers are located + * @default 0 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + headerRow? = 0; + /** + * Row index where data starts + * @default 1 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + dataStartRow? = 1; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + /** + * Column names that should be converted to numbers + * @default undefined + * @optional true + */ + numberColumns?: string[]; + } + + export class ParseToJsonWithHeadersDto { + constructor(csv?: string, headers?: string[], dataStartRow?: number, rowSeparator?: string, columnSeparator?: string, numberColumns?: string[]) { + if (csv !== undefined) { this.csv = csv; } + if (headers !== undefined) { this.headers = headers; } + if (dataStartRow !== undefined) { this.dataStartRow = dataStartRow; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + if (numberColumns !== undefined) { this.numberColumns = numberColumns; } + } + /** + * CSV text to parse + * @default John,30\nJane,25 + */ + csv = "John,30\nJane,25"; + /** + * Custom header names to use + * @default ["name", "age"] + */ + headers: string[] = ["name", "age"]; + /** + * Row index where data starts + * @default 0 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + dataStartRow? = 0; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + /** + * Column names that should be converted to numbers + * @default undefined + * @optional true + */ + numberColumns?: string[]; + } + + export class QueryColumnDto { + constructor(csv?: string, column?: string, headerRow?: number, dataStartRow?: number, rowSeparator?: string, columnSeparator?: string, asNumber?: boolean) { + if (csv !== undefined) { this.csv = csv; } + if (column !== undefined) { this.column = column; } + if (headerRow !== undefined) { this.headerRow = headerRow; } + if (dataStartRow !== undefined) { this.dataStartRow = dataStartRow; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + if (asNumber !== undefined) { this.asNumber = asNumber; } + } + /** + * CSV text to query + * @default name,age\nJohn,30\nJane,25 + */ + csv = "name,age\nJohn,30\nJane,25"; + /** + * Column name to query + * @default name + */ + column = "name"; + /** + * Row index where headers are located + * @default 0 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + headerRow? = 0; + /** + * Row index where data starts + * @default 1 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + dataStartRow? = 1; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + /** + * Convert column values to numbers + * @default false + */ + asNumber? = false; + } + + export class QueryRowsByValueDto { + constructor(csv?: string, column?: string, value?: string, headerRow?: number, dataStartRow?: number, rowSeparator?: string, columnSeparator?: string, numberColumns?: string[]) { + if (csv !== undefined) { this.csv = csv; } + if (column !== undefined) { this.column = column; } + if (value !== undefined) { this.value = value; } + if (headerRow !== undefined) { this.headerRow = headerRow; } + if (dataStartRow !== undefined) { this.dataStartRow = dataStartRow; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + if (numberColumns !== undefined) { this.numberColumns = numberColumns; } + } + /** + * CSV text to query + * @default name,age\nJohn,30\nJane,25 + */ + csv = "name,age\nJohn,30\nJane,25"; + /** + * Column name to filter by + * @default age + */ + column = "age"; + /** + * Value to match + * @default 30 + */ + value = "30"; + /** + * Row index where headers are located + * @default 0 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + headerRow? = 0; + /** + * Row index where data starts + * @default 1 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + dataStartRow? = 1; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + /** + * Column names that should be converted to numbers + * @default undefined + * @optional true + */ + numberColumns?: string[]; + } + + export class ArrayToCsvDto { + constructor(array?: (string | number | boolean | null | undefined)[][], rowSeparator?: string, columnSeparator?: string) { + if (array !== undefined) { this.array = array; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + } + /** + * 2D array to convert + * @default [["name", "age"], ["John", "30"]] + */ + array: (string | number | boolean | null | undefined)[][] = [["name", "age"], ["John", "30"]]; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + } + + export class JsonToCsvDto> { + constructor(json?: T[], headers?: string[], includeHeaders?: boolean, rowSeparator?: string, columnSeparator?: string) { + if (json !== undefined) { this.json = json; } + if (headers !== undefined) { this.headers = headers; } + if (includeHeaders !== undefined) { this.includeHeaders = includeHeaders; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + } + /** + * Array of JSON objects to convert + * @default [{"name": "John", "age": "30"}] + */ + json: T[] = [{ "name": "John", "age": "30" }] as T[]; + /** + * Headers to use (in order) + * @default ["name", "age"] + */ + headers: string[] = ["name", "age"]; + /** + * Whether to include headers in output + * @default true + */ + includeHeaders? = true; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + } + + export class JsonToCsvAutoDto> { + constructor(json?: T[], includeHeaders?: boolean, rowSeparator?: string, columnSeparator?: string) { + if (json !== undefined) { this.json = json; } + if (includeHeaders !== undefined) { this.includeHeaders = includeHeaders; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + } + /** + * Array of JSON objects to convert + * @default [{"name": "John", "age": "30"}] + */ + json: T[] = [{ "name": "John", "age": "30" }] as T[]; + /** + * Whether to include headers in output + * @default true + */ + includeHeaders? = true; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + } + + export class GetHeadersDto { + constructor(csv?: string, headerRow?: number, rowSeparator?: string, columnSeparator?: string) { + if (csv !== undefined) { this.csv = csv; } + if (headerRow !== undefined) { this.headerRow = headerRow; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + } + /** + * CSV text to get headers from + * @default name,age\nJohn,30 + */ + csv = "name,age\nJohn,30"; + /** + * Row index where headers are located + * @default 0 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + headerRow? = 0; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + } + + export class GetRowCountDto { + constructor(csv?: string, hasHeaders?: boolean, dataStartRow?: number, rowSeparator?: string, columnSeparator?: string) { + if (csv !== undefined) { this.csv = csv; } + if (hasHeaders !== undefined) { this.hasHeaders = hasHeaders; } + if (dataStartRow !== undefined) { this.dataStartRow = dataStartRow; } + if (rowSeparator !== undefined) { this.rowSeparator = rowSeparator; } + if (columnSeparator !== undefined) { this.columnSeparator = columnSeparator; } + } + /** + * CSV text to count rows + * @default name,age\nJohn,30\nJane,25 + */ + csv = "name,age\nJohn,30\nJane,25"; + /** + * Whether CSV has headers + * @default true + */ + hasHeaders? = true; + /** + * Row index where data starts (overrides hasHeaders if set) + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + dataStartRow?: number; + /** + * Row separator (newline character) + * @default \n + */ + rowSeparator? = "\n"; + /** + * Column separator (delimiter) + * @default , + */ + columnSeparator? = ","; + } +} diff --git a/packages/dev/core/lib/api/inputs/inputs.ts b/packages/dev/core/lib/api/inputs/inputs.ts index f23fafcb..13dc1a37 100644 --- a/packages/dev/core/lib/api/inputs/inputs.ts +++ b/packages/dev/core/lib/api/inputs/inputs.ts @@ -1,4 +1,5 @@ export * from "./asset-inputs"; +export * from "./csv-inputs"; export * from "./json-inputs"; export * from "./tag-inputs"; export * from "./time-inputs"; diff --git a/packages/dev/threejs/lib/api/bitbybit-base.ts b/packages/dev/threejs/lib/api/bitbybit-base.ts index e69df721..6dcbd771 100644 --- a/packages/dev/threejs/lib/api/bitbybit-base.ts +++ b/packages/dev/threejs/lib/api/bitbybit-base.ts @@ -7,6 +7,7 @@ import { OCCTW, Asset, JSONBitByBit, + CSVBitByBit, } from "@bitbybit-dev/core"; import { JSCAD } from "@bitbybit-dev/jscad-worker"; import { ManifoldBitByBit } from "@bitbybit-dev/manifold-worker"; @@ -38,6 +39,7 @@ export class BitByBitBase { public logic: Logic; public lists: Lists; public json: JSONBitByBit; + public csv: CSVBitByBit; public vector: Vector; public three: ThreeJS; public point: Point; @@ -84,6 +86,7 @@ export class BitByBitBase { this.asset = new Asset(); this.logic = new Logic(); this.json = new JSONBitByBit(this.context); + this.csv = new CSVBitByBit(); this.text = new TextBitByBit(this.point); this.dates = new Dates(); this.mesh = new MeshBitByBit(this.vector, this.polyline); From ba64c65e13b20992ff6de74b223db58a82bbd57d Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 22 Dec 2025 14:17:16 +0200 Subject: [PATCH 32/38] updated actual nr of API functions --- docs/src/pages/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 61a0f7b6..3510d6ca 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -164,7 +164,7 @@ function HeroSection() { CAD Kernels
- 1000+ + 1300+ API Functions
From 3f0d56f4a893bc45967f8c6516a3dd34bc521cae Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Mon, 22 Dec 2025 23:29:14 +0200 Subject: [PATCH 33/38] added option to remove trailing zeros to dimensions --- .../dev/occt/lib/api/inputs/occ-inputs.ts | 16 +- .../lib/services/base/dimensions.service.ts | 33 ++- .../dev/occt/lib/services/dimensions.test.ts | 222 ++++++++++++++++++ 3 files changed, 260 insertions(+), 11 deletions(-) diff --git a/packages/dev/occt/lib/api/inputs/occ-inputs.ts b/packages/dev/occt/lib/api/inputs/occ-inputs.ts index 00a7a8b6..aa90a1fc 100644 --- a/packages/dev/occt/lib/api/inputs/occ-inputs.ts +++ b/packages/dev/occt/lib/api/inputs/occ-inputs.ts @@ -7041,7 +7041,7 @@ export namespace OCCT { direction: Base.Vector3 = [0, 1, 0]; } export class SimpleLinearLengthDimensionDto { - constructor(start?: Base.Point3, end?: Base.Point3, direction?: Base.Vector3, offsetFromPoints?: number, crossingSize?: number, labelSuffix?: string, labelSize?: number, labelOffset?: number, labelRotation?: number, arrowType?: dimensionEndTypeEnum, arrowSize?: number, arrowAngle?: number, arrowsFlipped?: boolean, labelFlipHorizontal?: boolean, labelFlipVertical?: boolean, labelOverwrite?: string) { + constructor(start?: Base.Point3, end?: Base.Point3, direction?: Base.Vector3, offsetFromPoints?: number, crossingSize?: number, labelSuffix?: string, labelSize?: number, labelOffset?: number, labelRotation?: number, arrowType?: dimensionEndTypeEnum, arrowSize?: number, arrowAngle?: number, arrowsFlipped?: boolean, labelFlipHorizontal?: boolean, labelFlipVertical?: boolean, labelOverwrite?: string, removeTrailingZeros?: boolean) { if (start !== undefined) { this.start = start; } if (end !== undefined) { this.end = end; } if (direction !== undefined) { this.direction = direction; } @@ -7058,6 +7058,7 @@ export namespace OCCT { if (labelFlipHorizontal !== undefined) { this.labelFlipHorizontal = labelFlipHorizontal; } if (labelFlipVertical !== undefined) { this.labelFlipVertical = labelFlipVertical; } if (labelOverwrite !== undefined) { this.labelOverwrite = labelOverwrite; } + if (removeTrailingZeros !== undefined) { this.removeTrailingZeros = removeTrailingZeros; } } /** * The start point for dimension @@ -7169,9 +7170,14 @@ export namespace OCCT { * @optional true */ labelOverwrite? = "1*val"; + /** + * Remove trailing zeros from decimal places + * @default false + */ + removeTrailingZeros? = false; } export class SimpleAngularDimensionDto { - constructor(direction1?: Base.Point3, direction2?: Base.Point3, center?: Base.Point3, radius?: number, offsetFromCenter?: number, crossingSize?: number, radians?: boolean, labelSuffix?: string, labelSize?: number, labelOffset?: number, endType?: dimensionEndTypeEnum, arrowSize?: number, arrowAngle?: number, arrowsFlipped?: boolean, labelRotation?: number, labelFlipHorizontal?: boolean, labelFlipVertical?: boolean, labelOverwrite?: string) { + constructor(direction1?: Base.Point3, direction2?: Base.Point3, center?: Base.Point3, radius?: number, offsetFromCenter?: number, crossingSize?: number, radians?: boolean, labelSuffix?: string, labelSize?: number, labelOffset?: number, endType?: dimensionEndTypeEnum, arrowSize?: number, arrowAngle?: number, arrowsFlipped?: boolean, labelRotation?: number, labelFlipHorizontal?: boolean, labelFlipVertical?: boolean, labelOverwrite?: string, removeTrailingZeros?: boolean) { if (direction1 !== undefined) { this.direction1 = direction1; } if (direction2 !== undefined) { this.direction2 = direction2; } if (center !== undefined) { this.center = center; } @@ -7190,6 +7196,7 @@ export namespace OCCT { if (labelFlipHorizontal !== undefined) { this.labelFlipHorizontal = labelFlipHorizontal; } if (labelFlipVertical !== undefined) { this.labelFlipVertical = labelFlipVertical; } if (labelOverwrite !== undefined) { this.labelOverwrite = labelOverwrite; } + if (removeTrailingZeros !== undefined) { this.removeTrailingZeros = removeTrailingZeros; } } /** @@ -7315,6 +7322,11 @@ export namespace OCCT { * @optional true */ labelOverwrite? = "1*val"; + /** + * Remove trailing zeros from decimal places + * @default false + */ + removeTrailingZeros? = false; } export class PinWithLabelDto { constructor(startPoint?: Base.Point3, endPoint?: Base.Point3, direction?: Base.Vector3, offsetFromStart?: number, label?: string, labelOffset?: number, labelSize?: number, endType?: dimensionEndTypeEnum, arrowSize?: number, arrowAngle?: number, arrowsFlipped?: boolean, labelRotation?: number, labelFlipHorizontal?: boolean, labelFlipVertical?: boolean) { diff --git a/packages/dev/occt/lib/services/base/dimensions.service.ts b/packages/dev/occt/lib/services/base/dimensions.service.ts index 6e6d872e..ad94e1b7 100644 --- a/packages/dev/occt/lib/services/base/dimensions.service.ts +++ b/packages/dev/occt/lib/services/base/dimensions.service.ts @@ -27,9 +27,10 @@ export class DimensionsService { * @param expression The expression to evaluate (can contain 'val' placeholder) * @param value The numeric value to substitute for 'val' * @param decimalPlaces Number of decimal places to format the result + * @param removeTrailingZeros Whether to remove trailing zeros from the result * @returns The evaluated expression as a formatted string */ - private evaluateExpression(expression: string, value: number, decimalPlaces: number): string { + private evaluateExpression(expression: string, value: number, decimalPlaces: number, removeTrailingZeros = false): string { try { // Replace 'val' with the actual value in the expression const evaluatedExpression = expression.replace(/val/g, value.toString()); @@ -40,16 +41,22 @@ export class DimensionsService { if (safeExpression !== evaluatedExpression) { // If expression contains non-math characters, treat it as a template // For template strings, we still want to format numbers with decimal places - const formattedValue = value.toFixed(decimalPlaces); + const formattedValue = removeTrailingZeros + ? this.base.math.roundAndRemoveTrailingZeros({ number: value, decimalPlaces }).toString() + : value.toFixed(decimalPlaces); return expression.replace(/val/g, formattedValue); } // Evaluate mathematical expression and apply decimal places const result = Function("\"use strict\"; return (" + safeExpression + ")")(); - return result.toFixed(decimalPlaces); + return removeTrailingZeros + ? this.base.math.roundAndRemoveTrailingZeros({ number: result, decimalPlaces }).toString() + : result.toFixed(decimalPlaces); } catch (error) { // If evaluation fails, return the original value formatted - return value.toFixed(decimalPlaces); + return removeTrailingZeros + ? this.base.math.roundAndRemoveTrailingZeros({ number: value, decimalPlaces }).toString() + : value.toFixed(decimalPlaces); } } @@ -59,19 +66,25 @@ export class DimensionsService { * @param labelOverwrite Optional expression to evaluate instead of raw value * @param decimalPlaces Number of decimal places for formatting * @param labelSuffix Suffix to append to the text + * @param removeTrailingZeros Whether to remove trailing zeros from the result * @returns Formatted dimension label text */ private formatDimensionLabel( value: number, labelOverwrite: string | undefined, decimalPlaces: number, - labelSuffix: string + labelSuffix: string, + removeTrailingZeros = false ): string { let result: string; if (labelOverwrite) { - result = this.evaluateExpression(labelOverwrite, value, decimalPlaces); + result = this.evaluateExpression(labelOverwrite, value, decimalPlaces, removeTrailingZeros); } else { - result = value.toFixed(decimalPlaces); + if (removeTrailingZeros) { + result = this.base.math.roundAndRemoveTrailingZeros({ number: value, decimalPlaces }).toString(); + } else { + result = value.toFixed(decimalPlaces); + } } return result + " " + labelSuffix; } @@ -199,7 +212,8 @@ export class DimensionsService { length, inputs.labelOverwrite, inputs.decimalPlaces, - inputs.labelSuffix + inputs.labelSuffix, + inputs.removeTrailingZeros ); const txtOpt = new Inputs.OCCT.TextWiresDto(); @@ -389,7 +403,8 @@ export class DimensionsService { angle, inputs.labelOverwrite, inputs.decimalPlaces, - inputs.labelSuffix + inputs.labelSuffix, + inputs.removeTrailingZeros ); const txtOpt = new Inputs.OCCT.TextWiresDto(); diff --git a/packages/dev/occt/lib/services/dimensions.test.ts b/packages/dev/occt/lib/services/dimensions.test.ts index 4a949db8..90f389d6 100644 --- a/packages/dev/occt/lib/services/dimensions.test.ts +++ b/packages/dev/occt/lib/services/dimensions.test.ts @@ -954,4 +954,226 @@ describe("OCCT dimensions unit tests", () => { expect(result.IsNull()).toBe(false); }); }); + + describe("removeTrailingZeros", () => { + describe("simpleLinearLengthDimension", () => { + it("should remove trailing zeros when removeTrailingZeros is true", () => { + const inputs = new Inputs.OCCT.SimpleLinearLengthDimensionDto(); + inputs.start = [0, 0, 0]; + inputs.end = [10, 0, 0]; + inputs.direction = [0, 2, 0]; + inputs.decimalPlaces = 3; + inputs.labelSuffix = "mm"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleLinearLengthDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // The dimension should be created successfully + // Label should be "10 mm" instead of "10.000 mm" + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should keep trailing zeros when removeTrailingZeros is false", () => { + const inputs = new Inputs.OCCT.SimpleLinearLengthDimensionDto(); + inputs.start = [0, 0, 0]; + inputs.end = [10, 0, 0]; + inputs.direction = [0, 2, 0]; + inputs.decimalPlaces = 3; + inputs.labelSuffix = "mm"; + inputs.removeTrailingZeros = false; + + const result = dimensions.simpleLinearLengthDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // Label should be "10.000 mm" + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should remove trailing zeros from partial decimals", () => { + const inputs = new Inputs.OCCT.SimpleLinearLengthDimensionDto(); + inputs.start = [0, 0, 0]; + inputs.end = [10.5, 0, 0]; + inputs.direction = [0, 2, 0]; + inputs.decimalPlaces = 3; + inputs.labelSuffix = "mm"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleLinearLengthDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // Label should be "10.5 mm" instead of "10.500 mm" + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should work with very precise measurements", () => { + const inputs = new Inputs.OCCT.SimpleLinearLengthDimensionDto(); + inputs.start = [0, 0, 0]; + inputs.end = [3.14159, 0, 0]; + inputs.direction = [0, 1, 0]; + inputs.decimalPlaces = 5; + inputs.labelSuffix = "mm"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleLinearLengthDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // Label should be "3.14159 mm" (no trailing zeros to remove) + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should default to false when removeTrailingZeros is not specified", () => { + const inputs = new Inputs.OCCT.SimpleLinearLengthDimensionDto(); + inputs.start = [0, 0, 0]; + inputs.end = [5, 0, 0]; + inputs.direction = [0, 1, 0]; + inputs.decimalPlaces = 2; + + const result = dimensions.simpleLinearLengthDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // Should behave as removeTrailingZeros = false by default + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + }); + + describe("simpleAngularDimension", () => { + it("should remove trailing zeros from angle measurements", () => { + const inputs = new Inputs.OCCT.SimpleAngularDimensionDto(); + inputs.center = [0, 0, 0]; + inputs.direction1 = [1, 0, 0]; + inputs.direction2 = [0, 1, 0]; + inputs.radius = 3; + inputs.decimalPlaces = 3; + inputs.labelSuffix = "°"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleAngularDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // Label should be "90 °" instead of "90.000 °" + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should keep trailing zeros when removeTrailingZeros is false", () => { + const inputs = new Inputs.OCCT.SimpleAngularDimensionDto(); + inputs.center = [0, 0, 0]; + inputs.direction1 = [1, 0, 0]; + inputs.direction2 = [0, 1, 0]; + inputs.radius = 3; + inputs.decimalPlaces = 2; + inputs.labelSuffix = "°"; + inputs.removeTrailingZeros = false; + + const result = dimensions.simpleAngularDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // Label should be "90.00 °" + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should handle radian measurements with removeTrailingZeros", () => { + const inputs = new Inputs.OCCT.SimpleAngularDimensionDto(); + inputs.center = [0, 0, 0]; + inputs.direction1 = [1, 0, 0]; + inputs.direction2 = [0, 1, 0]; + inputs.radius = 4; + inputs.radians = true; + inputs.decimalPlaces = 4; + inputs.labelSuffix = "rad"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleAngularDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // Label should show π/2 ≈ 1.5708 rad (actual value depends on calculation) + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should handle small angles with trailing zeros", () => { + const inputs = new Inputs.OCCT.SimpleAngularDimensionDto(); + inputs.center = [0, 0, 0]; + inputs.direction1 = [1, 0, 0]; + inputs.direction2 = [0.99, 0, 0.1]; + inputs.radius = 3; + inputs.decimalPlaces = 3; + inputs.labelSuffix = "°"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleAngularDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + }); + + describe("with labelOverwrite", () => { + it("should not affect labelOverwrite expressions in linear dimensions", () => { + const inputs = new Inputs.OCCT.SimpleLinearLengthDimensionDto(); + inputs.start = [0, 0, 0]; + inputs.end = [10, 0, 0]; + inputs.direction = [0, 2, 0]; + inputs.labelOverwrite = "val*100"; + inputs.decimalPlaces = 2; + inputs.labelSuffix = "mm"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleLinearLengthDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + // labelOverwrite should be evaluated normally + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + + it("should not affect labelOverwrite template strings", () => { + const inputs = new Inputs.OCCT.SimpleAngularDimensionDto(); + inputs.center = [0, 0, 0]; + inputs.direction1 = [1, 0, 0]; + inputs.direction2 = [0, 1, 0]; + inputs.radius = 5; + inputs.labelOverwrite = "Angle: val"; + inputs.decimalPlaces = 1; + inputs.labelSuffix = "°"; + inputs.removeTrailingZeros = true; + + const result = dimensions.simpleAngularDimension(inputs); + + expect(result).toBeDefined(); + expect(result.IsNull()).toBe(false); + + const vertices = occHelper.shapeGettersService.getVertices({ shape: result }); + expect(vertices.length).toBeGreaterThan(0); + }); + }); + }); }); From 766f042ac8b771d1ca030f3efdafe3e38ac36b69 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Tue, 23 Dec 2025 13:38:54 +0200 Subject: [PATCH 34/38] new asset handling methods --- packages/dev/core/lib/api/bitbybit/asset.ts | 46 +++++++++++++++++++ .../dev/core/lib/api/inputs/asset-inputs.ts | 28 +++++++++++ packages/dev/core/lib/asset-manager.ts | 1 + 3 files changed, 75 insertions(+) diff --git a/packages/dev/core/lib/api/bitbybit/asset.ts b/packages/dev/core/lib/api/bitbybit/asset.ts index b9bed320..8ef2285c 100644 --- a/packages/dev/core/lib/api/bitbybit/asset.ts +++ b/packages/dev/core/lib/api/bitbybit/asset.ts @@ -19,6 +19,18 @@ export class Asset { return this.assetManager.getAsset(inputs.fileName); } + /** + * Gets the text from asset file stored in your browser. + * @param inputs asset name to get from project assets + * @returns Text of asset + * @group get + * @shortname text file + */ + async getTextFile(inputs: Inputs.Asset.GetAssetDto): Promise { + const file = await this.assetManager.getAsset(inputs.fileName); + return await file.text(); + } + /** * Gets the local asset file stored in your browser. * @param inputs asset name to get from local assets @@ -30,6 +42,22 @@ export class Asset { return this.assetManager.getLocalAsset(inputs.fileName); } + /** + * Gets the text from asset file stored in your browser. + * @param inputs asset name to get from local assets + * @returns Text of asset or array of texts + * @group get + * @shortname local text file + */ + async getLocalTextFile(inputs: Inputs.Asset.GetAssetDto): Promise { + const files = await this.getLocalFile(inputs); + if (Array.isArray(files)) { + return await Promise.all(files.map(f => f.text())); + } else { + return await files.text(); + } + } + /** * Fetches the blob from the given url, must be CORS enabled accessible endpoint * @param inputs url of the asset @@ -101,4 +129,22 @@ export class Asset { return inputs.files.map(f => URL.createObjectURL(f)); } + /** + * Downloads a file with the given content, extension, and content type. + * @param inputs file name, content, extension, and content type + * @group download + * @shortname download file + */ + download(inputs: Inputs.Asset.DownloadDto): void { + let blob: Blob; + + if (typeof inputs.content === "string") { + blob = new Blob([inputs.content], { type: inputs.contentType }); + } else { + blob = inputs.content; + } + + this.assetManager.downloadFile(blob, inputs.fileName, inputs.extension, inputs.contentType); + } + } diff --git a/packages/dev/core/lib/api/inputs/asset-inputs.ts b/packages/dev/core/lib/api/inputs/asset-inputs.ts index 4528a076..b3b6b292 100644 --- a/packages/dev/core/lib/api/inputs/asset-inputs.ts +++ b/packages/dev/core/lib/api/inputs/asset-inputs.ts @@ -79,4 +79,32 @@ export namespace Asset { */ hidden = false; } + export class DownloadDto { + constructor(fileName?: string, content?: string | Blob, extension?: string, contentType?: string) { + if (fileName !== undefined) { this.fileName = fileName; } + if (content !== undefined) { this.content = content; } + if (extension !== undefined) { this.extension = extension; } + if (contentType !== undefined) { this.contentType = contentType; } + } + /** + * The file name for the downloaded file + * @default undefined + */ + fileName: string; + /** + * The content to download (string or Blob) + * @default undefined + */ + content: string | Blob; + /** + * The file extension (without dot) + * @default txt + */ + extension = "txt"; + /** + * The content type for the file + * @default text/plain + */ + contentType = "text/plain"; + } } diff --git a/packages/dev/core/lib/asset-manager.ts b/packages/dev/core/lib/asset-manager.ts index 883f42c9..57338fcd 100644 --- a/packages/dev/core/lib/asset-manager.ts +++ b/packages/dev/core/lib/asset-manager.ts @@ -6,4 +6,5 @@ export class AssetManager { getAsset: (fileName: string) => Promise; getLocalAsset: (fileName: string) => Promise; fetch: (url: string) => Promise; + downloadFile: (blob: Blob, fileName: string, extension: string, contentType: string) => void; } From 3c4dc7b699c43966763e160bb0e983624f4f0ea0 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Tue, 23 Dec 2025 14:35:12 +0200 Subject: [PATCH 35/38] implemented prompt preview interface --- packages/dev/core/lib/api/context.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/dev/core/lib/api/context.ts b/packages/dev/core/lib/api/context.ts index 561c3c1b..2ade2997 100644 --- a/packages/dev/core/lib/api/context.ts +++ b/packages/dev/core/lib/api/context.ts @@ -1,6 +1,21 @@ import { PrintSaveInterface } from "../models/print-save.model"; +export interface PreviewDataInterface { + data: any; + viewMode?: "data" | "schema" | "metadata"; + hidden?: boolean; +} + +export interface PreviewCSVInterface { + data: string | any[][]; + columnSeparator?: string; + rowSeparator?: string; + startRow?: number; + maxRows?: number; + hidden?: boolean; +} + export class ContextBase { blocklyWorkspace: any; verb: any; @@ -10,6 +25,8 @@ export class ContextBase { promptPrintSave: (prompt: PrintSaveInterface) => void; promptPrint: (prompt: PrintSaveInterface) => void; + promptPreviewData: (data: PreviewDataInterface) => void; + promptPreviewCSV: (data: PreviewCSVInterface) => void; rerenderScene: () => void; tolerance = 0.00001; From 64343ddd430d2a208da34c54a0db74fa63e2aa1b Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Tue, 23 Dec 2025 15:14:34 +0200 Subject: [PATCH 36/38] fixed comment --- packages/dev/core/lib/api/bitbybit/asset.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/core/lib/api/bitbybit/asset.ts b/packages/dev/core/lib/api/bitbybit/asset.ts index 8ef2285c..ae67bd2d 100644 --- a/packages/dev/core/lib/api/bitbybit/asset.ts +++ b/packages/dev/core/lib/api/bitbybit/asset.ts @@ -20,7 +20,7 @@ export class Asset { } /** - * Gets the text from asset file stored in your browser. + * Gets the text from asset file stored in your cloud account. * @param inputs asset name to get from project assets * @returns Text of asset * @group get From 8b203d84a5dd6e596d39b3673e581a0c190ff0e6 Mon Sep 17 00:00:00 2001 From: Matas Ubarevicius Date: Tue, 23 Dec 2025 21:57:08 +0200 Subject: [PATCH 37/38] v0.20.14 --- .../2024-11-08-updated-bitbybit-runners.md | 2 +- .../theme-app-extensions/bitbybit-viewer.md | 2 +- .../tutorials/bitbybit-viewer/settings.md | 2 +- .../getting-started/common-settings.md | 4 +- .../product-customizable-text.mdx | 2 +- .../product-laptop-holder.mdx | 2 +- .../videos-tutorials/product-palm-table.mdx | 2 +- .../common/base/color/color-usage-examples.md | 6 +- .../base/point/point-hex-grid-example.md | 6 +- .../base/point/point-spiral-examples.md | 6 +- .../common/base/text/text-usage-examples.md | 6 +- .../base/vector/vector-usage-examples.md | 6 +- docs/learn/code/common/draw/examples.mdx | 6 +- .../code/common/occt/booleans/operations.mdx | 18 +- .../occt/dimensions/angular-dimension.md | 12 +- .../occt/dimensions/linear-dimension.md | 12 +- .../common/occt/dimensions/pin-with-label.md | 12 +- .../occt/fillets/chamfer-circular-edges.mdx | 6 +- .../common/occt/fillets/chamfers-intro.mdx | 6 +- .../chamfers-var-radius-on-spec-edges.mdx | 6 +- .../common/occt/fillets/fillet-3d-wires.mdx | 6 +- .../common/occt/fillets/fillets-intro.mdx | 6 +- .../fillets/fillets-on-2d-wire-corners.mdx | 6 +- .../occt/fillets/fillets-on-2d-wires.mdx | 6 +- .../fillets-var-radius-on-spec-edges.mdx | 6 +- .../io/dxf-file-format-for-2d-drawings.md | 6 +- .../modeling/festive-decor/frost-flower.md | 2 +- .../modeling/festive-decor/star-ornament.md | 18 +- .../occt/modeling/festive-decor/tree.md | 6 +- .../hollow-shapes/hexagon-hive-flat.md | 6 +- .../modeling/hollow-shapes/hexagon-hive.md | 6 +- .../hollow-shapes/hexagon-holes-on-face.md | 6 +- .../hollow-shapes/rectangle-holes-on-face.md | 6 +- .../modeling/hollow-shapes/simple-hole.md | 18 +- .../hollow-shapes/simple-hollow-grids.md | 6 +- .../modeling/parametric-art/simple-flower.md | 8 +- .../common/occt/operations/advanced-loft.md | 6 +- .../code/common/occt/operations/extrusions.md | 12 +- .../occt/operations/offset-operations.md | 24 +- .../occt/operations/rotated-extrusions.md | 12 +- .../common/occt/operations/simple-loft.md | 6 +- .../common/occt/operations/thick-solids.md | 6 +- .../occt/operations/wire-offset-multiple.md | 6 +- .../shapes/compound/compounded-drawing.md | 6 +- .../code/common/occt/shapes/compound/intro.md | 18 +- .../occt/shapes/edge/edge-constraints.md | 30 +- .../common/occt/shapes/edge/edge-indexes.mdx | 6 +- .../occt/shapes/edge/edge-primitives.md | 48 +- .../occt/shapes/face/face-basic-primitives.md | 6 +- .../occt/shapes/face/face-from-points.md | 6 +- .../common/occt/shapes/face/face-from-wire.md | 6 +- .../occt/shapes/face/face-hex-grid-pattern.md | 6 +- .../common/occt/shapes/shells/intro-shells.md | 12 +- .../common/occt/shapes/solids/intro-solids.md | 6 +- .../shapes/solids/volume-and-surface-area.md | 6 +- .../occt/shapes/wire/wire-basic-primitives.md | 6 +- .../occt/shapes/wire/wire-bezier-weights.md | 6 +- .../occt/shapes/wire/wire-from-edges.md | 6 +- .../wire/wire-hexagons-advanced-pattern.md | 6 +- .../occt/shapes/wire/wire-hexagons-in-grid.md | 6 +- .../occt/shapes/wire/wire-shape-primitives.md | 24 +- .../occt/shapes/wire/wire-via-points.md | 6 +- .../basics/assets/local/gltf.mdx | 6 +- .../basics/assets/local/step.mdx | 6 +- .../getting-started/blockly/hello-world.mdx | 4 +- .../blockly/parametric-cube.mdx | 2 +- .../getting-started/rete/hello-world.mdx | 6 +- .../getting-started/rete/parametric-cube.mdx | 4 +- .../typescript/hello-world.mdx | 4 +- .../typescript/how-to-code-in-monaco.md | 2 +- .../typescript/parametric-cube.mdx | 2 +- .../viewer-editor/basics/getting-started.md | 2 +- .../viewer-editor/basics/overview.md | 2 +- .../babylonjs/start-with-babylon-js.md | 2 +- .../threejs/start-with-three-js.md | 2 +- docs/learn/runners/intro-blockly.mdx | 4 +- docs/learn/runners/intro-rete.mdx | 2 +- docs/learn/runners/intro-typescript.mdx | 2 +- docs/learn/runners/intro.mdx | 4 +- .../live-examples/configurable-cad-part.mdx | 2 +- .../live-examples/static-3d-model-script.mdx | 2 +- .../runners/table-configurator-blockly.mdx | 4 +- .../learn/runners/table-configurator-rete.mdx | 2 +- .../runners/table-configurator-typescript.mdx | 2 +- .../babylonjs/laptop-holder/package-lock.json | 1486 ++- .../babylonjs/laptop-holder/package.json | 2 +- .../angular/threejs/simple/package-lock.json | 1410 ++- examples/angular/threejs/simple/package.json | 2 +- .../vite-basic-example/package-lock.json | 2544 +++-- .../threejs/vite-basic-example/package.json | 4 +- .../src/workers/manifold.worker.ts | 2 +- .../nextjs/babylonjs/simple/package-lock.json | 891 +- examples/nextjs/babylonjs/simple/package.json | 2 +- examples/node/basic/package-lock.json | 16 +- examples/node/basic/package.json | 2 +- examples/node/express-app/bitbybit.ts | 2 +- examples/node/express-app/package-lock.json | 1354 ++- examples/node/express-app/package.json | 2 +- .../nuxt/babylonjs/basic/package-lock.json | 886 +- examples/nuxt/babylonjs/basic/package.json | 2 +- examples/package.json | 2 +- .../react/babylonjs/cup/package-lock.json | 1435 ++- examples/react/babylonjs/cup/package.json | 2 +- .../babylonjs/laptop-holder/package-lock.json | 1509 ++- .../babylonjs/laptop-holder/package.json | 2 +- examples/react/threejs/vase/package-lock.json | 860 +- examples/react/threejs/vase/package.json | 4 +- .../babylon/full/inline-include/index.html | 2 +- .../full/simple-all-kernels/index.html | 118 + .../threejs/full/inline-include/index.html | 2 +- .../threejs/lite/external-scene/index.html | 4 +- .../runner/threejs/lite/simple/index.html | 4 +- .../threejs/lite/threejs-logo/index.html | 4 +- .../hex-house-concept/package-lock.json | 1492 ++- .../babylonjs/hex-house-concept/package.json | 4 +- .../src/workers/manifold.worker.ts | 2 +- .../babylonjs/hex-shell/package-lock.json | 1492 ++- .../vite/babylonjs/hex-shell/package.json | 4 +- .../hex-shell/src/workers/manifold.worker.ts | 2 +- .../starter-template/package-lock.json | 1492 ++- .../babylonjs/starter-template/package.json | 4 +- .../src/workers/manifold.worker.ts | 2 +- examples/vite/threejs/cup/package-lock.json | 889 +- examples/vite/threejs/cup/package.json | 4 +- .../cup/src/workers/manifold.worker.ts | 2 +- .../hex-house-concept/package-lock.json | 1416 ++- .../threejs/hex-house-concept/package.json | 4 +- .../src/workers/manifold.worker.ts | 2 +- .../vite/threejs/hex-shell/package-lock.json | 1416 ++- examples/vite/threejs/hex-shell/package.json | 4 +- .../hex-shell/src/workers/manifold.worker.ts | 2 +- .../starter-template/package-lock.json | 1416 ++- .../threejs/starter-template/package.json | 4 +- .../src/workers/manifold.worker.ts | 2 +- examples/webpack/threejs/package-lock.json | 872 +- examples/webpack/threejs/package.json | 4 +- .../threejs/src/html/cup-three/index.html | 2 +- .../threejs/src/html/homepage/index.html | 2 +- .../src/html/manifold-sliced-mesh/index.html | 2 +- .../threejs/src/html/patterns/index.html | 2 +- .../webpack/threejs/src/manifold.worker.ts | 2 +- examples/webpack/threejs/webpack.config.js | 4 +- languages/ar.json | 2469 ++--- languages/bn.json | 2619 ++--- languages/de.json | 2667 ++--- languages/en.json | 2667 ++--- languages/es.json | 2669 ++--- languages/et.json | 5867 +++++++++++ languages/fi.json | 5867 +++++++++++ languages/fr.json | 2669 ++--- languages/hi.json | 8781 +++++++++-------- languages/id.json | 2667 ++--- languages/lt.json | 2667 ++--- languages/lv.json | 5867 +++++++++++ languages/pl.json | 5867 +++++++++++ languages/pt.json | 2671 ++--- languages/ru.json | 2559 ++--- languages/sv.json | 5867 +++++++++++ languages/uk.json | 2667 ++--- languages/zh-hans.json | 2313 +++-- package.json | 2 +- packages/dev/babylonjs/package-lock.json | 1417 ++- packages/dev/babylonjs/package.json | 14 +- .../dev/base/lib/api/GlobalCDNProvider.ts | 2 +- packages/dev/base/package-lock.json | 4 +- packages/dev/base/package.json | 2 +- packages/dev/core/package-lock.json | 1323 ++- packages/dev/core/package.json | 10 +- packages/dev/jscad-worker/package-lock.json | 34 +- packages/dev/jscad-worker/package.json | 4 +- packages/dev/jscad/package-lock.json | 18 +- packages/dev/jscad/package.json | 4 +- .../dev/manifold-worker/package-lock.json | 1224 ++- packages/dev/manifold-worker/package.json | 4 +- packages/dev/manifold/package-lock.json | 4 +- packages/dev/manifold/package.json | 2 +- packages/dev/occt-worker/package-lock.json | 34 +- packages/dev/occt-worker/package.json | 4 +- packages/dev/occt/package-lock.json | 18 +- packages/dev/occt/package.json | 4 +- packages/dev/threejs/package-lock.json | 1379 ++- packages/dev/threejs/package.json | 8 +- 182 files changed, 76319 insertions(+), 22314 deletions(-) create mode 100644 examples/runner/babylon/full/simple-all-kernels/index.html create mode 100644 languages/et.json create mode 100644 languages/fi.json create mode 100644 languages/lv.json create mode 100644 languages/pl.json create mode 100644 languages/sv.json diff --git a/docs/blog/2024-11-08-updated-bitbybit-runners.md b/docs/blog/2024-11-08-updated-bitbybit-runners.md index df9ed1e4..55632a84 100644 --- a/docs/blog/2024-11-08-updated-bitbybit-runners.md +++ b/docs/blog/2024-11-08-updated-bitbybit-runners.md @@ -121,7 +121,7 @@ We are serving the Bitbybit Runners from the **JSDelivr CDN**. You can include t ``` -**Note:** You should replace `` with an actual version number (e.g., `0.20.13`). You can find all the official versions of Bitbybit.dev here: +**Note:** You should replace `` with an actual version number (e.g., `0.20.14`). You can find all the official versions of Bitbybit.dev here: ➡️ **[Bitbybit.dev GitHub Releases](https://github.com/bitbybit-dev/bitbybit/releases)** ### Examples of the Runners diff --git a/docs/learn/3d-bits/theme-app-extensions/bitbybit-viewer.md b/docs/learn/3d-bits/theme-app-extensions/bitbybit-viewer.md index 9360d927..ab888e1c 100644 --- a/docs/learn/3d-bits/theme-app-extensions/bitbybit-viewer.md +++ b/docs/learn/3d-bits/theme-app-extensions/bitbybit-viewer.md @@ -110,7 +110,7 @@ Save your JSON configurator as a file, upload it to Shopify CDN as a file. Copy While our Viewer Editor is the recommended way to create and manage the Scene Config JSON, you can also edit the JSON directly using any text editor. For a better editing experience with features like syntax highlighting and autocompletion (intellisense), we provide a JSON schema. -* **JSON Schema:** You can find the schema [here](https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.13.json). (Note: This schema link points to version `0.20.13`. The schema may be updated in the future, so ensure you refer to the latest version compatible with your "3D Bits" app version.) +* **JSON Schema:** You can find the schema [here](https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.14.json). (Note: This schema link points to version `0.20.14`. The schema may be updated in the future, so ensure you refer to the latest version compatible with your "3D Bits" app version.) Many modern code editors (like VS Code) can use this schema to provide validation and autocompletion as you edit the JSON. ## Video Tutorial: BITBYBIT VIEWER Block Setup diff --git a/docs/learn/3d-bits/tutorials/bitbybit-viewer/settings.md b/docs/learn/3d-bits/tutorials/bitbybit-viewer/settings.md index 56a4a98c..b3f53de2 100644 --- a/docs/learn/3d-bits/tutorials/bitbybit-viewer/settings.md +++ b/docs/learn/3d-bits/tutorials/bitbybit-viewer/settings.md @@ -203,7 +203,7 @@ The system automatically detects if you're providing a URL (starting with `http: - For complex configurations, use a URL to an external JSON file - it keeps your theme settings cleaner and makes updates easier - Use the Viewer Editor to generate valid configurations - Test your scene configuration thoroughly before going live -- The scene configuration follows a [JSON schema](https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.13.json) that defines all available options +- The scene configuration follows a [JSON schema](https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.14.json) that defines all available options ::: --- diff --git a/docs/learn/3d-bits/tutorials/getting-started/common-settings.md b/docs/learn/3d-bits/tutorials/getting-started/common-settings.md index d059f3a9..2143c3ec 100644 --- a/docs/learn/3d-bits/tutorials/getting-started/common-settings.md +++ b/docs/learn/3d-bits/tutorials/getting-started/common-settings.md @@ -121,7 +121,7 @@ These settings are specific to RUNNER and APPS blocks: ### Runner CDN Link **Available in:** VIEWER, RUNNER -**Default:** `https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.13/runner/bitbybit-runner-babylonjs.js` +**Default:** `https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.14/runner/bitbybit-runner-babylonjs.js` Specifies which version of the Bitbybit runner library to use. The runner is the core engine that loads and renders 3D content in your browser. @@ -173,7 +173,7 @@ The URL follows this pattern: https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@{VERSION}/runner/bitbybit-runner-babylonjs.js ``` -Replace `{VERSION}` with the desired version number (e.g., `0.20.13`). +Replace `{VERSION}` with the desired version number (e.g., `0.20.14`). **Self-Hosting on Shopify CDN:** diff --git a/docs/learn/3d-bits/tutorials/videos-tutorials/product-customizable-text.mdx b/docs/learn/3d-bits/tutorials/videos-tutorials/product-customizable-text.mdx index 50184e38..b8aa2bbd 100644 --- a/docs/learn/3d-bits/tutorials/videos-tutorials/product-customizable-text.mdx +++ b/docs/learn/3d-bits/tutorials/videos-tutorials/product-customizable-text.mdx @@ -153,7 +153,7 @@ To save you time, here is the embedded Bitbybit Rete script used in this tutoria diff --git a/docs/learn/3d-bits/tutorials/videos-tutorials/product-laptop-holder.mdx b/docs/learn/3d-bits/tutorials/videos-tutorials/product-laptop-holder.mdx index bb551a66..91134c04 100644 --- a/docs/learn/3d-bits/tutorials/videos-tutorials/product-laptop-holder.mdx +++ b/docs/learn/3d-bits/tutorials/videos-tutorials/product-laptop-holder.mdx @@ -63,7 +63,7 @@ To save you time and provide a starting point, here is the embedded Bitbybit Typ {\n\n laptops.forEach(laptop => {\n laptop.center = [0, laptop.height / 2 + laptopLiftedHeight, 0] as Bit.Inputs.Base.Point3;\n });\n\n let laptopFillets = [];\n let totalDistance = 0;\n let previousLaptopLength = 0;\n\n laptops.forEach(async (laptop, index) => {\n totalDistance += distanceBetweenLaptops + laptop.length / 2 + previousLaptopLength / 2;\n previousLaptopLength = laptop.length;\n laptop.center[2] = totalDistance;\n const laptopBaseModel = await bitbybit.occt.shapes.solid.createBox({\n width: laptop.width,\n length: laptop.length,\n height: laptop.height,\n center: laptop.center\n });\n const laptopFillet = await bitbybit.occt.fillets.filletEdges({ shape: laptopBaseModel, indexes: undefined, radius: 0.2 });\n laptopFillets.push(laptopFillet);\n\n const laptopVisModel = await bitbybit.occt.shapes.solid.createBox({\n width: laptop.width,\n length: laptop.length - 0.01,\n height: laptop.height,\n center: laptop.center\n });\n const laptopVisFillet = await bitbybit.occt.fillets.filletEdges({ shape: laptopVisModel, indexes: undefined, radius: 0.2 });\n laptopFillets.push(laptopFillet);\n\n const di = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n\n di.faceOpacity = 0.2;\n di.edgeWidth = 5;\n di.edgeOpacity = 0.6;\n di.edgeColour = whiteColor;\n di.faceColour = whiteColor;\n const laptopFilletMesh = await bitbybit.draw.drawAnyAsync({ entity: laptopVisFillet, options: di });\n laptopsFilletsMesh.push(laptopFilletMesh);\n })\n\n const polygonWire = await bitbybit.occt.shapes.wire.createPolygonWire({\n points: controlPoints\n });\n const extrusion = await bitbybit.occt.operations.extrude({\n shape: polygonWire, direction: [0, 0, totalDistance += distanceBetweenLaptops + previousLaptopLength / 2]\n });\n const laptopStandFillet = await bitbybit.occt.fillets.filletEdges({ shape: extrusion, indexes: undefined, radius: 1 });\n const laptopStandThick = await bitbybit.occt.operations.makeThickSolidSimple({ shape: laptopStandFillet, offset: -0.5 });\n\n laptopStand = await bitbybit.occt.booleans.difference({ shape: laptopStandThick, shapes: laptopFillets, keepEdges: false });\n const li = new Bit.Inputs.OCCT.DrawShapeDto(laptopStand);\n li.faceOpacity = 1;\n if (flipColor) {\n li.faceColour = \"#0000ff\";\n li.edgeColour = whiteColor;\n } else {\n li.faceColour = holderColor;\n li.edgeColour = whiteColor;\n }\n li.edgeWidth = 5;\n laptopStandMesh = await bitbybit.draw.drawAnyAsync({ entity: laptopStand, options: li });\n const laptopsMeshes = await Promise.all(laptopsFilletsMesh);\n return [laptopStandMesh, ...laptopsMeshes];\n }\n\n const meshes = await renderLaptops(laptops);\n return { meshes };\n}\n\nclass Laptop {\n width: number;\n length: number;\n height: number;\n center?: Bit.Inputs.Base.Point3;\n}\n\nBit.setBitbybitRunnerResult(start());","version":"0.20.13","type":"typescript"}} + script={{"script":"Bit.mockBitbybitRunnerInputs({\n \"Laptop Type\": \"MacBook Pro 16\",\n \"Number Laptops\": \"3\",\n \"Color\": \"Black\",\n});\nconst inputs = Bit.getBitbybitRunnerInputs();\n\nconst laptops: Laptop[] = []\n\nlet laptop: Laptop;\n\nswitch (inputs[\"Laptop Type\"]) {\n case \"MacBook Pro 16\":\n laptop = {\n length: 1.63,\n width: 35.8,\n height: 24.6\n };\n break;\n case \"MacBook Pro 14\":\n laptop = {\n length: 1.57,\n width: 31.3,\n height: 22.2\n }\n break;\n case \"MacBook Air\":\n laptop = {\n length: 1.2,\n width: 30.5,\n height: 21.6\n }\n break;\n default:\n break;\n}\n\nlet flipColor = false;\nswitch (inputs[\"Color\"]) {\n case \"Blue\":\n flipColor = true;\n break;\n default:\n break;\n}\n\nconsole.log(\"laptop \", laptop);\n\nconst nrLaptops = +inputs[\"Number Laptops\"];\n\nfor (let i = 0; i < nrLaptops; i++) {\n laptops.push({ ...laptop });\n}\n\nconst whiteColor = \"#ffffff\";\nconst holderColor = \"#333333\";\n\nconst laptopLiftedHeight = 3;\nconst distanceBetweenLaptops = 1.7;\nconst exportSTEP = false;\n\nbitbybit.babylon.scene.backgroundColour({ colour: \"#bbbbbb\" });\n\nconst pointLightConf = new Bit.Inputs.BabylonScene.PointLightDto();\npointLightConf.position = [-15, 20, -5];\npointLightConf.intensity = 8000;\npointLightConf.diffuse = \"#3333ff\";\npointLightConf.radius = 0;\nbitbybit.babylon.scene.drawPointLight(pointLightConf);\n\nconst controlPoints = [\n [-12.5, 0, 0],\n [-8, 13, 0],\n [-4, 11, 0],\n [-2, 6, 0],\n [2, 6, 0],\n [4, 14, 0],\n [8, 17, 0],\n [12.5, 0, 0]\n] as Bit.Inputs.Base.Point3[];\n\nlet laptopStand;\nlet laptopStandMesh;\n\nconst laptopsFilletsMesh = [];\n\nasync function start() {\n const ground = await bitbybit.occt.shapes.face.createCircleFace({ center: [0, 0, 0], direction: [0, 1, 0], radius: 75, });\n const groundOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n groundOptions.faceColour = whiteColor;\n groundOptions.drawEdges = false;\n await bitbybit.draw.drawAnyAsync({ entity: ground, options: groundOptions });\n\n const renderLaptops = async (laptops) => {\n\n laptops.forEach(laptop => {\n laptop.center = [0, laptop.height / 2 + laptopLiftedHeight, 0] as Bit.Inputs.Base.Point3;\n });\n\n let laptopFillets = [];\n let totalDistance = 0;\n let previousLaptopLength = 0;\n\n laptops.forEach(async (laptop, index) => {\n totalDistance += distanceBetweenLaptops + laptop.length / 2 + previousLaptopLength / 2;\n previousLaptopLength = laptop.length;\n laptop.center[2] = totalDistance;\n const laptopBaseModel = await bitbybit.occt.shapes.solid.createBox({\n width: laptop.width,\n length: laptop.length,\n height: laptop.height,\n center: laptop.center\n });\n const laptopFillet = await bitbybit.occt.fillets.filletEdges({ shape: laptopBaseModel, indexes: undefined, radius: 0.2 });\n laptopFillets.push(laptopFillet);\n\n const laptopVisModel = await bitbybit.occt.shapes.solid.createBox({\n width: laptop.width,\n length: laptop.length - 0.01,\n height: laptop.height,\n center: laptop.center\n });\n const laptopVisFillet = await bitbybit.occt.fillets.filletEdges({ shape: laptopVisModel, indexes: undefined, radius: 0.2 });\n laptopFillets.push(laptopFillet);\n\n const di = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n\n di.faceOpacity = 0.2;\n di.edgeWidth = 5;\n di.edgeOpacity = 0.6;\n di.edgeColour = whiteColor;\n di.faceColour = whiteColor;\n const laptopFilletMesh = await bitbybit.draw.drawAnyAsync({ entity: laptopVisFillet, options: di });\n laptopsFilletsMesh.push(laptopFilletMesh);\n })\n\n const polygonWire = await bitbybit.occt.shapes.wire.createPolygonWire({\n points: controlPoints\n });\n const extrusion = await bitbybit.occt.operations.extrude({\n shape: polygonWire, direction: [0, 0, totalDistance += distanceBetweenLaptops + previousLaptopLength / 2]\n });\n const laptopStandFillet = await bitbybit.occt.fillets.filletEdges({ shape: extrusion, indexes: undefined, radius: 1 });\n const laptopStandThick = await bitbybit.occt.operations.makeThickSolidSimple({ shape: laptopStandFillet, offset: -0.5 });\n\n laptopStand = await bitbybit.occt.booleans.difference({ shape: laptopStandThick, shapes: laptopFillets, keepEdges: false });\n const li = new Bit.Inputs.OCCT.DrawShapeDto(laptopStand);\n li.faceOpacity = 1;\n if (flipColor) {\n li.faceColour = \"#0000ff\";\n li.edgeColour = whiteColor;\n } else {\n li.faceColour = holderColor;\n li.edgeColour = whiteColor;\n }\n li.edgeWidth = 5;\n laptopStandMesh = await bitbybit.draw.drawAnyAsync({ entity: laptopStand, options: li });\n const laptopsMeshes = await Promise.all(laptopsFilletsMesh);\n return [laptopStandMesh, ...laptopsMeshes];\n }\n\n const meshes = await renderLaptops(laptops);\n return { meshes };\n}\n\nclass Laptop {\n width: number;\n length: number;\n height: number;\n center?: Bit.Inputs.Base.Point3;\n}\n\nBit.setBitbybitRunnerResult(start());","version":"0.20.14","type":"typescript"}} title="Bitbybit Rete Editor - 3D Laptop Holder" description="3D Laptop holder configurator" /> diff --git a/docs/learn/3d-bits/tutorials/videos-tutorials/product-palm-table.mdx b/docs/learn/3d-bits/tutorials/videos-tutorials/product-palm-table.mdx index e2225642..221fad79 100644 --- a/docs/learn/3d-bits/tutorials/videos-tutorials/product-palm-table.mdx +++ b/docs/learn/3d-bits/tutorials/videos-tutorials/product-palm-table.mdx @@ -157,7 +157,7 @@ To save you time, here is the embedded Rete script used in this tutorial for the diff --git a/docs/learn/code/common/base/color/color-usage-examples.md b/docs/learn/code/common/base/color/color-usage-examples.md index 12cd7403..3378a053 100644 --- a/docs/learn/code/common/base/color/color-usage-examples.md +++ b/docs/learn/code/common/base/color/color-usage-examples.md @@ -48,21 +48,21 @@ Click through the tabs below to see the implementation. You can interact with th colorParamfaceColoredgeColorcolorParam0faceColorcolorParam255colorParam0255edgeColor#0000ff6000TRUE0.01TRUEfaceColorTRUEedgeColor10","version":"0.20.13","type":"blockly"}} + script={{"script":"colorParamfaceColoredgeColorcolorParam0faceColorcolorParam255colorParam0255edgeColor#0000ff6000TRUE0.01TRUEfaceColorTRUEedgeColor10","version":"0.20.14","type":"blockly"}} title="Color Usage Example" /> {\n\n const colorParam = 55;\n const rgbToHexOptions = new Bit.Inputs.Color.RGBMinMaxDto();\n rgbToHexOptions.r = colorParam;\n rgbToHexOptions.b = colorParam;\n const faceColor = bitbybit.color.rgbToHex(rgbToHexOptions);\n\n // This might look strange as you could just assign the string to edgeColor directly, \n // but this identity function is nice to have in visual prgramming editors - check Rete & Blockly\n // examples\n \n const edgeColor = bitbybit.color.hexColor({ color: \"#ff0000\" });\n\n const cubeOptions = new Bit.Inputs.OCCT.CubeDto();\n cubeOptions.size = 6;\n const cube = await bitbybit.occt.shapes.solid.createCube(cubeOptions);\n\n const drawOpt = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOpt.faceColour = faceColor;\n drawOpt.edgeColour = edgeColor;\n drawOpt.edgeWidth = 10;\n bitbybit.draw.drawAnyAsync({ entity: cube, options: drawOpt });\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const colorParam = 55;\n const rgbToHexOptions = new Bit.Inputs.Color.RGBMinMaxDto();\n rgbToHexOptions.r = colorParam;\n rgbToHexOptions.b = colorParam;\n const faceColor = bitbybit.color.rgbToHex(rgbToHexOptions);\n\n // This might look strange as you could just assign the string to edgeColor directly, \n // but this identity function is nice to have in visual prgramming editors - check Rete & Blockly\n // examples\n \n const edgeColor = bitbybit.color.hexColor({ color: \"#ff0000\" });\n\n const cubeOptions = new Bit.Inputs.OCCT.CubeDto();\n cubeOptions.size = 6;\n const cube = await bitbybit.occt.shapes.solid.createCube(cubeOptions);\n\n const drawOpt = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOpt.faceColour = faceColor;\n drawOpt.edgeColour = edgeColor;\n drawOpt.edgeWidth = 10;\n bitbybit.draw.drawAnyAsync({ entity: cube, options: drawOpt });\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Color Usage Example" /> diff --git a/docs/learn/code/common/base/point/point-hex-grid-example.md b/docs/learn/code/common/base/point/point-hex-grid-example.md index f8ee4cb1..6af826a0 100644 --- a/docs/learn/code/common/base/point/point-hex-grid-example.md +++ b/docs/learn/code/common/base/point/point-hex-grid-example.md @@ -55,21 +55,21 @@ Click through the tabs below to see the implementation. Each example will genera hexagonspointshexCornershexPolylinesihexagons20101010FALSEFALSEFALSEFALSEFALSETRUEFALSEpointshexagonscentershexCornershexagonshexagonshexPolylinesihexCornersINSERTLASThexPolylinesiTRUEpointshexPolylines","version":"0.20.13","type":"blockly"}} + script={{"script":"hexagonspointshexCornershexPolylinesihexagons20101010FALSEFALSEFALSEFALSEFALSETRUEFALSEpointshexagonscentershexCornershexagonshexagonshexPolylinesihexCornersINSERTLASThexPolylinesiTRUEpointshexPolylines","version":"0.20.14","type":"blockly"}} title="Point Hex Grid Example" /> {\n\n // 1. Configure the hexagonal grid options\n const hexOptions = new Bit.Inputs.Point.HexGridScaledToFitDto();\n // Set options different from defaults. \n // TypeScript IntelliSense (e.g., typing \"hexOptions.\") will show all available parameters.\n hexOptions.width = 20;\n hexOptions.height = 10;\n hexOptions.nrHexagonsInWidth = 10;\n hexOptions.nrHexagonsInHeight = 10;\n hexOptions.centerGrid = true; // Center the entire grid at the world origin [0,0,0]\n // Example: hexOptions.flatTop = true; // To get flat-topped hexagons\n // Example: hexOptions.pointsOnGround = true; // To project to XZ plane if original is XY\n\n // 2. Generate the hex grid data\n // This function returns an object like: \n // { centers: Point3[], hexagons: Point3[][], shortestDistEdge, longestDistEdge, maxFilletRadius }\n const hexResult = bitbybit.point.hexGridScaledToFit(hexOptions);\n\n // 3. Create polylines for each hexagon's outline\n // hexResult.hexagons is a list of lists (e.g., [[v1,v2..v6 for hex1], [v1,v2..v6 for hex2], ...])\n // We .map() over this list to create a Polyline object for each hexagon.\n const polylines = hexResult.hexagons.map(singleHexagonCornerPoints => {\n const polylineOptions = new Bit.Inputs.Polyline.PolylineCreateDto();\n polylineOptions.points = singleHexagonCornerPoints; // The 6 corner points\n polylineOptions.isClosed = true; // Ensure the polyline forms a closed loop\n return bitbybit.polyline.create(polylineOptions);\n }) as Bit.Inputs.Base.Polyline3[]; // Type assertion: the result is an array of Polyline3 objects\n\n // 4. Draw the center points of the hexagons\n // hexResult.centers is a list of 3D points: [[cx1,cy1,cz1], [cx2,cy2,cz2], ...]\n bitbybit.draw.drawAnyAsync({ entity: hexResult.centers });\n\n // 5. Draw the polylines representing the hexagon outlines\n bitbybit.draw.drawAnyAsync({ entity: polylines });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = () => {\n\n // 1. Configure the hexagonal grid options\n const hexOptions = new Bit.Inputs.Point.HexGridScaledToFitDto();\n // Set options different from defaults. \n // TypeScript IntelliSense (e.g., typing \"hexOptions.\") will show all available parameters.\n hexOptions.width = 20;\n hexOptions.height = 10;\n hexOptions.nrHexagonsInWidth = 10;\n hexOptions.nrHexagonsInHeight = 10;\n hexOptions.centerGrid = true; // Center the entire grid at the world origin [0,0,0]\n // Example: hexOptions.flatTop = true; // To get flat-topped hexagons\n // Example: hexOptions.pointsOnGround = true; // To project to XZ plane if original is XY\n\n // 2. Generate the hex grid data\n // This function returns an object like: \n // { centers: Point3[], hexagons: Point3[][], shortestDistEdge, longestDistEdge, maxFilletRadius }\n const hexResult = bitbybit.point.hexGridScaledToFit(hexOptions);\n\n // 3. Create polylines for each hexagon's outline\n // hexResult.hexagons is a list of lists (e.g., [[v1,v2..v6 for hex1], [v1,v2..v6 for hex2], ...])\n // We .map() over this list to create a Polyline object for each hexagon.\n const polylines = hexResult.hexagons.map(singleHexagonCornerPoints => {\n const polylineOptions = new Bit.Inputs.Polyline.PolylineCreateDto();\n polylineOptions.points = singleHexagonCornerPoints; // The 6 corner points\n polylineOptions.isClosed = true; // Ensure the polyline forms a closed loop\n return bitbybit.polyline.create(polylineOptions);\n }) as Bit.Inputs.Base.Polyline3[]; // Type assertion: the result is an array of Polyline3 objects\n\n // 4. Draw the center points of the hexagons\n // hexResult.centers is a list of 3D points: [[cx1,cy1,cz1], [cx2,cy2,cz2], ...]\n bitbybit.draw.drawAnyAsync({ entity: hexResult.centers });\n\n // 5. Draw the polylines representing the hexagon outlines\n bitbybit.draw.drawAnyAsync({ entity: polylines });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Point Hex Grid Example" /> diff --git a/docs/learn/code/common/base/point/point-spiral-examples.md b/docs/learn/code/common/base/point/point-spiral-examples.md index 1c5acc66..40d1d89e 100644 --- a/docs/learn/code/common/base/point/point-spiral-examples.md +++ b/docs/learn/code/common/base/point/point-spiral-examples.md @@ -45,21 +45,21 @@ Click through the tabs below to see the implementation. Each example will genera 0.9300361","version":"0.20.13","type":"blockly"}} + script={{"script":"0.9300361","version":"0.20.14","type":"blockly"}} title="Point Spiral Example" /> {\n\n // 1. Configure the spiral parameters\n const spiralOptions = new Bit.Inputs.Point.SpiralDto();\n spiralOptions.numberPoints = 300;\n spiralOptions.radius = 6; // Overall extent of the spiral; default is 1\n spiralOptions.widening = 3; // Controls how tight the spiral is; default is 10\n spiralOptions.phi = 0.9; // Constant influencing the spiral pattern; default relates to Golden Angle\n spiralOptions.factor = 1; // General scaling factor; default is 1\n\n // 2. Generate the list of points forming the spiral\n // The bitbybit.point.spiral() function returns an array of 3D points.\n const points = bitbybit.point.spiral(spiralOptions);\n\n // 3. Draw the generated points in the scene\n // The drawAnyAsync function can take an array of points and will render them.\n bitbybit.draw.drawAnyAsync({ entity: points });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = () => {\n\n // 1. Configure the spiral parameters\n const spiralOptions = new Bit.Inputs.Point.SpiralDto();\n spiralOptions.numberPoints = 300;\n spiralOptions.radius = 6; // Overall extent of the spiral; default is 1\n spiralOptions.widening = 3; // Controls how tight the spiral is; default is 10\n spiralOptions.phi = 0.9; // Constant influencing the spiral pattern; default relates to Golden Angle\n spiralOptions.factor = 1; // General scaling factor; default is 1\n\n // 2. Generate the list of points forming the spiral\n // The bitbybit.point.spiral() function returns an array of 3D points.\n const points = bitbybit.point.spiral(spiralOptions);\n\n // 3. Draw the generated points in the scene\n // The drawAnyAsync function can take an array of points and will render them.\n bitbybit.draw.drawAnyAsync({ entity: points });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Point Spiral Example" /> diff --git a/docs/learn/code/common/base/text/text-usage-examples.md b/docs/learn/code/common/base/text/text-usage-examples.md index d142054c..ad41e6ad 100644 --- a/docs/learn/code/common/base/text/text-usage-examples.md +++ b/docs/learn/code/common/base/text/text-usage-examples.md @@ -53,21 +53,21 @@ Click through the tabs below to see the implementation. Each example will create namewordwordListnameJohnwordawesomewordListnamewordHi {0}, you are {1}!wordList'Roboto''Regular'20.2180000010'centerMiddle'","version":"0.20.13","type":"blockly"}} + script={{"script":"namewordwordListnameJohnwordawesomewordListnamewordHi {0}, you are {1}!wordList'Roboto''Regular'20.2180000010'centerMiddle'","version":"0.20.14","type":"blockly"}} title="Text Formatting And 3D Fonts" /> {\n const name = \"John\";\n const word = \"awesome\";\n\n const formatOpt = new Bit.Inputs.Text.TextFormatDto();\n formatOpt.text = \"Hi {0}, you are {1}!\";\n formatOpt.values = [name, word];\n const formattedText = bitbybit.text.format(formatOpt);\n\n const text3dOptions = new Bit.Advanced.Text3D.Text3DDto();\n text3dOptions.text = formattedText;\n text3dOptions.rotation = 180;\n text3dOptions.fontSize = 2;\n const text3d = await bitbybit.advanced.text3d.create(text3dOptions);\n bitbybit.draw.drawAnyAsync({ entity: text3d });\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const name = \"John\";\n const word = \"awesome\";\n\n const formatOpt = new Bit.Inputs.Text.TextFormatDto();\n formatOpt.text = \"Hi {0}, you are {1}!\";\n formatOpt.values = [name, word];\n const formattedText = bitbybit.text.format(formatOpt);\n\n const text3dOptions = new Bit.Advanced.Text3D.Text3DDto();\n text3dOptions.text = formattedText;\n text3dOptions.rotation = 180;\n text3dOptions.fontSize = 2;\n const text3d = await bitbybit.advanced.text3d.create(text3dOptions);\n bitbybit.draw.drawAnyAsync({ entity: text3d });\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Text Formatting And 3D Fonts" /> diff --git a/docs/learn/code/common/base/vector/vector-usage-examples.md b/docs/learn/code/common/base/vector/vector-usage-examples.md index 72f5233b..89fb25ae 100644 --- a/docs/learn/code/common/base/vector/vector-usage-examples.md +++ b/docs/learn/code/common/base/vector/vector-usage-examples.md @@ -60,21 +60,21 @@ Click through the tabs below to see the implementation in Rete, Blockly, and Typ spanItemsspanEaseItemsvectorsj40040010100.450.50.5FALSE#ffffff#ffffffspanItems0.205spanEaseItemsspanItems05'easeInSine'FALSEvectorsj1spanItems1INSERTLASTvectorsGETFROM_STARTspanItemsjGETFROM_STARTspanEaseItemsj0vectorsvectors","version":"0.20.13","type":"blockly"}} + script={{"script":"spanItemsspanEaseItemsvectorsj40040010100.450.50.5FALSE#ffffff#ffffffspanItems0.205spanEaseItemsspanItems05'easeInSine'FALSEvectorsj1spanItems1INSERTLASTvectorsGETFROM_STARTspanItemsjGETFROM_STARTspanEaseItemsj0vectorsvectors","version":"0.20.14","type":"blockly"}} title="Vector Span & Ease In Combination" /> {\n\n const spanOptions = new Bit.Inputs.Vector.SpanDto();\n spanOptions.step = 0.2;\n spanOptions.min = 0;\n spanOptions.max = 5;\n const spanItems = bitbybit.vector.span(spanOptions);\n\n const spanEaseOptions = new Bit.Inputs.Vector.SpanEaseItemsDto();\n spanEaseOptions.ease = Bit.Inputs.Math.easeEnum.easeInSine;\n spanEaseOptions.min = 0;\n spanEaseOptions.max = 5;\n spanEaseOptions.nrItems = spanItems.length;\n const spanEaseItems = bitbybit.vector.spanEaseItems(spanEaseOptions);\n\n const vectors = spanItems.map((s, index) => [s, spanEaseItems[index], 0]) as Bit.Inputs.Base.Vector3[];\n\n bitbybit.draw.drawGridMesh(new Bit.Inputs.Draw.SceneDrawGridMeshDto());\n bitbybit.draw.drawAnyAsync({ entity: vectors });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = () => {\n\n const spanOptions = new Bit.Inputs.Vector.SpanDto();\n spanOptions.step = 0.2;\n spanOptions.min = 0;\n spanOptions.max = 5;\n const spanItems = bitbybit.vector.span(spanOptions);\n\n const spanEaseOptions = new Bit.Inputs.Vector.SpanEaseItemsDto();\n spanEaseOptions.ease = Bit.Inputs.Math.easeEnum.easeInSine;\n spanEaseOptions.min = 0;\n spanEaseOptions.max = 5;\n spanEaseOptions.nrItems = spanItems.length;\n const spanEaseItems = bitbybit.vector.spanEaseItems(spanEaseOptions);\n\n const vectors = spanItems.map((s, index) => [s, spanEaseItems[index], 0]) as Bit.Inputs.Base.Vector3[];\n\n bitbybit.draw.drawGridMesh(new Bit.Inputs.Draw.SceneDrawGridMeshDto());\n bitbybit.draw.drawAnyAsync({ entity: vectors });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Vector Span & Ease In Combination" /> diff --git a/docs/learn/code/common/draw/examples.mdx b/docs/learn/code/common/draw/examples.mdx index c98c4b7b..a97abdbe 100644 --- a/docs/learn/code/common/draw/examples.mdx +++ b/docs/learn/code/common/draw/examples.mdx @@ -28,7 +28,7 @@ The primary component for custom drawing is typically found under the path: **Rete Example: Filleted Cube with Custom Drawing Options** @@ -44,7 +44,7 @@ The primary block for custom drawing is typically found under: 5000TRUE111#ff6600#000099#cc33cc20.3TRUETRUETRUE0.01FALSE0.06#ff00ffFALSE0.06#0000ff","version":"0.20.13","type":"blockly"}} + script={{"script":"5000TRUE111#ff6600#000099#cc33cc20.3TRUETRUETRUE0.01FALSE0.06#ff00ffFALSE0.06#0000ff","version":"0.20.14","type":"blockly"}} title="Blockly Drawing Example" description="Draws simple filletted cube geometry." /> @@ -55,7 +55,7 @@ Finally, we achieve the same result using TypeScript. The code follows a similar {\n\n const cubeOptions = new Bit.Inputs.OCCT.CubeDto();\n cubeOptions.size = 5;\n const cube = await bitbybit.occt.shapes.solid.createCube(cubeOptions);\n const filletOptions = new Bit.Inputs.OCCT.FilletDto()\n filletOptions.shape = cube;\n filletOptions.radius = 1;\n const roundedCube = await bitbybit.occt.fillets.filletEdges(filletOptions);\n\n const drawOcctOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOcctOptions.faceColour = \"#0000ff\";\n drawOcctOptions.edgeColour = \"#ff5555\";\n drawOcctOptions.drawVertices = true;\n drawOcctOptions.vertexSize = 0.3;\n // The rest of options remain default (initialized inside the instance)\n const drawnMesh = await bitbybit.draw.drawAnyAsync({ entity: roundedCube, options: drawOcctOptions })\n // drawnMesh is BABYLONJS Mesh if BabylonJS engine is used. In Three.JS it turns into Group.\n return drawnMesh;\n \n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const cubeOptions = new Bit.Inputs.OCCT.CubeDto();\n cubeOptions.size = 5;\n const cube = await bitbybit.occt.shapes.solid.createCube(cubeOptions);\n const filletOptions = new Bit.Inputs.OCCT.FilletDto()\n filletOptions.shape = cube;\n filletOptions.radius = 1;\n const roundedCube = await bitbybit.occt.fillets.filletEdges(filletOptions);\n\n const drawOcctOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOcctOptions.faceColour = \"#0000ff\";\n drawOcctOptions.edgeColour = \"#ff5555\";\n drawOcctOptions.drawVertices = true;\n drawOcctOptions.vertexSize = 0.3;\n // The rest of options remain default (initialized inside the instance)\n const drawnMesh = await bitbybit.draw.drawAnyAsync({ entity: roundedCube, options: drawOcctOptions })\n // drawnMesh is BABYLONJS Mesh if BabylonJS engine is used. In Three.JS it turns into Group.\n return drawnMesh;\n \n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="TypeScript Drawing Example" description="Draws simple filletted cube geometry." /> \ No newline at end of file diff --git a/docs/learn/code/common/occt/booleans/operations.mdx b/docs/learn/code/common/occt/booleans/operations.mdx index acfba5e1..b3911ea0 100644 --- a/docs/learn/code/common/occt/booleans/operations.mdx +++ b/docs/learn/code/common/occt/booleans/operations.mdx @@ -49,21 +49,21 @@ The following scripts demonstrate creating three solids (a box, a cylinder, and **TypeScript Example: Union of Solids** {\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 5;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.radius = 3;\n cylinderOpt.height = 7;\n cylinderOpt.center = [3, 0, 3];\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt);\n\n\n const sphereOpt = new Bit.Inputs.OCCT.SphereDto();\n sphereOpt.radius = 3;\n sphereOpt.center = [-1.5, 1.5, -5];\n const sphere = await bitbybit.occt.shapes.solid.createSphere(sphereOpt);\n\n const union = await bitbybit.occt.booleans.union({\n shapes: [box, cylinder, sphere],\n keepEdges: false\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: union\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 5;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.radius = 3;\n cylinderOpt.height = 7;\n cylinderOpt.center = [3, 0, 3];\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt);\n\n\n const sphereOpt = new Bit.Inputs.OCCT.SphereDto();\n sphereOpt.radius = 3;\n sphereOpt.center = [-1.5, 1.5, -5];\n const sphere = await bitbybit.occt.shapes.solid.createSphere(sphereOpt);\n\n const union = await bitbybit.occt.booleans.union({\n shapes: [box, cylinder, sphere],\n keepEdges: false\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: union\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Union Three Solids" /> **Blockly Example: Union of Solids** boxspherecylinderbox585000sphere3-1.51.5-5cylinder37303010boxcylindersphereFALSE","version":"0.20.13","type":"blockly"}} + script={{"script":"boxspherecylinderbox585000sphere3-1.51.5-5cylinder37303010boxcylindersphereFALSE","version":"0.20.14","type":"blockly"}} title="Union Three Solids" /> **Rete Example: Union of Solids** @@ -94,21 +94,21 @@ These scripts demonstrate creating a box, cylinder, and sphere, then subtracting **TypeScript Example: Difference of Solids** {\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 5;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.radius = 3;\n cylinderOpt.height = 7;\n cylinderOpt.center = [3, 0, 3];\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt);\n\n\n const sphereOpt = new Bit.Inputs.OCCT.SphereDto();\n sphereOpt.radius = 3;\n sphereOpt.center = [-1.5, 1.5, -5];\n const sphere = await bitbybit.occt.shapes.solid.createSphere(sphereOpt);\n\n const diff = await bitbybit.occt.booleans.difference({\n shape: box,\n shapes: [cylinder, sphere],\n keepEdges: false\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: diff\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 5;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.radius = 3;\n cylinderOpt.height = 7;\n cylinderOpt.center = [3, 0, 3];\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt);\n\n\n const sphereOpt = new Bit.Inputs.OCCT.SphereDto();\n sphereOpt.radius = 3;\n sphereOpt.center = [-1.5, 1.5, -5];\n const sphere = await bitbybit.occt.shapes.solid.createSphere(sphereOpt);\n\n const diff = await bitbybit.occt.booleans.difference({\n shape: box,\n shapes: [cylinder, sphere],\n keepEdges: false\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: diff\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Difference of Solids" /> **Blockly Example: Difference of Solids** boxspherecylinderbox585000sphere3-1.51.5-5cylinder37303010boxcylindersphereFALSE","version":"0.20.13","type":"blockly"}} + script={{"script":"boxspherecylinderbox585000sphere3-1.51.5-5cylinder37303010boxcylindersphereFALSE","version":"0.20.14","type":"blockly"}} title="Difference of Solids" /> **Rete Example: Difference of Solids** @@ -138,21 +138,21 @@ These scripts create a box, cylinder, and sphere, and then compute their common **TypeScript Example: Intersection of Solids** {\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 5;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.radius = 3;\n cylinderOpt.height = 7;\n cylinderOpt.center = [3, 0, 3];\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt);\n\n\n const sphereOpt = new Bit.Inputs.OCCT.SphereDto();\n sphereOpt.radius = 3;\n sphereOpt.center = [-1.5, 1.5, -5];\n const sphere = await bitbybit.occt.shapes.solid.createSphere(sphereOpt);\n\n const diff = await bitbybit.occt.booleans.intersection({\n shapes: [box, cylinder, sphere],\n keepEdges: false\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: diff\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 5;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.radius = 3;\n cylinderOpt.height = 7;\n cylinderOpt.center = [3, 0, 3];\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt);\n\n\n const sphereOpt = new Bit.Inputs.OCCT.SphereDto();\n sphereOpt.radius = 3;\n sphereOpt.center = [-1.5, 1.5, -5];\n const sphere = await bitbybit.occt.shapes.solid.createSphere(sphereOpt);\n\n const diff = await bitbybit.occt.booleans.intersection({\n shapes: [box, cylinder, sphere],\n keepEdges: false\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: diff\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Intersection of Solids" /> **Blockly Example: Intersection of Solids** boxspherecylinderbox585000sphere3-1.51.5-5cylinder37303010boxcylindersphereFALSE","version":"0.20.13","type":"blockly"}} + script={{"script":"boxspherecylinderbox585000sphere3-1.51.5-5cylinder37303010boxcylindersphereFALSE","version":"0.20.14","type":"blockly"}} title="Intersection of Solids" /> **Rete Example: Intersection of Solids** diff --git a/docs/learn/code/common/occt/dimensions/angular-dimension.md b/docs/learn/code/common/occt/dimensions/angular-dimension.md index 80a46fdd..d079b26a 100644 --- a/docs/learn/code/common/occt/dimensions/angular-dimension.md +++ b/docs/learn/code/common/occt/dimensions/angular-dimension.md @@ -32,21 +32,21 @@ Angular dimensions measure the angle between two direction vectors and display t direction1direction2centerdirection1100direction2011center000direction1direction2center20.30.11deg0.30.4FALSE","version":"0.20.13","type":"blockly"}} + script={{"script":"direction1direction2centerdirection1100direction2011center000direction1direction2center20.30.11deg0.30.4FALSE","version":"0.20.14","type":"blockly"}} title="Simple angular dimension between two directions" /> {\n // Define two direction vectors to measure angle between\n const direction1: Vector3 = [1, 0, 0]; // X-axis direction\n const direction2: Vector3 = [0, 1, 1]; // Direction at 45deg from Y and Z\n const center: Point3 = [0, 0, 0]; // Origin point\n\n // Create an angular dimension between the directions\n const dimensionOptions = new SimpleAngularDimensionDto();\n dimensionOptions.direction1 = direction1;\n dimensionOptions.direction2 = direction2;\n dimensionOptions.center = center;\n dimensionOptions.radius = 2;\n dimensionOptions.offsetFromCenter = 0.3;\n dimensionOptions.extraSize = 0.1;\n dimensionOptions.decimalPlaces = 1;\n dimensionOptions.labelSuffix = \"deg\";\n dimensionOptions.labelSize = 0.3;\n dimensionOptions.labelOffset = 0.4;\n dimensionOptions.radians = false;\n\n // Create and draw the dimension\n const dimension = await bitbybit.occt.dimensions.simpleAngularDimension(dimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: dimension });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import the required DTO for angular dimensions\nconst { SimpleAngularDimensionDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define the main function\nconst start = async () => {\n // Define two direction vectors to measure angle between\n const direction1: Vector3 = [1, 0, 0]; // X-axis direction\n const direction2: Vector3 = [0, 1, 1]; // Direction at 45deg from Y and Z\n const center: Point3 = [0, 0, 0]; // Origin point\n\n // Create an angular dimension between the directions\n const dimensionOptions = new SimpleAngularDimensionDto();\n dimensionOptions.direction1 = direction1;\n dimensionOptions.direction2 = direction2;\n dimensionOptions.center = center;\n dimensionOptions.radius = 2;\n dimensionOptions.offsetFromCenter = 0.3;\n dimensionOptions.extraSize = 0.1;\n dimensionOptions.decimalPlaces = 1;\n dimensionOptions.labelSuffix = \"deg\";\n dimensionOptions.labelSize = 0.3;\n dimensionOptions.labelOffset = 0.4;\n dimensionOptions.radians = false;\n\n // Create and draw the dimension\n const dimension = await bitbybit.occt.dimensions.simpleAngularDimension(dimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: dimension });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Simple angular dimension between two directions" /> @@ -62,21 +62,21 @@ Angular dimensions are particularly useful for complex 3D geometry like cones, w conerotatedConeconeEdgetranslatedEdgestartPointendPointdirection1cone52.75360000010rotatedConecone010-90coneEdgerotatedCone2translatedEdgeconeEdge-100startPointtranslatedEdgeendPointtranslatedEdgedirection1endPointstartPointrotatedConedirection1-100startPoint7202(deg)0.40.6FALSE","version":"0.20.13","type":"blockly"}} + script={{"script":"conerotatedConeconeEdgetranslatedEdgestartPointendPointdirection1cone52.75360000010rotatedConecone010-90coneEdgerotatedCone2translatedEdgeconeEdge-100startPointtranslatedEdgeendPointtranslatedEdgedirection1endPointstartPointrotatedConedirection1-100startPoint7202(deg)0.40.6FALSE","version":"0.20.14","type":"blockly"}} title="Cone with apex angle measurement" /> {\n // Create a cone with specific dimensions\n const coneOptions = new ConeDto();\n coneOptions.radius1 = 5;\n coneOptions.radius2 = 2.7; // Variable radius for apex angle\n coneOptions.height = 5;\n coneOptions.angle = 360;\n coneOptions.center = [0, 0, 0];\n coneOptions.direction = [0, 1, 0];\n const cone = await solid.createCone(coneOptions);\n\n // Rotate the cone -90 degrees around Y axis for better visualization\n const rotatedCone = await transforms.rotate({\n shape: cone,\n axis: [0, 1, 0],\n angle: -90\n });\n\n // Draw the rotated cone\n bitbybit.draw.drawAnyAsync({ entity: rotatedCone });\n\n // Get edge 2 from the cone (this is a generatrix line)\n const coneEdge = await edge.getEdge({ shape: rotatedCone, index: 2 });\n\n // Translate the edge to position it for measurement\n const translatedEdge = await transforms.translate({\n shape: coneEdge,\n translation: [-1, 0, 0]\n });\n\n // Get start and end points from the translated edge\n const startPoint = await edge.startPointOnEdge({ shape: translatedEdge });\n const endPoint = await edge.endPointOnEdge({ shape: translatedEdge });\n\n // Calculate direction vector from start to end point\n const direction1 = bitbybit.vector.sub({ first: endPoint, second: startPoint }) as Vector3;\n\n // Create angular dimension to measure apex angle\n const dimensionOptions = new SimpleAngularDimensionDto();\n dimensionOptions.direction1 = direction1;\n dimensionOptions.direction2 = [-1, 0, 0]; // Reference direction\n dimensionOptions.center = startPoint; // Apex point\n dimensionOptions.radius = 7;\n dimensionOptions.offsetFromCenter = 2;\n dimensionOptions.extraSize = 0;\n dimensionOptions.decimalPlaces = 2;\n dimensionOptions.labelSuffix = \"(deg)\";\n dimensionOptions.labelSize = 0.4;\n dimensionOptions.labelOffset = 0.6;\n dimensionOptions.radians = false;\n\n // Create and draw the angular dimension\n const dimension = await dimensions.simpleAngularDimension(dimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: dimension });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating shapes and dimensions\nconst { ConeDto, SimpleAngularDimensionDto } = Bit.Inputs.OCCT;\n// Import types for type safety\ntype TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT modules\nconst { shapes, dimensions, transforms } = bitbybit.occt;\nconst { solid, edge } = shapes;\n\n// Define the main function to create a cone with angular dimension\nconst start = async () => {\n // Create a cone with specific dimensions\n const coneOptions = new ConeDto();\n coneOptions.radius1 = 5;\n coneOptions.radius2 = 2.7; // Variable radius for apex angle\n coneOptions.height = 5;\n coneOptions.angle = 360;\n coneOptions.center = [0, 0, 0];\n coneOptions.direction = [0, 1, 0];\n const cone = await solid.createCone(coneOptions);\n\n // Rotate the cone -90 degrees around Y axis for better visualization\n const rotatedCone = await transforms.rotate({\n shape: cone,\n axis: [0, 1, 0],\n angle: -90\n });\n\n // Draw the rotated cone\n bitbybit.draw.drawAnyAsync({ entity: rotatedCone });\n\n // Get edge 2 from the cone (this is a generatrix line)\n const coneEdge = await edge.getEdge({ shape: rotatedCone, index: 2 });\n\n // Translate the edge to position it for measurement\n const translatedEdge = await transforms.translate({\n shape: coneEdge,\n translation: [-1, 0, 0]\n });\n\n // Get start and end points from the translated edge\n const startPoint = await edge.startPointOnEdge({ shape: translatedEdge });\n const endPoint = await edge.endPointOnEdge({ shape: translatedEdge });\n\n // Calculate direction vector from start to end point\n const direction1 = bitbybit.vector.sub({ first: endPoint, second: startPoint }) as Vector3;\n\n // Create angular dimension to measure apex angle\n const dimensionOptions = new SimpleAngularDimensionDto();\n dimensionOptions.direction1 = direction1;\n dimensionOptions.direction2 = [-1, 0, 0]; // Reference direction\n dimensionOptions.center = startPoint; // Apex point\n dimensionOptions.radius = 7;\n dimensionOptions.offsetFromCenter = 2;\n dimensionOptions.extraSize = 0;\n dimensionOptions.decimalPlaces = 2;\n dimensionOptions.labelSuffix = \"(deg)\";\n dimensionOptions.labelSize = 0.4;\n dimensionOptions.labelOffset = 0.6;\n dimensionOptions.radians = false;\n\n // Create and draw the angular dimension\n const dimension = await dimensions.simpleAngularDimension(dimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: dimension });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Cone with apex angle measurement" /> diff --git a/docs/learn/code/common/occt/dimensions/linear-dimension.md b/docs/learn/code/common/occt/dimensions/linear-dimension.md index 893f9a66..01c2fa62 100644 --- a/docs/learn/code/common/occt/dimensions/linear-dimension.md +++ b/docs/learn/code/common/occt/dimensions/linear-dimension.md @@ -26,21 +26,21 @@ Linear dimensions measure straight-line distances between two points and display point1point2point1-500point2500point1point2point1point20100.30.21cm0.40.80","version":"0.20.13","type":"blockly"}} + script={{"script":"point1point2point1-500point2500point1point2point1point20100.30.21cm0.40.80","version":"0.20.14","type":"blockly"}} title="Simple linear dimension between two points" /> {\n // Define two points to measure between\n const startPoint: Point3 = [-5, 0, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Draw the points for reference\n bitbybit.draw.drawAnyAsync({ entity: startPoint });\n bitbybit.draw.drawAnyAsync({ entity: endPoint });\n\n // Create a linear dimension between the points\n const dimensionOptions = new SimpleLinearLengthDimensionDto();\n dimensionOptions.start = startPoint;\n dimensionOptions.end = endPoint;\n dimensionOptions.direction = [0, 1, 0]; // Offset in Y direction\n dimensionOptions.offsetFromPoints = 0.3;\n dimensionOptions.crossingSize = 0.2;\n dimensionOptions.decimalPlaces = 1;\n dimensionOptions.labelSuffix = \"cm\";\n dimensionOptions.labelSize = 0.4;\n dimensionOptions.labelOffset = 0.8;\n dimensionOptions.labelRotation = 0;\n\n // Create and draw the dimension\n const dimension = await bitbybit.occt.dimensions.simpleLinearLengthDimension(dimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: dimension });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import the required DTO for linear dimensions\nconst { SimpleLinearLengthDimensionDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\n\n// Define the main function\nconst start = async () => {\n // Define two points to measure between\n const startPoint: Point3 = [-5, 0, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Draw the points for reference\n bitbybit.draw.drawAnyAsync({ entity: startPoint });\n bitbybit.draw.drawAnyAsync({ entity: endPoint });\n\n // Create a linear dimension between the points\n const dimensionOptions = new SimpleLinearLengthDimensionDto();\n dimensionOptions.start = startPoint;\n dimensionOptions.end = endPoint;\n dimensionOptions.direction = [0, 1, 0]; // Offset in Y direction\n dimensionOptions.offsetFromPoints = 0.3;\n dimensionOptions.crossingSize = 0.2;\n dimensionOptions.decimalPlaces = 1;\n dimensionOptions.labelSuffix = \"cm\";\n dimensionOptions.labelSize = 0.4;\n dimensionOptions.labelOffset = 0.8;\n dimensionOptions.labelRotation = 0;\n\n // Create and draw the dimension\n const dimension = await bitbybit.occt.dimensions.simpleLinearLengthDimension(dimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: dimension });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Simple linear dimension between two points" /> @@ -51,21 +51,21 @@ Linear dimensions update automatically when geometry changes, keeping your docum widthlengthheightboxfirstEdgesecondEdgethirdEdgewidth13.2length9.1height5.3boxwidthlengthheight000TRUEfirstEdgebox1secondEdgebox8thirdEdgebox11firstEdgefirstEdge-2000.20.22(cm)0.51180secondEdgesecondEdge2000.20.22(cm)0.51180thirdEdgethirdEdge00-20.20.22(cm)0.51180box","version":"0.20.13","type":"blockly"}} + script={{"script":"widthlengthheightboxfirstEdgesecondEdgethirdEdgewidth13.2length9.1height5.3boxwidthlengthheight000TRUEfirstEdgebox1secondEdgebox8thirdEdgebox11firstEdgefirstEdge-2000.20.22(cm)0.51180secondEdgesecondEdge2000.20.22(cm)0.51180thirdEdgethirdEdge00-20.20.22(cm)0.51180box","version":"0.20.14","type":"blockly"}} title="Linear dimensions applied on box" /> {\n // Create a box with specific dimensions\n const boxOptions = new BoxDto();\n boxOptions.width = 13.2;\n boxOptions.length = 9.1;\n boxOptions.height = 5.3;\n boxOptions.center = [0, 0, 0];\n boxOptions.originOnCenter = true;\n const box = await solid.createBox(boxOptions);\n\n // Draw the box first\n bitbybit.draw.drawAnyAsync({ entity: box });\n\n // Get all edges from the box for dimension measurements\n const edges = await edge.getEdges({ shape: box });\n\n // Create width dimension (measuring edge 0)\n const widthEdge = edges[0];\n const widthStartPoint = await edge.startPointOnEdge({ shape: widthEdge });\n const widthEndPoint = await edge.endPointOnEdge({ shape: widthEdge });\n\n const widthDimensionOptions = new SimpleLinearLengthDimensionDto();\n widthDimensionOptions.start = widthStartPoint;\n widthDimensionOptions.end = widthEndPoint;\n widthDimensionOptions.direction = [-2, 0, 0]; // Offset to the left\n widthDimensionOptions.offsetFromPoints = 0.2;\n widthDimensionOptions.crossingSize = 0.2;\n widthDimensionOptions.decimalPlaces = 2;\n widthDimensionOptions.labelSuffix = \"(cm)\";\n widthDimensionOptions.labelSize = 0.5;\n widthDimensionOptions.labelOffset = 1;\n widthDimensionOptions.labelRotation = 180;\n \n const widthDimension = await dimensions.simpleLinearLengthDimension(widthDimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: widthDimension });\n\n // Create height dimension (measuring edge 10)\n const heightEdge = edges[10];\n const heightStartPoint = await edge.startPointOnEdge({ shape: heightEdge });\n const heightEndPoint = await edge.endPointOnEdge({ shape: heightEdge });\n\n const heightDimensionOptions = new SimpleLinearLengthDimensionDto();\n heightDimensionOptions.start = heightStartPoint;\n heightDimensionOptions.end = heightEndPoint;\n heightDimensionOptions.direction = [0, 0, -2]; // Offset toward the back\n heightDimensionOptions.offsetFromPoints = 0.2;\n heightDimensionOptions.crossingSize = 0.2;\n heightDimensionOptions.decimalPlaces = 2;\n heightDimensionOptions.labelSuffix = \"(cm)\";\n heightDimensionOptions.labelSize = 0.5;\n heightDimensionOptions.labelOffset = 1;\n heightDimensionOptions.labelRotation = 180;\n \n const heightDimension = await dimensions.simpleLinearLengthDimension(heightDimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: heightDimension });\n\n // Create depth dimension (measuring edge 7)\n const depthEdge = edges[7];\n const depthStartPoint = await edge.endPointOnEdge({ shape: depthEdge });\n const depthEndPoint = await edge.startPointOnEdge({ shape: depthEdge });\n\n const depthDimensionOptions = new SimpleLinearLengthDimensionDto();\n depthDimensionOptions.start = depthStartPoint;\n depthDimensionOptions.end = depthEndPoint;\n depthDimensionOptions.direction = [2, 0, 0]; // Offset to the right\n depthDimensionOptions.offsetFromPoints = 0.2;\n depthDimensionOptions.crossingSize = 0.2;\n depthDimensionOptions.decimalPlaces = 2;\n depthDimensionOptions.labelSuffix = \"(cm)\";\n depthDimensionOptions.labelSize = 0.5;\n depthDimensionOptions.labelOffset = 1;\n depthDimensionOptions.labelRotation = 180;\n \n const depthDimension = await dimensions.simpleLinearLengthDimension(depthDimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: depthDimension });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating shapes and dimensions\nconst { BoxDto, SimpleLinearLengthDimensionDto } = Bit.Inputs.OCCT;\n// Import types for type safety\ntype TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Get access to OCCT modules\nconst { shapes, dimensions } = bitbybit.occt;\nconst { solid, edge } = shapes;\n\n// Define the main function to create a box with linear dimensions\nconst start = async () => {\n // Create a box with specific dimensions\n const boxOptions = new BoxDto();\n boxOptions.width = 13.2;\n boxOptions.length = 9.1;\n boxOptions.height = 5.3;\n boxOptions.center = [0, 0, 0];\n boxOptions.originOnCenter = true;\n const box = await solid.createBox(boxOptions);\n\n // Draw the box first\n bitbybit.draw.drawAnyAsync({ entity: box });\n\n // Get all edges from the box for dimension measurements\n const edges = await edge.getEdges({ shape: box });\n\n // Create width dimension (measuring edge 0)\n const widthEdge = edges[0];\n const widthStartPoint = await edge.startPointOnEdge({ shape: widthEdge });\n const widthEndPoint = await edge.endPointOnEdge({ shape: widthEdge });\n\n const widthDimensionOptions = new SimpleLinearLengthDimensionDto();\n widthDimensionOptions.start = widthStartPoint;\n widthDimensionOptions.end = widthEndPoint;\n widthDimensionOptions.direction = [-2, 0, 0]; // Offset to the left\n widthDimensionOptions.offsetFromPoints = 0.2;\n widthDimensionOptions.crossingSize = 0.2;\n widthDimensionOptions.decimalPlaces = 2;\n widthDimensionOptions.labelSuffix = \"(cm)\";\n widthDimensionOptions.labelSize = 0.5;\n widthDimensionOptions.labelOffset = 1;\n widthDimensionOptions.labelRotation = 180;\n \n const widthDimension = await dimensions.simpleLinearLengthDimension(widthDimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: widthDimension });\n\n // Create height dimension (measuring edge 10)\n const heightEdge = edges[10];\n const heightStartPoint = await edge.startPointOnEdge({ shape: heightEdge });\n const heightEndPoint = await edge.endPointOnEdge({ shape: heightEdge });\n\n const heightDimensionOptions = new SimpleLinearLengthDimensionDto();\n heightDimensionOptions.start = heightStartPoint;\n heightDimensionOptions.end = heightEndPoint;\n heightDimensionOptions.direction = [0, 0, -2]; // Offset toward the back\n heightDimensionOptions.offsetFromPoints = 0.2;\n heightDimensionOptions.crossingSize = 0.2;\n heightDimensionOptions.decimalPlaces = 2;\n heightDimensionOptions.labelSuffix = \"(cm)\";\n heightDimensionOptions.labelSize = 0.5;\n heightDimensionOptions.labelOffset = 1;\n heightDimensionOptions.labelRotation = 180;\n \n const heightDimension = await dimensions.simpleLinearLengthDimension(heightDimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: heightDimension });\n\n // Create depth dimension (measuring edge 7)\n const depthEdge = edges[7];\n const depthStartPoint = await edge.endPointOnEdge({ shape: depthEdge });\n const depthEndPoint = await edge.startPointOnEdge({ shape: depthEdge });\n\n const depthDimensionOptions = new SimpleLinearLengthDimensionDto();\n depthDimensionOptions.start = depthStartPoint;\n depthDimensionOptions.end = depthEndPoint;\n depthDimensionOptions.direction = [2, 0, 0]; // Offset to the right\n depthDimensionOptions.offsetFromPoints = 0.2;\n depthDimensionOptions.crossingSize = 0.2;\n depthDimensionOptions.decimalPlaces = 2;\n depthDimensionOptions.labelSuffix = \"(cm)\";\n depthDimensionOptions.labelSize = 0.5;\n depthDimensionOptions.labelOffset = 1;\n depthDimensionOptions.labelRotation = 180;\n \n const depthDimension = await dimensions.simpleLinearLengthDimension(depthDimensionOptions);\n bitbybit.draw.drawAnyAsync({ entity: depthDimension });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Linear dimensions applied on box" /> diff --git a/docs/learn/code/common/occt/dimensions/pin-with-label.md b/docs/learn/code/common/occt/dimensions/pin-with-label.md index 20683160..6f2695b0 100644 --- a/docs/learn/code/common/occt/dimensions/pin-with-label.md +++ b/docs/learn/code/common/occt/dimensions/pin-with-label.md @@ -39,21 +39,21 @@ The most basic use case is creating a pin with static text to mark important poi 0003211000Important Point0.30.4","version":"0.20.13","type":"blockly"}} + script={{"script":"0003211000Important Point0.30.4","version":"0.20.14","type":"blockly"}} title="Simple pin with static label" /> {\n // Define points for the pin\n const startPoint: Point3 = [0, 0, 0];\n const endPoint: Point3 = [3, 2, 1];\n const direction: Vector3 = [1, 0, 0];\n\n // Create pin with label options\n const pinOptions = new PinWithLabelDto();\n pinOptions.startPoint = startPoint;\n pinOptions.endPoint = endPoint;\n pinOptions.direction = direction;\n pinOptions.offsetFromStart = 0;\n pinOptions.label = \"Important Point\";\n pinOptions.labelOffset = 0.3;\n pinOptions.labelSize = 0.4;\n\n // Create and draw the pin\n const pin = await bitbybit.occt.dimensions.pinWithLabel(pinOptions);\n bitbybit.draw.drawAnyAsync({ entity: pin });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import the required DTO for pin with label\nconst { PinWithLabelDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define the main function\nconst start = async () => {\n // Define points for the pin\n const startPoint: Point3 = [0, 0, 0];\n const endPoint: Point3 = [3, 2, 1];\n const direction: Vector3 = [1, 0, 0];\n\n // Create pin with label options\n const pinOptions = new PinWithLabelDto();\n pinOptions.startPoint = startPoint;\n pinOptions.endPoint = endPoint;\n pinOptions.direction = direction;\n pinOptions.offsetFromStart = 0;\n pinOptions.label = \"Important Point\";\n pinOptions.labelOffset = 0.3;\n pinOptions.labelSize = 0.4;\n\n // Create and draw the pin\n const pin = await bitbybit.occt.dimensions.pinWithLabel(pinOptions);\n bitbybit.draw.drawAnyAsync({ entity: pin });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Simple pin with static label" /> @@ -74,21 +74,21 @@ A more practical application combines pin labels with calculated geometric prope radiusspherevolumeradius2.5sphereradius000volumesphere00radius43ADDradius21000Vol: {0} m3volume20.30.4sphere","version":"0.20.13","type":"blockly"}} + script={{"script":"radiusspherevolumeradius2.5sphereradius000volumesphere00radius43ADDradius21000Vol: {0} m3volume20.30.4sphere","version":"0.20.14","type":"blockly"}} title="Sphere with volume pin label" /> {\n // Parametric radius value\n const radius = 2.5;\n\n // Create a sphere\n const sphereOptions = new SphereDto();\n sphereOptions.radius = radius;\n sphereOptions.center = [0, 0, 0] as Point3;\n\n const sphere = await solid.createSphere(sphereOptions);\n\n // Calculate sphere volume\n const volume = await solid.getSolidVolume({ shape: sphere });\n const roundedVolume = math.roundToDecimals({ number: volume, decimalPlaces: 2 });\n\n // Format volume as text with units\n const volumeText = text.format({\n text: \"Vol: {0} m3\",\n values: [text.toString({ item: roundedVolume })]\n });\n\n // Create pin with calculated volume label\n const pinOptions = new PinWithLabelDto();\n pinOptions.startPoint = [0, 0, radius] as Point3; // Start at top of sphere\n pinOptions.endPoint = [4, 3, radius + 2] as Point3; // Position for visibility\n pinOptions.direction = [1, 0, 0] as Vector3;\n pinOptions.offsetFromStart = 0;\n pinOptions.label = volumeText;\n pinOptions.labelOffset = 0.3;\n pinOptions.labelSize = 0.4;\n\n // Create and draw the pin\n const pin = await dimensions.pinWithLabel(pinOptions);\n \n // Draw both sphere and pin\n bitbybit.draw.drawAnyAsync({ entity: sphere });\n bitbybit.draw.drawAnyAsync({ entity: pin });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types\nconst { SphereDto, PinWithLabelDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT modules\nconst { solid } = bitbybit.occt.shapes;\nconst { dimensions } = bitbybit.occt;\nconst { math, text } = bitbybit;\n\n// Define the main function\nconst start = async () => {\n // Parametric radius value\n const radius = 2.5;\n\n // Create a sphere\n const sphereOptions = new SphereDto();\n sphereOptions.radius = radius;\n sphereOptions.center = [0, 0, 0] as Point3;\n\n const sphere = await solid.createSphere(sphereOptions);\n\n // Calculate sphere volume\n const volume = await solid.getSolidVolume({ shape: sphere });\n const roundedVolume = math.roundToDecimals({ number: volume, decimalPlaces: 2 });\n\n // Format volume as text with units\n const volumeText = text.format({\n text: \"Vol: {0} m3\",\n values: [text.toString({ item: roundedVolume })]\n });\n\n // Create pin with calculated volume label\n const pinOptions = new PinWithLabelDto();\n pinOptions.startPoint = [0, 0, radius] as Point3; // Start at top of sphere\n pinOptions.endPoint = [4, 3, radius + 2] as Point3; // Position for visibility\n pinOptions.direction = [1, 0, 0] as Vector3;\n pinOptions.offsetFromStart = 0;\n pinOptions.label = volumeText;\n pinOptions.labelOffset = 0.3;\n pinOptions.labelSize = 0.4;\n\n // Create and draw the pin\n const pin = await dimensions.pinWithLabel(pinOptions);\n \n // Draw both sphere and pin\n bitbybit.draw.drawAnyAsync({ entity: sphere });\n bitbybit.draw.drawAnyAsync({ entity: pin });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Sphere with volume pin label" /> diff --git a/docs/learn/code/common/occt/fillets/chamfer-circular-edges.mdx b/docs/learn/code/common/occt/fillets/chamfer-circular-edges.mdx index 4178afea..32935ed8 100644 --- a/docs/learn/code/common/occt/fillets/chamfer-circular-edges.mdx +++ b/docs/learn/code/common/occt/fillets/chamfer-circular-edges.mdx @@ -25,21 +25,21 @@ The examples below demonstrate creating a box with a cylindrical hole through it **TypeScript Example: Chamfer Circular Edge of a Hole** {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.direction = [1, 0, 0];\n cylinderOpt.center = [-5, 0, 0];\n cylinderOpt.radius = 2;\n cylinderOpt.height = 10;\n\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt)\n\n const difference = await bitbybit.occt.booleans.difference({\n shape: box,\n shapes: [cylinder],\n keepEdges: false\n });\n\n const chamfered = await bitbybit.occt.fillets.chamferEdges({\n shape: difference,\n distance: 0.4\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: chamfered,\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n const cylinderOpt = new Bit.Inputs.OCCT.CylinderDto();\n cylinderOpt.direction = [1, 0, 0];\n cylinderOpt.center = [-5, 0, 0];\n cylinderOpt.radius = 2;\n cylinderOpt.height = 10;\n\n const cylinder = await bitbybit.occt.shapes.solid.createCylinder(cylinderOpt)\n\n const difference = await bitbybit.occt.booleans.difference({\n shape: box,\n shapes: [cylinder],\n keepEdges: false\n });\n\n const chamfered = await bitbybit.occt.fillets.chamferEdges({\n shape: difference,\n distance: 0.4\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: chamfered,\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Chamfer Circular Edge" /> **Blockly Example: Chamfer Circular Edge of a Hole** differenceSoliddifferenceSolid5810000210-500100FALSEdifferenceSolid0.4","version":"0.20.13","type":"blockly"}} + script={{"script":"differenceSoliddifferenceSolid5810000210-500100FALSEdifferenceSolid0.4","version":"0.20.14","type":"blockly"}} title="Chamfer Circular Edge" /> **Rete Example: Chamfer Circular Edge of a Hole** diff --git a/docs/learn/code/common/occt/fillets/chamfers-intro.mdx b/docs/learn/code/common/occt/fillets/chamfers-intro.mdx index b93840c7..55a1a022 100644 --- a/docs/learn/code/common/occt/fillets/chamfers-intro.mdx +++ b/docs/learn/code/common/occt/fillets/chamfers-intro.mdx @@ -48,21 +48,21 @@ The following examples in TypeScript, Rete, and Blockly demonstrate creating a s **TypeScript Example: Chamfer All Edges of a Solid** {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const chamfered = await bitbybit.occt.fillets.chamferEdges({\n shape: box,\n distance: 1,\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: chamfered,\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const chamfered = await bitbybit.occt.fillets.chamferEdges({\n shape: box,\n distance: 1,\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: chamfered,\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Chamfer All Edges of Solid" /> **Blockly Example: Chamfer All Edges of a Solid** 58100001","version":"0.20.13","type":"blockly"}} + script={{"script":"58100001","version":"0.20.14","type":"blockly"}} title="Chamfer All Edges of Solid" /> **Rete Example: Chamfer All Edges of a Solid** diff --git a/docs/learn/code/common/occt/fillets/chamfers-var-radius-on-spec-edges.mdx b/docs/learn/code/common/occt/fillets/chamfers-var-radius-on-spec-edges.mdx index 55d66058..3e331e79 100644 --- a/docs/learn/code/common/occt/fillets/chamfers-var-radius-on-spec-edges.mdx +++ b/docs/learn/code/common/occt/fillets/chamfers-var-radius-on-spec-edges.mdx @@ -28,21 +28,21 @@ The examples below demonstrate creating a solid box and then applying chamfers w **TypeScript Example: Chamfer Specific Edges with Variable Distances** {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const chamfered = await bitbybit.occt.fillets.chamferEdges({\n shape: box,\n distanceList: [0.2, 1.2, 2],\n indexes: [1, 2, 3]\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: chamfered,\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const chamfered = await bitbybit.occt.fillets.chamferEdges({\n shape: box,\n distanceList: [0.2, 1.2, 2],\n indexes: [1, 2, 3]\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: chamfered,\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Chamfer Specific Edges of Solid" /> **Blockly Example: Chamfer Specific Edges with Variable Distances** 58100000.10.31.22123","version":"0.20.13","type":"blockly"}} + script={{"script":"58100000.10.31.22123","version":"0.20.14","type":"blockly"}} title="Chamfer Specific Edges of Solid" /> **Rete Example: Chamfer Specific Edges with Variable Distances** diff --git a/docs/learn/code/common/occt/fillets/fillet-3d-wires.mdx b/docs/learn/code/common/occt/fillets/fillet-3d-wires.mdx index 2699038e..a222151c 100644 --- a/docs/learn/code/common/occt/fillets/fillet-3d-wires.mdx +++ b/docs/learn/code/common/occt/fillets/fillet-3d-wires.mdx @@ -55,21 +55,21 @@ In these examples, we first construct a 3D star-shaped wire. Then, we apply diff **TypeScript Example: Fillet Specific Corners of a 3D Wire** {\n\n const repeatOpt = new Bit.Inputs.Lists.MultiplyItemDto([innerFillet, outerFillet], nrRays);\n const radiusList = bitbybit.lists.repeat(repeatOpt).flat();\n const spanOptions = new Bit.Inputs.Vector.SpanDto(1, 1, nrRays * 2);\n const indexes = bitbybit.vector.span(spanOptions);\n\n const starOptions = new Bit.Inputs.OCCT.StarDto(outerStarRadius, innerStarRadius, nrRays, [0, 0, 0], [0, 1, 0], 4, false);\n const star = await bitbybit.occt.shapes.wire.createStarWire(starOptions);\n\n const filletOptions = new Bit.Inputs.OCCT.Fillet3DWireDto(star, undefined, [0, 1, 0], radiusList, indexes);\n const starFillet = await bitbybit.occt.fillets.fillet3DWire(filletOptions);\n\n const startFilletTranslated1 = await bitbybit.occt.transforms.translate({ shape: starFillet, translation: [0, 5, 0] })\n const startFilletTranslated2 = await bitbybit.occt.transforms.translate({ shape: starFillet, translation: [0, 10, 0] })\n\n\n const starFace = await bitbybit.occt.shapes.face.createFaceFromWire({ shape: startFilletTranslated2, planar: false });\n const starThick = await bitbybit.occt.operations.makeThickSolidSimple({ shape: starFace, offset: -1 });\n const starThickFillet = await bitbybit.occt.fillets.filletEdges({ shape: starThick, radius: 0.3 });\n\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.edgeWidth = 15;\n bitbybit.draw.drawAnyAsync({ entity: star, options: drawOptions });\n bitbybit.draw.drawAnyAsync({ entity: startFilletTranslated1, options: drawOptions });\n drawOptions.faceColour = \"#5555ff\";\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 2;\n drawOptions.precision = 0.005;\n bitbybit.draw.drawAnyAsync({ entity: starThickFillet, options: drawOptions });\n\n const gridOptions = new Bit.Inputs.Draw.SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n const skyboxOptions = new Bit.Inputs.BabylonScene.SkyboxDto();\n skyboxOptions.skybox = Bit.Inputs.Base.skyboxEnum.clearSky;\n bitbybit.babylon.scene.enableSkybox(skyboxOptions);\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const nrRays = 7;\nconst outerStarRadius = 10;\nconst innerStarRadius = 4;\nconst outerFillet = 0.6;\nconst innerFillet = 1.7;\n\nconst start = async () => {\n\n const repeatOpt = new Bit.Inputs.Lists.MultiplyItemDto([innerFillet, outerFillet], nrRays);\n const radiusList = bitbybit.lists.repeat(repeatOpt).flat();\n const spanOptions = new Bit.Inputs.Vector.SpanDto(1, 1, nrRays * 2);\n const indexes = bitbybit.vector.span(spanOptions);\n\n const starOptions = new Bit.Inputs.OCCT.StarDto(outerStarRadius, innerStarRadius, nrRays, [0, 0, 0], [0, 1, 0], 4, false);\n const star = await bitbybit.occt.shapes.wire.createStarWire(starOptions);\n\n const filletOptions = new Bit.Inputs.OCCT.Fillet3DWireDto(star, undefined, [0, 1, 0], radiusList, indexes);\n const starFillet = await bitbybit.occt.fillets.fillet3DWire(filletOptions);\n\n const startFilletTranslated1 = await bitbybit.occt.transforms.translate({ shape: starFillet, translation: [0, 5, 0] })\n const startFilletTranslated2 = await bitbybit.occt.transforms.translate({ shape: starFillet, translation: [0, 10, 0] })\n\n\n const starFace = await bitbybit.occt.shapes.face.createFaceFromWire({ shape: startFilletTranslated2, planar: false });\n const starThick = await bitbybit.occt.operations.makeThickSolidSimple({ shape: starFace, offset: -1 });\n const starThickFillet = await bitbybit.occt.fillets.filletEdges({ shape: starThick, radius: 0.3 });\n\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.edgeWidth = 15;\n bitbybit.draw.drawAnyAsync({ entity: star, options: drawOptions });\n bitbybit.draw.drawAnyAsync({ entity: startFilletTranslated1, options: drawOptions });\n drawOptions.faceColour = \"#5555ff\";\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 2;\n drawOptions.precision = 0.005;\n bitbybit.draw.drawAnyAsync({ entity: starThickFillet, options: drawOptions });\n\n const gridOptions = new Bit.Inputs.Draw.SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n const skyboxOptions = new Bit.Inputs.BabylonScene.SkyboxDto();\n skyboxOptions.skybox = Bit.Inputs.Base.skyboxEnum.clearSky;\n bitbybit.babylon.scene.enableSkybox(skyboxOptions);\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Fillet 3D Wire Specific Corners" /> **Blockly Example: Fillet Specific Corners of a 3D Wire** nrRaysinnerFilletRadiusouterRadiusFilletfilletIndexesradiusLististarPromisestarFilletPromisenrRays7innerFilletRadius1.7outerRadiusFillet0.6filletIndexes11MULTIPLYnrRays2'clearSky'10000.10.7radiusListi1MULTIPLYnrRays21EQi20INSERTLASTradiusListouterRadiusFilletINSERTLASTradiusListinnerFilletRadiusstarPromise000010nrRays1044FALSEstarFilletPromisestarPromiseradiusListfilletIndexes010starPromise0.01FALSE#ff0000TRUE#ffffff15starFilletPromise0500.01FALSE#ff0000TRUE#ffffff15starFilletPromise0100FALSE-10.30.005TRUE#3333ffTRUE#000000240040010100.450.50.5FALSE#ffffff#ffffff","version":"0.20.13","type":"blockly"}} + script={{"script":"nrRaysinnerFilletRadiusouterRadiusFilletfilletIndexesradiusLististarPromisestarFilletPromisenrRays7innerFilletRadius1.7outerRadiusFillet0.6filletIndexes11MULTIPLYnrRays2'clearSky'10000.10.7radiusListi1MULTIPLYnrRays21EQi20INSERTLASTradiusListouterRadiusFilletINSERTLASTradiusListinnerFilletRadiusstarPromise000010nrRays1044FALSEstarFilletPromisestarPromiseradiusListfilletIndexes010starPromise0.01FALSE#ff0000TRUE#ffffff15starFilletPromise0500.01FALSE#ff0000TRUE#ffffff15starFilletPromise0100FALSE-10.30.005TRUE#3333ffTRUE#000000240040010100.450.50.5FALSE#ffffff#ffffff","version":"0.20.14","type":"blockly"}} title="Fillet 3D Wire Specific Corners" /> **Rete Example: Fillet Specific Corners of a 3D Wire** diff --git a/docs/learn/code/common/occt/fillets/fillets-intro.mdx b/docs/learn/code/common/occt/fillets/fillets-intro.mdx index 68b75a4d..1719dc04 100644 --- a/docs/learn/code/common/occt/fillets/fillets-intro.mdx +++ b/docs/learn/code/common/occt/fillets/fillets-intro.mdx @@ -43,21 +43,21 @@ The following examples in TypeScript, Rete, and Blockly demonstrate creating a s **TypeScript Example: Fillet All Edges of a Solid** {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const filleted = await bitbybit.occt.fillets.filletEdges({\n shape: box,\n radius: 1,\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: filleted,\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const filleted = await bitbybit.occt.fillets.filletEdges({\n shape: box,\n radius: 1,\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: filleted,\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Fillet Solid" /> **Blockly Example: Fillet All Edges of a Solid** 58100001","version":"0.20.13","type":"blockly"}} + script={{"script":"58100001","version":"0.20.14","type":"blockly"}} title="Fillet Solid" /> **Rete Example: Fillet All Edges of a Solid** diff --git a/docs/learn/code/common/occt/fillets/fillets-on-2d-wire-corners.mdx b/docs/learn/code/common/occt/fillets/fillets-on-2d-wire-corners.mdx index d0d5f942..919a7a95 100644 --- a/docs/learn/code/common/occt/fillets/fillets-on-2d-wire-corners.mdx +++ b/docs/learn/code/common/occt/fillets/fillets-on-2d-wire-corners.mdx @@ -30,21 +30,21 @@ The examples below demonstrate creating a 2D wire and then applying fillets with **TypeScript Example: Fillet Specific Corners of a Wire with Variable Radii** {\n const squareOpt = new Bit.Inputs.OCCT.SquareDto();\n const square = await bitbybit.occt.shapes.wire.createSquareWire(squareOpt);\n const squareFillet = await bitbybit.occt.fillets.fillet2d({\n shape: square,\n radiusList: [0.1, 0.3],\n indexes: [1, 3]\n });\n\n bitbybit.draw.drawAnyAsync({\n entity: squareFillet\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const squareOpt = new Bit.Inputs.OCCT.SquareDto();\n const square = await bitbybit.occt.shapes.wire.createSquareWire(squareOpt);\n const squareFillet = await bitbybit.occt.fillets.fillet2d({\n shape: square,\n radiusList: [0.1, 0.3],\n indexes: [1, 3]\n });\n\n bitbybit.draw.drawAnyAsync({\n entity: squareFillet\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Fillet Specific Corners of Wire" /> **Blockly Example: Fillet Specific Corners of a Wire with Variable Radii** 10000100.10.313","version":"0.20.13","type":"blockly"}} + script={{"script":"10000100.10.313","version":"0.20.14","type":"blockly"}} title="Fillet Specific Corners of Wire" /> **Rete Example: Fillet Specific Corners of a Wire with Variable Radii** diff --git a/docs/learn/code/common/occt/fillets/fillets-on-2d-wires.mdx b/docs/learn/code/common/occt/fillets/fillets-on-2d-wires.mdx index 3497f5a5..40835027 100644 --- a/docs/learn/code/common/occt/fillets/fillets-on-2d-wires.mdx +++ b/docs/learn/code/common/occt/fillets/fillets-on-2d-wires.mdx @@ -29,21 +29,21 @@ The examples below demonstrate how to create a simple 2D wire (e.g., a polyline **TypeScript Example: Fillet All Corners of a Wire** {\n const squareOpt = new Bit.Inputs.OCCT.SquareDto();\n const square = await bitbybit.occt.shapes.wire.createSquareWire(squareOpt);\n const squareFillet = await bitbybit.occt.fillets.fillet2d({\n shape: square,\n radius: 0.25\n });\n\n bitbybit.draw.drawAnyAsync({\n entity: squareFillet\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const squareOpt = new Bit.Inputs.OCCT.SquareDto();\n const square = await bitbybit.occt.shapes.wire.createSquareWire(squareOpt);\n const squareFillet = await bitbybit.occt.fillets.fillet2d({\n shape: square,\n radius: 0.25\n });\n\n bitbybit.draw.drawAnyAsync({\n entity: squareFillet\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Fillet All Corners of Wire" /> **Blockly Example: Fillet All Corners of a Wire** 10000100.24","version":"0.20.13","type":"blockly"}} + script={{"script":"10000100.24","version":"0.20.14","type":"blockly"}} title="Fillet All Corners of Wire" /> **Rete Example: Fillet All Corners of a Wire** diff --git a/docs/learn/code/common/occt/fillets/fillets-var-radius-on-spec-edges.mdx b/docs/learn/code/common/occt/fillets/fillets-var-radius-on-spec-edges.mdx index ea3eaf01..f210a0d9 100644 --- a/docs/learn/code/common/occt/fillets/fillets-var-radius-on-spec-edges.mdx +++ b/docs/learn/code/common/occt/fillets/fillets-var-radius-on-spec-edges.mdx @@ -31,7 +31,7 @@ The examples below demonstrate creating a solid box and then applying fillets wi **TypeScript Example: Fillet Specific Edges with Variable Radii** {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const filleted = await bitbybit.occt.fillets.filletEdges({\n shape: box,\n radiusList: [1, 2, 0.3],\n indexes: [1, 2, 3]\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: filleted,\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const filleted = await bitbybit.occt.fillets.filletEdges({\n shape: box,\n radiusList: [1, 2, 0.3],\n indexes: [1, 2, 3]\n })\n\n bitbybit.draw.drawAnyAsync({\n entity: filleted,\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Variable Fillet Radius On Spec Edges" /> @@ -39,7 +39,7 @@ The examples below demonstrate creating a solid box and then applying fillets wi **Blockly Example: Fillet Specific Edges with Variable Radii** 5810000120.3123","version":"0.20.13","type":"blockly"}} + script={{"script":"5810000120.3123","version":"0.20.14","type":"blockly"}} title="Variable Fillet Radius On Spec Edges" /> @@ -47,7 +47,7 @@ The examples below demonstrate creating a solid box and then applying fillets wi **Rete Example: Fillet Specific Edges with Variable Radii** diff --git a/docs/learn/code/common/occt/io/dxf-file-format-for-2d-drawings.md b/docs/learn/code/common/occt/io/dxf-file-format-for-2d-drawings.md index f4d601b1..3b13ae66 100644 --- a/docs/learn/code/common/occt/io/dxf-file-format-for-2d-drawings.md +++ b/docs/learn/code/common/occt/io/dxf-file-format-for-2d-drawings.md @@ -36,21 +36,21 @@ It's worth noting that while DXF is an extensive format with numerous entity typ fontUrlorangeColorpurpleColorgreenColortext1text1Compoundtext1WiresfilletedWireswirefilletedWiretext1Compound2dxfPaths1dxfLayer1text2text2Compoundtext2Wirestext2Compound2dxfPaths2dxfLayer2hexagonshexagonsCompounddxfPaths3dxfLayer3allLayersdxfFilefontUrlhttps://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/fonts/Tektur/Tektur-Bold.ttforangeColor#ffa200purpleColor#cc80ffgreenColor#80ff00text1DXF @ bitbybit.devfontUrl1.50180002.5010centerMiddletext1Compoundtext1compoundtext1Wirestext1CompoundfilletedWireswiretext1WiresfilletedWirewire0.05INSERTLASTfilletedWiresfilletedWiretext1Compound2filletedWiresdxfPaths1text1Compound20.10.12dxfLayer1dxfPaths1BitbybitorangeColortext2CAD PowerfontUrl1018000-2010centerMiddletext2Compoundtext2compoundtext2Wirestext2Compoundtext2Compound2text2WiresdxfPaths2text2Compound20.10.12dxfLayer2dxfPaths2CAD PowerpurpleColorhexagons1524010TRUE[0.8,0.3][0.8,0.3][true, true, false]hexagonsCompoundhexagonsdxfPaths3hexagonsCompound0.10.12dxfLayer3dxfPaths3Hexagons#002afaallLayersdxfLayer1dxfLayer2dxfLayer3dxfFileallLayersbitbybit-dev.dxfTRUEfilletedWires0.01FALSETRUEorangeColor5text2Wires0.01FALSETRUEpurpleColor3hexagonsCompound0.01FALSETRUEgreenColor3","version":"0.20.13","type":"blockly"}} + script={{"script":"fontUrlorangeColorpurpleColorgreenColortext1text1Compoundtext1WiresfilletedWireswirefilletedWiretext1Compound2dxfPaths1dxfLayer1text2text2Compoundtext2Wirestext2Compound2dxfPaths2dxfLayer2hexagonshexagonsCompounddxfPaths3dxfLayer3allLayersdxfFilefontUrlhttps://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/fonts/Tektur/Tektur-Bold.ttforangeColor#ffa200purpleColor#cc80ffgreenColor#80ff00text1DXF @ bitbybit.devfontUrl1.50180002.5010centerMiddletext1Compoundtext1compoundtext1Wirestext1CompoundfilletedWireswiretext1WiresfilletedWirewire0.05INSERTLASTfilletedWiresfilletedWiretext1Compound2filletedWiresdxfPaths1text1Compound20.10.12dxfLayer1dxfPaths1BitbybitorangeColortext2CAD PowerfontUrl1018000-2010centerMiddletext2Compoundtext2compoundtext2Wirestext2Compoundtext2Compound2text2WiresdxfPaths2text2Compound20.10.12dxfLayer2dxfPaths2CAD PowerpurpleColorhexagons1524010TRUE[0.8,0.3][0.8,0.3][true, true, false]hexagonsCompoundhexagonsdxfPaths3hexagonsCompound0.10.12dxfLayer3dxfPaths3Hexagons#002afaallLayersdxfLayer1dxfLayer2dxfLayer3dxfFileallLayersbitbybit-dev.dxfTRUEfilletedWires0.01FALSETRUEorangeColor5text2Wires0.01FALSETRUEpurpleColor3hexagonsCompound0.01FALSETRUEgreenColor3","version":"0.20.14","type":"blockly"}} title="DXF Export with Layers" /> {\n // Font URL for text creation\n const fontUrl = \"https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/fonts/Tektur/Tektur-Bold.ttf\";\n\n // Define colors\n const orangeColor = \"#ffa200\";\n const purpleColor = \"#cc80ff\";\n const blueColor = \"#80ff00\";\n\n // Create first text: \"DXF @ bitbybit.dev\" at z=2.5\n const text1Options = new Text3DUrlDto();\n text1Options.text = \"DXF @ bitbybit.dev\";\n text1Options.fontUrl = fontUrl;\n text1Options.fontSize = 1.5;\n text1Options.height = 0;\n text1Options.rotation = 180;\n text1Options.origin = [0, 0, 2.5] as Point3;\n text1Options.direction = [0, 1, 0] as Vector3;\n\n const text1 = await bitbybit.advanced.text3d.createWithUrl(text1Options);\n const text1Compound = text1.compound;\n\n // Get wires from the first text\n const text1Wires: TopoDSWirePointer[] = await bitbybit.occt.shapes.wire.getWires({ shape: text1Compound });\n\n // Apply fillet to each wire\n const filletedWires: TopoDSWirePointer[] = [];\n for (const wire of text1Wires) {\n const filletOptions = new FilletDto();\n filletOptions.shape = wire;\n filletOptions.radius = 0.05;\n\n const filletedWire = await bitbybit.occt.fillets.fillet2d(filletOptions);\n filletedWires.push(filletedWire);\n }\n\n // Create compound from filleted wires\n const text1Compound2 = await bitbybit.occt.shapes.compound.makeCompound({ shapes: filletedWires });\n\n // Convert first text to DXF paths\n const dxfPaths1Options = new ShapeToDxfPathsDto();\n dxfPaths1Options.shape = text1Compound2;\n dxfPaths1Options.angularDeflection = 0.1;\n dxfPaths1Options.curvatureDeflection = 0.1;\n dxfPaths1Options.minimumOfPoints = 2;\n\n const dxfPaths1 = await bitbybit.occt.io.shapeToDxfPaths(dxfPaths1Options);\n\n // Create layer for first text\n const dxfLayer1Options = new DxfPathsWithLayerDto();\n dxfLayer1Options.paths = dxfPaths1;\n dxfLayer1Options.layer = \"Bitbybit\";\n dxfLayer1Options.color = orangeColor;\n\n const dxfLayer1 = await bitbybit.occt.io.dxfPathsWithLayer(dxfLayer1Options);\n\n // Create second text: \"CAD Power\" at z=-2\n const text2Options = new Text3DUrlDto();\n text2Options.text = \"CAD Power\";\n text2Options.fontUrl = fontUrl;\n text2Options.fontSize = 1;\n text2Options.height = 0;\n text2Options.rotation = 180;\n text2Options.origin = [0, 0, -2] as Point3;\n text2Options.direction = [0, 1, 0] as Vector3;\n\n const text2 = await bitbybit.advanced.text3d.createWithUrl(text2Options);\n const text2Compound = text2.compound;\n\n // Get wires from the second text\n const text2Wires: TopoDSWirePointer[] = await bitbybit.occt.shapes.wire.getWires({ shape: text2Compound });\n\n // Create compound from text2 wires (no fillet for second text)\n const text2Compound2 = await bitbybit.occt.shapes.compound.makeCompound({ shapes: text2Wires });\n\n // Convert second text to DXF paths\n const dxfPaths2Options = new ShapeToDxfPathsDto();\n dxfPaths2Options.shape = text2Compound2;\n dxfPaths2Options.angularDeflection = 0.1;\n dxfPaths2Options.curvatureDeflection = 0.1;\n dxfPaths2Options.minimumOfPoints = 2;\n\n const dxfPaths2 = await bitbybit.occt.io.shapeToDxfPaths(dxfPaths2Options);\n\n // Create layer for second text\n const dxfLayer2Options = new DxfPathsWithLayerDto();\n dxfLayer2Options.paths = dxfPaths2;\n dxfLayer2Options.layer = \"CAD Power\";\n dxfLayer2Options.color = purpleColor;\n\n const dxfLayer2 = await bitbybit.occt.io.dxfPathsWithLayer(dxfLayer2Options);\n\n // Create hexagonal grid\n const hexagonsOptions = new HexagonsInGridDto();\n hexagonsOptions.width = 15;\n hexagonsOptions.height = 2;\n hexagonsOptions.nrHexagonsInWidth = 40;\n hexagonsOptions.nrHexagonsInHeight = 10;\n hexagonsOptions.flatTop = true;\n hexagonsOptions.extendTop = false;\n hexagonsOptions.extendBottom = false;\n hexagonsOptions.extendLeft = false;\n hexagonsOptions.extendRight = false;\n hexagonsOptions.scalePatternWidth = [0.8, 0.3];\n hexagonsOptions.scalePatternHeight = [0.8, 0.3];\n hexagonsOptions.inclusionPattern = [true, true, false];\n\n const hexagons = await bitbybit.occt.shapes.wire.hexagonsInGrid(hexagonsOptions);\n\n // Create compound from hexagons\n const hexagonsCompound = await bitbybit.occt.shapes.compound.makeCompound({ shapes: hexagons });\n\n // Convert hexagons to DXF paths\n const dxfPaths3Options = new ShapeToDxfPathsDto();\n dxfPaths3Options.shape = hexagonsCompound;\n dxfPaths3Options.angularDeflection = 0.1;\n dxfPaths3Options.curvatureDeflection = 0.1;\n dxfPaths3Options.minimumOfPoints = 2;\n\n const dxfPaths3 = await bitbybit.occt.io.shapeToDxfPaths(dxfPaths3Options);\n\n // Create layer for hexagons\n const dxfLayer3Options = new DxfPathsWithLayerDto();\n dxfLayer3Options.paths = dxfPaths3;\n dxfLayer3Options.layer = \"Hexagons\";\n dxfLayer3Options.color = \"#002afa\";\n\n const dxfLayer3 = await bitbybit.occt.io.dxfPathsWithLayer(dxfLayer3Options);\n\n // Combine all layers\n const allLayers = [dxfLayer1, dxfLayer2, dxfLayer3];\n\n // Create DXF file\n const dxfCreateOptions = new DxfPathsPartsListDto();\n dxfCreateOptions.pathsParts = allLayers;\n dxfCreateOptions.fileName = \"bitbybit-dev.dxf\";\n dxfCreateOptions.tryDownload = true;\n\n await bitbybit.occt.io.dxfCreate(dxfCreateOptions);\n\n // Draw the shapes for visualization\n const drawOptions1 = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions1.precision = 0.01;\n drawOptions1.drawFaces = false;\n drawOptions1.drawEdges = true;\n drawOptions1.edgeColour = orangeColor;\n drawOptions1.edgeWidth = 5;\n\n bitbybit.draw.drawAnyAsync({ entity: filletedWires, options: drawOptions1 });\n\n const drawOptions2 = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions2.precision = 0.01;\n drawOptions2.drawFaces = false;\n drawOptions2.drawEdges = true;\n drawOptions2.edgeColour = purpleColor;\n drawOptions2.edgeWidth = 3;\n\n bitbybit.draw.drawAnyAsync({ entity: text2Wires, options: drawOptions2 });\n\n // Draw the shapes for visualization\n const drawOptions3 = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions3.precision = 0.01;\n drawOptions3.drawFaces = false;\n drawOptions3.drawEdges = true;\n drawOptions3.edgeColour = blueColor;\n drawOptions3.edgeWidth = 2;\n\n bitbybit.draw.drawAnyAsync({ entity: hexagonsCompound, options: drawOptions3 });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { Text3DUrlDto } = Bit.Advanced.Text3D;\nconst { ShapeToDxfPathsDto, DxfPathsWithLayerDto, DxfPathsPartsListDto } = Bit.Inputs.OCCT;\nconst { FilletDto } = Bit.Inputs.OCCT;\nconst { HexagonsInGridDto } = Bit.Inputs.OCCT;\n\n// Import required types\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define the main function\nconst start = async () => {\n // Font URL for text creation\n const fontUrl = \"https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/fonts/Tektur/Tektur-Bold.ttf\";\n\n // Define colors\n const orangeColor = \"#ffa200\";\n const purpleColor = \"#cc80ff\";\n const blueColor = \"#80ff00\";\n\n // Create first text: \"DXF @ bitbybit.dev\" at z=2.5\n const text1Options = new Text3DUrlDto();\n text1Options.text = \"DXF @ bitbybit.dev\";\n text1Options.fontUrl = fontUrl;\n text1Options.fontSize = 1.5;\n text1Options.height = 0;\n text1Options.rotation = 180;\n text1Options.origin = [0, 0, 2.5] as Point3;\n text1Options.direction = [0, 1, 0] as Vector3;\n\n const text1 = await bitbybit.advanced.text3d.createWithUrl(text1Options);\n const text1Compound = text1.compound;\n\n // Get wires from the first text\n const text1Wires: TopoDSWirePointer[] = await bitbybit.occt.shapes.wire.getWires({ shape: text1Compound });\n\n // Apply fillet to each wire\n const filletedWires: TopoDSWirePointer[] = [];\n for (const wire of text1Wires) {\n const filletOptions = new FilletDto();\n filletOptions.shape = wire;\n filletOptions.radius = 0.05;\n\n const filletedWire = await bitbybit.occt.fillets.fillet2d(filletOptions);\n filletedWires.push(filletedWire);\n }\n\n // Create compound from filleted wires\n const text1Compound2 = await bitbybit.occt.shapes.compound.makeCompound({ shapes: filletedWires });\n\n // Convert first text to DXF paths\n const dxfPaths1Options = new ShapeToDxfPathsDto();\n dxfPaths1Options.shape = text1Compound2;\n dxfPaths1Options.angularDeflection = 0.1;\n dxfPaths1Options.curvatureDeflection = 0.1;\n dxfPaths1Options.minimumOfPoints = 2;\n\n const dxfPaths1 = await bitbybit.occt.io.shapeToDxfPaths(dxfPaths1Options);\n\n // Create layer for first text\n const dxfLayer1Options = new DxfPathsWithLayerDto();\n dxfLayer1Options.paths = dxfPaths1;\n dxfLayer1Options.layer = \"Bitbybit\";\n dxfLayer1Options.color = orangeColor;\n\n const dxfLayer1 = await bitbybit.occt.io.dxfPathsWithLayer(dxfLayer1Options);\n\n // Create second text: \"CAD Power\" at z=-2\n const text2Options = new Text3DUrlDto();\n text2Options.text = \"CAD Power\";\n text2Options.fontUrl = fontUrl;\n text2Options.fontSize = 1;\n text2Options.height = 0;\n text2Options.rotation = 180;\n text2Options.origin = [0, 0, -2] as Point3;\n text2Options.direction = [0, 1, 0] as Vector3;\n\n const text2 = await bitbybit.advanced.text3d.createWithUrl(text2Options);\n const text2Compound = text2.compound;\n\n // Get wires from the second text\n const text2Wires: TopoDSWirePointer[] = await bitbybit.occt.shapes.wire.getWires({ shape: text2Compound });\n\n // Create compound from text2 wires (no fillet for second text)\n const text2Compound2 = await bitbybit.occt.shapes.compound.makeCompound({ shapes: text2Wires });\n\n // Convert second text to DXF paths\n const dxfPaths2Options = new ShapeToDxfPathsDto();\n dxfPaths2Options.shape = text2Compound2;\n dxfPaths2Options.angularDeflection = 0.1;\n dxfPaths2Options.curvatureDeflection = 0.1;\n dxfPaths2Options.minimumOfPoints = 2;\n\n const dxfPaths2 = await bitbybit.occt.io.shapeToDxfPaths(dxfPaths2Options);\n\n // Create layer for second text\n const dxfLayer2Options = new DxfPathsWithLayerDto();\n dxfLayer2Options.paths = dxfPaths2;\n dxfLayer2Options.layer = \"CAD Power\";\n dxfLayer2Options.color = purpleColor;\n\n const dxfLayer2 = await bitbybit.occt.io.dxfPathsWithLayer(dxfLayer2Options);\n\n // Create hexagonal grid\n const hexagonsOptions = new HexagonsInGridDto();\n hexagonsOptions.width = 15;\n hexagonsOptions.height = 2;\n hexagonsOptions.nrHexagonsInWidth = 40;\n hexagonsOptions.nrHexagonsInHeight = 10;\n hexagonsOptions.flatTop = true;\n hexagonsOptions.extendTop = false;\n hexagonsOptions.extendBottom = false;\n hexagonsOptions.extendLeft = false;\n hexagonsOptions.extendRight = false;\n hexagonsOptions.scalePatternWidth = [0.8, 0.3];\n hexagonsOptions.scalePatternHeight = [0.8, 0.3];\n hexagonsOptions.inclusionPattern = [true, true, false];\n\n const hexagons = await bitbybit.occt.shapes.wire.hexagonsInGrid(hexagonsOptions);\n\n // Create compound from hexagons\n const hexagonsCompound = await bitbybit.occt.shapes.compound.makeCompound({ shapes: hexagons });\n\n // Convert hexagons to DXF paths\n const dxfPaths3Options = new ShapeToDxfPathsDto();\n dxfPaths3Options.shape = hexagonsCompound;\n dxfPaths3Options.angularDeflection = 0.1;\n dxfPaths3Options.curvatureDeflection = 0.1;\n dxfPaths3Options.minimumOfPoints = 2;\n\n const dxfPaths3 = await bitbybit.occt.io.shapeToDxfPaths(dxfPaths3Options);\n\n // Create layer for hexagons\n const dxfLayer3Options = new DxfPathsWithLayerDto();\n dxfLayer3Options.paths = dxfPaths3;\n dxfLayer3Options.layer = \"Hexagons\";\n dxfLayer3Options.color = \"#002afa\";\n\n const dxfLayer3 = await bitbybit.occt.io.dxfPathsWithLayer(dxfLayer3Options);\n\n // Combine all layers\n const allLayers = [dxfLayer1, dxfLayer2, dxfLayer3];\n\n // Create DXF file\n const dxfCreateOptions = new DxfPathsPartsListDto();\n dxfCreateOptions.pathsParts = allLayers;\n dxfCreateOptions.fileName = \"bitbybit-dev.dxf\";\n dxfCreateOptions.tryDownload = true;\n\n await bitbybit.occt.io.dxfCreate(dxfCreateOptions);\n\n // Draw the shapes for visualization\n const drawOptions1 = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions1.precision = 0.01;\n drawOptions1.drawFaces = false;\n drawOptions1.drawEdges = true;\n drawOptions1.edgeColour = orangeColor;\n drawOptions1.edgeWidth = 5;\n\n bitbybit.draw.drawAnyAsync({ entity: filletedWires, options: drawOptions1 });\n\n const drawOptions2 = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions2.precision = 0.01;\n drawOptions2.drawFaces = false;\n drawOptions2.drawEdges = true;\n drawOptions2.edgeColour = purpleColor;\n drawOptions2.edgeWidth = 3;\n\n bitbybit.draw.drawAnyAsync({ entity: text2Wires, options: drawOptions2 });\n\n // Draw the shapes for visualization\n const drawOptions3 = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions3.precision = 0.01;\n drawOptions3.drawFaces = false;\n drawOptions3.drawEdges = true;\n drawOptions3.edgeColour = blueColor;\n drawOptions3.edgeWidth = 2;\n\n bitbybit.draw.drawAnyAsync({ entity: hexagonsCompound, options: drawOptions3 });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="DXF Export with Layers" /> diff --git a/docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md b/docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md index b5ff52d4..f2bd3e64 100644 --- a/docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md +++ b/docs/learn/code/common/occt/modeling/festive-decor/frost-flower.md @@ -34,7 +34,7 @@ The algorithm: diff --git a/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md b/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md index c420d5be..9d904508 100644 --- a/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md +++ b/docs/learn/code/common/occt/modeling/festive-decor/star-ornament.md @@ -28,21 +28,21 @@ Our star begins as a simple 2D wire created using the `createStarWire` function. 552000010FALSE0.3TRUE0100.150.001TRUE#0000001Star Material#ffffff#00ffcc0.80.991FALSE2","version":"0.20.13","type":"blockly"}} + script={{"script":"552000010FALSE0.3TRUE0100.150.001TRUE#0000001Star Material#ffffff#00ffcc0.80.991FALSE2","version":"0.20.14","type":"blockly"}} title="Star ornament" /> {\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = 5;\n starOptions.innerRadius = 2;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Apply 3D fillet to smooth all edges\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starSolid;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { StarDto, FilletDto, FaceFromWireDto, ExtrudeDto } = Bit.Inputs.OCCT;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\n\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;\n\nconst { wire, face } = bitbybit.occt.shapes;\nconst { fillets, operations } = bitbybit.occt;\n\nconst start = async () => {\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = 5;\n starOptions.innerRadius = 2;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Apply 3D fillet to smooth all edges\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starSolid;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Star ornament" /> @@ -75,21 +75,21 @@ The cylinder is positioned at the top of the star (at the outer radius on the Z- outerRadiusouterRadius5.10000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.150.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001","version":"0.20.13","type":"blockly"}} + script={{"script":"outerRadiusouterRadius5.10000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.150.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001","version":"0.20.14","type":"blockly"}} title="Star ornament with hanging hole" /> {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { StarDto, FilletDto, FaceFromWireDto, ExtrudeDto, CylinderDto, DifferenceDto } = Bit.Inputs.OCCT;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\n\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\nconst { wire, face, solid } = bitbybit.occt.shapes;\nconst { fillets, operations, booleans } = bitbybit.occt;\n\nconst start = async () => {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Star ornament with hanging hole" /> @@ -117,21 +117,21 @@ Now that we have our star ornament ready, let's export it as an STL file for 3D outerRadiusfinalStarouterRadius5.1finalStar0000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.15finalStar0.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001finalStarstar.stl0.001FALSETRUE","version":"0.20.13","type":"blockly"}} + script={{"script":"outerRadiusfinalStarouterRadius5.1finalStar0000105outerRadiusDIVIDEouterRadius3FALSE0.3TRUE010DIVIDEouterRadius2000100.220.15finalStar0.001Star Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001finalStarstar.stl0.001FALSETRUE","version":"0.20.14","type":"blockly"}} title="Star ornament with STL export" /> {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n\n // Export to STL for 3D printing\n const stlOptions = new SaveStlDto();\n stlOptions.shape = finalStar;\n stlOptions.fileName = \"star.stl\";\n stlOptions.precision = 0.001;\n stlOptions.adjustYtoZ = false;\n stlOptions.tryDownload = true;\n stlOptions.binary = true;\n await io.saveShapeStl(stlOptions);\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { StarDto, FilletDto, FaceFromWireDto, ExtrudeDto, CylinderDto, DifferenceDto } = Bit.Inputs.OCCT;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\nconst { SaveStlDto } = Bit.Inputs.OCCT;\n\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\nconst { wire, face, solid } = bitbybit.occt.shapes;\nconst { fillets, operations, booleans, io } = bitbybit.occt;\n\nconst start = async () => {\n // Define outer radius as a variable (like the slider in Rete)\n const outerRadius = 5.1;\n const innerRadius = outerRadius / 3;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Star Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the star wire with 5 points\n const starOptions = new StarDto();\n starOptions.numRays = 5;\n starOptions.outerRadius = outerRadius;\n starOptions.innerRadius = innerRadius;\n starOptions.center = [0, 0, 0];\n starOptions.direction = [0, 1, 0];\n starOptions.half = false;\n const starWire = await wire.createStarWire(starOptions);\n\n // Apply 2D fillet to round the star's corners\n const fillet2dOptions = new FilletDto();\n fillet2dOptions.shape = starWire;\n fillet2dOptions.radius = 0.3;\n const filletedWire = await fillets.fillet2d(fillet2dOptions);\n\n // Create a face from the filleted wire\n const faceOptions = new FaceFromWireDto();\n faceOptions.shape = filletedWire;\n faceOptions.planar = true;\n const starFace = await face.createFaceFromWire(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = starFace;\n extrudeOptions.direction = [0, 1, 0];\n const starSolid = await operations.extrude(extrudeOptions);\n\n // Create cylinder for the hanging hole\n const cylinderOptions = new CylinderDto();\n cylinderOptions.center = [outerRadius / 2, 0, 0]; // Position based on outer radius\n cylinderOptions.direction = [0, 1, 0];\n cylinderOptions.radius = 0.2;\n cylinderOptions.height = 2;\n const holeCylinder = await solid.createCylinder(cylinderOptions);\n\n // Boolean difference to cut the hole (before filleting edges)\n const diffOptions = new DifferenceDto();\n diffOptions.shape = starSolid;\n diffOptions.shapes = [holeCylinder];\n diffOptions.keepEdges = false;\n const starWithHole = await booleans.difference(diffOptions);\n\n // Apply 3D fillet to smooth all edges (after boolean)\n const fillet3dOptions = new FilletDto();\n fillet3dOptions.shape = starWithHole;\n fillet3dOptions.radius = 0.15;\n const finalStar = await fillets.filletEdges(fillet3dOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n bitbybit.draw.drawAnyAsync({\n entity: finalStar,\n options: drawOptions\n });\n\n // Export to STL for 3D printing\n const stlOptions = new SaveStlDto();\n stlOptions.shape = finalStar;\n stlOptions.fileName = \"star.stl\";\n stlOptions.precision = 0.001;\n stlOptions.adjustYtoZ = false;\n stlOptions.tryDownload = true;\n stlOptions.binary = true;\n await io.saveShapeStl(stlOptions);\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Star ornament with STL export" /> diff --git a/docs/learn/code/common/occt/modeling/festive-decor/tree.md b/docs/learn/code/common/occt/modeling/festive-decor/tree.md index 7bb6753c..9ba5bd41 100644 --- a/docs/learn/code/common/occt/modeling/festive-decor/tree.md +++ b/docs/learn/code/common/occt/modeling/festive-decor/tree.md @@ -28,21 +28,21 @@ Our tree starts with the `createChristmasTreeWire` function, which generates a s heightnrSkirtsthicknesstreeWireoffsetWirereversedWiretreeFacetreeSolidfinalTreedrawnTreeMeshheight8nrSkirts4thickness2treeWireheight1.53nrSkirts11FALSE0000010offsetWiretreeWire-0.20.1reversedWireoffsetWiretreeFacetreeWirereversedWireTRUEtreeSolidtreeFace00thicknessfinalTreetreeSolid00DIVIDENEGthickness2drawnTreeMeshfinalTree0.001Tree Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001drawnTreeMeshTRUE0.80TRUE","version":"0.20.13","type":"blockly"}} + script={{"script":"heightnrSkirtsthicknesstreeWireoffsetWirereversedWiretreeFacetreeSolidfinalTreedrawnTreeMeshheight8nrSkirts4thickness2treeWireheight1.53nrSkirts11FALSE0000010offsetWiretreeWire-0.20.1reversedWireoffsetWiretreeFacetreeWirereversedWireTRUEtreeSolidtreeFace00thicknessfinalTreetreeSolid00DIVIDENEGthickness2drawnTreeMeshfinalTree0.001Tree Material#ffffff#00ffcc0.80.991FALSE2TRUE#0000001drawnTreeMeshTRUE0.80TRUE","version":"0.20.14","type":"blockly"}} title="Christmas tree ornament" /> {\n // Define control variables\n const height = 6.5;\n const nrSkirts = 5;\n const thickness = 0.5;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Tree Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the Christmas tree wire\n const treeOptions = new ChristmasTreeDto();\n treeOptions.height = height;\n treeOptions.nrSkirts = nrSkirts;\n treeOptions.innerDist = 1.5;\n treeOptions.outerDist = 3;\n treeOptions.trunkHeight = 1;\n treeOptions.trunkWidth = 1;\n treeOptions.origin = [0, 0, 0];\n treeOptions.direction = [0, 1, 0];\n treeOptions.half = false;\n const treeWire = await wire.createChristmasTreeWire(treeOptions);\n\n // Create offset wire (inner boundary)\n const offsetOptions = new OffsetDto();\n offsetOptions.shape = treeWire;\n offsetOptions.distance = -0.2;\n offsetOptions.tolerance = 0.1;\n const offsetWire = await operations.offset(offsetOptions);\n\n // Reverse the inner wire for proper face creation\n const reversedWire = await wire.reversedWire({ shape: offsetWire });\n\n // Create face from both wires (outer and reversed inner)\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [treeWire, reversedWire];\n faceOptions.planar = true;\n const treeFace = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create thickness\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = treeFace;\n extrudeOptions.direction = [0, 0, thickness];\n const treeSolid = await operations.extrude(extrudeOptions);\n\n // Translate to center the shape\n const translateOptions = new TranslateDto();\n translateOptions.shape = treeSolid;\n translateOptions.translation = [0, 0, -thickness / 2];\n const finalTree = await transforms.translate(translateOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n const drawnTreeMesh = await bitbybit.draw.drawAnyAsync({\n entity: finalTree,\n options: drawOptions\n });\n\n const zoomOptions = new ZoomOnDto([drawnTreeMesh]);\n bitbybit.advanced.navigation.zoomOn(zoomOptions);\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { ChristmasTreeDto, OffsetDto, FaceFromWiresDto, ExtrudeDto, TranslateDto } = Bit.Inputs.OCCT;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\nconst { ZoomOnDto } = Bit.Advanced.Navigation;\n\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\nconst { wire, face } = bitbybit.occt.shapes;\nconst { operations, transforms } = bitbybit.occt;\n\nconst start = async () => {\n // Define control variables\n const height = 6.5;\n const nrSkirts = 5;\n const thickness = 0.5;\n\n // Create PBR material with emissive glow\n const materialOptions = new PBRMetallicRoughnessDto();\n materialOptions.name = \"Tree Material\";\n materialOptions.baseColor = \"#ffffff\";\n materialOptions.emissiveColor = \"#00ffcc\";\n materialOptions.metallic = 0.8;\n materialOptions.roughness = 0.99;\n materialOptions.alpha = 1;\n materialOptions.backFaceCulling = false;\n materialOptions.zOffset = 2;\n const material = bitbybit.babylon.material.pbrMetallicRoughness.create(materialOptions);\n\n // Create the Christmas tree wire\n const treeOptions = new ChristmasTreeDto();\n treeOptions.height = height;\n treeOptions.nrSkirts = nrSkirts;\n treeOptions.innerDist = 1.5;\n treeOptions.outerDist = 3;\n treeOptions.trunkHeight = 1;\n treeOptions.trunkWidth = 1;\n treeOptions.origin = [0, 0, 0];\n treeOptions.direction = [0, 1, 0];\n treeOptions.half = false;\n const treeWire = await wire.createChristmasTreeWire(treeOptions);\n\n // Create offset wire (inner boundary)\n const offsetOptions = new OffsetDto();\n offsetOptions.shape = treeWire;\n offsetOptions.distance = -0.2;\n offsetOptions.tolerance = 0.1;\n const offsetWire = await operations.offset(offsetOptions);\n\n // Reverse the inner wire for proper face creation\n const reversedWire = await wire.reversedWire({ shape: offsetWire });\n\n // Create face from both wires (outer and reversed inner)\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [treeWire, reversedWire];\n faceOptions.planar = true;\n const treeFace = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create thickness\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = treeFace;\n extrudeOptions.direction = [0, 0, thickness];\n const treeSolid = await operations.extrude(extrudeOptions);\n\n // Translate to center the shape\n const translateOptions = new TranslateDto();\n translateOptions.shape = treeSolid;\n translateOptions.translation = [0, 0, -thickness / 2];\n const finalTree = await transforms.translate(translateOptions);\n\n // Draw with custom material and edge styling\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOptions.precision = 0.001;\n drawOptions.drawEdges = true;\n drawOptions.edgeColour = \"#000000\";\n drawOptions.edgeWidth = 1;\n drawOptions.faceMaterial = material;\n\n const drawnTreeMesh = await bitbybit.draw.drawAnyAsync({\n entity: finalTree,\n options: drawOptions\n });\n\n const zoomOptions = new ZoomOnDto([drawnTreeMesh]);\n bitbybit.advanced.navigation.zoomOn(zoomOptions);\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Christmas tree ornament" /> diff --git a/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive-flat.md b/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive-flat.md index ac338012..c647526e 100644 --- a/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive-flat.md +++ b/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive-flat.md @@ -22,21 +22,21 @@ Flat hexagon hive construction creates optimized honeycomb panels by treating th widthheightnrHexWidthnrHexHeightwiresHexLargewiresHexSmallrandomHeightshexagonFacesiwidth10height20nrHexWidth10nrHexHeight20wiresHexLargewidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSEwiresHexSmallwidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSE0.70.7randomHeights12wiresHexLargehexagonFacesi1wiresHexLarge1INSERTLASThexagonFacesGETFROM_STARTwiresHexLargeiGETFROM_STARTwiresHexSmalliTRUEhexagonFaces1e-7TRUETRUETRUE010","version":"0.20.13","type":"blockly"}} + script={{"script":"widthheightnrHexWidthnrHexHeightwiresHexLargewiresHexSmallrandomHeightshexagonFacesiwidth10height20nrHexWidth10nrHexHeight20wiresHexLargewidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSEwiresHexSmallwidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSE0.70.7randomHeights12wiresHexLargehexagonFacesi1wiresHexLarge1INSERTLASThexagonFacesGETFROM_STARTwiresHexLargeiGETFROM_STARTwiresHexSmalliTRUEhexagonFaces1e-7TRUETRUETRUE010","version":"0.20.14","type":"blockly"}} title="Hexagon hive" /> {\n // Create hexagonal grid parameters\n const gridWidth = 10;\n const gridHeight = 20;\n const hexagonsInWidth = 10;\n const hexagonsInHeight = 20;\n\n // Generate large hexagonal wire grid (outer walls)\n const hexGridLargeOptions = new HexagonsInGridDto();\n hexGridLargeOptions.width = gridWidth;\n hexGridLargeOptions.height = gridHeight;\n hexGridLargeOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridLargeOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridLargeOptions.flatTop = false;\n hexGridLargeOptions.extendTop = false;\n hexGridLargeOptions.extendBottom = false;\n hexGridLargeOptions.extendLeft = false;\n hexGridLargeOptions.extendRight = false;\n\n const wiresHexLarge = await wire.hexagonsInGrid(hexGridLargeOptions);\n\n // Generate small hexagonal wire grid (inner holes)\n const hexGridSmallOptions = new HexagonsInGridDto();\n hexGridSmallOptions.width = gridWidth;\n hexGridSmallOptions.height = gridHeight;\n hexGridSmallOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridSmallOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridSmallOptions.flatTop = false;\n hexGridSmallOptions.extendTop = false;\n hexGridSmallOptions.extendBottom = false;\n hexGridSmallOptions.extendLeft = false;\n hexGridSmallOptions.extendRight = false;\n hexGridSmallOptions.scalePatternWidth = [0.7];\n hexGridSmallOptions.scalePatternHeight = [0.7];\n\n const wiresHexSmall = await wire.hexagonsInGrid(hexGridSmallOptions);\n\n // Generate random heights for creating individual faces\n const randomHeights = math.randomNumbers({\n low: 1,\n high: 2,\n count: wiresHexLarge.length\n });\n\n // Create faces with holes for each hexagon cell\n const hexagonFaces: TopoDSFacePointer[] = [];\n for (let i = 0; i < wiresHexLarge.length; i++) {\n // Get the outer and inner wires for this cell\n const outerWire = wiresHexLarge[i];\n const innerWire = wiresHexSmall[i];\n\n // Reverse the inner wire to create a hole\n const reverseOptions = new ShapeDto();\n reverseOptions.shape = innerWire;\n const reversedInnerWire = await wire.reversedWire(reverseOptions);\n\n // Create face with hole\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [outerWire, reversedInnerWire];\n faceOptions.planar = true;\n const hexFaceWithHole = await face.createFaceFromWires(faceOptions);\n\n hexagonFaces.push(hexFaceWithHole);\n }\n\n // Sew faces together\n const sewOptions = new SewDto(hexagonFaces);\n sewOptions.tolerance = 1e-7;\n const sewedFaces = await shell.sewFaces(sewOptions);\n\n // Unify same domain for cleaner geometry\n const unifyOptions = new UnifySameDomainDto();\n unifyOptions.shape = sewedFaces;\n unifyOptions.unifyEdges = true;\n unifyOptions.unifyFaces = true;\n unifyOptions.concatBSplines = true;\n const unifiedShape = await shape.unifySameDomain(unifyOptions);\n\n // Extrude the unified shape\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = unifiedShape;\n extrudeOptions.direction = [0, 1, 0];\n const extrudedHive = await operations.extrude(extrudeOptions);\n\n // Draw the resulting flat hive structure\n bitbybit.draw.drawAnyAsync({\n entity: extrudedHive\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for hexagon grid creation and operations\nconst { HexagonsInGridDto, FaceFromWiresDto, ExtrudeDto, ShapesDto, SewDto, UnifySameDomainDto, ShapeDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShellPointer = Bit.Inputs.OCCT.TopoDSShellPointer;\ntype TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;\n\n// Get access to OCCT modules and utilities\nconst { wire, face, shell, shape } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\nconst { math } = bitbybit;\n\n// Define the main function to create a flat hexagon hive\nconst start = async () => {\n // Create hexagonal grid parameters\n const gridWidth = 10;\n const gridHeight = 20;\n const hexagonsInWidth = 10;\n const hexagonsInHeight = 20;\n\n // Generate large hexagonal wire grid (outer walls)\n const hexGridLargeOptions = new HexagonsInGridDto();\n hexGridLargeOptions.width = gridWidth;\n hexGridLargeOptions.height = gridHeight;\n hexGridLargeOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridLargeOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridLargeOptions.flatTop = false;\n hexGridLargeOptions.extendTop = false;\n hexGridLargeOptions.extendBottom = false;\n hexGridLargeOptions.extendLeft = false;\n hexGridLargeOptions.extendRight = false;\n\n const wiresHexLarge = await wire.hexagonsInGrid(hexGridLargeOptions);\n\n // Generate small hexagonal wire grid (inner holes)\n const hexGridSmallOptions = new HexagonsInGridDto();\n hexGridSmallOptions.width = gridWidth;\n hexGridSmallOptions.height = gridHeight;\n hexGridSmallOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridSmallOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridSmallOptions.flatTop = false;\n hexGridSmallOptions.extendTop = false;\n hexGridSmallOptions.extendBottom = false;\n hexGridSmallOptions.extendLeft = false;\n hexGridSmallOptions.extendRight = false;\n hexGridSmallOptions.scalePatternWidth = [0.7];\n hexGridSmallOptions.scalePatternHeight = [0.7];\n\n const wiresHexSmall = await wire.hexagonsInGrid(hexGridSmallOptions);\n\n // Generate random heights for creating individual faces\n const randomHeights = math.randomNumbers({\n low: 1,\n high: 2,\n count: wiresHexLarge.length\n });\n\n // Create faces with holes for each hexagon cell\n const hexagonFaces: TopoDSFacePointer[] = [];\n for (let i = 0; i < wiresHexLarge.length; i++) {\n // Get the outer and inner wires for this cell\n const outerWire = wiresHexLarge[i];\n const innerWire = wiresHexSmall[i];\n\n // Reverse the inner wire to create a hole\n const reverseOptions = new ShapeDto();\n reverseOptions.shape = innerWire;\n const reversedInnerWire = await wire.reversedWire(reverseOptions);\n\n // Create face with hole\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [outerWire, reversedInnerWire];\n faceOptions.planar = true;\n const hexFaceWithHole = await face.createFaceFromWires(faceOptions);\n\n hexagonFaces.push(hexFaceWithHole);\n }\n\n // Sew faces together\n const sewOptions = new SewDto(hexagonFaces);\n sewOptions.tolerance = 1e-7;\n const sewedFaces = await shell.sewFaces(sewOptions);\n\n // Unify same domain for cleaner geometry\n const unifyOptions = new UnifySameDomainDto();\n unifyOptions.shape = sewedFaces;\n unifyOptions.unifyEdges = true;\n unifyOptions.unifyFaces = true;\n unifyOptions.concatBSplines = true;\n const unifiedShape = await shape.unifySameDomain(unifyOptions);\n\n // Extrude the unified shape\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = unifiedShape;\n extrudeOptions.direction = [0, 1, 0];\n const extrudedHive = await operations.extrude(extrudeOptions);\n\n // Draw the resulting flat hive structure\n bitbybit.draw.drawAnyAsync({\n entity: extrudedHive\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Hexagon hive" /> diff --git a/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive.md b/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive.md index 64ae749b..17eddda7 100644 --- a/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive.md +++ b/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-hive.md @@ -22,21 +22,21 @@ Moving beyond simple perforated patterns, three-dimensional hive structures crea widthheightnrHexWidthnrHexHeightwiresHexLargewiresHexSmallrandomHeightshexagons3DicompoundShapeForFastRenderingwidth10height20nrHexWidth10nrHexHeight20wiresHexLargewidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSEwiresHexSmallwidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSE0.80.8randomHeights12wiresHexLargehexagons3Di1wiresHexLarge1INSERTLASThexagons3DGETFROM_STARTwiresHexLargeiGETFROM_STARTwiresHexSmalliTRUE0GETFROM_STARTrandomHeightsi0compoundShapeForFastRenderinghexagons3DcompoundShapeForFastRendering","version":"0.20.13","type":"blockly"}} + script={{"script":"widthheightnrHexWidthnrHexHeightwiresHexLargewiresHexSmallrandomHeightshexagons3DicompoundShapeForFastRenderingwidth10height20nrHexWidth10nrHexHeight20wiresHexLargewidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSEwiresHexSmallwidthheightnrHexWidthnrHexHeightFALSEFALSEFALSEFALSEFALSE0.80.8randomHeights12wiresHexLargehexagons3Di1wiresHexLarge1INSERTLASThexagons3DGETFROM_STARTwiresHexLargeiGETFROM_STARTwiresHexSmalliTRUE0GETFROM_STARTrandomHeightsi0compoundShapeForFastRenderinghexagons3DcompoundShapeForFastRendering","version":"0.20.14","type":"blockly"}} title="Hexagon hive" /> {\n // Create hexagonal grid parameters\n const gridWidth = 10;\n const gridHeight = 20;\n const hexagonsInWidth = 10;\n const hexagonsInHeight = 20;\n\n // Generate large hexagonal wire grid (outer walls)\n const hexGridLargeOptions = new HexagonsInGridDto();\n hexGridLargeOptions.width = gridWidth;\n hexGridLargeOptions.height = gridHeight;\n hexGridLargeOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridLargeOptions.nrHexagonsInHeight = hexagonsInHeight;\n\n const wiresHexLarge = await wire.hexagonsInGrid(hexGridLargeOptions);\n\n // Generate small hexagonal wire grid (inner holes)\n const hexGridSmallOptions = new HexagonsInGridDto();\n hexGridSmallOptions.width = gridWidth;\n hexGridSmallOptions.height = gridHeight;\n hexGridSmallOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridSmallOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridSmallOptions.scalePatternWidth = [0.8];\n hexGridSmallOptions.scalePatternHeight = [0.8];\n\n const wiresHexSmall = await wire.hexagonsInGrid(hexGridSmallOptions);\n\n // Generate random heights for each hexagon cell\n const randomHeights = math.randomNumbers({\n low: 1,\n high: 2,\n count: wiresHexLarge.length\n });\n\n // Process each hexagon to create 3D cells with holes\n const hexagons3D: TopoDSSolidPointer[] = [];\n\n for (let i = 0; i < wiresHexLarge.length; i++) {\n // Get the outer and inner wires for this cell\n const outerWire = wiresHexLarge[i];\n const innerWire = wiresHexSmall[i];\n\n // Reverse the inner wire to create a hole\n const reverseOptions = new ShapeDto();\n reverseOptions.shape = innerWire;\n const reversedInnerWire = await wire.reversedWire(reverseOptions);\n\n // Create face with hole\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [outerWire, reversedInnerWire];\n faceOptions.planar = true;\n const hexFaceWithHole = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face with random height\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = hexFaceWithHole;\n extrudeOptions.direction = [0, randomHeights[i], 0];\n\n const extrudedCell = await operations.extrude(extrudeOptions);\n hexagons3D.push(extrudedCell);\n }\n\n // Combine all extruded cells into a compound shape for fast rendering\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = hexagons3D;\n const compoundShapeForFastRendering = await compound.makeCompound(compoundOptions);\n\n // Draw the resulting three-dimensional hive structure\n bitbybit.draw.drawAnyAsync({\n entity: compoundShapeForFastRendering\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for hexagon grid creation and operations\nconst { HexagonsInGridDto, FaceFromWiresDto, ExtrudeDto, CompoundShapesDto, ShapeDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;\n\n// Get access to OCCT modules and utilities\nconst { wire, face, compound } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\nconst { math } = bitbybit;\n\n// Define the main function to create a three-dimensional hexagon hive\nconst start = async () => {\n // Create hexagonal grid parameters\n const gridWidth = 10;\n const gridHeight = 20;\n const hexagonsInWidth = 10;\n const hexagonsInHeight = 20;\n\n // Generate large hexagonal wire grid (outer walls)\n const hexGridLargeOptions = new HexagonsInGridDto();\n hexGridLargeOptions.width = gridWidth;\n hexGridLargeOptions.height = gridHeight;\n hexGridLargeOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridLargeOptions.nrHexagonsInHeight = hexagonsInHeight;\n\n const wiresHexLarge = await wire.hexagonsInGrid(hexGridLargeOptions);\n\n // Generate small hexagonal wire grid (inner holes)\n const hexGridSmallOptions = new HexagonsInGridDto();\n hexGridSmallOptions.width = gridWidth;\n hexGridSmallOptions.height = gridHeight;\n hexGridSmallOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridSmallOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridSmallOptions.scalePatternWidth = [0.8];\n hexGridSmallOptions.scalePatternHeight = [0.8];\n\n const wiresHexSmall = await wire.hexagonsInGrid(hexGridSmallOptions);\n\n // Generate random heights for each hexagon cell\n const randomHeights = math.randomNumbers({\n low: 1,\n high: 2,\n count: wiresHexLarge.length\n });\n\n // Process each hexagon to create 3D cells with holes\n const hexagons3D: TopoDSSolidPointer[] = [];\n\n for (let i = 0; i < wiresHexLarge.length; i++) {\n // Get the outer and inner wires for this cell\n const outerWire = wiresHexLarge[i];\n const innerWire = wiresHexSmall[i];\n\n // Reverse the inner wire to create a hole\n const reverseOptions = new ShapeDto();\n reverseOptions.shape = innerWire;\n const reversedInnerWire = await wire.reversedWire(reverseOptions);\n\n // Create face with hole\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [outerWire, reversedInnerWire];\n faceOptions.planar = true;\n const hexFaceWithHole = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face with random height\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = hexFaceWithHole;\n extrudeOptions.direction = [0, randomHeights[i], 0];\n\n const extrudedCell = await operations.extrude(extrudeOptions);\n hexagons3D.push(extrudedCell);\n }\n\n // Combine all extruded cells into a compound shape for fast rendering\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = hexagons3D;\n const compoundShapeForFastRendering = await compound.makeCompound(compoundOptions);\n\n // Draw the resulting three-dimensional hive structure\n bitbybit.draw.drawAnyAsync({\n entity: compoundShapeForFastRendering\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Hexagon hive" /> diff --git a/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-holes-on-face.md b/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-holes-on-face.md index 3eb6d528..a60e6b6b 100644 --- a/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-holes-on-face.md +++ b/docs/learn/code/common/occt/modeling/hollow-shapes/hexagon-holes-on-face.md @@ -22,21 +22,21 @@ Hexagonal patterns are found throughout nature - from honeycomb structures to cr recFacefacesrecFace2014000010facesrecFace1010[0.9][0.9]FALSEFALSE0.010.01GETFIRSTfaces010","version":"0.20.13","type":"blockly"}} + script={{"script":"recFacefacesrecFace2014000010facesrecFace1010[0.9][0.9]FALSEFALSE0.010.01GETFIRSTfaces010","version":"0.20.14","type":"blockly"}} title="Hexagon holes on face" /> {\n // Create a rectangular face as the base shape\n const faceOptions = new RectangleDto();\n faceOptions.width = 20;\n faceOptions.length = 14;\n faceOptions.center = [0, 0, 0];\n faceOptions.direction = [0, 1, 0];\n const rectangleFace = await face.createRectangleFace(faceOptions);\n\n // Define scale patterns for U and V directions\n // For hexagons, uniform scaling often works best due to natural packing\n const scalePatternU = [0.9]; // Uniform hexagon size in U direction\n const scalePatternV = [0.9]; // Uniform hexagon size in V direction\n\n // Subdivide the face into hexagonal holes\n const subdivideOptions = new FaceSubdivideToHexagonHolesDto();\n subdivideOptions.shape = rectangleFace;\n subdivideOptions.nrHexagonsU = 10; // Number of hexagon divisions in U direction\n subdivideOptions.nrHexagonsV = 10; // Number of hexagon divisions in V direction\n subdivideOptions.flatU = false; // Pointy-top orientation (false) vs flat-top (true)\n subdivideOptions.holesToFaces = false; // Return wires instead of faces\n subdivideOptions.offsetFromBorderU = 0.01; // Small border offset in U direction\n subdivideOptions.offsetFromBorderV = 0.01; // Small border offset in V direction\n subdivideOptions.scalePatternU = scalePatternU;\n subdivideOptions.scalePatternV = scalePatternV;\n\n const holes = await face.subdivideToHexagonHoles(subdivideOptions);\n\n // Get the first hole (the outer boundary with hexagonal holes)\n const firstHole = lists.getItem({ list: holes, index: 0, clone: true });\n\n // Extrude the face with holes to create a 3D honeycomb structure\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = firstHole;\n extrudeOptions.direction = [0, 1, 0];\n const extrudedSolid = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with hexagonal holes\n bitbybit.draw.drawAnyAsync({\n entity: extrudedSolid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for face creation, subdivision, and extrusion\nconst { RectangleDto, FaceSubdivideToHexagonHolesDto, ExtrudeDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Get access to OCCT modules for face operations\nconst { face } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\nconst { lists } = bitbybit;\n\n// Define the main function to create hexagon holes on a face\nconst start = async () => {\n // Create a rectangular face as the base shape\n const faceOptions = new RectangleDto();\n faceOptions.width = 20;\n faceOptions.length = 14;\n faceOptions.center = [0, 0, 0];\n faceOptions.direction = [0, 1, 0];\n const rectangleFace = await face.createRectangleFace(faceOptions);\n\n // Define scale patterns for U and V directions\n // For hexagons, uniform scaling often works best due to natural packing\n const scalePatternU = [0.9]; // Uniform hexagon size in U direction\n const scalePatternV = [0.9]; // Uniform hexagon size in V direction\n\n // Subdivide the face into hexagonal holes\n const subdivideOptions = new FaceSubdivideToHexagonHolesDto();\n subdivideOptions.shape = rectangleFace;\n subdivideOptions.nrHexagonsU = 10; // Number of hexagon divisions in U direction\n subdivideOptions.nrHexagonsV = 10; // Number of hexagon divisions in V direction\n subdivideOptions.flatU = false; // Pointy-top orientation (false) vs flat-top (true)\n subdivideOptions.holesToFaces = false; // Return wires instead of faces\n subdivideOptions.offsetFromBorderU = 0.01; // Small border offset in U direction\n subdivideOptions.offsetFromBorderV = 0.01; // Small border offset in V direction\n subdivideOptions.scalePatternU = scalePatternU;\n subdivideOptions.scalePatternV = scalePatternV;\n\n const holes = await face.subdivideToHexagonHoles(subdivideOptions);\n\n // Get the first hole (the outer boundary with hexagonal holes)\n const firstHole = lists.getItem({ list: holes, index: 0, clone: true });\n\n // Extrude the face with holes to create a 3D honeycomb structure\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = firstHole;\n extrudeOptions.direction = [0, 1, 0];\n const extrudedSolid = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with hexagonal holes\n bitbybit.draw.drawAnyAsync({\n entity: extrudedSolid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Hexagon holes on face" /> diff --git a/docs/learn/code/common/occt/modeling/hollow-shapes/rectangle-holes-on-face.md b/docs/learn/code/common/occt/modeling/hollow-shapes/rectangle-holes-on-face.md index 19a6c523..3b4464a5 100644 --- a/docs/learn/code/common/occt/modeling/hollow-shapes/rectangle-holes-on-face.md +++ b/docs/learn/code/common/occt/modeling/hollow-shapes/rectangle-holes-on-face.md @@ -22,21 +22,21 @@ Creating regular patterns of holes manually can be time-consuming and error-pron recFacefacesrecFace2014000010facesrecFace1010[0.8,0.5,0.5][0.8,0.5,0.5]FALSE00GETFIRSTfaces010","version":"0.20.13","type":"blockly"}} + script={{"script":"recFacefacesrecFace2014000010facesrecFace1010[0.8,0.5,0.5][0.8,0.5,0.5]FALSE00GETFIRSTfaces010","version":"0.20.14","type":"blockly"}} title="Rectangle holes on face" /> {\n // Create a rectangular face as the base shape\n const faceOptions = new RectangleDto();\n faceOptions.width = 20;\n faceOptions.length = 14;\n faceOptions.center = [0, 0, 0];\n faceOptions.direction = [0, 1, 0];\n const rectangleFace = await face.createRectangleFace(faceOptions);\n\n // Define scale patterns for U and V directions\n // These arrays control the size of holes in each row and column\n const scalePatternU = [0.8, 0.5, 0.5]; // Varying hole sizes in U direction\n const scalePatternV = [0.8, 0.5, 0.5]; // Varying hole sizes in V direction\n\n // Subdivide the face into rectangular holes\n const subdivideOptions = new FaceSubdivideToRectangleHolesDto();\n subdivideOptions.shape = rectangleFace;\n subdivideOptions.nrRectanglesU = 10; // Number of divisions in U direction\n subdivideOptions.nrRectanglesV = 10; // Number of divisions in V direction\n subdivideOptions.holesToFaces = false; // Return wires instead of faces\n subdivideOptions.offsetFromBorderU = 0.05; // Border offset in U direction\n subdivideOptions.offsetFromBorderV = 0.05; // Border offset in V direction\n subdivideOptions.scalePatternU = scalePatternU;\n subdivideOptions.scalePatternV = scalePatternV;\n\n const holes = await face.subdivideToRectangleHoles(subdivideOptions);\n\n // Get the first hole (the outer boundary with holes)\n const firstHole = lists.getItem({ list: holes, index: 0, clone: true });\n\n // Extrude the face with holes to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = firstHole;\n extrudeOptions.direction = [0, 1, 0];\n const extrudedSolid = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with rectangular holes\n bitbybit.draw.drawAnyAsync({\n entity: extrudedSolid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for face creation, subdivision, and extrusion\nconst { RectangleDto, FaceSubdivideToRectangleHolesDto, ExtrudeDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Get access to OCCT modules for face operations\nconst { face } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\nconst { lists } = bitbybit;\n\n// Define the main function to create rectangle holes on a face\nconst start = async () => {\n // Create a rectangular face as the base shape\n const faceOptions = new RectangleDto();\n faceOptions.width = 20;\n faceOptions.length = 14;\n faceOptions.center = [0, 0, 0];\n faceOptions.direction = [0, 1, 0];\n const rectangleFace = await face.createRectangleFace(faceOptions);\n\n // Define scale patterns for U and V directions\n // These arrays control the size of holes in each row and column\n const scalePatternU = [0.8, 0.5, 0.5]; // Varying hole sizes in U direction\n const scalePatternV = [0.8, 0.5, 0.5]; // Varying hole sizes in V direction\n\n // Subdivide the face into rectangular holes\n const subdivideOptions = new FaceSubdivideToRectangleHolesDto();\n subdivideOptions.shape = rectangleFace;\n subdivideOptions.nrRectanglesU = 10; // Number of divisions in U direction\n subdivideOptions.nrRectanglesV = 10; // Number of divisions in V direction\n subdivideOptions.holesToFaces = false; // Return wires instead of faces\n subdivideOptions.offsetFromBorderU = 0.05; // Border offset in U direction\n subdivideOptions.offsetFromBorderV = 0.05; // Border offset in V direction\n subdivideOptions.scalePatternU = scalePatternU;\n subdivideOptions.scalePatternV = scalePatternV;\n\n const holes = await face.subdivideToRectangleHoles(subdivideOptions);\n\n // Get the first hole (the outer boundary with holes)\n const firstHole = lists.getItem({ list: holes, index: 0, clone: true });\n\n // Extrude the face with holes to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = firstHole;\n extrudeOptions.direction = [0, 1, 0];\n const extrudedSolid = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with rectangular holes\n bitbybit.draw.drawAnyAsync({\n entity: extrudedSolid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Rectangle holes on face" /> diff --git a/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hole.md b/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hole.md index cd935168..134e37aa 100644 --- a/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hole.md +++ b/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hole.md @@ -30,21 +30,21 @@ This technique is fundamental because it establishes the basic principle that ap 200000107000010TRUE","version":"0.20.13","type":"blockly"}} + script={{"script":"200000107000010TRUE","version":"0.20.14","type":"blockly"}} title="Simple hole from two wires" /> {\n // Create the outer square wire (boundary of the shape)\n const outerWireOptions = new SquareDto();\n outerWireOptions.size = 20;\n outerWireOptions.center = [0, 0, 0];\n outerWireOptions.direction = [0, 1, 0];\n const outerWire = await wire.createSquareWire(outerWireOptions);\n\n // Create the inner square wire (defines the hole)\n const innerWireOptions = new SquareDto();\n innerWireOptions.size = 7;\n innerWireOptions.center = [0, 0, 0];\n innerWireOptions.direction = [0, 1, 0];\n const innerWire = await wire.createSquareWire(innerWireOptions);\n\n // Reverse the inner wire - this is crucial for proper hole creation\n const reversedInnerWire = await wire.reversedWire({ shape: innerWire });\n\n // Create a face from both wires - outer boundary and inner hole\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [outerWire, reversedInnerWire];\n faceOptions.planar = true;\n const faceWithHole = await face.createFaceFromWires(faceOptions);\n\n // Draw the resulting face with hole\n bitbybit.draw.drawAnyAsync({\n entity: faceWithHole\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required types and DTOs for creating wire shapes and faces\nconst { SquareDto, FaceFromWiresDto } = Bit.Inputs.OCCT;\n// Import the wire pointer type for type safety\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Get direct access to OCCT wire and face creation functions\nconst { wire } = bitbybit.occt.shapes;\nconst { face } = bitbybit.occt.shapes;\n\n// Define the main function to create a simple hole\nconst start = async () => {\n // Create the outer square wire (boundary of the shape)\n const outerWireOptions = new SquareDto();\n outerWireOptions.size = 20;\n outerWireOptions.center = [0, 0, 0];\n outerWireOptions.direction = [0, 1, 0];\n const outerWire = await wire.createSquareWire(outerWireOptions);\n\n // Create the inner square wire (defines the hole)\n const innerWireOptions = new SquareDto();\n innerWireOptions.size = 7;\n innerWireOptions.center = [0, 0, 0];\n innerWireOptions.direction = [0, 1, 0];\n const innerWire = await wire.createSquareWire(innerWireOptions);\n\n // Reverse the inner wire - this is crucial for proper hole creation\n const reversedInnerWire = await wire.reversedWire({ shape: innerWire });\n\n // Create a face from both wires - outer boundary and inner hole\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [outerWire, reversedInnerWire];\n faceOptions.planar = true;\n const faceWithHole = await face.createFaceFromWires(faceOptions);\n\n // Draw the resulting face with hole\n bitbybit.draw.drawAnyAsync({\n entity: faceWithHole\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Simple hole from two wires" /> @@ -60,21 +60,21 @@ The transition from a 2D hollow face to a 3D hollow solid is achieved through ex 2014000010177000010101045TRUE020","version":"0.20.13","type":"blockly"}} + script={{"script":"2014000010177000010101045TRUE020","version":"0.20.14","type":"blockly"}} title="More complex solid with the hole" /> {\n // Create the outer rectangular wire\n const outerWireOptions = new RectangleDto();\n outerWireOptions.width = 20;\n outerWireOptions.length = 14;\n outerWireOptions.center = [0, 0, 0];\n outerWireOptions.direction = [0, 1, 0];\n const outerWire = await wire.createRectangleWire(outerWireOptions);\n\n // Apply fillet to the outer wire for rounded corners\n const outerFilletOptions = new FilletDto();\n outerFilletOptions.radius = 1;\n outerFilletOptions.shape = outerWire;\n const filletedOuterWire = await fillets.fillet2d(outerFilletOptions);\n\n // Create the inner rectangular wire (hole)\n const innerWireOptions = new RectangleDto();\n innerWireOptions.width = 7;\n innerWireOptions.length = 7;\n innerWireOptions.center = [0, 0, 0];\n innerWireOptions.direction = [0, 1, 0];\n const innerWire = await wire.createRectangleWire(innerWireOptions);\n\n // Apply fillet to the inner wire\n const innerFilletOptions = new FilletDto();\n innerFilletOptions.radius = 1;\n innerFilletOptions.shape = innerWire;\n const filletedInnerWire = await fillets.fillet2d(innerFilletOptions);\n\n // Rotate the inner wire by 45 degrees for visual interest\n const rotatedInnerWire = await transforms.rotate({\n shape: filletedInnerWire,\n axis: [0, 1, 0],\n angle: 45\n });\n\n // Reverse the rotated inner wire for proper hole creation\n const reversedInnerWire = await wire.reversedWire({ shape: rotatedInnerWire });\n\n // Create a face from both wires\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [filletedOuterWire, reversedInnerWire];\n faceOptions.planar = true;\n const faceWithHole = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = faceWithHole;\n extrudeOptions.direction = [0, 2, 0]; // Extrude 2 units in Y direction\n const solidWithHole = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with hole\n bitbybit.draw.drawAnyAsync({\n entity: solidWithHole\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating wires, fillets, faces, and extrusion operations\nconst { RectangleDto, FilletDto, FaceFromWiresDto, ExtrudeDto } = Bit.Inputs.OCCT;\n// Import wire pointer type for type safety\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\n\n// Get access to OCCT modules for creating shapes and operations\nconst { wire, face } = bitbybit.occt.shapes;\nconst { fillets, operations, transforms } = bitbybit.occt;\n\n// Define the main function to create an extruded solid with a hole\nconst start = async () => {\n // Create the outer rectangular wire\n const outerWireOptions = new RectangleDto();\n outerWireOptions.width = 20;\n outerWireOptions.length = 14;\n outerWireOptions.center = [0, 0, 0];\n outerWireOptions.direction = [0, 1, 0];\n const outerWire = await wire.createRectangleWire(outerWireOptions);\n\n // Apply fillet to the outer wire for rounded corners\n const outerFilletOptions = new FilletDto();\n outerFilletOptions.radius = 1;\n outerFilletOptions.shape = outerWire;\n const filletedOuterWire = await fillets.fillet2d(outerFilletOptions);\n\n // Create the inner rectangular wire (hole)\n const innerWireOptions = new RectangleDto();\n innerWireOptions.width = 7;\n innerWireOptions.length = 7;\n innerWireOptions.center = [0, 0, 0];\n innerWireOptions.direction = [0, 1, 0];\n const innerWire = await wire.createRectangleWire(innerWireOptions);\n\n // Apply fillet to the inner wire\n const innerFilletOptions = new FilletDto();\n innerFilletOptions.radius = 1;\n innerFilletOptions.shape = innerWire;\n const filletedInnerWire = await fillets.fillet2d(innerFilletOptions);\n\n // Rotate the inner wire by 45 degrees for visual interest\n const rotatedInnerWire = await transforms.rotate({\n shape: filletedInnerWire,\n axis: [0, 1, 0],\n angle: 45\n });\n\n // Reverse the rotated inner wire for proper hole creation\n const reversedInnerWire = await wire.reversedWire({ shape: rotatedInnerWire });\n\n // Create a face from both wires\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [filletedOuterWire, reversedInnerWire];\n faceOptions.planar = true;\n const faceWithHole = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create a 3D solid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = faceWithHole;\n extrudeOptions.direction = [0, 2, 0]; // Extrude 2 units in Y direction\n const solidWithHole = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with hole\n bitbybit.draw.drawAnyAsync({\n entity: solidWithHole\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="More complex solid with the hole" /> @@ -90,21 +90,21 @@ The process simply involves creating additional inner wires and including them a 231400001017700001010104550077000010101045-500TRUE020","version":"0.20.13","type":"blockly"}} + script={{"script":"231400001017700001010104550077000010101045-500TRUE020","version":"0.20.14","type":"blockly"}} title="Solid with 2 holes" /> {\n // Create the outer rectangular wire (larger this time)\n const outerWireOptions = new RectangleDto();\n outerWireOptions.width = 23;\n outerWireOptions.length = 14;\n outerWireOptions.center = [0, 0, 0];\n outerWireOptions.direction = [0, 1, 0];\n const outerWire = await wire.createRectangleWire(outerWireOptions);\n\n // Apply fillet to the outer wire\n const outerFilletOptions = new FilletDto();\n outerFilletOptions.radius = 1;\n outerFilletOptions.shape = outerWire;\n const filletedOuterWire = await fillets.fillet2d(outerFilletOptions);\n\n // Reverse the outer wire (optimization - reverse once instead of multiple inner wires)\n const reversedOuterWire = await wire.reversedWire({ shape: filletedOuterWire });\n\n // Create the base inner rectangular wire\n const innerWireOptions = new RectangleDto();\n innerWireOptions.width = 7;\n innerWireOptions.length = 7;\n innerWireOptions.center = [0, 0, 0];\n innerWireOptions.direction = [0, 1, 0];\n const innerWire = await wire.createRectangleWire(innerWireOptions);\n\n // Apply fillet to the inner wire\n const innerFilletOptions = new FilletDto();\n innerFilletOptions.radius = 1;\n innerFilletOptions.shape = innerWire;\n const filletedInnerWire = await fillets.fillet2d(innerFilletOptions);\n\n // Rotate the inner wire for visual interest\n const rotatedInnerWire = await transforms.rotate({\n shape: filletedInnerWire,\n axis: [0, 1, 0],\n angle: 45\n });\n\n // Create first hole by translating the rotated wire to the right\n const firstHole = await transforms.translate({\n shape: rotatedInnerWire,\n translation: [5, 0, 0]\n });\n\n // Create second hole by translating the rotated wire to the left\n const secondHole = await transforms.translate({\n shape: rotatedInnerWire,\n translation: [-5, 0, 0]\n });\n\n // Create a face from the outer boundary and multiple holes\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [reversedOuterWire, firstHole, secondHole];\n faceOptions.planar = true;\n const faceWithHoles = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create a 3D solid with multiple holes\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = faceWithHoles;\n extrudeOptions.direction = [0, 2, 0];\n const solidWithHoles = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with multiple holes\n bitbybit.draw.drawAnyAsync({\n entity: solidWithHoles\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating complex shapes with multiple holes\nconst { RectangleDto, FilletDto, FaceFromWiresDto, ExtrudeDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\n\n// Get access to OCCT modules\nconst { wire, face } = bitbybit.occt.shapes;\nconst { fillets, operations, transforms } = bitbybit.occt;\n\n// Define the main function to create a solid with multiple holes\nconst start = async () => {\n // Create the outer rectangular wire (larger this time)\n const outerWireOptions = new RectangleDto();\n outerWireOptions.width = 23;\n outerWireOptions.length = 14;\n outerWireOptions.center = [0, 0, 0];\n outerWireOptions.direction = [0, 1, 0];\n const outerWire = await wire.createRectangleWire(outerWireOptions);\n\n // Apply fillet to the outer wire\n const outerFilletOptions = new FilletDto();\n outerFilletOptions.radius = 1;\n outerFilletOptions.shape = outerWire;\n const filletedOuterWire = await fillets.fillet2d(outerFilletOptions);\n\n // Reverse the outer wire (optimization - reverse once instead of multiple inner wires)\n const reversedOuterWire = await wire.reversedWire({ shape: filletedOuterWire });\n\n // Create the base inner rectangular wire\n const innerWireOptions = new RectangleDto();\n innerWireOptions.width = 7;\n innerWireOptions.length = 7;\n innerWireOptions.center = [0, 0, 0];\n innerWireOptions.direction = [0, 1, 0];\n const innerWire = await wire.createRectangleWire(innerWireOptions);\n\n // Apply fillet to the inner wire\n const innerFilletOptions = new FilletDto();\n innerFilletOptions.radius = 1;\n innerFilletOptions.shape = innerWire;\n const filletedInnerWire = await fillets.fillet2d(innerFilletOptions);\n\n // Rotate the inner wire for visual interest\n const rotatedInnerWire = await transforms.rotate({\n shape: filletedInnerWire,\n axis: [0, 1, 0],\n angle: 45\n });\n\n // Create first hole by translating the rotated wire to the right\n const firstHole = await transforms.translate({\n shape: rotatedInnerWire,\n translation: [5, 0, 0]\n });\n\n // Create second hole by translating the rotated wire to the left\n const secondHole = await transforms.translate({\n shape: rotatedInnerWire,\n translation: [-5, 0, 0]\n });\n\n // Create a face from the outer boundary and multiple holes\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = [reversedOuterWire, firstHole, secondHole];\n faceOptions.planar = true;\n const faceWithHoles = await face.createFaceFromWires(faceOptions);\n\n // Extrude the face to create a 3D solid with multiple holes\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = faceWithHoles;\n extrudeOptions.direction = [0, 2, 0];\n const solidWithHoles = await operations.extrude(extrudeOptions);\n\n // Draw the resulting 3D solid with multiple holes\n bitbybit.draw.drawAnyAsync({\n entity: solidWithHoles\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Solid with 2 holes" /> diff --git a/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hollow-grids.md b/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hollow-grids.md index bd4fc225..332755a6 100644 --- a/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hollow-grids.md +++ b/docs/learn/code/common/occt/modeling/hollow-shapes/simple-hollow-grids.md @@ -30,21 +30,21 @@ This approach not only saves time but also ensures perfect regularity and makes gridSizeholeRadiusholeSpacingextrudeHeighthalfGridboundarySizesquarexPositionsyPositionswiresxzhollowFacehollowGridSolidgridSize14holeRadius0.8holeSpacing2extrudeHeight0.5halfGridDIVIDEgridSize2boundarySizeADDgridSize4squareboundarySize000010xPositionsholeSpacingNEGhalfGridhalfGridyPositionsholeSpacingNEGhalfGridhalfGridwiressquarex1xPositions1z1xPositions1INSERTLASTwiresholeRadiusGETFROM_STARTxPositionsx0GETFROM_STARTyPositionsz010hollowFacewiresTRUEhollowGridSolidhollowFace0extrudeHeight0hollowGridSolid","version":"0.20.13","type":"blockly"}} + script={{"script":"gridSizeholeRadiusholeSpacingextrudeHeighthalfGridboundarySizesquarexPositionsyPositionswiresxzhollowFacehollowGridSolidgridSize14holeRadius0.8holeSpacing2extrudeHeight0.5halfGridDIVIDEgridSize2boundarySizeADDgridSize4squareboundarySize000010xPositionsholeSpacingNEGhalfGridhalfGridyPositionsholeSpacingNEGhalfGridhalfGridwiressquarex1xPositions1z1xPositions1INSERTLASTwiresholeRadiusGETFROM_STARTxPositionsx0GETFROM_STARTyPositionsz010hollowFacewiresTRUEhollowGridSolidhollowFace0extrudeHeight0hollowGridSolid","version":"0.20.14","type":"blockly"}} title="Simple hollow grid" /> {\n // Grid parameters - easily adjustable for different requirements\n const gridSize = 14; // Overall grid dimension\n const holeRadius = 0.8; // Radius of each circular hole\n const holeSpacing = 2; // Distance between hole centers\n const extrudeHeight = 0.5; // Thickness of the final solid\n \n // Calculate grid boundaries\n const halfGrid = gridSize / 2;\n const boundarySize = gridSize + 4; // Add padding around holes\n \n // Create the outer boundary wire (square frame)\n const boundaryOptions = new SquareDto();\n boundaryOptions.size = boundarySize;\n boundaryOptions.center = [0, 0, 0];\n boundaryOptions.direction = [0, 1, 0];\n const boundaryWire = await wire.createSquareWire(boundaryOptions);\n \n // Generate grid positions using span functions\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: holeSpacing\n });\n \n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: holeSpacing\n });\n \n // Create grid points by combining X and Z coordinates\n const gridPoints: Point3[] = [];\n for (const xPos of xPositions) {\n for (const zPos of zPositions) {\n gridPoints.push([xPos, 0, zPos]);\n }\n }\n \n // Create the hole template (circular wire)\n const holeOptions = new CircleDto();\n holeOptions.radius = holeRadius;\n holeOptions.center = [0, 0, 0];\n holeOptions.direction = [0, 1, 0];\n const holeTemplate = await wire.createCircleWire(holeOptions);\n \n // Create holes at each grid position\n const holes: TopoDSWirePointer[] = [];\n for (const position of gridPoints) {\n const translatedHole = await transforms.translate({\n shape: holeTemplate,\n translation: position\n });\n \n // Reverse each hole wire for proper orientation\n const reversedHole = await wire.reversedWire({ shape: translatedHole });\n holes.push(reversedHole);\n }\n \n // Combine boundary and all holes into a single wire list\n const allWires = [boundaryWire, ...holes];\n \n // Create a face from the boundary and all holes\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = allWires;\n faceOptions.planar = true;\n const gridFace = await face.createFaceFromWires(faceOptions);\n \n // Extrude the face to create a 3D hollow grid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = gridFace;\n extrudeOptions.direction = [0, extrudeHeight, 0];\n const hollowGrid = await operations.extrude(extrudeOptions);\n \n // Draw the resulting hollow grid\n bitbybit.draw.drawAnyAsync({\n entity: hollowGrid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating grids, shapes, and operations\nconst { SquareDto, CircleDto, FaceFromWiresDto, ExtrudeDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\n\n// Get access to OCCT modules and utility functions\nconst { wire, face } = bitbybit.occt.shapes;\nconst { operations, transforms } = bitbybit.occt;\nconst { vector } = bitbybit;\n\n// Define the main function to create a parametric hollow grid\nconst start = async () => {\n // Grid parameters - easily adjustable for different requirements\n const gridSize = 14; // Overall grid dimension\n const holeRadius = 0.8; // Radius of each circular hole\n const holeSpacing = 2; // Distance between hole centers\n const extrudeHeight = 0.5; // Thickness of the final solid\n \n // Calculate grid boundaries\n const halfGrid = gridSize / 2;\n const boundarySize = gridSize + 4; // Add padding around holes\n \n // Create the outer boundary wire (square frame)\n const boundaryOptions = new SquareDto();\n boundaryOptions.size = boundarySize;\n boundaryOptions.center = [0, 0, 0];\n boundaryOptions.direction = [0, 1, 0];\n const boundaryWire = await wire.createSquareWire(boundaryOptions);\n \n // Generate grid positions using span functions\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: holeSpacing\n });\n \n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: holeSpacing\n });\n \n // Create grid points by combining X and Z coordinates\n const gridPoints: Point3[] = [];\n for (const xPos of xPositions) {\n for (const zPos of zPositions) {\n gridPoints.push([xPos, 0, zPos]);\n }\n }\n \n // Create the hole template (circular wire)\n const holeOptions = new CircleDto();\n holeOptions.radius = holeRadius;\n holeOptions.center = [0, 0, 0];\n holeOptions.direction = [0, 1, 0];\n const holeTemplate = await wire.createCircleWire(holeOptions);\n \n // Create holes at each grid position\n const holes: TopoDSWirePointer[] = [];\n for (const position of gridPoints) {\n const translatedHole = await transforms.translate({\n shape: holeTemplate,\n translation: position\n });\n \n // Reverse each hole wire for proper orientation\n const reversedHole = await wire.reversedWire({ shape: translatedHole });\n holes.push(reversedHole);\n }\n \n // Combine boundary and all holes into a single wire list\n const allWires = [boundaryWire, ...holes];\n \n // Create a face from the boundary and all holes\n const faceOptions = new FaceFromWiresDto();\n faceOptions.shapes = allWires;\n faceOptions.planar = true;\n const gridFace = await face.createFaceFromWires(faceOptions);\n \n // Extrude the face to create a 3D hollow grid\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = gridFace;\n extrudeOptions.direction = [0, extrudeHeight, 0];\n const hollowGrid = await operations.extrude(extrudeOptions);\n \n // Draw the resulting hollow grid\n bitbybit.draw.drawAnyAsync({\n entity: hollowGrid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Simple hollow grid" /> 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 index 2e1c7bd1..da3323c7 100644 --- a/docs/learn/code/common/occt/modeling/parametric-art/simple-flower.md +++ b/docs/learn/code/common/occt/modeling/parametric-art/simple-flower.md @@ -30,21 +30,21 @@ This method is highly parametric—by adjusting the control points, rotation ang 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.13","type":"blockly"}} + script={{"script":"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.14","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.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating curves, shapes, and operations\nconst { InterpolationDto, MirrorAlongNormalDto, ShapesDto, CompoundShapesDto,\n FaceFromWireDto, ThisckSolidSimpleDto, RotateDto } = Bit.Inputs.OCCT;\nconst { SceneTwoColorLinearGradientDto, DirectionalLightDto } = Bit.Inputs.BabylonScene;\nconst { gradientDirectionEnum } = Bit.Inputs.Base;\n\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\n// Get access to OCCT modules and utility functions\nconst { wire, face, compound } = bitbybit.occt.shapes;\nconst { operations, transforms } = bitbybit.occt;\nconst { vector } = bitbybit;\nconst { scene } = bitbybit.babylon;\nconst { pbrMetallicRoughness } = bitbybit.babylon.material;\n\n\nconst createEnvironment = async () => {\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.14","type":"typescript"}} title="Simple flower" /> @@ -107,7 +107,7 @@ Despite managing up to 25,000 particles simultaneously, the system maintains smo {\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.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating curves, shapes, and operations\nconst { InterpolationDto, MirrorAlongNormalDto, ShapesDto, CompoundShapesDto,\n FaceFromWireDto, ThisckSolidSimpleDto, RotateDto } = Bit.Inputs.OCCT;\nconst { SceneTwoColorLinearGradientDto, DirectionalLightDto } = Bit.Inputs.BabylonScene;\nconst { gradientDirectionEnum } = Bit.Inputs.Base;\n\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\ninterface Particle extends BABYLON.Particle {\n isLarge: boolean,\n isDrifter: boolean,\n driftDirection: BABYLON.Vector3\n}\n\n// Get access to OCCT modules and utility functions\nconst { wire, face, compound } = bitbybit.occt.shapes;\nconst { operations, transforms } = bitbybit.occt;\nconst { vector } = bitbybit;\nconst { scene } = bitbybit.babylon;\nconst { pbrMetallicRoughness } = bitbybit.babylon.material;\n\n\nconst createEnvironment = async () => {\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.14","type":"typescript"}} title="Simple flower" /> diff --git a/docs/learn/code/common/occt/operations/advanced-loft.md b/docs/learn/code/common/occt/operations/advanced-loft.md index 8c136b2d..0b3be30d 100644 --- a/docs/learn/code/common/occt/operations/advanced-loft.md +++ b/docs/learn/code/common/occt/operations/advanced-loft.md @@ -45,21 +45,21 @@ Here's a breakdown of the process described: nrCornerswire1wire2wire3nrCorners10151515030117910001000100010003wire1000010nrCorners31wire2030010nrCorners10.3wire3070010nrCorners60.5wire3wire2wire1FALSEFALSEFALSEFALSE10FALSE31e-7'approxChordLength'01000-300.01TRUE#cc33ccTRUE#ffffff2","version":"0.20.13","type":"blockly"}} + script={{"script":"nrCornerswire1wire2wire3nrCorners10151515030117910001000100010003wire1000010nrCorners31wire2030010nrCorners10.3wire3070010nrCorners60.5wire3wire2wire1FALSEFALSEFALSEFALSE10FALSE31e-7'approxChordLength'01000-300.01TRUE#cc33ccTRUE#ffffff2","version":"0.20.14","type":"blockly"}} title="Advanced Loft Operation" /> {\n\n // --- 1. Camera Configuration ---\n // Create a new configuration object for the scene's Arc Rotate Camera.\n const arcCameraOptions = new CameraConfigurationDto();\n // Set the camera's position in 3D space (x, y, z).\n arcCameraOptions.position = [15, 15, 15];\n // Set the point the camera will look at.\n arcCameraOptions.lookAt = [0, 3, 0];\n // Apply these settings to the active camera in the scene.\n scene.adjustActiveArcRotateCamera(arcCameraOptions);\n\n // Define the number of corners for the polygonal profiles.\n const nrCorners = 10;\n\n // --- 2. Create the First Profile (Bottom) ---\n // Create a DTO to define an n-sided polygon wire.\n const ngonOpt = new NGonWireDto();\n ngonOpt.nrCorners = nrCorners;\n ngonOpt.radius = 3;\n // Asynchronously create the first polygon wire shape at the default center [0,0,0].\n const wire1 = await wire.createNGonWire(ngonOpt);\n\n // Create a DTO for a 2D fillet operation to round the corners of a wire.\n const filletOpt = new FilletDto();\n filletOpt.radius = 0.3; // The radius of the rounded corners.\n filletOpt.shape = wire1; // Specify that the fillet should be applied to wire1.\n // Asynchronously apply the fillet and store the new, smoothed wire.\n const filletedWire1 = await fillets.fillet2d(filletOpt);\n\n // --- 3. Create the Second Profile (Middle) ---\n // Reuse the ngonOpt DTO but modify its properties for the next shape.\n ngonOpt.center = [0, 3, 0]; // Move the center up along the Y-axis.\n ngonOpt.radius = 1; // Make this polygon smaller.\n const wire2 = await wire.createNGonWire(ngonOpt);\n\n // Reuse the filletOpt DTO to apply the same fillet radius to the new wire.\n filletOpt.shape = wire2;\n const filletedWire2 = await fillets.fillet2d(filletOpt);\n\n // --- 4. Create the Third Profile (Top) ---\n // Reuse and modify the DTOs again for the third and final profile.\n ngonOpt.center = [0, 7, 0]; // Move this one even higher.\n ngonOpt.radius = 6; // Make this polygon the largest.\n const wire3 = await wire.createNGonWire(ngonOpt);\n\n // Use a larger fillet radius for this larger wire.\n filletOpt.radius = 0.5;\n filletOpt.shape = wire3;\n const filletedWire3 = await fillets.fillet2d(filletOpt);\n\n // --- 5. Perform the Loft Operation ---\n // A loft creates a 3D solid by connecting a series of 2D profiles.\n const loftAdvancedOptions = new LoftAdvancedDto();\n // Specify a single point where the loft should begin, creating a pointed top.\n loftAdvancedOptions.startVertex = [0, 10, 0];\n // Specify a single point where the loft should end, creating a pointed bottom.\n loftAdvancedOptions.endVertex = [0, -3, 0];\n // Provide the array of profiles to connect. The order matters.\n loftAdvancedOptions.shapes = [filletedWire3, filletedWire2, filletedWire1];\n // Asynchronously execute the loft operation to create the final 3D shape.\n const loftedShape = await operations.loftAdvanced(loftAdvancedOptions)\n\n // --- 6. Draw the Final Shape ---\n // Create a DTO to define how the shape should be drawn.\n const drawOptions = new DrawOcctShapeSimpleOptions();\n // Set the color of the shape's faces to magenta.\n drawOptions.faceColour = \"#ff00ff\";\n\n // Call the generic drawing function to render the OCCT shape in the scene.\n bitbybit.draw.drawAnyAsync({\n entity: loftedShape, // The shape to draw.\n options: drawOptions // The drawing options to apply.\n });\n\n}\n\n// Execute the main function to start the script.\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"\n// Import DTOs for configuring various operations. DTOs are objects used to pass data.\n// Import camera configuration for setting up the scene view.\nconst { CameraConfigurationDto } = Bit.Inputs.BabylonScene;\n// Import DTOs for OpenCASCADE (OCCT) geometric modeling: creating polygons, fillets, and lofts.\nconst { NGonWireDto, FilletDto, LoftAdvancedDto } = Bit.Inputs.OCCT;\n// Import DTO for specifying drawing options, like color and opacity.\nconst { DrawOcctShapeSimpleOptions } = Bit.Inputs.Draw;\n// Import a specific type for an OCCT wire, ensuring type safety in our code.\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Destructure the bitbybit API to get direct access to its main modules.\n// 'scene' provides access to Babylon.js scene controls.\nconst { scene } = bitbybit.babylon;\n// 'wire', 'fillets', and 'operations' are part of the OCCT module for creating and manipulating shapes.\nconst { wire } = bitbybit.occt.shapes;\nconst { fillets, operations } = bitbybit.occt;\n\n// Define an asynchronous function to execute the main logic.\n// Using async/await is necessary because geometry creation and drawing are non-blocking operations.\nconst start = async () => {\n\n // --- 1. Camera Configuration ---\n // Create a new configuration object for the scene's Arc Rotate Camera.\n const arcCameraOptions = new CameraConfigurationDto();\n // Set the camera's position in 3D space (x, y, z).\n arcCameraOptions.position = [15, 15, 15];\n // Set the point the camera will look at.\n arcCameraOptions.lookAt = [0, 3, 0];\n // Apply these settings to the active camera in the scene.\n scene.adjustActiveArcRotateCamera(arcCameraOptions);\n\n // Define the number of corners for the polygonal profiles.\n const nrCorners = 10;\n\n // --- 2. Create the First Profile (Bottom) ---\n // Create a DTO to define an n-sided polygon wire.\n const ngonOpt = new NGonWireDto();\n ngonOpt.nrCorners = nrCorners;\n ngonOpt.radius = 3;\n // Asynchronously create the first polygon wire shape at the default center [0,0,0].\n const wire1 = await wire.createNGonWire(ngonOpt);\n\n // Create a DTO for a 2D fillet operation to round the corners of a wire.\n const filletOpt = new FilletDto();\n filletOpt.radius = 0.3; // The radius of the rounded corners.\n filletOpt.shape = wire1; // Specify that the fillet should be applied to wire1.\n // Asynchronously apply the fillet and store the new, smoothed wire.\n const filletedWire1 = await fillets.fillet2d(filletOpt);\n\n // --- 3. Create the Second Profile (Middle) ---\n // Reuse the ngonOpt DTO but modify its properties for the next shape.\n ngonOpt.center = [0, 3, 0]; // Move the center up along the Y-axis.\n ngonOpt.radius = 1; // Make this polygon smaller.\n const wire2 = await wire.createNGonWire(ngonOpt);\n\n // Reuse the filletOpt DTO to apply the same fillet radius to the new wire.\n filletOpt.shape = wire2;\n const filletedWire2 = await fillets.fillet2d(filletOpt);\n\n // --- 4. Create the Third Profile (Top) ---\n // Reuse and modify the DTOs again for the third and final profile.\n ngonOpt.center = [0, 7, 0]; // Move this one even higher.\n ngonOpt.radius = 6; // Make this polygon the largest.\n const wire3 = await wire.createNGonWire(ngonOpt);\n\n // Use a larger fillet radius for this larger wire.\n filletOpt.radius = 0.5;\n filletOpt.shape = wire3;\n const filletedWire3 = await fillets.fillet2d(filletOpt);\n\n // --- 5. Perform the Loft Operation ---\n // A loft creates a 3D solid by connecting a series of 2D profiles.\n const loftAdvancedOptions = new LoftAdvancedDto();\n // Specify a single point where the loft should begin, creating a pointed top.\n loftAdvancedOptions.startVertex = [0, 10, 0];\n // Specify a single point where the loft should end, creating a pointed bottom.\n loftAdvancedOptions.endVertex = [0, -3, 0];\n // Provide the array of profiles to connect. The order matters.\n loftAdvancedOptions.shapes = [filletedWire3, filletedWire2, filletedWire1];\n // Asynchronously execute the loft operation to create the final 3D shape.\n const loftedShape = await operations.loftAdvanced(loftAdvancedOptions)\n\n // --- 6. Draw the Final Shape ---\n // Create a DTO to define how the shape should be drawn.\n const drawOptions = new DrawOcctShapeSimpleOptions();\n // Set the color of the shape's faces to magenta.\n drawOptions.faceColour = \"#ff00ff\";\n\n // Call the generic drawing function to render the OCCT shape in the scene.\n bitbybit.draw.drawAnyAsync({\n entity: loftedShape, // The shape to draw.\n options: drawOptions // The drawing options to apply.\n });\n\n}\n\n// Execute the main function to start the script.\nstart();","version":"0.20.14","type":"typescript"}} title="Advanced Loft Operation" /> diff --git a/docs/learn/code/common/occt/operations/extrusions.md b/docs/learn/code/common/occt/operations/extrusions.md index 409404ad..20e53872 100644 --- a/docs/learn/code/common/occt/operations/extrusions.md +++ b/docs/learn/code/common/occt/operations/extrusions.md @@ -35,21 +35,21 @@ By understanding extrusion, you gain a powerful tool for your 3D modeling toolki heightheight40000107740FALSE0.50height0","version":"0.20.13","type":"blockly"}} + script={{"script":"heightheight40000107740FALSE0.50height0","version":"0.20.14","type":"blockly"}} title="Extrude the wire into shell kind of shape" /> {\n\n // Define a constant 'height' for the extrusion operation.\n const height = 4;\n\n // Create a new StarDto instance to configure the properties of a star-shaped wire.\n const starOpt = new StarDto();\n // Set the inner radius of the star.\n starOpt.innerRadius = 4;\n // Set the outer radius of the star.\n starOpt.outerRadius = 7;\n // Asynchronously create the star-shaped wire using the configured options.\n // The 'await' keyword pauses execution until the wire creation is complete.\n // Bitbybit runs such CAD operations in the worker thread, which doesn't block UI\n // and is usually faster.\n const star = await wire.createStarWire(starOpt);\n\n // Create a new FilletDto instance to configure a 2D fillet operation.\n // The generic type specifies that this fillet will be applied to a wire.\n const filletOpt = new FilletDto();\n // Set the shape to be filleted to the previously created star wire.\n filletOpt.shape = star;\n // Set the radius for the fillet (rounding of corners).\n filletOpt.radius = 0.5;\n // Asynchronously apply the 2D fillet to the star wire.\n const roundedStar = await fillets.fillet2d(filletOpt);\n\n // Create a new ExtrudeDto instance to configure an extrusion operation.\n // The generic type specifies that a wire will be extruded.\n const extrudeOpt = new ExtrudeDto();\n // Set the shape to be extruded to the previously created rounded star wire.\n extrudeOpt.shape = roundedStar;\n // Set the direction and magnitude of the extrusion as a 3D vector [x, y, z].\n // Here, it extrudes along the Y-axis by the value of 'height'.\n extrudeOpt.direction = [0, height, 0];\n // Asynchronously perform the extrusion operation on the rounded star wire.\n const extrudedRoundStar = await operations.extrude(extrudeOpt);\n\n // Asynchronously draw the final extruded shape in the 3D scene.\n // 'entity' specifies the shape to be drawn.\n bitbybit.draw.drawAnyAsync({ entity: extrudedRoundStar });\n\n}\n\n// Call the 'start' function to execute the script.\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import the 'wire' module for creating wire shapes from the OCCT (OpenCASCADE Technology) library.\nconst { wire } = bitbybit.occt.shapes;\n// Import 'fillets' and 'operations' modules for performing filleting and extrusion operations on OCCT shapes.\nconst { fillets, operations } = bitbybit.occt;\n// Import Data Transfer Objects (DTOs) for configuring star creation, filleting, and extrusion.\n// DTOs are used to pass parameters to the respective functions.\nconst { StarDto, FilletDto, ExtrudeDto } = Bit.Inputs.OCCT;\n// Define a type alias for a pointer to a TopoDS_Wire, which is an OCCT data structure representing a wire.\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Define an asynchronous function named 'start' which will contain the main logic for creating and drawing the shape.\n// The 'async' keyword allows the use of 'await' for operations that might take time, like geometry creation.\nconst start = async () => {\n\n // Define a constant 'height' for the extrusion operation.\n const height = 4;\n\n // Create a new StarDto instance to configure the properties of a star-shaped wire.\n const starOpt = new StarDto();\n // Set the inner radius of the star.\n starOpt.innerRadius = 4;\n // Set the outer radius of the star.\n starOpt.outerRadius = 7;\n // Asynchronously create the star-shaped wire using the configured options.\n // The 'await' keyword pauses execution until the wire creation is complete.\n // Bitbybit runs such CAD operations in the worker thread, which doesn't block UI\n // and is usually faster.\n const star = await wire.createStarWire(starOpt);\n\n // Create a new FilletDto instance to configure a 2D fillet operation.\n // The generic type specifies that this fillet will be applied to a wire.\n const filletOpt = new FilletDto();\n // Set the shape to be filleted to the previously created star wire.\n filletOpt.shape = star;\n // Set the radius for the fillet (rounding of corners).\n filletOpt.radius = 0.5;\n // Asynchronously apply the 2D fillet to the star wire.\n const roundedStar = await fillets.fillet2d(filletOpt);\n\n // Create a new ExtrudeDto instance to configure an extrusion operation.\n // The generic type specifies that a wire will be extruded.\n const extrudeOpt = new ExtrudeDto();\n // Set the shape to be extruded to the previously created rounded star wire.\n extrudeOpt.shape = roundedStar;\n // Set the direction and magnitude of the extrusion as a 3D vector [x, y, z].\n // Here, it extrudes along the Y-axis by the value of 'height'.\n extrudeOpt.direction = [0, height, 0];\n // Asynchronously perform the extrusion operation on the rounded star wire.\n const extrudedRoundStar = await operations.extrude(extrudeOpt);\n\n // Asynchronously draw the final extruded shape in the 3D scene.\n // 'entity' specifies the shape to be drawn.\n bitbybit.draw.drawAnyAsync({ entity: extrudedRoundStar });\n\n}\n\n// Call the 'start' function to execute the script.\nstart();","version":"0.20.14","type":"typescript"}} title="Extrude the wire into shell kind of shape" /> @@ -69,21 +69,21 @@ In the following examples, we first construct a complex wire, then create a plan heightheight4000010070000100520FALSEFALSETRUETRUE0height0","version":"0.20.13","type":"blockly"}} + script={{"script":"heightheight4000010070000100520FALSEFALSETRUETRUE0height0","version":"0.20.14","type":"blockly"}} title="Extrude the face into solid shape" /> {\n\n const height = 4;\n\n const heartOpt = new Heart2DDto();\n heartOpt.sizeApprox = 7;\n const heart1 = await wire.createHeartWire(heartOpt);\n\n heartOpt.sizeApprox = 5;\n const heart2 = await wire.createHeartWire(heartOpt);\n\n const zigZagOpt = new ZigZagBetweenTwoWiresDto(heart1, heart2, 31);\n const zigZagWire = await wire.createZigZagBetweenTwoWires(zigZagOpt);\n\n const faceFromWireOpt = new FaceFromWireDto(zigZagWire, true);\n const zigZagFace = await face.createFaceFromWire(faceFromWireOpt);\n \n const extrudeOpt = new ExtrudeDto();\n extrudeOpt.shape = zigZagFace;\n extrudeOpt.direction = [0, height, 0];\n const extrudedZigZag = await operations.extrude(extrudeOpt);\n\n bitbybit.draw.drawAnyAsync({ entity: extrudedZigZag });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { wire, face } = bitbybit.occt.shapes;\nconst { fillets, operations } = bitbybit.occt;\nconst { FilletDto, ExtrudeDto, Heart2DDto, ZigZagBetweenTwoWiresDto, FaceFromWireDto } = Bit.Inputs.OCCT;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\nconst start = async () => {\n\n const height = 4;\n\n const heartOpt = new Heart2DDto();\n heartOpt.sizeApprox = 7;\n const heart1 = await wire.createHeartWire(heartOpt);\n\n heartOpt.sizeApprox = 5;\n const heart2 = await wire.createHeartWire(heartOpt);\n\n const zigZagOpt = new ZigZagBetweenTwoWiresDto(heart1, heart2, 31);\n const zigZagWire = await wire.createZigZagBetweenTwoWires(zigZagOpt);\n\n const faceFromWireOpt = new FaceFromWireDto(zigZagWire, true);\n const zigZagFace = await face.createFaceFromWire(faceFromWireOpt);\n \n const extrudeOpt = new ExtrudeDto();\n extrudeOpt.shape = zigZagFace;\n extrudeOpt.direction = [0, height, 0];\n const extrudedZigZag = await operations.extrude(extrudeOpt);\n\n bitbybit.draw.drawAnyAsync({ entity: extrudedZigZag });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Extrude the face into solid shape" /> diff --git a/docs/learn/code/common/occt/operations/offset-operations.md b/docs/learn/code/common/occt/operations/offset-operations.md index 426424e5..77367c35 100644 --- a/docs/learn/code/common/occt/operations/offset-operations.md +++ b/docs/learn/code/common/occt/operations/offset-operations.md @@ -44,21 +44,21 @@ Key parameters for wire offset include: hexagonoffsetDistanceoffsetShapehexagon00001046offsetDistance1.2offsetShapehexagonoffsetDistance0.1hexagon#00ff004offsetShape#ff00004","version":"0.20.13","type":"blockly"}} + script={{"script":"hexagonoffsetDistanceoffsetShapehexagon00001046offsetDistance1.2offsetShapehexagonoffsetDistance0.1hexagon#00ff004offsetShape#ff00004","version":"0.20.14","type":"blockly"}} title="Basic Wire Offset Operation" /> {\n\n // Define the offset distance - positive values expand the shape, negative values contract it\n const offsetDistance = 1.2;\n\n // Create a new NGonWireDto instance to configure a hexagonal wire\n const hexagonOpt = new Bit.Inputs.OCCT.NGonWireDto();\n // Set the center point of the polygon\n hexagonOpt.center = [0, 0, 0];\n // Set the direction vector (normal to the plane of the polygon)\n hexagonOpt.direction = [0, 1, 0];\n // Set the radius from center to vertices\n hexagonOpt.radius = 4;\n // Set the number of corners (6 for hexagon)\n hexagonOpt.nrCorners = 6;\n \n // Create the hexagonal wire\n const hexagon = await wire.createNGonWire(hexagonOpt);\n\n // Create a new OffsetDto instance to configure the offset operation\n const offsetOpt = new OffsetDto();\n // Set the shape to be offset\n offsetOpt.shape = hexagon;\n // Set the offset distance (positive = outward, negative = inward)\n offsetOpt.distance = offsetDistance;\n // Set the tolerance for the offset calculation\n offsetOpt.tolerance = 0.1;\n \n // Perform the offset operation\n const offsetShape = await operations.offset(offsetOpt);\n\n // Draw both the original and offset shapes with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const offsetOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offsetOptions.edgeWidth = 4;\n offsetOptions.edgeColour = \"#ff0000\"; // Red for offset\n \n bitbybit.draw.drawAnyAsync({ entity: hexagon, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: offsetShape, options: offsetOptions });\n\n}\n\n// Call the 'start' function to execute the script\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import the 'wire' module for creating wire shapes from the OCCT library\nconst { wire } = bitbybit.occt.shapes;\n// Import the 'operations' module for offset operations\nconst { operations } = bitbybit.occt;\n// Import Data Transfer Objects (DTOs) for configuring polygon creation and offset operations\nconst { NGonWireDto, OffsetDto } = Bit.Inputs.OCCT;\n// Define a type alias for a pointer to a TopoDS_Wire\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Define an asynchronous function named 'start' which will contain the main logic\nconst start = async () => {\n\n // Define the offset distance - positive values expand the shape, negative values contract it\n const offsetDistance = 1.2;\n\n // Create a new NGonWireDto instance to configure a hexagonal wire\n const hexagonOpt = new Bit.Inputs.OCCT.NGonWireDto();\n // Set the center point of the polygon\n hexagonOpt.center = [0, 0, 0];\n // Set the direction vector (normal to the plane of the polygon)\n hexagonOpt.direction = [0, 1, 0];\n // Set the radius from center to vertices\n hexagonOpt.radius = 4;\n // Set the number of corners (6 for hexagon)\n hexagonOpt.nrCorners = 6;\n \n // Create the hexagonal wire\n const hexagon = await wire.createNGonWire(hexagonOpt);\n\n // Create a new OffsetDto instance to configure the offset operation\n const offsetOpt = new OffsetDto();\n // Set the shape to be offset\n offsetOpt.shape = hexagon;\n // Set the offset distance (positive = outward, negative = inward)\n offsetOpt.distance = offsetDistance;\n // Set the tolerance for the offset calculation\n offsetOpt.tolerance = 0.1;\n \n // Perform the offset operation\n const offsetShape = await operations.offset(offsetOpt);\n\n // Draw both the original and offset shapes with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const offsetOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offsetOptions.edgeWidth = 4;\n offsetOptions.edgeColour = \"#ff0000\"; // Red for offset\n \n bitbybit.draw.drawAnyAsync({ entity: hexagon, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: offsetShape, options: offsetOptions });\n\n}\n\n// Call the 'start' function to execute the script\nstart();","version":"0.20.14","type":"typescript"}} title="Basic Wire Offset Operation" /> @@ -83,21 +83,21 @@ The join types available are: starWireoffsetDistanceoffsetShapestarWire000010531.50FALSEoffsetDistance0.8offsetShapestarWireoffsetDistance0.1intersectionFALSEstarWire#00ff004offsetShape#ff00004","version":"0.20.13","type":"blockly"}} + script={{"script":"starWireoffsetDistanceoffsetShapestarWire000010531.50FALSEoffsetDistance0.8offsetShapestarWireoffsetDistance0.1intersectionFALSEstarWire#00ff004offsetShape#ff00004","version":"0.20.14","type":"blockly"}} title="Advanced Offset with Join Type Control" /> {\n\n // Define the offset distance\n const offsetDistance = 0.8;\n\n // Create a star-shaped wire for demonstration\n const starOpt = new StarDto();\n starOpt.center = [0, 0, 0];\n starOpt.direction = [0, 1, 0];\n starOpt.numRays = 5;\n starOpt.outerRadius = 3;\n starOpt.innerRadius = 1.5;\n \n const starWire = await wire.createStarWire(starOpt);\n\n // Create an advanced offset with specific join type\n const offsetAdvOpt = new OffsetAdvancedDto();\n offsetAdvOpt.shape = starWire;\n offsetAdvOpt.distance = offsetDistance;\n offsetAdvOpt.tolerance = 0.1;\n // Set join type to 'intersection' for sharp corners\n // Other options: 'arc' (rounded), 'tangent' (beveled)\n offsetAdvOpt.joinType = Bit.Inputs.OCCT.joinTypeEnum.intersection;\n offsetAdvOpt.removeIntEdges = false;\n \n const offsetShape = await operations.offsetAdv(offsetAdvOpt);\n\n // Draw both shapes with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const offsetOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offsetOptions.edgeWidth = 4;\n offsetOptions.edgeColour = \"#ff0000\"; // Red for offset\n \n bitbybit.draw.drawAnyAsync({ entity: starWire, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: offsetShape, options: offsetOptions });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required modules for wire creation and operations\nconst { wire } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\n// Import DTOs and enums for star creation and advanced offset\nconst { StarDto, OffsetAdvancedDto } = Bit.Inputs.OCCT;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\nconst start = async () => {\n\n // Define the offset distance\n const offsetDistance = 0.8;\n\n // Create a star-shaped wire for demonstration\n const starOpt = new StarDto();\n starOpt.center = [0, 0, 0];\n starOpt.direction = [0, 1, 0];\n starOpt.numRays = 5;\n starOpt.outerRadius = 3;\n starOpt.innerRadius = 1.5;\n \n const starWire = await wire.createStarWire(starOpt);\n\n // Create an advanced offset with specific join type\n const offsetAdvOpt = new OffsetAdvancedDto();\n offsetAdvOpt.shape = starWire;\n offsetAdvOpt.distance = offsetDistance;\n offsetAdvOpt.tolerance = 0.1;\n // Set join type to 'intersection' for sharp corners\n // Other options: 'arc' (rounded), 'tangent' (beveled)\n offsetAdvOpt.joinType = Bit.Inputs.OCCT.joinTypeEnum.intersection;\n offsetAdvOpt.removeIntEdges = false;\n \n const offsetShape = await operations.offsetAdv(offsetAdvOpt);\n\n // Draw both shapes with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const offsetOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offsetOptions.edgeWidth = 4;\n offsetOptions.edgeColour = \"#ff0000\"; // Red for offset\n \n bitbybit.draw.drawAnyAsync({ entity: starWire, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: offsetShape, options: offsetOptions });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Advanced Offset with Join Type Control" /> @@ -117,21 +117,21 @@ The thick solid operation takes a shell (collection of connected faces) or a fac circleWirecircularFacethicknessthickSolidcircleWire0000103circularFacecircleWireTRUEthickness0.5thickSolidcircularFacethicknesstranslatedSolidthickSolid010circularFace#00ff004translatedSolid#ff00004","version":"0.20.13","type":"blockly"}} + script={{"script":"circleWirecircularFacethicknessthickSolidcircleWire0000103circularFacecircleWireTRUEthickness0.5thickSolidcircularFacethicknesstranslatedSolidthickSolid010circularFace#00ff004translatedSolid#ff00004","version":"0.20.14","type":"blockly"}} title="Creating Thick Solid from Face" /> {\n\n // Define the thickness for the solid\n const thickness = 0.5;\n\n // Step 1: Create a circular wire\n const circleOpt = new CircleDto();\n circleOpt.center = [0, 0, 0];\n circleOpt.direction = [0, 1, 0];\n circleOpt.radius = 3;\n \n const circleWire = await wire.createCircleWire(circleOpt);\n\n // Step 2: Convert the wire to a face\n const faceOpt = new FaceFromWireDto();\n faceOpt.shape = circleWire;\n faceOpt.planar = true;\n \n const circularFace = await face.createFaceFromWire(faceOpt);\n\n // Step 3: Create a thick solid from the face\n const thickSolidOpt = new ThisckSolidSimpleDto();\n thickSolidOpt.shape = circularFace;\n // Positive offset creates thickness in the direction of the face normal\n thickSolidOpt.offset = thickness;\n \n const thickSolid = await operations.makeThickSolidSimple(thickSolidOpt);\n\n // Step 4: Translate the thick solid upwards to make the original face visible\n const translationOpt = new TranslateDto();\n translationOpt.shape = thickSolid;\n translationOpt.translation = [0, 1, 0]; // Move up by 1 unit\n \n const translatedSolid = await transforms.translate(translationOpt);\n\n // Draw both the original face and resulting thick solid with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const thickSolidOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n thickSolidOptions.edgeWidth = 4;\n thickSolidOptions.edgeColour = \"#ff0000\"; // Red for thick solid\n \n bitbybit.draw.drawAnyAsync({ entity: circularFace, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: translatedSolid, options: thickSolidOptions });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required modules for wire, face creation and operations\nconst { wire, face } = bitbybit.occt.shapes;\nconst { operations, transforms } = bitbybit.occt;\n// Import DTOs for circle, face and thick solid creation\nconst { CircleDto, FaceFromWireDto, ThisckSolidSimpleDto, TranslateDto } = Bit.Inputs.OCCT;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\n\nconst start = async () => {\n\n // Define the thickness for the solid\n const thickness = 0.5;\n\n // Step 1: Create a circular wire\n const circleOpt = new CircleDto();\n circleOpt.center = [0, 0, 0];\n circleOpt.direction = [0, 1, 0];\n circleOpt.radius = 3;\n \n const circleWire = await wire.createCircleWire(circleOpt);\n\n // Step 2: Convert the wire to a face\n const faceOpt = new FaceFromWireDto();\n faceOpt.shape = circleWire;\n faceOpt.planar = true;\n \n const circularFace = await face.createFaceFromWire(faceOpt);\n\n // Step 3: Create a thick solid from the face\n const thickSolidOpt = new ThisckSolidSimpleDto();\n thickSolidOpt.shape = circularFace;\n // Positive offset creates thickness in the direction of the face normal\n thickSolidOpt.offset = thickness;\n \n const thickSolid = await operations.makeThickSolidSimple(thickSolidOpt);\n\n // Step 4: Translate the thick solid upwards to make the original face visible\n const translationOpt = new TranslateDto();\n translationOpt.shape = thickSolid;\n translationOpt.translation = [0, 1, 0]; // Move up by 1 unit\n \n const translatedSolid = await transforms.translate(translationOpt);\n\n // Draw both the original face and resulting thick solid with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const thickSolidOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n thickSolidOptions.edgeWidth = 4;\n thickSolidOptions.edgeColour = \"#ff0000\"; // Red for thick solid\n \n bitbybit.draw.drawAnyAsync({ entity: circularFace, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: translatedSolid, options: thickSolidOptions });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Creating Thick Solid from Face" /> @@ -151,21 +151,21 @@ This operation works by: curvedWireoffsetDistanceextrusionDirectionoffset3DWirecurvedWire0002124-11623FALSEoffsetDistance0.5extrusionDirection010offset3DWirecurvedWireextrusionDirectionoffsetDistancecurvedWire#00ff004offset3DWire#ff00004","version":"0.20.13","type":"blockly"}} + script={{"script":"curvedWireoffsetDistanceextrusionDirectionoffset3DWirecurvedWire0002124-11623FALSEoffsetDistance0.5extrusionDirection010offset3DWirecurvedWireextrusionDirectionoffsetDistancecurvedWire#00ff004offset3DWire#ff00004","version":"0.20.14","type":"blockly"}} title="3D Wire Offset Operation" /> {\n\n // Define parameters for the 3D offset\n const offsetDistance = 0.5;\n const extrusionDirection = [0, 1, 0]; // Y-axis direction\n\n // Step 1: Create a curved 3D wire using B-spline\n const curveOpt = new BSplineDto();\n // Define control points for a truly 3D curve with varying Y coordinates\n curveOpt.points = [\n [0, 0, 0], // Start point\n [2, 1, 2], // First control point (elevated in Y)\n [4, -1, 1], // Second control point (lowered in Y)\n [6, 2, 3] // End point (elevated in Y and Z)\n ];\n curveOpt.closed = false;\n \n const curvedWire = await wire.createBSpline(curveOpt);\n\n // Step 2: Create 3D offset of the wire\n const offset3DOpt = new Offset3DWireDto();\n offset3DOpt.shape = curvedWire;\n // Direction for the temporary extrusion used in the offset calculation\n offset3DOpt.direction = extrusionDirection as Bit.Inputs.Base.Point3;\n // Distance to offset the wire\n offset3DOpt.offset = offsetDistance;\n \n const offset3DWire = await operations.offset3DWire(offset3DOpt);\n\n // Draw both the original and offset wires with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const offsetOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offsetOptions.edgeWidth = 4;\n offsetOptions.edgeColour = \"#ff0000\"; // Red for offset\n \n bitbybit.draw.drawAnyAsync({ entity: curvedWire, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: offset3DWire, options: offsetOptions });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required modules for wire creation and operations\nconst { wire } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\n// Import DTOs for BSpline and 3D wire offset\nconst { BSplineDto, Offset3DWireDto } = Bit.Inputs.OCCT;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\nconst start = async () => {\n\n // Define parameters for the 3D offset\n const offsetDistance = 0.5;\n const extrusionDirection = [0, 1, 0]; // Y-axis direction\n\n // Step 1: Create a curved 3D wire using B-spline\n const curveOpt = new BSplineDto();\n // Define control points for a truly 3D curve with varying Y coordinates\n curveOpt.points = [\n [0, 0, 0], // Start point\n [2, 1, 2], // First control point (elevated in Y)\n [4, -1, 1], // Second control point (lowered in Y)\n [6, 2, 3] // End point (elevated in Y and Z)\n ];\n curveOpt.closed = false;\n \n const curvedWire = await wire.createBSpline(curveOpt);\n\n // Step 2: Create 3D offset of the wire\n const offset3DOpt = new Offset3DWireDto();\n offset3DOpt.shape = curvedWire;\n // Direction for the temporary extrusion used in the offset calculation\n offset3DOpt.direction = extrusionDirection as Bit.Inputs.Base.Point3;\n // Distance to offset the wire\n offset3DOpt.offset = offsetDistance;\n \n const offset3DWire = await operations.offset3DWire(offset3DOpt);\n\n // Draw both the original and offset wires with different colors for comparison\n const originalOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n originalOptions.edgeWidth = 4;\n originalOptions.edgeColour = \"#00ff00\"; // Green for original\n \n const offsetOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offsetOptions.edgeWidth = 4;\n offsetOptions.edgeColour = \"#ff0000\"; // Red for offset\n \n bitbybit.draw.drawAnyAsync({ entity: curvedWire, options: originalOptions });\n bitbybit.draw.drawAnyAsync({ entity: offset3DWire, options: offsetOptions });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="3D Wire Offset Operation" /> diff --git a/docs/learn/code/common/occt/operations/rotated-extrusions.md b/docs/learn/code/common/occt/operations/rotated-extrusions.md index 8663858e..0936ff6c 100644 --- a/docs/learn/code/common/occt/operations/rotated-extrusions.md +++ b/docs/learn/code/common/occt/operations/rotated-extrusions.md @@ -34,21 +34,21 @@ In the examples below, we will demonstrate how to create a simple solid 3D helic 130000104360TRUE","version":"0.20.13","type":"blockly"}} + script={{"script":"130000104360TRUE","version":"0.20.14","type":"blockly"}} title="Extrude the wire into shell kind of shape" /> {\n\n const recOpt = new Bit.Inputs.OCCT.RectangleDto(1, 3);\n const rectangle = await bitbybit.occt.shapes.wire.createRectangleWire(recOpt);\n const rotatedExtrudeOpt = new Bit.Inputs.OCCT.RotationExtrudeDto(rectangle, 4);\n const rotatedExtrude = await bitbybit.occt.operations.rotatedExtrude(rotatedExtrudeOpt)\n\n bitbybit.draw.drawAnyAsync({ entity: rotatedExtrude });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const recOpt = new Bit.Inputs.OCCT.RectangleDto(1, 3);\n const rectangle = await bitbybit.occt.shapes.wire.createRectangleWire(recOpt);\n const rotatedExtrudeOpt = new Bit.Inputs.OCCT.RotationExtrudeDto(rectangle, 4);\n const rotatedExtrude = await bitbybit.occt.operations.rotatedExtrude(rotatedExtrudeOpt)\n\n bitbybit.draw.drawAnyAsync({ entity: rotatedExtrude });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Extrude the wire into shell kind of shape" /> @@ -62,21 +62,21 @@ When the profile is further from the center it will form a helix like shape. Thi pointPromisespointsngonRotationPromisesi3050300150117910001000100010003pointPromises00001061012FALSETRUEpointspointPromisesngonRotationPromisesipointsINSERTLASTngonRotationPromisesi01061.530270TRUEngonRotationPromises","version":"0.20.13","type":"blockly"}} + script={{"script":"pointPromisespointsngonRotationPromisesi3050300150117910001000100010003pointPromises00001061012FALSETRUEpointspointPromisesngonRotationPromisesipointsINSERTLASTngonRotationPromisesi01061.530270TRUEngonRotationPromises","version":"0.20.14","type":"blockly"}} title="Extrude the wire into shell kind of shape" /> {\n\n // Adjust the default camera position to face the object\n const cameraOptions = new CameraConfigurationDto();\n cameraOptions.position = [30, 50, 50];\n cameraOptions.lookAt = [0, 15, 0];\n scene.adjustActiveArcRotateCamera(cameraOptions);\n\n // This ellipse will be used to derive origins for ngons on the ground plane\n const ellipseOptions = new EllipseDto();\n ellipseOptions.radiusMinor = 6;\n ellipseOptions.radiusMajor = 10;\n const ellipse = await wire.createEllipseWire(ellipseOptions);\n\n // We divide the wire into 12 segments and return the points\n const divideOptions = new DivideDto(ellipse);\n divideOptions.removeEndPoint = true;\n divideOptions.nrOfDivisions = 12;\n const points = await wire.divideWireByEqualDistanceToPoints(divideOptions);\n\n // Create ngons on these points\n const ngonOptions = new NGonWireDto();\n ngonOptions.radius = 1.5;\n const ngonPromises = points.map(point => {\n ngonOptions.center = point;\n return wire.createNGonWire(ngonOptions);\n });\n\n const ngons = await Promise.all(ngonPromises);\n\n // Form rotated extrusions on all of the points\n const rotatedExtrudeOptions = new RotationExtrudeDto();\n rotatedExtrudeOptions.angle = 270;\n rotatedExtrudeOptions.height = 30;\n const rotatedExtrusionPromises = ngons.map(ngon => {\n rotatedExtrudeOptions.shape = ngon;\n return operations.rotatedExtrude(rotatedExtrudeOptions);\n });\n\n const rotatedExtrusions = await Promise.all(rotatedExtrusionPromises);\n\n // Compounding multiple shapes will generally deliver much better rendering performance\n // as it will form a single mesh for all of the geometries involved in compound\n const compoundOptions = new CompoundShapesDto(rotatedExtrusions);\n const ngonCompound = await compound.makeCompound(compoundOptions);\n\n // As a last step we draw the ngon with default occt settings (defualt because we omit specifying options property)\n bitbybit.draw.drawAnyAsync({ entity: ngonCompound });\n\n}\n\n// Let's not forget to execute the start function, otherwise nothing will happen.\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// These destructured imports are optional, but convenient later on - option DTO's are classes\nconst { EllipseDto, RotationExtrudeDto, DivideDto, NGonWireDto, CompoundShapesDto } = Bit.Inputs.OCCT;\nconst { CameraConfigurationDto } = Bit.Inputs.BabylonScene;\n// These are parts of the bitbybit API that will be used in the script\nconst { wire, compound } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\nconst { scene } = bitbybit.babylon;\n// Types need to be destructured one by one\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Async definition of the start function where we can await on asynchronous CAD algorithms running inside the web workers\nconst start = async () => {\n\n // Adjust the default camera position to face the object\n const cameraOptions = new CameraConfigurationDto();\n cameraOptions.position = [30, 50, 50];\n cameraOptions.lookAt = [0, 15, 0];\n scene.adjustActiveArcRotateCamera(cameraOptions);\n\n // This ellipse will be used to derive origins for ngons on the ground plane\n const ellipseOptions = new EllipseDto();\n ellipseOptions.radiusMinor = 6;\n ellipseOptions.radiusMajor = 10;\n const ellipse = await wire.createEllipseWire(ellipseOptions);\n\n // We divide the wire into 12 segments and return the points\n const divideOptions = new DivideDto(ellipse);\n divideOptions.removeEndPoint = true;\n divideOptions.nrOfDivisions = 12;\n const points = await wire.divideWireByEqualDistanceToPoints(divideOptions);\n\n // Create ngons on these points\n const ngonOptions = new NGonWireDto();\n ngonOptions.radius = 1.5;\n const ngonPromises = points.map(point => {\n ngonOptions.center = point;\n return wire.createNGonWire(ngonOptions);\n });\n\n const ngons = await Promise.all(ngonPromises);\n\n // Form rotated extrusions on all of the points\n const rotatedExtrudeOptions = new RotationExtrudeDto();\n rotatedExtrudeOptions.angle = 270;\n rotatedExtrudeOptions.height = 30;\n const rotatedExtrusionPromises = ngons.map(ngon => {\n rotatedExtrudeOptions.shape = ngon;\n return operations.rotatedExtrude(rotatedExtrudeOptions);\n });\n\n const rotatedExtrusions = await Promise.all(rotatedExtrusionPromises);\n\n // Compounding multiple shapes will generally deliver much better rendering performance\n // as it will form a single mesh for all of the geometries involved in compound\n const compoundOptions = new CompoundShapesDto(rotatedExtrusions);\n const ngonCompound = await compound.makeCompound(compoundOptions);\n\n // As a last step we draw the ngon with default occt settings (defualt because we omit specifying options property)\n bitbybit.draw.drawAnyAsync({ entity: ngonCompound });\n\n}\n\n// Let's not forget to execute the start function, otherwise nothing will happen.\nstart();","version":"0.20.14","type":"typescript"}} title="Extrude the wire into shell kind of shape" /> diff --git a/docs/learn/code/common/occt/operations/simple-loft.md b/docs/learn/code/common/occt/operations/simple-loft.md index 697ae835..d37c06de 100644 --- a/docs/learn/code/common/occt/operations/simple-loft.md +++ b/docs/learn/code/common/occt/operations/simple-loft.md @@ -37,21 +37,21 @@ In this tutorial, we'll walk through the process of creating a simple lofted sha ellipse1ellipse2ellipse3ellipsesloftellipse100001048ellipse203001014ellipse307001026ellipsesellipse1ellipse2ellipse3loftellipsesFALSEloft0.001TRUE#6600ccTRUE#ffffff2","version":"0.20.13","type":"blockly"}} + script={{"script":"ellipse1ellipse2ellipse3ellipsesloftellipse100001048ellipse203001014ellipse307001026ellipsesellipse1ellipse2ellipse3loftellipsesFALSEloft0.001TRUE#6600ccTRUE#ffffff2","version":"0.20.14","type":"blockly"}} title="Simple Loft Operation" /> {\n\n // Create an instance of EllipseDto to define the properties of the first ellipse.\n const ellipseOpt = new EllipseDto();\n // Set the minor radius of the ellipse.\n ellipseOpt.radiusMinor = 4;\n // Set the major radius of the ellipse.\n ellipseOpt.radiusMajor = 8;\n // Create the first elliptical wire. The center defaults to [0,0,0] and direction to [0,1,0] if not specified.\n // 'await' is used because shape creation is an asynchronous operation.\n const ellipse1 = await shapes.wire.createEllipseWire(ellipseOpt);\n\n // Modify the ellipseOpt for the second ellipse.\n // Set the center of the second ellipse.\n ellipseOpt.center = [0, 3, 0];\n // Set the minor radius for the second ellipse.\n ellipseOpt.radiusMinor = 1;\n // Set the major radius for the second ellipse.\n ellipseOpt.radiusMajor = 4;\n // Create the second elliptical wire with the updated options.\n const ellipse2 = await shapes.wire.createEllipseWire(ellipseOpt);\n\n // Modify the ellipseOpt for the third ellipse.\n // Set the center of the third ellipse.\n ellipseOpt.center = [0, 7, 0];\n // Set the minor radius for the third ellipse.\n ellipseOpt.radiusMinor = 2;\n // Set the major radius for the third ellipse.\n ellipseOpt.radiusMajor = 6;\n // Create the third elliptical wire with the updated options.\n const ellipse3 = await shapes.wire.createEllipseWire(ellipseOpt);\n\n // Create an instance of LoftDto to define the parameters for the loft operation.\n // The generic type TopoDSShapePointer indicates the type of shapes to be lofted.\n const loftOpt = new LoftDto();\n // Assign an array of the created ellipse wires to the 'shapes' property of loftOpt.\n // These are the profiles that will be connected by the loft.\n loftOpt.shapes = [ellipse1, ellipse2, ellipse3];\n // Perform the loft operation using the defined options.\n // This will create a surface (or shell) connecting the three ellipses.\n // 'makeSolid' defaults to false, creating a shell.\n const loft = await operations.loft(loftOpt);\n\n // Create an instance of DrawOcctShapeSimpleOptions to define how the lofted shape will be displayed.\n const drawOpt = new DrawOcctShapeSimpleOptions();\n // Set the precision for drawing the shape. This affects the tessellation quality.\n drawOpt.precision = 0.001;\n // Set the color of the faces of the lofted shape.\n drawOpt.faceColour = \"#ff00ff\"; // Magenta\n // Draw the lofted shape asynchronously using the specified entity and drawing options.\n draw.drawAnyAsync({ entity: loft, options: drawOpt });\n\n}\n\n// Call the start function to execute the script.\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import necessary modules from the bitbybit library.\n// 'operations' and 'shapes' are for OpenCascade Technology (OCCT) functionalities like lofting and creating wires.\nconst { operations, shapes } = bitbybit.occt;\n// 'draw' module is used for rendering shapes on the canvas.\nconst { draw } = bitbybit;\n// Import Data Transfer Objects (DTOs) for defining OCCT inputs.\n// 'EllipseDto' for creating ellipses, 'LoftDto' for loft operation parameters.\nconst { EllipseDto, LoftDto } = Bit.Inputs.OCCT;\n// Import DTO for drawing options.\nconst { DrawOcctShapeSimpleOptions } = Bit.Inputs.Draw;\n// Define a type alias for OCCT shape pointers for better readability.\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\n// Define an asynchronous function 'start' which will contain the main logic.\nconst start = async () => {\n\n // Create an instance of EllipseDto to define the properties of the first ellipse.\n const ellipseOpt = new EllipseDto();\n // Set the minor radius of the ellipse.\n ellipseOpt.radiusMinor = 4;\n // Set the major radius of the ellipse.\n ellipseOpt.radiusMajor = 8;\n // Create the first elliptical wire. The center defaults to [0,0,0] and direction to [0,1,0] if not specified.\n // 'await' is used because shape creation is an asynchronous operation.\n const ellipse1 = await shapes.wire.createEllipseWire(ellipseOpt);\n\n // Modify the ellipseOpt for the second ellipse.\n // Set the center of the second ellipse.\n ellipseOpt.center = [0, 3, 0];\n // Set the minor radius for the second ellipse.\n ellipseOpt.radiusMinor = 1;\n // Set the major radius for the second ellipse.\n ellipseOpt.radiusMajor = 4;\n // Create the second elliptical wire with the updated options.\n const ellipse2 = await shapes.wire.createEllipseWire(ellipseOpt);\n\n // Modify the ellipseOpt for the third ellipse.\n // Set the center of the third ellipse.\n ellipseOpt.center = [0, 7, 0];\n // Set the minor radius for the third ellipse.\n ellipseOpt.radiusMinor = 2;\n // Set the major radius for the third ellipse.\n ellipseOpt.radiusMajor = 6;\n // Create the third elliptical wire with the updated options.\n const ellipse3 = await shapes.wire.createEllipseWire(ellipseOpt);\n\n // Create an instance of LoftDto to define the parameters for the loft operation.\n // The generic type TopoDSShapePointer indicates the type of shapes to be lofted.\n const loftOpt = new LoftDto();\n // Assign an array of the created ellipse wires to the 'shapes' property of loftOpt.\n // These are the profiles that will be connected by the loft.\n loftOpt.shapes = [ellipse1, ellipse2, ellipse3];\n // Perform the loft operation using the defined options.\n // This will create a surface (or shell) connecting the three ellipses.\n // 'makeSolid' defaults to false, creating a shell.\n const loft = await operations.loft(loftOpt);\n\n // Create an instance of DrawOcctShapeSimpleOptions to define how the lofted shape will be displayed.\n const drawOpt = new DrawOcctShapeSimpleOptions();\n // Set the precision for drawing the shape. This affects the tessellation quality.\n drawOpt.precision = 0.001;\n // Set the color of the faces of the lofted shape.\n drawOpt.faceColour = \"#ff00ff\"; // Magenta\n // Draw the lofted shape asynchronously using the specified entity and drawing options.\n draw.drawAnyAsync({ entity: loft, options: drawOpt });\n\n}\n\n// Call the start function to execute the script.\nstart();","version":"0.20.14","type":"typescript"}} title="Simple Loft Operation" /> diff --git a/docs/learn/code/common/occt/operations/thick-solids.md b/docs/learn/code/common/occt/operations/thick-solids.md index 07e41d48..38fa01ec 100644 --- a/docs/learn/code/common/occt/operations/thick-solids.md +++ b/docs/learn/code/common/occt/operations/thick-solids.md @@ -63,21 +63,21 @@ The following examples demonstrate creating thick solids from complex lofted sur bottomPointsmiddlePointstopPointsbottomWiremiddleWiretopWirewireListloftSurfacethickSolidtranslatedSolidthicknessoffsetVectorsurfaceOptionssolidOptionsbottomPoints-30-2-10000110030-2middlePoints-22-1-0.521.50220.521.522-1topPoints-1.540-0.540.50410.540.51.540bottomWirebottomPointsFALSE0.1middleWiremiddlePointsFALSE0.1topWiretopPointsFALSE0.1wireListbottomWiremiddleWiretopWireloftSurfacewireListFALSEthickness0.1thickSolidloftSurfacethicknessoffsetVector003translatedSolidthickSolidoffsetVectorsurfaceOptions0.01TRUE#00ff00TRUE#ffffff2solidOptions0.01TRUE#ff6600TRUE#ffffff2loftSurfacesurfaceOptionstranslatedSolidsolidOptions","version":"0.20.13","type":"blockly"}} + script={{"script":"bottomPointsmiddlePointstopPointsbottomWiremiddleWiretopWirewireListloftSurfacethickSolidtranslatedSolidthicknessoffsetVectorsurfaceOptionssolidOptionsbottomPoints-30-2-10000110030-2middlePoints-22-1-0.521.50220.521.522-1topPoints-1.540-0.540.50410.540.51.540bottomWirebottomPointsFALSE0.1middleWiremiddlePointsFALSE0.1topWiretopPointsFALSE0.1wireListbottomWiremiddleWiretopWireloftSurfacewireListFALSEthickness0.1thickSolidloftSurfacethicknessoffsetVector003translatedSolidthickSolidoffsetVectorsurfaceOptions0.01TRUE#00ff00TRUE#ffffff2solidOptions0.01TRUE#ff6600TRUE#ffffff2loftSurfacesurfaceOptionstranslatedSolidsolidOptions","version":"0.20.14","type":"blockly"}} title="Thick Solid from Lofted Surface (Blockly)" /> {\n\n // Define points for bottom curve (wave-like pattern)\n const bottomPoints = [\n [-3, 0, -2],\n [-1, 0, 0],\n [0, 0, 1],\n [1, 0, 0],\n [3, 0, -2]\n ] as Point3[];\n\n // Define points for middle curve (elevated and more curved)\n const middlePoints = [\n [-2, 2, -1],\n [-0.5, 2, 1.5],\n [0, 2, 2],\n [0.5, 2, 1.5],\n [2, 2, -1]\n ] as Point3[];\n\n // Define points for top curve (simpler, less curved)\n const topPoints = [\n [-1.5, 4, 0],\n [-0.5, 4, 0.5],\n [0, 4, 1],\n [0.5, 4, 0.5],\n [1.5, 4, 0]\n ] as Point3[];\n\n // Create interpolated wires from points\n const bottomWireOpt = new InterpolationDto();\n bottomWireOpt.points = bottomPoints;\n bottomWireOpt.periodic = false;\n bottomWireOpt.tolerance = 0.1;\n const bottomWire = await wire.interpolatePoints(bottomWireOpt);\n\n const middleWireOpt = new InterpolationDto();\n middleWireOpt.points = middlePoints;\n middleWireOpt.periodic = false;\n middleWireOpt.tolerance = 0.1;\n const middleWire = await wire.interpolatePoints(middleWireOpt);\n\n const topWireOpt = new InterpolationDto();\n topWireOpt.points = topPoints;\n topWireOpt.periodic = false;\n topWireOpt.tolerance = 0.1;\n const topWire = await wire.interpolatePoints(topWireOpt);\n\n // Create lofted surface from the three wires\n const loftOpt = new LoftDto();\n loftOpt.shapes = [bottomWire, middleWire, topWire];\n loftOpt.makeSolid = false;\n const loftSurface = await operations.loft(loftOpt);\n\n // Create thick solid from the lofted surface\n const thickness = 0.1;\n const thickSolidOpt = new ThisckSolidSimpleDto();\n thickSolidOpt.shape = loftSurface;\n thickSolidOpt.offset = thickness;\n const thickSolid = await operations.makeThickSolidSimple(thickSolidOpt);\n\n // Translate the thick solid for better visualization\n const offsetVector = [0, 0, 3] as Vector3;\n const translateOpt = new TranslateDto();\n translateOpt.shape = thickSolid;\n translateOpt.translation = offsetVector;\n const translatedSolid = await transforms.translate(translateOpt);\n\n // Create drawing options for the original surface (green)\n const surfaceOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n surfaceOptions.precision = 0.01;\n surfaceOptions.drawFaces = true;\n surfaceOptions.faceColour = \"#00ff00\";\n surfaceOptions.drawEdges = true;\n surfaceOptions.edgeColour = \"#ffffff\";\n surfaceOptions.edgeWidth = 2;\n\n // Create drawing options for the thick solid (orange)\n const solidOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n solidOptions.precision = 0.01;\n solidOptions.drawFaces = true;\n solidOptions.faceColour = \"#ff6600\";\n solidOptions.drawEdges = true;\n solidOptions.edgeColour = \"#ffffff\";\n solidOptions.edgeWidth = 2;\n\n // Draw both the original lofted surface and the thick solid\n bitbybit.draw.drawAnyAsync({ entity: loftSurface, options: surfaceOptions });\n bitbybit.draw.drawAnyAsync({ entity: translatedSolid, options: solidOptions });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required modules from global bitbybit variable\nconst { wire } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\nconst { transforms } = bitbybit.occt;\n\nconst { InterpolationDto, LoftDto, ThisckSolidSimpleDto, TranslateDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define a type alias for pointers\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\nconst start = async () => {\n\n // Define points for bottom curve (wave-like pattern)\n const bottomPoints = [\n [-3, 0, -2],\n [-1, 0, 0],\n [0, 0, 1],\n [1, 0, 0],\n [3, 0, -2]\n ] as Point3[];\n\n // Define points for middle curve (elevated and more curved)\n const middlePoints = [\n [-2, 2, -1],\n [-0.5, 2, 1.5],\n [0, 2, 2],\n [0.5, 2, 1.5],\n [2, 2, -1]\n ] as Point3[];\n\n // Define points for top curve (simpler, less curved)\n const topPoints = [\n [-1.5, 4, 0],\n [-0.5, 4, 0.5],\n [0, 4, 1],\n [0.5, 4, 0.5],\n [1.5, 4, 0]\n ] as Point3[];\n\n // Create interpolated wires from points\n const bottomWireOpt = new InterpolationDto();\n bottomWireOpt.points = bottomPoints;\n bottomWireOpt.periodic = false;\n bottomWireOpt.tolerance = 0.1;\n const bottomWire = await wire.interpolatePoints(bottomWireOpt);\n\n const middleWireOpt = new InterpolationDto();\n middleWireOpt.points = middlePoints;\n middleWireOpt.periodic = false;\n middleWireOpt.tolerance = 0.1;\n const middleWire = await wire.interpolatePoints(middleWireOpt);\n\n const topWireOpt = new InterpolationDto();\n topWireOpt.points = topPoints;\n topWireOpt.periodic = false;\n topWireOpt.tolerance = 0.1;\n const topWire = await wire.interpolatePoints(topWireOpt);\n\n // Create lofted surface from the three wires\n const loftOpt = new LoftDto();\n loftOpt.shapes = [bottomWire, middleWire, topWire];\n loftOpt.makeSolid = false;\n const loftSurface = await operations.loft(loftOpt);\n\n // Create thick solid from the lofted surface\n const thickness = 0.1;\n const thickSolidOpt = new ThisckSolidSimpleDto();\n thickSolidOpt.shape = loftSurface;\n thickSolidOpt.offset = thickness;\n const thickSolid = await operations.makeThickSolidSimple(thickSolidOpt);\n\n // Translate the thick solid for better visualization\n const offsetVector = [0, 0, 3] as Vector3;\n const translateOpt = new TranslateDto();\n translateOpt.shape = thickSolid;\n translateOpt.translation = offsetVector;\n const translatedSolid = await transforms.translate(translateOpt);\n\n // Create drawing options for the original surface (green)\n const surfaceOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n surfaceOptions.precision = 0.01;\n surfaceOptions.drawFaces = true;\n surfaceOptions.faceColour = \"#00ff00\";\n surfaceOptions.drawEdges = true;\n surfaceOptions.edgeColour = \"#ffffff\";\n surfaceOptions.edgeWidth = 2;\n\n // Create drawing options for the thick solid (orange)\n const solidOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n solidOptions.precision = 0.01;\n solidOptions.drawFaces = true;\n solidOptions.faceColour = \"#ff6600\";\n solidOptions.drawEdges = true;\n solidOptions.edgeColour = \"#ffffff\";\n solidOptions.edgeWidth = 2;\n\n // Draw both the original lofted surface and the thick solid\n bitbybit.draw.drawAnyAsync({ entity: loftSurface, options: surfaceOptions });\n bitbybit.draw.drawAnyAsync({ entity: translatedSolid, options: solidOptions });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Thick Solid from Lofted Surface" /> diff --git a/docs/learn/code/common/occt/operations/wire-offset-multiple.md b/docs/learn/code/common/occt/operations/wire-offset-multiple.md index 165b1b71..a55f8311 100644 --- a/docs/learn/code/common/occt/operations/wire-offset-multiple.md +++ b/docs/learn/code/common/occt/operations/wire-offset-multiple.md @@ -31,7 +31,7 @@ The following example demonstrates creating multiple offset curves from a single @@ -39,7 +39,7 @@ The following example demonstrates creating multiple offset curves from a single controlPointsbaseWirestepoverDistanceoffset1offset2offset3baseStyleoffset1Styleoffset2Styleoffset3StylecontrolPoints-40-2-202001203400stepoverDistance0.5baseWirecontrolPointsFALSE0.01offset1baseWirestepoverDistance0.01offset2baseWireMULTIPLYstepoverDistance20.01offset3baseWireMULTIPLYstepoverDistance30.01baseStyle0.01FALSETRUE#00ff004offset1Style0.01FALSETRUE#ff99003offset2Style0.01FALSETRUE#ff66003offset3Style0.01FALSETRUE#ff33003baseWirebaseStyleoffset1offset1Styleoffset2offset2Styleoffset3offset3Style","version":"0.20.13","type":"blockly"}} + script={{"script":"controlPointsbaseWirestepoverDistanceoffset1offset2offset3baseStyleoffset1Styleoffset2Styleoffset3StylecontrolPoints-40-2-202001203400stepoverDistance0.5baseWirecontrolPointsFALSE0.01offset1baseWirestepoverDistance0.01offset2baseWireMULTIPLYstepoverDistance20.01offset3baseWireMULTIPLYstepoverDistance30.01baseStyle0.01FALSETRUE#00ff004offset1Style0.01FALSETRUE#ff99003offset2Style0.01FALSETRUE#ff66003offset3Style0.01FALSETRUE#ff33003baseWirebaseStyleoffset1offset1Styleoffset2offset2Styleoffset3offset3Style","version":"0.20.14","type":"blockly"}} title="Multiple Wire Offsets" /> @@ -47,7 +47,7 @@ The following example demonstrates creating multiple offset curves from a single {\n\n // Define the stepover distance for CNC toolpaths\n const stepoverDistance = 0.5;\n\n // Step 1: Define control points for the base toolpath\n const points = [\n [-4, 0, -2],\n [-2, 0, 2],\n [0, 0, 1],\n [2, 0, 3],\n [4, 0, 0]\n ] as Point3[];\n\n // Step 2: Create base wire through interpolation\n const interpolationOpt = new InterpolationDto();\n interpolationOpt.points = points;\n interpolationOpt.periodic = false;\n interpolationOpt.tolerance = 0.01;\n \n const baseWire = await wire.interpolatePoints(interpolationOpt);\n\n // Step 3: Create multiple offset toolpaths\n // First offset\n const offset1Opt = new OffsetDto();\n offset1Opt.shape = baseWire;\n offset1Opt.distance = stepoverDistance;\n offset1Opt.tolerance = 0.01;\n \n const offset1 = await operations.offset(offset1Opt);\n\n // Second offset\n const offset2Opt = new OffsetDto();\n offset2Opt.shape = baseWire;\n offset2Opt.distance = stepoverDistance * 2;\n offset2Opt.tolerance = 0.01;\n \n const offset2 = await operations.offset(offset2Opt);\n\n // Third offset\n const offset3Opt = new OffsetDto();\n offset3Opt.shape = baseWire;\n offset3Opt.distance = stepoverDistance * 3;\n offset3Opt.tolerance = 0.01;\n \n const offset3 = await operations.offset(offset3Opt);\n\n // Step 4: Visualize all toolpaths with different colors\n // Base wire (green - reference path)\n const baseOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n baseOptions.edgeWidth = 4;\n baseOptions.edgeColour = \"#00ff00\"; // Green\n baseOptions.drawFaces = false;\n \n // Offset 1 (orange)\n const offset1Options = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offset1Options.edgeWidth = 3;\n offset1Options.edgeColour = \"#ff9900\"; // Orange\n offset1Options.drawFaces = false;\n \n // Offset 2 (red-orange)\n const offset2Options = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offset2Options.edgeWidth = 3;\n offset2Options.edgeColour = \"#ff6600\"; // Red-orange\n offset2Options.drawFaces = false;\n \n // Offset 3 (red)\n const offset3Options = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offset3Options.edgeWidth = 3;\n offset3Options.edgeColour = \"#ff3300\"; // Red\n offset3Options.drawFaces = false;\n \n // Draw all toolpaths\n bitbybit.draw.drawAnyAsync({ entity: baseWire, options: baseOptions });\n bitbybit.draw.drawAnyAsync({ entity: offset1, options: offset1Options });\n bitbybit.draw.drawAnyAsync({ entity: offset2, options: offset2Options });\n bitbybit.draw.drawAnyAsync({ entity: offset3, options: offset3Options });\n\n}\n\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required modules for wire creation and operations\nconst { wire } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\n// Import DTOs for interpolation and offset operations\nconst { InterpolationDto, OffsetDto } = Bit.Inputs.OCCT;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype Point3 = Bit.Inputs.Base.Point3;\n\nconst start = async () => {\n\n // Define the stepover distance for CNC toolpaths\n const stepoverDistance = 0.5;\n\n // Step 1: Define control points for the base toolpath\n const points = [\n [-4, 0, -2],\n [-2, 0, 2],\n [0, 0, 1],\n [2, 0, 3],\n [4, 0, 0]\n ] as Point3[];\n\n // Step 2: Create base wire through interpolation\n const interpolationOpt = new InterpolationDto();\n interpolationOpt.points = points;\n interpolationOpt.periodic = false;\n interpolationOpt.tolerance = 0.01;\n \n const baseWire = await wire.interpolatePoints(interpolationOpt);\n\n // Step 3: Create multiple offset toolpaths\n // First offset\n const offset1Opt = new OffsetDto();\n offset1Opt.shape = baseWire;\n offset1Opt.distance = stepoverDistance;\n offset1Opt.tolerance = 0.01;\n \n const offset1 = await operations.offset(offset1Opt);\n\n // Second offset\n const offset2Opt = new OffsetDto();\n offset2Opt.shape = baseWire;\n offset2Opt.distance = stepoverDistance * 2;\n offset2Opt.tolerance = 0.01;\n \n const offset2 = await operations.offset(offset2Opt);\n\n // Third offset\n const offset3Opt = new OffsetDto();\n offset3Opt.shape = baseWire;\n offset3Opt.distance = stepoverDistance * 3;\n offset3Opt.tolerance = 0.01;\n \n const offset3 = await operations.offset(offset3Opt);\n\n // Step 4: Visualize all toolpaths with different colors\n // Base wire (green - reference path)\n const baseOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n baseOptions.edgeWidth = 4;\n baseOptions.edgeColour = \"#00ff00\"; // Green\n baseOptions.drawFaces = false;\n \n // Offset 1 (orange)\n const offset1Options = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offset1Options.edgeWidth = 3;\n offset1Options.edgeColour = \"#ff9900\"; // Orange\n offset1Options.drawFaces = false;\n \n // Offset 2 (red-orange)\n const offset2Options = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offset2Options.edgeWidth = 3;\n offset2Options.edgeColour = \"#ff6600\"; // Red-orange\n offset2Options.drawFaces = false;\n \n // Offset 3 (red)\n const offset3Options = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n offset3Options.edgeWidth = 3;\n offset3Options.edgeColour = \"#ff3300\"; // Red\n offset3Options.drawFaces = false;\n \n // Draw all toolpaths\n bitbybit.draw.drawAnyAsync({ entity: baseWire, options: baseOptions });\n bitbybit.draw.drawAnyAsync({ entity: offset1, options: offset1Options });\n bitbybit.draw.drawAnyAsync({ entity: offset2, options: offset2Options });\n bitbybit.draw.drawAnyAsync({ entity: offset3, options: offset3Options });\n\n}\n\nstart();","version":"0.20.14","type":"typescript"}} title="Multiple Wire Offsets" /> diff --git a/docs/learn/code/common/occt/shapes/compound/compounded-drawing.md b/docs/learn/code/common/occt/shapes/compound/compounded-drawing.md index 5c190126..719c2a2f 100644 --- a/docs/learn/code/common/occt/shapes/compound/compounded-drawing.md +++ b/docs/learn/code/common/occt/shapes/compound/compounded-drawing.md @@ -40,21 +40,21 @@ The example below demonstrates this principle through a complex workflow that cr bottomPointsmiddlePointstopPointsbottomWiremiddleWiretopWirewiresListloftedSurfacemainFacenrHexagonsUnrHexagonsVscalePatternhexWires1hexWires2reversedWiresiframesfirstFramesCompoundsecondFramesCompoundmaterial1material2bottomPoints[[-30, 0, -20], [-10, 0, 0], [0, 0, 10], [10, 0, 0],[30, 0, -20]]middlePoints[[-20, 20, -10], [-5, 20, 15], [0, 20, 20], [5, 20, 15],[20, 20, -10]]topPoints[[-15, 30, 0], [-5, 30, 5], [0, 30, 10], [5, 30, 5],[15, 30, 0]]bottomWirebottomPointsFALSE0.1middleWiremiddlePointsFALSE0.1topWiretopPointsFALSE0.1wiresListbottomWiremiddleWiretopWireloftedSurfacewiresListFALSEmainFaceloftedSurface0nrHexagonsU28nrHexagonsV10scalePattern0.7hexWires1mainFacenrHexagonsUnrHexagonsVFALSE00FALSEFALSEFALSEFALSEhexWires2mainFacenrHexagonsUnrHexagonsVFALSEscalePatternscalePattern00FALSEFALSEFALSEFALSEreversedWiresi1hexWires21INSERTLASTreversedWiresGETFROM_STARThexWires2iframesi1hexWires11INSERTLASTframesGETFROM_STARThexWires1iGETFROM_STARTreversedWiresiFALSEfirstFramesCompoundframes[true, true, false]secondFramesCompoundframes[false,false,true]material1First#9155ff#0000000.150.91FALSE3material2Second#000000#0000000.90.151FALSE3firstFramesCompound0.01material1TRUE#00000010secondFramesCompound0.01material2TRUE#00000010-100-100-1003#ffffff#ffffff1024TRUE0TRUE0.20.00010.00210000'default'10000.10.7TRUE442244015011791000100010001000340040010100.450.50.5FALSE#ffffff#ffffff#050506#627a9d'to top'0100","version":"0.20.13","type":"blockly"}} + script={{"script":"bottomPointsmiddlePointstopPointsbottomWiremiddleWiretopWirewiresListloftedSurfacemainFacenrHexagonsUnrHexagonsVscalePatternhexWires1hexWires2reversedWiresiframesfirstFramesCompoundsecondFramesCompoundmaterial1material2bottomPoints[[-30, 0, -20], [-10, 0, 0], [0, 0, 10], [10, 0, 0],[30, 0, -20]]middlePoints[[-20, 20, -10], [-5, 20, 15], [0, 20, 20], [5, 20, 15],[20, 20, -10]]topPoints[[-15, 30, 0], [-5, 30, 5], [0, 30, 10], [5, 30, 5],[15, 30, 0]]bottomWirebottomPointsFALSE0.1middleWiremiddlePointsFALSE0.1topWiretopPointsFALSE0.1wiresListbottomWiremiddleWiretopWireloftedSurfacewiresListFALSEmainFaceloftedSurface0nrHexagonsU28nrHexagonsV10scalePattern0.7hexWires1mainFacenrHexagonsUnrHexagonsVFALSE00FALSEFALSEFALSEFALSEhexWires2mainFacenrHexagonsUnrHexagonsVFALSEscalePatternscalePattern00FALSEFALSEFALSEFALSEreversedWiresi1hexWires21INSERTLASTreversedWiresGETFROM_STARThexWires2iframesi1hexWires11INSERTLASTframesGETFROM_STARThexWires1iGETFROM_STARTreversedWiresiFALSEfirstFramesCompoundframes[true, true, false]secondFramesCompoundframes[false,false,true]material1First#9155ff#0000000.150.91FALSE3material2Second#000000#0000000.90.151FALSE3firstFramesCompound0.01material1TRUE#00000010secondFramesCompound0.01material2TRUE#00000010-100-100-1003#ffffff#ffffff1024TRUE0TRUE0.20.00010.00210000'default'10000.10.7TRUE442244015011791000100010001000340040010100.450.50.5FALSE#ffffff#ffffff#050506#627a9d'to top'0100","version":"0.20.14","type":"blockly"}} title="Compounding hex frames into single entity will render faster" /> {\n // Define point sets for three different wire levels\n const bottomPoints: Point3[] = [\n [-30, 0, -20],\n [-10, 0, 0],\n [0, 0, 10],\n [10, 0, 0],\n [30, 0, -20]\n ];\n\n const middlePoints: Point3[] = [\n [-20, 20, -10],\n [-5, 20, 15],\n [0, 20, 20],\n [5, 20, 15],\n [20, 20, -10]\n ];\n\n const topPoints: Point3[] = [\n [-15, 30, 0],\n [-5, 30, 5],\n [0, 30, 10],\n [5, 30, 5],\n [15, 30, 0]\n ];\n\n // Create wires by interpolating points\n const bottomWireOptions = new InterpolationDto();\n bottomWireOptions.points = bottomPoints;\n bottomWireOptions.periodic = false;\n bottomWireOptions.tolerance = 0.1;\n const bottomWire = await wire.interpolatePoints(bottomWireOptions);\n\n const middleWireOptions = new InterpolationDto();\n middleWireOptions.points = middlePoints;\n middleWireOptions.periodic = false;\n middleWireOptions.tolerance = 0.1;\n const middleWire = await wire.interpolatePoints(middleWireOptions);\n\n const topWireOptions = new InterpolationDto();\n topWireOptions.points = topPoints;\n topWireOptions.periodic = false;\n topWireOptions.tolerance = 0.1;\n const topWire = await wire.interpolatePoints(topWireOptions);\n\n // Create list of wires for lofting\n const wiresList = [bottomWire, middleWire, topWire];\n\n // Loft the wires to create a surface\n const loftOptions = new LoftDto();\n loftOptions.shapes = wiresList;\n loftOptions.makeSolid = false;\n const loftedSurface = await operations.loft(loftOptions);\n\n // Get the face from the lofted surface\n const getFaceOptions = new ShapeIndexDto();\n getFaceOptions.shape = loftedSurface;\n getFaceOptions.index = 0;\n const mainFace = await face.getFace(getFaceOptions);\n\n // Subdivision parameters\n const nrHexagonsU = 28;\n const nrHexagonsV = 10;\n const scalePattern = [0.7];\n\n // Create first hexagon subdivision (regular pattern)\n const hexSubdivision1Options = new FaceSubdivideToHexagonWiresDto();\n hexSubdivision1Options.shape = mainFace;\n hexSubdivision1Options.nrHexagonsU = nrHexagonsU;\n hexSubdivision1Options.nrHexagonsV = nrHexagonsV;\n const hexWires1 = await face.subdivideToHexagonWires(hexSubdivision1Options);\n\n // Create second hexagon subdivision (scaled pattern)\n const hexSubdivision2Options = new FaceSubdivideToHexagonWiresDto();\n hexSubdivision2Options.shape = mainFace;\n hexSubdivision2Options.nrHexagonsU = nrHexagonsU;\n hexSubdivision2Options.nrHexagonsV = nrHexagonsV;\n hexSubdivision2Options.scalePatternU = scalePattern;\n hexSubdivision2Options.scalePatternV = scalePattern;\n const hexWires2 = await face.subdivideToHexagonWires(hexSubdivision2Options);\n\n // Reverse the wires from the second subdivision using for loop\n const reversedWiresPromises: Promise[] = [];\n for (const hexWire of hexWires2) {\n const reversedWire = wire.reversedWire({ shape: hexWire });\n reversedWiresPromises.push(reversedWire);\n }\n\n const reversedWires = await Promise.all(reversedWiresPromises);\n\n // Combine both wire sets - equivalent to flip lists operation in Rete\n const frameWiresGrouped = hexWires1.map((h, i) => [h, reversedWires[i]]);\n\n // Create frames\n const framePromises = frameWiresGrouped.map(f => {\n const faceFromWires2Options = new FaceFromWiresDto();\n faceFromWires2Options.shapes = f;\n faceFromWires2Options.planar = false;\n return face.createFaceFromWires(faceFromWires2Options);\n });\n\n const frames = await Promise.all(framePromises);\n\n const firstFrames = await bitbybit.lists.getByPattern({\n list: frames,\n pattern: [true, true, false]\n });\n\n const secondFrames = await bitbybit.lists.getByPattern({\n list: frames,\n pattern: [false, false, true]\n })\n\n // Create first compound from the first pattern of hexagon faces\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = firstFrames;\n const firstCompound = await compound.makeCompound(compoundOptions);\n\n // Create second compound from the first pattern of hexagon faces\n compoundOptions.shapes = secondFrames;\n const secondCompound = await compound.makeCompound(compoundOptions);\n\n // Create materials for rendering\n const firstMaterial = new PBRMetallicRoughnessDto();\n firstMaterial.name = \"Blue Material\";\n firstMaterial.baseColor = \"#9155ff\";\n firstMaterial.metallic = 0.1;\n firstMaterial.roughness = 0.9;\n firstMaterial.backFaceCulling = false;\n firstMaterial.zOffset = 3;\n const blueMatResult = material.pbrMetallicRoughness.create(firstMaterial);\n\n // Create drawing options for the first frames\n const firstDrawOptions = new DrawOcctShapeOptions();\n firstDrawOptions.drawEdges = true;\n firstDrawOptions.edgeColour = \"#000000\";\n firstDrawOptions.edgeWidth = 10;\n firstDrawOptions.faceMaterial = blueMatResult;\n\n bitbybit.draw.drawAnyAsync({\n entity: firstCompound,\n options: firstDrawOptions\n });\n\n // Create materials for rendering\n const secondMaterial = new PBRMetallicRoughnessDto();\n secondMaterial.name = \"Black Material\";\n secondMaterial.baseColor = \"#000000\";\n secondMaterial.metallic = 0.9;\n secondMaterial.roughness = 0.23;\n secondMaterial.backFaceCulling = false;\n secondMaterial.zOffset = 3;\n const secondMatResult = material.pbrMetallicRoughness.create(secondMaterial);\n\n // Create drawing options for the first frames\n const secondDrawOptions = new DrawOcctShapeOptions();\n secondDrawOptions.drawEdges = true;\n secondDrawOptions.edgeColour = \"#000000\";\n secondDrawOptions.edgeWidth = 10;\n secondDrawOptions.faceMaterial = secondMatResult;\n\n bitbybit.draw.drawAnyAsync({\n entity: secondCompound,\n options: secondDrawOptions\n });\n\n // Set up scene lighting and camera\n const skyboxOptions = new SkyboxDto();\n skyboxOptions.skybox = skyboxEnum.city;\n skyboxOptions.hideSkybox = true;\n scene.enableSkybox(skyboxOptions);\n\n const dirLightOptions = new DirectionalLightDto();\n dirLightOptions.intensity = 3;\n scene.drawDirectionalLight(dirLightOptions);\n\n const gradientBackgroundOptions = new SceneTwoColorLinearGradientDto();\n gradientBackgroundOptions.colorFrom = \"#050506\";\n gradientBackgroundOptions.colorTo = \"#627a9d\";\n gradientBackgroundOptions.direction = gradientDirectionEnum.toTop;\n scene.twoColorLinearGradient(gradientBackgroundOptions);\n\n const cameraConfigurationOptions = new CameraConfigurationDto();\n cameraConfigurationOptions.position = [44, 30, 44];\n cameraConfigurationOptions.lookAt = [0, 15, 0];\n scene.adjustActiveArcRotateCamera(cameraConfigurationOptions);\n\n const gridOptions = new SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for wire creation, lofting, face operations, and compounds\nconst { InterpolationDto, LoftDto, ShapeIndexDto, FaceSubdivideToHexagonWiresDto, FaceFromWiresDto, CompoundShapesDto } = Bit.Inputs.OCCT;\nconst { DrawOcctShapeOptions, SceneDrawGridMeshDto } = Bit.Inputs.Draw;\nconst { PBRMetallicRoughnessDto } = Bit.Inputs.BabylonMaterial;\nconst { CameraConfigurationDto, DirectionalLightDto, SceneTwoColorLinearGradientDto, SkyboxDto } = Bit.Inputs.BabylonScene;\nconst { skyboxEnum, gradientDirectionEnum } = Bit.Inputs.Base;\n\n\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSCompoundPointer = Bit.Inputs.OCCT.TopoDSCompoundPointer;\n// Get access to OCCT modules\nconst { wire, face, compound } = bitbybit.occt.shapes;\nconst { operations } = bitbybit.occt;\nconst { material } = bitbybit.babylon;\nconst { scene } = bitbybit.babylon;\n\n// Define the main function to create complex face compound\nconst start = async () => {\n // Define point sets for three different wire levels\n const bottomPoints: Point3[] = [\n [-30, 0, -20],\n [-10, 0, 0],\n [0, 0, 10],\n [10, 0, 0],\n [30, 0, -20]\n ];\n\n const middlePoints: Point3[] = [\n [-20, 20, -10],\n [-5, 20, 15],\n [0, 20, 20],\n [5, 20, 15],\n [20, 20, -10]\n ];\n\n const topPoints: Point3[] = [\n [-15, 30, 0],\n [-5, 30, 5],\n [0, 30, 10],\n [5, 30, 5],\n [15, 30, 0]\n ];\n\n // Create wires by interpolating points\n const bottomWireOptions = new InterpolationDto();\n bottomWireOptions.points = bottomPoints;\n bottomWireOptions.periodic = false;\n bottomWireOptions.tolerance = 0.1;\n const bottomWire = await wire.interpolatePoints(bottomWireOptions);\n\n const middleWireOptions = new InterpolationDto();\n middleWireOptions.points = middlePoints;\n middleWireOptions.periodic = false;\n middleWireOptions.tolerance = 0.1;\n const middleWire = await wire.interpolatePoints(middleWireOptions);\n\n const topWireOptions = new InterpolationDto();\n topWireOptions.points = topPoints;\n topWireOptions.periodic = false;\n topWireOptions.tolerance = 0.1;\n const topWire = await wire.interpolatePoints(topWireOptions);\n\n // Create list of wires for lofting\n const wiresList = [bottomWire, middleWire, topWire];\n\n // Loft the wires to create a surface\n const loftOptions = new LoftDto();\n loftOptions.shapes = wiresList;\n loftOptions.makeSolid = false;\n const loftedSurface = await operations.loft(loftOptions);\n\n // Get the face from the lofted surface\n const getFaceOptions = new ShapeIndexDto();\n getFaceOptions.shape = loftedSurface;\n getFaceOptions.index = 0;\n const mainFace = await face.getFace(getFaceOptions);\n\n // Subdivision parameters\n const nrHexagonsU = 28;\n const nrHexagonsV = 10;\n const scalePattern = [0.7];\n\n // Create first hexagon subdivision (regular pattern)\n const hexSubdivision1Options = new FaceSubdivideToHexagonWiresDto();\n hexSubdivision1Options.shape = mainFace;\n hexSubdivision1Options.nrHexagonsU = nrHexagonsU;\n hexSubdivision1Options.nrHexagonsV = nrHexagonsV;\n const hexWires1 = await face.subdivideToHexagonWires(hexSubdivision1Options);\n\n // Create second hexagon subdivision (scaled pattern)\n const hexSubdivision2Options = new FaceSubdivideToHexagonWiresDto();\n hexSubdivision2Options.shape = mainFace;\n hexSubdivision2Options.nrHexagonsU = nrHexagonsU;\n hexSubdivision2Options.nrHexagonsV = nrHexagonsV;\n hexSubdivision2Options.scalePatternU = scalePattern;\n hexSubdivision2Options.scalePatternV = scalePattern;\n const hexWires2 = await face.subdivideToHexagonWires(hexSubdivision2Options);\n\n // Reverse the wires from the second subdivision using for loop\n const reversedWiresPromises: Promise[] = [];\n for (const hexWire of hexWires2) {\n const reversedWire = wire.reversedWire({ shape: hexWire });\n reversedWiresPromises.push(reversedWire);\n }\n\n const reversedWires = await Promise.all(reversedWiresPromises);\n\n // Combine both wire sets - equivalent to flip lists operation in Rete\n const frameWiresGrouped = hexWires1.map((h, i) => [h, reversedWires[i]]);\n\n // Create frames\n const framePromises = frameWiresGrouped.map(f => {\n const faceFromWires2Options = new FaceFromWiresDto();\n faceFromWires2Options.shapes = f;\n faceFromWires2Options.planar = false;\n return face.createFaceFromWires(faceFromWires2Options);\n });\n\n const frames = await Promise.all(framePromises);\n\n const firstFrames = await bitbybit.lists.getByPattern({\n list: frames,\n pattern: [true, true, false]\n });\n\n const secondFrames = await bitbybit.lists.getByPattern({\n list: frames,\n pattern: [false, false, true]\n })\n\n // Create first compound from the first pattern of hexagon faces\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = firstFrames;\n const firstCompound = await compound.makeCompound(compoundOptions);\n\n // Create second compound from the first pattern of hexagon faces\n compoundOptions.shapes = secondFrames;\n const secondCompound = await compound.makeCompound(compoundOptions);\n\n // Create materials for rendering\n const firstMaterial = new PBRMetallicRoughnessDto();\n firstMaterial.name = \"Blue Material\";\n firstMaterial.baseColor = \"#9155ff\";\n firstMaterial.metallic = 0.1;\n firstMaterial.roughness = 0.9;\n firstMaterial.backFaceCulling = false;\n firstMaterial.zOffset = 3;\n const blueMatResult = material.pbrMetallicRoughness.create(firstMaterial);\n\n // Create drawing options for the first frames\n const firstDrawOptions = new DrawOcctShapeOptions();\n firstDrawOptions.drawEdges = true;\n firstDrawOptions.edgeColour = \"#000000\";\n firstDrawOptions.edgeWidth = 10;\n firstDrawOptions.faceMaterial = blueMatResult;\n\n bitbybit.draw.drawAnyAsync({\n entity: firstCompound,\n options: firstDrawOptions\n });\n\n // Create materials for rendering\n const secondMaterial = new PBRMetallicRoughnessDto();\n secondMaterial.name = \"Black Material\";\n secondMaterial.baseColor = \"#000000\";\n secondMaterial.metallic = 0.9;\n secondMaterial.roughness = 0.23;\n secondMaterial.backFaceCulling = false;\n secondMaterial.zOffset = 3;\n const secondMatResult = material.pbrMetallicRoughness.create(secondMaterial);\n\n // Create drawing options for the first frames\n const secondDrawOptions = new DrawOcctShapeOptions();\n secondDrawOptions.drawEdges = true;\n secondDrawOptions.edgeColour = \"#000000\";\n secondDrawOptions.edgeWidth = 10;\n secondDrawOptions.faceMaterial = secondMatResult;\n\n bitbybit.draw.drawAnyAsync({\n entity: secondCompound,\n options: secondDrawOptions\n });\n\n // Set up scene lighting and camera\n const skyboxOptions = new SkyboxDto();\n skyboxOptions.skybox = skyboxEnum.city;\n skyboxOptions.hideSkybox = true;\n scene.enableSkybox(skyboxOptions);\n\n const dirLightOptions = new DirectionalLightDto();\n dirLightOptions.intensity = 3;\n scene.drawDirectionalLight(dirLightOptions);\n\n const gradientBackgroundOptions = new SceneTwoColorLinearGradientDto();\n gradientBackgroundOptions.colorFrom = \"#050506\";\n gradientBackgroundOptions.colorTo = \"#627a9d\";\n gradientBackgroundOptions.direction = gradientDirectionEnum.toTop;\n scene.twoColorLinearGradient(gradientBackgroundOptions);\n\n const cameraConfigurationOptions = new CameraConfigurationDto();\n cameraConfigurationOptions.position = [44, 30, 44];\n cameraConfigurationOptions.lookAt = [0, 15, 0];\n scene.adjustActiveArcRotateCamera(cameraConfigurationOptions);\n\n const gridOptions = new SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Compounding hex frames into single entity will render faster" /> diff --git a/docs/learn/code/common/occt/shapes/compound/intro.md b/docs/learn/code/common/occt/shapes/compound/intro.md index 5335f011..0bf3384f 100644 --- a/docs/learn/code/common/occt/shapes/compound/intro.md +++ b/docs/learn/code/common/occt/shapes/compound/intro.md @@ -48,21 +48,21 @@ If you're not experiencing noticeable performance issues in the first example, t gridSizesphereRadiusspacinghalfGridxPositionsyPositionszPositionsxyzgridSize3sphereRadius1spacing2.5halfGridDIVIDEMULTIPLYgridSizespacing2xPositionsspacingNEGhalfGridhalfGridyPositionsspacingNEGhalfGridhalfGridzPositionsspacingNEGhalfGridhalfGridx1xPositions1y1yPositions1z1zPositions1GETFROM_STARTxPositionsxGETFROM_STARTyPositionsyGETFROM_STARTzPositionsz","version":"0.20.13","type":"blockly"}} + script={{"script":"gridSizesphereRadiusspacinghalfGridxPositionsyPositionszPositionsxyzgridSize3sphereRadius1spacing2.5halfGridDIVIDEMULTIPLYgridSizespacing2xPositionsspacingNEGhalfGridhalfGridyPositionsspacingNEGhalfGridhalfGridzPositionsspacingNEGhalfGridhalfGridx1xPositions1y1yPositions1z1zPositions1GETFROM_STARTxPositionsxGETFROM_STARTyPositionsyGETFROM_STARTzPositionsz","version":"0.20.14","type":"blockly"}} title="Creating individual spheres leads to performance issues" /> {\n // Grid parameters\n const gridSize = 3; // Number of spheres per side\n const sphereRadius = 1; // Radius of each sphere\n const spacing = 2.5; // Distance between sphere centers\n \n // Calculate grid boundaries\n const halfGrid = (gridSize * spacing) / 2;\n \n // Generate grid positions using span functions for 3D grid\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n \n const yPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n \n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n \n // Create individual spheres using nested loops for 3D grid\n // This approach creates each sphere separately, leading to performance issues\n for (const xPos of xPositions) {\n for (const yPos of yPositions) {\n for (const zPos of zPositions) {\n // Define sphere creation options\n const sphereOptions = new SphereDto();\n sphereOptions.radius = sphereRadius;\n sphereOptions.center = [xPos, yPos, zPos];\n \n // Create and immediately draw each sphere individually\n // This is inefficient as each sphere requires its own mesh and draw call\n const sphere = await solid.createSphere(sphereOptions);\n \n // Each draw call is separate, creating performance overhead\n bitbybit.draw.drawAnyAsync({\n entity: sphere\n });\n }\n }\n }\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating spheres\nconst { SphereDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\n\n// Get access to OCCT modules and utility functions\nconst { solid } = bitbybit.occt.shapes;\nconst { vector } = bitbybit;\n\n// Define the main function to create individual spheres (performance problem)\nconst start = async () => {\n // Grid parameters\n const gridSize = 3; // Number of spheres per side\n const sphereRadius = 1; // Radius of each sphere\n const spacing = 2.5; // Distance between sphere centers\n \n // Calculate grid boundaries\n const halfGrid = (gridSize * spacing) / 2;\n \n // Generate grid positions using span functions for 3D grid\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n \n const yPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n \n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n \n // Create individual spheres using nested loops for 3D grid\n // This approach creates each sphere separately, leading to performance issues\n for (const xPos of xPositions) {\n for (const yPos of yPositions) {\n for (const zPos of zPositions) {\n // Define sphere creation options\n const sphereOptions = new SphereDto();\n sphereOptions.radius = sphereRadius;\n sphereOptions.center = [xPos, yPos, zPos];\n \n // Create and immediately draw each sphere individually\n // This is inefficient as each sphere requires its own mesh and draw call\n const sphere = await solid.createSphere(sphereOptions);\n \n // Each draw call is separate, creating performance overhead\n bitbybit.draw.drawAnyAsync({\n entity: sphere\n });\n }\n }\n }\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating individual spheres leads to performance issues" /> @@ -76,21 +76,21 @@ The solution to this performance problem is to use compounds. Instead of creatin gridSizesphereRadiusspacinghalfGridxPositionsyPositionszPositionsspheresxycompoundzgridSize3sphereRadius1spacing2.5halfGridDIVIDEMULTIPLYgridSizespacing2xPositionsspacingNEGhalfGridhalfGridyPositionsspacingNEGhalfGridhalfGridzPositionsspacingNEGhalfGridhalfGridspheresx1xPositions1y1yPositions1z1zPositions1INSERTLASTspheressphereRadiusGETFROM_STARTxPositionsxGETFROM_STARTyPositionsyGETFROM_STARTzPositionszcompoundspherescompound","version":"0.20.13","type":"blockly"}} + script={{"script":"gridSizesphereRadiusspacinghalfGridxPositionsyPositionszPositionsspheresxycompoundzgridSize3sphereRadius1spacing2.5halfGridDIVIDEMULTIPLYgridSizespacing2xPositionsspacingNEGhalfGridhalfGridyPositionsspacingNEGhalfGridhalfGridzPositionsspacingNEGhalfGridhalfGridspheresx1xPositions1y1yPositions1z1zPositions1INSERTLASTspheressphereRadiusGETFROM_STARTxPositionsxGETFROM_STARTyPositionsyGETFROM_STARTzPositionszcompoundspherescompound","version":"0.20.14","type":"blockly"}} title="Compounded spheres provide significantly better performance" /> {\n // Grid parameters\n const gridSize = 5; // Number of spheres per side\n const sphereRadius = 1; // Radius of each sphere\n const spacing = 2.5; // Distance between sphere centers\n\n // Calculate grid boundaries\n const halfGrid = (gridSize * spacing) / 2;\n\n // Generate grid positions using span functions\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const yPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n // Create array to store all spheres before compounding\n const spheres: TopoDSSolidPointer[] = [];\n\n // Create all spheres first and store them in the array\n for (const xPos of xPositions) {\n for (const yPos of yPositions) {\n for (const zPos of zPositions) {\n // Define sphere creation options\n const sphereOptions = new SphereDto();\n sphereOptions.radius = sphereRadius;\n sphereOptions.center = [xPos, yPos, zPos];\n\n // Create sphere and add to array\n const sphere = await solid.createSphere(sphereOptions);\n spheres.push(sphere);\n }\n }\n }\n\n // Create compound from all spheres\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = spheres;\n const sphereCompound = await compound.makeCompound(compoundOptions);\n\n // Draw compound\n bitbybit.draw.drawAnyAsync({\n entity: sphereCompound\n });\n\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating spheres, compounds, and transformations\nconst { SphereDto, CompoundShapesDto, RotateDto, TranslateDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;\ntype TopoDSCompoundPointer = Bit.Inputs.OCCT.TopoDSCompoundPointer;\n\n// Get access to OCCT modules and utility functions\nconst { solid, compound } = bitbybit.occt.shapes;\nconst { transforms } = bitbybit.occt;\nconst { vector } = bitbybit;\n\n// Define the main function to create a compound\nconst start = async () => {\n // Grid parameters\n const gridSize = 5; // Number of spheres per side\n const sphereRadius = 1; // Radius of each sphere\n const spacing = 2.5; // Distance between sphere centers\n\n // Calculate grid boundaries\n const halfGrid = (gridSize * spacing) / 2;\n\n // Generate grid positions using span functions\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const yPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n // Create array to store all spheres before compounding\n const spheres: TopoDSSolidPointer[] = [];\n\n // Create all spheres first and store them in the array\n for (const xPos of xPositions) {\n for (const yPos of yPositions) {\n for (const zPos of zPositions) {\n // Define sphere creation options\n const sphereOptions = new SphereDto();\n sphereOptions.radius = sphereRadius;\n sphereOptions.center = [xPos, yPos, zPos];\n\n // Create sphere and add to array\n const sphere = await solid.createSphere(sphereOptions);\n spheres.push(sphere);\n }\n }\n }\n\n // Create compound from all spheres\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = spheres;\n const sphereCompound = await compound.makeCompound(compoundOptions);\n\n // Draw compound\n bitbybit.draw.drawAnyAsync({\n entity: sphereCompound\n });\n\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Compounded spheres provide significantly better performance" /> @@ -105,21 +105,21 @@ Another major advantage of compounds is the ability to transform the entire grou gridSizesphereRadiusspacinghalfGridxPositionsyPositionszPositionsspheresxycompoundzrotatedCompoundgridSize5sphereRadius1spacing2.5halfGridDIVIDEMULTIPLYgridSizespacing2xPositionsspacingNEGhalfGridhalfGridyPositionsspacingNEGhalfGridhalfGridzPositionsspacingNEGhalfGridhalfGridspheresx1xPositions1y1yPositions1z1zPositions1INSERTLASTspheressphereRadiusGETFROM_STARTxPositionsxGETFROM_STARTyPositionsyGETFROM_STARTzPositionszcompoundspheresrotatedCompoundcompound00145rotatedCompound","version":"0.20.13","type":"blockly"}} + script={{"script":"gridSizesphereRadiusspacinghalfGridxPositionsyPositionszPositionsspheresxycompoundzrotatedCompoundgridSize5sphereRadius1spacing2.5halfGridDIVIDEMULTIPLYgridSizespacing2xPositionsspacingNEGhalfGridhalfGridyPositionsspacingNEGhalfGridhalfGridzPositionsspacingNEGhalfGridhalfGridspheresx1xPositions1y1yPositions1z1zPositions1INSERTLASTspheressphereRadiusGETFROM_STARTxPositionsxGETFROM_STARTyPositionsyGETFROM_STARTzPositionszcompoundspheresrotatedCompoundcompound00145rotatedCompound","version":"0.20.14","type":"blockly"}} title="Compound can be rotated and translated as a single grouped entity" /> {\n // Grid parameters\n const gridSize = 5; // Number of spheres per side\n const sphereRadius = 1; // Radius of each sphere\n const spacing = 2.5; // Distance between sphere centers\n\n // Calculate grid boundaries\n const halfGrid = (gridSize * spacing) / 2;\n\n // Generate grid positions using span functions\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const yPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n // Create array to store all spheres before compounding\n const spheres: TopoDSSolidPointer[] = [];\n\n // Create all spheres first and store them in the array\n for (const xPos of xPositions) {\n for (const yPos of yPositions) {\n for (const zPos of zPositions) {\n // Define sphere creation options\n const sphereOptions = new SphereDto();\n sphereOptions.radius = sphereRadius;\n sphereOptions.center = [xPos, yPos, zPos];\n\n // Create sphere and add to array\n const sphere = await solid.createSphere(sphereOptions);\n spheres.push(sphere);\n }\n }\n }\n\n // Create compound from all spheres\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = spheres;\n const sphereCompound = await compound.makeCompound(compoundOptions);\n\n // Transform the entire compound as a single entity\n // First, rotate the compound around the Z-axis\n const rotateOptions = new RotateDto();\n rotateOptions.shape = sphereCompound;\n rotateOptions.axis = [0, 0, 1]; // Z-axis rotation\n rotateOptions.angle = 45; // 45 degrees\n const rotatedCompound = await transforms.rotate(rotateOptions);\n\n // Draw the transformed compound\n // All spheres move together as a single unified entity\n bitbybit.draw.drawAnyAsync({\n entity: rotatedCompound\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating spheres, compounds, and transformations\nconst { SphereDto, CompoundShapesDto, RotateDto, TranslateDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSSolidPointer = Bit.Inputs.OCCT.TopoDSSolidPointer;\ntype TopoDSCompoundPointer = Bit.Inputs.OCCT.TopoDSCompoundPointer;\n\n// Get access to OCCT modules and utility functions\nconst { solid, compound } = bitbybit.occt.shapes;\nconst { transforms } = bitbybit.occt;\nconst { vector } = bitbybit;\n\n// Define the main function to create and transform a compound (unified transformation)\nconst start = async () => {\n // Grid parameters\n const gridSize = 5; // Number of spheres per side\n const sphereRadius = 1; // Radius of each sphere\n const spacing = 2.5; // Distance between sphere centers\n\n // Calculate grid boundaries\n const halfGrid = (gridSize * spacing) / 2;\n\n // Generate grid positions using span functions\n const xPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const yPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n const zPositions = vector.span({\n min: -halfGrid,\n max: halfGrid,\n step: spacing\n });\n\n // Create array to store all spheres before compounding\n const spheres: TopoDSSolidPointer[] = [];\n\n // Create all spheres first and store them in the array\n for (const xPos of xPositions) {\n for (const yPos of yPositions) {\n for (const zPos of zPositions) {\n // Define sphere creation options\n const sphereOptions = new SphereDto();\n sphereOptions.radius = sphereRadius;\n sphereOptions.center = [xPos, yPos, zPos];\n\n // Create sphere and add to array\n const sphere = await solid.createSphere(sphereOptions);\n spheres.push(sphere);\n }\n }\n }\n\n // Create compound from all spheres\n const compoundOptions = new CompoundShapesDto();\n compoundOptions.shapes = spheres;\n const sphereCompound = await compound.makeCompound(compoundOptions);\n\n // Transform the entire compound as a single entity\n // First, rotate the compound around the Z-axis\n const rotateOptions = new RotateDto();\n rotateOptions.shape = sphereCompound;\n rotateOptions.axis = [0, 0, 1]; // Z-axis rotation\n rotateOptions.angle = 45; // 45 degrees\n const rotatedCompound = await transforms.rotate(rotateOptions);\n\n // Draw the transformed compound\n // All spheres move together as a single unified entity\n bitbybit.draw.drawAnyAsync({\n entity: rotatedCompound\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Compound can be rotated and translated as a single grouped entity" /> diff --git a/docs/learn/code/common/occt/shapes/edge/edge-constraints.md b/docs/learn/code/common/occt/shapes/edge/edge-constraints.md index e0fb6157..1a47f2a3 100644 --- a/docs/learn/code/common/occt/shapes/edge/edge-constraints.md +++ b/docs/learn/code/common/occt/shapes/edge/edge-constraints.md @@ -46,21 +46,21 @@ This is particularly useful in mechanical design when you need to connect two sp circlepoint1point2tangentLinescircle4000010point1007point2909tangentLinescirclepoint1point21e-7'all''none'tangentLinescirclepoint1point2","version":"0.20.13","type":"blockly"}} + script={{"script":"circlepoint1point2tangentLinescircle4000010point1007point2909tangentLinescirclepoint1point21e-7'all''none'tangentLinescirclepoint1point2","version":"0.20.14","type":"blockly"}} title="Constraint tangent lines from two points to circle" /> {\n // Create a circle edge\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 circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Define two points\n const point1: Point3 = [0, 0, 7];\n const point2: Point3 = [9, 0, 9];\n\n // Create constraint options for tangent lines from two points to circle\n const constraintOptions = new ConstraintTanLinesFromTwoPtsToCircleDto();\n constraintOptions.circle = circle;\n constraintOptions.point1 = point1;\n constraintOptions.point2 = point2;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.positionResult = positionResultEnum.all;\n constraintOptions.circleRemainder = circleInclusionEnum.none;\n\n // Create the constraint tangent lines\n const tangentLines = await bitbybit.occt.shapes.edge.constraintTanLinesFromTwoPtsToCircle(constraintOptions);\n\n // Draw the tangent lines\n bitbybit.draw.drawAnyAsync({ entity: tangentLines });\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n // Draw the two points\n bitbybit.draw.drawAnyAsync({ entity: [point1] });\n bitbybit.draw.drawAnyAsync({ entity: [point2] });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ConstraintTanLinesFromTwoPtsToCircleDto, positionResultEnum, circleInclusionEnum } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Define the main function\nconst start = async () => {\n // Create a circle edge\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 circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Define two points\n const point1: Point3 = [0, 0, 7];\n const point2: Point3 = [9, 0, 9];\n\n // Create constraint options for tangent lines from two points to circle\n const constraintOptions = new ConstraintTanLinesFromTwoPtsToCircleDto();\n constraintOptions.circle = circle;\n constraintOptions.point1 = point1;\n constraintOptions.point2 = point2;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.positionResult = positionResultEnum.all;\n constraintOptions.circleRemainder = circleInclusionEnum.none;\n\n // Create the constraint tangent lines\n const tangentLines = await bitbybit.occt.shapes.edge.constraintTanLinesFromTwoPtsToCircle(constraintOptions);\n\n // Draw the tangent lines\n bitbybit.draw.drawAnyAsync({ entity: tangentLines });\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n // Draw the two points\n bitbybit.draw.drawAnyAsync({ entity: [point1] });\n bitbybit.draw.drawAnyAsync({ entity: [point2] });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Constraint tangent lines from two points to circle" /> @@ -77,21 +77,21 @@ This operation is commonly used in architectural drawings when you need to creat circlepointtangentLinescircle4000010point807tangentLinescirclepoint1e-7'all''none'tangentLinescirclepoint","version":"0.20.13","type":"blockly"}} + script={{"script":"circlepointtangentLinescircle4000010point807tangentLinescirclepoint1e-7'all''none'tangentLinescirclepoint","version":"0.20.14","type":"blockly"}} title="Constraint tangent lines from point to circle" /> {\n // Create a circle edge\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 circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Define a point\n const point: Point3 = [8, 0, 7];\n\n // Create constraint options for tangent lines from point to circle\n const constraintOptions = new ConstraintTanLinesFromPtToCircleDto();\n constraintOptions.circle = circle;\n constraintOptions.point = point;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.positionResult = positionResultEnum.all;\n constraintOptions.circleRemainder = circleInclusionEnum.none;\n\n // Create the constraint tangent lines\n const tangentLines = await bitbybit.occt.shapes.edge.constraintTanLinesFromPtToCircle(constraintOptions);\n\n // Draw the tangent lines\n bitbybit.draw.drawAnyAsync({ entity: tangentLines });\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n // Draw the point\n bitbybit.draw.drawAnyAsync({ entity: point });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ConstraintTanLinesFromPtToCircleDto, positionResultEnum, circleInclusionEnum } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Define the main function\nconst start = async () => {\n // Create a circle edge\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 circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Define a point\n const point: Point3 = [8, 0, 7];\n\n // Create constraint options for tangent lines from point to circle\n const constraintOptions = new ConstraintTanLinesFromPtToCircleDto();\n constraintOptions.circle = circle;\n constraintOptions.point = point;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.positionResult = positionResultEnum.all;\n constraintOptions.circleRemainder = circleInclusionEnum.none;\n\n // Create the constraint tangent lines\n const tangentLines = await bitbybit.occt.shapes.edge.constraintTanLinesFromPtToCircle(constraintOptions);\n\n // Draw the tangent lines\n bitbybit.draw.drawAnyAsync({ entity: tangentLines });\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n // Draw the point\n bitbybit.draw.drawAnyAsync({ entity: point });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Constraint tangent lines from point to circle" /> @@ -107,21 +107,21 @@ This is especially valuable in gear design, pulley systems, or any mechanical ap circle1circle2tangentLinescircle13000010circle22700010tangentLinescircle1circle21e-7'all''none'tangentLinescircle1circle2","version":"0.20.13","type":"blockly"}} + script={{"script":"circle1circle2tangentLinescircle13000010circle22700010tangentLinescircle1circle21e-7'all''none'tangentLinescircle1circle2","version":"0.20.14","type":"blockly"}} title="Constraint tangent lines on two circles" /> {\n // Create first circle edge\n const circle1Options = new CircleDto();\n circle1Options.radius = 3;\n circle1Options.center = [0, 0, 0] as Point3;\n circle1Options.direction = [0, 1, 0] as Vector3;\n\n const circle1 = await bitbybit.occt.shapes.edge.createCircleEdge(circle1Options);\n\n // Create second circle edge\n const circle2Options = new CircleDto();\n circle2Options.radius = 2;\n circle2Options.center = [7, 0, 0] as Point3;\n circle2Options.direction = [0, 1, 0] as Vector3;\n\n const circle2 = await bitbybit.occt.shapes.edge.createCircleEdge(circle2Options);\n\n // Create constraint options for tangent lines between two circles\n const constraintOptions = new ConstraintTanLinesOnTwoCirclesDto();\n constraintOptions.circle1 = circle1;\n constraintOptions.circle2 = circle2;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.positionResult = positionResultEnum.all;\n constraintOptions.circleRemainders = twoCircleInclusionEnum.none;\n\n // Create the constraint tangent lines\n const tangentLines = await bitbybit.occt.shapes.edge.constraintTanLinesOnTwoCircles(constraintOptions);\n\n // Draw the tangent lines\n bitbybit.draw.drawAnyAsync({ entity: tangentLines });\n // Draw the first circle\n bitbybit.draw.drawAnyAsync({ entity: circle1 });\n // Draw the second circle\n bitbybit.draw.drawAnyAsync({ entity: circle2 });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ConstraintTanLinesOnTwoCirclesDto, positionResultEnum, twoCircleInclusionEnum } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Define the main function\nconst start = async () => {\n // Create first circle edge\n const circle1Options = new CircleDto();\n circle1Options.radius = 3;\n circle1Options.center = [0, 0, 0] as Point3;\n circle1Options.direction = [0, 1, 0] as Vector3;\n\n const circle1 = await bitbybit.occt.shapes.edge.createCircleEdge(circle1Options);\n\n // Create second circle edge\n const circle2Options = new CircleDto();\n circle2Options.radius = 2;\n circle2Options.center = [7, 0, 0] as Point3;\n circle2Options.direction = [0, 1, 0] as Vector3;\n\n const circle2 = await bitbybit.occt.shapes.edge.createCircleEdge(circle2Options);\n\n // Create constraint options for tangent lines between two circles\n const constraintOptions = new ConstraintTanLinesOnTwoCirclesDto();\n constraintOptions.circle1 = circle1;\n constraintOptions.circle2 = circle2;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.positionResult = positionResultEnum.all;\n constraintOptions.circleRemainders = twoCircleInclusionEnum.none;\n\n // Create the constraint tangent lines\n const tangentLines = await bitbybit.occt.shapes.edge.constraintTanLinesOnTwoCircles(constraintOptions);\n\n // Draw the tangent lines\n bitbybit.draw.drawAnyAsync({ entity: tangentLines });\n // Draw the first circle\n bitbybit.draw.drawAnyAsync({ entity: circle1 });\n // Draw the second circle\n bitbybit.draw.drawAnyAsync({ entity: circle2 });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Constraint tangent lines on two circles" /> @@ -138,21 +138,21 @@ This is particularly useful for creating buffer zones around existing circular f circle1circle2tangentCirclescircle13000010circle21.5500010tangentCirclescircle1circle21e-70.8tangentCirclescircle1circle2","version":"0.20.13","type":"blockly"}} + script={{"script":"circle1circle2tangentCirclescircle13000010circle21.5500010tangentCirclescircle1circle21e-70.8tangentCirclescircle1circle2","version":"0.20.14","type":"blockly"}} title="Constraint tangent circles on two circles" /> {\n // Create first circle edge\n const circle1Options = new CircleDto();\n circle1Options.radius = 3;\n circle1Options.center = [0, 0, 0] as Point3;\n circle1Options.direction = [0, 1, 0] as Vector3;\n\n const circle1 = await bitbybit.occt.shapes.edge.createCircleEdge(circle1Options);\n\n // Create second circle edge\n const circle2Options = new CircleDto();\n circle2Options.radius = 1.5;\n circle2Options.center = [5, 0, 0] as Point3;\n circle2Options.direction = [0, 1, 0] as Vector3;\n\n const circle2 = await bitbybit.occt.shapes.edge.createCircleEdge(circle2Options);\n\n // Create constraint options for tangent circles between two circles\n const constraintOptions = new ConstraintTanCirclesOnTwoCirclesDto();\n constraintOptions.circle1 = circle1;\n constraintOptions.circle2 = circle2;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.radius = 0.8;\n\n // Create the constraint tangent circles\n const tangentCircles = await bitbybit.occt.shapes.edge.constraintTanCirclesOnTwoCircles(constraintOptions);\n\n // Draw the tangent circles\n bitbybit.draw.drawAnyAsync({ entity: tangentCircles });\n // Draw the first circle\n bitbybit.draw.drawAnyAsync({ entity: circle1 });\n // Draw the second circle\n bitbybit.draw.drawAnyAsync({ entity: circle2 });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ConstraintTanCirclesOnTwoCirclesDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Define the main function\nconst start = async () => {\n // Create first circle edge\n const circle1Options = new CircleDto();\n circle1Options.radius = 3;\n circle1Options.center = [0, 0, 0] as Point3;\n circle1Options.direction = [0, 1, 0] as Vector3;\n\n const circle1 = await bitbybit.occt.shapes.edge.createCircleEdge(circle1Options);\n\n // Create second circle edge\n const circle2Options = new CircleDto();\n circle2Options.radius = 1.5;\n circle2Options.center = [5, 0, 0] as Point3;\n circle2Options.direction = [0, 1, 0] as Vector3;\n\n const circle2 = await bitbybit.occt.shapes.edge.createCircleEdge(circle2Options);\n\n // Create constraint options for tangent circles between two circles\n const constraintOptions = new ConstraintTanCirclesOnTwoCirclesDto();\n constraintOptions.circle1 = circle1;\n constraintOptions.circle2 = circle2;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.radius = 0.8;\n\n // Create the constraint tangent circles\n const tangentCircles = await bitbybit.occt.shapes.edge.constraintTanCirclesOnTwoCircles(constraintOptions);\n\n // Draw the tangent circles\n bitbybit.draw.drawAnyAsync({ entity: tangentCircles });\n // Draw the first circle\n bitbybit.draw.drawAnyAsync({ entity: circle1 });\n // Draw the second circle\n bitbybit.draw.drawAnyAsync({ entity: circle2 });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Constraint tangent circles on two circles" /> @@ -169,21 +169,21 @@ This constraint is perfect for creating rounded transitions in designs where you circlepointtangentCirclescircle3000010point400tangentCirclescirclepoint1e-72tangentCirclescirclepoint","version":"0.20.13","type":"blockly"}} + script={{"script":"circlepointtangentCirclescircle3000010point400tangentCirclescirclepoint1e-72tangentCirclescirclepoint","version":"0.20.14","type":"blockly"}} title="Constraint tangent circles on circle and point" /> {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 3;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Define a point\n const point: Point3 = [4, 0, 0];\n\n // Create constraint options for tangent circles between circle and point\n const constraintOptions = new ConstraintTanCirclesOnCircleAndPntDto();\n constraintOptions.circle = circle;\n constraintOptions.point = point;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.radius = 2;\n\n // Create the constraint tangent circles\n const tangentCircles = await bitbybit.occt.shapes.edge.constraintTanCirclesOnCircleAndPnt(constraintOptions);\n\n // Draw the tangent circles\n bitbybit.draw.drawAnyAsync({ entity: tangentCircles });\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n // Draw the point\n bitbybit.draw.drawAnyAsync({ entity: point });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ConstraintTanCirclesOnCircleAndPntDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Define the main function\nconst start = async () => {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 3;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Define a point\n const point: Point3 = [4, 0, 0];\n\n // Create constraint options for tangent circles between circle and point\n const constraintOptions = new ConstraintTanCirclesOnCircleAndPntDto();\n constraintOptions.circle = circle;\n constraintOptions.point = point;\n constraintOptions.tolerance = 1e-7;\n constraintOptions.radius = 2;\n\n // Create the constraint tangent circles\n const tangentCircles = await bitbybit.occt.shapes.edge.constraintTanCirclesOnCircleAndPnt(constraintOptions);\n\n // Draw the tangent circles\n bitbybit.draw.drawAnyAsync({ entity: tangentCircles });\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n // Draw the point\n bitbybit.draw.drawAnyAsync({ entity: point });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Constraint tangent circles on circle and point" /> diff --git a/docs/learn/code/common/occt/shapes/edge/edge-indexes.mdx b/docs/learn/code/common/occt/shapes/edge/edge-indexes.mdx index 6d0485a0..02286289 100644 --- a/docs/learn/code/common/occt/shapes/edge/edge-indexes.mdx +++ b/docs/learn/code/common/occt/shapes/edge/edge-indexes.mdx @@ -42,21 +42,21 @@ Below are examples in TypeScript, Blockly, and Rete that demonstrate creating a **TypeScript Example: Drawing Edge Indexes** {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const drawOpt = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOpt.drawEdgeIndexes = true;\n drawOpt.faceOpacity = 0.3;\n drawOpt.edgeOpacity = 0.3;\n drawOpt.edgeIndexHeight = 0.24\n\n bitbybit.draw.drawAnyAsync({\n entity: box,\n options: drawOpt\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const boxOpt = new Bit.Inputs.OCCT.BoxDto();\n boxOpt.width = 5;\n boxOpt.length = 8;\n boxOpt.height = 10;\n const box = await bitbybit.occt.shapes.solid.createBox(boxOpt);\n\n const drawOpt = new Bit.Inputs.Draw.DrawOcctShapeOptions();\n drawOpt.drawEdgeIndexes = true;\n drawOpt.faceOpacity = 0.3;\n drawOpt.edgeOpacity = 0.3;\n drawOpt.edgeIndexHeight = 0.24\n\n bitbybit.draw.drawAnyAsync({\n entity: box,\n options: drawOpt\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Edge Indexing" /> **Blockly Example: Drawing Edge Indexes** 58100000.30.3#ffffff#ff00002TRUETRUE0.01TRUE0.24#ff00ffFALSE0.06#0000ff","version":"0.20.13","type":"blockly"}} + script={{"script":"58100000.30.3#ffffff#ff00002TRUETRUE0.01TRUE0.24#ff00ffFALSE0.06#0000ff","version":"0.20.14","type":"blockly"}} title="Edge Indexing" /> **Rete Example: Drawing Edge Indexes** diff --git a/docs/learn/code/common/occt/shapes/edge/edge-primitives.md b/docs/learn/code/common/occt/shapes/edge/edge-primitives.md index 89dd307e..2f79e403 100644 --- a/docs/learn/code/common/occt/shapes/edge/edge-primitives.md +++ b/docs/learn/code/common/occt/shapes/edge/edge-primitives.md @@ -48,21 +48,21 @@ Let's start with the most basic edge primitive - the line edge. startPointendPointstartPoint-500endPoint500startPointendPointstartPointendPoint","version":"0.20.13","type":"blockly"}} + script={{"script":"startPointendPointstartPoint-500endPoint500startPointendPointstartPointendPoint","version":"0.20.14","type":"blockly"}} title="Creating primitive solids" /> {\n // Create start and end points\n const startPoint: Point3 = [-5, 0, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Create a line between the points\n const lineOptions = new LineDto();\n lineOptions.start = startPoint;\n lineOptions.end = endPoint;\n\n const line = await bitbybit.occt.shapes.edge.line(lineOptions);\n\n // Draw the line and points\n bitbybit.draw.drawAnyAsync({ entity: line });\n bitbybit.draw.drawAnyAsync({ entity: startPoint });\n bitbybit.draw.drawAnyAsync({ entity: endPoint });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { LineDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\n\n// Define the main function\nconst start = async () => {\n // Create start and end points\n const startPoint: Point3 = [-5, 0, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Create a line between the points\n const lineOptions = new LineDto();\n lineOptions.start = startPoint;\n lineOptions.end = endPoint;\n\n const line = await bitbybit.occt.shapes.edge.line(lineOptions);\n\n // Draw the line and points\n bitbybit.draw.drawAnyAsync({ entity: line });\n bitbybit.draw.drawAnyAsync({ entity: startPoint });\n bitbybit.draw.drawAnyAsync({ entity: endPoint });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating primitive solids" /> @@ -88,21 +88,21 @@ This example demonstrates how to create a simple straight line edge in 3D space. startPointmidPointendPointstartPoint-500midPoint060endPoint500startPointmidPointendPointstartPointmidPointendPoint","version":"0.20.13","type":"blockly"}} + script={{"script":"startPointmidPointendPointstartPoint-500midPoint060endPoint500startPointmidPointendPointstartPointmidPointendPoint","version":"0.20.14","type":"blockly"}} title="Arc edge through 3 points" /> {\n // Create start and end points\n const startPoint: Point3 = [-5, 0, 0];\n const midPoint: Point3 = [0, 6, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Create a arc between three points\n const arcOptions = new ArcEdgeThreePointsDto();\n arcOptions.start = startPoint;\n arcOptions.middle = midPoint;\n arcOptions.end = endPoint;\n\n const arc = await bitbybit.occt.shapes.edge.arcThroughThreePoints(arcOptions);\n\n // Draw the arc and points\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [startPoint, midPoint, endPoint] });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { ArcEdgeThreePointsDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\n\n// Define the main function\nconst start = async () => {\n // Create start and end points\n const startPoint: Point3 = [-5, 0, 0];\n const midPoint: Point3 = [0, 6, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Create a arc between three points\n const arcOptions = new ArcEdgeThreePointsDto();\n arcOptions.start = startPoint;\n arcOptions.middle = midPoint;\n arcOptions.end = endPoint;\n\n const arc = await bitbybit.occt.shapes.edge.arcThroughThreePoints(arcOptions);\n\n // Draw the arc and points\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [startPoint, midPoint, endPoint] });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Arc edge through 3 points" /> @@ -128,21 +128,21 @@ This example shows how to create an arc edge that passes through three specific startPointvecEndPtendPointvectorstartPoint-500vecEndPt-230endPoint500vectorstartPointvecEndPtstartPointvectorendPointstartPointvecEndPtendPointstartPointvecEndPt","version":"0.20.13","type":"blockly"}} + script={{"script":"startPointvecEndPtendPointvectorstartPoint-500vecEndPt-230endPoint500vectorstartPointvecEndPtstartPointvectorendPointstartPointvecEndPtendPointstartPointvecEndPt","version":"0.20.14","type":"blockly"}} title="Arc edge from two points and tangent" /> {\n // Create start and end points\n const startPoint: Point3 = [-5, 0, 0];\n const vecEndPt: Point3 = [-2, 3, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Create tangent vector\n const vecOpt = new TwoVectorsDto();\n vecOpt.first = startPoint;\n vecOpt.second = vecEndPt;\n const vector = bitbybit.vector.sub(vecOpt) as Vector3;\n\n // Create an arc\n const arcOptions = new ArcEdgeTwoPointsTangentDto();\n arcOptions.start = startPoint;\n arcOptions.tangentVec = vector;\n arcOptions.end = endPoint;\n\n const arc = await bitbybit.occt.shapes.edge.arcThroughTwoPointsAndTangent(arcOptions);\n\n // Draw the arc, points and tangent\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [startPoint, vecEndPt, endPoint] });\n\n // When two points are provided Bitbybit draws them as a line segment\n bitbybit.draw.drawAnyAsync({ entity: [startPoint, vecEndPt] });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { ArcEdgeTwoPointsTangentDto } = Bit.Inputs.OCCT;\nconst { TwoVectorsDto } = Bit.Inputs.Vector;\n\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n\n// Define the main function\nconst start = async () => {\n // Create start and end points\n const startPoint: Point3 = [-5, 0, 0];\n const vecEndPt: Point3 = [-2, 3, 0];\n const endPoint: Point3 = [5, 0, 0];\n\n // Create tangent vector\n const vecOpt = new TwoVectorsDto();\n vecOpt.first = startPoint;\n vecOpt.second = vecEndPt;\n const vector = bitbybit.vector.sub(vecOpt) as Vector3;\n\n // Create an arc\n const arcOptions = new ArcEdgeTwoPointsTangentDto();\n arcOptions.start = startPoint;\n arcOptions.tangentVec = vector;\n arcOptions.end = endPoint;\n\n const arc = await bitbybit.occt.shapes.edge.arcThroughTwoPointsAndTangent(arcOptions);\n\n // Draw the arc, points and tangent\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [startPoint, vecEndPt, endPoint] });\n\n // When two points are provided Bitbybit draws them as a line segment\n bitbybit.draw.drawAnyAsync({ entity: [startPoint, vecEndPt] });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Arc edge from two points and tangent" /> @@ -169,21 +169,21 @@ This example demonstrates creating an arc using two endpoints and a tangent vect circlestartPointendPointcenterPointarccircle5000010startPoint800endPoint808centerPoint000arccirclestartPointendPointTRUEarccenterPointstartPointcenterPointendPointstartPointcenterPointendPoint","version":"0.20.13","type":"blockly"}} + script={{"script":"circlestartPointendPointcenterPointarccircle5000010startPoint800endPoint808centerPoint000arccirclestartPointendPointTRUEarccenterPointstartPointcenterPointendPointstartPointcenterPointendPoint","version":"0.20.14","type":"blockly"}} title="Arc edge from circle and two points" /> {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Create start and end points for the arc\n const startPoint: Point3 = [8, 0, 0];\n const endPoint: Point3 = [8, 0, 8];\n const centerPoint: Point3 = [0, 0, 0];\n\n // Create arc from circle and two points\n const arcOptions = new ArcEdgeCircleTwoPointsDto();\n arcOptions.circle = circle;\n arcOptions.start = startPoint;\n arcOptions.end = endPoint;\n arcOptions.sense = true;\n\n const arc = await bitbybit.occt.shapes.edge.arcFromCircleAndTwoPoints(arcOptions);\n // Draw the arc and helper points\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [endPoint, centerPoint, startPoint] });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint, endPoint] });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint, startPoint] });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ArcEdgeCircleTwoPointsDto } = Bit.Inputs.OCCT;\n// Import required types\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define the main function\nconst start = async () => {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Create start and end points for the arc\n const startPoint: Point3 = [8, 0, 0];\n const endPoint: Point3 = [8, 0, 8];\n const centerPoint: Point3 = [0, 0, 0];\n\n // Create arc from circle and two points\n const arcOptions = new ArcEdgeCircleTwoPointsDto();\n arcOptions.circle = circle;\n arcOptions.start = startPoint;\n arcOptions.end = endPoint;\n arcOptions.sense = true;\n\n const arc = await bitbybit.occt.shapes.edge.arcFromCircleAndTwoPoints(arcOptions);\n // Draw the arc and helper points\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [endPoint, centerPoint, startPoint] });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint, endPoint] });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint, startPoint] });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Arc edge from circle and two points" /> @@ -211,21 +211,21 @@ This example shows how to create an arc by extracting a portion of an existing c 500001045270TRUE","version":"0.20.13","type":"blockly"}} + script={{"script":"500001045270TRUE","version":"0.20.14","type":"blockly"}} title="Arc edge from circle and two points" /> {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Create arc from circle and two angles\n const arcOptions = new ArcEdgeCircleTwoAnglesDto();\n arcOptions.circle = circle;\n arcOptions.alphaAngle1 = 45;\n arcOptions.alphaAngle2 = 270;\n arcOptions.sense = true;\n\n const arc = await bitbybit.occt.shapes.edge.arcFromCircleAndTwoAngles(arcOptions);\n\n // Draw the arc\n bitbybit.draw.drawAnyAsync({ entity: arc });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ArcEdgeCircleTwoAnglesDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n\n// Define the main function\nconst start = async () => {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Create arc from circle and two angles\n const arcOptions = new ArcEdgeCircleTwoAnglesDto();\n arcOptions.circle = circle;\n arcOptions.alphaAngle1 = 45;\n arcOptions.alphaAngle2 = 270;\n arcOptions.sense = true;\n\n const arc = await bitbybit.occt.shapes.edge.arcFromCircleAndTwoAngles(arcOptions);\n\n // Draw the arc\n bitbybit.draw.drawAnyAsync({ entity: arc });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Arc edge from circle and two angles" /> @@ -252,21 +252,21 @@ This example demonstrates creating an arc by specifying a base circle and two an circlepointcenterPointarccircle5000010point808centerPoint000arccirclepoint360TRUEarccenterPointpointcenterPointpoint","version":"0.20.13","type":"blockly"}} + script={{"script":"circlepointcenterPointarccircle5000010point808centerPoint000arccirclepoint360TRUEarccenterPointpointcenterPointpoint","version":"0.20.14","type":"blockly"}} title="Arc edge from circle point and an angle" /> {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Create a point on the circle\n const point: Point3 = [8, 0, 8];\n const centerPoint: Point3 = [0, 0, 0];\n\n // Create arc from circle, point and angle\n const arcOptions = new ArcEdgeCirclePointAngleDto();\n arcOptions.circle = circle;\n arcOptions.point = point;\n arcOptions.alphaAngle = 360;\n arcOptions.sense = true;\n\n const arc = await bitbybit.occt.shapes.edge.arcFromCirclePointAndAngle(arcOptions);\n\n // Draw the arc and helper elements\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint, point] });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint] });\n bitbybit.draw.drawAnyAsync({ entity: [point] });\n\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto, ArcEdgeCirclePointAngleDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Define the main function\nconst start = async () => {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Create a point on the circle\n const point: Point3 = [8, 0, 8];\n const centerPoint: Point3 = [0, 0, 0];\n\n // Create arc from circle, point and angle\n const arcOptions = new ArcEdgeCirclePointAngleDto();\n arcOptions.circle = circle;\n arcOptions.point = point;\n arcOptions.alphaAngle = 360;\n arcOptions.sense = true;\n\n const arc = await bitbybit.occt.shapes.edge.arcFromCirclePointAndAngle(arcOptions);\n\n // Draw the arc and helper elements\n bitbybit.draw.drawAnyAsync({ entity: arc });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint, point] });\n bitbybit.draw.drawAnyAsync({ entity: [centerPoint] });\n bitbybit.draw.drawAnyAsync({ entity: [point] });\n\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Arc edge from circle point and an angle" /> @@ -294,21 +294,21 @@ This example shows how to create an arc starting from a specific point on a circ 5000010","version":"0.20.13","type":"blockly"}} + script={{"script":"5000010","version":"0.20.14","type":"blockly"}} title="Circle edge" /> {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { CircleDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define the main function\nconst start = async () => {\n // Create a circle edge\n const circleOptions = new CircleDto();\n circleOptions.radius = 5;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circle = await bitbybit.occt.shapes.edge.createCircleEdge(circleOptions);\n\n // Draw the circle\n bitbybit.draw.drawAnyAsync({ entity: circle });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Circle edge" /> @@ -335,21 +335,21 @@ This example demonstrates creating a complete circular edge, which is one of the 000010310","version":"0.20.13","type":"blockly"}} + script={{"script":"000010310","version":"0.20.14","type":"blockly"}} title="Ellipse edge" /> {\n // Create an ellipse edge\n const ellipseOptions = new EllipseDto();\n ellipseOptions.center = [0, 0, 0] as Point3;\n ellipseOptions.direction = [0, 1, 0] as Vector3;\n ellipseOptions.radiusMinor = 3;\n ellipseOptions.radiusMajor = 10;\n\n const ellipse = await bitbybit.occt.shapes.edge.createEllipseEdge(ellipseOptions);\n\n // Draw the ellipse\n bitbybit.draw.drawAnyAsync({ entity: ellipse });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { EllipseDto } = Bit.Inputs.OCCT;\n// Import required types\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define the main function\nconst start = async () => {\n // Create an ellipse edge\n const ellipseOptions = new EllipseDto();\n ellipseOptions.center = [0, 0, 0] as Point3;\n ellipseOptions.direction = [0, 1, 0] as Vector3;\n ellipseOptions.radiusMinor = 3;\n ellipseOptions.radiusMajor = 10;\n\n const ellipse = await bitbybit.occt.shapes.edge.createEllipseEdge(ellipseOptions);\n\n // Draw the ellipse\n bitbybit.draw.drawAnyAsync({ entity: ellipse });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Ellipse edge" /> 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 index a28c31f4..7b2ca442 100644 --- a/docs/learn/code/common/occt/shapes/face/face-basic-primitives.md +++ b/docs/learn/code/common/occt/shapes/face/face-basic-primitives.md @@ -26,21 +26,21 @@ Think of wires as the frame of a window, and faces as the glass that fills the f circleFacesquareFacerectangleFaceellipseFacecircleFace4000010squareFace3700010rectangleFace37-700010ellipseFace00-901013circleFacesquareFacerectangleFaceellipseFace","version":"0.20.13","type":"blockly"}} + script={{"script":"circleFacesquareFacerectangleFaceellipseFacecircleFace4000010squareFace3700010rectangleFace37-700010ellipseFace00-901013circleFacesquareFacerectangleFaceellipseFace","version":"0.20.14","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.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for face creation\nconst { CircleDto, SquareDto, RectangleDto, EllipseDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT face creation functions\nconst { face } = bitbybit.occt.shapes;\n\n// Define the main function to create various primitive faces\nconst start = async () => {\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.14","type":"typescript"}} title="Creating basic face primitives" /> 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 index a64fe635..a88df16f 100644 --- a/docs/learn/code/common/occt/shapes/face/face-from-points.md +++ b/docs/learn/code/common/occt/shapes/face/face-from-points.md @@ -28,21 +28,21 @@ This example demonstrates creating a quadrilateral face from four strategically point1point2point3point4pointsListpolygonFacepoint1-30-8point2-303point3303point4500pointsListpoint1point2point3point4polygonFacepointsListpolygonFacepointsList","version":"0.20.13","type":"blockly"}} + script={{"script":"point1point2point3point4pointsListpolygonFacepoint1-30-8point2-303point3303point4500pointsListpoint1point2point3point4polygonFacepointsListpolygonFacepointsList","version":"0.20.14","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.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for polygon face creation\nconst { PolygonDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\n\n// Get access to OCCT face creation functions\nconst { face } = bitbybit.occt.shapes;\n\n// Define the main function to create a polygon face from points\nconst start = async () => {\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.14","type":"typescript"}} title="Creating face from points" /> 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 index ca0bd490..41ceca58 100644 --- a/docs/learn/code/common/occt/shapes/face/face-from-wire.md +++ b/docs/learn/code/common/occt/shapes/face/face-from-wire.md @@ -36,21 +36,21 @@ This example demonstrates creating a star-shaped face from a reversed wire to co starWirereversedWirefaceFromWirestarWire0000107730FALSEreversedWirestarWirefaceFromWirereversedWireTRUEfaceFromWire","version":"0.20.13","type":"blockly"}} + script={{"script":"starWirereversedWirefaceFromWirestarWire0000107730FALSEreversedWirestarWirefaceFromWirereversedWireTRUEfaceFromWire","version":"0.20.14","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.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for wire and face creation\nconst { StarDto, FaceFromWireDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\n\n// Get access to OCCT wire and face creation functions\nconst { wire, face } = bitbybit.occt.shapes;\n\n// Define the main function to create a face from a wire\nconst start = async () => {\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.14","type":"typescript"}} title="Creating face from wire" /> 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 index 1a68fe10..d2269674 100644 --- 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 @@ -23,21 +23,21 @@ Learn how to create hexagonal face patterns that respond to a control point. Hex gridWidthgridHeighthexagonsInWidthhexagonsInHeightcontrolPointUcontrolPointVrectangleFacecontrolPointhexGridhexCentersdistancesminDistancemaxDistancescalePatterndistancescaledValueinclusionPatternhexagonFacesaffectorPointpingridWidth16.5gridHeight9hexagonsInWidth29hexagonsInHeight29controlPointU0controlPointV0.49rectangleFacegridWidthgridHeight000010controlPointrectangleFacecontrolPointUcontrolPointVhexGridgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSETRUETRUEhexCentershexGridcentersdistancescontrolPointhexCentersminDistancedistancesmaxDistancedistancesscalePatterndistancedistancesscaledValuedistanceminDistancemaxDistance0.2110.9INSERTLASTscalePatternscaledValueinclusionPattern[true, true, true, true, false]hexagonFacesgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSEscalePatternscalePatterninclusionPatternaffectorPointcontrolPoint030pincontrolPointaffectorPoint0010.1Affector0.30.1hexagonFacespincontrolPoint","version":"0.20.13","type":"blockly"}} + script={{"script":"gridWidthgridHeighthexagonsInWidthhexagonsInHeightcontrolPointUcontrolPointVrectangleFacecontrolPointhexGridhexCentersdistancesminDistancemaxDistancescalePatterndistancescaledValueinclusionPatternhexagonFacesaffectorPointpingridWidth16.5gridHeight9hexagonsInWidth29hexagonsInHeight29controlPointU0controlPointV0.49rectangleFacegridWidthgridHeight000010controlPointrectangleFacecontrolPointUcontrolPointVhexGridgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSETRUETRUEhexCentershexGridcentersdistancescontrolPointhexCentersminDistancedistancesmaxDistancedistancesscalePatterndistancedistancesscaledValuedistanceminDistancemaxDistance0.2110.9INSERTLASTscalePatternscaledValueinclusionPattern[true, true, true, true, false]hexagonFacesgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSEscalePatternscalePatterninclusionPatternaffectorPointcontrolPoint030pincontrolPointaffectorPoint0010.1Affector0.30.1hexagonFacespincontrolPoint","version":"0.20.14","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.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for face hexagon grid creation\nconst { HexagonsInGridDto, RectangleDto, DataOnUVDto, PinWithLabelDto } = Bit.Inputs.OCCT;\nconst { HexGridScaledToFitDto } = Bit.Inputs.Point;\nconst { RemapNumberDto } = Bit.Inputs.Math;\nconst { HexGridData } = Bit.Models.Point;\n\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\n\n// Get access to OCCT face, point, and utility functions\nconst { face } = bitbybit.occt.shapes;\nconst { point } = bitbybit;\nconst { math, vector, json, lists } = bitbybit;\nconst { dimensions } = bitbybit.occt;\n\n// Define the main function to create advanced hexagon face pattern\nconst start = async () => {\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.14","type":"typescript"}} title="Creating face from wire" /> diff --git a/docs/learn/code/common/occt/shapes/shells/intro-shells.md b/docs/learn/code/common/occt/shapes/shells/intro-shells.md index e61495c9..3c358250 100644 --- a/docs/learn/code/common/occt/shapes/shells/intro-shells.md +++ b/docs/learn/code/common/occt/shapes/shells/intro-shells.md @@ -40,21 +40,21 @@ This first example demonstrates the fundamental concept of shell creation by sew sizeface1face2rotatedFace2translatedFace2facesshellsize5face1size000010face2size000010rotatedFace2face200190translatedFace2rotatedFace2DIVIDEsize2DIVIDEsize20facesface1translatedFace2shellfaces0.0000001shell","version":"0.20.13","type":"blockly"}} + script={{"script":"sizeface1face2rotatedFace2translatedFace2facesshellsize5face1size000010face2size000010rotatedFace2face200190translatedFace2rotatedFace2DIVIDEsize2DIVIDEsize20facesface1translatedFace2shellfaces0.0000001shell","version":"0.20.14","type":"blockly"}} title="Creating shells by sewing two square faces" /> {\n // Define the size for both square faces\n const size = 5;\n\n // Create first square face at origin\n const face1Options = new SquareDto();\n face1Options.size = size;\n face1Options.center = [0, 0, 0] as Point3;\n face1Options.direction = [0, 1, 0] as Vector3; // Y-up orientation\n\n const face1 = await face.createSquareFace(face1Options);\n\n // Create second square face (initially identical to first)\n const face2Options = new SquareDto();\n face2Options.size = size;\n face2Options.center = [0, 0, 0] as Point3;\n face2Options.direction = [0, 1, 0] as Vector3;\n\n const face2 = await face.createSquareFace(face2Options);\n\n // Rotate the second face 90 degrees around Z-axis to create perpendicular orientation\n const rotateOptions = new RotateDto();\n rotateOptions.shape = face2;\n rotateOptions.axis = [0, 0, 1] as Vector3; // Z-axis\n rotateOptions.angle = 90; // 90 degrees\n\n const rotatedFace2 = await transforms.rotate(rotateOptions);\n\n // Translate the rotated face to share an edge with the first face\n const translateOptions = new TranslateDto();\n translateOptions.shape = rotatedFace2;\n translateOptions.translation = [size / 2, size / 2, 0] as Vector3;\n\n const translatedFace2 = await transforms.translate(translateOptions);\n\n // Create array of faces to sew together\n const faces = [face1, translatedFace2];\n\n // Sew the faces together to create a shell\n const sewOptions = new SewDto(faces);\n sewOptions.tolerance = 1e-7; // Very tight tolerance for precise sewing\n\n const resultShell = await shell.sewFaces(sewOptions);\n\n // Verify that we created a shell (not just individual faces)\n const shapeType = await shape.getShapeType({ shape: resultShell });\n console.log('Shell shape type:', shapeType); // Should output \"shell\"\n\n // Draw the resulting shell\n bitbybit.draw.drawAnyAsync({\n entity: resultShell\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating faces, shells, and transformations\nconst { SquareDto, SewDto, RotateDto, TranslateDto } = Bit.Inputs.OCCT;\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShellPointer = Bit.Inputs.OCCT.TopoDSShellPointer;\n\n// Get access to OCCT modules and utility functions\nconst { face, shell, shape } = bitbybit.occt.shapes;\nconst { transforms } = bitbybit.occt;\n\n// Define the main function to create a simple shell from two faces\nconst start = async () => {\n // Define the size for both square faces\n const size = 5;\n\n // Create first square face at origin\n const face1Options = new SquareDto();\n face1Options.size = size;\n face1Options.center = [0, 0, 0] as Point3;\n face1Options.direction = [0, 1, 0] as Vector3; // Y-up orientation\n\n const face1 = await face.createSquareFace(face1Options);\n\n // Create second square face (initially identical to first)\n const face2Options = new SquareDto();\n face2Options.size = size;\n face2Options.center = [0, 0, 0] as Point3;\n face2Options.direction = [0, 1, 0] as Vector3;\n\n const face2 = await face.createSquareFace(face2Options);\n\n // Rotate the second face 90 degrees around Z-axis to create perpendicular orientation\n const rotateOptions = new RotateDto();\n rotateOptions.shape = face2;\n rotateOptions.axis = [0, 0, 1] as Vector3; // Z-axis\n rotateOptions.angle = 90; // 90 degrees\n\n const rotatedFace2 = await transforms.rotate(rotateOptions);\n\n // Translate the rotated face to share an edge with the first face\n const translateOptions = new TranslateDto();\n translateOptions.shape = rotatedFace2;\n translateOptions.translation = [size / 2, size / 2, 0] as Vector3;\n\n const translatedFace2 = await transforms.translate(translateOptions);\n\n // Create array of faces to sew together\n const faces = [face1, translatedFace2];\n\n // Sew the faces together to create a shell\n const sewOptions = new SewDto(faces);\n sewOptions.tolerance = 1e-7; // Very tight tolerance for precise sewing\n\n const resultShell = await shell.sewFaces(sewOptions);\n\n // Verify that we created a shell (not just individual faces)\n const shapeType = await shape.getShapeType({ shape: resultShell });\n console.log('Shell shape type:', shapeType); // Should output \"shell\"\n\n // Draw the resulting shell\n bitbybit.draw.drawAnyAsync({\n entity: resultShell\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating shells by sewing two square faces" /> @@ -81,21 +81,21 @@ While this cylindrical solid **can be constructed in Bitbybit using the simple s radiusheightcircleWireextrusionVectorcylindricalSurfacebottomFacetopFacefacesshellsolidradius5height5circleWireradius000010extrusionVector0height0cylindricalSurfacecircleWireextrusionVectorbottomFacecircleWireTRUEtopFacebottomFaceextrusionVectorfacestopFacecylindricalSurfacebottomFaceshellfaces1e-7solidshellsolid","version":"0.20.13","type":"blockly"}} + script={{"script":"radiusheightcircleWireextrusionVectorcylindricalSurfacebottomFacetopFacefacesshellsolidradius5height5circleWireradius000010extrusionVector0height0cylindricalSurfacecircleWireextrusionVectorbottomFacecircleWireTRUEtopFacebottomFaceextrusionVectorfacestopFacecylindricalSurfacebottomFaceshellfaces1e-7solidshellsolid","version":"0.20.14","type":"blockly"}} title="Creating closed shells and converting to solids" /> {\n // Define geometric parameters\n const radius = 5;\n const height = 5;\n\n // Step 1: Create base circle wire\n const circleOptions = new CircleDto();\n circleOptions.radius = radius;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3; // Y-up orientation\n\n const circleWire = await wire.createCircleWire(circleOptions);\n\n // Step 2: Create extrusion vector for cylindrical surface\n const extrusionVector: Vector3 = [0, height, 0];\n\n // Step 3: Create cylindrical surface by extruding the circle wire\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = circleWire;\n extrudeOptions.direction = extrusionVector;\n\n const cylindricalSurface = await operations.extrude(extrudeOptions);\n\n // Step 4: Create bottom face from the circle wire\n const bottomFaceOptions = new FaceFromWireDto();\n bottomFaceOptions.shape = circleWire;\n bottomFaceOptions.planar = true; // Ensure flat circular face\n\n const bottomFace = await face.createFaceFromWire(bottomFaceOptions);\n\n // Step 5: Create top face by translating bottom face\n const translateOptions = new TranslateDto();\n translateOptions.shape = bottomFace;\n translateOptions.translation = extrusionVector;\n\n const topFace = await transforms.translate(translateOptions);\n\n // Step 6: Collect all faces that will form the closed shell\n const faces = [topFace, cylindricalSurface, bottomFace];\n\n // Step 7: Sew faces together to create a closed shell\n const sewOptions = new SewDto(faces);\n sewOptions.tolerance = 1e-7; // High precision for closed shell\n\n const closedShell = await shell.sewFaces(sewOptions);\n\n // Step 8: Convert closed shell to solid\n const solidOptions = new ShapeDto();\n solidOptions.shape = closedShell;\n\n const cylinderSolid = await solid.fromClosedShell(solidOptions);\n\n // Step 9: Verify the shape types\n const shellType = await shape.getShapeType({ shape: closedShell });\n const solidType = await shape.getShapeType({ shape: cylinderSolid });\n\n console.log('Shell type:', shellType); // Should output \"shell\"\n console.log('Solid type:', solidType); // Should output \"solid\"\n\n // Step 10: Draw the final solid\n bitbybit.draw.drawAnyAsync({\n entity: cylinderSolid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs for creating complex geometries\nconst { CircleDto, FaceFromWireDto, ShapeDto, SewDto, ExtrudeDto, TranslateDto } = Bit.Inputs.OCCT;\n\n// Import type definitions for type safety\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype TopoDSFacePointer = Bit.Inputs.OCCT.TopoDSFacePointer;\ntype TopoDSShellPointer = Bit.Inputs.OCCT.TopoDSShellPointer;\n\n// Get access to OCCT modules and utility functions\nconst { wire, face, shell, solid, shape } = bitbybit.occt.shapes;\nconst { operations, transforms } = bitbybit.occt;\n\n// Define the main function to create a complex shell and convert to solid\nconst start = async () => {\n // Define geometric parameters\n const radius = 5;\n const height = 5;\n\n // Step 1: Create base circle wire\n const circleOptions = new CircleDto();\n circleOptions.radius = radius;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3; // Y-up orientation\n\n const circleWire = await wire.createCircleWire(circleOptions);\n\n // Step 2: Create extrusion vector for cylindrical surface\n const extrusionVector: Vector3 = [0, height, 0];\n\n // Step 3: Create cylindrical surface by extruding the circle wire\n const extrudeOptions = new ExtrudeDto();\n extrudeOptions.shape = circleWire;\n extrudeOptions.direction = extrusionVector;\n\n const cylindricalSurface = await operations.extrude(extrudeOptions);\n\n // Step 4: Create bottom face from the circle wire\n const bottomFaceOptions = new FaceFromWireDto();\n bottomFaceOptions.shape = circleWire;\n bottomFaceOptions.planar = true; // Ensure flat circular face\n\n const bottomFace = await face.createFaceFromWire(bottomFaceOptions);\n\n // Step 5: Create top face by translating bottom face\n const translateOptions = new TranslateDto();\n translateOptions.shape = bottomFace;\n translateOptions.translation = extrusionVector;\n\n const topFace = await transforms.translate(translateOptions);\n\n // Step 6: Collect all faces that will form the closed shell\n const faces = [topFace, cylindricalSurface, bottomFace];\n\n // Step 7: Sew faces together to create a closed shell\n const sewOptions = new SewDto(faces);\n sewOptions.tolerance = 1e-7; // High precision for closed shell\n\n const closedShell = await shell.sewFaces(sewOptions);\n\n // Step 8: Convert closed shell to solid\n const solidOptions = new ShapeDto();\n solidOptions.shape = closedShell;\n\n const cylinderSolid = await solid.fromClosedShell(solidOptions);\n\n // Step 9: Verify the shape types\n const shellType = await shape.getShapeType({ shape: closedShell });\n const solidType = await shape.getShapeType({ shape: cylinderSolid });\n\n console.log('Shell type:', shellType); // Should output \"shell\"\n console.log('Solid type:', solidType); // Should output \"solid\"\n\n // Step 10: Draw the final solid\n bitbybit.draw.drawAnyAsync({\n entity: cylinderSolid\n });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating closed shells and converting to solids" /> diff --git a/docs/learn/code/common/occt/shapes/solids/intro-solids.md b/docs/learn/code/common/occt/shapes/solids/intro-solids.md index 2841f1eb..2f5c970c 100644 --- a/docs/learn/code/common/occt/shapes/solids/intro-solids.md +++ b/docs/learn/code/common/occt/shapes/solids/intro-solids.md @@ -32,21 +32,21 @@ This example demonstrates the creation of the most common primitive solid types: boxcubecylindersphereconebox378000TRUEcube5500TRUEcylinder23-500010360TRUEsphere1.51000cone213360-1000010boxcubecylinderspherecone","version":"0.20.13","type":"blockly"}} + script={{"script":"boxcubecylindersphereconebox378000TRUEcube5500TRUEcylinder23-500010360TRUEsphere1.51000cone213360-1000010boxcubecylinderspherecone","version":"0.20.14","type":"blockly"}} title="Creating primitive solids" /> {\n // Create a rectangular box\n const boxOptions = new BoxDto();\n boxOptions.width = 3;\n boxOptions.length = 7;\n boxOptions.height = 8;\n boxOptions.center = [0, 0, 0] as Point3;\n boxOptions.originOnCenter = true;\n \n const box = await solid.createBox(boxOptions);\n \n // Create a cube at a different position\n const cubeOptions = new CubeDto();\n cubeOptions.size = 5;\n cubeOptions.center = [5, 0, 0] as Point3;\n cubeOptions.originOnCenter = true;\n \n const cube = await solid.createCube(cubeOptions);\n \n // Create a cylinder\n const cylinderOptions = new CylinderDto();\n cylinderOptions.radius = 2;\n cylinderOptions.height = 3;\n cylinderOptions.center = [-5, 0, 0] as Point3;\n cylinderOptions.direction = [0, 1, 0] as Vector3;\n cylinderOptions.angle = 360;\n cylinderOptions.originOnCenter = true;\n \n const cylinder = await solid.createCylinder(cylinderOptions);\n \n // Create a sphere\n const sphereOptions = new SphereDto();\n sphereOptions.radius = 1.5;\n sphereOptions.center = [10, 0, 0] as Point3;\n \n const sphere = await solid.createSphere(sphereOptions);\n \n // Create a cone\n const coneOptions = new ConeDto();\n coneOptions.radius1 = 2;\n coneOptions.radius2 = 1;\n coneOptions.height = 3;\n coneOptions.angle = 360;\n coneOptions.center = [-10, 0, 0] as Point3;\n coneOptions.direction = [0, 1, 0] as Vector3;\n \n const cone = await solid.createCone(coneOptions);\n \n // Draw all the created solids\n bitbybit.draw.drawAnyAsync({ entity: box });\n bitbybit.draw.drawAnyAsync({ entity: cube });\n bitbybit.draw.drawAnyAsync({ entity: cylinder });\n bitbybit.draw.drawAnyAsync({ entity: sphere });\n bitbybit.draw.drawAnyAsync({ entity: cone });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for solid creation\nconst { BoxDto, CubeDto, CylinderDto, SphereDto, ConeDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT solid creation functions\nconst { solid } = bitbybit.occt.shapes;\n\n// Define the main function to create various primitive solids\nconst start = async () => {\n // Create a rectangular box\n const boxOptions = new BoxDto();\n boxOptions.width = 3;\n boxOptions.length = 7;\n boxOptions.height = 8;\n boxOptions.center = [0, 0, 0] as Point3;\n boxOptions.originOnCenter = true;\n \n const box = await solid.createBox(boxOptions);\n \n // Create a cube at a different position\n const cubeOptions = new CubeDto();\n cubeOptions.size = 5;\n cubeOptions.center = [5, 0, 0] as Point3;\n cubeOptions.originOnCenter = true;\n \n const cube = await solid.createCube(cubeOptions);\n \n // Create a cylinder\n const cylinderOptions = new CylinderDto();\n cylinderOptions.radius = 2;\n cylinderOptions.height = 3;\n cylinderOptions.center = [-5, 0, 0] as Point3;\n cylinderOptions.direction = [0, 1, 0] as Vector3;\n cylinderOptions.angle = 360;\n cylinderOptions.originOnCenter = true;\n \n const cylinder = await solid.createCylinder(cylinderOptions);\n \n // Create a sphere\n const sphereOptions = new SphereDto();\n sphereOptions.radius = 1.5;\n sphereOptions.center = [10, 0, 0] as Point3;\n \n const sphere = await solid.createSphere(sphereOptions);\n \n // Create a cone\n const coneOptions = new ConeDto();\n coneOptions.radius1 = 2;\n coneOptions.radius2 = 1;\n coneOptions.height = 3;\n coneOptions.angle = 360;\n coneOptions.center = [-10, 0, 0] as Point3;\n coneOptions.direction = [0, 1, 0] as Vector3;\n \n const cone = await solid.createCone(coneOptions);\n \n // Draw all the created solids\n bitbybit.draw.drawAnyAsync({ entity: box });\n bitbybit.draw.drawAnyAsync({ entity: cube });\n bitbybit.draw.drawAnyAsync({ entity: cylinder });\n bitbybit.draw.drawAnyAsync({ entity: sphere });\n bitbybit.draw.drawAnyAsync({ entity: cone });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating primitive solids" /> diff --git a/docs/learn/code/common/occt/shapes/solids/volume-and-surface-area.md b/docs/learn/code/common/occt/shapes/solids/volume-and-surface-area.md index ecd4efcd..474ca29d 100644 --- a/docs/learn/code/common/occt/shapes/solids/volume-and-surface-area.md +++ b/docs/learn/code/common/occt/shapes/solids/volume-and-surface-area.md @@ -32,21 +32,21 @@ The example shows a practical workflow for measuring and displaying geometric pr heightconevolumesurfaceArearoundedVolumeroundedSurfaceAreavolumeTextareaTextvolumePinareaPinheight5cone21height360000010volumeconesurfaceAreaconeroundedVolumevolume2roundedSurfaceAreasurfaceArea2volumeTextVolume {0} m3roundedVolumeareaTextArea {0} m3roundedSurfaceAreavolumePin0height01ADDheight301000volumeText0.30.3areaPin0height01ADDheight401000areaText0.30.3conevolumePinareaPin","version":"0.20.13","type":"blockly"}} + script={{"script":"heightconevolumesurfaceArearoundedVolumeroundedSurfaceAreavolumeTextareaTextvolumePinareaPinheight5cone21height360000010volumeconesurfaceAreaconeroundedVolumevolume2roundedSurfaceAreasurfaceArea2volumeTextVolume {0} m3roundedVolumeareaTextArea {0} m3roundedSurfaceAreavolumePin0height01ADDheight301000volumeText0.30.3areaPin0height01ADDheight401000areaText0.30.3conevolumePinareaPin","version":"0.20.14","type":"blockly"}} title="Get volume and surface area from solid" /> {\n // Parametric height value\n const height = 5;\n\n // Create a cone\n const coneOptions = new ConeDto();\n coneOptions.radius1 = 2;\n coneOptions.radius2 = 1;\n coneOptions.height = height;\n coneOptions.angle = 360;\n coneOptions.center = [0, 0, 0] as Point3;\n coneOptions.direction = [0, 1, 0] as Vector3;\n\n const cone = await solid.createCone(coneOptions);\n\n // Get volume and surface area\n const volume = await solid.getSolidVolume({ shape: cone });\n const surfaceArea = await solid.getSolidSurfaceArea({ shape: cone });\n\n // Round to 2 decimal places\n const roundedVolume = math.roundToDecimals({ number: volume, decimalPlaces: 2 });\n const roundedSurfaceArea = math.roundToDecimals({ number: surfaceArea, decimalPlaces: 2 });\n\n // Format as text with units\n const volumeText = text.format({\n text: \"Volume {0} m3\",\n values: [text.toString({ item: roundedVolume })]\n });\n const areaText = text.format({\n text: \"Area {0} m2\",\n values: [text.toString({ item: roundedSurfaceArea })]\n });\n\n // Create pin labels to display the measurements\n const volumePinOptions = new PinWithLabelDto();\n volumePinOptions.startPoint = [0, height, 0] as Point3;\n volumePinOptions.endPoint = [1, height + 3, 0] as Point3;\n volumePinOptions.direction = [1, 0, 0] as Vector3;\n volumePinOptions.offsetFromStart = 0;\n volumePinOptions.label = volumeText;\n volumePinOptions.labelOffset = 0.3;\n volumePinOptions.labelSize = 0.3;\n\n const volumePin = await dimensions.pinWithLabel(volumePinOptions);\n\n const areaPinOptions = new PinWithLabelDto();\n areaPinOptions.startPoint = [0, height, 0] as Point3;\n areaPinOptions.endPoint = [1, height + 4, 0] as Point3;\n areaPinOptions.direction = [1, 0, 0] as Vector3;\n areaPinOptions.offsetFromStart = 0;\n areaPinOptions.label = areaText;\n areaPinOptions.labelOffset = 0.3;\n areaPinOptions.labelSize = 0.3;\n\n const areaPin = await dimensions.pinWithLabel(areaPinOptions);\n\n // Draw the cone and labels\n bitbybit.draw.drawAnyAsync({ entity: cone });\n bitbybit.draw.drawAnyAsync({ entity: volumePin });\n bitbybit.draw.drawAnyAsync({ entity: areaPin });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types\nconst { ConeDto, PinWithLabelDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT modules\nconst { solid } = bitbybit.occt.shapes;\nconst { dimensions } = bitbybit.occt;\nconst { math, text } = bitbybit;\n\n// Define the main function to demonstrate volume and surface area calculation\nconst start = async () => {\n // Parametric height value\n const height = 5;\n\n // Create a cone\n const coneOptions = new ConeDto();\n coneOptions.radius1 = 2;\n coneOptions.radius2 = 1;\n coneOptions.height = height;\n coneOptions.angle = 360;\n coneOptions.center = [0, 0, 0] as Point3;\n coneOptions.direction = [0, 1, 0] as Vector3;\n\n const cone = await solid.createCone(coneOptions);\n\n // Get volume and surface area\n const volume = await solid.getSolidVolume({ shape: cone });\n const surfaceArea = await solid.getSolidSurfaceArea({ shape: cone });\n\n // Round to 2 decimal places\n const roundedVolume = math.roundToDecimals({ number: volume, decimalPlaces: 2 });\n const roundedSurfaceArea = math.roundToDecimals({ number: surfaceArea, decimalPlaces: 2 });\n\n // Format as text with units\n const volumeText = text.format({\n text: \"Volume {0} m3\",\n values: [text.toString({ item: roundedVolume })]\n });\n const areaText = text.format({\n text: \"Area {0} m2\",\n values: [text.toString({ item: roundedSurfaceArea })]\n });\n\n // Create pin labels to display the measurements\n const volumePinOptions = new PinWithLabelDto();\n volumePinOptions.startPoint = [0, height, 0] as Point3;\n volumePinOptions.endPoint = [1, height + 3, 0] as Point3;\n volumePinOptions.direction = [1, 0, 0] as Vector3;\n volumePinOptions.offsetFromStart = 0;\n volumePinOptions.label = volumeText;\n volumePinOptions.labelOffset = 0.3;\n volumePinOptions.labelSize = 0.3;\n\n const volumePin = await dimensions.pinWithLabel(volumePinOptions);\n\n const areaPinOptions = new PinWithLabelDto();\n areaPinOptions.startPoint = [0, height, 0] as Point3;\n areaPinOptions.endPoint = [1, height + 4, 0] as Point3;\n areaPinOptions.direction = [1, 0, 0] as Vector3;\n areaPinOptions.offsetFromStart = 0;\n areaPinOptions.label = areaText;\n areaPinOptions.labelOffset = 0.3;\n areaPinOptions.labelSize = 0.3;\n\n const areaPin = await dimensions.pinWithLabel(areaPinOptions);\n\n // Draw the cone and labels\n bitbybit.draw.drawAnyAsync({ entity: cone });\n bitbybit.draw.drawAnyAsync({ entity: volumePin });\n bitbybit.draw.drawAnyAsync({ entity: areaPin });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Get volume and surface area from solid" /> diff --git a/docs/learn/code/common/occt/shapes/wire/wire-basic-primitives.md b/docs/learn/code/common/occt/shapes/wire/wire-basic-primitives.md index 94c640c3..6363967a 100644 --- a/docs/learn/code/common/occt/shapes/wire/wire-basic-primitives.md +++ b/docs/learn/code/common/occt/shapes/wire/wire-basic-primitives.md @@ -33,21 +33,21 @@ This example demonstrates the creation of the most common primitive wire types: circleWiresquareWirengonWireparallelogramWirerectangleWireellipseWirecircleWire3000010squareWire3500010ngonWire-60001062parallelogramWire-1100010TRUE3230rectangleWire26900010ellipseWire120001014circleWiresquareWirengonWireparallelogramWirerectangleWireellipseWire","version":"0.20.13","type":"blockly"}} + script={{"script":"circleWiresquareWirengonWireparallelogramWirerectangleWireellipseWirecircleWire3000010squareWire3500010ngonWire-60001062parallelogramWire-1100010TRUE3230rectangleWire26900010ellipseWire120001014circleWiresquareWirengonWireparallelogramWirerectangleWireellipseWire","version":"0.20.14","type":"blockly"}} title="Creating basic wire primitives" /> {\n // Create a circle wire\n const circleOptions = new CircleDto();\n circleOptions.radius = 3;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circleWire = await wire.createCircleWire(circleOptions);\n\n // Create a square wire at a different position\n const squareOptions = new SquareDto();\n squareOptions.size = 3;\n squareOptions.center = [5, 0, 0] as Point3;\n squareOptions.direction = [0, 1, 0] as Vector3;\n\n const squareWire = await wire.createSquareWire(squareOptions);\n\n // Create an NGon wire (hexagon)\n const ngonOptions = new NGonWireDto();\n ngonOptions.center = [-6, 0, 0] as Point3;\n ngonOptions.direction = [0, 1, 0] as Vector3;\n ngonOptions.nrCorners = 6;\n ngonOptions.radius = 2;\n\n const ngonWire = await wire.createNGonWire(ngonOptions);\n\n // Create a parallelogram wire\n const parallelogramOptions = new ParallelogramDto();\n parallelogramOptions.center = [-11, 0, 0] as Point3;\n parallelogramOptions.direction = [0, 1, 0] as Vector3;\n parallelogramOptions.aroundCenter = true;\n parallelogramOptions.width = 3;\n parallelogramOptions.height = 2;\n parallelogramOptions.angle = 30;\n\n const parallelogramWire = await wire.createParallelogramWire(parallelogramOptions);\n\n // Create a rectangle wire\n const rectangleOptions = new RectangleDto();\n rectangleOptions.width = 2;\n rectangleOptions.length = 6;\n rectangleOptions.center = [9, 0, 0] as Point3;\n rectangleOptions.direction = [0, 1, 0] as Vector3;\n\n const rectangleWire = await wire.createRectangleWire(rectangleOptions);\n\n // Create an ellipse wire\n const ellipseOptions = new EllipseDto();\n ellipseOptions.center = [12, 0, 0] as Point3;\n ellipseOptions.direction = [0, 1, 0] as Vector3;\n ellipseOptions.radiusMinor = 1;\n ellipseOptions.radiusMajor = 4;\n\n const ellipseWire = await wire.createEllipseWire(ellipseOptions);\n\n // Draw all the created wires\n bitbybit.draw.drawAnyAsync({ entity: circleWire });\n bitbybit.draw.drawAnyAsync({ entity: squareWire });\n bitbybit.draw.drawAnyAsync({ entity: ngonWire });\n bitbybit.draw.drawAnyAsync({ entity: parallelogramWire });\n bitbybit.draw.drawAnyAsync({ entity: rectangleWire });\n bitbybit.draw.drawAnyAsync({ entity: ellipseWire });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for wire creation\nconst { CircleDto, SquareDto, NGonWireDto, ParallelogramDto, RectangleDto, EllipseDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT wire creation functions\nconst { wire } = bitbybit.occt.shapes;\n\n// Define the main function to create various primitive wires\nconst start = async () => {\n // Create a circle wire\n const circleOptions = new CircleDto();\n circleOptions.radius = 3;\n circleOptions.center = [0, 0, 0] as Point3;\n circleOptions.direction = [0, 1, 0] as Vector3;\n\n const circleWire = await wire.createCircleWire(circleOptions);\n\n // Create a square wire at a different position\n const squareOptions = new SquareDto();\n squareOptions.size = 3;\n squareOptions.center = [5, 0, 0] as Point3;\n squareOptions.direction = [0, 1, 0] as Vector3;\n\n const squareWire = await wire.createSquareWire(squareOptions);\n\n // Create an NGon wire (hexagon)\n const ngonOptions = new NGonWireDto();\n ngonOptions.center = [-6, 0, 0] as Point3;\n ngonOptions.direction = [0, 1, 0] as Vector3;\n ngonOptions.nrCorners = 6;\n ngonOptions.radius = 2;\n\n const ngonWire = await wire.createNGonWire(ngonOptions);\n\n // Create a parallelogram wire\n const parallelogramOptions = new ParallelogramDto();\n parallelogramOptions.center = [-11, 0, 0] as Point3;\n parallelogramOptions.direction = [0, 1, 0] as Vector3;\n parallelogramOptions.aroundCenter = true;\n parallelogramOptions.width = 3;\n parallelogramOptions.height = 2;\n parallelogramOptions.angle = 30;\n\n const parallelogramWire = await wire.createParallelogramWire(parallelogramOptions);\n\n // Create a rectangle wire\n const rectangleOptions = new RectangleDto();\n rectangleOptions.width = 2;\n rectangleOptions.length = 6;\n rectangleOptions.center = [9, 0, 0] as Point3;\n rectangleOptions.direction = [0, 1, 0] as Vector3;\n\n const rectangleWire = await wire.createRectangleWire(rectangleOptions);\n\n // Create an ellipse wire\n const ellipseOptions = new EllipseDto();\n ellipseOptions.center = [12, 0, 0] as Point3;\n ellipseOptions.direction = [0, 1, 0] as Vector3;\n ellipseOptions.radiusMinor = 1;\n ellipseOptions.radiusMajor = 4;\n\n const ellipseWire = await wire.createEllipseWire(ellipseOptions);\n\n // Draw all the created wires\n bitbybit.draw.drawAnyAsync({ entity: circleWire });\n bitbybit.draw.drawAnyAsync({ entity: squareWire });\n bitbybit.draw.drawAnyAsync({ entity: ngonWire });\n bitbybit.draw.drawAnyAsync({ entity: parallelogramWire });\n bitbybit.draw.drawAnyAsync({ entity: rectangleWire });\n bitbybit.draw.drawAnyAsync({ entity: ellipseWire });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating basic wire primitives" /> 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 index 150f10a2..f3b33834 100644 --- a/docs/learn/code/common/occt/shapes/wire/wire-bezier-weights.md +++ b/docs/learn/code/common/occt/shapes/wire/wire-bezier-weights.md @@ -34,21 +34,21 @@ This technique is invaluable for design work where you need the curve to emphasi point1point2point3point4point5pointsListweightsbezierWirepolylineRefminWeightmaxWeightspheressphere1sphere2sphere3sphere4sphere5point1-1000point2-503point307-5point4503point51000pointsListpoint1point2point3point4point5weights10602003010bezierWirepointsListweightsFALSEbezierWire0.01FALSETRUE#e4ff1a10polylineRefpointsListFALSEpolylineRefminWeightweightsmaxWeightweightssphere1point110minWeightmaxWeight0.30.9sphere2point260minWeightmaxWeight0.30.9sphere3point3200minWeightmaxWeight0.30.9sphere4point430minWeightmaxWeight0.30.9sphere5point510minWeightmaxWeight0.30.9spheressphere1sphere2sphere3sphere4sphere5spheres0.05TRUEFALSE#37ff00","version":"0.20.13","type":"blockly"}} + script={{"script":"point1point2point3point4point5pointsListweightsbezierWirepolylineRefminWeightmaxWeightspheressphere1sphere2sphere3sphere4sphere5point1-1000point2-503point307-5point4503point51000pointsListpoint1point2point3point4point5weights10602003010bezierWirepointsListweightsFALSEbezierWire0.01FALSETRUE#e4ff1a10polylineRefpointsListFALSEpolylineRefminWeightweightsmaxWeightweightssphere1point110minWeightmaxWeight0.30.9sphere2point260minWeightmaxWeight0.30.9sphere3point3200minWeightmaxWeight0.30.9sphere4point430minWeightmaxWeight0.30.9sphere5point510minWeightmaxWeight0.30.9spheressphere1sphere2sphere3sphere4sphere5spheres0.05TRUEFALSE#37ff00","version":"0.20.14","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.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types\nconst { BezierWeightsDto, SphereDto } = Bit.Inputs.OCCT;\nconst { DrawOcctShapeSimpleOptions } = Bit.Inputs.Draw;\nconst { RemapNumberDto } = Bit.Inputs.Math;\nconst { PolylineCreateDto } = Bit.Inputs.Polyline;\n\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\n// Get access to BitByBit functions\nconst { wire } = bitbybit.occt.shapes;\nconst { shapes } = bitbybit.occt;\nconst { draw, vector, polyline } = bitbybit;\n\nconst start = async () => {\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.14","type":"typescript"}} title="Bezier curves with different weight distributions" /> diff --git a/docs/learn/code/common/occt/shapes/wire/wire-from-edges.md b/docs/learn/code/common/occt/shapes/wire/wire-from-edges.md index 432a11aa..2d47da58 100644 --- a/docs/learn/code/common/occt/shapes/wire/wire-from-edges.md +++ b/docs/learn/code/common/occt/shapes/wire/wire-from-edges.md @@ -60,21 +60,21 @@ This example showcases how simple geometric operations (arc creation and rotatio arc1arc2arc3arc4edgesListcombinedWirearc1505003-505arc2arc101090arc3arc201090arc4arc301090edgesListarc1arc2arc3arc4combinedWireedgesListcombinedWire","version":"0.20.13","type":"blockly"}} + script={{"script":"arc1arc2arc3arc4edgesListcombinedWirearc1505003-505arc2arc101090arc3arc201090arc4arc301090edgesListarc1arc2arc3arc4combinedWireedgesListcombinedWire","version":"0.20.14","type":"blockly"}} title="Combining edges into a wire" /> {\n // Create the first arc through three points\n const arcOptions = new ArcEdgeThreePointsDto();\n arcOptions.start = [5, 0, 5] as Point3;\n arcOptions.middle = [0, 0, 3] as Point3;\n arcOptions.end = [-5, 0, 5] as Point3;\n\n const arc1 = await edge.arcThroughThreePoints(arcOptions);\n\n // Create rotation options for Y-axis rotation\n const rotateOptions = new RotateDto();\n rotateOptions.axis = [0, 1, 0] as Vector3;\n rotateOptions.angle = 90; // 90 degrees\n\n // Create three more arcs by rotating the first arc\n rotateOptions.shape = arc1;\n const arc2 = await transforms.rotate(rotateOptions);\n\n rotateOptions.shape = arc2;\n const arc3 = await transforms.rotate(rotateOptions);\n\n rotateOptions.shape = arc3;\n const arc4 = await transforms.rotate(rotateOptions);\n\n // Combine all arc edges into a single wire\n const combineOptions = new ShapesDto();\n combineOptions.shapes = [arc1, arc2, arc3, arc4];\n\n const combinedWire = await wire.combineEdgesAndWiresIntoAWire(combineOptions);\n\n // Draw the combined wire\n bitbybit.draw.drawAnyAsync({ entity: combinedWire });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for edge and wire creation\nconst { ArcEdgeThreePointsDto, RotateDto, ShapesDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\ntype TopoDSEdgePointer = Bit.Inputs.OCCT.TopoDSEdgePointer;\n\n// Get access to OCCT edge, wire, and transform functions\nconst { edge, wire } = bitbybit.occt.shapes;\nconst { transforms } = bitbybit.occt;\n\n// Define the main function to create a wire from multiple edges\nconst start = async () => {\n // Create the first arc through three points\n const arcOptions = new ArcEdgeThreePointsDto();\n arcOptions.start = [5, 0, 5] as Point3;\n arcOptions.middle = [0, 0, 3] as Point3;\n arcOptions.end = [-5, 0, 5] as Point3;\n\n const arc1 = await edge.arcThroughThreePoints(arcOptions);\n\n // Create rotation options for Y-axis rotation\n const rotateOptions = new RotateDto();\n rotateOptions.axis = [0, 1, 0] as Vector3;\n rotateOptions.angle = 90; // 90 degrees\n\n // Create three more arcs by rotating the first arc\n rotateOptions.shape = arc1;\n const arc2 = await transforms.rotate(rotateOptions);\n\n rotateOptions.shape = arc2;\n const arc3 = await transforms.rotate(rotateOptions);\n\n rotateOptions.shape = arc3;\n const arc4 = await transforms.rotate(rotateOptions);\n\n // Combine all arc edges into a single wire\n const combineOptions = new ShapesDto();\n combineOptions.shapes = [arc1, arc2, arc3, arc4];\n\n const combinedWire = await wire.combineEdgesAndWiresIntoAWire(combineOptions);\n\n // Draw the combined wire\n bitbybit.draw.drawAnyAsync({ entity: combinedWire });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Combining edges into a wire" /> 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 db5ea227..1c9d8473 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 @@ -35,21 +35,21 @@ This approach demonstrates the power of parametric design thinking, where simple gridWidthgridHeighthexagonsInWidthhexagonsInHeightcontrolPointXcontrolPointZcontrolPointhexGridhexCentersdistancesminDistancemaxDistancescalePatternidistancescaledValueinclusionPatternhexagonWiresboundaryWiregridWidth16.5gridHeight9hexagonsInWidth20hexagonsInHeight20controlPointX2.2controlPointZ2.8controlPointcontrolPointX0controlPointZhexGridgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSETRUETRUEhexCentershexGridcentersdistancescontrolPointhexCentersminDistancedistancesmaxDistancedistancesscalePatterndistancedistancesscaledValuedistanceminDistancemaxDistance0.1110.999INSERTLASTscalePatternscaledValueinclusionPattern[true, true, false]hexagonWiresgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSEscalePatternscalePatterninclusionPatternboundaryWiregridWidthgridHeight000010hexagonWiresboundaryWirecontrolPoint","version":"0.20.13","type":"blockly"}} + script={{"script":"gridWidthgridHeighthexagonsInWidthhexagonsInHeightcontrolPointXcontrolPointZcontrolPointhexGridhexCentersdistancesminDistancemaxDistancescalePatternidistancescaledValueinclusionPatternhexagonWiresboundaryWiregridWidth16.5gridHeight9hexagonsInWidth20hexagonsInHeight20controlPointX2.2controlPointZ2.8controlPointcontrolPointX0controlPointZhexGridgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSETRUETRUEhexCentershexGridcentersdistancescontrolPointhexCentersminDistancedistancesmaxDistancedistancesscalePatterndistancedistancesscaledValuedistanceminDistancemaxDistance0.1110.999INSERTLASTscalePatternscaledValueinclusionPattern[true, true, false]hexagonWiresgridWidthgridHeighthexagonsInWidthhexagonsInHeightTRUEFALSEFALSEFALSEFALSEscalePatternscalePatterninclusionPatternboundaryWiregridWidthgridHeight000010hexagonWiresboundaryWirecontrolPoint","version":"0.20.14","type":"blockly"}} title="Creating hexagon grids in beautiful patterns" /> {\n // Define grid dimensions\n const gridWidth = 16.5;\n const gridHeight = 9;\n const hexagonsInWidth = 20;\n const hexagonsInHeight = 20;\n\n // Control point for distance calculation\n const controlPointX = 2.2;\n const controlPointZ = 2.8;\n const controlPoint: Point3 = [controlPointX, 0, controlPointZ];\n\n // Generate hex grid to get center points\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: HexGridData = bitbybit.point.hexGridScaledToFit(hexGridOptions);\n const hexCenters = hexGrid.centers;\n\n // Calculate distances from control point to all hex centers\n const distanceOptions = new StartEndPointsListDto();\n distanceOptions.startPoint = controlPoint;\n distanceOptions.endPoints = hexCenters;\n\n const distances = bitbybit.point.distancesToPoints(distanceOptions);\n\n // Find min and max distances for remapping\n const minDistance = bitbybit.vector.min({ vector: distances });\n const maxDistance = bitbybit.vector.max({ vector: distances });\n\n // Remap distances to scale values between 0.111 and 0.999\n const scalePattern: number[] = [];\n\n for (const distance of distances) {\n const remapOptions = new RemapNumberDto();\n remapOptions.number = distance;\n remapOptions.fromLow = minDistance;\n remapOptions.fromHigh = maxDistance;\n remapOptions.toLow = 0.111;\n remapOptions.toHigh = 0.999;\n\n const scaledValue = bitbybit.math.remap(remapOptions);\n scalePattern.push(scaledValue);\n }\n\n // Create hexagonal grid with patterns\n const hexGridPatternOptions = new HexagonsInGridDto();\n hexGridPatternOptions.width = gridWidth;\n hexGridPatternOptions.height = gridHeight;\n hexGridPatternOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridPatternOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridPatternOptions.flatTop = true;\n hexGridPatternOptions.extendTop = false;\n hexGridPatternOptions.extendBottom = false;\n hexGridPatternOptions.extendLeft = false;\n hexGridPatternOptions.extendRight = false;\n\n // Apply the distance-based scale pattern to both width and height\n hexGridPatternOptions.scalePatternWidth = scalePattern;\n hexGridPatternOptions.scalePatternHeight = scalePattern;\n\n // Apply inclusion pattern to selectively remove some hexagons\n const inclusionPattern = [true, true, false]; // Pattern repeats across the grid\n hexGridPatternOptions.inclusionPattern = inclusionPattern;\n\n // Generate the patterned hexagon grid\n const hexagonWires = await bitbybit.occt.shapes.wire.hexagonsInGrid(hexGridPatternOptions);\n\n // Create a boundary rectangle for reference\n const boundaryOptions = new RectangleDto();\n boundaryOptions.width = gridWidth;\n boundaryOptions.length = gridHeight;\n boundaryOptions.center = [0, 0, 0] as Point3;\n boundaryOptions.direction = [0, 1, 0] as Vector3;\n\n const boundaryWire = await bitbybit.occt.shapes.wire.createRectangleWire(boundaryOptions);\n\n // Draw the patterned hexagon grid and boundary\n bitbybit.draw.drawAnyAsync({ entity: hexagonWires });\n bitbybit.draw.drawAnyAsync({ entity: boundaryWire });\n\n // Optionally draw the control point for reference\n bitbybit.draw.drawAnyAsync({ entity: controlPoint });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { HexagonsInGridDto, RectangleDto } = Bit.Inputs.OCCT;\nconst { HexGridScaledToFitDto, StartEndPointsListDto } = Bit.Inputs.Point;\nconst { RemapNumberDto } = Bit.Inputs.Math;\n// Import required types\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype HexGridData = Bit.Models.Point.HexGridData;\n\n// Define the main function\nconst start = async () => {\n // Define grid dimensions\n const gridWidth = 16.5;\n const gridHeight = 9;\n const hexagonsInWidth = 20;\n const hexagonsInHeight = 20;\n\n // Control point for distance calculation\n const controlPointX = 2.2;\n const controlPointZ = 2.8;\n const controlPoint: Point3 = [controlPointX, 0, controlPointZ];\n\n // Generate hex grid to get center points\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: HexGridData = bitbybit.point.hexGridScaledToFit(hexGridOptions);\n const hexCenters = hexGrid.centers;\n\n // Calculate distances from control point to all hex centers\n const distanceOptions = new StartEndPointsListDto();\n distanceOptions.startPoint = controlPoint;\n distanceOptions.endPoints = hexCenters;\n\n const distances = bitbybit.point.distancesToPoints(distanceOptions);\n\n // Find min and max distances for remapping\n const minDistance = bitbybit.vector.min({ vector: distances });\n const maxDistance = bitbybit.vector.max({ vector: distances });\n\n // Remap distances to scale values between 0.111 and 0.999\n const scalePattern: number[] = [];\n\n for (const distance of distances) {\n const remapOptions = new RemapNumberDto();\n remapOptions.number = distance;\n remapOptions.fromLow = minDistance;\n remapOptions.fromHigh = maxDistance;\n remapOptions.toLow = 0.111;\n remapOptions.toHigh = 0.999;\n\n const scaledValue = bitbybit.math.remap(remapOptions);\n scalePattern.push(scaledValue);\n }\n\n // Create hexagonal grid with patterns\n const hexGridPatternOptions = new HexagonsInGridDto();\n hexGridPatternOptions.width = gridWidth;\n hexGridPatternOptions.height = gridHeight;\n hexGridPatternOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridPatternOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridPatternOptions.flatTop = true;\n hexGridPatternOptions.extendTop = false;\n hexGridPatternOptions.extendBottom = false;\n hexGridPatternOptions.extendLeft = false;\n hexGridPatternOptions.extendRight = false;\n\n // Apply the distance-based scale pattern to both width and height\n hexGridPatternOptions.scalePatternWidth = scalePattern;\n hexGridPatternOptions.scalePatternHeight = scalePattern;\n\n // Apply inclusion pattern to selectively remove some hexagons\n const inclusionPattern = [true, true, false]; // Pattern repeats across the grid\n hexGridPatternOptions.inclusionPattern = inclusionPattern;\n\n // Generate the patterned hexagon grid\n const hexagonWires = await bitbybit.occt.shapes.wire.hexagonsInGrid(hexGridPatternOptions);\n\n // Create a boundary rectangle for reference\n const boundaryOptions = new RectangleDto();\n boundaryOptions.width = gridWidth;\n boundaryOptions.length = gridHeight;\n boundaryOptions.center = [0, 0, 0] as Point3;\n boundaryOptions.direction = [0, 1, 0] as Vector3;\n\n const boundaryWire = await bitbybit.occt.shapes.wire.createRectangleWire(boundaryOptions);\n\n // Draw the patterned hexagon grid and boundary\n bitbybit.draw.drawAnyAsync({ entity: hexagonWires });\n bitbybit.draw.drawAnyAsync({ entity: boundaryWire });\n\n // Optionally draw the control point for reference\n bitbybit.draw.drawAnyAsync({ entity: controlPoint });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating hexagon grids" /> 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 ff5b9018..3fb8c7e4 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 @@ -50,21 +50,21 @@ The component also includes advanced patterning capabilities through optional ar widthheightwidth16.5height9widthheight2318FALSEFALSEFALSEFALSEFALSEwidthheight000010","version":"0.20.13","type":"blockly"}} + script={{"script":"widthheightwidth16.5height9widthheight2318FALSEFALSEFALSEFALSEFALSEwidthheight000010","version":"0.20.14","type":"blockly"}} title="Creating hexagon grids" /> {\n // Define grid dimensions\n const gridWidth = 16.5;\n const gridHeight = 9;\n const hexagonsInWidth = 23;\n const hexagonsInHeight = 18;\n\n // Create hexagonal grid parameters\n const hexGridOptions = new HexagonsInGridDto();\n hexGridOptions.width = gridWidth;\n hexGridOptions.height = gridHeight;\n hexGridOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridOptions.flatTop = false; // Pointed-top hexagons\n hexGridOptions.extendTop = false;\n hexGridOptions.extendBottom = false;\n hexGridOptions.extendLeft = false;\n hexGridOptions.extendRight = false;\n\n // Generate the hexagon grid\n const hexagonWires = await bitbybit.occt.shapes.wire.hexagonsInGrid(hexGridOptions);\n\n // Create a boundary rectangle for reference\n const boundaryOptions = new RectangleDto();\n boundaryOptions.width = gridWidth;\n boundaryOptions.length = gridHeight;\n boundaryOptions.center = [0, 0, 0] as Point3;\n boundaryOptions.direction = [0, 1, 0] as Vector3;\n\n const boundaryWire = await bitbybit.occt.shapes.wire.createRectangleWire(boundaryOptions);\n\n // Draw the hexagon grid and boundary\n bitbybit.draw.drawAnyAsync({ entity: hexagonWires });\n bitbybit.draw.drawAnyAsync({ entity: boundaryWire });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"const { HexagonsInGridDto, RectangleDto } = Bit.Inputs.OCCT;\n// Import required types\ntype TopoDSWirePointer = Bit.Inputs.OCCT.TopoDSWirePointer;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Define the main function\nconst start = async () => {\n // Define grid dimensions\n const gridWidth = 16.5;\n const gridHeight = 9;\n const hexagonsInWidth = 23;\n const hexagonsInHeight = 18;\n\n // Create hexagonal grid parameters\n const hexGridOptions = new HexagonsInGridDto();\n hexGridOptions.width = gridWidth;\n hexGridOptions.height = gridHeight;\n hexGridOptions.nrHexagonsInWidth = hexagonsInWidth;\n hexGridOptions.nrHexagonsInHeight = hexagonsInHeight;\n hexGridOptions.flatTop = false; // Pointed-top hexagons\n hexGridOptions.extendTop = false;\n hexGridOptions.extendBottom = false;\n hexGridOptions.extendLeft = false;\n hexGridOptions.extendRight = false;\n\n // Generate the hexagon grid\n const hexagonWires = await bitbybit.occt.shapes.wire.hexagonsInGrid(hexGridOptions);\n\n // Create a boundary rectangle for reference\n const boundaryOptions = new RectangleDto();\n boundaryOptions.width = gridWidth;\n boundaryOptions.length = gridHeight;\n boundaryOptions.center = [0, 0, 0] as Point3;\n boundaryOptions.direction = [0, 1, 0] as Vector3;\n\n const boundaryWire = await bitbybit.occt.shapes.wire.createRectangleWire(boundaryOptions);\n\n // Draw the hexagon grid and boundary\n bitbybit.draw.drawAnyAsync({ entity: hexagonWires });\n bitbybit.draw.drawAnyAsync({ entity: boundaryWire });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating hexagon grids" /> diff --git a/docs/learn/code/common/occt/shapes/wire/wire-shape-primitives.md b/docs/learn/code/common/occt/shapes/wire/wire-shape-primitives.md index 578c10b2..18192ed2 100644 --- a/docs/learn/code/common/occt/shapes/wire/wire-shape-primitives.md +++ b/docs/learn/code/common/occt/shapes/wire/wire-shape-primitives.md @@ -50,21 +50,21 @@ The star wire creates beautiful star-shaped patterns with customizable rays, rad starWirestarWire000010107.83.70FALSEstarWire","version":"0.20.13","type":"blockly"}} + script={{"script":"starWirestarWire000010107.83.70FALSEstarWire","version":"0.20.14","type":"blockly"}} title="Creating basic wire star primitive" /> {\n // Create a star wire with customizable parameters\n const starOptions = new StarDto();\n starOptions.center = [0, 0, 0] as Point3;\n starOptions.direction = [0, 1, 0] as Vector3;\n starOptions.numRays = 10;\n starOptions.outerRadius = 7.8;\n starOptions.innerRadius = 3.7;\n starOptions.offsetOuterEdges = 0;\n starOptions.half = false;\n\n const starWire = await wire.createStarWire(starOptions);\n\n // Draw the created star wire\n bitbybit.draw.drawAnyAsync({ entity: starWire });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for star wire creation\nconst { StarDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT wire creation functions\nconst { wire } = bitbybit.occt.shapes;\n\n// Define the main function to create a star wire\nconst start = async () => {\n // Create a star wire with customizable parameters\n const starOptions = new StarDto();\n starOptions.center = [0, 0, 0] as Point3;\n starOptions.direction = [0, 1, 0] as Vector3;\n starOptions.numRays = 10;\n starOptions.outerRadius = 7.8;\n starOptions.innerRadius = 3.7;\n starOptions.offsetOuterEdges = 0;\n starOptions.half = false;\n\n const starWire = await wire.createStarWire(starOptions);\n\n // Draw the created star wire\n bitbybit.draw.drawAnyAsync({ entity: starWire });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating basic wire star primitive" /> @@ -86,21 +86,21 @@ The Christmas tree wire creates detailed tree-like structures perfect for season christmasTreeWirechristmasTreeWire61.53511FALSE0000010christmasTreeWire","version":"0.20.13","type":"blockly"}} + script={{"script":"christmasTreeWirechristmasTreeWire61.53511FALSE0000010christmasTreeWire","version":"0.20.14","type":"blockly"}} title="Creating basic wire primitive of christmass tree" /> {\n // Create a Christmas tree wire with customizable parameters\n const treeOptions = new ChristmasTreeDto();\n treeOptions.height = 6;\n treeOptions.innerDist = 1.5;\n treeOptions.outerDist = 3;\n treeOptions.nrSkirts = 5;\n treeOptions.trunkHeight = 1;\n treeOptions.trunkWidth = 1;\n treeOptions.half = false;\n treeOptions.rotation = 0;\n treeOptions.origin = [0, 0, 0] as Point3;\n treeOptions.direction = [0, 1, 0] as Vector3;\n\n const christmasTreeWire = await wire.createChristmasTreeWire(treeOptions);\n\n // Draw the created Christmas tree wire\n bitbybit.draw.drawAnyAsync({ entity: christmasTreeWire });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for Christmas tree wire creation\nconst { ChristmasTreeDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT wire creation functions\nconst { wire } = bitbybit.occt.shapes;\n\n// Define the main function to create a Christmas tree wire\nconst start = async () => {\n // Create a Christmas tree wire with customizable parameters\n const treeOptions = new ChristmasTreeDto();\n treeOptions.height = 6;\n treeOptions.innerDist = 1.5;\n treeOptions.outerDist = 3;\n treeOptions.nrSkirts = 5;\n treeOptions.trunkHeight = 1;\n treeOptions.trunkWidth = 1;\n treeOptions.half = false;\n treeOptions.rotation = 0;\n treeOptions.origin = [0, 0, 0] as Point3;\n treeOptions.direction = [0, 1, 0] as Vector3;\n\n const christmasTreeWire = await wire.createChristmasTreeWire(treeOptions);\n\n // Draw the created Christmas tree wire\n bitbybit.draw.drawAnyAsync({ entity: christmasTreeWire });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating basic wire primitive of christmass tree" /> @@ -122,21 +122,21 @@ The heart wire creates elegant heart-shaped curves, perfect for decorative appli heartWireheartWire00001008heartWire","version":"0.20.13","type":"blockly"}} + script={{"script":"heartWireheartWire00001008heartWire","version":"0.20.14","type":"blockly"}} title="Creating basic wire primitive of heart" /> {\n // Create a heart wire with customizable parameters\n const heartOptions = new Heart2DDto();\n heartOptions.center = [0, 0, 0] as Point3;\n heartOptions.direction = [0, 1, 0] as Vector3;\n heartOptions.rotation = 0;\n heartOptions.sizeApprox = 8;\n\n const heartWire = await wire.createHeartWire(heartOptions);\n\n // Draw the created heart wire\n bitbybit.draw.drawAnyAsync({ entity: heartWire });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for heart wire creation\nconst { Heart2DDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT wire creation functions\nconst { wire } = bitbybit.occt.shapes;\n\n// Define the main function to create a heart wire\nconst start = async () => {\n // Create a heart wire with customizable parameters\n const heartOptions = new Heart2DDto();\n heartOptions.center = [0, 0, 0] as Point3;\n heartOptions.direction = [0, 1, 0] as Vector3;\n heartOptions.rotation = 0;\n heartOptions.sizeApprox = 8;\n\n const heartWire = await wire.createHeartWire(heartOptions);\n\n // Draw the created heart wire\n bitbybit.draw.drawAnyAsync({ entity: heartWire });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating basic wire primitive of heart" /> @@ -156,21 +156,21 @@ The L-polygon wire creates precise L-shaped structures essential for architectur lPolygonWirelPolygonWire3537'outside'0000010lPolygonWire","version":"0.20.13","type":"blockly"}} + script={{"script":"lPolygonWirelPolygonWire3537'outside'0000010lPolygonWire","version":"0.20.14","type":"blockly"}} title="Creating basic wire primitive of L polygon" /> {\n // Create an L polygon wire with customizable parameters\n const lPolygonOptions = new LPolygonDto();\n lPolygonOptions.widthFirst = 3;\n lPolygonOptions.lengthFirst = 5;\n lPolygonOptions.widthSecond = 3;\n lPolygonOptions.lengthSecond = 7;\n lPolygonOptions.align = directionEnum.outside;\n lPolygonOptions.rotation = 0;\n lPolygonOptions.center = [0, 0, 0] as Point3;\n lPolygonOptions.direction = [0, 1, 0] as Vector3;\n\n const lPolygonWire = await wire.createLPolygonWire(lPolygonOptions);\n\n // Draw the created L polygon wire\n bitbybit.draw.drawAnyAsync({ entity: lPolygonWire });\n}\n\n// Execute the function\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for L polygon wire creation\nconst { LPolygonDto, directionEnum } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\n\n// Get access to OCCT wire creation functions\nconst { wire } = bitbybit.occt.shapes;\n\n// Define the main function to create an L polygon wire\nconst start = async () => {\n // Create an L polygon wire with customizable parameters\n const lPolygonOptions = new LPolygonDto();\n lPolygonOptions.widthFirst = 3;\n lPolygonOptions.lengthFirst = 5;\n lPolygonOptions.widthSecond = 3;\n lPolygonOptions.lengthSecond = 7;\n lPolygonOptions.align = directionEnum.outside;\n lPolygonOptions.rotation = 0;\n lPolygonOptions.center = [0, 0, 0] as Point3;\n lPolygonOptions.direction = [0, 1, 0] as Vector3;\n\n const lPolygonWire = await wire.createLPolygonWire(lPolygonOptions);\n\n // Draw the created L polygon wire\n bitbybit.draw.drawAnyAsync({ entity: lPolygonWire });\n}\n\n// Execute the function\nstart();","version":"0.20.14","type":"typescript"}} title="Creating basic wire primitive of L polygon" /> 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 index 406833fe..dfa37262 100644 --- a/docs/learn/code/common/occt/shapes/wire/wire-via-points.md +++ b/docs/learn/code/common/occt/shapes/wire/wire-via-points.md @@ -75,21 +75,21 @@ This example demonstrates all major methods for creating wires from points, show point1point2point3point4point5pointsListlineWirepolylineWirepolygonWireinterpolatedWireinterpolatedPeriodicWirebezierWirebsplineWirepoint1-400point2-201point300-1point4202point5400pointsListpoint1point2point3point4point5lineWirepoint1point5polylineWirepointsList010polygonWirepointsList020interpolatedWirepointsListFALSE1e-7030interpolatedPeriodicWirepointsListTRUE1e-7040bezierWirepointsList050bsplineWirepointsListFALSE3060lineWirepolylineWirepolygonWireinterpolatedWireinterpolatedPeriodicWirebezierWirebsplineWire","version":"0.20.13","type":"blockly"}} + script={{"script":"point1point2point3point4point5pointsListlineWirepolylineWirepolygonWireinterpolatedWireinterpolatedPeriodicWirebezierWirebsplineWirepoint1-400point2-201point300-1point4202point5400pointsListpoint1point2point3point4point5lineWirepoint1point5polylineWirepointsList010polygonWirepointsList020interpolatedWirepointsListFALSE1e-7030interpolatedPeriodicWirepointsListTRUE1e-7040bezierWirepointsList050bsplineWirepointsListFALSE3060lineWirepolylineWirepolygonWireinterpolatedWireinterpolatedPeriodicWirebezierWirebsplineWire","version":"0.20.14","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.13","type":"typescript"}} + script={{"script":"// Import required DTOs and types for wire creation\nconst { LineDto, PolylineDto, PolygonDto, InterpolationDto, BezierDto, BSplineDto, TranslateDto } = Bit.Inputs.OCCT;\ntype Point3 = Bit.Inputs.Base.Point3;\ntype Vector3 = Bit.Inputs.Base.Vector3;\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer;\n\n// Get access to OCCT wire creation and transform functions\nconst { wire } = bitbybit.occt.shapes;\nconst { transforms } = bitbybit.occt;\n\n// Define the main function to demonstrate various wire creation methods\nconst start = async () => {\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.14","type":"typescript"}} title="Comprehensive wire creation from points" /> diff --git a/docs/learn/getting-started/basics/assets/local/gltf.mdx b/docs/learn/getting-started/basics/assets/local/gltf.mdx index fc2f6cd0..cc657268 100644 --- a/docs/learn/getting-started/basics/assets/local/gltf.mdx +++ b/docs/learn/getting-started/basics/assets/local/gltf.mdx @@ -59,7 +59,7 @@ Your Rete graph should now look similar to the setup in the embedded editor belo **Rete Editor Example:** @@ -101,7 +101,7 @@ After assembling the blocks, click "Run". You should see the BoomBox model. **Blockly Editor Example:** TRUEBoomBoxFALSE","version":"0.20.13","type":"blockly"}} + script={{"script":"TRUEBoomBoxFALSE","version":"0.20.14","type":"blockly"}} title="Bitbybit Blockly Editor - Using Local glTF Asset" description="Upload local asset from tutorial named accordingly and it will appear in 3D scene after you hit run." /> @@ -126,7 +126,7 @@ Here's the example code, which should be fairly self-explanatory for those famil **TypeScript Editor Example:** {\n bitbybit.babylon.scene.useRightHandedSystem({\n use: true,\n });\n const file = await bitbybit.asset.getLocalFile({\n fileName: \"BoomBox\"\n }) as File;\n await bitbybit.babylon.io.loadAssetIntoScene({\n assetFile: file,\n hidden: false\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n bitbybit.babylon.scene.useRightHandedSystem({\n use: true,\n });\n const file = await bitbybit.asset.getLocalFile({\n fileName: \"BoomBox\"\n }) as File;\n await bitbybit.babylon.io.loadAssetIntoScene({\n assetFile: file,\n hidden: false\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Bitbybit Blockly Editor - Using Local glTF Asset" description="Upload local asset from tutorial named accordingly and it will appear in 3D scene after you hit run." /> diff --git a/docs/learn/getting-started/basics/assets/local/step.mdx b/docs/learn/getting-started/basics/assets/local/step.mdx index 87a7337b..5bfc0080 100644 --- a/docs/learn/getting-started/basics/assets/local/step.mdx +++ b/docs/learn/getting-started/basics/assets/local/step.mdx @@ -54,7 +54,7 @@ That's it! Your Rete graph should be set up. It might take a moment for the Kuka **Rete Editor Example:** @@ -95,7 +95,7 @@ After assembling the blocks, click "Run". **Blockly Editor Example:** KukaRobotTRUE","version":"0.20.13","type":"blockly"}} + script={{"script":"KukaRobotTRUE","version":"0.20.14","type":"blockly"}} title="Bitbybit Blockly Editor - Using Local STEP Asset" description="Upload local asset from tutorial named accordingly and it will appear in 3D scene after you hit run." /> @@ -122,7 +122,7 @@ Here's an example of how to do that: **TypeScript Editor Example:** {\n\n const file = await bitbybit.asset.getLocalFile({\n fileName: \"KukaRobot\"\n }) as File;\n\n const shape = await bitbybit.occt.io.loadSTEPorIGES({\n assetFile: file,\n adjustZtoY: true,\n });\n\n bitbybit.draw.drawAnyAsync({\n entity: shape\n })\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n\n const file = await bitbybit.asset.getLocalFile({\n fileName: \"KukaRobot\"\n }) as File;\n\n const shape = await bitbybit.occt.io.loadSTEPorIGES({\n assetFile: file,\n adjustZtoY: true,\n });\n\n bitbybit.draw.drawAnyAsync({\n entity: shape\n })\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Bitbybit TypeScript Editor - Using Local STEP Asset" description="Upload local asset from tutorial named accordingly and it will appear in 3D scene after you hit run." /> diff --git a/docs/learn/getting-started/blockly/hello-world.mdx b/docs/learn/getting-started/blockly/hello-world.mdx index acf9e491..2109d89f 100644 --- a/docs/learn/getting-started/blockly/hello-world.mdx +++ b/docs/learn/getting-started/blockly/hello-world.mdx @@ -56,7 +56,7 @@ Your setup should resemble this interactive example: Hello World!'Aboreto''Regular'1.50.20000010'leftTop'","version":"0.20.13","type":"blockly"}} + script={{"script":"Hello World!'Aboreto''Regular'1.50.20000010'leftTop'","version":"0.20.14","type":"blockly"}} title="Draw 3D Text" description="Draws the text on the screen." /> @@ -88,7 +88,7 @@ After completing these steps, your Blockly script should look similar to this: 40040010100.450.50.5FALSE#ffffff#ffffffHello World!'Roboto''Regular'1.50.2-9000000-1'centerBottom'","version":"0.20.13","type":"blockly"}} + script={{"script":"40040010100.450.50.5FALSE#ffffff#ffffffHello World!'Roboto''Regular'1.50.2-9000000-1'centerBottom'","version":"0.20.14","type":"blockly"}} title="Draw 3D text and grid" description="Draws 3D text and the grid on the screen. Text is placed in better orientation." /> diff --git a/docs/learn/getting-started/blockly/parametric-cube.mdx b/docs/learn/getting-started/blockly/parametric-cube.mdx index 0677b0a4..f11d2f7b 100644 --- a/docs/learn/getting-started/blockly/parametric-cube.mdx +++ b/docs/learn/getting-started/blockly/parametric-cube.mdx @@ -69,7 +69,7 @@ Your Blockly workspace should now look something like this interactive example: sizesize3size000","version":"0.20.13","type":"blockly"}} + script={{"script":"sizesize3size000","version":"0.20.14","type":"blockly"}} title="Parametric cube example" description="Draws the parametrically controlled cube." /> diff --git a/docs/learn/getting-started/rete/hello-world.mdx b/docs/learn/getting-started/rete/hello-world.mdx index a6b9cb49..2cbcf06b 100644 --- a/docs/learn/getting-started/rete/hello-world.mdx +++ b/docs/learn/getting-started/rete/hello-world.mdx @@ -41,7 +41,7 @@ If you've done it correctly, your canvas should have the "Draw Grid Mesh" compon @@ -79,7 +79,7 @@ The setup should resemble this: @@ -109,7 +109,7 @@ Here's the final result you should aim for: diff --git a/docs/learn/getting-started/rete/parametric-cube.mdx b/docs/learn/getting-started/rete/parametric-cube.mdx index a4dc750b..be8dbf57 100644 --- a/docs/learn/getting-started/rete/parametric-cube.mdx +++ b/docs/learn/getting-started/rete/parametric-cube.mdx @@ -44,7 +44,7 @@ We'll use the OpenCascade Technology (OCCT) geometry kernel to create our cube. @@ -69,7 +69,7 @@ If you've connected them correctly, your setup should resemble the following int diff --git a/docs/learn/getting-started/typescript/hello-world.mdx b/docs/learn/getting-started/typescript/hello-world.mdx index 43bff6c0..1b9a58b8 100644 --- a/docs/learn/getting-started/typescript/hello-world.mdx +++ b/docs/learn/getting-started/typescript/hello-world.mdx @@ -26,7 +26,7 @@ Check out the script below: {\n const gridOptions = new Bit.Inputs.Draw.SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const gridOptions = new Bit.Inputs.Draw.SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Draw the grid" description="Draws the grid mesh with lines in 3D space." /> @@ -51,7 +51,7 @@ The script below shows a working example. We've set the `rotation` to -90 degree {\n const gridOptions = new Bit.Inputs.Draw.SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n\n const textOpt = new Bit.Advanced.Text3D.Text3DDto();\n textOpt.text = \"Hello World!\";\n textOpt.rotation = -90;\n textOpt.originAlignment = Bit.Advanced.Text3D.recAlignmentEnum.centerBottom;\n textOpt.direction = [0, 0, -1];\n const text3D = await bitbybit.advanced.text3d.create(textOpt);\n\n bitbybit.draw.drawAnyAsync({\n entity: text3D\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const gridOptions = new Bit.Inputs.Draw.SceneDrawGridMeshDto();\n bitbybit.draw.drawGridMesh(gridOptions);\n\n const textOpt = new Bit.Advanced.Text3D.Text3DDto();\n textOpt.text = \"Hello World!\";\n textOpt.rotation = -90;\n textOpt.originAlignment = Bit.Advanced.Text3D.recAlignmentEnum.centerBottom;\n textOpt.direction = [0, 0, -1];\n const text3D = await bitbybit.advanced.text3d.create(textOpt);\n\n bitbybit.draw.drawAnyAsync({\n entity: text3D\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Draw the text & grid" description="Draws the grid mesh with text in 3D space." /> diff --git a/docs/learn/getting-started/typescript/how-to-code-in-monaco.md b/docs/learn/getting-started/typescript/how-to-code-in-monaco.md index 800d3977..5a00e009 100644 --- a/docs/learn/getting-started/typescript/how-to-code-in-monaco.md +++ b/docs/learn/getting-started/typescript/how-to-code-in-monaco.md @@ -197,7 +197,7 @@ start(); {\n // Step 3a: Create input objects (DTOs) and set their properties for a cube\n const cubeOptions = new CubeDto(); // Instantiate the DTO\n cubeOptions.size = 6; // Set the cube's size\n // Call the bitbybit function to create the cube, awaiting its promise\n const cube: TopoDSShapePointer = await solid.createCube(cubeOptions);\n\n // Step 3b: Create input objects (DTOs) for a sphere\n const sphereOptions = new SphereDto();\n sphereOptions.radius = 3;\n sphereOptions.center = [3, 3, -3]; // Define center as [x, y, z] coordinates\n const sphere: TopoDSShapePointer = await solid.createSphere(sphereOptions);\n\n // Step 4: Perform geometric operations\n // Example: Boolean difference (subtract sphere from cube)\n const diffOptions = new DifferenceDto(); // Generic type for the shapes involved\n diffOptions.shape = cube; // The base shape\n diffOptions.shapes = [sphere]; // An array of shapes to subtract\n const diff: TopoDSShapePointer = await booleans.difference(diffOptions);\n\n // Example: Apply fillets (round edges) to the result of the difference\n const roundingOptions = new FilletDto();\n roundingOptions.shape = diff; // The shape to fillet\n roundingOptions.radius = 1; // The radius of the fillet\n // Note: Some operations might have specific methods like 'filletEdges' for common tasks\n const solidRoundedCorners: TopoDSShapePointer = await fillets.filletEdges(roundingOptions);\n\n // Step 5: Visualize the result in the 3D viewer\n // Prepare drawing options to customize appearance\n const occtDrawOptions = new DrawOcctShapeOptions();\n occtDrawOptions.faceColour = \"#0000ff\"; // Blue faces\n occtDrawOptions.edgeColour = \"#ff00ff\"; // Magenta edges\n occtDrawOptions.edgeWidth = 5; // Width of the edges\n occtDrawOptions.precision = 0.001; // Rendering precision for complex shapes (lower is finer)\n // Draw the final shape. 'drawAnyAsync' is a versatile function for drawing various entity types.\n draw.drawAnyAsync({ entity: solidRoundedCorners, options: occtDrawOptions });\n\n // Step 6: (Optional) Adjust scene elements like lighting for better visualization\n const dirLight = new DirectionalLightDto();\n dirLight.shadowGeneratorMapSize = 2000; // Higher values for better shadow quality\n dirLight.intensity = 3; // Light intensity\n scene.drawDirectionalLight(dirLight); // Adds or updates a directional light in the scene\n\n // Step 7: (Optional) Export your model to common CAD file formats\n // Export as STEP file (a common format for solid models)\n const stepExportOptions = new SaveStepDto();\n stepExportOptions.shape = solidRoundedCorners;\n stepExportOptions.adjustYtoZ = true; // Optional: Adjusts coordinate system (Y-up to Z-up) if needed\n stepExportOptions.fileName = \"cube_with_sphere_cutout.step\";\n stepExportOptions.tryDownload = true; // Attempts to trigger a browser download of the file\n await io.saveShapeSTEP(stepExportOptions); // Use the destructured 'io'\n\n // Export as STL file (a common format for 3D printing)\n const stlExportOptions = new SaveStlDto();\n stlExportOptions.shape = solidRoundedCorners;\n stlExportOptions.adjustYtoZ = true;\n stlExportOptions.fileName = \"cube_with_sphere_cutout.stl\";\n stlExportOptions.precision = 0.001; // Affects STL mesh quality (smaller values for finer mesh)\n stlExportOptions.tryDownload = true;\n await io.saveShapeStl(stlExportOptions); // Use the destructured 'io'\n};\n\n// Step 8: Call the start function to execute your script\nstart();","version":"0.20.13","type":"typescript"}} + script={{"script":"// Step 1: (Optional but Recommended) Destructure for convenience\n// This makes your code less verbose and easier to read.\nconst { solid } = bitbybit.occt.shapes;\nconst { booleans, fillets, io } = bitbybit.occt; // Added 'io' for export functions\nconst { draw } = bitbybit;\nconst { scene } = bitbybit.babylon;\n\n// Step 2: Import type definitions for input objects and shapes\n// This enables type checking and autocompletion.\ntype TopoDSShapePointer = Bit.Inputs.OCCT.TopoDSShapePointer; // Represents an OCCT shape\n\nconst { CubeDto, SphereDto, FilletDto, DifferenceDto, SaveStepDto, SaveStlDto } = Bit.Inputs.OCCT;\nconst DrawOcctShapeOptions = Bit.Inputs.Draw.DrawOcctShapeOptions;\nconst DirectionalLightDto = Bit.Inputs.BabylonScene.DirectionalLightDto;\n\n// Step 3: Define your main logic within an async function\nconst start = async () => {\n // Step 3a: Create input objects (DTOs) and set their properties for a cube\n const cubeOptions = new CubeDto(); // Instantiate the DTO\n cubeOptions.size = 6; // Set the cube's size\n // Call the bitbybit function to create the cube, awaiting its promise\n const cube: TopoDSShapePointer = await solid.createCube(cubeOptions);\n\n // Step 3b: Create input objects (DTOs) for a sphere\n const sphereOptions = new SphereDto();\n sphereOptions.radius = 3;\n sphereOptions.center = [3, 3, -3]; // Define center as [x, y, z] coordinates\n const sphere: TopoDSShapePointer = await solid.createSphere(sphereOptions);\n\n // Step 4: Perform geometric operations\n // Example: Boolean difference (subtract sphere from cube)\n const diffOptions = new DifferenceDto(); // Generic type for the shapes involved\n diffOptions.shape = cube; // The base shape\n diffOptions.shapes = [sphere]; // An array of shapes to subtract\n const diff: TopoDSShapePointer = await booleans.difference(diffOptions);\n\n // Example: Apply fillets (round edges) to the result of the difference\n const roundingOptions = new FilletDto();\n roundingOptions.shape = diff; // The shape to fillet\n roundingOptions.radius = 1; // The radius of the fillet\n // Note: Some operations might have specific methods like 'filletEdges' for common tasks\n const solidRoundedCorners: TopoDSShapePointer = await fillets.filletEdges(roundingOptions);\n\n // Step 5: Visualize the result in the 3D viewer\n // Prepare drawing options to customize appearance\n const occtDrawOptions = new DrawOcctShapeOptions();\n occtDrawOptions.faceColour = \"#0000ff\"; // Blue faces\n occtDrawOptions.edgeColour = \"#ff00ff\"; // Magenta edges\n occtDrawOptions.edgeWidth = 5; // Width of the edges\n occtDrawOptions.precision = 0.001; // Rendering precision for complex shapes (lower is finer)\n // Draw the final shape. 'drawAnyAsync' is a versatile function for drawing various entity types.\n draw.drawAnyAsync({ entity: solidRoundedCorners, options: occtDrawOptions });\n\n // Step 6: (Optional) Adjust scene elements like lighting for better visualization\n const dirLight = new DirectionalLightDto();\n dirLight.shadowGeneratorMapSize = 2000; // Higher values for better shadow quality\n dirLight.intensity = 3; // Light intensity\n scene.drawDirectionalLight(dirLight); // Adds or updates a directional light in the scene\n\n // Step 7: (Optional) Export your model to common CAD file formats\n // Export as STEP file (a common format for solid models)\n const stepExportOptions = new SaveStepDto();\n stepExportOptions.shape = solidRoundedCorners;\n stepExportOptions.adjustYtoZ = true; // Optional: Adjusts coordinate system (Y-up to Z-up) if needed\n stepExportOptions.fileName = \"cube_with_sphere_cutout.step\";\n stepExportOptions.tryDownload = true; // Attempts to trigger a browser download of the file\n await io.saveShapeSTEP(stepExportOptions); // Use the destructured 'io'\n\n // Export as STL file (a common format for 3D printing)\n const stlExportOptions = new SaveStlDto();\n stlExportOptions.shape = solidRoundedCorners;\n stlExportOptions.adjustYtoZ = true;\n stlExportOptions.fileName = \"cube_with_sphere_cutout.stl\";\n stlExportOptions.precision = 0.001; // Affects STL mesh quality (smaller values for finer mesh)\n stlExportOptions.tryDownload = true;\n await io.saveShapeStl(stlExportOptions); // Use the destructured 'io'\n};\n\n// Step 8: Call the start function to execute your script\nstart();","version":"0.20.14","type":"typescript"}} title="Create And Download STEP & STL 3D Models" description="Contains example code that can be executed directly inside the editor by clicking Run button." /> diff --git a/docs/learn/getting-started/typescript/parametric-cube.mdx b/docs/learn/getting-started/typescript/parametric-cube.mdx index 58fb4038..0fa455cf 100644 --- a/docs/learn/getting-started/typescript/parametric-cube.mdx +++ b/docs/learn/getting-started/typescript/parametric-cube.mdx @@ -35,7 +35,7 @@ The script shown in the editor below is fairly straightforward, but let's break {\n const size = 10;\n const cubeOptions = new Bit.Inputs.OCCT.CubeDto();\n cubeOptions.size = size;\n const cube = await bitbybit.occt.shapes.solid.createCube(cubeOptions);\n\n bitbybit.draw.drawAnyAsync({\n entity: cube\n });\n}\n\nstart();\n","version":"0.20.13","type":"typescript"}} + script={{"script":"const start = async () => {\n const size = 10;\n const cubeOptions = new Bit.Inputs.OCCT.CubeDto();\n cubeOptions.size = size;\n const cube = await bitbybit.occt.shapes.solid.createCube(cubeOptions);\n\n bitbybit.draw.drawAnyAsync({\n entity: cube\n });\n}\n\nstart();\n","version":"0.20.14","type":"typescript"}} title="Draw the grid" description="Draws the grid mesh with lines in 3D space." /> diff --git a/docs/learn/getting-started/viewer-editor/basics/getting-started.md b/docs/learn/getting-started/viewer-editor/basics/getting-started.md index 44f20837..23f7eafd 100644 --- a/docs/learn/getting-started/viewer-editor/basics/getting-started.md +++ b/docs/learn/getting-started/viewer-editor/basics/getting-started.md @@ -139,7 +139,7 @@ When you export, you get JSON like this: ```json { - "$schema": "https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.13.json", + "$schema": "https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.14.json", "models": [ { "name": "Main Product", diff --git a/docs/learn/getting-started/viewer-editor/basics/overview.md b/docs/learn/getting-started/viewer-editor/basics/overview.md index db661821..17f25dfc 100644 --- a/docs/learn/getting-started/viewer-editor/basics/overview.md +++ b/docs/learn/getting-started/viewer-editor/basics/overview.md @@ -78,7 +78,7 @@ For Shopify users, see [Subscription Plans](../../../3d-bits/plans/subscription- ## Generated Output -The Viewer Editor generates valid JSON that conforms to the [**Viewer Scene Schema**](https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.13.json). This JSON can be: +The Viewer Editor generates valid JSON that conforms to the [**Viewer Scene Schema**](https://app-store.bitbybit.dev/files/ecommerce/viewer-editor/viewer-scene-schema-v0.20.14.json). This JSON can be: - Copied to clipboard and pasted into Shopify product metafields - Downloaded as a file and hosted on your CDN diff --git a/docs/learn/npm-packages/babylonjs/start-with-babylon-js.md b/docs/learn/npm-packages/babylonjs/start-with-babylon-js.md index 4f480490..d0a545e6 100644 --- a/docs/learn/npm-packages/babylonjs/start-with-babylon-js.md +++ b/docs/learn/npm-packages/babylonjs/start-with-babylon-js.md @@ -159,7 +159,7 @@ const init = async () => { // This CDN link provides a hosted version. // For production, you might want to host this yourself. locateFile: () => { - return 'https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/wasm/manifold.cc2ddd38.wasm'; + return 'https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/wasm/manifold-3-3-2.wasm'; }, }); wasm.setup(); // Additional setup step for Manifold diff --git a/docs/learn/npm-packages/threejs/start-with-three-js.md b/docs/learn/npm-packages/threejs/start-with-three-js.md index f7cf0d82..23e1af05 100644 --- a/docs/learn/npm-packages/threejs/start-with-three-js.md +++ b/docs/learn/npm-packages/threejs/start-with-three-js.md @@ -159,7 +159,7 @@ const init = async () => { // This CDN link provides a hosted version. // For production, you might want to host this yourself. locateFile: () => { - return 'https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/wasm/manifold.cc2ddd38.wasm'; + return 'https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/wasm/manifold-3-3-2.wasm'; }, }); wasm.setup(); // Additional setup step for Manifold diff --git a/docs/learn/runners/intro-blockly.mdx b/docs/learn/runners/intro-blockly.mdx index 93c710dc..b8126267 100644 --- a/docs/learn/runners/intro-blockly.mdx +++ b/docs/learn/runners/intro-blockly.mdx @@ -48,7 +48,7 @@ The following Bitbybit Blockly script is the visual program we'll be creating. T sizecubeMeshsizesizesizesize1cubeMeshsize0000.40.005TRUE#000099TRUE#ffffff1cubeMeshcubeMesh","version":"0.20.13","type":"blockly"}} + script={{"script":"sizecubeMeshsizesizesizesize1cubeMeshsize0000.40.005TRUE#000099TRUE#ffffff1cubeMeshcubeMesh","version":"0.20.14","type":"blockly"}} title="Bitbybit Blockly Editor - Simple Cube for Runner Tutorial" description="Draws 3D Cube and controls its size via inputs coming from external executing program" /> @@ -68,7 +68,7 @@ Below are the `index.html` and `script.js` files you would use on StackBlitz or - + diff --git a/docs/learn/runners/intro-rete.mdx b/docs/learn/runners/intro-rete.mdx index d27d73c5..76c0bc84 100644 --- a/docs/learn/runners/intro-rete.mdx +++ b/docs/learn/runners/intro-rete.mdx @@ -51,7 +51,7 @@ The following Bitbybit Rete script is the visual program we'll be creating. The diff --git a/docs/learn/runners/intro-typescript.mdx b/docs/learn/runners/intro-typescript.mdx index 30db1276..c43499c3 100644 --- a/docs/learn/runners/intro-typescript.mdx +++ b/docs/learn/runners/intro-typescript.mdx @@ -46,7 +46,7 @@ The following is the Bitbybit TypeScript script we'll be creating. The JavaScrip {\n const cube = await occt.shapes.solid.createCube({\n size: inputs.size,\n center: [0, 0, 0],\n });\n const filletCube = await occt.fillets.filletEdges({\n shape: cube,\n radius: 0.4\n });\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions.faceColour = \"#0000ff\";\n drawOptions.edgeWidth = 1;\n drawOptions.precision = 0.005;\n const cubeMesh = await bitbybit.draw.drawAnyAsync({\n entity: filletCube,\n options: drawOptions,\n });\n return { cubeMesh };\n}\n\nconst runnerOutput = start();\nBit.setBitbybitRunnerResult(runnerOutput);\n","version":"0.20.13","type":"typescript"}} + script={{"script":"type Inputs = {\n size: number;\n}\n\nBit.mockBitbybitRunnerInputs({ size: 1 });\nconst inputs: Inputs = Bit.getBitbybitRunnerInputs();\n\nconst { occt } = bitbybit;\n\nconst start = async () => {\n const cube = await occt.shapes.solid.createCube({\n size: inputs.size,\n center: [0, 0, 0],\n });\n const filletCube = await occt.fillets.filletEdges({\n shape: cube,\n radius: 0.4\n });\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions.faceColour = \"#0000ff\";\n drawOptions.edgeWidth = 1;\n drawOptions.precision = 0.005;\n const cubeMesh = await bitbybit.draw.drawAnyAsync({\n entity: filletCube,\n options: drawOptions,\n });\n return { cubeMesh };\n}\n\nconst runnerOutput = start();\nBit.setBitbybitRunnerResult(runnerOutput);\n","version":"0.20.14","type":"typescript"}} title="Bitbybit TypeScript Editor - Simple Cube for Runner Tutorial" description="Draws 3D Cube and controls its size via inputs coming from external executing program" /> diff --git a/docs/learn/runners/intro.mdx b/docs/learn/runners/intro.mdx index 887c3f97..b83e60ab 100644 --- a/docs/learn/runners/intro.mdx +++ b/docs/learn/runners/intro.mdx @@ -587,8 +587,8 @@ function hideSpinner() { diff --git a/docs/learn/runners/live-examples/configurable-cad-part.mdx b/docs/learn/runners/live-examples/configurable-cad-part.mdx index 854a6d7b..c602104c 100644 --- a/docs/learn/runners/live-examples/configurable-cad-part.mdx +++ b/docs/learn/runners/live-examples/configurable-cad-part.mdx @@ -30,7 +30,7 @@ Below is an interactive preview of the Rete visual program. Notice how this scri diff --git a/docs/learn/runners/table-configurator-blockly.mdx b/docs/learn/runners/table-configurator-blockly.mdx index f4b0c99f..28702d96 100644 --- a/docs/learn/runners/table-configurator-blockly.mdx +++ b/docs/learn/runners/table-configurator-blockly.mdx @@ -46,7 +46,7 @@ The following Bitbybit Blockly script is the visual program that defines the log widthlengthlegHeightheighthalfLegheightthicknesshalfThicknesswidthOffsetlengthOffsetlegShapecompoundShapetable'clearSky'10000.10.7-100-100-1003#ffffff#ffffff1024TRUE0legHeightMINUSheightthicknesshalfLegheightDIVIDElegHeight2halfThicknessDIVIDEthickness2widthOffsetMINUSDIVIDEwidth2halfThicknesslengthOffsetMINUSDIVIDElength2halfThicknesslegShapethicknessthicknesslegHeight000compoundShapewidthlengththickness0MINUSheighthalfThickness0legShapewidthOffsethalfLegheightlengthOffsetlegShapeNEGwidthOffsethalfLegheightlengthOffsetlegShapewidthOffsethalfLegheightNEGlengthOffsetlegShapeNEGwidthOffsethalfLegheightNEGlengthOffset2000010tablecompoundShape0.01TRUE#999999TRUE#ffffff1tabletablesetupParamsDescribe this function...widthwidthlengthlengthheightheightthicknessthicknesswidthwidth1lengthlength1heightheight0.5thicknessthickness0.05","version":"0.20.13","type":"blockly"}} + script={{"script":"widthlengthlegHeightheighthalfLegheightthicknesshalfThicknesswidthOffsetlengthOffsetlegShapecompoundShapetable'clearSky'10000.10.7-100-100-1003#ffffff#ffffff1024TRUE0legHeightMINUSheightthicknesshalfLegheightDIVIDElegHeight2halfThicknessDIVIDEthickness2widthOffsetMINUSDIVIDEwidth2halfThicknesslengthOffsetMINUSDIVIDElength2halfThicknesslegShapethicknessthicknesslegHeight000compoundShapewidthlengththickness0MINUSheighthalfThickness0legShapewidthOffsethalfLegheightlengthOffsetlegShapeNEGwidthOffsethalfLegheightlengthOffsetlegShapewidthOffsethalfLegheightNEGlengthOffsetlegShapeNEGwidthOffsethalfLegheightNEGlengthOffset2000010tablecompoundShape0.01TRUE#999999TRUE#ffffff1tabletablesetupParamsDescribe this function...widthwidthlengthlengthheightheightthicknessthicknesswidthwidth1lengthlength1heightheight0.5thicknessthickness0.05","version":"0.20.14","type":"blockly"}} title="Bitbybit Blockly Editor - Simple Cube for Runner Tutorial" description="Draws 3D Table and controls its size via inputs coming from external executing program" /> @@ -66,7 +66,7 @@ Below are the `index.html` and `script.js` files you would use on StackBlitz or - + diff --git a/docs/learn/runners/table-configurator-rete.mdx b/docs/learn/runners/table-configurator-rete.mdx index bbab2dbe..4e27b743 100644 --- a/docs/learn/runners/table-configurator-rete.mdx +++ b/docs/learn/runners/table-configurator-rete.mdx @@ -46,7 +46,7 @@ The following Bitbybit Rete script is the visual program that defines the logic diff --git a/docs/learn/runners/table-configurator-typescript.mdx b/docs/learn/runners/table-configurator-typescript.mdx index bc3f2230..d663739b 100644 --- a/docs/learn/runners/table-configurator-typescript.mdx +++ b/docs/learn/runners/table-configurator-typescript.mdx @@ -51,7 +51,7 @@ The following Bitbybit TypeScript script is the program that defines the logic f {\n\n const skyboxOptions = new Bit.Inputs.BabylonScene.SkyboxDto();\n skyboxOptions.skybox = Bit.Inputs.Base.skyboxEnum.clearSky;\n bitbybit.babylon.scene.enableSkybox(skyboxOptions);\n\n const lightOptions = new Bit.Inputs.BabylonScene.DirectionalLightDto();\n lightOptions.intensity = 3;\n bitbybit.babylon.scene.drawDirectionalLight(lightOptions);\n\n const tableTopShape = await solid.createBox({\n width: inputs.width,\n length: inputs.length,\n height: inputs.thickness,\n center: [0, inputs.height - halfThickness, 0],\n });\n\n const leg1Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [widthOffset, halfLegHeight, lengthOffset],\n });\n const leg2Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [-widthOffset, halfLegHeight, lengthOffset],\n });\n const leg3Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [widthOffset, halfLegHeight, -lengthOffset],\n });\n const leg4Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [-widthOffset, halfLegHeight, -lengthOffset],\n });\n\n const groundShape = await face.createCircleFace({\n radius: 2,\n center: [0, 0, 0],\n direction: [0, 1, 0]\n });\n\n const compoundShape = await compound.makeCompound({\n shapes: [tableTopShape, leg1Shape, leg2Shape, leg3Shape, leg4Shape, groundShape],\n });\n\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions.faceColour = \"#555577\";\n drawOptions.edgeWidth = 1;\n const table = await bitbybit.draw.drawAnyAsync({ entity: compoundShape, options: drawOptions });\n return { table };\n}\n\nconst runnerOutput = start();\nBit.setBitbybitRunnerResult(runnerOutput);\n","version":"0.20.13","type":"typescript"}} + script={{"script":"type Inputs = {\n width: number;\n length: number;\n height: number;\n thickness: number;\n};\n\nconst defaultValues: Inputs = {\n width: 1,\n length: 1,\n height: 0.5,\n thickness: 0.05,\n};\n\nBit.mockBitbybitRunnerInputs(defaultValues);\nconst inputs: Inputs = Bit.getBitbybitRunnerInputs();\n\nconst { solid, compound, face } = bitbybit.occt.shapes;\n\nconst legHeight = inputs.height - inputs.thickness;\nconst halfLegHeight = legHeight / 2;\nconst halfThickness = inputs.thickness / 2;\nconst widthOffset = inputs.width / 2 - halfThickness;\nconst lengthOffset = inputs.length / 2 - halfThickness;\n\n\nconst start = async () => {\n\n const skyboxOptions = new Bit.Inputs.BabylonScene.SkyboxDto();\n skyboxOptions.skybox = Bit.Inputs.Base.skyboxEnum.clearSky;\n bitbybit.babylon.scene.enableSkybox(skyboxOptions);\n\n const lightOptions = new Bit.Inputs.BabylonScene.DirectionalLightDto();\n lightOptions.intensity = 3;\n bitbybit.babylon.scene.drawDirectionalLight(lightOptions);\n\n const tableTopShape = await solid.createBox({\n width: inputs.width,\n length: inputs.length,\n height: inputs.thickness,\n center: [0, inputs.height - halfThickness, 0],\n });\n\n const leg1Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [widthOffset, halfLegHeight, lengthOffset],\n });\n const leg2Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [-widthOffset, halfLegHeight, lengthOffset],\n });\n const leg3Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [widthOffset, halfLegHeight, -lengthOffset],\n });\n const leg4Shape = await solid.createBox({\n width: inputs.thickness,\n length: inputs.thickness,\n height: legHeight,\n center: [-widthOffset, halfLegHeight, -lengthOffset],\n });\n\n const groundShape = await face.createCircleFace({\n radius: 2,\n center: [0, 0, 0],\n direction: [0, 1, 0]\n });\n\n const compoundShape = await compound.makeCompound({\n shapes: [tableTopShape, leg1Shape, leg2Shape, leg3Shape, leg4Shape, groundShape],\n });\n\n const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions();\n drawOptions.faceColour = \"#555577\";\n drawOptions.edgeWidth = 1;\n const table = await bitbybit.draw.drawAnyAsync({ entity: compoundShape, options: drawOptions });\n return { table };\n}\n\nconst runnerOutput = start();\nBit.setBitbybitRunnerResult(runnerOutput);\n","version":"0.20.14","type":"typescript"}} title="Bitbybit TypeScript Editor - 3D Table Configurator" description="Draws 3D Table and controls its size via inputs coming from external executing program" /> diff --git a/examples/angular/babylonjs/laptop-holder/package-lock.json b/examples/angular/babylonjs/laptop-holder/package-lock.json index 3949591e..99e0cc2b 100644 --- a/examples/angular/babylonjs/laptop-holder/package-lock.json +++ b/examples/angular/babylonjs/laptop-holder/package-lock.json @@ -17,7 +17,7 @@ "@angular/platform-browser": "13.3.0", "@angular/platform-browser-dynamic": "13.3.0", "@angular/router": "13.3.0", - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "rxjs": "7.5.5", "tslib": "2.3.1", "zone.js": "0.11.5" @@ -2331,15 +2331,15 @@ } }, "node_modules/@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==", "license": "Apache-2.0" }, "node_modules/@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0" @@ -2354,9 +2354,9 @@ } }, "node_modules/@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -2364,18 +2364,18 @@ } }, "node_modules/@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.6.0" } }, "node_modules/@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -2383,49 +2383,49 @@ } }, "node_modules/@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "license": "MIT", "dependencies": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -2434,50 +2434,50 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, @@ -2526,12 +2526,536 @@ "node": ">=10.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2558,28 +3082,28 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@jscad/3mf-serializer": { @@ -2632,6 +3156,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -2996,6 +3526,12 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "12.11.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz", @@ -3688,9 +4224,9 @@ } }, "node_modules/babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "license": "Apache-2.0", "peer": true }, @@ -4468,7 +5004,6 @@ "version": "3.20.3", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "dev": true, "hasInstallScript": true, "funding": { "type": "opencollective", @@ -4807,6 +5342,15 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/date-format": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.6.tgz", @@ -4991,6 +5535,15 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -5577,6 +6130,18 @@ "node": ">=12" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, "node_modules/esbuild-sunos-64": { "version": "0.14.22", "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", @@ -6899,6 +7464,12 @@ "node": ">=8" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -6948,6 +7519,12 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -7635,6 +8212,12 @@ "node": ">= 8" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/less": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", @@ -8001,10 +8584,58 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/manifold-3d/node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/media-typer": { "version": "0.3.0", @@ -8318,9 +8949,9 @@ "dev": true }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -8336,6 +8967,47 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/needle": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", @@ -9878,6 +10550,12 @@ "node": ">=10" } }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -10640,6 +11318,62 @@ "node": ">=8" } }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11353,6 +12087,15 @@ } } }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", @@ -11485,6 +12228,12 @@ "node": ">=4" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -13719,14 +14468,14 @@ } }, "@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==" + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==" }, "@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "requires": {} }, "@babylonjs/havok": { @@ -13738,63 +14487,63 @@ } }, "@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "requires": {} }, "@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "requires": {} }, "@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "requires": {} }, "@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "requires": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==" + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==" }, "@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", - "requires": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", + "requires": { + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "requires": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -13803,45 +14552,45 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "requires": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "requires": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "requires": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "requires": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, @@ -13875,12 +14624,241 @@ "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", "dev": true }, + "@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true + } + } + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, + "@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "requires": { + "property-graph": "^3.0.0" + } + }, + "@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + } + }, + "@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + } + }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==" + }, + "@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "optional": true + }, + "@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "optional": true + }, + "@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "optional": true, + "requires": { + "@emnapi/runtime": "^1.7.0" + } + }, + "@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "optional": true + }, + "@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "optional": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -13901,25 +14879,22 @@ "dev": true }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@jscad/3mf-serializer": { @@ -13966,6 +14941,11 @@ "@jscad/modeling": "2.12.3" } }, + "@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==" + }, "@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -14279,6 +15259,11 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==" + }, "@types/node": { "version": "12.11.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz", @@ -14847,9 +15832,9 @@ } }, "babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "peer": true }, "balanced-match": { @@ -15434,8 +16419,7 @@ "core-js": { "version": "3.20.3", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "dev": true + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==" }, "core-js-compat": { "version": "3.21.1", @@ -15680,6 +16664,14 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, "date-format": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.6.tgz", @@ -15816,6 +16808,11 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, "detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -16199,6 +17196,14 @@ "dev": true, "optional": true }, + "esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "requires": { + "ts-replace-all": "^1.0.0" + } + }, "esbuild-sunos-64": { "version": "0.14.22", "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", @@ -17176,6 +18181,11 @@ } } }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -17213,6 +18223,11 @@ "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -17722,6 +18737,11 @@ "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", "dev": true }, + "ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==" + }, "less": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", @@ -17989,9 +19009,43 @@ } }, "manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "requires": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "dependencies": { + "commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==" + }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + } + } }, "media-typer": { "version": "0.3.0", @@ -18225,9 +19279,9 @@ "dev": true }, "nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "optional": true }, "nanoid": { @@ -18236,6 +19290,43 @@ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "requires": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "requires": { + "cwise-compiler": "^1.0.0" + } + }, + "ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "requires": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "needle": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", @@ -19371,6 +20462,11 @@ "retry": "^0.12.0" } }, + "property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -19952,6 +21048,47 @@ "kind-of": "^6.0.2" } }, + "sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "dependencies": { + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" + } + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -20459,6 +21596,14 @@ "yn": "3.1.1" } }, + "ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "requires": { + "core-js": "^3.4.1" + } + }, "tsconfig-paths": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", @@ -20549,6 +21694,11 @@ "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", "dev": true }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, "unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", diff --git a/examples/angular/babylonjs/laptop-holder/package.json b/examples/angular/babylonjs/laptop-holder/package.json index 507d02c5..03f1d0be 100644 --- a/examples/angular/babylonjs/laptop-holder/package.json +++ b/examples/angular/babylonjs/laptop-holder/package.json @@ -10,7 +10,7 @@ }, "private": true, "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "@angular/animations": "13.3.0", "@angular/common": "13.3.0", "@angular/compiler": "13.3.0", diff --git a/examples/angular/threejs/simple/package-lock.json b/examples/angular/threejs/simple/package-lock.json index 72919fa8..fca49b2e 100644 --- a/examples/angular/threejs/simple/package-lock.json +++ b/examples/angular/threejs/simple/package-lock.json @@ -17,7 +17,7 @@ "@angular/platform-browser": "13.3.0", "@angular/platform-browser-dynamic": "13.3.0", "@angular/router": "13.3.0", - "@bitbybit-dev/threejs": "0.20.13", + "@bitbybit-dev/threejs": "0.20.14", "rxjs": "7.5.5", "tslib": "2.3.1", "zone.js": "0.11.5" @@ -2331,33 +2331,33 @@ } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -2366,61 +2366,61 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/threejs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.13.tgz", - "integrity": "sha512-tDI1f00bikeFP8q//rLNBwDRr15Hw1x8myBKhOsROJelIvulJnXWtxudx+byD366VtHN33IZfmMSJvau3Yt7vw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.14.tgz", + "integrity": "sha512-2vb552Yoz0QM+R268k5F7SwZrzV8JQb71xxkzxqwl3VmvZl2yFQJBDUpeiIQF2N5iyKfgIuiM7LXzZYdCaY4PQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/core": "0.20.13", - "three": "0.181.2" + "@bitbybit-dev/core": "0.20.14", + "three": "0.182.0" } }, "node_modules/@cspotcode/source-map-consumer": { @@ -2468,12 +2468,536 @@ "node": ">=10.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2500,28 +3024,28 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@jscad/3mf-serializer": { @@ -2574,6 +3098,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -2933,6 +3463,12 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "12.11.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz", @@ -4398,7 +4934,6 @@ "version": "3.20.3", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "dev": true, "hasInstallScript": true, "funding": { "type": "opencollective", @@ -4737,6 +5272,15 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/date-format": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.6.tgz", @@ -4921,6 +5465,15 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -5502,6 +6055,18 @@ "node": ">=12" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, "node_modules/esbuild-sunos-64": { "version": "0.14.22", "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", @@ -6824,6 +7389,12 @@ "node": ">=8" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -6873,6 +7444,12 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -7560,6 +8137,12 @@ "node": ">= 8" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/less": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", @@ -7926,10 +8509,58 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/manifold-3d/node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/media-typer": { "version": "0.3.0", @@ -8243,9 +8874,9 @@ "dev": true }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -8261,6 +8892,47 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/needle": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", @@ -9803,6 +10475,12 @@ "node": ">=10" } }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -10565,6 +11243,62 @@ "node": ">=8" } }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11173,9 +11907,9 @@ "dev": true }, "node_modules/three": { - "version": "0.181.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", - "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", "license": "MIT" }, "node_modules/through": { @@ -11284,6 +12018,15 @@ } } }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", @@ -11416,6 +12159,12 @@ "node": ">=4" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -13650,30 +14399,30 @@ } }, "@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==" + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==" }, "@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", - "requires": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", + "requires": { + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "requires": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -13682,55 +14431,55 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "requires": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "requires": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "requires": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "requires": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/threejs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.13.tgz", - "integrity": "sha512-tDI1f00bikeFP8q//rLNBwDRr15Hw1x8myBKhOsROJelIvulJnXWtxudx+byD366VtHN33IZfmMSJvau3Yt7vw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.14.tgz", + "integrity": "sha512-2vb552Yoz0QM+R268k5F7SwZrzV8JQb71xxkzxqwl3VmvZl2yFQJBDUpeiIQF2N5iyKfgIuiM7LXzZYdCaY4PQ==", "requires": { - "@bitbybit-dev/core": "0.20.13", - "three": "0.181.2" + "@bitbybit-dev/core": "0.20.14", + "three": "0.182.0" } }, "@cspotcode/source-map-consumer": { @@ -13763,12 +14512,241 @@ "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", "dev": true }, + "@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true + } + } + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, + "@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "requires": { + "property-graph": "^3.0.0" + } + }, + "@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + } + }, + "@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + } + }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==" + }, + "@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "optional": true + }, + "@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "optional": true + }, + "@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "optional": true, + "requires": { + "@emnapi/runtime": "^1.7.0" + } + }, + "@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "optional": true + }, + "@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "optional": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -13789,25 +14767,22 @@ "dev": true }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@jscad/3mf-serializer": { @@ -13854,6 +14829,11 @@ "@jscad/modeling": "2.12.3" } }, + "@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==" + }, "@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -14162,6 +15142,11 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==" + }, "@types/node": { "version": "12.11.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz", @@ -15311,8 +16296,7 @@ "core-js": { "version": "3.20.3", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "dev": true + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==" }, "core-js-compat": { "version": "3.21.1", @@ -15557,6 +16541,14 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, "date-format": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.6.tgz", @@ -15693,6 +16685,11 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, "detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -16071,6 +17068,14 @@ "dev": true, "optional": true }, + "esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "requires": { + "ts-replace-all": "^1.0.0" + } + }, "esbuild-sunos-64": { "version": "0.14.22", "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", @@ -17048,6 +18053,11 @@ } } }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -17085,6 +18095,11 @@ "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -17594,6 +18609,11 @@ "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", "dev": true }, + "ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==" + }, "less": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", @@ -17861,9 +18881,43 @@ } }, "manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "requires": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "dependencies": { + "commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==" + }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + } + } }, "media-typer": { "version": "0.3.0", @@ -18097,9 +19151,9 @@ "dev": true }, "nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "optional": true }, "nanoid": { @@ -18108,6 +19162,43 @@ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "requires": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "requires": { + "cwise-compiler": "^1.0.0" + } + }, + "ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "requires": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "needle": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", @@ -19243,6 +20334,11 @@ "retry": "^0.12.0" } }, + "property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -19824,6 +20920,47 @@ "kind-of": "^6.0.2" } }, + "sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "dependencies": { + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" + } + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -20263,9 +21400,9 @@ "dev": true }, "three": { - "version": "0.181.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", - "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==" + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==" }, "through": { "version": "2.3.8", @@ -20336,6 +21473,14 @@ "yn": "3.1.1" } }, + "ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "requires": { + "core-js": "^3.4.1" + } + }, "tsconfig-paths": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", @@ -20426,6 +21571,11 @@ "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", "dev": true }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, "unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", diff --git a/examples/angular/threejs/simple/package.json b/examples/angular/threejs/simple/package.json index 25f32752..46d9d932 100644 --- a/examples/angular/threejs/simple/package.json +++ b/examples/angular/threejs/simple/package.json @@ -10,7 +10,7 @@ }, "private": true, "dependencies": { - "@bitbybit-dev/threejs": "0.20.13", + "@bitbybit-dev/threejs": "0.20.14", "@angular/animations": "13.3.0", "@angular/common": "13.3.0", "@angular/compiler": "13.3.0", diff --git a/examples/angular/threejs/vite-basic-example/package-lock.json b/examples/angular/threejs/vite-basic-example/package-lock.json index 901a2db1..82849187 100644 --- a/examples/angular/threejs/vite-basic-example/package-lock.json +++ b/examples/angular/threejs/vite-basic-example/package-lock.json @@ -13,7 +13,7 @@ "@angular/forms": "^20.0.0", "@angular/platform-browser": "^20.0.0", "@angular/router": "^20.0.0", - "@bitbybit-dev/threejs": "0.20.13", + "@bitbybit-dev/threejs": "0.20.14", "rxjs": "7.5.5", "tslib": "^2.5.0", "zone.js": "~0.15.0" @@ -22,7 +22,7 @@ "@angular/build": "^20.0.0", "@angular/cli": "^20.0.0", "@angular/compiler-cli": "^20.0.0", - "@types/three": "0.181.0", + "@types/three": "0.182.0", "typescript": "^5.8.2" } }, @@ -250,13 +250,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.2002.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2002.2.tgz", - "integrity": "sha512-amppp/UqKyj+B8hYFU16j4t6SVN+SS0AEnHivDjKy41NNJgXv+5Sm2Q2jaMHviCT3rclyT0wqwNAi0RDjyLx5Q==", + "version": "0.2003.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.13.tgz", + "integrity": "sha512-JyH6Af6PNC1IHJToColFk1RaXDU87mpPjz7M5sWDfn8bC+KBipw6dSdRkCEuw0D9HY1lZkC9EBV9k9GhpvHjCQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.2.2", + "@angular-devkit/core": "20.3.13", "rxjs": "7.8.2" }, "engines": { @@ -276,9 +276,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "20.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.2.2.tgz", - "integrity": "sha512-SC+f5isSWJBpEgR+R7jP++2Z14WExNWLAdKpIickLWjuL8FlGkj+kaF3dWXhh0KcXo+r6kKb4pWUptSaqer5gA==", + "version": "20.3.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.13.tgz", + "integrity": "sha512-/D84T1Caxll3I2sRihPDR9UaWBhF50M+tAX15PdP6uSh/TxwAlLl9p7Rm1bD0mPjPercqaEKA+h9a9qLP16hug==", "dev": true, "license": "MIT", "dependencies": { @@ -314,13 +314,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "20.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.2.2.tgz", - "integrity": "sha512-rtL7slZjzdChQoiADKZv/Ra8D3C3tIw/WcVxd2stiLHdK/Oaf9ejx5m/X9o0QMEbNsy2Fy/RKodNqmz1CjzpCg==", + "version": "20.3.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.13.tgz", + "integrity": "sha512-hdMKY4rUTko8xqeWYGnwwDYDomkeOoLsYsP6SdaHWK7hpGvzWsT6Q/aIv8J8NrCYkLu+M+5nLiKOooweUZu3GQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.2.2", + "@angular-devkit/core": "20.3.13", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "8.2.0", @@ -343,9 +343,9 @@ } }, "node_modules/@angular/animations": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.2.4.tgz", - "integrity": "sha512-mXiTlXZgAF4uYonOt7l2w7uvLLTJEk6jqs3H291bYuoDRM8R166UjN7ygAeBmPiJ4TLMyKGkwMQy3b1Vvw4RQA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.3.15.tgz", + "integrity": "sha512-ikyKfhkxoqQA6JcBN0B9RaN6369sM1XYX81Id0lI58dmWCe7gYfrTp8ejqxxKftl514psQO3pkW8Gn1nJ131Gw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -354,18 +354,18 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "20.2.4" + "@angular/core": "20.3.15" } }, "node_modules/@angular/build": { - "version": "20.2.2", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.2.2.tgz", - "integrity": "sha512-rvlKMt3OmeenHOwejRpI4OLcyERQn6Hl4ODRWlYfNX70Ki1zu6eAD0pWULzcD+HSQd0a26Xzt3gcpEy2vOEAzg==", + "version": "20.3.13", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.13.tgz", + "integrity": "sha512-/5pM3ZS+lLkZgA+n6TMmNV8I6t9Ow1C6Vkj6bXqWeOgFDH5LwnIEZFAKzEDBkCGos0m2gPKPcREcDD5tfp9h4g==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2002.2", + "@angular-devkit/architect": "0.2003.13", "@babel/core": "7.28.3", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -383,12 +383,12 @@ "parse5-html-rewriting-stream": "8.0.0", "picomatch": "4.0.3", "piscina": "5.1.3", - "rolldown": "1.0.0-beta.32", + "rollup": "4.52.3", "sass": "1.90.0", "semver": "7.7.2", "source-map-support": "0.5.21", "tinyglobby": "0.2.14", - "vite": "7.1.2", + "vite": "7.1.11", "watchpack": "2.4.4" }, "engines": { @@ -407,7 +407,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-server": "^20.0.0", "@angular/service-worker": "^20.0.0", - "@angular/ssr": "^20.2.2", + "@angular/ssr": "^20.3.13", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^20.0.0", @@ -457,19 +457,19 @@ } }, "node_modules/@angular/cli": { - "version": "20.2.2", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.2.2.tgz", - "integrity": "sha512-0K8cmuHzRTpPzy/w0+S5o3s0JPV++9/s2JhK4aw/+OnQRpUbodoqjm1ur5k5DUBQfIHi7aM73ZIW3G43lv4F0g==", + "version": "20.3.13", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.13.tgz", + "integrity": "sha512-G78I/HDJULloS2LSqfUfbmBlhDCbcWujIRWfuMnGsRf82TyGA2OEPe3IA/F8MrJfeOzPQim2fMyn24MqHL40Vg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.2002.2", - "@angular-devkit/core": "20.2.2", - "@angular-devkit/schematics": "20.2.2", + "@angular-devkit/architect": "0.2003.13", + "@angular-devkit/core": "20.3.13", + "@angular-devkit/schematics": "20.3.13", "@inquirer/prompts": "7.8.2", "@listr2/prompt-adapter-inquirer": "3.0.1", - "@modelcontextprotocol/sdk": "1.17.3", - "@schematics/angular": "20.2.2", + "@modelcontextprotocol/sdk": "1.24.0", + "@schematics/angular": "20.3.13", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.35.0", "ini": "5.0.0", @@ -480,7 +480,7 @@ "resolve": "1.22.10", "semver": "7.7.2", "yargs": "18.0.0", - "zod": "3.25.76" + "zod": "4.1.13" }, "bin": { "ng": "bin/ng.js" @@ -492,9 +492,9 @@ } }, "node_modules/@angular/common": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.2.4.tgz", - "integrity": "sha512-mc6Sq1cYjaPJYThnvG6x0f/E27pWksqwaNJxT1RtwhAGc1i2jsc0su6b7e5NnXEgVbdPqu1MZHAEFdXZ5+/MwQ==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.15.tgz", + "integrity": "sha512-k4mCXWRFiOHK3bUKfWkRQQ8KBPxW8TAJuKLYCsSHPCpMz6u0eA1F0VlrnOkZVKWPI792fOaEAWH2Y4PTaXlUHw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -503,14 +503,14 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "20.2.4", + "@angular/core": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.2.4.tgz", - "integrity": "sha512-LQzf+Azb/Ms+BavpCFIat+f1C0gUJpby2RW4yebF3JkBFKfJ7M8d49TQpF8rSnGxMRTf49mln7laz4nBYTLDGA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.15.tgz", + "integrity": "sha512-lMicIAFAKZXa+BCZWs3soTjNQPZZXrF/WMVDinm8dQcggNarnDj4UmXgKSyXkkyqK5SLfnLsXVzrX6ndVT6z7A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -520,9 +520,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.2.4.tgz", - "integrity": "sha512-II2hEpfbo73dL12D42DoIHYGiTYAiO9cpwh29BIo8VD054ei4cm0oK+jCyryDQH5T3+wyCWlj0OFjcZ/GmO7HQ==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.15.tgz", + "integrity": "sha512-8sJoxodxsfyZ8eJ5r6Bx7BCbazXYgsZ1+dE8t5u5rTQ6jNggwNtYEzkyReoD5xvP+MMtRkos3xpwq4rtFnpI6A==", "dev": true, "license": "MIT", "dependencies": { @@ -543,7 +543,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.2.4", + "@angular/compiler": "20.3.15", "typescript": ">=5.8 <6.0" }, "peerDependenciesMeta": { @@ -553,9 +553,9 @@ } }, "node_modules/@angular/core": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.2.4.tgz", - "integrity": "sha512-8yvfvPDWX8M7o82GBl5P1nlvm1ywQ2XZi5HWj3llKpSJE2XjzhATgPrpKwiNVnpgjZWTOwM11fpoAaRKqQjxTA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.15.tgz", + "integrity": "sha512-NMbX71SlTZIY9+rh/SPhRYFJU0pMJYW7z/TBD4lqiO+b0DTOIg1k7Pg9ydJGqSjFO1Z4dQaA6TteNuF99TJCNw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -564,7 +564,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.2.4", + "@angular/compiler": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" }, @@ -578,9 +578,9 @@ } }, "node_modules/@angular/forms": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.2.4.tgz", - "integrity": "sha512-wbgnW+GALVAmK6hgFegkwlHKw35onvh9Z5A236HCyUySEAOiaD/3CoDg5Hw4iHQAiSU6Fn2NwDiv+W0xki6WDw==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.15.tgz", + "integrity": "sha512-gS5hQkinq52pm/7mxz4yHPCzEcmRWjtUkOVddPH0V1BW/HMni/p4Y6k2KqKBeGb9p8S5EAp6PDxDVLOPukp3mg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -589,16 +589,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.2.4", - "@angular/core": "20.2.4", - "@angular/platform-browser": "20.2.4", + "@angular/common": "20.3.15", + "@angular/core": "20.3.15", + "@angular/platform-browser": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.2.4.tgz", - "integrity": "sha512-81vzW8xhnJU7AiYJKXLR2MuvawzhRDgwyNkPEep58wty5zNuIUCXdUERJSsXo7m/U2Dg1FUFfqLm4RC2UkqLzA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.15.tgz", + "integrity": "sha512-TxRM/wTW/oGXv/3/Iohn58yWoiYXOaeEnxSasiGNS1qhbkcKtR70xzxW6NjChBUYAixz2ERkLURkpx3pI8Q6Dw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -607,9 +607,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/animations": "20.2.4", - "@angular/common": "20.2.4", - "@angular/core": "20.2.4" + "@angular/animations": "20.3.15", + "@angular/common": "20.3.15", + "@angular/core": "20.3.15" }, "peerDependenciesMeta": { "@angular/animations": { @@ -618,9 +618,9 @@ } }, "node_modules/@angular/router": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.2.4.tgz", - "integrity": "sha512-KoduI1o+iBfCBGtXMvmy/qncDIwGxd2hNt2hDkkiYZTftmSg/XUJDxJqN84ckm2WLkdJpR9EirrwfHapJBIZOQ==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.15.tgz", + "integrity": "sha512-6+qgk8swGSoAu7ISSY//GatAyCP36hEvvUgvjbZgkXLLH9yUQxdo77ij05aJ5s0OyB25q/JkqS8VTY0z1yE9NQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -629,9 +629,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.2.4", - "@angular/core": "20.2.4", - "@angular/platform-browser": "20.2.4", + "@angular/common": "20.3.15", + "@angular/core": "20.3.15", + "@angular/platform-browser": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -651,9 +651,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "license": "MIT", "engines": { @@ -709,14 +709,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -831,9 +831,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -865,13 +865,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -896,18 +896,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -915,47 +915,47 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -964,61 +964,61 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/threejs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.13.tgz", - "integrity": "sha512-tDI1f00bikeFP8q//rLNBwDRr15Hw1x8myBKhOsROJelIvulJnXWtxudx+byD366VtHN33IZfmMSJvau3Yt7vw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.14.tgz", + "integrity": "sha512-2vb552Yoz0QM+R268k5F7SwZrzV8JQb71xxkzxqwl3VmvZl2yFQJBDUpeiIQF2N5iyKfgIuiM7LXzZYdCaY4PQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/core": "0.20.13", - "three": "0.181.2" + "@bitbybit-dev/core": "0.20.14", + "three": "0.182.0" } }, "node_modules/@dimforge/rapier3d-compat": { @@ -1028,34 +1028,10 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", "license": "MIT", "optional": true, "dependencies": { @@ -1504,18 +1480,535 @@ "node": ">=18" } }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@inquirer/checkbox": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.2.tgz", - "integrity": "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -1552,20 +2045,20 @@ } }, "node_modules/@inquirer/core": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", - "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -1580,15 +2073,15 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz", - "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==", + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/external-editor": "^1.0.1", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -1603,15 +2096,15 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.18.tgz", - "integrity": "sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -1626,14 +2119,14 @@ } }, "node_modules/@inquirer/external-editor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", - "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", "dev": true, "license": "MIT", "dependencies": { - "chardet": "^2.1.0", - "iconv-lite": "^0.6.3" + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" }, "engines": { "node": ">=18" @@ -1648,9 +2141,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", - "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", "dev": true, "license": "MIT", "engines": { @@ -1658,14 +2151,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.2.tgz", - "integrity": "sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -1680,14 +2173,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.18.tgz", - "integrity": "sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==", + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -1702,15 +2195,15 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.18.tgz", - "integrity": "sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -1755,15 +2248,15 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.6.tgz", - "integrity": "sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -1778,16 +2271,16 @@ } }, "node_modules/@inquirer/search": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.1.tgz", - "integrity": "sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -1802,17 +2295,17 @@ } }, "node_modules/@inquirer/select": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.2.tgz", - "integrity": "sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -1827,9 +2320,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", - "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", "dev": true, "license": "MIT", "engines": { @@ -1966,7 +2459,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1976,14 +2468,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2040,6 +2530,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -2180,13 +2676,14 @@ ] }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz", - "integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.0.tgz", + "integrity": "sha512-D8h5KXY2vHFW8zTuxn2vuZGN0HGrQ5No6LkHwlEA9trVgNdPL3TF1dSqKA7Dny6BbBYKSW/rOBDXdC8KJAjUCg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.6", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", @@ -2194,39 +2691,28 @@ "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" }, "engines": { "node": ">=18" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", @@ -2634,19 +3120,6 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.3.tgz", - "integrity": "sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.5", - "@emnapi/runtime": "^1.4.5", - "@tybys/wasm-util": "^0.10.0" - } - }, "node_modules/@npmcli/agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", @@ -2896,26 +3369,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@oxc-project/runtime": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.81.0.tgz", - "integrity": "sha512-zm/LDVOq9FEmHiuM8zO4DWirv0VP2Tv2VsgaiHby9nvpq+FVrcqNYgv+TysLKOITQXWZj/roluTxFvpkHP0Iuw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.81.0.tgz", - "integrity": "sha512-CnOqkybZK8z6Gx7Wb1qF7AEnSzbol1WwcIzxYOr8e91LytGOjo0wCpgoYWZo8sdbpqX+X+TJayIzo4Pv0R/KjA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", @@ -3080,259 +3533,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", - "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", - "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", - "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", - "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/@parcel/watcher/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "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/@rolldown/binding-android-arm64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.32.tgz", - "integrity": "sha512-Gs+313LfR4Ka3hvifdag9r44WrdKQaohya7ZXUXzARF7yx0atzFlVZjsvxtKAw1Vmtr4hB/RjUD1jf73SW7zDw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.32.tgz", - "integrity": "sha512-W8oMqzGcI7wKPXUtS3WJNXzbghHfNiuM1UBAGpVb+XlUCgYRQJd2PRGP7D3WGql3rR3QEhUvSyAuCBAftPQw6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.32.tgz", - "integrity": "sha512-pM4c4sKUk37noJrnnDkJknLhCsfZu7aWyfe67bD0GQHfzAPjV16wPeD9CmQg4/0vv+5IfHYaa4VE536xbA+W0Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.32.tgz", - "integrity": "sha512-M8SUgFlYb5kJJWcFC8gUMRiX4WLFxPKMed3SJ2YrxontgIrEcpizPU8nLNVsRYEStoSfKHKExpQw3OP6fm+5bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.32.tgz", - "integrity": "sha512-FuQpbNC/hE//bvv29PFnk0AtpJzdPdYl5CMhlWPovd9g3Kc3lw9TrEPIbL7gRPUdhKAiq6rVaaGvOnXxsa0eww==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.32.tgz", - "integrity": "sha512-hRZygRlaGCjcNTNY9GV7dDI18sG1dK3cc7ujHq72LoDad23zFDUGMQjiSxHWK+/r92iMV+j2MiHbvzayxqynsg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", "cpu": [ "arm64" ], @@ -3341,12 +3544,19 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.32.tgz", - "integrity": "sha512-HzgT6h+CXLs+GKAU0Wvkt3rvcv0CmDBsDjlPhh4GHysOKbG9NjpKYX2zvjx671E9pGbTvcPpwy7gGsy7xpu+8g==", + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", "cpu": [ "arm64" ], @@ -3355,12 +3565,19 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.32.tgz", - "integrity": "sha512-Ab/wbf6gdzphDbsg51UaxsC93foQ7wxhtg0SVCXd25BrV4MAJ1HoDtKN/f4h0maFmJobkqYub2DlmoasUzkvBg==", + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", "cpu": [ "x64" ], @@ -3369,12 +3586,19 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.32.tgz", - "integrity": "sha512-VoxqGEfh5A1Yx+zBp/FR5QwAbtzbuvky2SVc+ii4g1gLD4zww6mt/hPi5zG+b88zYPFBKHpxMtsz9cWqXU5V5Q==", + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", "cpu": [ "x64" ], @@ -3383,43 +3607,19 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.32.tgz", - "integrity": "sha512-qZ1ViyOUDGbiZrSAJ/FIAhYUElDfVxxFW6DLT/w4KeoZN3HsF4jmRP95mXtl51/oGrqzU9l9Q2f7/P4O/o2ZZA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.32.tgz", - "integrity": "sha512-hEkG3wD+f3wytV0lqwb/uCrXc4r4Ny/DWJFJPfQR3VeMWplhWGgSHNwZc2Q7k86Yi36f9NNzzWmrIuvHI9lCVw==", - "cpu": [ - "wasm32" ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.3" - }, "engines": { - "node": ">=14.0.0" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.32.tgz", - "integrity": "sha512-k3MvDf8SiA7uP2ikP0unNouJ2YCrnwi7xcVW+RDgMp5YXVr3Xu6svmT3HGn0tkCKUuPmf+uy8I5uiHt5qWQbew==", + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", "cpu": [ "arm64" ], @@ -3428,12 +3628,19 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rolldown/binding-win32-ia32-msvc": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.32.tgz", - "integrity": "sha512-wAi/FxGh7arDOUG45UmnXE1sZUa0hY4cXAO2qWAjFa3f7bTgz/BqwJ7XN5SUezvAJPNkME4fEpInfnBvM25a0w==", + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", "cpu": [ "ia32" ], @@ -3442,12 +3649,19 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.32.tgz", - "integrity": "sha512-Ej0i4PZk8ltblZtzVK8ouaGUacUtxRmTm5S9794mdyU/tYxXjAJNseOfxrnHpMWKjMDrOKbqkPqJ52T9NR4LQQ==", + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", "cpu": [ "x64" ], @@ -3456,19 +3670,52 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz", - "integrity": "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==", + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "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/@rollup/rollup-android-arm-eabi": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", - "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", "cpu": [ "arm" ], @@ -3480,9 +3727,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz", - "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", "cpu": [ "arm64" ], @@ -3494,9 +3741,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz", - "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", "cpu": [ "arm64" ], @@ -3508,9 +3755,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz", - "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", "cpu": [ "x64" ], @@ -3522,9 +3769,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz", - "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", "cpu": [ "arm64" ], @@ -3536,9 +3783,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz", - "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", "cpu": [ "x64" ], @@ -3550,9 +3797,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz", - "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", "cpu": [ "arm" ], @@ -3564,9 +3811,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz", - "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", "cpu": [ "arm" ], @@ -3578,9 +3825,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz", - "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", "cpu": [ "arm64" ], @@ -3592,9 +3839,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz", - "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", "cpu": [ "arm64" ], @@ -3605,10 +3852,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz", - "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", "cpu": [ "loong64" ], @@ -3620,9 +3867,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz", - "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", "cpu": [ "ppc64" ], @@ -3634,9 +3881,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz", - "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", "cpu": [ "riscv64" ], @@ -3648,9 +3895,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz", - "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", "cpu": [ "riscv64" ], @@ -3662,9 +3909,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz", - "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", "cpu": [ "s390x" ], @@ -3676,9 +3923,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz", - "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", "cpu": [ "x64" ], @@ -3690,9 +3937,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz", - "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", "cpu": [ "x64" ], @@ -3704,9 +3951,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz", - "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", "cpu": [ "arm64" ], @@ -3718,9 +3965,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz", - "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", "cpu": [ "arm64" ], @@ -3732,9 +3979,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz", - "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", "cpu": [ "ia32" ], @@ -3745,10 +3992,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz", - "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", "cpu": [ "x64" ], @@ -3760,14 +4021,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "20.2.2", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.2.2.tgz", - "integrity": "sha512-VzJsEIiBmHzJAOVaKHn1CwTuOqvI1GwZuneUk/tmyYKkKdWEgxnoNBvz1ql6eHstkLz3S9yt6aUuAgjQC+J2Xw==", + "version": "20.3.13", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.13.tgz", + "integrity": "sha512-ETJ1budKmrkdxojo5QP6TPr6zQZYGxtWWf8NrX1cBIS851zPCmFkKyhSFLZsoksariYF/LP8ljvm8tlcIzt/XA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.2.2", - "@angular-devkit/schematics": "20.2.2", + "@angular-devkit/core": "20.3.13", + "@angular-devkit/schematics": "20.3.13", "jsonc-parser": "3.3.1" }, "engines": { @@ -3887,17 +4148,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", - "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3905,6 +4155,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/stats.js": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", @@ -3913,16 +4169,16 @@ "license": "MIT" }, "node_modules/@types/three": { - "version": "0.181.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.181.0.tgz", - "integrity": "sha512-MLF1ks8yRM2k71D7RprFpDb9DOX0p22DbdPqT/uAkc6AtQXjxWCVDjCy23G9t1o8HcQPk7woD2NIyiaWcWPYmA==", + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", + "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", "dev": true, "license": "MIT", "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", - "@types/webxr": "*", + "@types/webxr": ">=0.5.17", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" @@ -3936,9 +4192,9 @@ "license": "MIT" }, "node_modules/@types/webxr": { - "version": "0.5.23", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.23.tgz", - "integrity": "sha512-GPe4AsfOSpqWd3xA/0gwoKod13ChcfV67trvxaW2krUbgb9gxQjnCx8zGshzMl8LSHZlNH5gQ8LNScsDuc7nGQ==", + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", "dev": true, "license": "MIT" }, @@ -3956,9 +4212,9 @@ } }, "node_modules/@webgpu/types": { - "version": "0.1.64", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.64.tgz", - "integrity": "sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==", + "version": "0.1.68", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.68.tgz", + "integrity": "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA==", "dev": true, "license": "BSD-3-Clause" }, @@ -4065,16 +4321,16 @@ } }, "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", "dev": true, "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" + "environment": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4106,16 +4362,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansis": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", - "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4123,6 +4369,16 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/beasties": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.5.tgz", @@ -4154,24 +4410,28 @@ } }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "dev": true, "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", - "debug": "^4.4.0", + "debug": "^4.4.3", "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", + "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/boolbase": { @@ -4206,9 +4466,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", - "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -4226,10 +4486,11 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001737", - "electron-to-chromium": "^1.5.211", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -4296,34 +4557,17 @@ "dev": true, "license": "ISC" }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacache/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -4372,9 +4616,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001741", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", - "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", + "version": "1.0.30001761", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", "dev": true, "funding": [ { @@ -4406,9 +4650,9 @@ } }, "node_modules/chardet": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", - "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "dev": true, "license": "MIT" }, @@ -4554,17 +4798,27 @@ "dev": true, "license": "MIT" }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -4604,6 +4858,17 @@ "node": ">=6.6.0" } }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -4663,10 +4928,19 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -4692,12 +4966,10 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "dev": true, + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } @@ -4791,16 +5063,16 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.215", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.215.tgz", - "integrity": "sha512-TIvGp57UpeNetj/wV/xpFNpWGb0b/ROw372lHPx5Aafx02gjTBtWnEEcaSX3W2dLM3OSdGGyHX/cHl01JQsLaQ==", + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, @@ -4825,6 +5097,20 @@ "iconv-lite": "^0.6.2" } }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -4943,6 +5229,30 @@ "@esbuild/win32-x64": "0.25.9" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -5001,26 +5311,27 @@ } }, "node_modules/exponential-backoff": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", - "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", "dev": true, "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -5073,13 +5384,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -5143,9 +5447,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { @@ -5157,7 +5461,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/foreground-child": { @@ -5308,9 +5616,9 @@ } }, "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==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -5382,9 +5690,9 @@ } }, "node_modules/hosted-git-info": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.0.tgz", - "integrity": "sha512-gEf705MZLrDPkbbhi8PnoO4ZwYgKoNL+ISZ3AjZMht2r3N5tuTwncyDi6Fv2/qDnMmZxgs0yI8WDOyR8q3G+SQ==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", "dev": true, "license": "ISC", "dependencies": { @@ -5395,11 +5703,11 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", - "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -5445,30 +5753,24 @@ "license": "BSD-2-Clause" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-proxy-agent": { @@ -5500,9 +5802,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "dev": true, "license": "MIT", "dependencies": { @@ -5510,6 +5812,10 @@ }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ignore-walk": { @@ -5526,11 +5832,11 @@ } }, "node_modules/ignore-walk/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -5542,9 +5848,9 @@ } }, "node_modules/immutable": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", - "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", "dev": true, "license": "MIT" }, @@ -5575,10 +5881,16 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", "engines": { @@ -5595,6 +5907,12 @@ "node": ">= 0.10" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -5743,6 +6061,16 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5837,6 +6165,12 @@ "node": ">=18.0.0" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/listr2": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.1.tgz", @@ -5951,22 +6285,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.0.tgz", - "integrity": "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", @@ -6062,10 +6380,37 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -6147,16 +6492,20 @@ } }, "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-function": { @@ -6329,9 +6678,9 @@ "license": "ISC" }, "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { @@ -6372,9 +6721,9 @@ "license": "MIT" }, "node_modules/msgpackr": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz", - "integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==", + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.8.tgz", + "integrity": "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==", "dev": true, "license": "MIT", "optional": true, @@ -6416,9 +6765,9 @@ } }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -6441,6 +6790,47 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -6460,9 +6850,9 @@ "optional": true }, "node_modules/node-gyp": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.4.2.tgz", - "integrity": "sha512-3gD+6zsrLQH7DyYOUIutaauuXrcyxeTPyQuZQCQoNPZMHMMS5m4y0xclNpvYzoK3VNzuyxT6eF4mkIL4WSZ1eQ==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz", + "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6520,34 +6910,17 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/node-gyp/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -6581,9 +6954,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.20.tgz", - "integrity": "sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, @@ -6656,18 +7029,29 @@ } }, "node_modules/npm-packlist": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.1.tgz", - "integrity": "sha512-vaC03b2PqJA6QqmwHi1jNU8fAPXEnnyv4j/W4PVfgm24C4/zZGSVut3z0YUeN0WIFCo1oGOL02+6LbvFK7JL4Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", + "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", "dev": true, "license": "ISC", "dependencies": { - "ignore-walk": "^8.0.0" + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/npm-pick-manifest": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", @@ -6893,9 +7277,9 @@ "optional": true }, "node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", "engines": { @@ -7143,9 +7527,9 @@ } }, "node_modules/pkce-challenge": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", - "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "dev": true, "license": "MIT", "engines": { @@ -7212,6 +7596,12 @@ "node": ">=10" } }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -7226,16 +7616,6 @@ "node": ">= 0.10" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -7263,38 +7643,21 @@ } }, "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.10" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -7381,42 +7744,10 @@ "dev": true, "license": "MIT" }, - "node_modules/rolldown": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.32.tgz", - "integrity": "sha512-vxI2sPN07MMaoYKlFrVva5qZ1Y7DAZkgp7MQwTnyHt4FUMz9Sh+YeCzNFV9JYHI6ZNwoGWLCfCViE3XVsRC1cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/runtime": "=0.81.0", - "@oxc-project/types": "=0.81.0", - "@rolldown/pluginutils": "1.0.0-beta.32", - "ansis": "^4.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-beta.32", - "@rolldown/binding-darwin-arm64": "1.0.0-beta.32", - "@rolldown/binding-darwin-x64": "1.0.0-beta.32", - "@rolldown/binding-freebsd-x64": "1.0.0-beta.32", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.32", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.32", - "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.32", - "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.32", - "@rolldown/binding-linux-x64-musl": "1.0.0-beta.32", - "@rolldown/binding-openharmony-arm64": "1.0.0-beta.32", - "@rolldown/binding-wasm32-wasi": "1.0.0-beta.32", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.32", - "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.32", - "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.32" - } - }, "node_modules/rollup": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", - "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", "dev": true, "license": "MIT", "dependencies": { @@ -7430,27 +7761,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.1", - "@rollup/rollup-android-arm64": "4.50.1", - "@rollup/rollup-darwin-arm64": "4.50.1", - "@rollup/rollup-darwin-x64": "4.50.1", - "@rollup/rollup-freebsd-arm64": "4.50.1", - "@rollup/rollup-freebsd-x64": "4.50.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", - "@rollup/rollup-linux-arm-musleabihf": "4.50.1", - "@rollup/rollup-linux-arm64-gnu": "4.50.1", - "@rollup/rollup-linux-arm64-musl": "4.50.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", - "@rollup/rollup-linux-ppc64-gnu": "4.50.1", - "@rollup/rollup-linux-riscv64-gnu": "4.50.1", - "@rollup/rollup-linux-riscv64-musl": "4.50.1", - "@rollup/rollup-linux-s390x-gnu": "4.50.1", - "@rollup/rollup-linux-x64-gnu": "4.50.1", - "@rollup/rollup-linux-x64-musl": "4.50.1", - "@rollup/rollup-openharmony-arm64": "4.50.1", - "@rollup/rollup-win32-arm64-msvc": "4.50.1", - "@rollup/rollup-win32-ia32-msvc": "4.50.1", - "@rollup/rollup-win32-x64-msvc": "4.50.1", + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" } }, @@ -7480,27 +7812,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -7549,32 +7860,36 @@ } }, "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "statuses": "^2.0.2" }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "dev": true, "license": "MIT", "dependencies": { @@ -7585,6 +7900,10 @@ }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { @@ -7594,6 +7913,62 @@ "dev": true, "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8111,9 +8486,9 @@ "license": "ISC" }, "node_modules/three": { - "version": "0.181.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", - "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", "license": "MIT" }, "node_modules/tinyglobby": { @@ -8157,6 +8532,15 @@ "node": ">=0.6" } }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -8178,19 +8562,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -8207,9 +8578,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8220,6 +8591,12 @@ "node": ">=14.17" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/unique-filename": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", @@ -8257,9 +8634,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -8287,16 +8664,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -8338,18 +8705,18 @@ } }, "node_modules/vite": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz", - "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", + "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", - "tinyglobby": "^0.2.14" + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -8412,6 +8779,23 @@ } } }, + "node_modules/vite/node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", @@ -8707,9 +9091,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "dev": true, "license": "MIT", "funding": { @@ -8717,13 +9101,13 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", "dev": true, "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25 || ^4" } }, "node_modules/zone.js": { diff --git a/examples/angular/threejs/vite-basic-example/package.json b/examples/angular/threejs/vite-basic-example/package.json index bb4b2dd6..0f3cf8fa 100644 --- a/examples/angular/threejs/vite-basic-example/package.json +++ b/examples/angular/threejs/vite-basic-example/package.json @@ -14,7 +14,7 @@ "@angular/forms": "^20.0.0", "@angular/platform-browser": "^20.0.0", "@angular/router": "^20.0.0", - "@bitbybit-dev/threejs": "0.20.13", + "@bitbybit-dev/threejs": "0.20.14", "rxjs": "7.5.5", "tslib": "^2.5.0", "zone.js": "~0.15.0" @@ -24,6 +24,6 @@ "@angular/cli": "^20.0.0", "@angular/compiler-cli": "^20.0.0", "typescript": "^5.8.2", - "@types/three": "0.181.0" + "@types/three": "0.182.0" } } \ No newline at end of file diff --git a/examples/angular/threejs/vite-basic-example/src/workers/manifold.worker.ts b/examples/angular/threejs/vite-basic-example/src/workers/manifold.worker.ts index ba7e8ebe..b25cf736 100644 --- a/examples/angular/threejs/vite-basic-example/src/workers/manifold.worker.ts +++ b/examples/angular/threejs/vite-basic-example/src/workers/manifold.worker.ts @@ -7,7 +7,7 @@ import Module from "manifold-3d"; const init = async () => { const wasm = await Module({ locateFile: () => { - return "https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.13/wasm/manifold.cc2ddd38.wasm"; + return "https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.14/wasm/manifold-3-3-2.wasm"; }, }); wasm.setup(); diff --git a/examples/nextjs/babylonjs/simple/package-lock.json b/examples/nextjs/babylonjs/simple/package-lock.json index 9cd13551..6451d3e1 100644 --- a/examples/nextjs/babylonjs/simple/package-lock.json +++ b/examples/nextjs/babylonjs/simple/package-lock.json @@ -8,7 +8,7 @@ "name": "simple", "version": "0.1.0", "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "file-loader": "6.2.0", "next": "15.0.1", "react": "19.0.0-rc-69d4b800-20241021", @@ -38,15 +38,15 @@ } }, "node_modules/@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==", "license": "Apache-2.0" }, "node_modules/@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0" @@ -61,9 +61,9 @@ } }, "node_modules/@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -71,18 +71,18 @@ } }, "node_modules/@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.6.0" } }, "node_modules/@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -90,49 +90,49 @@ } }, "node_modules/@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "license": "MIT", "dependencies": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -141,57 +141,58 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@emnapi/runtime": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", - "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -253,6 +254,48 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -288,6 +331,15 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -390,6 +442,38 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", @@ -492,6 +576,50 @@ "@img/sharp-libvips-linux-arm64": "1.0.4" } }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, "node_modules/@img/sharp-linux-s390x": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", @@ -594,6 +722,25 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-ia32": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", @@ -719,9 +866,10 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -777,6 +925,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -1036,6 +1190,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.17.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.0.tgz", @@ -1773,9 +1933,9 @@ } }, "node_modules/babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "license": "Apache-2.0", "peer": true }, @@ -2064,6 +2224,23 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2096,6 +2273,15 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -2211,10 +2397,10 @@ } }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "optional": true, + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "engines": { "node": ">=8" } @@ -2450,6 +2636,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3444,6 +3654,12 @@ "node": ">= 0.4" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -3521,6 +3737,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-bun-module": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz", @@ -4014,6 +4236,12 @@ "json-buffer": "3.0.1" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -4133,10 +4361,40 @@ "dev": true }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -4235,9 +4493,9 @@ } }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -4264,6 +4522,452 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/ndarray-pixels/node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -4836,6 +5540,12 @@ "react-is": "^16.13.1" } }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5135,10 +5845,10 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "devOptional": true, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -5803,6 +6513,15 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -5950,6 +6669,12 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", diff --git a/examples/nextjs/babylonjs/simple/package.json b/examples/nextjs/babylonjs/simple/package.json index ece4699f..37df7572 100644 --- a/examples/nextjs/babylonjs/simple/package.json +++ b/examples/nextjs/babylonjs/simple/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "react": "19.0.0-rc-69d4b800-20241021", "react-dom": "19.0.0-rc-69d4b800-20241021", "next": "15.0.1", diff --git a/examples/node/basic/package-lock.json b/examples/node/basic/package-lock.json index 01ad03ba..58a81b18 100644 --- a/examples/node/basic/package-lock.json +++ b/examples/node/basic/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "^0.20.13" + "@bitbybit-dev/occt": "^0.20.14" }, "devDependencies": { "concurrently": "^7.6.0", @@ -34,18 +34,18 @@ } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/ansi-regex": { diff --git a/examples/node/basic/package.json b/examples/node/basic/package.json index 447af574..e8fa0856 100644 --- a/examples/node/basic/package.json +++ b/examples/node/basic/package.json @@ -15,7 +15,7 @@ "node": ">=20.19.4" }, "dependencies": { - "@bitbybit-dev/occt": "^0.20.13" + "@bitbybit-dev/occt": "^0.20.14" }, "devDependencies": { "extensionless": "1.9.9", diff --git a/examples/node/express-app/bitbybit.ts b/examples/node/express-app/bitbybit.ts index d5e060a6..1bd8d9fa 100644 --- a/examples/node/express-app/bitbybit.ts +++ b/examples/node/express-app/bitbybit.ts @@ -36,7 +36,7 @@ export class BitByBitBase { this.jscad = new Jscad(jscad); const wasm = await Module({ locateFile: () => { - return "./manifold-3-0-0.wasm"; + return "./manifold-3-3-2.wasm"; }, }); wasm.setup(); diff --git a/examples/node/express-app/package-lock.json b/examples/node/express-app/package-lock.json index 381557d4..14027819 100644 --- a/examples/node/express-app/package-lock.json +++ b/examples/node/express-app/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@bitbybit-dev/core": "0.20.13", + "@bitbybit-dev/core": "0.20.14", "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", @@ -41,33 +41,33 @@ } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -76,53 +76,595 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@jscad/3mf-serializer": { "version": "2.1.12", "resolved": "https://registry.npmjs.org/@jscad/3mf-serializer/-/3mf-serializer-2.1.12.tgz", @@ -173,6 +715,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -250,6 +798,12 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "18.19.31", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", @@ -534,6 +1088,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -586,6 +1149,12 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -599,6 +1168,17 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -611,6 +1191,15 @@ "node": ">= 0.10" } }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -668,6 +1257,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -717,6 +1315,30 @@ "node": ">= 0.4" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -1006,6 +1628,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1026,6 +1654,12 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1090,6 +1724,12 @@ "node": ">=18.0.0" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -1097,10 +1737,31 @@ "dev": true }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/media-typer": { "version": "0.3.0", @@ -1176,6 +1837,47 @@ "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", "optional": true }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1348,6 +2050,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1539,6 +2247,62 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shell-quote": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", @@ -1692,6 +2456,15 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -1734,6 +2507,12 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1853,30 +2632,30 @@ } }, "@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==" + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==" }, "@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", - "requires": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", + "requires": { + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "requires": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -1885,48 +2664,288 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "requires": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "requires": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "requires": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "requires": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, + "@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "requires": { + "property-graph": "^3.0.0" + } + }, + "@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + } + }, + "@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + } + }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==" + }, + "@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "optional": true + }, + "@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "optional": true + }, + "@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "optional": true, + "requires": { + "@emnapi/runtime": "^1.7.0" + } + }, + "@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "optional": true + }, + "@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "optional": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@jscad/3mf-serializer": { "version": "2.1.12", "resolved": "https://registry.npmjs.org/@jscad/3mf-serializer/-/3mf-serializer-2.1.12.tgz", @@ -1971,6 +2990,11 @@ "@jscad/modeling": "2.12.3" } }, + "@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==" + }, "@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -2038,6 +3062,11 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==" + }, "@types/node": { "version": "18.19.31", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", @@ -2264,6 +3293,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2300,6 +3334,11 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -2310,6 +3349,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==" + }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -2319,6 +3363,14 @@ "vary": "^1" } }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, "date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -2356,6 +3408,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, "dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -2390,6 +3447,19 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, + "esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "requires": { + "ts-replace-all": "^1.0.0" + } + }, + "esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==" + }, "escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -2604,6 +3674,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2618,6 +3693,11 @@ "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2660,6 +3740,11 @@ "jsep": "^1.3.9" } }, + "ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==" + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2667,9 +3752,28 @@ "dev": true }, "manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "requires": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "dependencies": { + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + } + } }, "media-typer": { "version": "0.3.0", @@ -2724,6 +3828,43 @@ "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", "optional": true }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "requires": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "requires": { + "cwise-compiler": "^1.0.0" + } + }, + "ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "requires": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -2845,6 +3986,11 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2990,6 +4136,47 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "dependencies": { + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" + } + } + }, "shell-quote": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", @@ -3103,6 +4290,14 @@ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, + "ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "requires": { + "core-js": "^3.4.1" + } + }, "tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -3135,6 +4330,11 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/examples/node/express-app/package.json b/examples/node/express-app/package.json index e0fe271d..ff085211 100644 --- a/examples/node/express-app/package.json +++ b/examples/node/express-app/package.json @@ -11,7 +11,7 @@ "author": "Bit By Bit Developers", "license": "MIT", "dependencies": { - "@bitbybit-dev/core": "0.20.13", + "@bitbybit-dev/core": "0.20.14", "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", diff --git a/examples/nuxt/babylonjs/basic/package-lock.json b/examples/nuxt/babylonjs/basic/package-lock.json index 017105e8..b46442b7 100644 --- a/examples/nuxt/babylonjs/basic/package-lock.json +++ b/examples/nuxt/babylonjs/basic/package-lock.json @@ -8,7 +8,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "@pinia/nuxt": "^0.5.4", "nuxt": "^3.13.0", "pinia": "^2.2.2", @@ -486,15 +486,15 @@ } }, "node_modules/@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==", "license": "Apache-2.0" }, "node_modules/@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0" @@ -509,9 +509,9 @@ } }, "node_modules/@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -519,18 +519,18 @@ } }, "node_modules/@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.6.0" } }, "node_modules/@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -538,49 +538,49 @@ } }, "node_modules/@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "license": "MIT", "dependencies": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -589,50 +589,50 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, @@ -658,6 +658,16 @@ "node": ">=10.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", @@ -1026,6 +1036,513 @@ "node": ">=14" } }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", @@ -1164,9 +1681,10 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1222,6 +1740,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -2301,6 +2825,12 @@ "@types/node": "*" } }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.5.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", @@ -3028,9 +3558,9 @@ "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" }, "node_modules/babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "license": "Apache-2.0", "peer": true }, @@ -3560,6 +4090,17 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3817,6 +4358,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/db0": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/db0/-/db0-0.1.4.tgz", @@ -3944,9 +4494,10 @@ } }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "engines": { "node": ">=8" } @@ -4152,6 +4703,30 @@ "@esbuild/win32-x64": "0.23.1" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4809,6 +5384,12 @@ "url": "https://opencollective.com/ioredis" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/iron-webcrypto": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", @@ -4828,6 +5409,12 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-builtin-module": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", @@ -5153,6 +5740,12 @@ "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/launch-editor": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", @@ -5354,10 +5947,40 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/mdn-data": { "version": "2.0.30", @@ -5510,9 +6133,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -5538,6 +6161,47 @@ "resolved": "https://registry.npmjs.org/nanotar/-/nanotar-0.1.1.tgz", "integrity": "sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==" }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/nitropack": { "version": "2.9.7", "resolved": "https://registry.npmjs.org/nitropack/-/nitropack-2.9.7.tgz", @@ -7158,6 +7822,12 @@ "node": ">= 6" } }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/protocols": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", @@ -7503,9 +8173,10 @@ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -7608,6 +8279,50 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8083,6 +8798,15 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -8223,6 +8947,12 @@ "unplugin": "^1.14.1" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", diff --git a/examples/nuxt/babylonjs/basic/package.json b/examples/nuxt/babylonjs/basic/package.json index e1c2ac1a..4f55e580 100644 --- a/examples/nuxt/babylonjs/basic/package.json +++ b/examples/nuxt/babylonjs/basic/package.json @@ -11,7 +11,7 @@ "postinstall": "nuxt prepare" }, "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "@pinia/nuxt": "^0.5.4", "nuxt": "^3.13.0", "pinia": "^2.2.2", diff --git a/examples/package.json b/examples/package.json index e2589f4e..547a3cde 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,6 +1,6 @@ { "name": "bitbybit-examples", - "version": "0.20.13", + "version": "0.20.14", "description": "Monorepo for browser CAD which holds bitbybit.dev npm packages", "main": "index.js", "scripts": { diff --git a/examples/react/babylonjs/cup/package-lock.json b/examples/react/babylonjs/cup/package-lock.json index 5f843b7f..fbd80d23 100644 --- a/examples/react/babylonjs/cup/package-lock.json +++ b/examples/react/babylonjs/cup/package-lock.json @@ -8,7 +8,7 @@ "name": "cup", "version": "0.1.0", "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "@emotion/react": "11.9.0", "@emotion/styled": "11.8.1", "@mui/icons-material": "5.6.2", @@ -1809,15 +1809,15 @@ } }, "node_modules/@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==", "license": "Apache-2.0" }, "node_modules/@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0" @@ -1832,9 +1832,9 @@ } }, "node_modules/@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -1842,18 +1842,18 @@ } }, "node_modules/@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.6.0" } }, "node_modules/@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -1866,49 +1866,49 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "node_modules/@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "license": "MIT", "dependencies": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -1917,50 +1917,50 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, @@ -2235,6 +2235,16 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.10.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", @@ -2438,6 +2448,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -2468,6 +2520,471 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3195,12 +3712,13 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@jscad/3mf-serializer": { @@ -3253,6 +3771,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -4363,6 +4887,12 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", @@ -5694,9 +6224,9 @@ } }, "node_modules/babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "license": "Apache-2.0", "peer": true }, @@ -6829,6 +7359,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -6988,6 +7527,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -7430,6 +7978,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -9553,6 +10125,12 @@ "node": ">= 0.4" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -9631,6 +10209,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -12229,6 +12813,12 @@ "node": ">= 8" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -12406,10 +12996,46 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/mdn-data": { "version": "2.0.4", @@ -12639,9 +13265,9 @@ } }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -12666,6 +13292,47 @@ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -14586,6 +15253,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -15753,6 +16426,62 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16569,6 +17298,15 @@ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -16748,6 +17486,12 @@ "node": ">=4" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -19074,14 +19818,14 @@ } }, "@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==" + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==" }, "@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "requires": {} }, "@babylonjs/havok": { @@ -19093,21 +19837,21 @@ } }, "@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "requires": {} }, "@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "requires": {} }, "@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "requires": {} }, "@bcoe/v8-coverage": { @@ -19116,45 +19860,45 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "requires": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==" + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==" }, "@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", - "requires": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", + "requires": { + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "requires": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -19163,45 +19907,45 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "requires": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "requires": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "requires": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "requires": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, @@ -19331,6 +20075,15 @@ "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", "requires": {} }, + "@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, "@emotion/babel-plugin": { "version": "11.10.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", @@ -19489,6 +20242,36 @@ } } }, + "@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "requires": { + "property-graph": "^3.0.0" + } + }, + "@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + } + }, + "@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + } + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -19509,6 +20292,188 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==" + }, + "@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "optional": true + }, + "@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "optional": true + }, + "@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "optional": true, + "requires": { + "@emnapi/runtime": "^1.7.0" + } + }, + "@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "optional": true + }, + "@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "optional": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -20047,12 +21012,12 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@jscad/3mf-serializer": { @@ -20099,6 +21064,11 @@ "@jscad/modeling": "2.12.3" } }, + "@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==" + }, "@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -20826,6 +21796,11 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==" + }, "@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", @@ -21830,9 +22805,9 @@ } }, "babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "peer": true }, "balanced-match": { @@ -22653,6 +23628,14 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -22767,6 +23750,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -23116,6 +24104,19 @@ "is-symbol": "^1.0.2" } }, + "esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "requires": { + "ts-replace-all": "^1.0.0" + } + }, + "esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==" + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -24650,6 +25651,11 @@ "side-channel": "^1.0.4" } }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -24704,6 +25710,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -26563,6 +27574,11 @@ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==" }, + "ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==" + }, "language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -26707,9 +27723,38 @@ } }, "manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "requires": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "dependencies": { + "commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + } + } }, "mdn-data": { "version": "2.0.4", @@ -26872,9 +27917,9 @@ } }, "nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "optional": true }, "nanoid": { @@ -26892,6 +27937,43 @@ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "requires": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "requires": { + "cwise-compiler": "^1.0.0" + } + }, + "ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "requires": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -28092,6 +29174,11 @@ } } }, + "property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -28940,6 +30027,47 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "dependencies": { + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" + } + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -29557,6 +30685,14 @@ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, + "ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "requires": { + "core-js": "^3.4.1" + } + }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -29688,6 +30824,11 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", diff --git a/examples/react/babylonjs/cup/package.json b/examples/react/babylonjs/cup/package.json index 3152c028..c551f610 100644 --- a/examples/react/babylonjs/cup/package.json +++ b/examples/react/babylonjs/cup/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "https://app-store.bitbybit.dev/cup", "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "@emotion/react": "11.9.0", "@emotion/styled": "11.8.1", "web-ifc": "0.0.68", diff --git a/examples/react/babylonjs/laptop-holder/package-lock.json b/examples/react/babylonjs/laptop-holder/package-lock.json index 55221bd0..4b1dbc2c 100644 --- a/examples/react/babylonjs/laptop-holder/package-lock.json +++ b/examples/react/babylonjs/laptop-holder/package-lock.json @@ -8,7 +8,7 @@ "name": "laptop-holder", "version": "0.1.0", "dependencies": { - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "@emotion/react": "11.9.0", "@emotion/styled": "11.8.1", "@mui/icons-material": "5.6.2", @@ -1847,15 +1847,15 @@ } }, "node_modules/@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==", "license": "Apache-2.0" }, "node_modules/@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0" @@ -1870,9 +1870,9 @@ } }, "node_modules/@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -1880,18 +1880,18 @@ } }, "node_modules/@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.6.0" } }, "node_modules/@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "license": "Apache-2.0", "peerDependencies": { "@babylonjs/core": "^8.0.0", @@ -1904,49 +1904,49 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "node_modules/@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "license": "MIT", "dependencies": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -1955,50 +1955,50 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, @@ -2122,6 +2122,16 @@ "postcss": "^8.3" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.7.2", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz", @@ -2353,6 +2363,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -2371,6 +2423,471 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3024,25 +3541,28 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@jscad/3mf-serializer": { @@ -3095,6 +3615,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -4177,6 +4703,12 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "16.11.26", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", @@ -5400,9 +5932,9 @@ } }, "node_modules/babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "license": "Apache-2.0", "peer": true }, @@ -6530,6 +7062,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -6688,6 +7229,15 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -7080,10 +7630,34 @@ "is-symbol": "^1.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18" } }, "node_modules/escalade": { @@ -9063,6 +9637,12 @@ "node": ">= 0.4" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -9133,6 +9713,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", @@ -11469,6 +12055,12 @@ "node": ">= 8" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/language-subtag-registry": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", @@ -11599,17 +12191,6 @@ "tslib": "^2.0.3" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/lz-string": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", @@ -11657,10 +12238,46 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/mdn-data": { "version": "2.0.4", @@ -11892,9 +12509,9 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -11914,6 +12531,47 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -13715,6 +14373,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -14676,12 +15340,10 @@ } }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -14811,6 +15473,50 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15706,6 +16412,15 @@ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -15737,9 +16452,10 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/tsutils": { "version": "3.21.0", @@ -15872,6 +16588,12 @@ "node": ">=4" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -16906,11 +17628,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -18179,14 +18896,14 @@ } }, "@babylonjs/core": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.39.2.tgz", - "integrity": "sha512-B2bqTDKy6S73mOzDCGP4yG3lwJ2asI+y09WR2CAhhIiILXpwcioDsB5oKakVexls/jVE1Djueja+bGM7gVViHw==" + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.42.0.tgz", + "integrity": "sha512-Jg8bJrTW4XgcB8tssIkvW8/M2uoVcbH662cYSn2/e5jim5SVbeS3hdbtk9t56s5MJnfJoOQgkz3+hFtWjM3Tmg==" }, "@babylonjs/gui": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.39.2.tgz", - "integrity": "sha512-+ZNSKTjFA0cN/iWdyUMECiexNTYwPBjp6Ez4O7u4l9uBjetFCBldPRHOh5AFzURjucjbalsi6JYNz38kGs+tMQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.42.0.tgz", + "integrity": "sha512-Ayp0AhAKtPUDXrMFmgtP+PcQTQIlTkQfe20dXFakchSBBHvmoqNiBSwN7rN40XlJPps3pOaDuZsDoboJWkJxCQ==", "requires": {} }, "@babylonjs/havok": { @@ -18198,21 +18915,21 @@ } }, "@babylonjs/loaders": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.39.2.tgz", - "integrity": "sha512-F89RunJ3cNYlcv07fXvYJftAC7KwaY+cu7Ol15eWLvqErUfSN4F1vUgPWssiTopAZqESSfpaHoBkfAEqHtm1sw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.42.0.tgz", + "integrity": "sha512-sDthqDlgU7Nn4J/TcWFwsh8+ZJqp/tQtexLYlyDxs0VXInSwu9Hm1oCCCuKAdU/e9t4Qr5+ZpfnTDa4s0PcqBA==", "requires": {} }, "@babylonjs/materials": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.39.2.tgz", - "integrity": "sha512-66ji/ktUHjj2CcGn25Cm/v9zQpYFRZNLyUNBI3G6m5ocb2y4QVL2KmxXIbzTdAVbEUSIk67nfNXIQGOONraMRg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.42.0.tgz", + "integrity": "sha512-fZsc+HWDlh4qrSeC2vfjmki4854V1o9NNIQvukZhkEoT8FsHqMaUUDn6EFRjrA1TBn4SU14uxTOPWrEGvMT/ag==", "requires": {} }, "@babylonjs/serializers": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.39.2.tgz", - "integrity": "sha512-RcyD/EqEisoM+3rlSVDfZtg9ku3MSlYVsFoExNjcacIVtfErrN52f4qWxGi3RGij48ro7olX/BdrMZ8DbixVvQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.42.0.tgz", + "integrity": "sha512-VptrGNtGHn+hqpeev/ozJsPBrAn35s76rNZIdrmrLHIEJIWSo30IgNy4oZRNITcwqJ0H5Bv2r8QqBYZzQifPOg==", "requires": {} }, "@bcoe/v8-coverage": { @@ -18221,45 +18938,45 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "@bitbybit-dev/babylonjs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.13.tgz", - "integrity": "sha512-K8S2V5nxyyYTxc9Ga5ST5J9mIAlFCNLcbPHFHn3KKAnBI2qCf8w/PHnbjQ8WG4RqqhxZhMBt9RxGi644wh1VMQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/babylonjs/-/babylonjs-0.20.14.tgz", + "integrity": "sha512-5V0HlvP8IZoTMSIaaafobWmFHnwf/nXKkQF2fvDNtw2urLFUJV08SVz8FggAjQTKZHNbmDp69ovJqQmEes7+HQ==", "requires": { - "@babylonjs/core": "8.39.2", - "@babylonjs/gui": "8.39.2", + "@babylonjs/core": "8.42.0", + "@babylonjs/gui": "8.42.0", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.39.2", - "@babylonjs/materials": "8.39.2", - "@babylonjs/serializers": "8.39.2", - "@bitbybit-dev/core": "0.20.13", + "@babylonjs/loaders": "8.42.0", + "@babylonjs/materials": "8.42.0", + "@babylonjs/serializers": "8.42.0", + "@bitbybit-dev/core": "0.20.14", "earcut": "2.2.3" } }, "@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==" + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==" }, "@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", - "requires": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", + "requires": { + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "requires": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -18268,45 +18985,45 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "requires": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "requires": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "requires": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "requires": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, @@ -18382,6 +19099,15 @@ "postcss-value-parser": "^4.2.0" } }, + "@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, "@emotion/babel-plugin": { "version": "11.7.2", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz", @@ -18562,6 +19288,36 @@ } } }, + "@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "requires": { + "property-graph": "^3.0.0" + } + }, + "@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + } + }, + "@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "requires": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + } + }, "@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -18577,6 +19333,188 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==" + }, + "@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "optional": true + }, + "@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "optional": true + }, + "@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "optional": true, + "requires": { + "@emnapi/runtime": "^1.7.0" + } + }, + "@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "optional": true + }, + "@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "optional": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -19057,22 +19995,22 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@jscad/3mf-serializer": { @@ -19119,6 +20057,11 @@ "@jscad/modeling": "2.12.3" } }, + "@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==" + }, "@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -19814,6 +20757,11 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, + "@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==" + }, "@types/node": { "version": "16.11.26", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", @@ -20738,9 +21686,9 @@ } }, "babylonjs-gltf2interface": { - "version": "8.39.2", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.39.2.tgz", - "integrity": "sha512-kqVI+FPXnUVz9yWZRjZ2QXUmh5iUWL7hP0fGUYNvdHqrZpStelwARVp7rB4tSQCuBYmvOCopAYcAWZTtjtWIjw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.42.0.tgz", + "integrity": "sha512-PYYMVNATzo3yEesPh9b9tgp0/r3O6AXcBA6vc886Pk+XWYW9xiYwTe3B6pwJVsd/Zd12t2SBhDyBpWBqK8w8sw==", "peer": true }, "balanced-match": { @@ -21562,6 +22510,14 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -21679,6 +22635,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -21993,6 +22954,19 @@ "is-symbol": "^1.0.2" } }, + "esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "requires": { + "ts-replace-all": "^1.0.0" + } + }, + "esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==" + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -23417,6 +24391,11 @@ "side-channel": "^1.0.4" } }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -23466,6 +24445,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", @@ -25126,6 +26110,11 @@ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" }, + "ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==" + }, "language-subtag-registry": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", @@ -25232,14 +26221,6 @@ "tslib": "^2.0.3" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, "lz-string": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", @@ -25277,9 +26258,38 @@ } }, "manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "requires": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "dependencies": { + "commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + } + } }, "mdn-data": { "version": "2.0.4", @@ -25447,9 +26457,9 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, "nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "optional": true }, "nanoid": { @@ -25462,6 +26472,43 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "requires": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "requires": { + "cwise-compiler": "^1.0.0" + } + }, + "ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "requires": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -26638,6 +27685,11 @@ } } }, + "property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -27314,12 +28366,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" }, "send": { "version": "0.17.2", @@ -27437,6 +28486,40 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -28103,6 +29186,14 @@ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, + "ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "requires": { + "core-js": "^3.4.1" + } + }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -28130,9 +29221,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "tsutils": { "version": "3.21.0", @@ -28224,6 +29315,11 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -29029,11 +30125,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", diff --git a/examples/react/babylonjs/laptop-holder/package.json b/examples/react/babylonjs/laptop-holder/package.json index afd3be65..c2715c54 100644 --- a/examples/react/babylonjs/laptop-holder/package.json +++ b/examples/react/babylonjs/laptop-holder/package.json @@ -16,7 +16,7 @@ "react-scripts": "5.0.1", "typescript": "^4.6.2", "web-vitals": "^2.1.4", - "@bitbybit-dev/babylonjs": "0.20.13", + "@bitbybit-dev/babylonjs": "0.20.14", "file-loader": "6.2.0", "@mui/icons-material": "5.6.2", "@mui/material": "5.6.4", diff --git a/examples/react/threejs/vase/package-lock.json b/examples/react/threejs/vase/package-lock.json index af7a3682..5c6fd964 100644 --- a/examples/react/threejs/vase/package-lock.json +++ b/examples/react/threejs/vase/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11", - "@bitbybit-dev/threejs": "0.20.13", + "@bitbybit-dev/threejs": "0.20.14", "@emotion/react": "11.11.0", "@emotion/styled": "11.11.0", "@mui/icons-material": "5.11.16", @@ -21,7 +21,7 @@ "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-dom": "18.2.4", - "@types/three": "0.181.0", + "@types/three": "0.182.0", "react": "18.2.0", "react-app-rewired": "2.2.1", "react-dom": "18.2.0", @@ -1995,33 +1995,33 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.13.tgz", - "integrity": "sha512-DM+SJA9K/4F7xsQmI4Ne6fnFOJ4AoKfVSgwUgB1YUFr0rYiBQQeATwS/+uId0viw5PTBWcQlQHivTyiTgbT+rw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.14.tgz", + "integrity": "sha512-Nv8eEwoVDU0Ug3TJIyzPtvIX03whvVIWsUw0xTkImFISUWmU0v1rNSaEd9FL9Rx7rMt2MC9Cxs3lXLkhZS3iAQ==", "license": "MIT" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.13.tgz", - "integrity": "sha512-g4Mb+1o+kg+EzyS7r+y5oyckAJg8kVyLEAkIZDXngFA6OVsnucl/CQzsN7ndEFBQHtbVhVv2nKJXbXjWUFzZ5w==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.14.tgz", + "integrity": "sha512-meYYY5SEfoT+NEiM2W2lpwBWtFZzvRzrTcOuzDn/uNgmzyIqUARGYZroQgGEG9XmGiCUEfeYDc7Q7ML2aesyxg==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", - "@bitbybit-dev/jscad-worker": "0.20.13", - "@bitbybit-dev/manifold-worker": "0.20.13", - "@bitbybit-dev/occt-worker": "0.20.13", + "@bitbybit-dev/base": "0.20.14", + "@bitbybit-dev/jscad-worker": "0.20.14", + "@bitbybit-dev/manifold-worker": "0.20.14", + "@bitbybit-dev/occt-worker": "0.20.14", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.13.tgz", - "integrity": "sha512-QEGiMoBIX1riKVuIBzpz7U02CC6udsjMbCtszWvnIZZp+6YjWyt2RLJXj4h+0C+ZUEBCY5Ylq7lMDlWY4u+EcA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.14.tgz", + "integrity": "sha512-14paqSiCmYRkJxnaaSsY0X89Y9gi8NlWw/PCjtwh0Ds32W4pMbmee2i78ygVnds19qL4Gd1wje5UukVHqPVQTA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13", + "@bitbybit-dev/base": "0.20.14", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -2030,61 +2030,61 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.13.tgz", - "integrity": "sha512-8t1Ez5qmvKCz3ZlsgOsSILLNz6mT3A2Smu01xcg8vEThQ6/FunaYWg7BqCdIO+YdC3r6yrTdgzf+YGi4TZuVsA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.14.tgz", + "integrity": "sha512-clF2kTptnZxMS2fWR+sjmnSiQ3OGF8mJYpsL4TqBdiewoIMR2IVmSoKunb0TzAucNdwsecc67gFv8OrXvW8ccA==", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.13", + "@bitbybit-dev/jscad": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.13.tgz", - "integrity": "sha512-//bVGc8N+vWAM+bVdtPpuIonRAsagYiBcbmUpaIiaPkZvMvPeYhwKj/rvqrQ2bu+PG+9nv5szo9EmG6Frg64Og==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.14.tgz", + "integrity": "sha512-Cpe3P+LblDOIiyjCsMi/fAwlyOi7AGbhAoZMgGP7ItIKqf11yXXCSRTGF2daXYSPoUht1d86wLZdjProXyAOtQ==", "license": "MIT", "dependencies": { - "manifold-3d": "3.0.0" + "manifold-3d": "3.3.2" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.13.tgz", - "integrity": "sha512-Ef26p8o+T00IS8R002AHGc/r3zTXnvKET5PvmCj4jP2Q1RdsXaqt9vreKa+P/xiHVy/IYc4pqAWlW0cjplU3Ew==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.14.tgz", + "integrity": "sha512-o4yAo0BlxWRbv2vsg3G76QodkG3TOzMe7mWGvxeMJNvUrZTV01i9oDCOZudS9pU5lXMeSMpzRE56vbEInL8BqQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.13", + "@bitbybit-dev/manifold": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.13.tgz", - "integrity": "sha512-fm2g3UnSoXPj5+S7v7KH8xymBNsQJ9w+0+Nve72xzsVo7Bp1d7O6ibSjns2UHnB5pY6bz7Pf3KDWt07I1qS2aQ==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.14.tgz", + "integrity": "sha512-f915HUNZd345OHbvxzNm+qOudDJ92akrUCUZooYLPSdSuDubNrXPan+ZwNofB1IACHuyCF/sheNc5t6zuEiy0A==", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.13" + "@bitbybit-dev/base": "0.20.14" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.13.tgz", - "integrity": "sha512-8+WE+IqVDo+bDaWPMtdy98zEQ1JAjSW5cH+rw4VyEYAaLwZQGOGQubXQJ6EY755X8qoGjKr7YTINHK0NazzhYA==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.14.tgz", + "integrity": "sha512-0AmcXulDOikH/3cs64fbedZwZ+CgxJ6OyZvlPRUHyLXMPSmExMBh3QkImGOE+APL91QNKwBlWdDW/30IBtMQXQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.13", + "@bitbybit-dev/occt": "0.20.14", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/threejs": { - "version": "0.20.13", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.13.tgz", - "integrity": "sha512-tDI1f00bikeFP8q//rLNBwDRr15Hw1x8myBKhOsROJelIvulJnXWtxudx+byD366VtHN33IZfmMSJvau3Yt7vw==", + "version": "0.20.14", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/threejs/-/threejs-0.20.14.tgz", + "integrity": "sha512-2vb552Yoz0QM+R268k5F7SwZrzV8JQb71xxkzxqwl3VmvZl2yFQJBDUpeiIQF2N5iyKfgIuiM7LXzZYdCaY4PQ==", "license": "MIT", "dependencies": { - "@bitbybit-dev/core": "0.20.13", - "three": "0.181.2" + "@bitbybit-dev/core": "0.20.14", + "three": "0.182.0" } }, "node_modules/@csstools/normalize.css": { @@ -2362,6 +2362,16 @@ "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==" }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", @@ -2596,6 +2606,48 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@gltf-transform/core": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-4.2.1.tgz", + "integrity": "sha512-qKhrQ29KJ9K6sz7xGoGRllqPBlROjPJMQPvCLBOfgMPH3S/Ph5E0e6fD5WmwTY5QPyBsDMRqKd1Vl0ncThTXGw==", + "license": "MIT", + "dependencies": { + "property-graph": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/extensions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-4.2.1.tgz", + "integrity": "sha512-ieHSJU9qvb3ucWDxgHFcff+C7fLtFPa6G0Wtc/ki7GsN6Rh7o9eoyqi7Ggj2LO4i3ynCwLLPb/onbJKw2fBtsA==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "ktx-parse": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, + "node_modules/@gltf-transform/functions": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@gltf-transform/functions/-/functions-4.2.1.tgz", + "integrity": "sha512-hFCI80N6zt7yHlqXbzlYEYQbFwK+2OOZNvacrRtrWiCWKI0k1Ad5+nZFsmuIZZV03aXb5XG6UF7JINT3wETS1Q==", + "license": "MIT", + "dependencies": { + "@gltf-transform/core": "^4.2.1", + "@gltf-transform/extensions": "^4.2.1", + "ktx-parse": "^1.0.1", + "ndarray": "^1.0.19", + "ndarray-lanczos": "^0.3.0", + "ndarray-pixels": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/donmccurdy" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -2628,6 +2680,471 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead" }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3469,9 +3986,10 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3527,6 +4045,12 @@ "@jscad/modeling": "2.12.3" } }, + "node_modules/@jscadui/3mf-export": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@jscadui/3mf-export/-/3mf-export-0.5.0.tgz", + "integrity": "sha512-y5vZktqCjyi7wA38zqNlLIdZUIRZoOO9vCjLzwmL4bR0hk7B/Zm1IeffzJPFe1vFc0C1IEv3hm8caDv3doRk9g==", + "license": "MIT" + }, "node_modules/@jsep-plugin/assignment": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", @@ -4631,6 +5155,12 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, + "node_modules/@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.2.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", @@ -4777,15 +5307,15 @@ } }, "node_modules/@types/three": { - "version": "0.181.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.181.0.tgz", - "integrity": "sha512-MLF1ks8yRM2k71D7RprFpDb9DOX0p22DbdPqT/uAkc6AtQXjxWCVDjCy23G9t1o8HcQPk7woD2NIyiaWcWPYmA==", + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", + "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", "license": "MIT", "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", - "@types/webxr": "*", + "@types/webxr": ">=0.5.17", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" @@ -7109,6 +7639,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -7329,6 +7868,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -7818,6 +8366,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild-plugin-text-replace": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-text-replace/-/esbuild-plugin-text-replace-1.3.0.tgz", + "integrity": "sha512-RWB/bbdP0xDHBOtA0st4CAE6UZtky76aCB7Shw5r350JY403lfvrj2UMkInUB346tMtFWXKWXNf4gqNM+WbXag==", + "license": "BSD-2-Clause", + "dependencies": { + "ts-replace-all": "^1.0.0" + }, + "engines": { + "node": ">=10.1.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.12.tgz", + "integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==", + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -9822,6 +10394,12 @@ "node": ">= 0.4" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -9916,6 +10494,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -12506,6 +13090,12 @@ "node": ">= 8" } }, + "node_modules/ktx-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-1.1.0.tgz", + "integrity": "sha512-mKp3y+FaYgR7mXWAbyyzpa/r1zDWeaunH+INJO4fou3hb45XuNSwar+7llrRyvpMWafxSIi99RNFJ05MHedaJQ==", + "license": "MIT" + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -12700,10 +13290,46 @@ } }, "node_modules/manifold-3d": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.0.0.tgz", - "integrity": "sha512-6XxRf5LH4s7WIlRrvtzLMg+CHio5TznIE0w0PU/Ad8IzD36QLjb64QdSeQp9ISrk4tGtIqVAycevpPe1ExGTKg==", - "license": "Apache-2.0" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/manifold-3d/-/manifold-3d-3.3.2.tgz", + "integrity": "sha512-Xx+S7kkbqlZxSPfBKH5yVKDO6k/eW7JRvnG1dKCFO0D4zjifOV5GM18TR0sREDcbKAzglHZ5OoxxpZRkxg6U5A==", + "license": "Apache-2.0", + "dependencies": { + "@gltf-transform/core": "^4.2.0", + "@gltf-transform/extensions": "^4.2.0", + "@gltf-transform/functions": "^4.2.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@jscadui/3mf-export": "^0.5.0", + "commander": "^13.1.0", + "convert-source-map": "^2.0.0", + "esbuild-plugin-text-replace": "^1.3.0", + "esbuild-wasm": "^0.25.11", + "fflate": "^0.8.0" + }, + "bin": { + "manifold-cad": "bin/manifold-cad" + } + }, + "node_modules/manifold-3d/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/manifold-3d/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/manifold-3d/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" }, "node_modules/mdn-data": { "version": "2.0.4", @@ -12911,9 +13537,9 @@ } }, "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", "license": "MIT", "optional": true }, @@ -12944,6 +13570,47 @@ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-lanczos": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ndarray-lanczos/-/ndarray-lanczos-0.3.0.tgz", + "integrity": "sha512-5kBmmG3Zvyj77qxIAC4QFLKuYdDIBJwCG+DukT6jQHNa1Ft74/hPH1z5mbQXeHBt8yvGPBGVrr3wEOdJPYYZYg==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.11", + "ndarray": "^1.0.19" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pixels": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-5.0.1.tgz", + "integrity": "sha512-IBtrpefpqlI8SPDCGjXk4v5NV5z7r3JSuCbfuEEXaM0vrOJtNGgYUa4C3Lt5H+qWdYF4BCPVFsnXhNC7QvZwkw==", + "license": "MIT", + "dependencies": { + "@types/ndarray": "^1.0.14", + "ndarray": "^1.0.19", + "ndarray-ops": "^1.2.2", + "sharp": "^0.34.0" + } + }, "node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", @@ -14864,6 +15531,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/property-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-3.0.0.tgz", + "integrity": "sha512-TnzxUsttmGtw+OiU0LDw+0FlMbJ8vV8pOjyDI7+Kdni4Tj0hW5BFh7TatQu7Y68hcvvFmiFOHilKShsA4R82fA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -16055,6 +16728,62 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -17106,9 +17835,9 @@ } }, "node_modules/three": { - "version": "0.181.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", - "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", "license": "MIT" }, "node_modules/throat": { @@ -17188,6 +17917,15 @@ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "node_modules/ts-replace-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-replace-all/-/ts-replace-all-1.0.0.tgz", + "integrity": "sha512-6uBtdkw3jHXkPtx/e9xB/5vcngMm17CyJYsS2YZeQ+9FdRnt6Ev5g931Sg2p+dxbtMGoCm13m3ax/obicTZIkQ==", + "license": "MIT", + "dependencies": { + "core-js": "^3.4.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -17428,6 +18166,12 @@ "node": ">=4" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", diff --git a/examples/react/threejs/vase/package.json b/examples/react/threejs/vase/package.json index 732569ef..43d4f201 100644 --- a/examples/react/threejs/vase/package.json +++ b/examples/react/threejs/vase/package.json @@ -4,14 +4,14 @@ "private": true, "homepage": "https://app-store.bitbybit.dev/bitbybit-threejs", "dependencies": { - "@bitbybit-dev/threejs": "0.20.13", + "@bitbybit-dev/threejs": "0.20.14", "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.4.3", "@types/jest": "29.5.1", "@types/node": "20.2.5", "@types/react": "18.2.7", - "@types/three": "0.181.0", + "@types/three": "0.182.0", "@types/react-dom": "18.2.4", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/examples/runner/babylon/full/inline-include/index.html b/examples/runner/babylon/full/inline-include/index.html index c1d97a5c..7ebd92b8 100644 --- a/examples/runner/babylon/full/inline-include/index.html +++ b/examples/runner/babylon/full/inline-include/index.html @@ -34,7 +34,7 @@ // This function simply outputs the script that was exported from the Rete editor by clicking "Export to Runner" and selecting Minify option. function exportedScript() { - return '{\"type\":\"rete\",\"version\":\"0.20.13\",\"script\":\"!async function(e,t,s,n,r){let a={};a={x:[0],y:[0],z:[1],...a};const o=[{result:e.HS.executeBasedOnType(a,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let i={};i={text:[\\"[true,false]\\"],...i};const c=[{result:e.HS.executeBasedOnType(i,!1,(e=>t.json.parse(e))),transformers:[]}];let p={};p={text:[\\"[false,true]\\"],...p};const u=[{result:e.HS.executeBasedOnType(p,!1,(e=>t.json.parse(e))),transformers:[]}],l=[{result:[5],transformers:[]}];let d={};d={x:[1],y:[0],z:[0],...d};const m=[{result:e.HS.executeBasedOnType(d,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}],y=[{result:[12],transformers:[]}],S=[{result:[7],transformers:[]}];let H={};H={x:[0],y:[1],z:[0],...H};const f=[{result:e.HS.executeBasedOnType(H,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let h={};h={x:[0],y:[0],z:[1],...h};const x=[{result:e.HS.executeBasedOnType(h,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let v={};v={number:[.4],...v};const O=[{result:e.HS.executeBasedOnType(v,!1,(e=>t.math.number(e))),transformers:[]}];let I={};I={x:[0],y:[0],z:[-1],...I};const L=[{result:e.HS.executeBasedOnType(I,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let B={};B={x:[0],y:[0],z:[-2],...B};const w=[{result:e.HS.executeBasedOnType(B,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let T={};T={x:[0],y:[0],z:[1],...T};const g=[{result:e.HS.executeBasedOnType(T,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let A={};A={x:[0],y:[1.5],z:[0],...A};const E=[{result:e.HS.executeBasedOnType(A,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let b={};b={...{faceOpacity:[.5],edgeOpacity:[.5],edgeColour:[\\"#000000\\"],faceColour:[\\"#212121\\"],vertexColour:[\\"#ff00ff\\"],faceMaterial:[void 0],edgeWidth:[2],vertexSize:[.03],drawEdges:[!0],drawFaces:[!0],drawVertices:[!1],precision:[.02],drawEdgeIndexes:[!1],edgeIndexHeight:[.06],edgeIndexColour:[\\"ff00ff\\"],drawFaceIndexes:[!1],faceIndexHeight:[.06],faceIndexColour:[\\"#0000ff\\"]},...b};const z=[{result:e.HS.executeBasedOnType(b,!1,(e=>t.draw.optionsOcctShape(e))),transformers:[]}];let W={};W={name:[\\"Custom Material\\"],baseColor:[\\"#9c9cba\\"],emissiveColor:[\\"#000000\\"],metallic:[.9],roughness:[.1],alpha:[1],backFaceCulling:[!1],zOffset:[2],...W};const C=[{result:e.HS.executeBasedOnType(W,!1,(e=>t.babylon.material.pbrMetallicRoughness.create(e))),transformers:[]}];let P={};P={x:[0],y:[0],z:[-1],...P};const X=[{result:e.HS.executeBasedOnType(P,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Y={};Y={x:[0],y:[0],z:[-1.5],...Y};const Z=[{result:e.HS.executeBasedOnType(Y,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let N={};N={x:[0],y:[0],z:[1],...N};const k=[{result:e.HS.executeBasedOnType(N,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let M={};M={skybox:[\\"city\\"],size:[1e3],blur:[.4],environmentIntensity:[.4],...M};e.HS.executeBasedOnType(M,!1,(e=>t.babylon.scene.enableSkybox(e)));let F={number:[{result:[20],transformers:[]}]};e.HS.updateListInputs(F),F={number:[20],...F};const D=[{result:e.HS.executeBasedOnType(F,!1,(e=>t.math.number(e))),transformers:[]}];let R={};R.y=y,e.HS.updateListInputs(R),R={x:[0],y:[0],z:[0],...R};const j=[{result:e.HS.executeBasedOnType(R,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let q={};q.item=y,e.HS.updateListInputs(q),q={...q};const V=[{result:q.item}];let G={};G.first=S,e.HS.updateListInputs(G),G={first:[1],second:[-2],operation:[\\"divide\\"],...G};const J=[{result:e.HS.executeBasedOnType(G,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let K={};K.first=S,e.HS.updateListInputs(K),K={first:[1],second:[-4],operation:[\\"divide\\"],...K};const Q=[{result:e.HS.executeBasedOnType(K,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let U={};U.first=y,U.second=O,e.HS.updateListInputs(U),U={first:[1],second:[.4],operation:[\\"add\\"],...U};const $=[{result:e.HS.executeBasedOnType(U,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let _={};_.item=S,e.HS.updateListInputs(_),_={..._};const ee=[{result:_.item}],te={faceOpacity:[1],edgeOpacity:[1],edgeColour:[\\"#1c1c1c\\"],faceColour:[\\"#bdbdbd\\"],vertexColour:[\\"#ff00ff\\"],faceMaterial:[void 0],edgeWidth:[2],vertexSize:[.03],drawEdges:[!0],drawFaces:[!0],drawVertices:[!1],precision:[.01],drawEdgeIndexes:[!1],edgeIndexHeight:[.06],edgeIndexColour:[\\"ff00ff\\"],drawFaceIndexes:[!1],faceIndexHeight:[.06],faceIndexColour:[\\"#0000ff\\"]};let se={};se.faceMaterial=C,e.HS.updateListInputs(se),se={...te,...se};const ne=[{result:e.HS.executeBasedOnType(se,!1,(e=>t.draw.optionsOcctShape(e))),transformers:[]}];let re={};re.center=Z,re.direction=X,e.HS.updateListInputs(re),re={radius:[3],height:[1.9],center:[[0,0,0]],direction:[[0,1,0]],...re};const ae=[{result:await e.HS.executeBasedOnTypeAsync(re,!1,(e=>t.occt.shapes.solid.createCylinder(e))),transformers:[]}];let oe={};oe.y=$,e.HS.updateListInputs(oe),oe={x:[0],y:[12],z:[0],...oe};const ie=[{result:e.HS.executeBasedOnType(oe,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let ce={};ce.first=D,e.HS.updateListInputs(ce),ce={first:[1],second:[3],operation:[\\"multiply\\"],...ce};const pe=[{result:e.HS.executeBasedOnType(ce,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let ue={};ue.first=V,ue.second=O,e.HS.updateListInputs(ue),ue={first:[1],second:[.4],operation:[\\"add\\"],...ue};const le=[{result:e.HS.executeBasedOnType(ue,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let de={};de.first=V,de.second=O,e.HS.updateListInputs(de),de={first:[1],second:[.4],operation:[\\"subtract\\"],...de};const me=[{result:e.HS.executeBasedOnType(de,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let ye={};ye.first=ee,e.HS.updateListInputs(ye),ye={first:[1],second:[-.2],operation:[\\"multiply\\"],...ye};const Se=[{result:e.HS.executeBasedOnType(ye,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let He={};He.second=D,e.HS.updateListInputs(He),He={first:[360],second:[1],operation:[\\"divide\\"],...He};const fe=[{result:e.HS.executeBasedOnType(He,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}],he={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let xe={};xe.shape=ae,e.HS.updateListInputs(xe),xe={...he,...xe};const ve=[{result:await e.HS.executeBasedOnTypeAsync(xe,!1,(e=>t.occt.fillets.filletEdges(e))),transformers:[]}];let Oe={};Oe.start=L,Oe.end=ie,e.HS.updateListInputs(Oe),Oe={start:[[0,0,0]],end:[[0,1,0]],...Oe};const Ie=[{result:await e.HS.executeBasedOnTypeAsync(Oe,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let Le={};Le.second=pe,e.HS.updateListInputs(Le),Le={first:[360],second:[1],operation:[\\"divide\\"],...Le};const Be=[{result:e.HS.executeBasedOnType(Le,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let we={};we.start=w,we.end=ie,e.HS.updateListInputs(we),we={start:[[0,0,0]],end:[[0,1,0]],...we};const Te=[{result:await e.HS.executeBasedOnTypeAsync(we,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let ge={};ge.y=le,e.HS.updateListInputs(ge),ge={x:[0],y:[0],z:[.05],...ge};const Ae=[{result:e.HS.executeBasedOnType(ge,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Ee={};Ee.y=me,Ee.z=Q,e.HS.updateListInputs(Ee),Ee={x:[0],y:[0],z:[-1],...Ee};const be=[{result:e.HS.executeBasedOnType(Ee,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let ze={};ze.y=me,ze.z=J,e.HS.updateListInputs(ze),ze={x:[0],y:[0],z:[0],...ze};const We=[{result:e.HS.executeBasedOnType(ze,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Ce={};Ce.z=Se,e.HS.updateListInputs(Ce),Ce={x:[0],y:[0],z:[0],...Ce};const Pe=[{result:e.HS.executeBasedOnType(Ce,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Xe={};Xe.step=fe,e.HS.updateListInputs(Xe),Xe={step:[.1],min:[0],max:[360],...Xe};const Ye=e.HS.executeBasedOnType(Xe,!1,(e=>t.vector.span(e))),Ze=[];for(let e=0;e<1;e++)Ze.push({type:\\"flat\\"});const Ne=[{result:Ye,transformers:Ze}];let ke={};ke.first=Se,e.HS.updateListInputs(ke),ke={first:[2],second:[-2],operation:[\\"multiply\\"],...ke};e.HS.executeBasedOnType(ke,!1,(e=>t.math.twoNrOperation(e)));let Me={};Me.listElements=ve,e.HS.updateListInputs(Me),Me={...Me};const Fe=[{result:[Me.listElements?Me.listElements:[]]}],De={shape:[void 0],axis:[[0,0,1]],angle:[0]};let Re={};Re.shape=Ie,Re.axis=o,Re.angle=Be,e.HS.updateListInputs(Re),Re={...De,...Re};const je=[{result:await e.HS.executeBasedOnTypeAsync(Re,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let qe={};qe.first=Be,e.HS.updateListInputs(qe),qe={first:[1],second:[.4],operation:[\\"multiply\\"],...qe};const Ve=[{result:e.HS.executeBasedOnType(qe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Ge={};Ge.first=Be,e.HS.updateListInputs(Ge),Ge={first:[1],second:[.6],operation:[\\"multiply\\"],...Ge};const Je=[{result:e.HS.executeBasedOnType(Ge,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Ke={};Ke.listElements=[Ae[0],j[0],be[0],We[0]],e.HS.updateListInputs(Ke),Ke={...Ke};const Qe=[{result:[Ke.listElements?Ke.listElements:[]]}];let Ue={};Ue.item=Ne,e.HS.updateListInputs(Ue),Ue={...Ue};const $e=[{result:Ue.item}],_e={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let et={};et.shape=je,et.nrOfDivisions=l,e.HS.updateListInputs(et),et={..._e,...et};const tt=[{result:await e.HS.executeBasedOnTypeAsync(et,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],st={shape:[void 0],axis:[[0,0,1]],angle:[0]};let nt={};nt.shape=Te,nt.axis=o,nt.angle=[Ve[0],Je[0]],e.HS.updateListInputs(nt),nt={...st,...nt};const rt=[{result:await e.HS.executeBasedOnTypeAsync(nt,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let at={};at.number=Ve,e.HS.updateListInputs(at),at={number:[1],operation:[\\"negate\\"],...at};const ot=[{result:e.HS.executeBasedOnType(at,!1,(e=>t.math.oneNrOperation(e))),transformers:[]}],it={points:[void 0]};let ct={};ct.points=Qe,e.HS.updateListInputs(ct),ct={...it,...ct};const pt=[{result:await e.HS.executeBasedOnTypeAsync(ct,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];e.HS.drawNodeMeshes(pt,t);const ut={list:[void 0],pattern:[[!0,!0,!1]]};let lt={};lt.list=tt,lt.pattern=u,e.HS.updateListInputs(lt),lt={...ut,...lt};const dt=[{result:e.HS.executeBasedOnType(lt,!0,(e=>t.lists.getByPattern(e))),transformers:[]}];let mt={};mt.listElements=rt,e.HS.updateListInputs(mt),mt={...mt};const yt=[{result:[mt.listElements?mt.listElements:[]]}],St={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let Ht={};Ht.shape=pt,Ht.origin=We,Ht.direction=f,e.HS.updateListInputs(Ht),Ht={...St,...Ht};const ft=[{result:await e.HS.executeBasedOnTypeAsync(Ht,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],ht={shape:[void 0]};let xt={};xt.shape=pt,e.HS.updateListInputs(xt),xt={...ht,...xt};const vt=await e.HS.executeBasedOnTypeAsync(xt,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),Ot=[];for(let e=0;e<1;e++)Ot.push({type:\\"flat\\"});const It=[{result:vt,transformers:Ot}],Lt={list:[void 0],index:[0],clone:[!0]};let Bt={};Bt.list=yt,e.HS.updateListInputs(Bt),Bt={...Lt,...Bt};const wt=[{result:e.HS.executeBasedOnType(Bt,!1,(e=>t.lists.getItem(e))),transformers:[]}],Tt={shape:[void 0]};let gt={};gt.shape=ft,e.HS.updateListInputs(gt),gt={...Tt,...gt};const At=[{result:await e.HS.executeBasedOnTypeAsync(gt,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),transformers:[]}],Et={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let bt={};bt.shape=wt,bt.nrOfDivisions=l,e.HS.updateListInputs(bt),bt={...Et,...bt};const zt=[{result:await e.HS.executeBasedOnTypeAsync(bt,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],Wt={list:[void 0],index:[3],clone:[!0]};let Ct={};Ct.list=At,e.HS.updateListInputs(Ct),Ct={...Wt,...Ct};const Pt=[{result:e.HS.executeBasedOnType(Ct,!1,(e=>t.lists.removeItemAtIndex(e))),transformers:[]}],Xt={list:[void 0],pattern:[[!0,!0,!1]]};let Yt={};Yt.list=zt,Yt.pattern=c,e.HS.updateListInputs(Yt),Yt={...Xt,...Yt};const Zt=[{result:e.HS.executeBasedOnType(Yt,!1,(e=>t.lists.getByPattern(e))),transformers:[]}],Nt={list:[void 0],clone:[!0]};let kt={};kt.list=Pt,e.HS.updateListInputs(kt),kt={...Nt,...kt};const Mt=e.HS.executeBasedOnType(kt,!1,(e=>t.lists.reverse(e))),Ft=[];for(let e=0;e<1;e++)Ft.push({type:\\"flat\\"});const Dt=[{result:Mt,transformers:Ft}];let Rt={};Rt.listElements=[Zt[0],dt[0]],e.HS.updateListInputs(Rt),Rt={...Rt};const jt=[{result:[Rt.listElements?Rt.listElements:[]]}];let qt={};qt.listElements=[It[0],Dt[0]],e.HS.updateListInputs(qt),qt={...qt};const Vt=[{result:[qt.listElements?qt.listElements:[]]}],Gt={list:[void 0],clone:[!0]};let Jt={};Jt.list=jt,e.HS.updateListInputs(Jt),Jt={...Gt,...Jt};const Kt=e.HS.executeBasedOnType(Jt,!1,(e=>t.lists.flipLists(e))),Qt=[];for(let e=0;e<2;e++)Qt.push({type:\\"flat\\"});const Ut=[{result:Kt,transformers:Qt}],$t={points:[void 0]};let _t={};_t.points=Vt,e.HS.updateListInputs(_t),_t={...$t,..._t};const es=[{result:await e.HS.executeBasedOnTypeAsync(_t,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let ts={};ts.listElements=Ut,e.HS.updateListInputs(ts),ts={...ts};const ss=[{result:[ts.listElements?ts.listElements:[]]}],ns={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let rs={};rs.shape=es,e.HS.updateListInputs(rs),rs={...ns,...rs};const as=[{result:await e.HS.executeBasedOnTypeAsync(rs,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],os={points:[void 0]};let is={};is.points=ss,e.HS.updateListInputs(is),is={...os,...is};const cs=[{result:await e.HS.executeBasedOnTypeAsync(is,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}],ps={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let us={};us.shape=as,us.direction=x,e.HS.updateListInputs(us),us={...ps,...us};const ls=[{result:await e.HS.executeBasedOnTypeAsync(us,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],ds={shape:[void 0]};let ms={};ms.shape=as,e.HS.updateListInputs(ms),ms={...ds,...ms};const ys=[{result:await e.HS.executeBasedOnTypeAsync(ms,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],Ss={shape:[void 0]};let Hs={};Hs.shape=as,e.HS.updateListInputs(Hs),Hs={...Ss,...Hs};const fs=[{result:await e.HS.executeBasedOnTypeAsync(Hs,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],hs={shape:[void 0]};let xs={};xs.shape=as,e.HS.updateListInputs(xs),xs={...hs,...xs};const vs=[{result:await e.HS.executeBasedOnTypeAsync(xs,!1,(e=>t.occt.shapes.wire.closeOpenWire(e))),transformers:[]}],Os={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Is={};Is.shape=vs,Is.direction=x,e.HS.updateListInputs(Is),Is={...Os,...Is};const Ls=[{result:await e.HS.executeBasedOnTypeAsync(Is,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],Bs={shape:[void 0],radius:[1],radiusList:[void 0],indexes:[void 0],direction:[[0,1,0]]};let ws={};ws.shape=cs,ws.direction=g,e.HS.updateListInputs(ws),ws={...Bs,...ws};const Ts=[{result:await e.HS.executeBasedOnTypeAsync(ws,!1,(e=>t.occt.fillets.fillet3DWire(e))),transformers:[]}],gs={shape:[void 0],face:[void 0],distance:[-.2],tolerance:[.1]};let As={};As.shape=ls,e.HS.updateListInputs(As),As={...gs,...As};const Es=[{result:await e.HS.executeBasedOnTypeAsync(As,!1,(e=>t.occt.operations.offset(e))),transformers:[]}],bs={shape:[void 0],index:[0]};let zs={};zs.shape=ls,e.HS.updateListInputs(zs),zs={...bs,...zs};const Ws=[{result:await e.HS.executeBasedOnTypeAsync(zs,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}];let Cs={};Cs.item=ys,e.HS.updateListInputs(Cs),Cs={...Cs};const Ps=[{result:Cs.item}];let Xs={};Xs.item=fs,e.HS.updateListInputs(Xs),Xs={...Xs};const Ys=[{result:Xs.item}];let Zs={};Zs.start=fs,Zs.end=ys,e.HS.updateListInputs(Zs),Zs={start:[[0,0,0]],end:[[0,1,0]],...Zs};const Ns=[{result:await e.HS.executeBasedOnTypeAsync(Zs,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];e.HS.drawNodeMeshes(Ns,t);const ks={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Ms={};Ms.shape=Ts,Ms.angle=ot,Ms.direction=o,e.HS.updateListInputs(Ms),Ms={...ks,...Ms};const Fs=[{result:await e.HS.executeBasedOnTypeAsync(Ms,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],Ds={shape:[void 0]};let Rs={};Rs.shape=Ls,e.HS.updateListInputs(Rs),Rs={...Ds,...Rs};const js=[{result:await e.HS.executeBasedOnTypeAsync(Rs,!1,(e=>t.occt.shapes.solid.fromClosedShell(e))),transformers:[]}],qs={shape:[void 0],index:[1]};let Vs={};Vs.shape=Ws,e.HS.updateListInputs(Vs),Vs={...qs,...Vs};const Gs=[{result:await e.HS.executeBasedOnTypeAsync(Vs,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}],Js={shape:[void 0],index:[0]};let Ks={};Ks.shape=Es,e.HS.updateListInputs(Ks),Ks={...Js,...Ks};const Qs=[{result:await e.HS.executeBasedOnTypeAsync(Ks,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}],Us={shape:[void 0],translation:[[0,0,0]]};let $s={};$s.shape=Ns,$s.translation=E,e.HS.updateListInputs($s),$s={...Us,...$s};const _s=[{result:await e.HS.executeBasedOnTypeAsync($s,!1,(e=>t.occt.transforms.translate(e))),transformers:[]}],en={shape:[void 0],direction:[[0,1,0]]};let tn={};tn.shape=Fs,tn.direction=Pe,e.HS.updateListInputs(tn),tn={...en,...tn};const sn=[{result:await e.HS.executeBasedOnTypeAsync(tn,!1,(e=>t.occt.operations.extrude(e))),transformers:[]}];let nn={};nn.listElements=js,e.HS.updateListInputs(nn),nn={...nn};const rn=[{result:[nn.listElements?nn.listElements:[]]}],an={shape:[void 0],index:[1]};let on={};on.shape=Qs,e.HS.updateListInputs(on),on={...an,...on};const cn=[{result:await e.HS.executeBasedOnTypeAsync(on,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}];let pn={};pn.listElements=Gs,e.HS.updateListInputs(pn),pn={...pn};const un=[{result:[pn.listElements?pn.listElements:[]]}],ln={shape:[void 0]};let dn={};dn.shape=_s,e.HS.updateListInputs(dn),dn={...ln,...dn};const mn=[{result:await e.HS.executeBasedOnTypeAsync(dn,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],yn={shape:[void 0]};let Sn={};Sn.shape=_s,e.HS.updateListInputs(Sn),Sn={...yn,...Sn};const Hn=[{result:await e.HS.executeBasedOnTypeAsync(Sn,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],fn={shapes:[void 0]};let hn={};hn.shapes=un,e.HS.updateListInputs(hn),hn={...fn,...hn};const xn=[{result:await e.HS.executeBasedOnTypeAsync(hn,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let vn={};vn.listElements=cn,e.HS.updateListInputs(vn),vn={...vn};const On=[{result:[vn.listElements?vn.listElements:[]]}],In={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let Ln={};Ln.shape=sn,Ln.shapes=Fe,e.HS.updateListInputs(Ln),Ln={...In,...Ln};const Bn=[{result:await e.HS.executeBasedOnTypeAsync(Ln,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}];let wn={};wn.item=Hn,e.HS.updateListInputs(wn),wn={...wn};const Tn=[{result:wn.item}];let gn={};gn.item=mn,e.HS.updateListInputs(gn),gn={...gn};const An=[{result:gn.item}],En={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let bn={};bn.shape=Bn,bn.shapes=rn,e.HS.updateListInputs(bn),bn={...En,...bn};const zn=[{result:await e.HS.executeBasedOnTypeAsync(bn,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}],Wn={shapes:[void 0]};let Cn={};Cn.shapes=On,e.HS.updateListInputs(Cn),Cn={...Wn,...Cn};const Pn=[{result:await e.HS.executeBasedOnTypeAsync(Cn,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let Xn={};Xn.listElements=[Ps[0],Tn[0],An[0],Ys[0]],e.HS.updateListInputs(Xn),Xn={...Xn};const Yn=[{result:[Xn.listElements?Xn.listElements:[]]}],Zn={shape:[void 0],origin:[[0,0,0]],normal:[[0,0,1]]};let Nn={};Nn.shape=zn,Nn.normal=m,e.HS.updateListInputs(Nn),Nn={...Zn,...Nn};const kn=[{result:await e.HS.executeBasedOnTypeAsync(Nn,!1,(e=>t.occt.transforms.mirrorAlongNormal(e))),transformers:[]}];let Mn={};Mn.listElements=[xn[0],Pn[0]],e.HS.updateListInputs(Mn),Mn={...Mn};const Fn=[{result:[Mn.listElements?Mn.listElements:[]]}],Dn={points:[void 0]};let Rn={};Rn.points=Yn,e.HS.updateListInputs(Rn),Rn={...Dn,...Rn};const jn=[{result:await e.HS.executeBasedOnTypeAsync(Rn,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let qn={};qn.listElements=[kn[0],zn[0]],e.HS.updateListInputs(qn),qn={...qn};const Vn=[{result:[qn.listElements?qn.listElements:[]]}],Gn={shapes:[void 0],makeSolid:[!1]};let Jn={};Jn.shapes=Fn,e.HS.updateListInputs(Jn),Jn={...Gn,...Jn};const Kn=[{result:await e.HS.executeBasedOnTypeAsync(Jn,!1,(e=>t.occt.operations.loft(e))),transformers:[]}],Qn={shape:[void 0],radius:[.5],radiusList:[void 0],indexes:[void 0]};let Un={};Un.shape=jn,e.HS.updateListInputs(Un),Un={...Qn,...Un};const $n=[{result:await e.HS.executeBasedOnTypeAsync(Un,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],_n={shapes:[void 0]};let er={};er.shapes=Vn,e.HS.updateListInputs(er),er={..._n,...er};const tr=[{result:await e.HS.executeBasedOnTypeAsync(er,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],sr={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let nr={};nr.shape=Kn,nr.origin=We,nr.direction=f,e.HS.updateListInputs(nr),nr={...sr,...nr};const rr=[{result:await e.HS.executeBasedOnTypeAsync(nr,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],ar={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let or={};or.shape=$n,or.direction=x,e.HS.updateListInputs(or),or={...ar,...or};const ir=[{result:await e.HS.executeBasedOnTypeAsync(or,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}];let cr={};cr.listElements=[ls[0],Es[0],Kn[0],rr[0]],e.HS.updateListInputs(cr),cr={...cr};const pr=[{result:[cr.listElements?cr.listElements:[]]}],ur={shape:[void 0],offset:[-.1]};let lr={};lr.shape=ir,e.HS.updateListInputs(lr),lr={...ur,...lr};const dr=[{result:await e.HS.executeBasedOnTypeAsync(lr,!1,(e=>t.occt.operations.makeThickSolidSimple(e))),transformers:[]}],mr={shape:[void 0],angle:[0],center:[[0,0,0]],axis:[[0,0,1]]};let yr={};yr.shape=tr,yr.angle=$e,yr.axis=k,e.HS.updateListInputs(yr),yr={...mr,...yr};const Sr=[{result:await e.HS.executeBasedOnTypeAsync(yr,!1,(e=>t.occt.transforms.rotateAroundCenter(e))),transformers:[]}],Hr={shapes:[void 0],tolerance:[1e-7]};let fr={};fr.shapes=pr,e.HS.updateListInputs(fr),fr={...Hr,...fr};const hr=[{result:await e.HS.executeBasedOnTypeAsync(fr,!1,(e=>t.occt.shapes.shell.sewFaces(e))),transformers:[]}],xr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let vr={};vr.entity=dr,vr.options=z,e.HS.updateListInputs(vr),vr={...xr,...vr};await e.HS.executeBasedOnTypeAsync(vr,!1,(e=>t.draw.drawAnyAsync(e)));let Or={};Or.listElements=Sr,e.HS.updateListInputs(Or),Or={...Or};const Ir=[{result:[Or.listElements?Or.listElements:[]]}],Lr={shapes:[void 0]};let Br={};Br.shapes=Ir,e.HS.updateListInputs(Br),Br={...Lr,...Br};const wr=[{result:await e.HS.executeBasedOnTypeAsync(Br,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}];let Tr={};Tr.listElements=[hr[0],ve[0],wr[0]],e.HS.updateListInputs(Tr),Tr={...Tr};const gr=[{result:[Tr.listElements?Tr.listElements:[]]}],Ar={shapes:[void 0]};let Er={};Er.shapes=gr,e.HS.updateListInputs(Er),Er={...Ar,...Er};const br=[{result:await e.HS.executeBasedOnTypeAsync(Er,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],zr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let Wr={};Wr.entity=br,Wr.options=ne,e.HS.updateListInputs(Wr),Wr={...zr,...Wr};await e.HS.executeBasedOnTypeAsync(Wr,!1,(e=>t.draw.drawAnyAsync(e)))}(BitByBit,bitbybit,bitbybitRunnerResult,bitbybitRunnerInputs,Bit);\"}' + return '{\"type\":\"rete\",\"version\":\"0.20.14\",\"script\":\"!async function(e,t,s,n,r){let a={};a={x:[0],y:[0],z:[1],...a};const o=[{result:e.HS.executeBasedOnType(a,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let i={};i={text:[\\"[true,false]\\"],...i};const c=[{result:e.HS.executeBasedOnType(i,!1,(e=>t.json.parse(e))),transformers:[]}];let p={};p={text:[\\"[false,true]\\"],...p};const u=[{result:e.HS.executeBasedOnType(p,!1,(e=>t.json.parse(e))),transformers:[]}],l=[{result:[5],transformers:[]}];let d={};d={x:[1],y:[0],z:[0],...d};const m=[{result:e.HS.executeBasedOnType(d,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}],y=[{result:[12],transformers:[]}],S=[{result:[7],transformers:[]}];let H={};H={x:[0],y:[1],z:[0],...H};const f=[{result:e.HS.executeBasedOnType(H,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let h={};h={x:[0],y:[0],z:[1],...h};const x=[{result:e.HS.executeBasedOnType(h,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let v={};v={number:[.4],...v};const O=[{result:e.HS.executeBasedOnType(v,!1,(e=>t.math.number(e))),transformers:[]}];let I={};I={x:[0],y:[0],z:[-1],...I};const L=[{result:e.HS.executeBasedOnType(I,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let B={};B={x:[0],y:[0],z:[-2],...B};const w=[{result:e.HS.executeBasedOnType(B,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let T={};T={x:[0],y:[0],z:[1],...T};const g=[{result:e.HS.executeBasedOnType(T,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let A={};A={x:[0],y:[1.5],z:[0],...A};const E=[{result:e.HS.executeBasedOnType(A,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let b={};b={...{faceOpacity:[.5],edgeOpacity:[.5],edgeColour:[\\"#000000\\"],faceColour:[\\"#212121\\"],vertexColour:[\\"#ff00ff\\"],faceMaterial:[void 0],edgeWidth:[2],vertexSize:[.03],drawEdges:[!0],drawFaces:[!0],drawVertices:[!1],precision:[.02],drawEdgeIndexes:[!1],edgeIndexHeight:[.06],edgeIndexColour:[\\"ff00ff\\"],drawFaceIndexes:[!1],faceIndexHeight:[.06],faceIndexColour:[\\"#0000ff\\"]},...b};const z=[{result:e.HS.executeBasedOnType(b,!1,(e=>t.draw.optionsOcctShape(e))),transformers:[]}];let W={};W={name:[\\"Custom Material\\"],baseColor:[\\"#9c9cba\\"],emissiveColor:[\\"#000000\\"],metallic:[.9],roughness:[.1],alpha:[1],backFaceCulling:[!1],zOffset:[2],...W};const C=[{result:e.HS.executeBasedOnType(W,!1,(e=>t.babylon.material.pbrMetallicRoughness.create(e))),transformers:[]}];let P={};P={x:[0],y:[0],z:[-1],...P};const X=[{result:e.HS.executeBasedOnType(P,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Y={};Y={x:[0],y:[0],z:[-1.5],...Y};const Z=[{result:e.HS.executeBasedOnType(Y,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let N={};N={x:[0],y:[0],z:[1],...N};const k=[{result:e.HS.executeBasedOnType(N,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let M={};M={skybox:[\\"city\\"],size:[1e3],blur:[.4],environmentIntensity:[.4],...M};e.HS.executeBasedOnType(M,!1,(e=>t.babylon.scene.enableSkybox(e)));let F={number:[{result:[20],transformers:[]}]};e.HS.updateListInputs(F),F={number:[20],...F};const D=[{result:e.HS.executeBasedOnType(F,!1,(e=>t.math.number(e))),transformers:[]}];let R={};R.y=y,e.HS.updateListInputs(R),R={x:[0],y:[0],z:[0],...R};const j=[{result:e.HS.executeBasedOnType(R,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let q={};q.item=y,e.HS.updateListInputs(q),q={...q};const V=[{result:q.item}];let G={};G.first=S,e.HS.updateListInputs(G),G={first:[1],second:[-2],operation:[\\"divide\\"],...G};const J=[{result:e.HS.executeBasedOnType(G,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let K={};K.first=S,e.HS.updateListInputs(K),K={first:[1],second:[-4],operation:[\\"divide\\"],...K};const Q=[{result:e.HS.executeBasedOnType(K,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let U={};U.first=y,U.second=O,e.HS.updateListInputs(U),U={first:[1],second:[.4],operation:[\\"add\\"],...U};const $=[{result:e.HS.executeBasedOnType(U,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let _={};_.item=S,e.HS.updateListInputs(_),_={..._};const ee=[{result:_.item}],te={faceOpacity:[1],edgeOpacity:[1],edgeColour:[\\"#1c1c1c\\"],faceColour:[\\"#bdbdbd\\"],vertexColour:[\\"#ff00ff\\"],faceMaterial:[void 0],edgeWidth:[2],vertexSize:[.03],drawEdges:[!0],drawFaces:[!0],drawVertices:[!1],precision:[.01],drawEdgeIndexes:[!1],edgeIndexHeight:[.06],edgeIndexColour:[\\"ff00ff\\"],drawFaceIndexes:[!1],faceIndexHeight:[.06],faceIndexColour:[\\"#0000ff\\"]};let se={};se.faceMaterial=C,e.HS.updateListInputs(se),se={...te,...se};const ne=[{result:e.HS.executeBasedOnType(se,!1,(e=>t.draw.optionsOcctShape(e))),transformers:[]}];let re={};re.center=Z,re.direction=X,e.HS.updateListInputs(re),re={radius:[3],height:[1.9],center:[[0,0,0]],direction:[[0,1,0]],...re};const ae=[{result:await e.HS.executeBasedOnTypeAsync(re,!1,(e=>t.occt.shapes.solid.createCylinder(e))),transformers:[]}];let oe={};oe.y=$,e.HS.updateListInputs(oe),oe={x:[0],y:[12],z:[0],...oe};const ie=[{result:e.HS.executeBasedOnType(oe,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let ce={};ce.first=D,e.HS.updateListInputs(ce),ce={first:[1],second:[3],operation:[\\"multiply\\"],...ce};const pe=[{result:e.HS.executeBasedOnType(ce,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let ue={};ue.first=V,ue.second=O,e.HS.updateListInputs(ue),ue={first:[1],second:[.4],operation:[\\"add\\"],...ue};const le=[{result:e.HS.executeBasedOnType(ue,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let de={};de.first=V,de.second=O,e.HS.updateListInputs(de),de={first:[1],second:[.4],operation:[\\"subtract\\"],...de};const me=[{result:e.HS.executeBasedOnType(de,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let ye={};ye.first=ee,e.HS.updateListInputs(ye),ye={first:[1],second:[-.2],operation:[\\"multiply\\"],...ye};const Se=[{result:e.HS.executeBasedOnType(ye,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let He={};He.second=D,e.HS.updateListInputs(He),He={first:[360],second:[1],operation:[\\"divide\\"],...He};const fe=[{result:e.HS.executeBasedOnType(He,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}],he={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let xe={};xe.shape=ae,e.HS.updateListInputs(xe),xe={...he,...xe};const ve=[{result:await e.HS.executeBasedOnTypeAsync(xe,!1,(e=>t.occt.fillets.filletEdges(e))),transformers:[]}];let Oe={};Oe.start=L,Oe.end=ie,e.HS.updateListInputs(Oe),Oe={start:[[0,0,0]],end:[[0,1,0]],...Oe};const Ie=[{result:await e.HS.executeBasedOnTypeAsync(Oe,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let Le={};Le.second=pe,e.HS.updateListInputs(Le),Le={first:[360],second:[1],operation:[\\"divide\\"],...Le};const Be=[{result:e.HS.executeBasedOnType(Le,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let we={};we.start=w,we.end=ie,e.HS.updateListInputs(we),we={start:[[0,0,0]],end:[[0,1,0]],...we};const Te=[{result:await e.HS.executeBasedOnTypeAsync(we,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let ge={};ge.y=le,e.HS.updateListInputs(ge),ge={x:[0],y:[0],z:[.05],...ge};const Ae=[{result:e.HS.executeBasedOnType(ge,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Ee={};Ee.y=me,Ee.z=Q,e.HS.updateListInputs(Ee),Ee={x:[0],y:[0],z:[-1],...Ee};const be=[{result:e.HS.executeBasedOnType(Ee,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let ze={};ze.y=me,ze.z=J,e.HS.updateListInputs(ze),ze={x:[0],y:[0],z:[0],...ze};const We=[{result:e.HS.executeBasedOnType(ze,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Ce={};Ce.z=Se,e.HS.updateListInputs(Ce),Ce={x:[0],y:[0],z:[0],...Ce};const Pe=[{result:e.HS.executeBasedOnType(Ce,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Xe={};Xe.step=fe,e.HS.updateListInputs(Xe),Xe={step:[.1],min:[0],max:[360],...Xe};const Ye=e.HS.executeBasedOnType(Xe,!1,(e=>t.vector.span(e))),Ze=[];for(let e=0;e<1;e++)Ze.push({type:\\"flat\\"});const Ne=[{result:Ye,transformers:Ze}];let ke={};ke.first=Se,e.HS.updateListInputs(ke),ke={first:[2],second:[-2],operation:[\\"multiply\\"],...ke};e.HS.executeBasedOnType(ke,!1,(e=>t.math.twoNrOperation(e)));let Me={};Me.listElements=ve,e.HS.updateListInputs(Me),Me={...Me};const Fe=[{result:[Me.listElements?Me.listElements:[]]}],De={shape:[void 0],axis:[[0,0,1]],angle:[0]};let Re={};Re.shape=Ie,Re.axis=o,Re.angle=Be,e.HS.updateListInputs(Re),Re={...De,...Re};const je=[{result:await e.HS.executeBasedOnTypeAsync(Re,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let qe={};qe.first=Be,e.HS.updateListInputs(qe),qe={first:[1],second:[.4],operation:[\\"multiply\\"],...qe};const Ve=[{result:e.HS.executeBasedOnType(qe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Ge={};Ge.first=Be,e.HS.updateListInputs(Ge),Ge={first:[1],second:[.6],operation:[\\"multiply\\"],...Ge};const Je=[{result:e.HS.executeBasedOnType(Ge,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Ke={};Ke.listElements=[Ae[0],j[0],be[0],We[0]],e.HS.updateListInputs(Ke),Ke={...Ke};const Qe=[{result:[Ke.listElements?Ke.listElements:[]]}];let Ue={};Ue.item=Ne,e.HS.updateListInputs(Ue),Ue={...Ue};const $e=[{result:Ue.item}],_e={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let et={};et.shape=je,et.nrOfDivisions=l,e.HS.updateListInputs(et),et={..._e,...et};const tt=[{result:await e.HS.executeBasedOnTypeAsync(et,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],st={shape:[void 0],axis:[[0,0,1]],angle:[0]};let nt={};nt.shape=Te,nt.axis=o,nt.angle=[Ve[0],Je[0]],e.HS.updateListInputs(nt),nt={...st,...nt};const rt=[{result:await e.HS.executeBasedOnTypeAsync(nt,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let at={};at.number=Ve,e.HS.updateListInputs(at),at={number:[1],operation:[\\"negate\\"],...at};const ot=[{result:e.HS.executeBasedOnType(at,!1,(e=>t.math.oneNrOperation(e))),transformers:[]}],it={points:[void 0]};let ct={};ct.points=Qe,e.HS.updateListInputs(ct),ct={...it,...ct};const pt=[{result:await e.HS.executeBasedOnTypeAsync(ct,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];e.HS.drawNodeMeshes(pt,t);const ut={list:[void 0],pattern:[[!0,!0,!1]]};let lt={};lt.list=tt,lt.pattern=u,e.HS.updateListInputs(lt),lt={...ut,...lt};const dt=[{result:e.HS.executeBasedOnType(lt,!0,(e=>t.lists.getByPattern(e))),transformers:[]}];let mt={};mt.listElements=rt,e.HS.updateListInputs(mt),mt={...mt};const yt=[{result:[mt.listElements?mt.listElements:[]]}],St={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let Ht={};Ht.shape=pt,Ht.origin=We,Ht.direction=f,e.HS.updateListInputs(Ht),Ht={...St,...Ht};const ft=[{result:await e.HS.executeBasedOnTypeAsync(Ht,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],ht={shape:[void 0]};let xt={};xt.shape=pt,e.HS.updateListInputs(xt),xt={...ht,...xt};const vt=await e.HS.executeBasedOnTypeAsync(xt,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),Ot=[];for(let e=0;e<1;e++)Ot.push({type:\\"flat\\"});const It=[{result:vt,transformers:Ot}],Lt={list:[void 0],index:[0],clone:[!0]};let Bt={};Bt.list=yt,e.HS.updateListInputs(Bt),Bt={...Lt,...Bt};const wt=[{result:e.HS.executeBasedOnType(Bt,!1,(e=>t.lists.getItem(e))),transformers:[]}],Tt={shape:[void 0]};let gt={};gt.shape=ft,e.HS.updateListInputs(gt),gt={...Tt,...gt};const At=[{result:await e.HS.executeBasedOnTypeAsync(gt,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),transformers:[]}],Et={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let bt={};bt.shape=wt,bt.nrOfDivisions=l,e.HS.updateListInputs(bt),bt={...Et,...bt};const zt=[{result:await e.HS.executeBasedOnTypeAsync(bt,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],Wt={list:[void 0],index:[3],clone:[!0]};let Ct={};Ct.list=At,e.HS.updateListInputs(Ct),Ct={...Wt,...Ct};const Pt=[{result:e.HS.executeBasedOnType(Ct,!1,(e=>t.lists.removeItemAtIndex(e))),transformers:[]}],Xt={list:[void 0],pattern:[[!0,!0,!1]]};let Yt={};Yt.list=zt,Yt.pattern=c,e.HS.updateListInputs(Yt),Yt={...Xt,...Yt};const Zt=[{result:e.HS.executeBasedOnType(Yt,!1,(e=>t.lists.getByPattern(e))),transformers:[]}],Nt={list:[void 0],clone:[!0]};let kt={};kt.list=Pt,e.HS.updateListInputs(kt),kt={...Nt,...kt};const Mt=e.HS.executeBasedOnType(kt,!1,(e=>t.lists.reverse(e))),Ft=[];for(let e=0;e<1;e++)Ft.push({type:\\"flat\\"});const Dt=[{result:Mt,transformers:Ft}];let Rt={};Rt.listElements=[Zt[0],dt[0]],e.HS.updateListInputs(Rt),Rt={...Rt};const jt=[{result:[Rt.listElements?Rt.listElements:[]]}];let qt={};qt.listElements=[It[0],Dt[0]],e.HS.updateListInputs(qt),qt={...qt};const Vt=[{result:[qt.listElements?qt.listElements:[]]}],Gt={list:[void 0],clone:[!0]};let Jt={};Jt.list=jt,e.HS.updateListInputs(Jt),Jt={...Gt,...Jt};const Kt=e.HS.executeBasedOnType(Jt,!1,(e=>t.lists.flipLists(e))),Qt=[];for(let e=0;e<2;e++)Qt.push({type:\\"flat\\"});const Ut=[{result:Kt,transformers:Qt}],$t={points:[void 0]};let _t={};_t.points=Vt,e.HS.updateListInputs(_t),_t={...$t,..._t};const es=[{result:await e.HS.executeBasedOnTypeAsync(_t,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let ts={};ts.listElements=Ut,e.HS.updateListInputs(ts),ts={...ts};const ss=[{result:[ts.listElements?ts.listElements:[]]}],ns={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let rs={};rs.shape=es,e.HS.updateListInputs(rs),rs={...ns,...rs};const as=[{result:await e.HS.executeBasedOnTypeAsync(rs,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],os={points:[void 0]};let is={};is.points=ss,e.HS.updateListInputs(is),is={...os,...is};const cs=[{result:await e.HS.executeBasedOnTypeAsync(is,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}],ps={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let us={};us.shape=as,us.direction=x,e.HS.updateListInputs(us),us={...ps,...us};const ls=[{result:await e.HS.executeBasedOnTypeAsync(us,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],ds={shape:[void 0]};let ms={};ms.shape=as,e.HS.updateListInputs(ms),ms={...ds,...ms};const ys=[{result:await e.HS.executeBasedOnTypeAsync(ms,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],Ss={shape:[void 0]};let Hs={};Hs.shape=as,e.HS.updateListInputs(Hs),Hs={...Ss,...Hs};const fs=[{result:await e.HS.executeBasedOnTypeAsync(Hs,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],hs={shape:[void 0]};let xs={};xs.shape=as,e.HS.updateListInputs(xs),xs={...hs,...xs};const vs=[{result:await e.HS.executeBasedOnTypeAsync(xs,!1,(e=>t.occt.shapes.wire.closeOpenWire(e))),transformers:[]}],Os={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Is={};Is.shape=vs,Is.direction=x,e.HS.updateListInputs(Is),Is={...Os,...Is};const Ls=[{result:await e.HS.executeBasedOnTypeAsync(Is,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],Bs={shape:[void 0],radius:[1],radiusList:[void 0],indexes:[void 0],direction:[[0,1,0]]};let ws={};ws.shape=cs,ws.direction=g,e.HS.updateListInputs(ws),ws={...Bs,...ws};const Ts=[{result:await e.HS.executeBasedOnTypeAsync(ws,!1,(e=>t.occt.fillets.fillet3DWire(e))),transformers:[]}],gs={shape:[void 0],face:[void 0],distance:[-.2],tolerance:[.1]};let As={};As.shape=ls,e.HS.updateListInputs(As),As={...gs,...As};const Es=[{result:await e.HS.executeBasedOnTypeAsync(As,!1,(e=>t.occt.operations.offset(e))),transformers:[]}],bs={shape:[void 0],index:[0]};let zs={};zs.shape=ls,e.HS.updateListInputs(zs),zs={...bs,...zs};const Ws=[{result:await e.HS.executeBasedOnTypeAsync(zs,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}];let Cs={};Cs.item=ys,e.HS.updateListInputs(Cs),Cs={...Cs};const Ps=[{result:Cs.item}];let Xs={};Xs.item=fs,e.HS.updateListInputs(Xs),Xs={...Xs};const Ys=[{result:Xs.item}];let Zs={};Zs.start=fs,Zs.end=ys,e.HS.updateListInputs(Zs),Zs={start:[[0,0,0]],end:[[0,1,0]],...Zs};const Ns=[{result:await e.HS.executeBasedOnTypeAsync(Zs,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];e.HS.drawNodeMeshes(Ns,t);const ks={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Ms={};Ms.shape=Ts,Ms.angle=ot,Ms.direction=o,e.HS.updateListInputs(Ms),Ms={...ks,...Ms};const Fs=[{result:await e.HS.executeBasedOnTypeAsync(Ms,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],Ds={shape:[void 0]};let Rs={};Rs.shape=Ls,e.HS.updateListInputs(Rs),Rs={...Ds,...Rs};const js=[{result:await e.HS.executeBasedOnTypeAsync(Rs,!1,(e=>t.occt.shapes.solid.fromClosedShell(e))),transformers:[]}],qs={shape:[void 0],index:[1]};let Vs={};Vs.shape=Ws,e.HS.updateListInputs(Vs),Vs={...qs,...Vs};const Gs=[{result:await e.HS.executeBasedOnTypeAsync(Vs,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}],Js={shape:[void 0],index:[0]};let Ks={};Ks.shape=Es,e.HS.updateListInputs(Ks),Ks={...Js,...Ks};const Qs=[{result:await e.HS.executeBasedOnTypeAsync(Ks,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}],Us={shape:[void 0],translation:[[0,0,0]]};let $s={};$s.shape=Ns,$s.translation=E,e.HS.updateListInputs($s),$s={...Us,...$s};const _s=[{result:await e.HS.executeBasedOnTypeAsync($s,!1,(e=>t.occt.transforms.translate(e))),transformers:[]}],en={shape:[void 0],direction:[[0,1,0]]};let tn={};tn.shape=Fs,tn.direction=Pe,e.HS.updateListInputs(tn),tn={...en,...tn};const sn=[{result:await e.HS.executeBasedOnTypeAsync(tn,!1,(e=>t.occt.operations.extrude(e))),transformers:[]}];let nn={};nn.listElements=js,e.HS.updateListInputs(nn),nn={...nn};const rn=[{result:[nn.listElements?nn.listElements:[]]}],an={shape:[void 0],index:[1]};let on={};on.shape=Qs,e.HS.updateListInputs(on),on={...an,...on};const cn=[{result:await e.HS.executeBasedOnTypeAsync(on,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}];let pn={};pn.listElements=Gs,e.HS.updateListInputs(pn),pn={...pn};const un=[{result:[pn.listElements?pn.listElements:[]]}],ln={shape:[void 0]};let dn={};dn.shape=_s,e.HS.updateListInputs(dn),dn={...ln,...dn};const mn=[{result:await e.HS.executeBasedOnTypeAsync(dn,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],yn={shape:[void 0]};let Sn={};Sn.shape=_s,e.HS.updateListInputs(Sn),Sn={...yn,...Sn};const Hn=[{result:await e.HS.executeBasedOnTypeAsync(Sn,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],fn={shapes:[void 0]};let hn={};hn.shapes=un,e.HS.updateListInputs(hn),hn={...fn,...hn};const xn=[{result:await e.HS.executeBasedOnTypeAsync(hn,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let vn={};vn.listElements=cn,e.HS.updateListInputs(vn),vn={...vn};const On=[{result:[vn.listElements?vn.listElements:[]]}],In={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let Ln={};Ln.shape=sn,Ln.shapes=Fe,e.HS.updateListInputs(Ln),Ln={...In,...Ln};const Bn=[{result:await e.HS.executeBasedOnTypeAsync(Ln,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}];let wn={};wn.item=Hn,e.HS.updateListInputs(wn),wn={...wn};const Tn=[{result:wn.item}];let gn={};gn.item=mn,e.HS.updateListInputs(gn),gn={...gn};const An=[{result:gn.item}],En={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let bn={};bn.shape=Bn,bn.shapes=rn,e.HS.updateListInputs(bn),bn={...En,...bn};const zn=[{result:await e.HS.executeBasedOnTypeAsync(bn,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}],Wn={shapes:[void 0]};let Cn={};Cn.shapes=On,e.HS.updateListInputs(Cn),Cn={...Wn,...Cn};const Pn=[{result:await e.HS.executeBasedOnTypeAsync(Cn,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let Xn={};Xn.listElements=[Ps[0],Tn[0],An[0],Ys[0]],e.HS.updateListInputs(Xn),Xn={...Xn};const Yn=[{result:[Xn.listElements?Xn.listElements:[]]}],Zn={shape:[void 0],origin:[[0,0,0]],normal:[[0,0,1]]};let Nn={};Nn.shape=zn,Nn.normal=m,e.HS.updateListInputs(Nn),Nn={...Zn,...Nn};const kn=[{result:await e.HS.executeBasedOnTypeAsync(Nn,!1,(e=>t.occt.transforms.mirrorAlongNormal(e))),transformers:[]}];let Mn={};Mn.listElements=[xn[0],Pn[0]],e.HS.updateListInputs(Mn),Mn={...Mn};const Fn=[{result:[Mn.listElements?Mn.listElements:[]]}],Dn={points:[void 0]};let Rn={};Rn.points=Yn,e.HS.updateListInputs(Rn),Rn={...Dn,...Rn};const jn=[{result:await e.HS.executeBasedOnTypeAsync(Rn,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let qn={};qn.listElements=[kn[0],zn[0]],e.HS.updateListInputs(qn),qn={...qn};const Vn=[{result:[qn.listElements?qn.listElements:[]]}],Gn={shapes:[void 0],makeSolid:[!1]};let Jn={};Jn.shapes=Fn,e.HS.updateListInputs(Jn),Jn={...Gn,...Jn};const Kn=[{result:await e.HS.executeBasedOnTypeAsync(Jn,!1,(e=>t.occt.operations.loft(e))),transformers:[]}],Qn={shape:[void 0],radius:[.5],radiusList:[void 0],indexes:[void 0]};let Un={};Un.shape=jn,e.HS.updateListInputs(Un),Un={...Qn,...Un};const $n=[{result:await e.HS.executeBasedOnTypeAsync(Un,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],_n={shapes:[void 0]};let er={};er.shapes=Vn,e.HS.updateListInputs(er),er={..._n,...er};const tr=[{result:await e.HS.executeBasedOnTypeAsync(er,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],sr={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let nr={};nr.shape=Kn,nr.origin=We,nr.direction=f,e.HS.updateListInputs(nr),nr={...sr,...nr};const rr=[{result:await e.HS.executeBasedOnTypeAsync(nr,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],ar={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let or={};or.shape=$n,or.direction=x,e.HS.updateListInputs(or),or={...ar,...or};const ir=[{result:await e.HS.executeBasedOnTypeAsync(or,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}];let cr={};cr.listElements=[ls[0],Es[0],Kn[0],rr[0]],e.HS.updateListInputs(cr),cr={...cr};const pr=[{result:[cr.listElements?cr.listElements:[]]}],ur={shape:[void 0],offset:[-.1]};let lr={};lr.shape=ir,e.HS.updateListInputs(lr),lr={...ur,...lr};const dr=[{result:await e.HS.executeBasedOnTypeAsync(lr,!1,(e=>t.occt.operations.makeThickSolidSimple(e))),transformers:[]}],mr={shape:[void 0],angle:[0],center:[[0,0,0]],axis:[[0,0,1]]};let yr={};yr.shape=tr,yr.angle=$e,yr.axis=k,e.HS.updateListInputs(yr),yr={...mr,...yr};const Sr=[{result:await e.HS.executeBasedOnTypeAsync(yr,!1,(e=>t.occt.transforms.rotateAroundCenter(e))),transformers:[]}],Hr={shapes:[void 0],tolerance:[1e-7]};let fr={};fr.shapes=pr,e.HS.updateListInputs(fr),fr={...Hr,...fr};const hr=[{result:await e.HS.executeBasedOnTypeAsync(fr,!1,(e=>t.occt.shapes.shell.sewFaces(e))),transformers:[]}],xr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let vr={};vr.entity=dr,vr.options=z,e.HS.updateListInputs(vr),vr={...xr,...vr};await e.HS.executeBasedOnTypeAsync(vr,!1,(e=>t.draw.drawAnyAsync(e)));let Or={};Or.listElements=Sr,e.HS.updateListInputs(Or),Or={...Or};const Ir=[{result:[Or.listElements?Or.listElements:[]]}],Lr={shapes:[void 0]};let Br={};Br.shapes=Ir,e.HS.updateListInputs(Br),Br={...Lr,...Br};const wr=[{result:await e.HS.executeBasedOnTypeAsync(Br,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}];let Tr={};Tr.listElements=[hr[0],ve[0],wr[0]],e.HS.updateListInputs(Tr),Tr={...Tr};const gr=[{result:[Tr.listElements?Tr.listElements:[]]}],Ar={shapes:[void 0]};let Er={};Er.shapes=gr,e.HS.updateListInputs(Er),Er={...Ar,...Er};const br=[{result:await e.HS.executeBasedOnTypeAsync(Er,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],zr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let Wr={};Wr.entity=br,Wr.options=ne,e.HS.updateListInputs(Wr),Wr={...zr,...Wr};await e.HS.executeBasedOnTypeAsync(Wr,!1,(e=>t.draw.drawAnyAsync(e)))}(BitByBit,bitbybit,bitbybitRunnerResult,bitbybitRunnerInputs,Bit);\"}' }; + + + + + + + \ No newline at end of file diff --git a/examples/runner/threejs/full/inline-include/index.html b/examples/runner/threejs/full/inline-include/index.html index c7c0d764..82981e31 100644 --- a/examples/runner/threejs/full/inline-include/index.html +++ b/examples/runner/threejs/full/inline-include/index.html @@ -34,7 +34,7 @@ // This function simply outputs the script that was exported from the Rete editor by clicking "Export to Runner" and selecting Minify option. function exportedScript() { - return '{\"type\":\"rete\",\"version\":\"0.20.13\",\"script\":\"async function(e,t,s,n,r){let a={};a={x:[0],y:[0],z:[1],...a};const o=[{result:e.HS.executeBasedOnType(a,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let i={};i={text:[\\"[true,false]\\"],...i};const p=[{result:e.HS.executeBasedOnType(i,!1,(e=>t.json.parse(e))),transformers:[]}];let c={};c={text:[\\"[false,true]\\"],...c};const u=[{result:e.HS.executeBasedOnType(c,!1,(e=>t.json.parse(e))),transformers:[]}],l=[{result:[5],transformers:[]}];let d={};d={x:[1],y:[0],z:[0],...d};const m=[{result:e.HS.executeBasedOnType(d,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}],y=[{result:[12],transformers:[]}],S=[{result:[7],transformers:[]}];let H={};H={x:[0],y:[1],z:[0],...H};const f=[{result:e.HS.executeBasedOnType(H,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let h={};h={x:[0],y:[0],z:[1],...h};const x=[{result:e.HS.executeBasedOnType(h,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let v={};v={number:[.4],...v};const O=[{result:e.HS.executeBasedOnType(v,!1,(e=>t.math.number(e))),transformers:[]}];let I={};I={x:[0],y:[0],z:[-1],...I};const L=[{result:e.HS.executeBasedOnType(I,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let B={};B={x:[0],y:[0],z:[-2],...B};const T=[{result:e.HS.executeBasedOnType(B,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let w={};w={x:[0],y:[0],z:[1],...w};const A=[{result:e.HS.executeBasedOnType(w,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let E={};E={x:[0],y:[1.5],z:[0],...E};const g=[{result:e.HS.executeBasedOnType(E,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let b={};b={...{faceOpacity:[.5],edgeOpacity:[.5],edgeColour:[\\"#000000\\"],faceColour:[\\"#212121\\"],vertexColour:[\\"#ff00ff\\"],faceMaterial:[void 0],edgeWidth:[2],vertexSize:[.03],drawEdges:[!0],drawFaces:[!0],drawVertices:[!1],precision:[.02],drawEdgeIndexes:[!1],edgeIndexHeight:[.06],edgeIndexColour:[\\"ff00ff\\"],drawFaceIndexes:[!1],faceIndexHeight:[.06],faceIndexColour:[\\"#0000ff\\"]},...b};const W=[{result:e.HS.executeBasedOnType(b,!1,(e=>t.draw.optionsOcctShape(e))),transformers:[]}];let z={};z={x:[0],y:[0],z:[-1],...z};const P=[{result:e.HS.executeBasedOnType(z,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let X={};X={x:[0],y:[0],z:[-1.5],...X};const Y=[{result:e.HS.executeBasedOnType(X,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Z={};Z={x:[0],y:[0],z:[1],...Z};const N=[{result:e.HS.executeBasedOnType(Z,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let C={number:[{result:[20],transformers:[]}]};e.HS.updateListInputs(C),C={number:[20],...C};const k=[{result:e.HS.executeBasedOnType(C,!1,(e=>t.math.number(e))),transformers:[]}];let D={};D.y=y,e.HS.updateListInputs(D),D={x:[0],y:[0],z:[0],...D};const F=[{result:e.HS.executeBasedOnType(D,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let M={};M.item=y,e.HS.updateListInputs(M),M={...M};const R=[{result:M.item}];let j={};j.first=S,e.HS.updateListInputs(j),j={first:[1],second:[-2],operation:[\\"divide\\"],...j};const q=[{result:e.HS.executeBasedOnType(j,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let V={};V.first=S,e.HS.updateListInputs(V),V={first:[1],second:[-4],operation:[\\"divide\\"],...V};const G=[{result:e.HS.executeBasedOnType(V,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let J={};J.first=y,J.second=O,e.HS.updateListInputs(J),J={first:[1],second:[.4],operation:[\\"add\\"],...J};const K=[{result:e.HS.executeBasedOnType(J,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Q={};Q.item=S,e.HS.updateListInputs(Q),Q={...Q};const U=[{result:Q.item}];let $={};$.center=Y,$.direction=P,e.HS.updateListInputs($),$={radius:[3],height:[1.9],center:[[0,0,0]],direction:[[0,1,0]],...$};const _=[{result:await e.HS.executeBasedOnTypeAsync($,!1,(e=>t.occt.shapes.solid.createCylinder(e))),transformers:[]}];let ee={};ee.y=K,e.HS.updateListInputs(ee),ee={x:[0],y:[12],z:[0],...ee};const te=[{result:e.HS.executeBasedOnType(ee,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let se={};se.first=k,e.HS.updateListInputs(se),se={first:[1],second:[3],operation:[\\"multiply\\"],...se};const ne=[{result:e.HS.executeBasedOnType(se,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let re={};re.first=R,re.second=O,e.HS.updateListInputs(re),re={first:[1],second:[.4],operation:[\\"add\\"],...re};const ae=[{result:e.HS.executeBasedOnType(re,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let oe={};oe.first=R,oe.second=O,e.HS.updateListInputs(oe),oe={first:[1],second:[.4],operation:[\\"subtract\\"],...oe};const ie=[{result:e.HS.executeBasedOnType(oe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let pe={};pe.first=U,e.HS.updateListInputs(pe),pe={first:[1],second:[-.2],operation:[\\"multiply\\"],...pe};const ce=[{result:e.HS.executeBasedOnType(pe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let ue={};ue.second=k,e.HS.updateListInputs(ue),ue={first:[360],second:[1],operation:[\\"divide\\"],...ue};const le=[{result:e.HS.executeBasedOnType(ue,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}],de={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let me={};me.shape=_,e.HS.updateListInputs(me),me={...de,...me};const ye=[{result:await e.HS.executeBasedOnTypeAsync(me,!1,(e=>t.occt.fillets.filletEdges(e))),transformers:[]}];let Se={};Se.start=L,Se.end=te,e.HS.updateListInputs(Se),Se={start:[[0,0,0]],end:[[0,1,0]],...Se};const He=[{result:await e.HS.executeBasedOnTypeAsync(Se,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let fe={};fe.second=ne,e.HS.updateListInputs(fe),fe={first:[360],second:[1],operation:[\\"divide\\"],...fe};const he=[{result:e.HS.executeBasedOnType(fe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let xe={};xe.start=T,xe.end=te,e.HS.updateListInputs(xe),xe={start:[[0,0,0]],end:[[0,1,0]],...xe};const ve=[{result:await e.HS.executeBasedOnTypeAsync(xe,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let Oe={};Oe.y=ae,e.HS.updateListInputs(Oe),Oe={x:[0],y:[0],z:[.05],...Oe};const Ie=[{result:e.HS.executeBasedOnType(Oe,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Le={};Le.y=ie,Le.z=G,e.HS.updateListInputs(Le),Le={x:[0],y:[0],z:[-1],...Le};const Be=[{result:e.HS.executeBasedOnType(Le,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Te={};Te.y=ie,Te.z=q,e.HS.updateListInputs(Te),Te={x:[0],y:[0],z:[0],...Te};const we=[{result:e.HS.executeBasedOnType(Te,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Ae={};Ae.z=ce,e.HS.updateListInputs(Ae),Ae={x:[0],y:[0],z:[0],...Ae};const Ee=[{result:e.HS.executeBasedOnType(Ae,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let ge={};ge.step=le,e.HS.updateListInputs(ge),ge={step:[.1],min:[0],max:[360],...ge};const be=e.HS.executeBasedOnType(ge,!1,(e=>t.vector.span(e))),We=[];for(let e=0;e<1;e++)We.push({type:\\"flat\\"});const ze=[{result:be,transformers:We}];let Pe={};Pe.first=ce,e.HS.updateListInputs(Pe),Pe={first:[2],second:[-2],operation:[\\"multiply\\"],...Pe};e.HS.executeBasedOnType(Pe,!1,(e=>t.math.twoNrOperation(e)));let Xe={};Xe.listElements=ye,e.HS.updateListInputs(Xe),Xe={...Xe};const Ye=[{result:[Xe.listElements?Xe.listElements:[]]}],Ze={shape:[void 0],axis:[[0,0,1]],angle:[0]};let Ne={};Ne.shape=He,Ne.axis=o,Ne.angle=he,e.HS.updateListInputs(Ne),Ne={...Ze,...Ne};const Ce=[{result:await e.HS.executeBasedOnTypeAsync(Ne,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let ke={};ke.first=he,e.HS.updateListInputs(ke),ke={first:[1],second:[.4],operation:[\\"multiply\\"],...ke};const De=[{result:e.HS.executeBasedOnType(ke,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Fe={};Fe.first=he,e.HS.updateListInputs(Fe),Fe={first:[1],second:[.6],operation:[\\"multiply\\"],...Fe};const Me=[{result:e.HS.executeBasedOnType(Fe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Re={};Re.listElements=[Ie[0],F[0],Be[0],we[0]],e.HS.updateListInputs(Re),Re={...Re};const je=[{result:[Re.listElements?Re.listElements:[]]}];let qe={};qe.item=ze,e.HS.updateListInputs(qe),qe={...qe};const Ve=[{result:qe.item}],Ge={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let Je={};Je.shape=Ce,Je.nrOfDivisions=l,e.HS.updateListInputs(Je),Je={...Ge,...Je};const Ke=[{result:await e.HS.executeBasedOnTypeAsync(Je,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],Qe={shape:[void 0],axis:[[0,0,1]],angle:[0]};let Ue={};Ue.shape=ve,Ue.axis=o,Ue.angle=[De[0],Me[0]],e.HS.updateListInputs(Ue),Ue={...Qe,...Ue};const $e=[{result:await e.HS.executeBasedOnTypeAsync(Ue,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let _e={};_e.number=De,e.HS.updateListInputs(_e),_e={number:[1],operation:[\\"negate\\"],..._e};const et=[{result:e.HS.executeBasedOnType(_e,!1,(e=>t.math.oneNrOperation(e))),transformers:[]}],tt={points:[void 0]};let st={};st.points=je,e.HS.updateListInputs(st),st={...tt,...st};const nt=[{result:await e.HS.executeBasedOnTypeAsync(st,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}],rt={list:[void 0],pattern:[[!0,!0,!1]]};let at={};at.list=Ke,at.pattern=u,e.HS.updateListInputs(at),at={...rt,...at};const ot=[{result:e.HS.executeBasedOnType(at,!0,(e=>t.lists.getByPattern(e))),transformers:[]}];let it={};it.listElements=$e,e.HS.updateListInputs(it),it={...it};const pt=[{result:[it.listElements?it.listElements:[]]}],ct={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let ut={};ut.shape=nt,ut.origin=we,ut.direction=f,e.HS.updateListInputs(ut),ut={...ct,...ut};const lt=[{result:await e.HS.executeBasedOnTypeAsync(ut,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],dt={shape:[void 0]};let mt={};mt.shape=nt,e.HS.updateListInputs(mt),mt={...dt,...mt};const yt=await e.HS.executeBasedOnTypeAsync(mt,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),St=[];for(let e=0;e<1;e++)St.push({type:\\"flat\\"});const Ht=[{result:yt,transformers:St}],ft={list:[void 0],index:[0],clone:[!0]};let ht={};ht.list=pt,e.HS.updateListInputs(ht),ht={...ft,...ht};const xt=[{result:e.HS.executeBasedOnType(ht,!1,(e=>t.lists.getItem(e))),transformers:[]}],vt={shape:[void 0]};let Ot={};Ot.shape=lt,e.HS.updateListInputs(Ot),Ot={...vt,...Ot};const It=[{result:await e.HS.executeBasedOnTypeAsync(Ot,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),transformers:[]}],Lt={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let Bt={};Bt.shape=xt,Bt.nrOfDivisions=l,e.HS.updateListInputs(Bt),Bt={...Lt,...Bt};const Tt=[{result:await e.HS.executeBasedOnTypeAsync(Bt,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],wt={list:[void 0],index:[3],clone:[!0]};let At={};At.list=It,e.HS.updateListInputs(At),At={...wt,...At};const Et=[{result:e.HS.executeBasedOnType(At,!1,(e=>t.lists.removeItemAtIndex(e))),transformers:[]}],gt={list:[void 0],pattern:[[!0,!0,!1]]};let bt={};bt.list=Tt,bt.pattern=p,e.HS.updateListInputs(bt),bt={...gt,...bt};const Wt=[{result:e.HS.executeBasedOnType(bt,!1,(e=>t.lists.getByPattern(e))),transformers:[]}],zt={list:[void 0],clone:[!0]};let Pt={};Pt.list=Et,e.HS.updateListInputs(Pt),Pt={...zt,...Pt};const Xt=e.HS.executeBasedOnType(Pt,!1,(e=>t.lists.reverse(e))),Yt=[];for(let e=0;e<1;e++)Yt.push({type:\\"flat\\"});const Zt=[{result:Xt,transformers:Yt}];let Nt={};Nt.listElements=[Wt[0],ot[0]],e.HS.updateListInputs(Nt),Nt={...Nt};const Ct=[{result:[Nt.listElements?Nt.listElements:[]]}];let kt={};kt.listElements=[Ht[0],Zt[0]],e.HS.updateListInputs(kt),kt={...kt};const Dt=[{result:[kt.listElements?kt.listElements:[]]}],Ft={list:[void 0],clone:[!0]};let Mt={};Mt.list=Ct,e.HS.updateListInputs(Mt),Mt={...Ft,...Mt};const Rt=e.HS.executeBasedOnType(Mt,!1,(e=>t.lists.flipLists(e))),jt=[];for(let e=0;e<2;e++)jt.push({type:\\"flat\\"});const qt=[{result:Rt,transformers:jt}],Vt={points:[void 0]};let Gt={};Gt.points=Dt,e.HS.updateListInputs(Gt),Gt={...Vt,...Gt};const Jt=[{result:await e.HS.executeBasedOnTypeAsync(Gt,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let Kt={};Kt.listElements=qt,e.HS.updateListInputs(Kt),Kt={...Kt};const Qt=[{result:[Kt.listElements?Kt.listElements:[]]}],Ut={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let $t={};$t.shape=Jt,e.HS.updateListInputs($t),$t={...Ut,...$t};const _t=[{result:await e.HS.executeBasedOnTypeAsync($t,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],es={points:[void 0]};let ts={};ts.points=Qt,e.HS.updateListInputs(ts),ts={...es,...ts};const ss=[{result:await e.HS.executeBasedOnTypeAsync(ts,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}],ns={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let rs={};rs.shape=_t,rs.direction=x,e.HS.updateListInputs(rs),rs={...ns,...rs};const as=[{result:await e.HS.executeBasedOnTypeAsync(rs,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],os={shape:[void 0]};let is={};is.shape=_t,e.HS.updateListInputs(is),is={...os,...is};const ps=[{result:await e.HS.executeBasedOnTypeAsync(is,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],cs={shape:[void 0]};let us={};us.shape=_t,e.HS.updateListInputs(us),us={...cs,...us};const ls=[{result:await e.HS.executeBasedOnTypeAsync(us,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],ds={shape:[void 0]};let ms={};ms.shape=_t,e.HS.updateListInputs(ms),ms={...ds,...ms};const ys=[{result:await e.HS.executeBasedOnTypeAsync(ms,!1,(e=>t.occt.shapes.wire.closeOpenWire(e))),transformers:[]}],Ss={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Hs={};Hs.shape=ys,Hs.direction=x,e.HS.updateListInputs(Hs),Hs={...Ss,...Hs};const fs=[{result:await e.HS.executeBasedOnTypeAsync(Hs,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],hs={shape:[void 0],radius:[1],radiusList:[void 0],indexes:[void 0],direction:[[0,1,0]]};let xs={};xs.shape=ss,xs.direction=A,e.HS.updateListInputs(xs),xs={...hs,...xs};const vs=[{result:await e.HS.executeBasedOnTypeAsync(xs,!1,(e=>t.occt.fillets.fillet3DWire(e))),transformers:[]}],Os={shape:[void 0],face:[void 0],distance:[-.2],tolerance:[.1]};let Is={};Is.shape=as,e.HS.updateListInputs(Is),Is={...Os,...Is};const Ls=[{result:await e.HS.executeBasedOnTypeAsync(Is,!1,(e=>t.occt.operations.offset(e))),transformers:[]}],Bs={shape:[void 0],index:[0]};let Ts={};Ts.shape=as,e.HS.updateListInputs(Ts),Ts={...Bs,...Ts};const ws=[{result:await e.HS.executeBasedOnTypeAsync(Ts,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}];let As={};As.item=ps,e.HS.updateListInputs(As),As={...As};const Es=[{result:As.item}];let gs={};gs.item=ls,e.HS.updateListInputs(gs),gs={...gs};const bs=[{result:gs.item}];let Ws={};Ws.start=ls,Ws.end=ps,e.HS.updateListInputs(Ws),Ws={start:[[0,0,0]],end:[[0,1,0]],...Ws};const zs=[{result:await e.HS.executeBasedOnTypeAsync(Ws,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}],Ps={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Xs={};Xs.shape=vs,Xs.angle=et,Xs.direction=o,e.HS.updateListInputs(Xs),Xs={...Ps,...Xs};const Ys=[{result:await e.HS.executeBasedOnTypeAsync(Xs,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],Zs={shape:[void 0]};let Ns={};Ns.shape=fs,e.HS.updateListInputs(Ns),Ns={...Zs,...Ns};const Cs=[{result:await e.HS.executeBasedOnTypeAsync(Ns,!1,(e=>t.occt.shapes.solid.fromClosedShell(e))),transformers:[]}],ks={shape:[void 0],index:[1]};let Ds={};Ds.shape=ws,e.HS.updateListInputs(Ds),Ds={...ks,...Ds};const Fs=[{result:await e.HS.executeBasedOnTypeAsync(Ds,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}],Ms={shape:[void 0],index:[0]};let Rs={};Rs.shape=Ls,e.HS.updateListInputs(Rs),Rs={...Ms,...Rs};const js=[{result:await e.HS.executeBasedOnTypeAsync(Rs,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}],qs={shape:[void 0],translation:[[0,0,0]]};let Vs={};Vs.shape=zs,Vs.translation=g,e.HS.updateListInputs(Vs),Vs={...qs,...Vs};const Gs=[{result:await e.HS.executeBasedOnTypeAsync(Vs,!1,(e=>t.occt.transforms.translate(e))),transformers:[]}],Js={shape:[void 0],direction:[[0,1,0]]};let Ks={};Ks.shape=Ys,Ks.direction=Ee,e.HS.updateListInputs(Ks),Ks={...Js,...Ks};const Qs=[{result:await e.HS.executeBasedOnTypeAsync(Ks,!1,(e=>t.occt.operations.extrude(e))),transformers:[]}];let Us={};Us.listElements=Cs,e.HS.updateListInputs(Us),Us={...Us};const $s=[{result:[Us.listElements?Us.listElements:[]]}],_s={shape:[void 0],index:[1]};let en={};en.shape=js,e.HS.updateListInputs(en),en={..._s,...en};const tn=[{result:await e.HS.executeBasedOnTypeAsync(en,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}];let sn={};sn.listElements=Fs,e.HS.updateListInputs(sn),sn={...sn};const nn=[{result:[sn.listElements?sn.listElements:[]]}],rn={shape:[void 0]};let an={};an.shape=Gs,e.HS.updateListInputs(an),an={...rn,...an};const on=[{result:await e.HS.executeBasedOnTypeAsync(an,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],pn={shape:[void 0]};let cn={};cn.shape=Gs,e.HS.updateListInputs(cn),cn={...pn,...cn};const un=[{result:await e.HS.executeBasedOnTypeAsync(cn,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],ln={shapes:[void 0]};let dn={};dn.shapes=nn,e.HS.updateListInputs(dn),dn={...ln,...dn};const mn=[{result:await e.HS.executeBasedOnTypeAsync(dn,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let yn={};yn.listElements=tn,e.HS.updateListInputs(yn),yn={...yn};const Sn=[{result:[yn.listElements?yn.listElements:[]]}],Hn={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let fn={};fn.shape=Qs,fn.shapes=Ye,e.HS.updateListInputs(fn),fn={...Hn,...fn};const hn=[{result:await e.HS.executeBasedOnTypeAsync(fn,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}];let xn={};xn.item=un,e.HS.updateListInputs(xn),xn={...xn};const vn=[{result:xn.item}];let On={};On.item=on,e.HS.updateListInputs(On),On={...On};const In=[{result:On.item}],Ln={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let Bn={};Bn.shape=hn,Bn.shapes=$s,e.HS.updateListInputs(Bn),Bn={...Ln,...Bn};const Tn=[{result:await e.HS.executeBasedOnTypeAsync(Bn,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}],wn={shapes:[void 0]};let An={};An.shapes=Sn,e.HS.updateListInputs(An),An={...wn,...An};const En=[{result:await e.HS.executeBasedOnTypeAsync(An,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let gn={};gn.listElements=[Es[0],vn[0],In[0],bs[0]],e.HS.updateListInputs(gn),gn={...gn};const bn=[{result:[gn.listElements?gn.listElements:[]]}],Wn={shape:[void 0],origin:[[0,0,0]],normal:[[0,0,1]]};let zn={};zn.shape=Tn,zn.normal=m,e.HS.updateListInputs(zn),zn={...Wn,...zn};const Pn=[{result:await e.HS.executeBasedOnTypeAsync(zn,!1,(e=>t.occt.transforms.mirrorAlongNormal(e))),transformers:[]}];let Xn={};Xn.listElements=[mn[0],En[0]],e.HS.updateListInputs(Xn),Xn={...Xn};const Yn=[{result:[Xn.listElements?Xn.listElements:[]]}],Zn={points:[void 0]};let Nn={};Nn.points=bn,e.HS.updateListInputs(Nn),Nn={...Zn,...Nn};const Cn=[{result:await e.HS.executeBasedOnTypeAsync(Nn,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let kn={};kn.listElements=[Pn[0],Tn[0]],e.HS.updateListInputs(kn),kn={...kn};const Dn=[{result:[kn.listElements?kn.listElements:[]]}],Fn={shapes:[void 0],makeSolid:[!1]};let Mn={};Mn.shapes=Yn,e.HS.updateListInputs(Mn),Mn={...Fn,...Mn};const Rn=[{result:await e.HS.executeBasedOnTypeAsync(Mn,!1,(e=>t.occt.operations.loft(e))),transformers:[]}],jn={shape:[void 0],radius:[.5],radiusList:[void 0],indexes:[void 0]};let qn={};qn.shape=Cn,e.HS.updateListInputs(qn),qn={...jn,...qn};const Vn=[{result:await e.HS.executeBasedOnTypeAsync(qn,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],Gn={shapes:[void 0]};let Jn={};Jn.shapes=Dn,e.HS.updateListInputs(Jn),Jn={...Gn,...Jn};const Kn=[{result:await e.HS.executeBasedOnTypeAsync(Jn,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],Qn={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let Un={};Un.shape=Rn,Un.origin=we,Un.direction=f,e.HS.updateListInputs(Un),Un={...Qn,...Un};const $n=[{result:await e.HS.executeBasedOnTypeAsync(Un,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],_n={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let er={};er.shape=Vn,er.direction=x,e.HS.updateListInputs(er),er={..._n,...er};const tr=[{result:await e.HS.executeBasedOnTypeAsync(er,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}];let sr={};sr.listElements=[as[0],Ls[0],Rn[0],$n[0]],e.HS.updateListInputs(sr),sr={...sr};const nr=[{result:[sr.listElements?sr.listElements:[]]}],rr={shape:[void 0],offset:[-.1]};let ar={};ar.shape=tr,e.HS.updateListInputs(ar),ar={...rr,...ar};const or=[{result:await e.HS.executeBasedOnTypeAsync(ar,!1,(e=>t.occt.operations.makeThickSolidSimple(e))),transformers:[]}],ir={shape:[void 0],angle:[0],center:[[0,0,0]],axis:[[0,0,1]]};let pr={};pr.shape=Kn,pr.angle=Ve,pr.axis=N,e.HS.updateListInputs(pr),pr={...ir,...pr};const cr=[{result:await e.HS.executeBasedOnTypeAsync(pr,!1,(e=>t.occt.transforms.rotateAroundCenter(e))),transformers:[]}],ur={shapes:[void 0],tolerance:[1e-7]};let lr={};lr.shapes=nr,e.HS.updateListInputs(lr),lr={...ur,...lr};const dr=[{result:await e.HS.executeBasedOnTypeAsync(lr,!1,(e=>t.occt.shapes.shell.sewFaces(e))),transformers:[]}],mr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let yr={};yr.entity=or,yr.options=W,e.HS.updateListInputs(yr),yr={...mr,...yr};await e.HS.executeBasedOnTypeAsync(yr,!1,(e=>t.draw.drawAnyAsync(e)));let Sr={};Sr.listElements=cr,e.HS.updateListInputs(Sr),Sr={...Sr};const Hr=[{result:[Sr.listElements?Sr.listElements:[]]}],fr={shapes:[void 0]};let hr={};hr.shapes=Hr,e.HS.updateListInputs(hr),hr={...fr,...hr};const xr=[{result:await e.HS.executeBasedOnTypeAsync(hr,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}];let vr={};vr.listElements=[dr[0],ye[0],xr[0]],e.HS.updateListInputs(vr),vr={...vr};const Or=[{result:[vr.listElements?vr.listElements:[]]}],Ir={shapes:[void 0]};let Lr={};Lr.shapes=Or,e.HS.updateListInputs(Lr),Lr={...Ir,...Lr};const Br=[{result:await e.HS.executeBasedOnTypeAsync(Lr,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],Tr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let wr={};wr.entity=Br,e.HS.updateListInputs(wr),wr={...Tr,...wr};await e.HS.executeBasedOnTypeAsync(wr,!1,(e=>t.draw.drawAnyAsync(e)))}(BitByBit,bitbybit,bitbybitRunnerResult,bitbybitRunnerInputs,Bit);\"}' + return '{\"type\":\"rete\",\"version\":\"0.20.14\",\"script\":\"async function(e,t,s,n,r){let a={};a={x:[0],y:[0],z:[1],...a};const o=[{result:e.HS.executeBasedOnType(a,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let i={};i={text:[\\"[true,false]\\"],...i};const p=[{result:e.HS.executeBasedOnType(i,!1,(e=>t.json.parse(e))),transformers:[]}];let c={};c={text:[\\"[false,true]\\"],...c};const u=[{result:e.HS.executeBasedOnType(c,!1,(e=>t.json.parse(e))),transformers:[]}],l=[{result:[5],transformers:[]}];let d={};d={x:[1],y:[0],z:[0],...d};const m=[{result:e.HS.executeBasedOnType(d,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}],y=[{result:[12],transformers:[]}],S=[{result:[7],transformers:[]}];let H={};H={x:[0],y:[1],z:[0],...H};const f=[{result:e.HS.executeBasedOnType(H,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let h={};h={x:[0],y:[0],z:[1],...h};const x=[{result:e.HS.executeBasedOnType(h,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let v={};v={number:[.4],...v};const O=[{result:e.HS.executeBasedOnType(v,!1,(e=>t.math.number(e))),transformers:[]}];let I={};I={x:[0],y:[0],z:[-1],...I};const L=[{result:e.HS.executeBasedOnType(I,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let B={};B={x:[0],y:[0],z:[-2],...B};const T=[{result:e.HS.executeBasedOnType(B,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let w={};w={x:[0],y:[0],z:[1],...w};const A=[{result:e.HS.executeBasedOnType(w,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let E={};E={x:[0],y:[1.5],z:[0],...E};const g=[{result:e.HS.executeBasedOnType(E,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let b={};b={...{faceOpacity:[.5],edgeOpacity:[.5],edgeColour:[\\"#000000\\"],faceColour:[\\"#212121\\"],vertexColour:[\\"#ff00ff\\"],faceMaterial:[void 0],edgeWidth:[2],vertexSize:[.03],drawEdges:[!0],drawFaces:[!0],drawVertices:[!1],precision:[.02],drawEdgeIndexes:[!1],edgeIndexHeight:[.06],edgeIndexColour:[\\"ff00ff\\"],drawFaceIndexes:[!1],faceIndexHeight:[.06],faceIndexColour:[\\"#0000ff\\"]},...b};const W=[{result:e.HS.executeBasedOnType(b,!1,(e=>t.draw.optionsOcctShape(e))),transformers:[]}];let z={};z={x:[0],y:[0],z:[-1],...z};const P=[{result:e.HS.executeBasedOnType(z,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let X={};X={x:[0],y:[0],z:[-1.5],...X};const Y=[{result:e.HS.executeBasedOnType(X,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Z={};Z={x:[0],y:[0],z:[1],...Z};const N=[{result:e.HS.executeBasedOnType(Z,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let C={number:[{result:[20],transformers:[]}]};e.HS.updateListInputs(C),C={number:[20],...C};const k=[{result:e.HS.executeBasedOnType(C,!1,(e=>t.math.number(e))),transformers:[]}];let D={};D.y=y,e.HS.updateListInputs(D),D={x:[0],y:[0],z:[0],...D};const F=[{result:e.HS.executeBasedOnType(D,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let M={};M.item=y,e.HS.updateListInputs(M),M={...M};const R=[{result:M.item}];let j={};j.first=S,e.HS.updateListInputs(j),j={first:[1],second:[-2],operation:[\\"divide\\"],...j};const q=[{result:e.HS.executeBasedOnType(j,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let V={};V.first=S,e.HS.updateListInputs(V),V={first:[1],second:[-4],operation:[\\"divide\\"],...V};const G=[{result:e.HS.executeBasedOnType(V,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let J={};J.first=y,J.second=O,e.HS.updateListInputs(J),J={first:[1],second:[.4],operation:[\\"add\\"],...J};const K=[{result:e.HS.executeBasedOnType(J,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Q={};Q.item=S,e.HS.updateListInputs(Q),Q={...Q};const U=[{result:Q.item}];let $={};$.center=Y,$.direction=P,e.HS.updateListInputs($),$={radius:[3],height:[1.9],center:[[0,0,0]],direction:[[0,1,0]],...$};const _=[{result:await e.HS.executeBasedOnTypeAsync($,!1,(e=>t.occt.shapes.solid.createCylinder(e))),transformers:[]}];let ee={};ee.y=K,e.HS.updateListInputs(ee),ee={x:[0],y:[12],z:[0],...ee};const te=[{result:e.HS.executeBasedOnType(ee,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let se={};se.first=k,e.HS.updateListInputs(se),se={first:[1],second:[3],operation:[\\"multiply\\"],...se};const ne=[{result:e.HS.executeBasedOnType(se,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let re={};re.first=R,re.second=O,e.HS.updateListInputs(re),re={first:[1],second:[.4],operation:[\\"add\\"],...re};const ae=[{result:e.HS.executeBasedOnType(re,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let oe={};oe.first=R,oe.second=O,e.HS.updateListInputs(oe),oe={first:[1],second:[.4],operation:[\\"subtract\\"],...oe};const ie=[{result:e.HS.executeBasedOnType(oe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let pe={};pe.first=U,e.HS.updateListInputs(pe),pe={first:[1],second:[-.2],operation:[\\"multiply\\"],...pe};const ce=[{result:e.HS.executeBasedOnType(pe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let ue={};ue.second=k,e.HS.updateListInputs(ue),ue={first:[360],second:[1],operation:[\\"divide\\"],...ue};const le=[{result:e.HS.executeBasedOnType(ue,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}],de={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let me={};me.shape=_,e.HS.updateListInputs(me),me={...de,...me};const ye=[{result:await e.HS.executeBasedOnTypeAsync(me,!1,(e=>t.occt.fillets.filletEdges(e))),transformers:[]}];let Se={};Se.start=L,Se.end=te,e.HS.updateListInputs(Se),Se={start:[[0,0,0]],end:[[0,1,0]],...Se};const He=[{result:await e.HS.executeBasedOnTypeAsync(Se,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let fe={};fe.second=ne,e.HS.updateListInputs(fe),fe={first:[360],second:[1],operation:[\\"divide\\"],...fe};const he=[{result:e.HS.executeBasedOnType(fe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let xe={};xe.start=T,xe.end=te,e.HS.updateListInputs(xe),xe={start:[[0,0,0]],end:[[0,1,0]],...xe};const ve=[{result:await e.HS.executeBasedOnTypeAsync(xe,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}];let Oe={};Oe.y=ae,e.HS.updateListInputs(Oe),Oe={x:[0],y:[0],z:[.05],...Oe};const Ie=[{result:e.HS.executeBasedOnType(Oe,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Le={};Le.y=ie,Le.z=G,e.HS.updateListInputs(Le),Le={x:[0],y:[0],z:[-1],...Le};const Be=[{result:e.HS.executeBasedOnType(Le,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Te={};Te.y=ie,Te.z=q,e.HS.updateListInputs(Te),Te={x:[0],y:[0],z:[0],...Te};const we=[{result:e.HS.executeBasedOnType(Te,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let Ae={};Ae.z=ce,e.HS.updateListInputs(Ae),Ae={x:[0],y:[0],z:[0],...Ae};const Ee=[{result:e.HS.executeBasedOnType(Ae,!1,(e=>t.vector.vectorXYZ(e))),transformers:[]}];let ge={};ge.step=le,e.HS.updateListInputs(ge),ge={step:[.1],min:[0],max:[360],...ge};const be=e.HS.executeBasedOnType(ge,!1,(e=>t.vector.span(e))),We=[];for(let e=0;e<1;e++)We.push({type:\\"flat\\"});const ze=[{result:be,transformers:We}];let Pe={};Pe.first=ce,e.HS.updateListInputs(Pe),Pe={first:[2],second:[-2],operation:[\\"multiply\\"],...Pe};e.HS.executeBasedOnType(Pe,!1,(e=>t.math.twoNrOperation(e)));let Xe={};Xe.listElements=ye,e.HS.updateListInputs(Xe),Xe={...Xe};const Ye=[{result:[Xe.listElements?Xe.listElements:[]]}],Ze={shape:[void 0],axis:[[0,0,1]],angle:[0]};let Ne={};Ne.shape=He,Ne.axis=o,Ne.angle=he,e.HS.updateListInputs(Ne),Ne={...Ze,...Ne};const Ce=[{result:await e.HS.executeBasedOnTypeAsync(Ne,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let ke={};ke.first=he,e.HS.updateListInputs(ke),ke={first:[1],second:[.4],operation:[\\"multiply\\"],...ke};const De=[{result:e.HS.executeBasedOnType(ke,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Fe={};Fe.first=he,e.HS.updateListInputs(Fe),Fe={first:[1],second:[.6],operation:[\\"multiply\\"],...Fe};const Me=[{result:e.HS.executeBasedOnType(Fe,!1,(e=>t.math.twoNrOperation(e))),transformers:[]}];let Re={};Re.listElements=[Ie[0],F[0],Be[0],we[0]],e.HS.updateListInputs(Re),Re={...Re};const je=[{result:[Re.listElements?Re.listElements:[]]}];let qe={};qe.item=ze,e.HS.updateListInputs(qe),qe={...qe};const Ve=[{result:qe.item}],Ge={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let Je={};Je.shape=Ce,Je.nrOfDivisions=l,e.HS.updateListInputs(Je),Je={...Ge,...Je};const Ke=[{result:await e.HS.executeBasedOnTypeAsync(Je,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],Qe={shape:[void 0],axis:[[0,0,1]],angle:[0]};let Ue={};Ue.shape=ve,Ue.axis=o,Ue.angle=[De[0],Me[0]],e.HS.updateListInputs(Ue),Ue={...Qe,...Ue};const $e=[{result:await e.HS.executeBasedOnTypeAsync(Ue,!1,(e=>t.occt.transforms.rotate(e))),transformers:[]}];let _e={};_e.number=De,e.HS.updateListInputs(_e),_e={number:[1],operation:[\\"negate\\"],..._e};const et=[{result:e.HS.executeBasedOnType(_e,!1,(e=>t.math.oneNrOperation(e))),transformers:[]}],tt={points:[void 0]};let st={};st.points=je,e.HS.updateListInputs(st),st={...tt,...st};const nt=[{result:await e.HS.executeBasedOnTypeAsync(st,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}],rt={list:[void 0],pattern:[[!0,!0,!1]]};let at={};at.list=Ke,at.pattern=u,e.HS.updateListInputs(at),at={...rt,...at};const ot=[{result:e.HS.executeBasedOnType(at,!0,(e=>t.lists.getByPattern(e))),transformers:[]}];let it={};it.listElements=$e,e.HS.updateListInputs(it),it={...it};const pt=[{result:[it.listElements?it.listElements:[]]}],ct={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let ut={};ut.shape=nt,ut.origin=we,ut.direction=f,e.HS.updateListInputs(ut),ut={...ct,...ut};const lt=[{result:await e.HS.executeBasedOnTypeAsync(ut,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],dt={shape:[void 0]};let mt={};mt.shape=nt,e.HS.updateListInputs(mt),mt={...dt,...mt};const yt=await e.HS.executeBasedOnTypeAsync(mt,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),St=[];for(let e=0;e<1;e++)St.push({type:\\"flat\\"});const Ht=[{result:yt,transformers:St}],ft={list:[void 0],index:[0],clone:[!0]};let ht={};ht.list=pt,e.HS.updateListInputs(ht),ht={...ft,...ht};const xt=[{result:e.HS.executeBasedOnType(ht,!1,(e=>t.lists.getItem(e))),transformers:[]}],vt={shape:[void 0]};let Ot={};Ot.shape=lt,e.HS.updateListInputs(Ot),Ot={...vt,...Ot};const It=[{result:await e.HS.executeBasedOnTypeAsync(Ot,!1,(e=>t.occt.shapes.edge.getCornerPointsOfEdgesForShape(e))),transformers:[]}],Lt={shape:[void 0],nrOfDivisions:[11],removeStartPoint:[!1],removeEndPoint:[!1]};let Bt={};Bt.shape=xt,Bt.nrOfDivisions=l,e.HS.updateListInputs(Bt),Bt={...Lt,...Bt};const Tt=[{result:await e.HS.executeBasedOnTypeAsync(Bt,!1,(e=>t.occt.shapes.wire.divideWireByEqualDistanceToPoints(e))),transformers:[]}],wt={list:[void 0],index:[3],clone:[!0]};let At={};At.list=It,e.HS.updateListInputs(At),At={...wt,...At};const Et=[{result:e.HS.executeBasedOnType(At,!1,(e=>t.lists.removeItemAtIndex(e))),transformers:[]}],gt={list:[void 0],pattern:[[!0,!0,!1]]};let bt={};bt.list=Tt,bt.pattern=p,e.HS.updateListInputs(bt),bt={...gt,...bt};const Wt=[{result:e.HS.executeBasedOnType(bt,!1,(e=>t.lists.getByPattern(e))),transformers:[]}],zt={list:[void 0],clone:[!0]};let Pt={};Pt.list=Et,e.HS.updateListInputs(Pt),Pt={...zt,...Pt};const Xt=e.HS.executeBasedOnType(Pt,!1,(e=>t.lists.reverse(e))),Yt=[];for(let e=0;e<1;e++)Yt.push({type:\\"flat\\"});const Zt=[{result:Xt,transformers:Yt}];let Nt={};Nt.listElements=[Wt[0],ot[0]],e.HS.updateListInputs(Nt),Nt={...Nt};const Ct=[{result:[Nt.listElements?Nt.listElements:[]]}];let kt={};kt.listElements=[Ht[0],Zt[0]],e.HS.updateListInputs(kt),kt={...kt};const Dt=[{result:[kt.listElements?kt.listElements:[]]}],Ft={list:[void 0],clone:[!0]};let Mt={};Mt.list=Ct,e.HS.updateListInputs(Mt),Mt={...Ft,...Mt};const Rt=e.HS.executeBasedOnType(Mt,!1,(e=>t.lists.flipLists(e))),jt=[];for(let e=0;e<2;e++)jt.push({type:\\"flat\\"});const qt=[{result:Rt,transformers:jt}],Vt={points:[void 0]};let Gt={};Gt.points=Dt,e.HS.updateListInputs(Gt),Gt={...Vt,...Gt};const Jt=[{result:await e.HS.executeBasedOnTypeAsync(Gt,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let Kt={};Kt.listElements=qt,e.HS.updateListInputs(Kt),Kt={...Kt};const Qt=[{result:[Kt.listElements?Kt.listElements:[]]}],Ut={shape:[void 0],radius:[.3],radiusList:[void 0],indexes:[void 0]};let $t={};$t.shape=Jt,e.HS.updateListInputs($t),$t={...Ut,...$t};const _t=[{result:await e.HS.executeBasedOnTypeAsync($t,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],es={points:[void 0]};let ts={};ts.points=Qt,e.HS.updateListInputs(ts),ts={...es,...ts};const ss=[{result:await e.HS.executeBasedOnTypeAsync(ts,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}],ns={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let rs={};rs.shape=_t,rs.direction=x,e.HS.updateListInputs(rs),rs={...ns,...rs};const as=[{result:await e.HS.executeBasedOnTypeAsync(rs,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],os={shape:[void 0]};let is={};is.shape=_t,e.HS.updateListInputs(is),is={...os,...is};const ps=[{result:await e.HS.executeBasedOnTypeAsync(is,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],cs={shape:[void 0]};let us={};us.shape=_t,e.HS.updateListInputs(us),us={...cs,...us};const ls=[{result:await e.HS.executeBasedOnTypeAsync(us,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],ds={shape:[void 0]};let ms={};ms.shape=_t,e.HS.updateListInputs(ms),ms={...ds,...ms};const ys=[{result:await e.HS.executeBasedOnTypeAsync(ms,!1,(e=>t.occt.shapes.wire.closeOpenWire(e))),transformers:[]}],Ss={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Hs={};Hs.shape=ys,Hs.direction=x,e.HS.updateListInputs(Hs),Hs={...Ss,...Hs};const fs=[{result:await e.HS.executeBasedOnTypeAsync(Hs,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],hs={shape:[void 0],radius:[1],radiusList:[void 0],indexes:[void 0],direction:[[0,1,0]]};let xs={};xs.shape=ss,xs.direction=A,e.HS.updateListInputs(xs),xs={...hs,...xs};const vs=[{result:await e.HS.executeBasedOnTypeAsync(xs,!1,(e=>t.occt.fillets.fillet3DWire(e))),transformers:[]}],Os={shape:[void 0],face:[void 0],distance:[-.2],tolerance:[.1]};let Is={};Is.shape=as,e.HS.updateListInputs(Is),Is={...Os,...Is};const Ls=[{result:await e.HS.executeBasedOnTypeAsync(Is,!1,(e=>t.occt.operations.offset(e))),transformers:[]}],Bs={shape:[void 0],index:[0]};let Ts={};Ts.shape=as,e.HS.updateListInputs(Ts),Ts={...Bs,...Ts};const ws=[{result:await e.HS.executeBasedOnTypeAsync(Ts,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}];let As={};As.item=ps,e.HS.updateListInputs(As),As={...As};const Es=[{result:As.item}];let gs={};gs.item=ls,e.HS.updateListInputs(gs),gs={...gs};const bs=[{result:gs.item}];let Ws={};Ws.start=ls,Ws.end=ps,e.HS.updateListInputs(Ws),Ws={start:[[0,0,0]],end:[[0,1,0]],...Ws};const zs=[{result:await e.HS.executeBasedOnTypeAsync(Ws,!1,(e=>t.occt.shapes.wire.createLineWire(e))),transformers:[]}],Ps={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let Xs={};Xs.shape=vs,Xs.angle=et,Xs.direction=o,e.HS.updateListInputs(Xs),Xs={...Ps,...Xs};const Ys=[{result:await e.HS.executeBasedOnTypeAsync(Xs,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}],Zs={shape:[void 0]};let Ns={};Ns.shape=fs,e.HS.updateListInputs(Ns),Ns={...Zs,...Ns};const Cs=[{result:await e.HS.executeBasedOnTypeAsync(Ns,!1,(e=>t.occt.shapes.solid.fromClosedShell(e))),transformers:[]}],ks={shape:[void 0],index:[1]};let Ds={};Ds.shape=ws,e.HS.updateListInputs(Ds),Ds={...ks,...Ds};const Fs=[{result:await e.HS.executeBasedOnTypeAsync(Ds,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}],Ms={shape:[void 0],index:[0]};let Rs={};Rs.shape=Ls,e.HS.updateListInputs(Rs),Rs={...Ms,...Rs};const js=[{result:await e.HS.executeBasedOnTypeAsync(Rs,!1,(e=>t.occt.shapes.wire.getWire(e))),transformers:[]}],qs={shape:[void 0],translation:[[0,0,0]]};let Vs={};Vs.shape=zs,Vs.translation=g,e.HS.updateListInputs(Vs),Vs={...qs,...Vs};const Gs=[{result:await e.HS.executeBasedOnTypeAsync(Vs,!1,(e=>t.occt.transforms.translate(e))),transformers:[]}],Js={shape:[void 0],direction:[[0,1,0]]};let Ks={};Ks.shape=Ys,Ks.direction=Ee,e.HS.updateListInputs(Ks),Ks={...Js,...Ks};const Qs=[{result:await e.HS.executeBasedOnTypeAsync(Ks,!1,(e=>t.occt.operations.extrude(e))),transformers:[]}];let Us={};Us.listElements=Cs,e.HS.updateListInputs(Us),Us={...Us};const $s=[{result:[Us.listElements?Us.listElements:[]]}],_s={shape:[void 0],index:[1]};let en={};en.shape=js,e.HS.updateListInputs(en),en={..._s,...en};const tn=[{result:await e.HS.executeBasedOnTypeAsync(en,!1,(e=>t.occt.shapes.edge.getEdge(e))),transformers:[]}];let sn={};sn.listElements=Fs,e.HS.updateListInputs(sn),sn={...sn};const nn=[{result:[sn.listElements?sn.listElements:[]]}],rn={shape:[void 0]};let an={};an.shape=Gs,e.HS.updateListInputs(an),an={...rn,...an};const on=[{result:await e.HS.executeBasedOnTypeAsync(an,!1,(e=>t.occt.shapes.wire.startPointOnWire(e))),transformers:[]}],pn={shape:[void 0]};let cn={};cn.shape=Gs,e.HS.updateListInputs(cn),cn={...pn,...cn};const un=[{result:await e.HS.executeBasedOnTypeAsync(cn,!1,(e=>t.occt.shapes.wire.endPointOnWire(e))),transformers:[]}],ln={shapes:[void 0]};let dn={};dn.shapes=nn,e.HS.updateListInputs(dn),dn={...ln,...dn};const mn=[{result:await e.HS.executeBasedOnTypeAsync(dn,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let yn={};yn.listElements=tn,e.HS.updateListInputs(yn),yn={...yn};const Sn=[{result:[yn.listElements?yn.listElements:[]]}],Hn={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let fn={};fn.shape=Qs,fn.shapes=Ye,e.HS.updateListInputs(fn),fn={...Hn,...fn};const hn=[{result:await e.HS.executeBasedOnTypeAsync(fn,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}];let xn={};xn.item=un,e.HS.updateListInputs(xn),xn={...xn};const vn=[{result:xn.item}];let On={};On.item=on,e.HS.updateListInputs(On),On={...On};const In=[{result:On.item}],Ln={shape:[void 0],shapes:[void 0],keepEdges:[!1]};let Bn={};Bn.shape=hn,Bn.shapes=$s,e.HS.updateListInputs(Bn),Bn={...Ln,...Bn};const Tn=[{result:await e.HS.executeBasedOnTypeAsync(Bn,!1,(e=>t.occt.booleans.difference(e))),transformers:[]}],wn={shapes:[void 0]};let An={};An.shapes=Sn,e.HS.updateListInputs(An),An={...wn,...An};const En=[{result:await e.HS.executeBasedOnTypeAsync(An,!1,(e=>t.occt.shapes.wire.combineEdgesAndWiresIntoAWire(e))),transformers:[]}];let gn={};gn.listElements=[Es[0],vn[0],In[0],bs[0]],e.HS.updateListInputs(gn),gn={...gn};const bn=[{result:[gn.listElements?gn.listElements:[]]}],Wn={shape:[void 0],origin:[[0,0,0]],normal:[[0,0,1]]};let zn={};zn.shape=Tn,zn.normal=m,e.HS.updateListInputs(zn),zn={...Wn,...zn};const Pn=[{result:await e.HS.executeBasedOnTypeAsync(zn,!1,(e=>t.occt.transforms.mirrorAlongNormal(e))),transformers:[]}];let Xn={};Xn.listElements=[mn[0],En[0]],e.HS.updateListInputs(Xn),Xn={...Xn};const Yn=[{result:[Xn.listElements?Xn.listElements:[]]}],Zn={points:[void 0]};let Nn={};Nn.points=bn,e.HS.updateListInputs(Nn),Nn={...Zn,...Nn};const Cn=[{result:await e.HS.executeBasedOnTypeAsync(Nn,!1,(e=>t.occt.shapes.wire.createPolylineWire(e))),transformers:[]}];let kn={};kn.listElements=[Pn[0],Tn[0]],e.HS.updateListInputs(kn),kn={...kn};const Dn=[{result:[kn.listElements?kn.listElements:[]]}],Fn={shapes:[void 0],makeSolid:[!1]};let Mn={};Mn.shapes=Yn,e.HS.updateListInputs(Mn),Mn={...Fn,...Mn};const Rn=[{result:await e.HS.executeBasedOnTypeAsync(Mn,!1,(e=>t.occt.operations.loft(e))),transformers:[]}],jn={shape:[void 0],radius:[.5],radiusList:[void 0],indexes:[void 0]};let qn={};qn.shape=Cn,e.HS.updateListInputs(qn),qn={...jn,...qn};const Vn=[{result:await e.HS.executeBasedOnTypeAsync(qn,!1,(e=>t.occt.fillets.fillet2d(e))),transformers:[]}],Gn={shapes:[void 0]};let Jn={};Jn.shapes=Dn,e.HS.updateListInputs(Jn),Jn={...Gn,...Jn};const Kn=[{result:await e.HS.executeBasedOnTypeAsync(Jn,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],Qn={shape:[void 0],origin:[[0,0,0]],direction:[[0,0,1]]};let Un={};Un.shape=Rn,Un.origin=we,Un.direction=f,e.HS.updateListInputs(Un),Un={...Qn,...Un};const $n=[{result:await e.HS.executeBasedOnTypeAsync(Un,!1,(e=>t.occt.transforms.mirror(e))),transformers:[]}],_n={shape:[void 0],angle:[360],direction:[[0,1,0]],copy:[!1]};let er={};er.shape=Vn,er.direction=x,e.HS.updateListInputs(er),er={..._n,...er};const tr=[{result:await e.HS.executeBasedOnTypeAsync(er,!1,(e=>t.occt.operations.revolve(e))),transformers:[]}];let sr={};sr.listElements=[as[0],Ls[0],Rn[0],$n[0]],e.HS.updateListInputs(sr),sr={...sr};const nr=[{result:[sr.listElements?sr.listElements:[]]}],rr={shape:[void 0],offset:[-.1]};let ar={};ar.shape=tr,e.HS.updateListInputs(ar),ar={...rr,...ar};const or=[{result:await e.HS.executeBasedOnTypeAsync(ar,!1,(e=>t.occt.operations.makeThickSolidSimple(e))),transformers:[]}],ir={shape:[void 0],angle:[0],center:[[0,0,0]],axis:[[0,0,1]]};let pr={};pr.shape=Kn,pr.angle=Ve,pr.axis=N,e.HS.updateListInputs(pr),pr={...ir,...pr};const cr=[{result:await e.HS.executeBasedOnTypeAsync(pr,!1,(e=>t.occt.transforms.rotateAroundCenter(e))),transformers:[]}],ur={shapes:[void 0],tolerance:[1e-7]};let lr={};lr.shapes=nr,e.HS.updateListInputs(lr),lr={...ur,...lr};const dr=[{result:await e.HS.executeBasedOnTypeAsync(lr,!1,(e=>t.occt.shapes.shell.sewFaces(e))),transformers:[]}],mr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let yr={};yr.entity=or,yr.options=W,e.HS.updateListInputs(yr),yr={...mr,...yr};await e.HS.executeBasedOnTypeAsync(yr,!1,(e=>t.draw.drawAnyAsync(e)));let Sr={};Sr.listElements=cr,e.HS.updateListInputs(Sr),Sr={...Sr};const Hr=[{result:[Sr.listElements?Sr.listElements:[]]}],fr={shapes:[void 0]};let hr={};hr.shapes=Hr,e.HS.updateListInputs(hr),hr={...fr,...hr};const xr=[{result:await e.HS.executeBasedOnTypeAsync(hr,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}];let vr={};vr.listElements=[dr[0],ye[0],xr[0]],e.HS.updateListInputs(vr),vr={...vr};const Or=[{result:[vr.listElements?vr.listElements:[]]}],Ir={shapes:[void 0]};let Lr={};Lr.shapes=Or,e.HS.updateListInputs(Lr),Lr={...Ir,...Lr};const Br=[{result:await e.HS.executeBasedOnTypeAsync(Lr,!1,(e=>t.occt.shapes.compound.makeCompound(e))),transformers:[]}],Tr={entity:[void 0],options:[void 0],babylonMesh:[void 0]};let wr={};wr.entity=Br,e.HS.updateListInputs(wr),wr={...Tr,...wr};await e.HS.executeBasedOnTypeAsync(wr,!1,(e=>t.draw.drawAnyAsync(e)))}(BitByBit,bitbybit,bitbybitRunnerResult,bitbybitRunnerInputs,Bit);\"}' };