From 13f2f8f6ee1e8f6bbdadeac73f4ede720253e50d Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Thu, 13 Apr 2017 17:49:03 -0700 Subject: [PATCH] PIP for NURBS --- web/app/3d/debug.js | 18 ++- web/app/3d/scene/brep-scene-object.js | 24 ++++ web/app/3d/tpi.js | 4 +- web/app/brep/brep-builder.js | 8 +- web/app/brep/geom/curve.js | 3 +- web/app/brep/geom/impl/line.js | 3 +- web/app/brep/geom/impl/nurbs.js | 45 ++++++- web/app/brep/geom/impl/plane.js | 34 ++++- web/app/brep/operations/boolean.js | 171 +++++++++++++++++-------- web/app/math/vector.js | 6 +- web/test/cases/brep-pip.js | 177 +++++++++++++++++++++++++- 11 files changed, 415 insertions(+), 78 deletions(-) diff --git a/web/app/3d/debug.js b/web/app/3d/debug.js index be770c34..e4e489ef 100644 --- a/web/app/3d/debug.js +++ b/web/app/3d/debug.js @@ -24,9 +24,14 @@ function addGlobalDebugActions(app) { app.viewer.render(); }, AddSegment: (a, b, color) => { - debugGroup.add(createLine(a, b, color)); - debugGroup.add(createPoint(a, 0x000088)); - debugGroup.add(createPoint(b, 0x880000)); + __DEBUG__.AddPolyLine([a, b], color); + }, + AddPolyLine: (points, color) => { + for (let i = 1; i < points.length; ++i) { + debugGroup.add(createLine(points[i - 1], points[i], color)); + } + debugGroup.add(createPoint(points[0], 0x000088)); + debugGroup.add(createPoint(points[points.length - 1], 0x880000)); app.viewer.render(); }, AddPoint: (coordinates, or, vector, andColorAtTheEnd) => { @@ -55,7 +60,12 @@ function addGlobalDebugActions(app) { app.viewer.render(); }, AddHalfEdge: (he, color) => { - window.__DEBUG__.AddSegment(he.vertexA.point, he.vertexB.point, color); + const points = [he.vertexA.point]; + if (he.edge && he.edge.curve) { + he.edge.curve.approximate(10, he.vertexA.point, he.vertexB.point, points); + } + points.push(he.vertexB.point); + window.__DEBUG__.AddPolyLine(points, color); }, AddFace: (face, color) => { for (let e of face.edges) __DEBUG__.AddHalfEdge(e, color); diff --git a/web/app/3d/scene/brep-scene-object.js b/web/app/3d/scene/brep-scene-object.js index 1c18e938..489f3aaf 100644 --- a/web/app/3d/scene/brep-scene-object.js +++ b/web/app/3d/scene/brep-scene-object.js @@ -149,6 +149,30 @@ export function triangulateToThree(shell, geom) { } //view.setFaceColor(sceneFace, utils.isSmoothPiece(group.shared) ? 0xFF0000 : null); } + } else if (brepFace.surface.constructor.name == 'NurbsSurface1') { + const off = geom.vertices.length; + const contours = []; + + for (let loop of brepFace.loops) { + const points = []; + for (let he of loop.halfEdges) { + points.push(he.vertexA.point); + he.edge.curve.approximate(10, he.vertexA.point, he.vertexB.point, points); + } + const verb = brepFace.surface.verb; + const uvs = points.map(point => verb.closestParam(point.data())); + uvs.forEach(uv => uv.push(0)); // add z coord + contours.push(uvs); + //....TODO + for (let i = 0; i < tessedUVs.length; i += 3 ) { + var a = new Vector().set3(tessedUVs[i]); + var b = new Vector().set3(tessedUVs[i + 1]); + var c = new Vector().set3(tessedUVs[i + 2]); + const normalOrNormals = normalOfCCWSeq([a, b, c]).three(); + const face = new THREE.Face3(off, off + 1, off + 2, normalOrNormals); + addFace(face); + } + } } else if (brepFace.surface.constructor.name == 'NurbsSurface') { const off = geom.vertices.length; const tess = brepFace.surface.verb.tessellate({maxDepth: 3}); diff --git a/web/app/3d/tpi.js b/web/app/3d/tpi.js index f8ec8ceb..339fdb3e 100644 --- a/web/app/3d/tpi.js +++ b/web/app/3d/tpi.js @@ -9,6 +9,8 @@ import {Face} from '../brep/topo/face'; import {Shell} from '../brep/topo/shell'; import {Vertex} from '../brep/topo/vertex'; import {Point} from '../brep/geom/point'; +import {NurbsCurve} from '../brep/geom/impl/nurbs'; +import {Plane} from '../brep/geom/impl/plane'; export default { brep: { @@ -17,7 +19,7 @@ export default { bool: BREPBool, validator: BREPValidator, geom: { - Point + Point, NurbsCurve, Plane }, topo: { HalfEdge, Edge, Loop, Face, Shell, Vertex diff --git a/web/app/brep/brep-builder.js b/web/app/brep/brep-builder.js index 07dc006a..c1efe83f 100644 --- a/web/app/brep/brep-builder.js +++ b/web/app/brep/brep-builder.js @@ -186,12 +186,14 @@ export function invertLoop(loop) { linkSegments(loop.halfEdges); } -export function createPlaneLoop(vertices) { +export function createPlaneLoop(vertices, curves) { const loop = new Loop(); - iterateSegments(vertices, (a, b) => { - createHalfEdge(loop, a, b) + iterateSegments(vertices, (a, b, i) => { + const halfEdge = createHalfEdge(loop, a, b); + halfEdge.edge = new Edge(curves[i] ? curves[i] : Line.fromSegment(a.point, b.point)); + return halfEdge; }); linkSegments(loop.halfEdges); diff --git a/web/app/brep/geom/curve.js b/web/app/brep/geom/curve.js index 80167e26..bb2961de 100644 --- a/web/app/brep/geom/curve.js +++ b/web/app/brep/geom/curve.js @@ -2,7 +2,6 @@ export class Curve { constructor() { - this.isLine = false; } intersectCurve(curve) { @@ -20,7 +19,7 @@ export class Curve { approximate(resolution, from, to, path) { } } - +Curve.prototype.isLine = false; export class CompositeCurve { diff --git a/web/app/brep/geom/impl/line.js b/web/app/brep/geom/impl/line.js index a722a4cc..e8185ba8 100644 --- a/web/app/brep/geom/impl/line.js +++ b/web/app/brep/geom/impl/line.js @@ -4,7 +4,6 @@ export class Line extends Curve { constructor(p0, v) { super(); - this.isLine = true; this.p0 = p0; this.v = v; this._pointsCache = new Map(); @@ -56,6 +55,8 @@ export class Line extends Curve { offset() {}; } +Line.prototype.isLine = true; + Line.fromTwoPlanesIntersection = function(plane1, plane2) { const n1 = plane1.normal; const n2 = plane2.normal; diff --git a/web/app/brep/geom/impl/nurbs.js b/web/app/brep/geom/impl/nurbs.js index 883b3e3c..2fa843df 100644 --- a/web/app/brep/geom/impl/nurbs.js +++ b/web/app/brep/geom/impl/nurbs.js @@ -1,6 +1,7 @@ import verb from 'verb-nurbs' import {Matrix3} from '../../../math/l3space' -import Vector from '../../../math/vector' +import * as math from '../../../math/math' +import {Point} from '../point' export class NurbsCurve { @@ -31,7 +32,7 @@ export class NurbsCurve { const step = this.verb.paramAtLength(length / resolution); u += step; for (;u < endU; u += step) { - out.push(new Vector().set3(this.verb.point(u))); + out.push(new Point().set3(this.verb.point(u))); } if (reverse) { for (let i = off, j = out.length - 1; i != j; ++i, --j) { @@ -41,8 +42,48 @@ export class NurbsCurve { } } } + + approximateU(resolution, paramFrom, paramTo, consumer) { + let u = paramFrom; + let endU = paramTo; + let step = this.verb.paramAtLength(resolution); + if (u > endU) { + step *= -1; + } + u += step; + for (;step > 0 ? u < endU : u > endU; u += step) { + consumer(u); + } + } + + tangentAtPoint(point) { + return new Point().set3(this.verb.tangent(this.verb.closestParam(point.data()))); + } + + closestDistanceToPoint(point) { + const closest = this.verb.closestPoint(point.data()); + return math.distance3(point.x, point.y, point.z, closest[0], closest[1], closest[2]); + } + + tangent(point) { + return new Point().set3(this.verb.tangent( this.verb.closestParam(point.data() ))); + } + + intersect(other, tolerance) { + return verb.geom.Intersect.curves(this.verb, other.verb, tolerance).map(i => new Point().set3(i.point0)); + } + + static createByPoints(points, degeree) { + points = points.map(p => p.data()); + return new NurbsCurve(new verb.geom.NurbsCurve.byPoints(points, degeree)); + } } +NurbsCurve.prototype.createLinearNurbs = function(a, b) { + return new NurbsCurve(new verb.geom.Line(a.data(), b.data())); +}; + + export class NurbsSurface { constructor(verbSurface) { diff --git a/web/app/brep/geom/impl/plane.js b/web/app/brep/geom/impl/plane.js index a816431b..0af42941 100644 --- a/web/app/brep/geom/impl/plane.js +++ b/web/app/brep/geom/impl/plane.js @@ -1,4 +1,5 @@ import {Surface} from '../surface' +import {Point} from '../point' import {Line} from './line' import {Matrix3, AXIS, BasisForPlane} from '../../../math/l3space' import * as math from '../../../math/math' @@ -38,11 +39,17 @@ export class Plane extends Surface { } get2DTransformation() { - return this.get3DTransformation().invert(); + if (!this.__2dTr) { + this.__2dTr = this.get3DTransformation().invert(); + } + return this.__2dTr; } get3DTransformation() { - return new Matrix3().setBasis(this.basis()); + if (!this.__3dTr) { + this.__3dTr = new Matrix3().setBasis(this.basis()); + } + return this.__3dTr; } coplanarUnsigned(other, tol) { @@ -58,8 +65,27 @@ export class Plane extends Surface { } toParametricForm() { - const basis = BasisForPlane(this.normal); - return new ParametricPlane(this.normal.multiply(this.w), basis.x, basis.y); + if (!this.__parametricForm) { + const basis = BasisForPlane(this.normal); + this.__parametricForm = new ParametricPlane(this.normal.multiply(this.w), basis.x, basis.y); + } + return this.__parametricForm; + } + + toUV(point) { + return this.get2DTransformation().apply(point); + } + + fromUV(u, v) { + return this.get3DTransformation()._apply(new Point(u, v, 0)); + } + + domainU() { + return [Number.MIN_VALUE, Number.MAX_VALUE]; + } + + domainV() { + return [Number.MIN_VALUE, Number.MAX_VALUE]; } } diff --git a/web/app/brep/operations/boolean.js b/web/app/brep/operations/boolean.js index 3a4e0d9e..62e5878b 100644 --- a/web/app/brep/operations/boolean.js +++ b/web/app/brep/operations/boolean.js @@ -667,7 +667,6 @@ export function loopsToFaces(originFace, loops, out) { } function getNestedLoops(face, brepLoops) { - const tr = face.surface.get2DTransformation(); function NestedLoop(loop) { this.loop = loop; this.nesting = []; @@ -677,7 +676,7 @@ function getNestedLoops(face, brepLoops) { const loops = brepLoops.map(loop => new NestedLoop(loop)); function contains(loop, other) { for (let point of other.asPolygon()) { - if (!classifyPointInsideLoop(tr.apply(point), loop, tr).inside) { + if (!classifyPointInsideLoop(point, loop, face.surface).inside) { return false; } } @@ -1076,9 +1075,9 @@ function classifyPointToFace(point, face) { } return result; } - const tr = face.surface.get2DTransformation(); - const point2d = tr.apply(point); - const outer = classifyPointInsideLoop(point2d, face.outerLoop, tr); + + const uvPt = face.surface.toUV(point); + const outer = classifyPointInsideLoop(point, face.outerLoop, face.surface, uvPt); if (outer.inside) { if (outer.vertex || outer.edge) { @@ -1087,7 +1086,7 @@ function classifyPointToFace(point, face) { } for (let innerLoop of face.innerLoops) { - const inner = classifyPointInsideLoop(point2d, innerLoop, tr); + const inner = classifyPointInsideLoop(point, innerLoop, face.surface, uvPt); if (inner.vertex || inner.edge) { return inner; } @@ -1209,7 +1208,7 @@ class FaceSolveData { } } -export function classifyPointInsideLoop( inPt, loop, tr ) { +export function classifyPointInsideLoop( pt, loop, surface, uvPt ) { function VertexResult(vertex) { this.inside = true; @@ -1221,20 +1220,37 @@ export function classifyPointInsideLoop( inPt, loop, tr ) { this.edge = edge; } - const _2dCoords = new Map(); + if (!uvPt) { + uvPt = surface.toUV(pt); + } + + function isLine(edge) { + return !edge.edge || !edge.edge.curve || edge.edge.curve.isLine; + } + + const uvCoords = new Map(); for( let edge of loop.halfEdges ) { - const p = tr.apply(edge.vertexA.point); - if (math.areEqual(inPt.y, p.y, TOLERANCE) && math.areEqual(inPt.x, p.x, TOLERANCE)) { + const uv = surface.toUV(edge.vertexA.point); + if (math.areEqual(uvPt.y, uv.y, TOLERANCE) && math.areEqual(uvPt.x, uv.x, TOLERANCE)) { return new VertexResult(edge.vertexA); } - _2dCoords.set(edge.vertexA, p); + uvCoords.set(edge.vertexA, uv); } const grads = []; for( let edge of loop.halfEdges ) { - const a = _2dCoords.get(edge.vertexA); - const b = _2dCoords.get(edge.vertexB); - const dy = b.y - a.y; + const a = uvCoords.get(edge.vertexA); + const b = uvCoords.get(edge.vertexB); + let dy; + if (isLine(edge)) { + dy = b.y - a.y; + } else { + const tangent = edge.edge.curve.tangent(edge.vertexA.point); + dy = surface.toUV(tangent).y; + if (edge.edge.invertedToCurve) { + dy *= -1; + } + } if (math.areEqual(dy, 0, TOLERANCE)) { grads.push(0) } else if (dy > 0) { @@ -1264,6 +1280,7 @@ export function classifyPointInsideLoop( inPt, loop, tr ) { const skip = new Set(); + let ray = null; let inside = false; for( let i = 0; i < loop.halfEdges.length; ++i) { @@ -1271,11 +1288,11 @@ export function classifyPointInsideLoop( inPt, loop, tr ) { var shouldBeSkipped = skip.has(edge.vertexA) || skip.has(edge.vertexB); - const a = _2dCoords.get(edge.vertexA); - const b = _2dCoords.get(edge.vertexB); + const a = uvCoords.get(edge.vertexA); + const b = uvCoords.get(edge.vertexB); - const aEq = math.areEqual(inPt.y, a.y, TOLERANCE); - const bEq = math.areEqual(inPt.y, b.y, TOLERANCE); + const aEq = math.areEqual(uvPt.y, a.y, TOLERANCE); + const bEq = math.areEqual(uvPt.y, b.y, TOLERANCE); if (aEq) { skip.add(edge.vertexA); @@ -1287,50 +1304,98 @@ export function classifyPointInsideLoop( inPt, loop, tr ) { if (math.areVectorsEqual(a, b, TOLERANCE)) { console.error('unable to classify invalid polygon'); } - - var edgeLowPt = a; - var edgeHighPt = b; - var edgeDx = edgeHighPt.x - edgeLowPt.x; - var edgeDy = edgeHighPt.y - edgeLowPt.y; - - if (aEq && bEq) { - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) { - return new EdgeResult(edge); - } else { + if (isLine(edge)) { + let edgeLowPt = a; + let edgeHighPt = b; + + let edgeDx = edgeHighPt.x - edgeLowPt.x; + let edgeDy = edgeHighPt.y - edgeLowPt.y; + + if (aEq && bEq) { + if ( ( ( edgeHighPt.x <= uvPt.x ) && ( uvPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= uvPt.x ) && ( uvPt.x <= edgeHighPt.x ) ) ) { + return new EdgeResult(edge); + } else { + continue; + } + } + + if (shouldBeSkipped) { continue; } - } - - if (shouldBeSkipped) { - continue; - } - - if ( edgeDy < 0 ) { - edgeLowPt = b; edgeDx = - edgeDx; - edgeHighPt = a; edgeDy = - edgeDy; - } - if (!aEq && !bEq && ( inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y ) ) { - continue; - } - - if (bEq) { - if (grads[i] * nextGrad(i) < 0) { + + if ( edgeDy < 0 ) { + edgeLowPt = b; edgeDx = - edgeDx; + edgeHighPt = a; edgeDy = - edgeDy; + } + if (!aEq && !bEq && ( uvPt.y < edgeLowPt.y || uvPt.y > edgeHighPt.y ) ) { continue; } - } else if (aEq) { - if (grads[i] * prevGrad(i) < 0) { + + if (bEq) { + if (grads[i] * nextGrad(i) < 0) { + continue; + } + } else if (aEq) { + if (grads[i] * prevGrad(i) < 0) { + continue; + } + } + + let perpEdge = edgeDx * (uvPt.y - edgeLowPt.y) - edgeDy * (uvPt.x - edgeLowPt.x); + if ( math.areEqual(perpEdge, 0, TOLERANCE) ) return new EdgeResult(edge); // uvPt is on contour ? + if ( perpEdge < 0 ) { continue; } - } + inside = ! inside; // true intersection left of uvPt + + } else { + + if (aEq && bEq) { + if (math.areEqual(edge.edge.curve.closestDistanceToPoint(pt), 0, TOLERANCE)) { + return new EdgeResult(edge); + } else { + continue; + } + } - let perpEdge = edgeDx * (inPt.y - edgeLowPt.y) - edgeDy * (inPt.x - edgeLowPt.x); - if ( math.areEqual(perpEdge, 0, TOLERANCE) ) return new EdgeResult(edge); // inPt is on contour ? - if ( perpEdge < 0 ) { - continue; + if (shouldBeSkipped) { + continue; + } + + if (bEq) { + if (grads[i] * nextGrad(i) < 0) { + continue; + } + } else if (aEq) { + if (grads[i] * prevGrad(i) < 0) { + continue; + } + } + + if (math.areEqual(edge.edge.curve.closestDistanceToPoint(pt), 0, TOLERANCE)) { + return new EdgeResult(edge); + } + + if (ray == null) { + + let rayEnd = pt.copy(); + //fixme!! + rayEnd.x = 1000000;//surface.fromUV(surface.domainU()[1]).x; + ray = edge.edge.curve.createLinearNurbs(pt, rayEnd); + } + + const hits = edge.edge.curve.intersect(ray); + + for (let hit of hits) { + //if ray just touches + const onlyTouches = math.areEqual(edge.edge.curve.tangent(hit).normalize().y, 0, TOLERANCE); + if (!onlyTouches) { + inside = ! inside; + } + } } - inside = ! inside; // true intersection left of inPt } return {inside}; diff --git a/web/app/math/vector.js b/web/app/math/vector.js index 7f3db534..b36e20d1 100644 --- a/web/app/math/vector.js +++ b/web/app/math/vector.js @@ -15,9 +15,9 @@ Vector.prototype.set = function(x, y, z) { }; Vector.prototype.set3 = function(data) { - this.x = data[0]; - this.y = data[1]; - this.z = data[2]; + this.x = data[0] || 0; + this.y = data[1] || 0; + this.z = data[2] || 0; return this; }; diff --git a/web/test/cases/brep-pip.js b/web/test/cases/brep-pip.js index 23f7211e..8b667d8c 100644 --- a/web/test/cases/brep-pip.js +++ b/web/test/cases/brep-pip.js @@ -1,5 +1,5 @@ import * as test from '../test' -import {Matrix3} from '../../app/math/l3space' +import {AXIS} from '../../app/math/l3space' export default { @@ -639,6 +639,167 @@ export default { })); }, + /** + * o--------o + * | \ + * *--> | ) + * | / + * o--------o + */ + testPIPClassification1NurbsOut: function (env) { + test.modeller(env.test((win, app) => { + const loop = createLoop(app.TPI,[ + [100, 100], + [500, 100], + [500, 500], + [100, 500] + ], [,['nurbs', [500, 100], [700, 250], [500, 500] ]]); + const result = classify(app, win, loop, [-300, 300]); + env.assertFalse(result.inside); + env.done(); + })); + }, + + /** + * o--------o + * | \ + * *--> | ) + * | / + * o--------o + */ + testPIPClassification1NurbsIn: function (env) { + test.modeller(env.test((win, app) => { + const loop = createLoop(app.TPI,[ + [100, 100], + [500, 100], + [500, 500], + [100, 500] + ], [,['nurbs', [500, 100], [700, 250], [500, 500] ]]); + const result = classify(app, win, loop, [300, 300]); + env.assertTrue(result.inside); + env.done(); + })); + }, + + + /** + * *--> + * + * . ' . + * / \ + * o o + * | | + * | | + * | | + * o--------o + */ + testPIPClassificationCloseToNurbsOut: function (env) { + test.modeller(env.test((win, app) => { + const loop = createLoop(app.TPI,[ + [100, 100], + [500, 100], + [500, 500], + [100, 500] + ], [,,['nurbs', [500, 500], [250, 750], [100, 500] ]]); + const result = classify(app, win, loop, [-300, 780]); + env.assertFalse(result.inside); + env.done(); + })); + }, + + /** + * *--> + * . ' . + * / \ + * o o + * | | + * | | + * | | + * o--------o + */ + testPIPClassificationTouchesNurbsOut: function (env) { + test.modeller(env.test((win, app) => { + const loop = createLoop(app.TPI,[ + [100, 100], + [500, 100], + [500, 500], + [100, 500] + ], [,,['nurbs', [500, 500], [250, 750], [100, 500] ]]); + const result = classify(app, win, loop, [-300, 750]); + env.assertFalse(result.inside); + env.done(); + })); + }, + + /** + * + * *--> . ' . + * / \ + * o o + * | | + * | | + * | | + * o--------o + */ + testPIPClassificationThroughNurbsOut: function (env) { + test.modeller(env.test((win, app) => { + const loop = createLoop(app.TPI,[ + [100, 100], + [500, 100], + [500, 500], + [100, 500] + ], [,,['nurbs', [500, 500], [250, 750], [100, 500] ]]); + const result = classify(app, win, loop, [-300, 650]); + env.assertFalse(result.inside); + env.done(); + })); + }, + + /** + * + * . ' . + * / *-> \ + * o o + * | | + * | | + * | | + * o--------o + */ + testPIPClassificationCrossesNurbsIn: function (env) { + test.modeller(env.test((win, app) => { + const loop = createLoop(app.TPI,[ + [100, 100], + [500, 100], + [500, 500], + [100, 500] + ], [,,['nurbs', [500, 500], [250, 750], [100, 500] ]]); + const result = classify(app, win, loop, [300, 650]); + env.assertTrue(result.inside); + env.done(); + })); + }, + + /** + * o--------o + * \ \ + * *--> ) ) + * / / + * o--------o + */ + testPIPClassification2NurbsOut: function (env) { + test.modeller(env.test((win, app) => { + const loop = createLoop(app.TPI,[ + [100, 100], + [500, 100], + [500, 500], + [100, 500] + ], [,['nurbs', [500, 100], [700, 250], [500, 500]], , ['nurbs', [100, 500], [250, 250], [100, 100]]]); + const result = classify(app, win, loop, [-300, 300]); + env.assertFalse(result.inside); + env.done(); + })); + }, + testPIPClassification_TR_OUT_TR_INNER: function (env) { test.modeller(env.test((win, app) => { const loop = createLoop(app.TPI,[ @@ -660,14 +821,13 @@ export default { } function classify(app, win, loop, p) { - const IDENTITY = new Matrix3(); loop.halfEdges.forEach(e => win.__DEBUG__.AddHalfEdge(e, 0xffff00)); const pnt = point(app.TPI, p[0], p[1], 0); const beam = pnt.copy(); beam.x += 1700; win.__DEBUG__.AddLine(pnt, beam); win.__DEBUG__.AddPoint(pnt, 0xffffff); - const result = app.TPI.brep.bool.classifyPointInsideLoop(pnt, loop, IDENTITY); + const result = app.TPI.brep.bool.classifyPointInsideLoop(pnt, loop, new app.TPI.brep.geom.Plane(AXIS.Z, 0)); win.__DEBUG__.AddPoint(pnt, result.inside ? 0x00ff00 : 0xff0000); if (result.edge) { win.__DEBUG__.AddHalfEdge(result.edge, 0xffffff) @@ -676,9 +836,16 @@ function classify(app, win, loop, p) { } -function createLoop(tpi, points) { +function createLoop(tpi, points, curves) { + curves = curves || []; const vertices = points.map(p => vertex(tpi, p[0], p[1], 0)); - return tpi.brep.builder.createPlaneLoop(vertices); + return tpi.brep.builder.createPlaneLoop(vertices, curves.map(c => { + if (c && c[0] == 'nurbs') { + const points = c.slice(1).map(p => new tpi.brep.geom.Point().set3(p) ); + return tpi.brep.geom.NurbsCurve.createByPoints(points, 2); + } + return undefined; + })); } function vertex(tpi, x, y, z) {