mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-07 17:04:58 +01:00
support nurbs / adopt nurbs api for boolean
This commit is contained in:
parent
43b27d18c5
commit
e3859bdebc
6 changed files with 76 additions and 79 deletions
|
|
@ -122,6 +122,11 @@ function addGlobalDebugActions(app) {
|
|||
__DEBUG__.AddPoint(nurbs.point(1, 1), 0x0000ff);
|
||||
__DEBUG__.AddPoint(nurbs.point(0, 1), 0x00ffff);
|
||||
},
|
||||
AddNormal: (atPoint, normal, color, scale) => {
|
||||
scale = scale || 100;
|
||||
__DEBUG__.AddSegment(atPoint, atPoint.plus(normal.multiply(scale)), color);
|
||||
},
|
||||
|
||||
HideSolids: () => {
|
||||
app.findAllSolidsOnScene().forEach(s => s.cadGroup.traverse(o => o.visible = false));
|
||||
app.viewer.render();
|
||||
|
|
|
|||
|
|
@ -84,14 +84,16 @@ App.prototype.addShellOnScene = function(shell, skin) {
|
|||
};
|
||||
|
||||
App.prototype.scratchCode = function() {
|
||||
let box = createBox(500, 500, 500);
|
||||
let sphere = createSphere([0, 200, 0], 300);
|
||||
let clylinder = createCylinder(150, 500);
|
||||
|
||||
this.viewer.workGroup.add(box.toThreeMesh());
|
||||
// this.viewer.workGroup.add(sphere.toThreeMesh());
|
||||
this.viewer.workGroup.add(clylinder.toThreeMesh());
|
||||
this.viewer.render();
|
||||
const app = this;
|
||||
const box1 = app.TPI.brep.primitives.box(500, 500, 500);
|
||||
const box2 = app.TPI.brep.primitives.box(250, 250, 750, new Matrix3().translate(25, 25, 0));
|
||||
const box3 = app.TPI.brep.primitives.box(150, 600, 350, new Matrix3().translate(25, 25, -250));
|
||||
let result = app.TPI.brep.bool.union(box1, box2);
|
||||
// let result = app.TPI.brep.bool.subtract(box1, box2);
|
||||
// result = app.TPI.brep.bool.subtract(result, box3);
|
||||
app.addShellOnScene(box1);
|
||||
app.addShellOnScene(box2);
|
||||
app.viewer.render();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -357,16 +357,6 @@ export function iterateSegments(items, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
export function invertLoop(loop) {
|
||||
for (let halfEdge of loop.halfEdges) {
|
||||
const t = halfEdge.vertexA;
|
||||
halfEdge.vertexA = halfEdge.vertexB;
|
||||
halfEdge.vertexB = t;
|
||||
}
|
||||
loop.halfEdges.reverse();
|
||||
linkSegments(loop.halfEdges);
|
||||
}
|
||||
|
||||
export function createPlaneLoop(vertices, curves) {
|
||||
|
||||
const loop = new Loop();
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ export class NurbsCurve extends Curve {
|
|||
}
|
||||
|
||||
tangentAtPoint(point) {
|
||||
return new Point().set3(this.verb.tangent(this.verb.closestParam(point.data())));
|
||||
return new Point().set3(this.verb.tangent(this.verb.closestParam(point.data())))._normalize();
|
||||
}
|
||||
|
||||
tangentAtParam(param) {
|
||||
return new Point().set3(this.verb.tangent(param ));
|
||||
return new Point().set3(this.verb.tangent(param ))._normalize();
|
||||
}
|
||||
|
||||
closestDistanceToPoint(point) {
|
||||
|
|
@ -67,10 +67,6 @@ export class NurbsCurve extends Curve {
|
|||
return this.verb.split(this.verb.closestParam(point.data)).map(v => new NurbsCurve(v));
|
||||
}
|
||||
|
||||
intersect(other, tolerance) {
|
||||
return verb.geom.Intersect.curves(this.verb, other.verb, tolerance);
|
||||
}
|
||||
|
||||
invert() {
|
||||
return new NurbsCurve(this.verb.reverse());
|
||||
}
|
||||
|
|
@ -78,7 +74,16 @@ export class NurbsCurve extends Curve {
|
|||
point(u) {
|
||||
return new Point().set3(this.verb.point(u));
|
||||
}
|
||||
|
||||
|
||||
intersectCurve(other, tol) {
|
||||
return verb.geom.Intersect.curves(this.verb, other.verb, tol).map( i => ({
|
||||
u0: i.u0,
|
||||
u1: i.u1,
|
||||
p0: new Vector().set3(i.point0),
|
||||
p1: new Vector().set3(i.point1)
|
||||
}));
|
||||
}
|
||||
|
||||
static createByPoints(points, degeree) {
|
||||
points = points.map(p => p.data());
|
||||
return new NurbsCurve(new verb.geom.NurbsCurve.byPoints(points, degeree));
|
||||
|
|
@ -111,6 +116,7 @@ export class NurbsSurface extends Surface {
|
|||
if (this.inverted) {
|
||||
normal._negate();
|
||||
}
|
||||
normal._normalize();
|
||||
return normal;
|
||||
}
|
||||
|
||||
|
|
@ -119,22 +125,19 @@ export class NurbsSurface extends Surface {
|
|||
if (this.inverted) {
|
||||
normal._negate();
|
||||
}
|
||||
normal._normalize();
|
||||
return normal;
|
||||
}
|
||||
|
||||
normalInMiddle(point) {
|
||||
let normal = new Vector().set3(this.verb.normal(0.5, 0.5));
|
||||
if (this.inverted) {
|
||||
normal._negate();
|
||||
}
|
||||
return normal;
|
||||
normalInMiddle() {
|
||||
return this.normalUV(0.5, 0.5);
|
||||
}
|
||||
|
||||
point(u, v) {
|
||||
return new Point().set3(this.verb.point(u, v));
|
||||
}
|
||||
|
||||
intersectForSameClass(other, tol) {
|
||||
intersectSurfaceForSameClass(other, tol) {
|
||||
const curves = verb.geom.Intersect.surfaces(this.verb, other.verb, tol);
|
||||
let inverted = this.inverted !== other.inverted;
|
||||
return curves.map(curve => new NurbsCurve(inverted ? curve.reverse() : curve));
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ export class Surface {
|
|||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
intersectForSameClass() {
|
||||
intersectSurfaceForSameClass() {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
||||
intersect(other, tol) {
|
||||
intersectSurface(other, tol) {
|
||||
if (this.isSameClass(other)) {
|
||||
return this.intersectForSameClass(other, tol)
|
||||
return this.intersectSurfaceForSameClass(other, tol)
|
||||
}
|
||||
return this.toNurbs().intersectForSameClass(other.toNurbs(), tol);
|
||||
return this.toNurbs().intersectSurfaceForSameClass(other.toNurbs(), tol);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -46,20 +46,20 @@ export function subtract( shell1, shell2 ) {
|
|||
export function invert( shell ) {
|
||||
for (let face of shell.faces) {
|
||||
face.surface = face.surface.invert();
|
||||
for (let loop of face.loops) {
|
||||
invertLoop(loop);
|
||||
}
|
||||
for (let edge of shell.edges) {
|
||||
edge.invert();
|
||||
}
|
||||
for (let loop of face.loops) {
|
||||
for (let i = 0; i < loop.halfEdges.length; i++) {
|
||||
loop.halfEdges[i] = loop.halfEdges[i].twin();
|
||||
}
|
||||
loop.halfEdges.reverse();
|
||||
loop.link();
|
||||
}
|
||||
}
|
||||
BREPValidator.validateToConsole(shell);
|
||||
}
|
||||
|
||||
function invertLoop(loop) {
|
||||
BREPBuilder.invertLoop(loop);
|
||||
}
|
||||
|
||||
export function BooleanAlgorithm( shell1, shell2, type ) {
|
||||
|
||||
POINT_TO_VERT.clear();
|
||||
|
|
@ -297,11 +297,16 @@ function leftTurningMeasure(v1, v2, normal) {
|
|||
}
|
||||
|
||||
function intersectEdges(shell1, shell2) {
|
||||
const tuples1 = [];
|
||||
const tuples2 = [];
|
||||
function collectTuples(shell) {
|
||||
const tuples = [];
|
||||
for (let edge of shell.edges) {
|
||||
tuples.push([edge]);
|
||||
}
|
||||
return tuples;
|
||||
}
|
||||
|
||||
shell1.edges.forEach(e => tuples1.push([e]));
|
||||
shell2.edges.forEach(e => tuples2.push([e]));
|
||||
const tuples1 = collectTuples(shell1);
|
||||
const tuples2 = collectTuples(shell2);
|
||||
|
||||
for (let i = 0; i < tuples1.length; i++) {
|
||||
const edges1 = tuples1[i];
|
||||
|
|
@ -311,7 +316,7 @@ function intersectEdges(shell1, shell2) {
|
|||
const e1 = edges1[k];
|
||||
for (let l = edges2.length - 1; l >= 0 ; l--) {
|
||||
const e2 = edges2[l];
|
||||
let points = e1.curve.intersect(e2.curve, TOLERANCE);
|
||||
let points = e1.curve.intersectCurve(e2.curve, TOLERANCE);
|
||||
|
||||
for (let point of points) {
|
||||
const {u0, u1} = point;
|
||||
|
|
@ -344,26 +349,23 @@ function intersectEdges(shell1, shell2) {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: extract to a unit test
|
||||
function curveDirectionValidityTest(curve, surface1, surface2, operationType) {
|
||||
|
||||
function fixCurveDirection(curve, surface1, surface2, operationType) {
|
||||
let point = curve.point(0.5);
|
||||
let tangent = curve.tangentAtPoint(point);
|
||||
assert('tangent should be normalized', eq(tangent.length, 1));
|
||||
|
||||
let normal1 = surface1.normal(point);
|
||||
assert('normal should be normalized', eq(normal1.length, 1));
|
||||
|
||||
let normal2 = surface2.normal(point);
|
||||
assert('normal should be normalized', eq(normal2.length, 1));
|
||||
|
||||
let expectedDirection = normal1.cross(normal2);
|
||||
|
||||
if (operationType === TYPE.UNION) {
|
||||
expectedDirection._negate();
|
||||
}
|
||||
|
||||
let sameAsExpected = expectedDirection.dot(tangent) > 0;
|
||||
assert('wrong intersection curve direction', sameAsExpected);
|
||||
if (sameAsExpected) {
|
||||
curve = curve.invert();
|
||||
}
|
||||
return curve;
|
||||
}
|
||||
|
||||
//TODO: extract to a unit test
|
||||
|
|
@ -392,13 +394,10 @@ function intersectFaces(shell1, shell2, operationType) {
|
|||
}
|
||||
}
|
||||
|
||||
let curves = face1.surface.intersect(face2.surface, TOLERANCE);
|
||||
let curves = face1.surface.intersectSurface(face2.surface, TOLERANCE);
|
||||
|
||||
for (let curve of curves) {
|
||||
if (invert) {
|
||||
curve = curve.invert();
|
||||
}
|
||||
curveDirectionValidityTest(curve, face1.surface, face2.surface, invert);
|
||||
curve = fixCurveDirection(curve, face1.surface, face2.surface, operationType);
|
||||
const nodes = [];
|
||||
collectNodesOfIntersectionOfFace(curve, face1, nodes);
|
||||
collectNodesOfIntersectionOfFace(curve, face2, nodes);
|
||||
|
|
@ -409,7 +408,7 @@ function intersectFaces(shell1, shell2, operationType) {
|
|||
split(nodes, curve, newEdges);
|
||||
|
||||
newEdges.forEach(e => {
|
||||
newEdgeDirectionValidityTest(e, curve)
|
||||
newEdgeDirectionValidityTest(e, curve);
|
||||
addNewEdge(face1, e.halfEdge1);
|
||||
addNewEdge(face2, e.halfEdge2);
|
||||
});
|
||||
|
|
@ -465,33 +464,31 @@ function collectNodesOfIntersectionOfFace(curve, face, nodes) {
|
|||
|
||||
function collectNodesOfIntersection(curve, loop, nodes) {
|
||||
for (let edge of loop.halfEdges) {
|
||||
intersectCurveWithEdge(curves, edge, nodes);
|
||||
intersectCurveWithEdge(curve, edge, nodes);
|
||||
}
|
||||
}
|
||||
|
||||
function intersectCurveWithEdge(curve, edge, result) {
|
||||
for (let i = 0; i < curves.length; ++i) {
|
||||
const points = edge.edge.curve.intersectCurve(curve);
|
||||
for (let point of points) {
|
||||
const {u0, u1} = point;
|
||||
const points = edge.edge.curve.intersectCurve(curve, TOLERANCE);
|
||||
for (let point of points) {
|
||||
const {u0, u1} = point;
|
||||
|
||||
let vertex;
|
||||
if (equal(u0, 0)) {
|
||||
vertex = edge.edge.halfEdge1.vertexA;
|
||||
} else if (equal(u0, 1)) {
|
||||
vertex = edge.edge.halfEdge1.vertexB;
|
||||
} else {
|
||||
vertex = new Vertex(edge.edge.curve.point(u0));
|
||||
}
|
||||
|
||||
result.push(new Node(vertex, edge, curve, u1));
|
||||
let vertex;
|
||||
if (equal(u0, 0)) {
|
||||
vertex = edge.edge.halfEdge1.vertexA;
|
||||
} else if (equal(u0, 1)) {
|
||||
vertex = edge.edge.halfEdge1.vertexB;
|
||||
} else {
|
||||
vertex = new Vertex(edge.edge.curve.point(u0));
|
||||
}
|
||||
|
||||
result.push(new Node(vertex, edge, curve, u1));
|
||||
}
|
||||
}
|
||||
|
||||
function split(nodes, curve, result) {
|
||||
nodes.sort((n1, n2) => n1.u - n2.u);
|
||||
nodes = nodes.filter(n => n !== null);
|
||||
nodes.sort((n1, n2) => n1.u - n2.u);
|
||||
for (let i = 0; i < nodes.length - 1; i++) {
|
||||
let inNode = nodes[i];
|
||||
let outNode = nodes[i + 1];
|
||||
|
|
|
|||
Loading…
Reference in a new issue