jsketcher/modules/geom/curves/brepCurve.ts
2022-08-15 23:47:20 -07:00

198 lines
5 KiB
TypeScript

import NurbsCurve from "./nurbsCurve";
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 Vector from 'math/vector';
import cache from "../impl/cache";
import {Tessellation1D} from "engine/tessellation";
import {Matrix3x4} from "math/matrix";
import {areEqual} from "math/equality";
import {Vec3} from "math/vec";
import {ParametricCurve} from "geom/curves/parametricCurve";
export default class BrepCurve {
impl: ParametricCurve;
uMin: number;
uMax: number;
uMid: number;
private __middlePoint: Vector;
constructor(_impl: ParametricCurve, uMin?: number, uMax?: number) {
this.impl = _impl;
[uMin, uMax] = this.impl.domain();
this.uMin = uMin;
this.uMax = uMax;
this.uMid = (uMax - uMin) * 0.5;
}
get degree(): number {
return this.impl.degree();
}
translate(vector: Vector): BrepCurve {
const tr = new Matrix3x4().translate(vector.x, vector.y, vector.z);
return this.transform(tr);
}
transform(tr: Matrix3x4): BrepCurve {
return new BrepCurve(this.impl.transform(tr.toArray()), this.uMin, this.uMax);
}
tangentAtPoint(point: Vector): Vector {
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: number) {
const dr = this.impl.eval(u, 1);
return pt(dr[1])._normalize();
}
param(point: Vector): number {
return this.impl.param(point.data());
}
split(point: Vector): [BrepCurve, BrepCurve] {
return this.splitByParam(this.param(point));
}
splitByParam(u: number): [BrepCurve, BrepCurve] {
if (ueq(u, this.uMin) || ueq(u, this.uMax) || u < this.uMin || u > this.uMax) {
return null
}
const split = this.impl.split(u);
return split.map(v => new BrepCurve(v)) as [BrepCurve, BrepCurve];
// return [
// new BrepCurve(this.impl, this.uMin, u),
// new BrepCurve(this.impl, u, this.uMax)
// ];
}
point(u: number): Vector {
return pt(this.impl.point(u));
}
tessellateToData(tessTol: number, scale: number): Tessellation1D<Vec3> {
return CURVE_CACHING_TESSELLATOR(this.impl, this.uMin, this.uMax, tessTol, scale);
}
tessellate(tessTol?: number, scale?: number): Tessellation1D<Vector> {
return this.tessellateToData(tessTol, scale).map(p => pt(p));
}
boundary(): [number, number] {
return [this.uMin, this.uMax];
}
intersectCurve(other) {
let isecs = [];
const eq = veq3;
function add(i0) {
for (const 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
).forEach(i => add(i));
isecs.forEach(i => {
i.p0 = pt(i.p0);
i.p1 = pt(i.p1);
});
isecs = isecs.filter(({u0, u1}) => {
const t0 = this.tangentAtParam(u0);
const t1 = other.tangentAtParam(u1);
return !veq(t0, t1) && !veqNeg(t0, t1);
});
return isecs;
}
isInside(u: number): boolean {
return u >= this.uMin && u <= this.uMax;
}
invert(): BrepCurve {
return new BrepCurve(this.impl.invert());
}
startPoint(): Vector {
return this.point(this.uMin);
}
endPoint(): Vector {
return this.point(this.uMax);
}
middlePoint(): Vector {
return this.__middlePoint || (this.__middlePoint = this.point(this.uMid));
}
passesThrough(point: Vector): boolean {
return eqSqTol(0, point.distanceToSquared(this.point(this.param(point))));
}
static createLinearCurve = function(a: Vector, b: Vector): BrepCurve {
const line = verb.geom.NurbsCurve.byKnotsControlPointsWeights( 1, [0,0,1,1], [a.data(), b.data()]);
return new BrepCurve(new NurbsCurve(line));
};
}
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.knots().map(u => curve.point(u));
}
return curveTess(curve, min, max, tessTol, scale);
}