From 9a699cdf1da4a121acb36f1785db9df7b090e643 Mon Sep 17 00:00:00 2001 From: xibyte Date: Wed, 4 Oct 2017 14:25:45 -0700 Subject: [PATCH] move face evolve to a module / fix UI --- web/app/3d/craft/brep/cut-extrude.js | 54 ++++------- .../craft/brep/wizards/cut-extrude-wizard.js | 31 ++++--- .../3d/craft/brep/wizards/revolve-wizard.js | 2 +- web/app/3d/craft/sketch/sketch-model.js | 46 +++++----- web/app/3d/debug.js | 4 +- web/app/3d/modeler-app.js | 17 ++-- web/app/3d/scene/brep-scene-object.js | 4 +- web/app/3d/tess/brep-tess.js | 27 ++---- web/app/brep/brep-builder.js | 3 +- web/app/brep/brep-enclose.js | 19 ++-- web/app/brep/brep-primitives.js | 12 ++- web/app/brep/geom/impl/nurbs.js | 34 +++++-- web/app/brep/geom/impl/plane.js | 12 +-- web/app/brep/operations/boolean.js | 10 +-- web/app/brep/operations/evolve-face.js | 90 +++++++++++++++++++ web/app/brep/topo/loop.js | 17 ++++ web/app/math/vector.js | 4 + 17 files changed, 252 insertions(+), 134 deletions(-) create mode 100644 web/app/brep/operations/evolve-face.js diff --git a/web/app/3d/craft/brep/cut-extrude.js b/web/app/3d/craft/brep/cut-extrude.js index 952ad2be..0cd633a0 100644 --- a/web/app/3d/craft/brep/cut-extrude.js +++ b/web/app/3d/craft/brep/cut-extrude.js @@ -1,7 +1,7 @@ import {Matrix3, BasisForPlane, ORIGIN} from '../../../math/l3space' import * as math from '../../../math/math' import Vector from '../../../math/vector' -import {enclose, iterateSegments} from '../../../brep/brep-builder' +import {enclose, iterateSegments} from '../../../brep/brep-enclose' import * as stitching from '../../../brep/stitching' import {Loop} from '../../../brep/topo/loop' import {incRefCounter} from '../../../brep/topo/topo-object' @@ -30,21 +30,11 @@ export function doOperation(app, params, cut) { const sketch = ReadSketchFromFace(app, face); const details = getEncloseDetails(params, sketch.fetchContours(), face.surface(), !cut, false); - const operand = combineShells(details.map(d => enclose(d.basePath, d.lidPath, d.baseSurface, d.lidSurface, wallJoiner))); + const operand = combineShells(details.map(d => enclose(d.basePath, d.lidPath, d.baseSurface, d.lidSurface))); return BooleanOperation(face, solid, operand, cut ? 'subtract' : 'union'); } -export function wallJoiner(wall, group) { - if (group && group.constructor.name != 'Segment') { - const wallFace = wall.faces[0]; - if (!group.stitchedSurface) { - group.stitchedSurface = new stitching.StitchedSurface(); - } - group.stitchedSurface.addFace(wallFace); - } -} - -export function getEncloseDetails(params, contours, sketchSurface, invert, forceApproximation) { +export function getEncloseDetails(params, contours, sketchSurface, invert) { let value = params.value; if (value < 0) { value = Math.abs(value); @@ -54,7 +44,9 @@ export function getEncloseDetails(params, contours, sketchSurface, invert, force const baseSurface = invert ? sketchSurface.invert() : sketchSurface; let target; - const targetDir = baseSurface.normal.negate(); + let baseSurfaceNormal = baseSurface.normalInMiddle ? baseSurface.normalInMiddle() : baseSurface.normal; + + const targetDir = baseSurfaceNormal.negate(); if (params.rotation != 0) { const basis = sketchSurface.basis(); @@ -70,34 +62,18 @@ export function getEncloseDetails(params, contours, sketchSurface, invert, force let details = []; for (let contour of contours) { if (invert) contour.reverse(); - const basePath = contour.transferOnSurface(sketchSurface, forceApproximation); + const basePath = contour.transferOnSurface(sketchSurface); if (invert) contour.reverse(); - const lidPath = new CompositeCurve(); - - let lidPoints = basePath.points; - var applyPrism = !math.equal(params.prism, 1); - if (applyPrism) { - const _3D = sketchSurface.get3DTransformation(); - const _2D = _3D.invert(); - lidPoints = math.polygonOffset(lidPoints.map(p => _2D.apply(p)) , params.prism).map(p => _3D._apply(p)); - } - lidPoints = lidPoints.map(p => p.plus(target)); - for (let i = 0; i < basePath.points.length; ++i) { - const curve = basePath.curves[i]; - const point = lidPoints[i]; - const group = basePath.groups[i]; - let lidCurve; - if (curve.isLine) { - //TODO: breaks test_TR_OUT_TR_INNER - lidCurve = Line.fromSegment(point, lidPoints[(i + 1) % lidPoints.length]); - } else { - lidCurve = curve.translate(target); - if (applyPrism) { - lidCurve = lidCurve.offset(params.prism); - } + const lidPath = []; + var applyPrism = !math.equal(params.prism, 1); + for (let i = 0; i < basePath.length; ++i) { + const curve = basePath[i]; + let lidCurve = curve.translate(target); + if (applyPrism) { + lidCurve = lidCurve.offset(params.prism); } - lidPath.add(lidCurve, point, group); + lidPath.push(lidCurve); } const lidSurface = baseSurface.translate(target).invert(); diff --git a/web/app/3d/craft/brep/wizards/cut-extrude-wizard.js b/web/app/3d/craft/brep/wizards/cut-extrude-wizard.js index 5f26878b..b83a3fc9 100644 --- a/web/app/3d/craft/brep/wizards/cut-extrude-wizard.js +++ b/web/app/3d/craft/brep/wizards/cut-extrude-wizard.js @@ -52,23 +52,34 @@ export class ExtrudePreviewer extends SketchBasedPreviewer { } createImpl(app, params, sketch, face) { - const encloseDetails = getEncloseDetails(params, sketch, face.surface(), !this.inversed, true); + const encloseDetails = getEncloseDetails(params, sketch, face.surface(), !this.inversed); const triangles = []; - for (let d of encloseDetails) { - const base = d.basePath.points; - const lid = d.lidPath.points; - const n = base.length; - for (let p = n - 1, q = 0; q < n; p = q ++) { - triangles.push([ base[p], base[q], lid[q] ]); - triangles.push([ lid[q], lid[p], base[p] ]); + + for (let {basePath, lidPath, baseSurface, lidSurface} of encloseDetails) { + const basePoints = []; + const lidPoints = []; + for (let i = 0; i < basePath.length; ++i) { + let baseNurbs = basePath[i]; + let lidNurbs = lidPath[i]; + const params = verb.eval.Tess.rationalCurveAdaptiveSample(baseNurbs.verb._data,1,true).map(p => p[0]); + const base = params.map(u => baseNurbs.point(u)); + const lid = params.map(u => lidNurbs.point(u)); + const n = base.length; + for (let p = n - 1, q = 0; q < n; p = q ++) { + triangles.push([ base[p], base[q], lid[q] ]); + triangles.push([ lid[q], lid[p], base[p] ]); + } + base.forEach(p => basePoints.push(new Vector().set3(p))); + lid.forEach(p => lidPoints.push(new Vector().set3(p))); + } function collectOnSurface(points, normal) { TriangulatePolygons([points], normal, (v) => v.toArray(), (arr) => new Vector().set3(arr)) .forEach(tr => triangles.push(tr)); } - collectOnSurface(base, d.baseSurface.normal); - collectOnSurface(lid, d.lidSurface.normal); + collectOnSurface(basePoints, baseSurface.normal); + collectOnSurface(lidPoints, lidSurface.normal); } return triangles; } diff --git a/web/app/3d/craft/brep/wizards/revolve-wizard.js b/web/app/3d/craft/brep/wizards/revolve-wizard.js index a42a10a8..9ddf9b8d 100644 --- a/web/app/3d/craft/brep/wizards/revolve-wizard.js +++ b/web/app/3d/craft/brep/wizards/revolve-wizard.js @@ -29,7 +29,7 @@ export class RevolvePreviewer extends SketchBasedNurbsPreviewer { const nurbses = []; const contours = sketch.fetchContours(); for (let contour of contours) { - const basePath = contour.transferOnSurface(surface); + const basePath = contour.approximateOnSurface(surface); revolveToWallNurbs(basePath, surface, pivot.p0, pivot.v, params.angle).forEach(nurbs => nurbses.push(nurbs)); } return nurbses; diff --git a/web/app/3d/craft/sketch/sketch-model.js b/web/app/3d/craft/sketch/sketch-model.js index 784327a0..33178b0f 100644 --- a/web/app/3d/craft/sketch/sketch-model.js +++ b/web/app/3d/craft/sketch/sketch-model.js @@ -224,16 +224,6 @@ export class Ellipse extends SketchPrimitive { } } -const USE_APPROX_FOR = new Set(); -//USE_APPROX_FOR.add('Arc'); - -const USE_NURBS_FOR = new Set(); -USE_NURBS_FOR.add('Arc'); -USE_NURBS_FOR.add('Circle'); -//USE_NURBS_FOR.add('Ellipse'); -//USE_NURBS_FOR.add('EllipticalArc'); -//USE_NURBS_FOR.add('BezierCurve'); - export class Contour { constructor() { @@ -244,7 +234,7 @@ export class Contour { this.segments.push(obj); } - transferOnSurface(surface, forceApproximation) { + approximateOnSurface(surface) { const cc = new CompositeCurve(); const tr = to3DTrFunc(surface); @@ -265,19 +255,27 @@ export class Contour { approximation[n - 1] = firstPoint; } - if (!forceApproximation && USE_APPROX_FOR.has(segment.constructor.name)) { - cc.add(new ApproxCurve(approximation, segment), prev, segment); - prev = approximation[n - 1]; - } else if (!forceApproximation && USE_NURBS_FOR.has(segment.constructor.name)) { - cc.add(segment.toNurbs(surface), prev, segment); - prev = approximation[n - 1]; - } else { - for (let i = 1; i < n; ++i) { - const curr = approximation[i]; - cc.add(new Line.fromSegment(prev, curr), prev, segment); - prev = curr; - } - } + cc.add(segment.toNurbs(surface), prev, segment); + prev = approximation[n - 1]; + + //It might be an optimization for segments + // for (let i = 1; i < n; ++i) { + // const curr = approximation[i]; + // cc.add(new Line.fromSegment(prev, curr), prev, segment); + // prev = curr; + // } + } + return cc; + } + + transferOnSurface(surface) { + const cc = []; + + let prev = null; + let firstPoint = null; + for (let segIdx = 0; segIdx < this.segments.length; ++segIdx) { + let segment = this.segments[segIdx]; + cc.push(segment.toNurbs(surface)); } return cc; } diff --git a/web/app/3d/debug.js b/web/app/3d/debug.js index 6629654f..f30492b0 100644 --- a/web/app/3d/debug.js +++ b/web/app/3d/debug.js @@ -131,7 +131,9 @@ function addGlobalDebugActions(app) { scale = scale || 100; __DEBUG__.AddSegment(atPoint, atPoint.plus(normal.multiply(scale)), color); }, - + AddSurfaceNormal: (surface) => { + __DEBUG__.AddNormal(surface.point(0.5, 0.5), surface.normalInMiddle()); + }, HideSolids: () => { app.findAllSolidsOnScene().forEach(s => s.cadGroup.traverse(o => o.visible = false)); app.viewer.render(); diff --git a/web/app/3d/modeler-app.js b/web/app/3d/modeler-app.js index 586ffa57..03a504fc 100644 --- a/web/app/3d/modeler-app.js +++ b/web/app/3d/modeler-app.js @@ -109,6 +109,13 @@ App.prototype.test1 = function() { this.addShellOnScene(result); } + +App.prototype.cylTest = function() { + + const cylinder1 = BREPPrimitives.cylinder(200, 500); + this.addShellOnScene(cylinder1); + } + App.prototype.test2 = function() { function square() { @@ -149,10 +156,10 @@ App.prototype.test3 = function() { 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(result); + let result = app.TPI.brep.bool.subtract(box1, box2); + result = app.TPI.brep.bool.subtract(result, box3); + // app.addShellOnScene(box1); + app.addShellOnScene(result); }; @@ -207,7 +214,7 @@ App.prototype.test5 = function() { App.prototype.scratchCode = function() { const app = this; - this.test5(); + this.cylTest(); diff --git a/web/app/3d/scene/brep-scene-object.js b/web/app/3d/scene/brep-scene-object.js index 359238ac..ccba998b 100644 --- a/web/app/3d/scene/brep-scene-object.js +++ b/web/app/3d/scene/brep-scene-object.js @@ -59,7 +59,7 @@ export class BREPSceneSolid extends SceneSolid { createVertices() { } } - + class BREPSceneFace extends SceneFace { constructor(brepFace, solid) { super(solid, brepFace.id); @@ -70,7 +70,7 @@ class BREPSceneFace extends SceneFace { normal() { - return this.brepFace.surface.normal; + return this.brepFace.surface.normalInMiddle(); } depth() { diff --git a/web/app/3d/tess/brep-tess.js b/web/app/3d/tess/brep-tess.js index 5f2b715c..2d7c33e4 100644 --- a/web/app/3d/tess/brep-tess.js +++ b/web/app/3d/tess/brep-tess.js @@ -3,20 +3,6 @@ import earcut from 'earcut' import Vector from "../../math/vector"; export default function A(face) { - function uv(p) { - return face.surface.verb.closestParam(p); - } - - const workingPt = (uv, pt3d) => { - let wpt = new Vector(uv[0], uv[1], 0); - wpt._multiply(1000); - wpt.__3D = pt3d; - return wpt; - }; - - const pt = (pt3d) => workingPt(uv(pt3d), pt3d); -// throw 1 - const mirrored = isMirrored(face.surface); let loops = []; for (let loop of face.loops) { @@ -24,13 +10,12 @@ export default function A(face) { loops.push(pipLoop); for (let e of loop.halfEdges) { let curvePoints = e.edge.curve.verb.tessellate(100000); - let inverted = mirrored !== e.inverted; - if (inverted) { + if (e.inverted) { curvePoints.reverse(); } curvePoints.pop(); for (let point of curvePoints) { - let p = pt(point); + let p = face.surface.workingPoint(Vector.fromData(point)); pipLoop.push(p); } } @@ -39,7 +24,7 @@ export default function A(face) { let steinerPoints = []; let tess = face.surface.verb.tessellate({maxDepth: 3}); for (let i = 0; i < tess.points.length; i++) { - steinerPoints.push(workingPt(tess.uvs[i], tess.points[i])); + steinerPoints.push(face.surface.createWorkingPoint(tess.uvs[i], Vector.fromData(tess.points[i]))); } let [outer, ...inners] = loops; @@ -73,7 +58,7 @@ export default function A(face) { for (let i = 0; i < trs.length; i += 3) { const tr = [trs[i], trs[i + 1], trs[i + 2]]; - // __DEBUG__.AddPointPolygon(tr.map( ii => new Vector(pointsData[ii * 2], pointsData[ii * 2 + 1], 0) )); + __DEBUG__.AddPointPolygon(tr.map( ii => new Vector(pointsData[ii * 2], pointsData[ii * 2 + 1], 0) )); triangles.push(tr.map(i => points[i])); } @@ -84,7 +69,7 @@ export default function A(face) { for (let tr of triangles) { for (let i = 0; i < tr.length; i++) { - tr[i] = new Vector().set3(tr[i].__3D); + tr[i] = tr[i].__3D; } } @@ -93,7 +78,7 @@ export default function A(face) { function splitTriangles(triangles, steinerPoints) { for (let sp of steinerPoints) { - // __DEBUG__.AddPoint(sp); + __DEBUG__.AddPoint(sp); let newTrs = []; for (let i = 0; i < triangles.length; ++i) { let tr = triangles[i]; diff --git a/web/app/brep/brep-builder.js b/web/app/brep/brep-builder.js index d97fc44c..eb8e5a2b 100644 --- a/web/app/brep/brep-builder.js +++ b/web/app/brep/brep-builder.js @@ -71,7 +71,7 @@ export default class BrepBuilder { loop.link(); } if (face.surface === null) { - face.surface = createBoundingNurbs(face.outerLoop.asPolygon()); + face.surface = createBoundingNurbs(face.outerLoop.tess()); } } for (let face of this._shell.faces) { @@ -101,7 +101,6 @@ export function createBoundingNurbs(points, plane) { points2d.forEach(p => bBox.checkPoint(p)); let to3D = plane.get3DTransformation(); - let polygon = bBox.toPolygon(); polygon = polygon.map(p => to3D._apply(p)); diff --git a/web/app/brep/brep-enclose.js b/web/app/brep/brep-enclose.js index 00282517..e37b779a 100644 --- a/web/app/brep/brep-enclose.js +++ b/web/app/brep/brep-enclose.js @@ -35,29 +35,28 @@ export function createPrism(basePoints, height) { const extrudeVector = baseSurface.normal.multiply( - height); const lidSurface = baseSurface.translate(extrudeVector).invert(); const lidPoints = basePoints.map(p => p.plus(extrudeVector)); - const basePath = new CompositeCurve(); - const lidPath = new CompositeCurve(); + const basePath = []; + const lidPath = []; for (let i = 0; i < basePoints.length; i++) { let j = (i + 1) % basePoints.length; - basePath.add(NurbsCurve.createLinearNurbs(basePoints[i], basePoints[j]), basePoints[i], null); - lidPath.add(NurbsCurve.createLinearNurbs(lidPoints[i], lidPoints[j]), lidPoints[i], null); + basePath.push(NurbsCurve.createLinearNurbs(basePoints[i], basePoints[j])); + lidPath.push(NurbsCurve.createLinearNurbs(lidPoints[i], lidPoints[j])); } return enclose(basePath, lidPath, baseSurface, lidSurface); } export function enclose(basePath, lidPath, basePlane, lidPlane) { - if (basePath.points.length !== lidPath.points.length) { + if (basePath.length !== lidPath.length) { throw 'illegal arguments'; } const walls = []; - const n = basePath.points.length; + const n = basePath.length; for (let i = 0; i < n; i++) { - let j = (i + 1) % n; - const wall = createWall(basePath.curves[i], lidPath.curves[i]); + const wall = createWall(basePath[i], lidPath[i]); walls.push(wall); } return assemble(walls, basePlane, lidPlane) @@ -104,8 +103,8 @@ function assemble(walls, basePlane, lidPlane) { base.outerLoop.link(); lid.outerLoop.link(); - base.surface = createBoundingNurbs(base.outerLoop.asPolygon(), basePlane); - lid.surface = createBoundingNurbs(lid.outerLoop.asPolygon(), lidPlane); + base.surface = createBoundingNurbs(base.outerLoop.tess(), basePlane); + lid.surface = createBoundingNurbs(lid.outerLoop.tess(), lidPlane); shell.faces.push(base, lid); shell.faces.forEach(f => f.shell = shell); diff --git a/web/app/brep/brep-primitives.js b/web/app/brep/brep-primitives.js index 6c5b89c7..e9db8e3f 100644 --- a/web/app/brep/brep-primitives.js +++ b/web/app/brep/brep-primitives.js @@ -1,6 +1,8 @@ import {Point} from './geom/point' -import {createPrism} from './brep-enclose' +import {Plane} from './geom/impl/plane' +import {createPrism, enclose} from './brep-enclose' import {Matrix3} from '../math/l3space' +import {Circle} from '../3d/craft/sketch/sketch-model' export function box(w, h, d, tr) { const wh = w * 0.5; @@ -17,4 +19,12 @@ export function box(w, h, d, tr) { ], d); } + +export function cylinder(r, h, tr) { + let circle1 = new Circle(-1, new Point(0,0,0), r).toNurbs(Plane.XY); + let circle2 = circle1.translate(new Point(0,h,0)) + return enclose(circle1, circle2); +} + + const IDENTITY = new Matrix3(); \ No newline at end of file diff --git a/web/app/brep/geom/impl/nurbs.js b/web/app/brep/geom/impl/nurbs.js index c345ceb0..d7ad546d 100644 --- a/web/app/brep/geom/impl/nurbs.js +++ b/web/app/brep/geom/impl/nurbs.js @@ -36,6 +36,11 @@ export class NurbsCurve extends Curve { } splitByParam(u) { + const split = this.verb.split(u); + if (!math.equal(this.verb.closestParam(split[0].point(0)),0)) { + // throw 'wrong split'; + console.error('wrong split') + } return this.verb.split(u).map(v => new NurbsCurve(v)); } @@ -110,10 +115,11 @@ NurbsCurve.createLinearNurbs = function(a, b) { export class NurbsSurface extends Surface { - constructor(verbSurface) { + constructor(verbSurface, inverted) { super(); this.verb = verbSurface; - this.inverted = false; + this.inverted = inverted === true; + this.mirrored = NurbsSurface.isMirrored(this); } toNurbs() { @@ -147,6 +153,26 @@ export class NurbsSurface extends Surface { return pt(this.verb.point(u, v)); } + workingPoint(point) { + return this.createWorkingPoint(this.verb.closestParam(point.data()), point); + } + + createWorkingPoint(uv, pt3d) { + const wp = new Vector(uv[0], uv[1], 0)._multiply(1000); + if (this.mirrored) { + wp.x *= -1; + } + wp.__3D = pt3d; + return wp; + } + + static isMirrored(surface) { + let a = surface.point(0, 0); + let b = surface.point(1, 0); + let c = surface.point(1, 1); + return b.minus(a).cross(c.minus(a))._normalize().dot(surface.normalUV(0, 0)) < 0; + } + intersectSurfaceForSameClass(other, tol) { const curves = verb_surface_isec(this.verb, other.verb, tol); let inverted = this.inverted !== other.inverted; @@ -154,9 +180,7 @@ export class NurbsSurface extends Surface { } invert() { - let inverted = new NurbsSurface(this.verb); - inverted.inverted = !this.inverted; - return inverted; + return new NurbsSurface(this.verb, !this.inverted); } isoCurve(param, useV) { diff --git a/web/app/brep/geom/impl/plane.js b/web/app/brep/geom/impl/plane.js index adb7babb..78561958 100644 --- a/web/app/brep/geom/impl/plane.js +++ b/web/app/brep/geom/impl/plane.js @@ -87,18 +87,14 @@ export class Plane extends Surface { return [Number.MIN_VALUE, Number.MAX_VALUE]; } - classifyCognateCurve(line, tol) { - const parallel = math.areEqual(line.v.dot(this.normal), 0, tol); - const pointOnPlane = math.areEqual(this.normal.dot(line.p0), this.w, tol); - return { - hit: !parallel || pointOnPlane, - parallel - } - } } Plane.prototype.isPlane = true; +Plane.XY = new Plane(AXIS.Z, 0); +Plane.XZ = new Plane(AXIS.Y, 0); +Plane.YZ = new Plane(AXIS.X, 0); + class ParametricPlane { constructor(r0, r1, r2) { diff --git a/web/app/brep/operations/boolean.js b/web/app/brep/operations/boolean.js index 3c8de4ce..b1480d86 100644 --- a/web/app/brep/operations/boolean.js +++ b/web/app/brep/operations/boolean.js @@ -4,6 +4,7 @@ import {Loop} from '../topo/loop'; import {Face} from '../topo/face'; import {Shell} from '../topo/shell'; import {Vertex} from '../topo/vertex'; +import {evolveFace} from './evolve-face' import Vector from '../../math/vector'; import * as math from '../../math/math'; @@ -220,13 +221,12 @@ function traverseFaces(face, validFaces, callback) { } export function loopsToFaces(originFace, loops, out) { - const face = new Face(originFace.surface); - face.innerLoops = loops; - loops.forEach(loop => loop.face = face); - out.push(face); + const newFaces = evolveFace(originFace, loops); + for (let newFace of newFaces) { + out.push(newFace); + } } - function initSolveData(shell, facesData) { for (let face of shell.faces) { const solveData = new FaceSolveData(face); diff --git a/web/app/brep/operations/evolve-face.js b/web/app/brep/operations/evolve-face.js new file mode 100644 index 00000000..167eef02 --- /dev/null +++ b/web/app/brep/operations/evolve-face.js @@ -0,0 +1,90 @@ +import {Face} from '../topo/face'; +import {Vertex} from '../topo/vertex'; +import Vector from '../../math/vector'; +import {isCCW} from '../../math/math'; +import PIP from '../../3d/tess/pip'; + +export function evolveFace(originFace, loops) { + let out = []; + const originSurface = originFace.surface; + let invertedSurface = null; + function invertSurface() { + if (invertedSurface == null) { + invertedSurface = originSurface.invert(); + } + return invertedSurface; + } + + function createFaces(nestedLoop, level) { + let surface; + __DEBUG__.AddPointPolygon(nestedLoop.workingPolygon) + if (nestedLoop.inverted) { + surface = invertSurface(surface); + } else { + surface = originSurface; + } + + const loop = nestedLoop.loop; + const newFace = new Face(surface); + Object.assign(newFace.data, originFace.data); + newFace.outerLoop = loop; + loop.face = newFace; + out.push(newFace); + + for (let child of nestedLoop.nesting) { + if (child.level == level + 2) { + createFaces(child, level + 2); + } else if (child.level == level + 1) { + if (nestedLoop.inverted !== child.inverted) { + child.loop.face = newFace; + newFace.innerLoops.push(child.loop); + } else { + createFaces(child, level + 1); + } + } + } + } + const nestedLoops = getNestedLoops(originFace, loops); + for (let nestedLoop of nestedLoops) { + if (nestedLoop.level == 0) { + createFaces(nestedLoop, 0); + } + } + if (out.length !== 0) { + out[0].id = originFace.id; + } + return out; +} + +function getNestedLoops(face, brepLoops) { + function NestedLoop(loop) { + this.loop = loop; + this.workingPolygon = loop.asPolygon().map(p => face.surface.workingPoint(p)); + this.inverted = !isCCW(this.workingPolygon); + this.pip = PIP(this.workingPolygon); + this.nesting = []; + this.level = 0; + } + + const loops = brepLoops.map(loop => new NestedLoop(loop)); + function contains(loop, other) { + for (let point of other.workingPolygon) { + if (!loop.pip(point).inside) { + return false; + } + } + return true; + } + for (let i = 0; i < loops.length; ++i) { + const loop = loops[i]; + for (let j = 0; j < loops.length; ++j) { + if (i == j) continue; + const other = loops[j]; + if (contains(loop, other)) { + loop.nesting.push(other); + other.level ++; + } + } + } + return loops.filter(l => l.level == 0); +} diff --git a/web/app/brep/topo/loop.js b/web/app/brep/topo/loop.js index c28dd7b3..e04b6558 100644 --- a/web/app/brep/topo/loop.js +++ b/web/app/brep/topo/loop.js @@ -1,4 +1,5 @@ import {TopoObject} from './topo-object' +import {Point} from '../geom/point' import * as math from '../../math/math' @@ -30,6 +31,22 @@ export class Loop extends TopoObject { curr.loop = this; } } + + tess() { + let out = []; + for (let e of this.halfEdges) { + let curvePoints = e.edge.curve.verb.tessellate(100000); + if (e.inverted) { + curvePoints.reverse(); + } + curvePoints.pop(); + for (let point of curvePoints) { + let p = Point.fromData(point); + out.push(p); + } + } + return out; + } } Loop.isPolygonCCWOnSurface = function(polygon, surface) { diff --git a/web/app/math/vector.js b/web/app/math/vector.js index b36e20d1..6940ffbb 100644 --- a/web/app/math/vector.js +++ b/web/app/math/vector.js @@ -136,4 +136,8 @@ Vector.prototype.three = function() { return new THREE.Vector3(this.x, this.y, this.z); }; +Vector.fromData = function(arr) { + return new Vector().set3(arr); +} + export default Vector; \ No newline at end of file