mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-15 04:45:06 +01:00
boolean manifold
This commit is contained in:
parent
1714daefe8
commit
bd490f13b7
7 changed files with 267 additions and 73 deletions
|
|
@ -88,17 +88,22 @@ function addGlobalDebugActions(app) {
|
|||
AddFace: (face, color) => {
|
||||
for (let e of face.edges) __DEBUG__.AddHalfEdge(e, color);
|
||||
},
|
||||
AddLoop: (loop, color) => {
|
||||
for (let e of loop.halfEdges) __DEBUG__.AddHalfEdge(e, color);
|
||||
},
|
||||
AddVolume: (shell, color) => {
|
||||
color = color || 0xffffff;
|
||||
const geometry = new THREE.Geometry();
|
||||
triangulateToThree(shell.faces, geometry);
|
||||
triangulateToThree(shell, geometry);
|
||||
const mesh = new THREE.Mesh(geometry, createSolidMaterial({
|
||||
color,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
opacity: 0.3,
|
||||
depthWrite: false,
|
||||
depthTest: false
|
||||
}));
|
||||
debugVolumeGroup.add(mesh);
|
||||
window.__DEBUG__.AddWireframe(shell, color);
|
||||
// window.__DEBUG__.AddWireframe(shell, color);
|
||||
app.viewer.render();
|
||||
},
|
||||
AddWireframe: (shell, color) => {
|
||||
|
|
|
|||
|
|
@ -236,9 +236,9 @@ App.prototype.test5 = function() {
|
|||
App.prototype.scratchCode = function() {
|
||||
// const app = this;
|
||||
// this.test3();
|
||||
// this.cylTest();
|
||||
this.cylTest();
|
||||
|
||||
// return
|
||||
return
|
||||
|
||||
// let curve1 = new NurbsCurve(new verb.geom.NurbsCurve({"degree":6,"controlPoints":[[150,149.99999999999997,-249.99999999999994,1],[108.33333333333051,150.00000000000907,-250.00000000001975,1],[66.6666666666712,149.99999999998562,-249.99999999996987,1],[24.99999999999545,150.00000000001364,-250.00000000002711,1],[-16.66666666666362,149.99999999999145,-249.9999999999837,1],[-58.33333333333436,150.0000000000029,-250.00000000000531,1],[-99.99999999999997,150,-250,1]],"knots":[0,0,0,0,0,0,0,1,1,1,1,1,1,1]}));
|
||||
// let curve2 = new NurbsCurve(new verb.geom.NurbsCurve({"degree":9,"controlPoints":[[100,-250,-250,1],[99.9999999999927,-194.44444444444687,-250.00000000000028,1],[100.00000000002228,-138.8888888888811,-249.99999999999838,1],[99.99999999995923,-83.33333333334777,-250.00000000000287,1],[100.00000000005268,-27.77777777775936,-249.99999999999744,1],[99.9999999999493,27.777777777760704,-250.0000000000008,1],[100.00000000003591,83.33333333334477,-250.00000000000063,1],[99.99999999998269,138.88888888888374,-249.99999999999966,1],[100.00000000000443,194.44444444444562,-249.99999999999986,1],[100,250,-250,1]],"knots":[0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]}));
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ function Viewer(bus, container) {
|
|||
axisGeom.vertices.push(axis.multiply(1000).three());
|
||||
scene.add(new THREE.Line(axisGeom, lineMaterial));
|
||||
}
|
||||
addAxis(AXIS.X, 0xFF0000);
|
||||
addAxis(AXIS.Y, 0x00FF00);
|
||||
addAxis(AXIS.Z, 0x0000FF);
|
||||
// addAxis(AXIS.X, 0xFF0000);
|
||||
// addAxis(AXIS.Y, 0x00FF00);
|
||||
// addAxis(AXIS.Z, 0x0000FF);
|
||||
|
||||
this.updateControlsAndHelpers = function() {
|
||||
trackballControls.update();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as vec from "../../../math/vec";
|
||||
import * as math from '../../../math/math'
|
||||
import {eqEps, TOLERANCE, TOLERANCE_SQ} from '../tolerance';
|
||||
import {eqEps, TOLERANCE, TOLERANCE_01, TOLERANCE_SQ} from '../tolerance';
|
||||
import {fmin_bfgs} from "../../../math/optim";
|
||||
|
||||
export function curveStep(curve, u, tessTol, scale) {
|
||||
|
|
@ -100,7 +100,7 @@ export function surfaceIntersect(surface0, surface1) {
|
|||
fixTessNaNPoitns(surface0, tess0);
|
||||
fixTessNaNPoitns(surface1, tess1);
|
||||
|
||||
const resApprox = verb.eval.Intersect.meshes(tess0,tess1);
|
||||
const resApprox = meshesIntersect(tess0,tess1, TOLERANCE, TOLERANCE_SQ, TOLERANCE_01);
|
||||
const exactPls = resApprox.map(function(pl) {
|
||||
return pl.map(function(inter) {
|
||||
return verb.eval.Intersect.surfacesAtPointWithEstimate(surface0,surface1,inter.uv0,inter.uv1,TOLERANCE);
|
||||
|
|
@ -118,6 +118,30 @@ export function surfaceIntersect(surface0, surface1) {
|
|||
});
|
||||
}
|
||||
|
||||
function meshesIntersect(mesh0,mesh1, TOLERANCE, TOLERANCE_SQ, TOLERANCE_01) {
|
||||
let bbtree0 = new verb.core.LazyMeshBoundingBoxTree(mesh0);
|
||||
let bbtree1 = new verb.core.LazyMeshBoundingBoxTree(mesh1);
|
||||
let bbints = verb.eval.Intersect.boundingBoxTrees(bbtree0,bbtree1,TOLERANCE);
|
||||
let segments = verb.core.ArrayExtensions.unique(bbints.map(function(ids) {
|
||||
return verb.eval.Intersect.triangles(mesh0,ids.item0,mesh1,ids.item1);
|
||||
}).filter(function(x) {
|
||||
return x != null;
|
||||
}).filter(function(x1) {
|
||||
return verb.core.Vec.distSquared(x1.min.point,x1.max.point) > TOLERANCE_SQ;
|
||||
}),function(a,b) {
|
||||
let s1 = verb.core.Vec.sub(a.min.uv0,b.min.uv0);
|
||||
let d1 = verb.core.Vec.dot(s1,s1);
|
||||
let s2 = verb.core.Vec.sub(a.max.uv0,b.max.uv0);
|
||||
let d2 = verb.core.Vec.dot(s2,s2);
|
||||
let s3 = verb.core.Vec.sub(a.min.uv0,b.max.uv0);
|
||||
let d3 = verb.core.Vec.dot(s3,s3);
|
||||
let s4 = verb.core.Vec.sub(a.max.uv0,b.min.uv0);
|
||||
let d4 = verb.core.Vec.dot(s4,s4);
|
||||
return d1 < TOLERANCE_01 && d2 < TOLERANCE_01 || d3 < TOLERANCE_01 && d4 < TOLERANCE_01;
|
||||
});
|
||||
return verb.eval.Intersect.makeMeshIntersectionPolylines(segments);
|
||||
}
|
||||
|
||||
export function surfaceMaxDegree(surface) {
|
||||
return Math.max(surface.degreeU, surface.degreeV);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,8 +336,8 @@ export class NurbsSurface extends Surface {
|
|||
return b.minus(a).cross(c.minus(a))._normalize().dot(surface.normalUV(0, 0)) < 0;
|
||||
}
|
||||
|
||||
intersectSurfaceForSameClass(other, tol) {
|
||||
let curves = ext.surfaceIntersect(this.data, other.data, tol);
|
||||
intersectSurfaceForSameClass(other) {
|
||||
let curves = ext.surfaceIntersect(this.data, other.data);
|
||||
let inverted = this.inverted !== other.inverted;
|
||||
if (inverted) {
|
||||
curves = curves.map(curve => ext.curveInvert(curve));
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import {Shell} from '../topo/shell';
|
|||
import {Vertex} from '../topo/vertex';
|
||||
import {evolveFace} from './evolve-face'
|
||||
import * as math from '../../math/math';
|
||||
import {eqTol, TOLERANCE, ueq, veq} from '../geom/tolerance';
|
||||
import {eqEps, eqTol, TOLERANCE, ueq, veq} from '../geom/tolerance';
|
||||
|
||||
const DEBUG = {
|
||||
OPERANDS_MODE: false,
|
||||
LOOP_DETECTION: false,
|
||||
LOOP_DETECTION: true,
|
||||
FACE_FACE_INTERSECTION: false,
|
||||
NOOP: () => {}
|
||||
};
|
||||
|
|
@ -57,7 +57,7 @@ export function BooleanAlgorithm( shell1, shell2, type ) {
|
|||
let facesData = [];
|
||||
|
||||
mergeVertices(shell1, shell2);
|
||||
initVertexFactory(shell1, shell2)
|
||||
initVertexFactory(shell1, shell2);
|
||||
|
||||
intersectEdges(shell1, shell2);
|
||||
|
||||
|
|
@ -70,20 +70,29 @@ export function BooleanAlgorithm( shell1, shell2, type ) {
|
|||
faceData.initGraph();
|
||||
}
|
||||
|
||||
const allFaces = [];
|
||||
const newLoops = new Set();
|
||||
for (let faceData of facesData) {
|
||||
const face = faceData.face;
|
||||
const loops = detectLoops(faceData.face);
|
||||
for (let loop of loops) {
|
||||
for (let edge of loop.halfEdges) {
|
||||
if (isNew(edge)) newLoops.add(loop);
|
||||
}
|
||||
}
|
||||
loopsToFaces(face, loops, allFaces);
|
||||
faceData.detectedLoops = detectLoops(faceData.face);
|
||||
}
|
||||
let faces = allFaces;
|
||||
faces = filterFaces(faces, newLoops);
|
||||
|
||||
let detectedLoops = new Set();
|
||||
for (let faceData of facesData) {
|
||||
for (let loop of faceData.detectedLoops) {
|
||||
detectedLoops.add(loop);
|
||||
}
|
||||
}
|
||||
|
||||
// let invalidLoops = invalidateLoops(detectedLoops);
|
||||
|
||||
let faces = [];
|
||||
|
||||
for (let faceData of facesData) {
|
||||
// faceData.detectedLoops = faceData.detectedLoops.filter(l => !invalidLoops.has(l));
|
||||
loopsToFaces(faceData.face, faceData.detectedLoops, faces);
|
||||
}
|
||||
|
||||
faces = filterFaces(faces);
|
||||
|
||||
|
||||
const result = new Shell();
|
||||
faces.forEach(face => {
|
||||
face.shell = result;
|
||||
|
|
@ -93,7 +102,7 @@ export function BooleanAlgorithm( shell1, shell2, type ) {
|
|||
cleanUpSolveData(result);
|
||||
BREPValidator.validateToConsole(result);
|
||||
|
||||
__DEBUG__.ClearVolumes();
|
||||
// __DEBUG__.ClearVolumes();
|
||||
// __DEBUG__.Clear();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -108,12 +117,8 @@ function detectLoops(face) {
|
|||
|
||||
const loops = [];
|
||||
const seen = new Set();
|
||||
let edges = [];
|
||||
for (let e of face.edges) {
|
||||
edges.push(e);
|
||||
}
|
||||
while (true) {
|
||||
let edge = edges.pop();
|
||||
let edge = faceData.graphEdges.pop();
|
||||
if (!edge) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -122,12 +127,19 @@ function detectLoops(face) {
|
|||
}
|
||||
const loop = new Loop(null);
|
||||
let surface = face.surface;
|
||||
|
||||
while (edge) {
|
||||
if (DEBUG.LOOP_DETECTION) {
|
||||
__DEBUG__.AddHalfEdge(edge);
|
||||
}
|
||||
loop.halfEdges.push(edge);
|
||||
seen.add(edge);
|
||||
loop.halfEdges.push(edge);
|
||||
if (loop.halfEdges[0].vertexA === edge.vertexB) {
|
||||
loop.link();
|
||||
loops.push(loop);
|
||||
break;
|
||||
}
|
||||
|
||||
let candidates = faceData.vertexToEdge.get(edge.vertexB);
|
||||
if (!candidates) {
|
||||
break;
|
||||
|
|
@ -137,11 +149,6 @@ function detectLoops(face) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loop.halfEdges[0].vertexA === loop.halfEdges[loop.halfEdges.length - 1].vertexB) {
|
||||
loop.link();
|
||||
loops.push(loop);
|
||||
}
|
||||
}
|
||||
return loops;
|
||||
}
|
||||
|
|
@ -170,29 +177,54 @@ export function mergeVertices(shell1, shell2) {
|
|||
}
|
||||
}
|
||||
|
||||
function filterFaces(faces, newLoops) {
|
||||
const validFaces = new Set(faces);
|
||||
const result = new Set();
|
||||
for (let face of faces) {
|
||||
traverseFaces(face, validFaces, (it) => {
|
||||
if (result.has(it) || isFaceContainNewLoop(it, newLoops)) {
|
||||
result.add(face);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
|
||||
function filterFaces(faces) {
|
||||
|
||||
|
||||
return faces.filter(raycastFilter);
|
||||
|
||||
|
||||
//
|
||||
// function isFaceContainNewEdge(face) {
|
||||
// for (let e of face.edges) {
|
||||
// if (isNewNM(e)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// const validFaces = new Set(faces);
|
||||
// const result = new Set();
|
||||
// for (let face of faces) {
|
||||
// __DEBUG__.Clear();
|
||||
// __DEBUG__.AddFace(face);
|
||||
// traverseFaces(face, validFaces, (it) => {
|
||||
// if (result.has(it) || isFaceContainNewEdge(it)) {
|
||||
// result.add(face);
|
||||
// return true;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// return result;
|
||||
}
|
||||
|
||||
function isFaceContainNewLoop(face, newLoops) {
|
||||
for (let loop of face.loops) {
|
||||
if (newLoops.has(loop)) {
|
||||
return true;
|
||||
}
|
||||
function raycastFilter(face, shell, opType) {
|
||||
|
||||
let testPt = getPointOnFace(face);
|
||||
let testCurve = ;
|
||||
|
||||
|
||||
for (let testFace of face.faces) {
|
||||
let pts = testFace.surface.intersectCurve(testCurve)
|
||||
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function traverseFaces(face, validFaces, callback) {
|
||||
const stack = [face];
|
||||
const seen = new Set();
|
||||
|
|
@ -203,18 +235,64 @@ function traverseFaces(face, validFaces, callback) {
|
|||
if (callback(face) === true) {
|
||||
return;
|
||||
}
|
||||
if (!validFaces.has(face)) continue;
|
||||
for (let loop of face.loops) {
|
||||
if (!validFaces.has(face)) continue;
|
||||
for (let halfEdge of loop.halfEdges) {
|
||||
const twin = halfEdge.twin();
|
||||
if (validFaces.has(twin.loop.face)) {
|
||||
stack.push(twin.loop.face)
|
||||
for (let twin of halfEdge.twins()) {
|
||||
if (validFaces.has(twin.loop.face)) {
|
||||
stack.push(twin.loop.face)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function invalidateLoops(newLoops) {
|
||||
// __DEBUG__.Clear();
|
||||
const invalid = new Set();
|
||||
for (let loop of newLoops) {
|
||||
// __DEBUG__.AddLoop(loop);
|
||||
for (let e of loop.halfEdges) {
|
||||
if (e.manifold !== null) {
|
||||
let manifold = [e, ...e.manifold];
|
||||
manifold.filter(me => newLoops.has(me.twin().loop));
|
||||
if (manifold.length === 0) {
|
||||
invalid.add(loop);
|
||||
} else {
|
||||
let [me, ...rest] = manifold;
|
||||
e.edge = me.edge;
|
||||
e.manifold = rest.length === 0 ? null : rest;
|
||||
}
|
||||
} else {
|
||||
if (!newLoops.has(e.twin().loop)) {
|
||||
invalid.add(loop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const seen = new Set();
|
||||
//
|
||||
// const stack = Array.from(invalid);
|
||||
//
|
||||
// while (stack.length !== 0) {
|
||||
// let loop = stack.pop();
|
||||
// if (!seen.has(loop)) continue;
|
||||
// seen.add(loop);
|
||||
//
|
||||
// for (let he of loop.halfEdges) {
|
||||
// let twins = he.twins();
|
||||
// for (let twin of twins) {
|
||||
// invalid.add(twin.loop);
|
||||
// stack.push(twin.loop);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return invalid;
|
||||
}
|
||||
|
||||
export function loopsToFaces(originFace, loops, out) {
|
||||
const newFaces = evolveFace(originFace, loops);
|
||||
for (let newFace of newFaces) {
|
||||
|
|
@ -250,13 +328,20 @@ function findMaxTurningLeft(pivotEdge, edges, surface) {
|
|||
const pivot = pivotEdge.tangent(pivotEdge.vertexB.point).negate();
|
||||
const normal = surface.normal(pivotEdge.vertexB.point);
|
||||
edges.sort((e1, e2) => {
|
||||
return leftTurningMeasure(pivot, edgeVector(e1), normal) - leftTurningMeasure(pivot, edgeVector(e2), normal);
|
||||
let delta = leftTurningMeasure(pivot, edgeVector(e1), normal) - leftTurningMeasure(pivot, edgeVector(e2), normal);
|
||||
if (ueq(delta, 0)) {
|
||||
return isNew(e1) ? (isNew(e2) ? 0 : -1) : (isNew(e2) ? 1 : 0)
|
||||
}
|
||||
return delta;
|
||||
});
|
||||
return edges[0];
|
||||
}
|
||||
|
||||
function leftTurningMeasure(v1, v2, normal) {
|
||||
let measure = v1.dot(v2);
|
||||
if (ueq(measure, 1)) {
|
||||
return 0;
|
||||
}
|
||||
measure += 3; //-1..1 => 2..4
|
||||
if (v1.cross(v2).dot(normal) < 0) {
|
||||
measure = 4 - measure;
|
||||
|
|
@ -365,7 +450,7 @@ function intersectFaces(shell1, shell2, operationType) {
|
|||
}
|
||||
}
|
||||
|
||||
let curves = face1.surface.intersectSurface(face2.surface, TOLERANCE);
|
||||
let curves = face1.surface.intersectSurface(face2.surface);
|
||||
|
||||
for (let curve of curves) {
|
||||
// __DEBUG__.AddCurve(curve);
|
||||
|
|
@ -394,7 +479,6 @@ function addNewEdge(face, halfEdge) {
|
|||
data.loopOfNew.halfEdges.push(halfEdge);
|
||||
halfEdge.loop = data.loopOfNew;
|
||||
EdgeSolveData.createIfEmpty(halfEdge).newEdgeFlag = true;
|
||||
//addToListInMap(data.vertexToEdge, halfEdge.vertexA, halfEdge);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -563,6 +647,18 @@ function isNew(edge) {
|
|||
return EdgeSolveData.get(edge).newEdgeFlag === true
|
||||
}
|
||||
|
||||
function isNewNM(edge) {
|
||||
if (edge.manifold === null) {
|
||||
return isNew(edge);
|
||||
}
|
||||
for (let me of edge.manifold) {
|
||||
if (isNew(me)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return isNew(edge);
|
||||
}
|
||||
|
||||
function Node(vertex, edge, curve, u) {
|
||||
this.vertex = vertex;
|
||||
this.edge = edge;
|
||||
|
|
@ -625,6 +721,7 @@ class FaceSolveData {
|
|||
this.loopOfNew = new Loop(face);
|
||||
face.innerLoops.push(this.loopOfNew);
|
||||
this.vertexToEdge = new Map();
|
||||
this.graphEdges = [];
|
||||
}
|
||||
|
||||
initGraph() {
|
||||
|
|
@ -632,20 +729,46 @@ class FaceSolveData {
|
|||
for (let he of this.face.edges) {
|
||||
this.addToGraph(he);
|
||||
}
|
||||
this.removeOppositeEdges();
|
||||
}
|
||||
|
||||
addToGraph(he) {
|
||||
addToListInMap(this.vertexToEdge, he.vertexA, he);
|
||||
// __DEBUG__.Clear();
|
||||
// __DEBUG__.AddFace(he.loop.face);
|
||||
// __DEBUG__.AddHalfEdge(he, 0xffffff);
|
||||
let list = this.vertexToEdge.get(he.vertexA);
|
||||
if (!list) {
|
||||
list = [];
|
||||
this.vertexToEdge.set(he.vertexA, list);
|
||||
} else {
|
||||
for (let ex of list) {
|
||||
if (he.vertexB === ex.vertexB && isSameEdge(he, ex)) {
|
||||
ex.attachManifold(he);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
list.push(he);
|
||||
this.graphEdges.push(he);
|
||||
}
|
||||
}
|
||||
|
||||
function addToListInMap(map, key, value) {
|
||||
let list = map.get(key);
|
||||
if (!list) {
|
||||
list = [];
|
||||
map.set(key, list);
|
||||
removeOppositeEdges() {
|
||||
let toRemove = new Set();
|
||||
for (let e1 of this.graphEdges) {
|
||||
let others = this.vertexToEdge.get(e1.vertexB);
|
||||
for (let e2 of others) {
|
||||
if (e1 === e2) continue;
|
||||
if (e1.vertexA === e2.vertexB && isSameEdge(e1, e2)) {
|
||||
toRemove.add(e1);
|
||||
toRemove.add(e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let e of toRemove) {
|
||||
removeFromListInMap(this.vertexToEdge, e.vertexA, e);
|
||||
}
|
||||
this.graphEdges = this.graphEdges.filter(e => !toRemove.has(e));
|
||||
}
|
||||
list.push(value);
|
||||
}
|
||||
|
||||
function removeFromListInMap(map, key, value) {
|
||||
|
|
@ -658,6 +781,17 @@ function removeFromListInMap(map, key, value) {
|
|||
}
|
||||
}
|
||||
|
||||
function isSameEdge(e1, e2) {
|
||||
let tess = e1.tessellate();
|
||||
for (let pt1 of tess) {
|
||||
let pt2 = e2.edge.curve.point(e2.edge.curve.param(pt1));
|
||||
if (!veq(pt1, pt2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function $DEBUG_OPERANDS(shell1, shell2) {
|
||||
if (DEBUG.OPERANDS_MODE) {
|
||||
__DEBUG__.HideSolids();
|
||||
|
|
@ -676,3 +810,4 @@ function assert(name, cond) {
|
|||
|
||||
const MY = '__BOOLEAN_ALGORITHM_DATA__';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,10 +40,17 @@ class HalfEdge extends TopoObject {
|
|||
this.loop = null;
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
this.manifold = null;
|
||||
this.manifoldHolder = null;
|
||||
}
|
||||
|
||||
twin() {
|
||||
return this.edge.halfEdge1 === this ? this.edge.halfEdge2 : this.edge.halfEdge1;
|
||||
let twin = this.edge.halfEdge1 === this ? this.edge.halfEdge2 : this.edge.halfEdge1;
|
||||
return twin.manifoldHolder === null ? twin : twin.manifoldHolder;
|
||||
}
|
||||
|
||||
twins() {
|
||||
return this.manifold === null ? [this.twin()] : [this.twin(), ...this.manifold.map(me => me.twin())];
|
||||
}
|
||||
|
||||
tangent(point) {
|
||||
|
|
@ -54,4 +61,27 @@ class HalfEdge extends TopoObject {
|
|||
}
|
||||
return tangent;
|
||||
}
|
||||
|
||||
tessellate() {
|
||||
let res = this.edge.curve.tessellate.apply(this.edge.curve, arguments);
|
||||
if (this.inverted) {
|
||||
res = res.slice().reverse();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
attachManifold(he, shallow) {
|
||||
if (this.manifold === null) {
|
||||
this.manifold = [];
|
||||
}
|
||||
if (this.manifold.indexOf(he) === -1) {
|
||||
this.manifold.push(he);
|
||||
}
|
||||
he.manifoldHolder = this;
|
||||
// if (shallow === true) {
|
||||
// return;
|
||||
// }
|
||||
// this.twin().attachManifold()
|
||||
// he.attachManifold(, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue