mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
198 lines
5 KiB
TypeScript
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);
|
|
}
|
|
|
|
|
|
|