diff --git a/bindings/wasm/examples/public/examples.js b/bindings/wasm/examples/public/examples.js index e753a41e0..4136e8fb6 100644 --- a/bindings/wasm/examples/public/examples.js +++ b/bindings/wasm/examples/public/examples.js @@ -50,9 +50,9 @@ export const examples = { TetrahedronPuzzle: function() { // A tetrahedron cut into two identical halves that can screw together as - // a puzzle. This only outputs one of the halves. This demonstrates how - // redundant points along a polygon can be used to make twisted extrusions - // smoother. Based on the screw puzzle by George Hart: + // a puzzle. This demonstrates how redundant points along a polygon can be + // used to make twisted extrusions smoother. It also showcases animated + // assemblies. Based on the screw puzzle by George Hart: // https://www.thingiverse.com/thing:186372 const edgeLength = 50; // Length of each edge of the overall tetrahedron. @@ -61,19 +61,21 @@ export const examples = { const scale = edgeLength / (2 * Math.sqrt(2)); - const tet = Manifold.tetrahedron().scale(scale); + const tet = Manifold.tetrahedron().intersect( + Manifold.tetrahedron().rotate([0, 0, 90]).scale(2.5)); const box = []; - box.push([1, -1], [1, 1]); + box.push([2, -2], [2, 2]); for (let i = 0; i <= nDivisions; ++i) { - box.push([gap / (4 * scale), 1 - i * 2 / nDivisions]); + box.push([gap / (2 * scale), 2 - i * 4 / nDivisions]); } const cyan = [0, 1, 1]; const magenta = [1, 0, 1]; const fade = (color, pos) => { for (let i = 0; i < 3; ++i) { - color[i] = cyan[i] * pos[2] + magenta[i] * (1 - pos[2]); + const x = pos[2] / 2; + color[i] = cyan[i] * x + magenta[i] * (1 - x); } }; @@ -82,11 +84,12 @@ export const examples = { // channels as colors, and sets the factor to white, since our default is // yellow. const screw = setMaterial( - Manifold.extrude(box, 1, nDivisions, 270).setProperties(3, fade), + Manifold.extrude(box, 2, nDivisions, 270).setProperties(3, fade), {baseColorFactor: [1, 1, 1], attributes: ['COLOR_0']}); - const result = tet.intersect( - screw.rotate([0, 0, -45]).translate([0, 0, -0.5]).scale(2 * scale)); + const result = + tet.intersect(screw.rotate([0, 0, -45]).translate([0, 0, -1])) + .scale(scale); // Assigned materials are only applied to a GLTFNode. Note that material // definitions cascade, applying recursively to all child surfaces, but @@ -94,15 +97,21 @@ export const examples = { // properties, as well as animation parameters can be set via // globalDefaults. - const node = new GLTFNode(); - node.rotation = [45, -Math.atan(1 / Math.sqrt(2)) * 180 / Math.PI, 120]; + const layFlat = new GLTFNode(); + layFlat.rotation = + [45, -Math.atan(1 / Math.sqrt(2)) * 180 / Math.PI, 120]; + layFlat.translation = [0, 0, scale * Math.sqrt(3) / 3]; - const fixed = new GLTFNode(node); + const fixed = new GLTFNode(layFlat); fixed.manifold = result; fixed.rotation = [0, 0, 180]; - const moving = new GLTFNode(node); + // For 3MF export, only top-level objects are independently arrangeable. + const layFlat2 = layFlat.clone(); + + const moving = new GLTFNode(layFlat2); moving.manifold = result; + // Use functions to create animation, which runs from t=0 to t=1. moving.translation = (t) => { const a = 1 - t; const x = a > 0.5 ? scale * 2 * (0.5 - a) : 0; @@ -110,7 +119,7 @@ export const examples = { }; moving.rotation = (t) => [0, 0, 270 * 2 * (1 - t)]; - globalDefaults.animationLength = 10; + globalDefaults.animationLength = 10; // seconds globalDefaults.animationMode = 'ping-pong'; return result; }, diff --git a/bindings/wasm/examples/worker.test.js b/bindings/wasm/examples/worker.test.js index aeee237bc..9873fa656 100644 --- a/bindings/wasm/examples/worker.test.js +++ b/bindings/wasm/examples/worker.test.js @@ -94,8 +94,8 @@ suite('Examples', () => { test('Tetrahedron Puzzle', async () => { const result = await runExample('Tetrahedron Puzzle'); expect(result.genus).to.equal(0, 'Genus'); - expect(result.volume).to.be.closeTo(7297, 1, 'Volume'); - expect(result.surfaceArea).to.be.closeTo(3303, 1, 'Surface Area'); + expect(result.volume).to.be.closeTo(7240, 1, 'Volume'); + expect(result.surfaceArea).to.be.closeTo(3235, 1, 'Surface Area'); }); test('Rounded Frame', async () => {