Repository: elalish/manifold (language: python)

Issue:
Error when `slice`-ing the bottom face of a manifold
It appears the [`slice` function doesn't work as advertised](https://github.com/elalish/manifold/blob/4760ba3a33edded2b619557a789b70b86a97cfc2/bindings/wasm/manifold-encapsulated-types.d.ts#L830-L838) for extracting the bottom faces of a manifold. I'm hitting an undefined error that I can reproduce with a trivial example on [manifoldcad.org](https://manifoldcad.org)

```js
const box = Manifold.cube(1)
console.log(box.boundingBox().min);
console.log(box.slice(0));
```

Produces

```
[
    0,
    0,
    0
]
TypeError: A[0] is undefined
```

Related, is there a way to use `slice` to extract the top faces?

> using a height equal to the top of the bounding box will return empty.

Assuming this bug gets resolved, I can work around it by mirroring and slicing the bottom, but that feels like a kludge.



Discussion / hints:
It actually appears `slice` doesn't work at all, get the same error with `cube(1).slice(0.5)`. I suspect something is broken with the WASM bindings.

I debugged this a bit, the failure is  [here in `polygons2vec`](https://github.com/elalish/manifold/blob/4760ba3a33edded2b619557a789b70b86a97cfc2/bindings/wasm/bindings.js#L54).

![Screen Shot 2024-09-15 at 8 59 00 AM](https://github.com/user-attachments/assets/0a4b1f15-7e5e-496e-8ede-94b164f9778e)

I've got limited WASM experience, but it looks like a WASM-bridged C++ object is getting passed to the function rather than an expected JS array?

![Screen Shot 2024-09-15 at 9 01 10 AM](https://github.com/user-attachments/assets/832a2924-004b-49a4-947f-0d8952a51283)

If I'm reading correctly, it's coming [directly from `Manifold._Slice`](https://github.com/elalish/manifold/blob/4760ba3a33edded2b619557a789b70b86a97cfc2/bindings/wasm/bindings.js#L287).

Thanks! Looks like we must be missing a WASM test example that uses slice. @pca006132 wasn't there a recent refactor of this? Maybe something got missed?
Yeah, probably something simple, will check later when I have time.
I noticed `project` as the same problem. Seems like this is the culprit change - 85d71f5cef43d9b026bc67f9ddf9d64e7872a8e8

Fix patch:
diff --git a/.github/workflows/manifold.yml b/.github/workflows/manifold.yml
index 6362c963a..2c423b6e5 100644
--- a/.github/workflows/manifold.yml
+++ b/.github/workflows/manifold.yml
@@ -153,7 +153,7 @@ jobs:
         cp ../manifold.* ./dist/
     - name: Upload WASM files
       uses: actions/upload-artifact@v4
-      if: matrix.exception == 'ON'
+      if: matrix.exception == 'ON' && github.event_name == 'push'
       with:
         name: wasm
         path: bindings/wasm/examples/dist/
diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js
index b413859fc..1cd545bd8 100644
--- a/bindings/wasm/bindings.js
+++ b/bindings/wasm/bindings.js
@@ -284,11 +284,17 @@ Module.setup = function() {
   };
 
   Module.Manifold.prototype.slice = function(height = 0.) {
-    return Module.CrossSection(this._Slice(height));
+    const polygonsVec = this._Slice(height);
+    const result = new CrossSectionCtor(polygonsVec, fillRuleToInt('Positive'));
+    disposePolygons(polygonsVec);
+    return result;
   };
 
   Module.Manifold.prototype.project = function() {
-    return Module.CrossSection(this._Project()).simplify(this.precision());
+    const polygonsVec = this._Project();
+    const result = new CrossSectionCtor(polygonsVec, fillRuleToInt('Positive'));
+    disposePolygons(polygonsVec);
+    return result.simplify(this.precision);
   };
 
   Module.Manifold.prototype.split = function(manifold) {
diff --git a/bindings/wasm/examples/public/examples.js b/bindings/wasm/examples/public/examples.js
index dbe1c4bfc..e3eefba0f 100644
--- a/bindings/wasm/examples/public/examples.js
+++ b/bindings/wasm/examples/public/examples.js
@@ -51,6 +51,34 @@ export const examples = {
       return result;
     },
 
+    Auger: function() {
+      const outerRadius = 20;
+      const beadRadius = 2;
+      const height = 40;
+      const twist = 90;
+
+      const {revolve, sphere, union, extrude} = Manifold;
+      const {circle} = CrossSection;
+      setMinCircularEdgeLength(0.1);
+
+      const bead1 =
+          revolve(circle(beadRadius).translate([outerRadius, 0]), 50, 90)
+              .add(sphere(beadRadius).translate([outerRadius, 0, 0]))
+              .translate([0, -outerRadius, 0]);
+
+      const beads = [];
+      for (let i = 0; i < 3; i++) {
+        beads.push(bead1.rotate(0, 0, 120 * i));
+      }
+      const bead = union(beads);
+
+      const auger = extrude(bead.slice(0), height, 50, twist);
+
+      const result =
+          auger.add(bead).add(bead.translate(0, 0, height).rotate(0, 0, twist));
+      return result;
+    },
+
     TetrahedronPuzzle: function() {
       // A tetrahedron cut into two identical halves that can screw together as
       // a puzzle. This demonstrates how redundant points along a polygon can be
@@ -550,7 +578,7 @@ export const examples = {
         0, 0, size / Math.sqrt(2)
       ]);
       return result;
-    }
+    },
   },
 
   functionBodies: new Map()
diff --git a/format.sh b/format.sh
index fe2295ba2..13aa65241 100755
--- a/format.sh
+++ b/format.sh
@@ -11,7 +11,9 @@ $CLANG_FORMAT -i samples/**/*.{h,cpp}
 $CLANG_FORMAT -i test/*.{h,cpp}
 $CLANG_FORMAT -i bindings/*/*.cpp
 $CLANG_FORMAT -i bindings/c/include/manifold/*.h
-$CLANG_FORMAT -i bindings/wasm/**/*.{js,ts,html}
+$CLANG_FORMAT -i bindings/wasm/*.{js,ts}
+$CLANG_FORMAT -i bindings/wasm/examples/*.{js,ts,html}
+$CLANG_FORMAT -i bindings/wasm/examples/public/*.{js,ts}
 $CLANG_FORMAT -i src/*/src/*.{h,cpp}
 $CLANG_FORMAT -i src/*/include/manifold/*.h
 black bindings/python/examples/*.py


Test patch:
diff --git a/bindings/wasm/examples/worker.test.ts b/bindings/wasm/examples/worker.test.ts
index 2de9f42a6..8c3bd83a1 100644
--- a/bindings/wasm/examples/worker.test.ts
+++ b/bindings/wasm/examples/worker.test.ts
@@ -59,6 +59,13 @@ suite('Examples', () => {
     expect(result.surfaceArea).to.be.closeTo(62046, 1, 'Surface Area');
   });
 
+  test('Auger', async () => {
+    const result = await runExample('Auger');
+    expect(result.genus).to.equal(0, 'Genus');
+    expect(result.volume).to.be.closeTo(16842, 1, 'Volume');
+    expect(result.surfaceArea).to.be.closeTo(10519, 1, 'Surface Area');
+  });
+
   test('Tetrahedron Puzzle', async () => {
     const result = await runExample('Tetrahedron Puzzle');
     expect(result.genus).to.equal(0, 'Genus');