* almost updated scallop * scallop working * fixed scallop * sharpened scallop * removed some smoothing APIs * fixed some tests * tests passing * updated bindings examples * fix formatting * fix more formatting
799 lines
23 KiB
JavaScript
799 lines
23 KiB
JavaScript
// Copyright 2022 The Manifold Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
var _ManifoldInitialized = false;
|
|
Module.setup = function() {
|
|
if (_ManifoldInitialized) return;
|
|
_ManifoldInitialized = true;
|
|
|
|
// warmup tbb for emscripten, according to
|
|
// https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations
|
|
Module.initTBB();
|
|
// conversion utilities
|
|
|
|
function toVec(vec, list, f = x => x) {
|
|
if (list) {
|
|
for (let x of list) {
|
|
vec.push_back(f(x));
|
|
}
|
|
}
|
|
return vec;
|
|
}
|
|
|
|
function fromVec(vec, f = x => x) {
|
|
const result = [];
|
|
const size = vec.size();
|
|
for (let i = 0; i < size; i++) result.push(f(vec.get(i)));
|
|
return result;
|
|
}
|
|
|
|
function vec2polygons(vec, f = x => x) {
|
|
const result = [];
|
|
const nPoly = vec.size();
|
|
for (let i = 0; i < nPoly; i++) {
|
|
const v = vec.get(i);
|
|
const nPts = v.size();
|
|
const poly = [];
|
|
for (let j = 0; j < nPts; j++) {
|
|
poly.push(f(v.get(j)));
|
|
}
|
|
result.push(poly);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function polygons2vec(polygons) {
|
|
if (polygons[0].length < 3) {
|
|
polygons = [polygons];
|
|
}
|
|
return toVec(
|
|
new Module.Vector2_vec2(), polygons,
|
|
poly => toVec(new Module.Vector_vec2(), poly, p => {
|
|
if (p instanceof Array) return {x: p[0], y: p[1]};
|
|
return p;
|
|
}));
|
|
}
|
|
|
|
function disposePolygons(polygonsVec) {
|
|
for (let i = 0; i < polygonsVec.size(); i++) polygonsVec.get(i).delete();
|
|
polygonsVec.delete();
|
|
}
|
|
|
|
function vararg2vec2(vec) {
|
|
if (vec[0] instanceof Array) return {x: vec[0][0], y: vec[0][1]};
|
|
if (typeof (vec[0]) == 'number')
|
|
// default to 0
|
|
return {x: vec[0] || 0, y: vec[1] || 0};
|
|
return vec[0];
|
|
}
|
|
|
|
function vararg2vec3(vec) {
|
|
if (vec[0] instanceof Array)
|
|
return {x: vec[0][0], y: vec[0][1], z: vec[0][2]};
|
|
if (typeof (vec[0]) == 'number')
|
|
// default to 0
|
|
return {x: vec[0] || 0, y: vec[1] || 0, z: vec[2] || 0};
|
|
return vec[0];
|
|
}
|
|
|
|
function joinTypeToInt(joinType) {
|
|
return joinType == 'Round' ? 1 :
|
|
joinType == 'Miter' ? 2 :
|
|
joinType == 'Bevel' ? 3 :
|
|
/* Square */ 0;
|
|
}
|
|
|
|
// CrossSection methods
|
|
|
|
const CrossSectionCtor = Module.CrossSection;
|
|
|
|
function cross(polygons) {
|
|
if (polygons instanceof CrossSectionCtor) {
|
|
return polygons;
|
|
} else {
|
|
const polygonsVec = polygons2vec(polygons);
|
|
const cs = new CrossSectionCtor(polygonsVec);
|
|
disposePolygons(polygonsVec);
|
|
return cs;
|
|
}
|
|
};
|
|
|
|
Module.CrossSection.prototype.translate = function(...vec) {
|
|
return this._Translate(vararg2vec2(vec));
|
|
};
|
|
|
|
Module.CrossSection.prototype.scale = function(vec) {
|
|
// if only one factor provided, scale both x and y with it
|
|
if (typeof vec == 'number') {
|
|
return this._Scale({x: vec, y: vec});
|
|
}
|
|
return this._Scale(vararg2vec2([vec]));
|
|
};
|
|
|
|
Module.CrossSection.prototype.mirror = function(vec) {
|
|
return this._Mirror(vararg2vec2([vec]));
|
|
};
|
|
|
|
Module.CrossSection.prototype.warp = function(func) {
|
|
const wasmFuncPtr = addFunction(function(vec2Ptr) {
|
|
const x = getValue(vec2Ptr, 'double');
|
|
const y = getValue(vec2Ptr + 8, 'double');
|
|
const vert = [x, y];
|
|
func(vert);
|
|
setValue(vec2Ptr, vert[0], 'double');
|
|
setValue(vec2Ptr + 8, vert[1], 'double');
|
|
}, 'vi');
|
|
const out = this._Warp(wasmFuncPtr);
|
|
removeFunction(wasmFuncPtr);
|
|
return out;
|
|
};
|
|
|
|
Module.CrossSection.prototype.decompose = function() {
|
|
const vec = this._Decompose();
|
|
const result = fromVec(vec);
|
|
vec.delete();
|
|
return result;
|
|
};
|
|
|
|
Module.CrossSection.prototype.bounds = function() {
|
|
const result = this._Bounds();
|
|
return {
|
|
min: ['x', 'y'].map(f => result.min[f]),
|
|
max: ['x', 'y'].map(f => result.max[f]),
|
|
};
|
|
};
|
|
|
|
Module.CrossSection.prototype.offset = function(
|
|
delta, joinType = 'Round', miterLimit = 2.0, circularSegments = 0) {
|
|
return this._Offset(
|
|
delta, joinTypeToInt(joinType), miterLimit, circularSegments);
|
|
};
|
|
|
|
Module.CrossSection.prototype.simplify = function(tolerance = 0) {
|
|
return this._Simplify(tolerance);
|
|
};
|
|
|
|
Module.CrossSection.prototype.extrude = function(
|
|
height, nDivisions = 0, twistDegrees = 0.0, scaleTop = [1.0, 1.0],
|
|
center = false) {
|
|
scaleTop = vararg2vec2([scaleTop]);
|
|
const man = Module._Extrude(
|
|
this._ToPolygons(), height, nDivisions, twistDegrees, scaleTop);
|
|
return (center ? man.translate([0., 0., -height / 2.]) : man);
|
|
};
|
|
|
|
Module.CrossSection.prototype.revolve = function(
|
|
circularSegments = 0, revolveDegrees = 360.0) {
|
|
return Module._Revolve(
|
|
this._ToPolygons(), circularSegments, revolveDegrees);
|
|
};
|
|
|
|
Module.CrossSection.prototype.add = function(other) {
|
|
return this._add(cross(other));
|
|
};
|
|
|
|
Module.CrossSection.prototype.subtract = function(other) {
|
|
return this._subtract(cross(other));
|
|
};
|
|
|
|
Module.CrossSection.prototype.intersect = function(other) {
|
|
return this._intersect(cross(other));
|
|
};
|
|
|
|
Module.CrossSection.prototype.toPolygons = function() {
|
|
const vec = this._ToPolygons();
|
|
const result = vec2polygons(vec, v => [v.x, v.y]);
|
|
vec.delete();
|
|
return result;
|
|
};
|
|
|
|
// Manifold methods
|
|
|
|
Module.Manifold.prototype.warp = function(func) {
|
|
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
|
const x = getValue(vec3Ptr, 'double');
|
|
const y = getValue(vec3Ptr + 8, 'double');
|
|
const z = getValue(vec3Ptr + 16, 'double');
|
|
const vert = [x, y, z];
|
|
func(vert);
|
|
setValue(vec3Ptr, vert[0], 'double');
|
|
setValue(vec3Ptr + 8, vert[1], 'double');
|
|
setValue(vec3Ptr + 16, vert[2], 'double');
|
|
}, 'vi');
|
|
const out = this._Warp(wasmFuncPtr);
|
|
removeFunction(wasmFuncPtr);
|
|
|
|
const status = out.status();
|
|
if (status !== 'NoError') {
|
|
throw new Module.ManifoldError(status);
|
|
}
|
|
return out;
|
|
};
|
|
|
|
Module.Manifold.prototype.warpBatch = function(func) {
|
|
const wasmFuncPtr = addFunction(function(ptr, count) {
|
|
const heapF64 = Module.HEAPF64 ?? HEAPF64;
|
|
if (!heapF64) {
|
|
throw new Error('WASM heap is not initialized (HEAPF64 unavailable)');
|
|
}
|
|
const verts = new Float64Array(heapF64.buffer, ptr, count * 3);
|
|
|
|
func(verts, count);
|
|
}, 'vii');
|
|
|
|
const out = this._WarpBatch(wasmFuncPtr);
|
|
removeFunction(wasmFuncPtr);
|
|
|
|
const status = out.status();
|
|
if (status !== 'NoError') {
|
|
throw new Module.ManifoldError(status);
|
|
}
|
|
return out;
|
|
};
|
|
|
|
Module.Manifold.prototype.calculateNormals = function(
|
|
normalIdx = 0, minSharpAngle = 52.5) {
|
|
return this._CalculateNormals(normalIdx, minSharpAngle);
|
|
};
|
|
|
|
Module.Manifold.prototype.smoothByNormals = function(normalIdx = 0) {
|
|
return this._SmoothByNormals(normalIdx);
|
|
};
|
|
|
|
Module.Manifold.prototype.setProperties = function(numProp, func) {
|
|
const oldNumProp = this.numProp();
|
|
const wasmFuncPtr = addFunction(function(newPtr, vec3Ptr, oldPtr) {
|
|
const newProp = [];
|
|
for (let i = 0; i < numProp; ++i) {
|
|
newProp[i] = getValue(newPtr + 8 * i, 'double');
|
|
}
|
|
const pos = [];
|
|
for (let i = 0; i < 3; ++i) {
|
|
pos[i] = getValue(vec3Ptr + 8 * i, 'double');
|
|
}
|
|
const oldProp = [];
|
|
for (let i = 0; i < oldNumProp; ++i) {
|
|
oldProp[i] = getValue(oldPtr + 8 * i, 'double');
|
|
}
|
|
|
|
func(newProp, pos, oldProp);
|
|
|
|
for (let i = 0; i < numProp; ++i) {
|
|
setValue(newPtr + 8 * i, newProp[i], 'double');
|
|
}
|
|
}, 'viii');
|
|
const out = this._SetProperties(numProp, wasmFuncPtr);
|
|
removeFunction(wasmFuncPtr);
|
|
return out;
|
|
};
|
|
|
|
Module.Manifold.prototype.translate = function(...vec) {
|
|
return this._Translate(vararg2vec3(vec));
|
|
};
|
|
|
|
Module.Manifold.prototype.rotate = function(xOrVec, y, z) {
|
|
if (Array.isArray(xOrVec)) {
|
|
return this._Rotate(...xOrVec);
|
|
} else {
|
|
return this._Rotate(xOrVec, y || 0, z || 0);
|
|
}
|
|
};
|
|
|
|
Module.Manifold.prototype.scale = function(vec) {
|
|
// if only one factor provided, scale all three dimensions (xyz) with it
|
|
if (typeof vec == 'number') {
|
|
return this._Scale({x: vec, y: vec, z: vec});
|
|
}
|
|
return this._Scale(vararg2vec3([vec]));
|
|
};
|
|
|
|
Module.Manifold.prototype.mirror = function(vec) {
|
|
return this._Mirror(vararg2vec3([vec]));
|
|
};
|
|
|
|
Module.Manifold.prototype.trimByPlane = function(normal, offset = 0.) {
|
|
return this._TrimByPlane(vararg2vec3([normal]), offset);
|
|
};
|
|
|
|
Module.Manifold.prototype.slice = function(height = 0.) {
|
|
const polygonsVec = this._Slice(height);
|
|
const result = new CrossSectionCtor(polygonsVec);
|
|
disposePolygons(polygonsVec);
|
|
return result;
|
|
};
|
|
|
|
Module.Manifold.prototype.project = function() {
|
|
const polygonsVec = this._Project();
|
|
const result = new CrossSectionCtor(polygonsVec);
|
|
disposePolygons(polygonsVec);
|
|
return result;
|
|
};
|
|
|
|
Module.Manifold.prototype.windingNumber = function(points) {
|
|
const inVec = new Module.Vector_vec3();
|
|
for (const p of points) inVec.push_back(vararg2vec3([p]));
|
|
const outVec = this._WindingNumber(inVec);
|
|
inVec.delete();
|
|
const result = fromVec(outVec);
|
|
outVec.delete();
|
|
return result;
|
|
};
|
|
|
|
Module.Manifold.prototype.rayCast = function(origin, endpoint) {
|
|
const vec = this._RayCast(vararg2vec3([origin]), vararg2vec3([endpoint]));
|
|
const result =
|
|
fromVec(vec, hit => ({
|
|
faceID: hit.faceID,
|
|
distance: hit.distance,
|
|
position: ['x', 'y', 'z'].map(f => hit.position[f]),
|
|
normal: ['x', 'y', 'z'].map(f => hit.normal[f]),
|
|
}));
|
|
vec.delete();
|
|
return result;
|
|
};
|
|
|
|
|
|
Module.Manifold.prototype.split = function(manifold) {
|
|
const vec = this._Split(manifold);
|
|
const result = fromVec(vec);
|
|
vec.delete();
|
|
return result;
|
|
};
|
|
|
|
Module.Manifold.prototype.splitByPlane = function(normal, offset = 0.) {
|
|
const vec = this._SplitByPlane(vararg2vec3([normal]), offset);
|
|
const result = fromVec(vec);
|
|
vec.delete();
|
|
return result;
|
|
};
|
|
|
|
Module.Manifold.prototype.decompose = function() {
|
|
const vec = this._Decompose();
|
|
const result = fromVec(vec);
|
|
vec.delete();
|
|
return result;
|
|
};
|
|
|
|
Module.Manifold.prototype.boundingBox = function() {
|
|
const result = this._boundingBox();
|
|
return {
|
|
min: ['x', 'y', 'z'].map(f => result.min[f]),
|
|
max: ['x', 'y', 'z'].map(f => result.max[f]),
|
|
};
|
|
};
|
|
|
|
Module.Manifold.prototype.simplify = function(tolerance = 0) {
|
|
return this._Simplify(tolerance);
|
|
};
|
|
|
|
class Mesh {
|
|
constructor({
|
|
numProp = 3,
|
|
triVerts = new Uint32Array(),
|
|
vertProperties = new Float32Array(),
|
|
mergeFromVert,
|
|
mergeToVert,
|
|
runIndex,
|
|
runOriginalID,
|
|
faceID,
|
|
halfedgeTangent,
|
|
runTransform,
|
|
runFlags,
|
|
tolerance = 0
|
|
} = {}) {
|
|
this.numProp = numProp;
|
|
this.triVerts = triVerts;
|
|
this.vertProperties = vertProperties;
|
|
this.mergeFromVert = mergeFromVert;
|
|
this.mergeToVert = mergeToVert;
|
|
this.runIndex = runIndex;
|
|
this.runOriginalID = runOriginalID;
|
|
this.faceID = faceID;
|
|
this.halfedgeTangent = halfedgeTangent;
|
|
this.runTransform = runTransform;
|
|
this.runFlags = runFlags;
|
|
this.tolerance = tolerance;
|
|
}
|
|
|
|
get numTri() {
|
|
return this.triVerts.length / 3;
|
|
}
|
|
|
|
get numVert() {
|
|
return this.vertProperties.length / this.numProp;
|
|
}
|
|
|
|
get numRun() {
|
|
return this.runOriginalID.length;
|
|
}
|
|
|
|
merge() {
|
|
const {changed, mesh} = Module._Merge(this);
|
|
Object.assign(this, {...mesh});
|
|
return changed;
|
|
}
|
|
|
|
verts(tri) {
|
|
return this.triVerts.subarray(3 * tri, 3 * (tri + 1));
|
|
}
|
|
|
|
position(vert) {
|
|
return this.vertProperties.subarray(
|
|
this.numProp * vert, this.numProp * vert + 3);
|
|
}
|
|
|
|
extras(vert) {
|
|
return this.vertProperties.subarray(
|
|
this.numProp * vert + 3, this.numProp * (vert + 1));
|
|
}
|
|
|
|
tangent(halfedge) {
|
|
return this.halfedgeTangent.subarray(4 * halfedge, 4 * (halfedge + 1));
|
|
}
|
|
|
|
transform(run) {
|
|
const mat4 = new Array(16);
|
|
for (const col of [0, 1, 2, 3]) {
|
|
for (const row of [0, 1, 2]) {
|
|
mat4[4 * col + row] = this.runTransform[12 * run + 3 * col + row];
|
|
}
|
|
}
|
|
mat4[15] = 1;
|
|
return mat4;
|
|
}
|
|
|
|
backside(run) {
|
|
return this.runFlags != null && run < this.runFlags.length &&
|
|
(this.runFlags[run] & 1) !== 0;
|
|
}
|
|
|
|
hasNormals(run) {
|
|
return this.runFlags != null && run < this.runFlags.length &&
|
|
(this.runFlags[run] & 2) !== 0;
|
|
}
|
|
}
|
|
|
|
Module.Mesh = Mesh;
|
|
|
|
Module.Manifold.prototype.getMesh = function(normalIdx = -1) {
|
|
return new Mesh(this._GetMeshJS(normalIdx));
|
|
};
|
|
|
|
Module.ManifoldError = function ManifoldError(code, ...args) {
|
|
let message = 'Unknown error';
|
|
switch (code) {
|
|
case 'NonFiniteVertex':
|
|
message = 'Non-finite vertex';
|
|
break;
|
|
case 'NotManifold':
|
|
message = 'Not manifold';
|
|
break;
|
|
case 'VertexOutOfBounds':
|
|
message = 'Vertex index out of bounds';
|
|
break;
|
|
case 'PropertiesWrongLength':
|
|
message = 'Properties have wrong length';
|
|
break;
|
|
case 'MissingPositionProperties':
|
|
message = 'Less than three properties';
|
|
break;
|
|
case 'MergeVectorsDifferentLengths':
|
|
message = 'Merge vectors have different lengths';
|
|
break;
|
|
case 'MergeIndexOutOfBounds':
|
|
message = 'Merge index out of bounds';
|
|
break;
|
|
case 'TransformWrongLength':
|
|
message = 'Transform vector has wrong length';
|
|
break;
|
|
case 'RunIndexWrongLength':
|
|
message = 'Run index vector has wrong length';
|
|
break;
|
|
case 'FaceIDWrongLength':
|
|
message = 'Face ID vector has wrong length';
|
|
break;
|
|
case 'InvalidConstruction':
|
|
message = 'Manifold constructed with invalid parameters';
|
|
break;
|
|
case 'ResultTooLarge':
|
|
message = 'Result exceeds maximum size';
|
|
break;
|
|
case 'InvalidTangents':
|
|
message = 'Invalid halfedge tangents';
|
|
break;
|
|
}
|
|
|
|
const base = Error.apply(this, [message, ...args]);
|
|
base.name = this.name = 'ManifoldError';
|
|
this.message = base.message;
|
|
this.stack = base.stack;
|
|
this.code = code;
|
|
};
|
|
|
|
Module.ManifoldError.prototype = Object.create(Error.prototype, {
|
|
constructor:
|
|
{value: Module.ManifoldError, writable: true, configurable: true}
|
|
});
|
|
|
|
// CrossSection Constructors
|
|
|
|
Module.CrossSection = function(polygons) {
|
|
const polygonsVec = polygons2vec(polygons);
|
|
const cs = new CrossSectionCtor(polygonsVec);
|
|
disposePolygons(polygonsVec);
|
|
return cs;
|
|
};
|
|
|
|
Module.CrossSection.ofPolygons = function(polygons) {
|
|
return new Module.CrossSection(polygons);
|
|
};
|
|
|
|
Module.CrossSection.evenOdd = function(polygons) {
|
|
const polygonsVec = polygons2vec(polygons);
|
|
const cs = Module._EvenOdd(polygonsVec);
|
|
disposePolygons(polygonsVec);
|
|
return cs;
|
|
};
|
|
|
|
Module.CrossSection.square = function(...args) {
|
|
let size = undefined;
|
|
if (args.length == 0)
|
|
size = {x: 1, y: 1};
|
|
else if (typeof args[0] == 'number')
|
|
size = {x: args[0], y: args[0]};
|
|
else
|
|
size = vararg2vec2(args);
|
|
const center = args[1] || false;
|
|
return Module._Square(size, center);
|
|
};
|
|
|
|
Module.CrossSection.circle = function(radius, circularSegments = 0) {
|
|
return Module._Circle(radius, circularSegments);
|
|
};
|
|
|
|
// allows args to be either CrossSection or polygons (constructed with
|
|
// Positive fill)
|
|
function crossSectionBatchbool(name) {
|
|
return function(...args) {
|
|
if (args.length == 1) args = args[0];
|
|
const v = new Module.Vector_crossSection();
|
|
for (const cs of args) v.push_back(cross(cs));
|
|
const result = Module['_crossSection' + name](v);
|
|
v.delete();
|
|
return result;
|
|
};
|
|
}
|
|
|
|
Module.CrossSection.union = crossSectionBatchbool('UnionN');
|
|
Module.CrossSection.difference = crossSectionBatchbool('DifferenceN');
|
|
Module.CrossSection.intersection = crossSectionBatchbool('IntersectionN');
|
|
|
|
function pushVec2(vec, ps) {
|
|
toVec(vec, ps, p => {
|
|
if (p instanceof Array) return {x: p[0], y: p[1]};
|
|
return p;
|
|
})
|
|
}
|
|
|
|
Module.CrossSection.hull = function(...args) {
|
|
if (args.length == 1) args = args[0];
|
|
let pts = new Module.Vector_vec2();
|
|
for (const cs of args) {
|
|
if (cs instanceof CrossSectionCtor) {
|
|
Module._crossSectionCollectVertices(pts, cs);
|
|
} else if (
|
|
cs instanceof Array && cs.length == 2 && typeof cs[0] == 'number') {
|
|
pts.push_back({x: cs[0], y: cs[1]});
|
|
} else if (cs.x) {
|
|
pts.push_back(cs);
|
|
} else {
|
|
const wrap =
|
|
((cs[0].length == 2 && typeof cs[0][0] == 'number') || cs[0].x);
|
|
const polys = wrap ? [cs] : cs;
|
|
for (const poly of polys) pushVec2(pts, poly);
|
|
}
|
|
}
|
|
const result = Module._crossSectionHullPoints(pts);
|
|
pts.delete();
|
|
return result;
|
|
};
|
|
|
|
Module.CrossSection.prototype = Object.create(CrossSectionCtor.prototype);
|
|
|
|
// Because the constructor and prototype are being replaced, instanceof will
|
|
// not work as desired unless we refer back to the original like this
|
|
Object.defineProperty(Module.CrossSection, Symbol.hasInstance, {
|
|
get: () => (t) => {
|
|
return (t instanceof CrossSectionCtor);
|
|
}
|
|
});
|
|
|
|
// Manifold Constructors
|
|
|
|
const ManifoldCtor = Module.Manifold;
|
|
Module.Manifold = function(mesh) {
|
|
const manifold = new ManifoldCtor(mesh);
|
|
|
|
const status = manifold.status();
|
|
if (status !== 'NoError') {
|
|
throw new Module.ManifoldError(status);
|
|
}
|
|
|
|
return manifold;
|
|
};
|
|
|
|
Module.Manifold.ofMesh = function(mesh) {
|
|
return new Module.Manifold(mesh);
|
|
};
|
|
|
|
Module.Manifold.tetrahedron = function() {
|
|
return Module._Tetrahedron();
|
|
};
|
|
|
|
Module.Manifold.cube = function(...args) {
|
|
let size = undefined;
|
|
if (args.length == 0)
|
|
size = {x: 1, y: 1, z: 1};
|
|
else if (typeof args[0] == 'number')
|
|
size = {x: args[0], y: args[0], z: args[0]};
|
|
else
|
|
size = vararg2vec3(args);
|
|
const center = args[1] || false;
|
|
return Module._Cube(size, center);
|
|
};
|
|
|
|
Module.Manifold.cylinder = function(
|
|
height, radiusLow, radiusHigh = -1.0, circularSegments = 0,
|
|
center = false) {
|
|
return Module._Cylinder(
|
|
height, radiusLow, radiusHigh, circularSegments, center);
|
|
};
|
|
|
|
Module.Manifold.sphere = function(radius, circularSegments = 0) {
|
|
return Module._Sphere(radius, circularSegments);
|
|
};
|
|
|
|
Module.Manifold.extrude = function(
|
|
polygons, height, nDivisions = 0, twistDegrees = 0.0,
|
|
scaleTop = [1.0, 1.0], center = false) {
|
|
const cs = (polygons instanceof CrossSectionCtor) ?
|
|
polygons :
|
|
Module.CrossSection(polygons);
|
|
return cs.extrude(height, nDivisions, twistDegrees, scaleTop, center);
|
|
};
|
|
|
|
Module.Manifold.revolve = function(
|
|
polygons, circularSegments = 0, revolveDegrees = 360.0) {
|
|
const cs = (polygons instanceof CrossSectionCtor) ?
|
|
polygons :
|
|
Module.CrossSection(polygons);
|
|
return cs.revolve(circularSegments, revolveDegrees);
|
|
};
|
|
|
|
Module.Manifold.reserveIDs = function(n) {
|
|
return Module._ReserveIDs(n);
|
|
};
|
|
|
|
function manifoldBatchbool(name) {
|
|
return function(...args) {
|
|
if (args.length == 1) args = args[0];
|
|
const v = new Module.Vector_manifold();
|
|
for (const m of args) v.push_back(m);
|
|
const result = Module['_manifold' + name + 'N'](v);
|
|
v.delete();
|
|
return result;
|
|
};
|
|
}
|
|
|
|
Module.Manifold.union = manifoldBatchbool('Union');
|
|
// Aliasing compose to union.
|
|
// Native compose has some issues, and is deprecated.
|
|
Module.Manifold.compose = Module.Manifold.union;
|
|
|
|
Module.Manifold.difference = manifoldBatchbool('Difference');
|
|
Module.Manifold.intersection = manifoldBatchbool('Intersection');
|
|
|
|
Module.Manifold.levelSet = function(
|
|
sdf, bounds, edgeLength, level = 0, tolerance = -1) {
|
|
const bounds2 = {
|
|
min: {x: bounds.min[0], y: bounds.min[1], z: bounds.min[2]},
|
|
max: {x: bounds.max[0], y: bounds.max[1], z: bounds.max[2]},
|
|
};
|
|
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
|
const x = getValue(vec3Ptr, 'double');
|
|
const y = getValue(vec3Ptr + 8, 'double');
|
|
const z = getValue(vec3Ptr + 16, 'double');
|
|
const vert = [x, y, z];
|
|
return sdf(vert);
|
|
}, 'di');
|
|
const out =
|
|
Module._LevelSet(wasmFuncPtr, bounds2, edgeLength, level, tolerance);
|
|
removeFunction(wasmFuncPtr);
|
|
return out;
|
|
};
|
|
|
|
// ctx-aware static factories: mirror Manifold.ofMesh / levelSet but
|
|
// run under this ExecutionContext so progress/cancellation are observed.
|
|
Module.ExecutionContext.prototype.fromMesh = function(mesh) {
|
|
return this._FromMesh(mesh);
|
|
};
|
|
|
|
Module.ExecutionContext.prototype.levelSet = function(
|
|
sdf, bounds, edgeLength, level = 0, tolerance = -1) {
|
|
const bounds2 = {
|
|
min: {x: bounds.min[0], y: bounds.min[1], z: bounds.min[2]},
|
|
max: {x: bounds.max[0], y: bounds.max[1], z: bounds.max[2]},
|
|
};
|
|
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
|
const x = getValue(vec3Ptr, 'double');
|
|
const y = getValue(vec3Ptr + 8, 'double');
|
|
const z = getValue(vec3Ptr + 16, 'double');
|
|
const vert = [x, y, z];
|
|
return sdf(vert);
|
|
}, 'di');
|
|
const out =
|
|
this._LevelSet(wasmFuncPtr, bounds2, edgeLength, level, tolerance);
|
|
removeFunction(wasmFuncPtr);
|
|
return out;
|
|
};
|
|
|
|
function pushVec3(vec, ps) {
|
|
toVec(vec, ps, p => {
|
|
if (p instanceof Array) return {x: p[0], y: p[1], z: p[2]};
|
|
return p;
|
|
})
|
|
}
|
|
|
|
Module.Manifold.hull = function(...args) {
|
|
if (args.length == 1) args = args[0];
|
|
let pts = new Module.Vector_vec3();
|
|
for (const m of args) {
|
|
if (m instanceof ManifoldCtor) {
|
|
Module._manifoldCollectVertices(pts, m);
|
|
} else if (
|
|
m instanceof Array && m.length == 3 && typeof m[0] == 'number') {
|
|
pts.push_back({x: m[0], y: m[1], z: m[2]});
|
|
} else if (m.x) {
|
|
pts.push_back(m);
|
|
} else {
|
|
pushVec3(pts, m);
|
|
}
|
|
}
|
|
const result = Module._manifoldHullPoints(pts);
|
|
pts.delete();
|
|
return result;
|
|
};
|
|
|
|
Module.Manifold.prototype = Object.create(ManifoldCtor.prototype);
|
|
|
|
// Because the constructor and prototype are being replaced, instanceof will
|
|
// not work as desired unless we refer back to the original like this
|
|
Object.defineProperty(Module.Manifold, Symbol.hasInstance, {
|
|
get: () => (t) => {
|
|
return (t instanceof ManifoldCtor);
|
|
}
|
|
});
|
|
|
|
// Top-level functions
|
|
|
|
Module.triangulate = function(polygons, epsilon = -1, allowConvex = true) {
|
|
const polygonsVec = polygons2vec(polygons);
|
|
const result = fromVec(
|
|
Module._Triangulate(polygonsVec, epsilon, allowConvex),
|
|
(x) => [x[0], x[1], x[2]]);
|
|
disposePolygons(polygonsVec);
|
|
return result;
|
|
};
|
|
};
|