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 { return CURVE_CACHING_TESSELLATOR(this.impl, this.uMin, this.uMax, tessTol, scale); } tessellate(tessTol?: number, scale?: number): Tessellation1D { 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); }