mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
abstract and refactor curve geometry
This commit is contained in:
parent
7eee7074ba
commit
ccda6f20f0
16 changed files with 385 additions and 317 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import {NurbsSurface, NurbsCurve} from './geom/impl/nurbs';
|
import {NurbsSurface} from './geom/impl/nurbs';
|
||||||
|
import BrepCurve from './geom/curves/brepCurve';
|
||||||
import {Plane} from './geom/impl/plane';
|
import {Plane} from './geom/impl/plane';
|
||||||
import {Point} from './geom/point';
|
import {Point} from './geom/point';
|
||||||
import {Shell} from './topo/shell';
|
import {Shell} from './topo/shell';
|
||||||
|
|
@ -53,7 +54,7 @@ export default class BrepBuilder {
|
||||||
let he = a.edgeFor(b);
|
let he = a.edgeFor(b);
|
||||||
if (he === null) {
|
if (he === null) {
|
||||||
if (!curve) {
|
if (!curve) {
|
||||||
curve = NurbsCurve.createLinearNurbs(a.point, b.point);
|
curve = BrepCurve.createLinearNurbs(a.point, b.point);
|
||||||
}
|
}
|
||||||
const e = new Edge(curve, a, b);
|
const e = new Edge(curve, a, b);
|
||||||
he = e.halfEdge1;
|
he = e.halfEdge1;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import {Loop} from './topo/loop'
|
||||||
import {Face} from './topo/face'
|
import {Face} from './topo/face'
|
||||||
import {HalfEdge, Edge} from './topo/edge'
|
import {HalfEdge, Edge} from './topo/edge'
|
||||||
import {Line} from './geom/impl/line'
|
import {Line} from './geom/impl/line'
|
||||||
import {NurbsSurface, NurbsCurve} from './geom/impl/nurbs'
|
import {NurbsSurface} from './geom/impl/nurbs'
|
||||||
|
import BrepCurve from './geom/curves/brepCurve';
|
||||||
import {Plane} from './geom/impl/plane'
|
import {Plane} from './geom/impl/plane'
|
||||||
import {Point} from './geom/point'
|
import {Point} from './geom/point'
|
||||||
import {BasisForPlane, Matrix3} from '../math/l3space'
|
import {BasisForPlane, Matrix3} from '../math/l3space'
|
||||||
|
|
@ -39,8 +40,8 @@ export function createPrism(basePoints, height) {
|
||||||
|
|
||||||
for (let i = 0; i < basePoints.length; i++) {
|
for (let i = 0; i < basePoints.length; i++) {
|
||||||
let j = (i + 1) % basePoints.length;
|
let j = (i + 1) % basePoints.length;
|
||||||
basePath.push(NurbsCurve.createLinearNurbs(basePoints[i], basePoints[j]));
|
basePath.push(BrepCurve.createLinearNurbs(basePoints[i], basePoints[j]));
|
||||||
lidPath.push(NurbsCurve.createLinearNurbs(lidPoints[i], lidPoints[j]));
|
lidPath.push(BrepCurve.createLinearNurbs(lidPoints[i], lidPoints[j]));
|
||||||
}
|
}
|
||||||
return enclose(basePath, lidPath, baseSurface, lidSurface);
|
return enclose(basePath, lidPath, baseSurface, lidSurface);
|
||||||
}
|
}
|
||||||
|
|
@ -120,13 +121,7 @@ function bothClassOf(o1, o2, className) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createWall(curve1, curve2) {
|
export function createWall(curve1, curve2) {
|
||||||
if (bothClassOf(curve1, curve2, 'Line')) {
|
return NurbsSurface.loft(curve2, curve1, 1);
|
||||||
throw 'unsupported'
|
|
||||||
} else if (bothClassOf(curve1, curve2, 'NurbsCurve')) {
|
|
||||||
return NurbsSurface.loft(curve2, curve1, 1);
|
|
||||||
} else {
|
|
||||||
throw 'unsupported';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
|
|
||||||
export class CompositeCurve {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.curves = [];
|
|
||||||
this.points = [];
|
|
||||||
this.groups = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
add(curve, point, group) {
|
|
||||||
this.curves.push(curve);
|
|
||||||
this.points.push(point);
|
|
||||||
this.groups.push(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0
web/app/brep/geom/curves/basicCurve.js
Normal file
0
web/app/brep/geom/curves/basicCurve.js
Normal file
189
web/app/brep/geom/curves/brepCurve.js
Normal file
189
web/app/brep/geom/curves/brepCurve.js
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
import NurbsCurve from "./nurbsCurve";
|
||||||
|
import {Matrix3} from '../../../math/l3space'
|
||||||
|
import * as math from '../../../math/math'
|
||||||
|
import {areEqual} from '../../../math/math'
|
||||||
|
|
||||||
|
import {eqSqTol, ueq, veq, veq3, veqNeg} from "../tolerance";
|
||||||
|
import curveIntersect from "../impl/curve/curves-isec";
|
||||||
|
import curveTess from "../impl/curve/curve-tess";
|
||||||
|
import Point from 'math/vector';
|
||||||
|
import cache from "../impl/cache";
|
||||||
|
|
||||||
|
export default class BrepCurve {
|
||||||
|
|
||||||
|
constructor(_impl, uMin, uMax) {
|
||||||
|
let [iMin, iMax] = _impl.domain();
|
||||||
|
if (iMin !== 0 || iMax !== 1) {
|
||||||
|
throw 'only normalized(0..1) parametrization is supported';
|
||||||
|
}
|
||||||
|
this.impl = _impl;
|
||||||
|
// if (uMin === undefined || uMax === undefined) {
|
||||||
|
// [uMin, uMax] = this.impl.domain();
|
||||||
|
// }
|
||||||
|
// this.uMin = uMin;
|
||||||
|
// this.uMax = uMax;
|
||||||
|
this.uMin = 0;
|
||||||
|
this.uMax = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(vector) {
|
||||||
|
const tr = new Matrix3().translate(vector.x, vector.y, vector.z);
|
||||||
|
return new BrepCurve(this.impl.transform(tr.toArray()), this.uMin, this.uMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
tangentAtPoint(point) {
|
||||||
|
let u = this.impl.param(point.data());
|
||||||
|
if (areEqual(u, this.uMax, 1e-3)) { // we don't need much tolerance here
|
||||||
|
//TODO:
|
||||||
|
// let cps = this.impl.data.controlPoints;
|
||||||
|
// return pt(cps[cps.length - 1])._minus(pt(cps[cps.length - 2]))._normalize();
|
||||||
|
u -= 1e-3;
|
||||||
|
}
|
||||||
|
return this.tangentAtParam(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
tangentAtParam(u) {
|
||||||
|
const dr = this.impl.eval(u, 1);
|
||||||
|
return pt(dr[1])._normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
param(point) {
|
||||||
|
return this.impl.param(point.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
split(point) {
|
||||||
|
return this.splitByParam(this.param(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
splitByParam(u) {
|
||||||
|
if (ueq(this.uMin) || ueq(this.uMax) || u < this.uMin || u > this.uMax) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let split = this.impl.split(u);
|
||||||
|
|
||||||
|
const splitCheck = (split) => {
|
||||||
|
return (
|
||||||
|
math.equal(this.impl.param(split[0].point(1)), this.impl.param(split[1].point(0))) &&
|
||||||
|
math.equal(this.impl.param(split[0].point(0)), 0) &&
|
||||||
|
math.equal(this.impl.param(split[0].point(1)), u) &&
|
||||||
|
math.equal(this.impl.param(split[1].point(0)), u) &&
|
||||||
|
math.equal(this.impl.param(split[1].point(1)), 1)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if (!splitCheck(split)) {
|
||||||
|
throw 'wrong split';
|
||||||
|
}
|
||||||
|
return split.map(v => new BrepCurve(v));
|
||||||
|
|
||||||
|
// return [
|
||||||
|
// new BrepCurve(this.impl, this.uMin, u),
|
||||||
|
// new BrepCurve(this.impl, u, this.uMax)
|
||||||
|
// ];
|
||||||
|
}
|
||||||
|
|
||||||
|
point(u) {
|
||||||
|
return pt(this.impl.point(u));
|
||||||
|
}
|
||||||
|
|
||||||
|
tessellate(tessTol, scale) {
|
||||||
|
return CURVE_CACHING_TESSELLATOR(this.impl, this.uMin, this.uMax, tessTol, scale).map(p => pt(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
boundary() {
|
||||||
|
return [this.uMin, this.uMax];
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectCurve(other) {
|
||||||
|
let isecs = [];
|
||||||
|
|
||||||
|
const eq = veq3;
|
||||||
|
|
||||||
|
function add(i0) {
|
||||||
|
for (let i1 of isecs) {
|
||||||
|
if (eq(i0.p0, i1.p0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isecs.push(i0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isecOn(c0, c1, u0) {
|
||||||
|
const p0 = c0.impl.point(u0);
|
||||||
|
const u1 = c1.impl.param(p0);
|
||||||
|
if (!c1.isInside(u1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const p1 = c1.impl.point(u1);
|
||||||
|
if (eq(p0, p1)) {
|
||||||
|
if (c0 === other) {
|
||||||
|
add({u0: u1, u1: u0, p0: p1, p1: p0});
|
||||||
|
} else {
|
||||||
|
add({u0, u1, p0, p1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isecOn(this, other, this.uMin);
|
||||||
|
isecOn(this, other, this.uMax);
|
||||||
|
isecOn(other, this, other.uMin);
|
||||||
|
isecOn(other, this, other.uMax);
|
||||||
|
|
||||||
|
curveIntersect(
|
||||||
|
this.impl, other.impl,
|
||||||
|
this.boundary(), other.boundary(),
|
||||||
|
CURVE_CACHING_TESSELLATOR, CURVE_CACHING_TESSELLATOR
|
||||||
|
).forEach(i => add(i));
|
||||||
|
|
||||||
|
isecs.forEach(i => {
|
||||||
|
i.p0 = pt(i.p0);
|
||||||
|
i.p1 = pt(i.p1);
|
||||||
|
});
|
||||||
|
isecs = isecs.filter(({u0, u1}) => {
|
||||||
|
let t0 = this.tangentAtParam(u0);
|
||||||
|
let t1 = other.tangentAtParam(u1);
|
||||||
|
return !veq(t0, t1) && !veqNeg(t0, t1);
|
||||||
|
});
|
||||||
|
return isecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInside(u) {
|
||||||
|
return u >= this.uMin && u <= this.uMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
invert() {
|
||||||
|
return new BrepCurve(this.impl.invert());
|
||||||
|
}
|
||||||
|
|
||||||
|
middlePoint() {
|
||||||
|
if (!this.__middlePoint) {
|
||||||
|
this.__middlePoint = this.point(0.5);
|
||||||
|
}
|
||||||
|
return this.__middlePoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
passesThrough(point) {
|
||||||
|
return eqSqTol(0, point.distanceToSquared(this.point(this.param(point))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pt(data) {
|
||||||
|
return new Point().set3(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CURVE_CACHING_TESSELLATOR = function(curve, min, max, tessTol, scale) {
|
||||||
|
return cache('tess', [min, max, tessTol, scale], curve, () => degree1OptTessellator(curve, min, max, tessTol, scale));
|
||||||
|
};
|
||||||
|
|
||||||
|
function degree1OptTessellator(curve, min, max, tessTol, scale) {
|
||||||
|
if (curve.degree() === 1) {
|
||||||
|
return curve.degree1Tess().map(u => curve.point(u));
|
||||||
|
}
|
||||||
|
return curveTess(curve, min, max, tessTol, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
BrepCurve.createLinearNurbs = function(a, b) {
|
||||||
|
let line = verb.geom.NurbsCurve.byKnotsControlPointsWeights( 1, [0,0,1,1], [a.data(), b.data()]);
|
||||||
|
return new BrepCurve(new NurbsCurve(line));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
72
web/app/brep/geom/curves/nurbsCurve.js
Normal file
72
web/app/brep/geom/curves/nurbsCurve.js
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import * as ext from '../impl/nurbs-ext';
|
||||||
|
import {newVerbCurve} from "../impl/nurbs";
|
||||||
|
|
||||||
|
|
||||||
|
export default class NurbsCurve {
|
||||||
|
|
||||||
|
constructor(verbCurve) {
|
||||||
|
this.verb = verbCurve;
|
||||||
|
this.data = verbCurve.asNurbs();
|
||||||
|
}
|
||||||
|
|
||||||
|
domain() {
|
||||||
|
return ext.curveDomain(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
degree1Tess() {
|
||||||
|
return ext.distinctKnots(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
degree() {
|
||||||
|
return this.data.degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(tr) {
|
||||||
|
return new NurbsCurve(this.verb.transform(tr));
|
||||||
|
}
|
||||||
|
|
||||||
|
point(u) {
|
||||||
|
return this.verb.point(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
param(point) {
|
||||||
|
return this.verb.closestParam(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
eval(u, num) {
|
||||||
|
return verb.eval.Eval.rationalCurveDerivatives( this.data, u, num );
|
||||||
|
}
|
||||||
|
|
||||||
|
optimalSplits() {
|
||||||
|
return this.data.knots.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
invert() {
|
||||||
|
|
||||||
|
let inverted = ext.curveInvert(this.data);
|
||||||
|
ext.normalizeCurveParametrizationIfNeeded(inverted);
|
||||||
|
// let [min, max] = curveDomain(curve);
|
||||||
|
// for (let i = 0; i < reversed.knots.length; i++) {
|
||||||
|
// if (eqEps(reversed.knots[i], max)) {
|
||||||
|
// reversed.knots[i] = max;
|
||||||
|
// } else {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// for (let i = reversed.knots.length - 1; i >= 0 ; i--) {
|
||||||
|
// if (eqEps(reversed.knots[i], min)) {
|
||||||
|
// reversed.knots[i] = min;
|
||||||
|
// } else {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return new NurbsCurve(newVerbCurve(inverted));
|
||||||
|
}
|
||||||
|
|
||||||
|
split(u) {
|
||||||
|
let split = verb.eval.Divide.curveSplit(this.data, u);
|
||||||
|
split.forEach(n => ext.normalizeCurveParametrization(n));
|
||||||
|
return split.map(c => new NurbsCurve(newVerbCurve(c)));
|
||||||
|
}
|
||||||
|
}
|
||||||
24
web/app/brep/geom/curves/parametricCurve.js
Normal file
24
web/app/brep/geom/curves/parametricCurve.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
export default class ParametricCurve {
|
||||||
|
|
||||||
|
domain() { }
|
||||||
|
|
||||||
|
degree() { }
|
||||||
|
|
||||||
|
degree1Tess() {}
|
||||||
|
|
||||||
|
eval(u, num) { }
|
||||||
|
|
||||||
|
point(param) { }
|
||||||
|
|
||||||
|
param(point) { }
|
||||||
|
|
||||||
|
transform(tr) { }
|
||||||
|
|
||||||
|
optimalSplits() { }
|
||||||
|
|
||||||
|
normalizeParametrization() { }
|
||||||
|
|
||||||
|
invert() { }
|
||||||
|
}
|
||||||
|
|
||||||
7
web/app/brep/geom/impl/cache.js
Normal file
7
web/app/brep/geom/impl/cache.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default function cache(id, keys, obj, op) {
|
||||||
|
id = '__cache__:' + id + ':' + keys.join('/');
|
||||||
|
if (!obj[id]) {
|
||||||
|
obj[id] = op();
|
||||||
|
}
|
||||||
|
return obj[id];
|
||||||
|
}
|
||||||
69
web/app/brep/geom/impl/intersectionCurve.js
Normal file
69
web/app/brep/geom/impl/intersectionCurve.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import {TOLERANCE} from '../tolerance';
|
||||||
|
import {curveClosestParam, curveDomain, curvePoint, surfaceClosestParam} from './nurbs-ext';
|
||||||
|
import * as vec from "../../../math/vec";
|
||||||
|
|
||||||
|
export class IntersectionCurve {
|
||||||
|
|
||||||
|
constructor(approxPolyline, surfaceA, surfaceB) {
|
||||||
|
this.surfaceA = surfaceA;
|
||||||
|
this.surfaceB = surfaceB;
|
||||||
|
this.approxPolyline = approxPolyline;
|
||||||
|
this.exactify = (pt) => {
|
||||||
|
let uvA = surfaceClosestParam(surfaceA, pt);
|
||||||
|
let uvB = surfaceClosestParam(surfaceB, pt);
|
||||||
|
return verb.eval.Intersect.surfacesAtPointWithEstimate(surfaceA,surfaceB,uvA,uvB,TOLERANCE).point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
domain() {
|
||||||
|
return curveDomain(this.approxPolyline);
|
||||||
|
}
|
||||||
|
|
||||||
|
eval(u, num) {
|
||||||
|
let pt = this.point(u);
|
||||||
|
|
||||||
|
let [uA, vA] = surfaceClosestParam(this.surfaceA, pt);
|
||||||
|
let [uB, vB] = surfaceClosestParam(this.surfaceB, pt);
|
||||||
|
|
||||||
|
let dA = verb.eval.Eval.rationalSurfaceDerivatives(this.surfaceA, uA, vA, num);
|
||||||
|
let dB = verb.eval.Eval.rationalSurfaceDerivatives(this.surfaceB, uB, vB, num);
|
||||||
|
|
||||||
|
let out = [];
|
||||||
|
for (let i = 0; i < num + 1; ++i) {
|
||||||
|
if (i === 0) {
|
||||||
|
out.push(pt);
|
||||||
|
} else {
|
||||||
|
let nA = vec.cross(dA[i][0], dA[0][i]);
|
||||||
|
let nB = vec.cross(dB[i][0], dB[0][i]);
|
||||||
|
out.push(vec.cross(nA, nB));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
point(u) {
|
||||||
|
let pt = curvePoint(this.approxPolyline, u);
|
||||||
|
return this.exactify(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
param(point) {
|
||||||
|
let pointOnCurve = this.exactify(point);
|
||||||
|
return curveClosestParam(this.approxPolyline, pointOnCurve);
|
||||||
|
}
|
||||||
|
|
||||||
|
optimalSplits() {
|
||||||
|
return this.approxPolyline.knots.length - 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
degree() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(tr) {
|
||||||
|
throw 'unsupported;'
|
||||||
|
}
|
||||||
|
|
||||||
|
invert() {
|
||||||
|
throw 'unsupported;'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,272 +9,9 @@ import curveIntersect from "./curve/curves-isec";
|
||||||
import curveTess from "./curve/curve-tess";
|
import curveTess from "./curve/curve-tess";
|
||||||
import {areEqual} from "../../../math/math";
|
import {areEqual} from "../../../math/math";
|
||||||
import {Plane} from "./plane";
|
import {Plane} from "./plane";
|
||||||
|
import BrepCurve from "../curves/brepCurve";
|
||||||
|
import NurbsCurve from "../curves/nurbsCurve";
|
||||||
class ParametricCurve {
|
import cache from "./cache";
|
||||||
|
|
||||||
domain() { }
|
|
||||||
|
|
||||||
degree() { }
|
|
||||||
|
|
||||||
degree1Tess() {}
|
|
||||||
|
|
||||||
eval(u, num) { }
|
|
||||||
|
|
||||||
point(param) { }
|
|
||||||
|
|
||||||
param(point) { }
|
|
||||||
|
|
||||||
transform(tr) { }
|
|
||||||
|
|
||||||
optimalSplits() { }
|
|
||||||
|
|
||||||
normalizeParametrization() { }
|
|
||||||
|
|
||||||
invert() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NurbsCurveImpl { //TODO: rename to NurbsCurve implements ParametricCurve
|
|
||||||
|
|
||||||
constructor(verbCurve) {
|
|
||||||
this.verb = verbCurve;
|
|
||||||
this.data = verbCurve.asNurbs();
|
|
||||||
}
|
|
||||||
|
|
||||||
domain() {
|
|
||||||
return ext.curveDomain(this.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
degree1Tess() {
|
|
||||||
return ext.distinctKnots(this.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
degree() {
|
|
||||||
return this.data.degree;
|
|
||||||
}
|
|
||||||
|
|
||||||
transform(tr) {
|
|
||||||
return new NurbsCurveImpl(this.verb.transform(tr));
|
|
||||||
}
|
|
||||||
|
|
||||||
point(u) {
|
|
||||||
return this.verb.point(u);
|
|
||||||
}
|
|
||||||
|
|
||||||
param(point) {
|
|
||||||
return this.verb.closestParam(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(u, num) {
|
|
||||||
return verb.eval.Eval.rationalCurveDerivatives( this.data, u, num );
|
|
||||||
}
|
|
||||||
|
|
||||||
optimalSplits() {
|
|
||||||
return this.data.knots.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
invert() {
|
|
||||||
|
|
||||||
let inverted = ext.curveInvert(this.data);
|
|
||||||
ext.normalizeCurveParametrizationIfNeeded(inverted);
|
|
||||||
// let [min, max] = curveDomain(curve);
|
|
||||||
// for (let i = 0; i < reversed.knots.length; i++) {
|
|
||||||
// if (eqEps(reversed.knots[i], max)) {
|
|
||||||
// reversed.knots[i] = max;
|
|
||||||
// } else {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for (let i = reversed.knots.length - 1; i >= 0 ; i--) {
|
|
||||||
// if (eqEps(reversed.knots[i], min)) {
|
|
||||||
// reversed.knots[i] = min;
|
|
||||||
// } else {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return new NurbsCurveImpl(newVerbCurve(inverted));
|
|
||||||
}
|
|
||||||
|
|
||||||
split(u) {
|
|
||||||
let split = verb.eval.Divide.curveSplit(this.data, u);
|
|
||||||
split.forEach(n => ext.normalizeCurveParametrization(n));
|
|
||||||
return split.map(c => new NurbsCurveImpl(newVerbCurve(c)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NurbsCurve { //TODO: rename to BrepCurve
|
|
||||||
|
|
||||||
constructor(_impl, uMin, uMax) {
|
|
||||||
let [iMin, iMax] = _impl.domain();
|
|
||||||
if (iMin !== 0 || iMax !== 1) {
|
|
||||||
throw 'only normalized(0..1) parametrization is supported';
|
|
||||||
}
|
|
||||||
this.impl = _impl;
|
|
||||||
// if (uMin === undefined || uMax === undefined) {
|
|
||||||
// [uMin, uMax] = this.impl.domain();
|
|
||||||
// }
|
|
||||||
// this.uMin = uMin;
|
|
||||||
// this.uMax = uMax;
|
|
||||||
this.uMin = 0;
|
|
||||||
this.uMax = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
translate(vector) {
|
|
||||||
const tr = new Matrix3().translate(vector.x, vector.y, vector.z);
|
|
||||||
return new NurbsCurve(this.impl.transform(tr.toArray()), this.uMin, this.uMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
tangentAtPoint(point) {
|
|
||||||
let u = this.impl.param(point.data());
|
|
||||||
if (areEqual(u, this.uMax, 1e-3)) { // we don't need much tolerance here
|
|
||||||
//TODO:
|
|
||||||
// let cps = this.impl.data.controlPoints;
|
|
||||||
// return pt(cps[cps.length - 1])._minus(pt(cps[cps.length - 2]))._normalize();
|
|
||||||
u -= 1e-3;
|
|
||||||
}
|
|
||||||
return this.tangentAtParam(u);
|
|
||||||
}
|
|
||||||
|
|
||||||
tangentAtParam(u) {
|
|
||||||
const dr = this.impl.eval(u, 1);
|
|
||||||
return pt(dr[1])._normalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
param(point) {
|
|
||||||
return this.impl.param(point.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
split(point) {
|
|
||||||
return this.splitByParam(this.param(point));
|
|
||||||
}
|
|
||||||
|
|
||||||
splitByParam(u) {
|
|
||||||
if (ueq(this.uMin) || ueq(this.uMax) || u < this.uMin || u > this.uMax) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
let split = this.impl.split(u);
|
|
||||||
|
|
||||||
const splitCheck = (split) => {
|
|
||||||
return (
|
|
||||||
math.equal(this.impl.param(split[0].point(1)), this.impl.param(split[1].point(0))) &&
|
|
||||||
math.equal(this.impl.param(split[0].point(0)), 0) &&
|
|
||||||
math.equal(this.impl.param(split[0].point(1)), u) &&
|
|
||||||
math.equal(this.impl.param(split[1].point(0)), u) &&
|
|
||||||
math.equal(this.impl.param(split[1].point(1)), 1)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if (!splitCheck(split)) {
|
|
||||||
throw 'wrong split';
|
|
||||||
}
|
|
||||||
return split.map(v => new NurbsCurve(v));
|
|
||||||
|
|
||||||
// return [
|
|
||||||
// new NurbsCurve(this.impl, this.uMin, u),
|
|
||||||
// new NurbsCurve(this.impl, u, this.uMax)
|
|
||||||
// ];
|
|
||||||
}
|
|
||||||
|
|
||||||
point(u) {
|
|
||||||
return pt(this.impl.point(u));
|
|
||||||
}
|
|
||||||
|
|
||||||
tessellate(tessTol, scale) {
|
|
||||||
return CURVE_CACHING_TESSELLATOR(this.impl, this.uMin, this.uMax, tessTol, scale).map(p => pt(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
boundary() {
|
|
||||||
return [this.uMin, this.uMax];
|
|
||||||
}
|
|
||||||
|
|
||||||
intersectCurve(other) {
|
|
||||||
let isecs = [];
|
|
||||||
|
|
||||||
const eq = veq3;
|
|
||||||
|
|
||||||
function add(i0) {
|
|
||||||
for (let i1 of isecs) {
|
|
||||||
if (eq(i0.p0, i1.p0)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isecs.push(i0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isecOn(c0, c1, u0) {
|
|
||||||
const p0 = c0.impl.point(u0);
|
|
||||||
const u1 = c1.impl.param(p0);
|
|
||||||
if (!c1.isInside(u1)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const p1 = c1.impl.point(u1);
|
|
||||||
if (eq(p0, p1)) {
|
|
||||||
if (c0 === other) {
|
|
||||||
add({u0: u1, u1: u0, p0: p1, p1: p0});
|
|
||||||
} else {
|
|
||||||
add({u0, u1, p0, p1});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isecOn(this, other, this.uMin);
|
|
||||||
isecOn(this, other, this.uMax);
|
|
||||||
isecOn(other, this, other.uMin);
|
|
||||||
isecOn(other, this, other.uMax);
|
|
||||||
|
|
||||||
curveIntersect(
|
|
||||||
this.impl, other.impl,
|
|
||||||
this.boundary(), other.boundary(),
|
|
||||||
CURVE_CACHING_TESSELLATOR, CURVE_CACHING_TESSELLATOR
|
|
||||||
).forEach(i => add(i));
|
|
||||||
|
|
||||||
isecs.forEach(i => {
|
|
||||||
i.p0 = pt(i.p0);
|
|
||||||
i.p1 = pt(i.p1);
|
|
||||||
});
|
|
||||||
isecs = isecs.filter(({u0, u1}) => {
|
|
||||||
let t0 = this.tangentAtParam(u0);
|
|
||||||
let t1 = other.tangentAtParam(u1);
|
|
||||||
return !veq(t0, t1) && !veqNeg(t0, t1);
|
|
||||||
});
|
|
||||||
return isecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
isInside(u) {
|
|
||||||
return u >= this.uMin && u <= this.uMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
invert() {
|
|
||||||
return new NurbsCurve(this.impl.invert());
|
|
||||||
}
|
|
||||||
|
|
||||||
middlePoint() {
|
|
||||||
if (!this.__middlePoint) {
|
|
||||||
this.__middlePoint = this.point(0.5);
|
|
||||||
}
|
|
||||||
return this.__middlePoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
passesThrough(point) {
|
|
||||||
return eqSqTol(0, point.distanceToSquared(this.point(this.param(point))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CURVE_CACHING_TESSELLATOR = function(curve, min, max, tessTol, scale) {
|
|
||||||
return cache('tess', [min, max, tessTol, scale], curve, () => degree1OptTessellator(curve, min, max, tessTol, scale));
|
|
||||||
};
|
|
||||||
|
|
||||||
function degree1OptTessellator(curve, min, max, tessTol, scale) {
|
|
||||||
if (curve.degree() === 1) {
|
|
||||||
return curve.degree1Tess().map(u => curve.point(u));
|
|
||||||
}
|
|
||||||
return curveTess(curve, min, max, tessTol, scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
NurbsCurve.createLinearNurbs = function(a, b) {
|
|
||||||
let line = verb.geom.NurbsCurve.byKnotsControlPointsWeights( 1, [0,0,1,1], [a.data(), b.data()]);
|
|
||||||
return new NurbsCurve(new NurbsCurveImpl(line));
|
|
||||||
};
|
|
||||||
|
|
||||||
export class NurbsSurface extends Surface {
|
export class NurbsSurface extends Surface {
|
||||||
|
|
||||||
|
|
@ -394,7 +131,7 @@ export class NurbsSurface extends Surface {
|
||||||
curves = curves.map(curve => ext.curveInvert(curve));
|
curves = curves.map(curve => ext.curveInvert(curve));
|
||||||
}
|
}
|
||||||
curves.forEach(curve => ext.normalizeCurveParametrizationIfNeeded(curve))
|
curves.forEach(curve => ext.normalizeCurveParametrizationIfNeeded(curve))
|
||||||
return curves.map(curve => new NurbsCurve(new NurbsCurveImpl(newVerbCurve(curve))));
|
return curves.map(curve => new BrepCurve(new NurbsCurve(newVerbCurve(curve))));
|
||||||
}
|
}
|
||||||
|
|
||||||
invert() {
|
invert() {
|
||||||
|
|
@ -404,7 +141,7 @@ export class NurbsSurface extends Surface {
|
||||||
isoCurve(param, useV) {
|
isoCurve(param, useV) {
|
||||||
const data = verb.eval.Make.surfaceIsocurve(this.verb._data, param, useV);
|
const data = verb.eval.Make.surfaceIsocurve(this.verb._data, param, useV);
|
||||||
const isoCurve = newVerbCurve(data);
|
const isoCurve = newVerbCurve(data);
|
||||||
return new NurbsCurve(new NurbsCurveImpl(isoCurve));
|
return new BrepCurve(new NurbsCurve(isoCurve));
|
||||||
}
|
}
|
||||||
|
|
||||||
isoCurveAlignU(param) {
|
isoCurveAlignU(param) {
|
||||||
|
|
@ -436,7 +173,7 @@ NurbsSurface.loft = function(curve1, curve2) {
|
||||||
return new NurbsSurface(verb.geom.NurbsSurface.byLoftingCurves([curve1.impl.verb, curve2.impl.verb], 1));
|
return new NurbsSurface(verb.geom.NurbsSurface.byLoftingCurves([curve1.impl.verb, curve2.impl.verb], 1));
|
||||||
};
|
};
|
||||||
|
|
||||||
function newVerbCurve(data) {
|
export function newVerbCurve(data) {
|
||||||
return new verb.geom.NurbsCurve(data);
|
return new verb.geom.NurbsCurve(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,14 +181,6 @@ function pt(data) {
|
||||||
return new Point().set3(data);
|
return new Point().set3(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cache(id, keys, obj, op) {
|
|
||||||
id = '__cache__:' + id + ':' + keys.join('/');
|
|
||||||
if (!obj[id]) {
|
|
||||||
obj[id] = op();
|
|
||||||
}
|
|
||||||
return obj[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
const surTess = verb.eval.Tess.rationalSurfaceAdaptive;
|
const surTess = verb.eval.Tess.rationalSurfaceAdaptive;
|
||||||
verb.eval.Tess.rationalSurfaceAdaptive = function(surface, opts) {
|
verb.eval.Tess.rationalSurfaceAdaptive = function(surface, opts) {
|
||||||
const keys = [opts ? opts.maxDepth: 'undefined'];
|
const keys = [opts ? opts.maxDepth: 'undefined'];
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import {TopoObject} from './topo-object'
|
import {TopoObject} from './topo-object'
|
||||||
import {Loop} from './loop'
|
import {Loop} from './loop'
|
||||||
import PIP from '../../cad/tess/pip';
|
import PIP from '../../cad/tess/pip';
|
||||||
import {NurbsCurve} from "../geom/impl/nurbs";
|
|
||||||
import {eqSqTol, veq, veqNeg} from "../geom/tolerance";
|
import {eqSqTol, veq, veqNeg} from "../geom/tolerance";
|
||||||
import {
|
import {
|
||||||
ENCLOSE_CLASSIFICATION, isCurveEntersEdgeAtPoint, isCurveEntersEnclose, isInsideEnclose,
|
ENCLOSE_CLASSIFICATION, isCurveEntersEdgeAtPoint, isCurveEntersEnclose, isInsideEnclose,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import pertrub from './vector-petrub';
|
import pertrub from './vector-petrub';
|
||||||
import {NurbsCurve} from '../geom/impl/nurbs';
|
import BrepCurve from '../geom/curves/brepCurve';
|
||||||
|
|
||||||
|
|
||||||
export class Ray {
|
export class Ray {
|
||||||
|
|
@ -12,7 +12,7 @@ export class Ray {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCurve() {
|
updateCurve() {
|
||||||
this.curve = NurbsCurve.createLinearNurbs(this.pt, this.pt.plus(this.dir.multiply(this.reachableDistance)));
|
this.curve = BrepCurve.createLinearNurbs(this.pt, this.pt.plus(this.dir.multiply(this.reachableDistance)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pertrub() {
|
pertrub() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import verb from 'verb-nurbs'
|
import verb from 'verb-nurbs'
|
||||||
import {NurbsCurve, NurbsCurveImpl} from '../../brep/geom/impl/nurbs'
|
import BrepCurve from '../../brep/geom/curves/brepCurve';
|
||||||
|
import NurbsCurve from '../../brep/geom/curves/nurbsCurve';
|
||||||
import {Point} from '../../brep/geom/point'
|
import {Point} from '../../brep/geom/point'
|
||||||
import {LUT} from '../../math/bezier-cubic'
|
import {LUT} from '../../math/bezier-cubic'
|
||||||
import {distanceAB, isCCW, makeAngle0_360} from '../../math/math'
|
import {distanceAB, isCCW, makeAngle0_360} from '../../math/math'
|
||||||
|
|
@ -38,7 +39,7 @@ class SketchPrimitive {
|
||||||
normalizeCurveEnds(data);
|
normalizeCurveEnds(data);
|
||||||
verbNurbs = new verb.geom.NurbsCurve(data);
|
verbNurbs = new verb.geom.NurbsCurve(data);
|
||||||
|
|
||||||
return new NurbsCurve(new NurbsCurveImpl(verbNurbs));
|
return new BrepCurve(new NurbsCurve(verbNurbs));
|
||||||
}
|
}
|
||||||
|
|
||||||
toVerbNurbs(plane, _3dtr) {
|
toVerbNurbs(plane, _3dtr) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ import {Face} from '../../brep/topo/face';
|
||||||
import {Shell} from '../../brep/topo/shell';
|
import {Shell} from '../../brep/topo/shell';
|
||||||
import {Vertex} from '../../brep/topo/vertex';
|
import {Vertex} from '../../brep/topo/vertex';
|
||||||
import {Point} from '../../brep/geom/point';
|
import {Point} from '../../brep/geom/point';
|
||||||
import {NurbsCurve, NurbsSurface} from '../../brep/geom/impl/nurbs';
|
import {NurbsSurface} from '../../brep/geom/impl/nurbs';
|
||||||
|
import BrepCurve from '../../brep/geom/curves/brepCurve';
|
||||||
import {Plane} from '../../brep/geom/impl/plane';
|
import {Plane} from '../../brep/geom/impl/plane';
|
||||||
import pip from '../tess/pip';
|
import pip from '../tess/pip';
|
||||||
|
|
||||||
|
|
@ -23,7 +24,7 @@ export default {
|
||||||
pip,
|
pip,
|
||||||
validator: BREPValidator,
|
validator: BREPValidator,
|
||||||
geom: {
|
geom: {
|
||||||
Point, NurbsCurve, Plane, NurbsSurface, createBoundingNurbs
|
Point, BrepCurve, Plane, NurbsSurface, createBoundingNurbs
|
||||||
},
|
},
|
||||||
topo: {
|
topo: {
|
||||||
HalfEdge, Edge, Loop, Face, Shell, Vertex
|
HalfEdge, Edge, Loop, Face, Shell, Vertex
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,6 @@ export function __cross(v1, v2, out) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _cross(v1, v2) {
|
|
||||||
return __cross(v1, v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cross(v1, v2) {
|
export function cross(v1, v2) {
|
||||||
return __cross(v1, v2, []);
|
return __cross(v1, v2, []);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,11 +128,11 @@ function createEnclosure(tpi, a, b, c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEdge(tpi, a, b) {
|
function createEdge(tpi, a, b) {
|
||||||
return new tpi.brep.topo.Edge(tpi.brep.geom.NurbsCurve.createLinearNurbs(a.point, b.point), a, b).halfEdge1;
|
return new tpi.brep.topo.Edge(tpi.brep.geom.BrepCurve.createLinearNurbs(a.point, b.point), a, b).halfEdge1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCurve(tpi, a, b) {
|
function createCurve(tpi, a, b) {
|
||||||
return tpi.brep.geom.NurbsCurve.createLinearNurbs(pt(tpi,a), pt(tpi,b));
|
return tpi.brep.geom.BrepCurve.createLinearNurbs(pt(tpi,a), pt(tpi,b));
|
||||||
}
|
}
|
||||||
|
|
||||||
const pt = (tpi, arr) => new tpi.brep.geom.Point().set3(arr);
|
const pt = (tpi, arr) => new tpi.brep.geom.Point().set3(arr);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue