move face evolve to a module / fix UI

This commit is contained in:
xibyte 2017-10-04 14:25:45 -07:00 committed by Val Erastov
parent b1d37daa6f
commit 9a699cdf1d
17 changed files with 252 additions and 134 deletions

View file

@ -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();

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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();

View file

@ -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();

View file

@ -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() {

View file

@ -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];

View file

@ -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));

View file

@ -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);

View file

@ -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();

View file

@ -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) {

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}

View file

@ -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) {

View file

@ -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;