Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion common/api/core-geometry.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,10 @@ export class CurveLocationDetail {
get hasFraction1(): boolean;
intervalRole?: CurveIntervalRole;
inverseInterpolateFraction(f: number, defaultFraction?: number): number;
isInterval(): this is {
fraction1: number;
point1: Point3d;
};
get isIsolated(): boolean;
isSameCurveAndFraction(other: CurveLocationDetail | {
curve: CurvePrimitive;
Expand Down Expand Up @@ -2488,12 +2492,15 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection {
static createArrayOfGrowableXYZArray(data: MultiLineStringDataVariant): GrowableXYZArray[] | undefined;
static createCompressed(source: IndexedXYZCollection, tolerance?: number, result?: GrowableXYZArray): GrowableXYZArray;
crossProductIndexIndexIndex(originIndex: number, targetAIndex: number, targetBIndex: number, result?: Vector3d): Vector3d | undefined;
crossProductUncheckedIndexIndexIndex(originIndex: number, targetAIndex: number, targetBIndex: number, result?: Vector3d): Vector3d;
crossProductXYAndZIndexIndex(origin: XYAndZ, targetAIndex: number, targetBIndex: number, result?: Vector3d): Vector3d | undefined;
static distanceBetweenPointsIn2Arrays(arrayA: GrowableXYZArray, i: number, arrayB: GrowableXYZArray, j: number): number | undefined;
distanceIndexIndex(i: number, j: number): number | undefined;
distanceIndexToPoint(i: number, spacePoint: XYAndZ): number | undefined;
static distanceRangeBetweenCorrespondingPoints(arrayA: GrowableXYZArray, arrayB: GrowableXYZArray): Range1d;
distanceSquaredIndexIndex(i: number, j: number): number | undefined;
distanceSquaredUncheckedIndexIndex(i: number, j: number): number;
distanceUncheckedIndexIndex(i: number, j: number): number;
ensureCapacity(pointCapacity: number, applyGrowthFactor?: boolean): void;
evaluateUncheckedIndexDotProductXYZ(pointIndex: number, x: number, y: number, z: number): number;
evaluateUncheckedIndexPlaneAltitude(pointIndex: number, plane: PlaneAltitudeEvaluator): number;
Expand Down Expand Up @@ -2548,7 +2555,9 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection {
transferFromGrowableXYZArray(destIndex: number, source: IndexedXYZCollection, sourceIndex: number): boolean;
tryTransformInverseInPlace(transform: Transform): boolean;
vectorIndexIndex(i: number, j: number, result?: Vector3d): Vector3d | undefined;
vectorUncheckedIndexIndex(i: number, j: number, result?: Vector3d): Vector3d;
vectorXYAndZIndex(origin: XYAndZ, j: number, result?: Vector3d): Vector3d | undefined;
vectorXYAndZUncheckedIndex(origin: XYAndZ, j: number, result?: Vector3d): Vector3d;
}

// @public
Expand Down Expand Up @@ -2994,19 +3003,27 @@ export abstract class IndexedXYZCollection {
abstract accumulateCrossProductIndexIndexIndex(origin: number, indexA: number, indexB: number, result: Vector3d): void;
abstract accumulateScaledXYZ(index: number, scale: number, sum: Point3d): void;
almostEqualIndexIndex(index0: number, index1: number, tolerance?: number): boolean | undefined;
almostEqualUncheckedIndexIndex(index0: number, index1: number, tolerance?: number): boolean;
almostEqualXYIndexIndex(index0: number, index1: number, tolerance?: number): boolean | undefined;
almostEqualXYUncheckedIndexIndex(index0: number, index1: number, tolerance?: number): boolean;
back(result?: Point3d): Point3d | undefined;
backUnchecked(result?: Point3d): Point3d;
abstract crossProductIndexIndexIndex(origin: number, indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined;
crossProductIndexIndexXYAndZ(origin: number, indexA: number, targetB: XYAndZ, result?: Vector3d): Vector3d | undefined;
abstract crossProductXYAndZIndexIndex(origin: XYAndZ, indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined;
cyclicIndex(i: number): number;
abstract distanceIndexIndex(index0: number, index1: number): number | undefined;
abstract distanceSquaredIndexIndex(index0: number, index1: number): number | undefined;
distanceSquaredIndexXYAndZ(index0: number, target: XYAndZ): number | undefined;
abstract distanceSquaredUncheckedIndexIndex(index0: number, index1: number): number;
distanceSquaredUncheckedIndexXYAndZ(index0: number, target: XYAndZ): number;
abstract distanceUncheckedIndexIndex(index0: number, index1: number): number;
dotProductIndexIndexIndex(origin: number, indexA: number, indexB: number): number | undefined;
dotProductIndexIndexXYAndZ(origin: number, indexA: number, targetB: XYAndZ): number | undefined;
dotProductUncheckedIndexIndexXYAndZ(origin: number, indexA: number, targetB: XYAndZ): number;
findOrderedDuplicates(tolerance?: number, preserveLast?: boolean): number[];
front(result?: Point3d): Point3d | undefined;
frontUnchecked(result?: Point3d): Point3d;
getArray(): Point3d[];
abstract getPoint3dAtCheckedPointIndex(index: number, result?: Point3d): Point3d | undefined;
abstract getPoint3dAtUncheckedPointIndex(index: number, result?: Point3d): Point3d;
Expand All @@ -3016,13 +3033,16 @@ export abstract class IndexedXYZCollection {
abstract getYAtUncheckedPointIndex(pointIndex: number): number;
abstract getZAtUncheckedPointIndex(pointIndex: number): number;
interpolateIndexIndex(index0: number, fraction: number, index1: number, result?: Point3d): Point3d | undefined;
interpolateUncheckedIndexIndex(index0: number, fraction: number, index1: number, result?: Point3d): Point3d;
isIndexValid(index: number): boolean;
abstract get length(): number;
linearCombination(scales: number[], result?: Point3d | Vector3d): XYZ;
get points(): Iterable<Point3d>;
abstract vectorIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined;
vectorIndexXYAndZ(indexA: number, target: XYAndZ, result?: Vector3d): Vector3d | undefined;
abstract vectorUncheckedIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d;
abstract vectorXYAndZIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d | undefined;
abstract vectorXYAndZUncheckedIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d;
}

// @public
Expand Down Expand Up @@ -3358,6 +3378,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions {
fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d;
static fromJSON(json?: any): LineString3d;
getIndexedSegment(index: number, result?: LineSegment3d): LineSegment3d | undefined;
getUncheckedIndexedSegment(index: number, result?: LineSegment3d): LineSegment3d;
globalFractionToSegmentIndexAndLocalFraction(globalFraction: number): {
index: number;
fraction: number;
Expand Down Expand Up @@ -3399,6 +3420,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions {
surfaceNormalAt(i: number, result?: Vector3d): Vector3d | undefined;
toJSON(): XYZProps[];
tryTransformInPlace(transform: Transform): boolean;
uncheckedPointAt(i: number, result?: Point3d): Point3d;
vectorBetween(i: number, j: number, result?: Vector3d): Vector3d | undefined;
}

Expand Down Expand Up @@ -3535,7 +3557,9 @@ export class Matrix3d implements BeJSONFunctions {
columnZCrossVector(vector: XYZ, result?: Vector3d): Vector3d;
columnZMagnitude(): number;
columnZMagnitudeSquared(): number;
computeCachedInverse(useCacheIfAvailable: boolean): boolean;
computeCachedInverse(useCacheIfAvailable: boolean): this is {
inverseCoffs: Float64Array;
};
conditionNumber(): number;
static create90DegreeRotationAroundAxis(axisIndex: number): Matrix3d;
static createCapture(coffs: Float64Array, inverseCoffs?: Float64Array): Matrix3d;
Expand Down Expand Up @@ -4393,6 +4417,8 @@ export class Point3dArrayCarrier extends IndexedReadWriteXYZCollection {
data: Point3d[];
distanceIndexIndex(index0: number, index1: number): number | undefined;
distanceSquaredIndexIndex(index0: number, index1: number): number | undefined;
distanceSquaredUncheckedIndexIndex(index0: number, index1: number): number;
distanceUncheckedIndexIndex(index0: number, index1: number): number;
front(result?: Point3d): Point3d | undefined;
getPoint3dAtCheckedPointIndex(index: number, result?: Point3d): Point3d | undefined;
getPoint3dAtUncheckedPointIndex(index: number, result?: Point3d): Point3d;
Expand All @@ -4407,7 +4433,9 @@ export class Point3dArrayCarrier extends IndexedReadWriteXYZCollection {
pushXYZ(x?: number, y?: number, z?: number): void;
reverseInPlace(): void;
vectorIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined;
vectorUncheckedIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d;
vectorXYAndZIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d | undefined;
vectorXYAndZUncheckedIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d;
}

// @public
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/core-geometry",
"comment": "",
"type": "none"
}
],
"packageName": "@itwin/core-geometry"
}
5 changes: 3 additions & 2 deletions core/geometry/src/bspline/BSplineCurve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ export abstract class BSplineCurve3dBase extends CurvePrimitive {
const minMax = Range1d.createNull();
// put the altitudes of all the B-spline poles in one array
for (let i = 0; i < numPole; i++) {
allCoffs[i] = plane.weightedAltitude(this.getPolePoint4d(i, point4d)!);
this.getPolePoint4d(i, point4d);
allCoffs[i] = plane.weightedAltitude(point4d);
minMax.extendX(allCoffs[i]);
}
// A univariate B-spline through the altitude poles gives altitude as function of the B-spline knot.
Expand All @@ -376,7 +377,7 @@ export abstract class BSplineCurve3dBase extends CurvePrimitive {
minMax.extendArraySubset(allCoffs, spanIndex, order);
if (minMax.containsX(0.0)) {
// pack the B-spline support into a univariate bezier
univariateBezier = UnivariateBezier.createArraySubset(allCoffs, spanIndex, order, univariateBezier)!;
univariateBezier = UnivariateBezier.createArraySubset(allCoffs, spanIndex, order, univariateBezier);
// saturate and solve the bezier
Bezier1dNd.saturate1dInPlace(univariateBezier.coffs, this._bcurve.knots, spanIndex);
const roots = univariateBezier.roots(0.0, true);
Expand Down
4 changes: 3 additions & 1 deletion core/geometry/src/bspline/BSplineCurveOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,9 @@ export namespace BSplineCurveOps {
} else { // closed
if (undefined !== (poles = this.solveNearTridiagonal(options.fitPoints, alpha, beta, gamma))) {
if (poles.length > 2) {
poles.unshift(poles.pop()!); // shift poles right to line up with the knots
const pole = poles.pop();
if (pole)
poles.unshift(pole); // shift poles right to line up with the knots
for (let i = 0; i < options.order - 1; ++i)
poles.push(poles[i].clone()); // periodically extend (the modern way)
}
Expand Down
22 changes: 15 additions & 7 deletions core/geometry/src/bspline/BezierCurve3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ export class BezierCurve3d extends BezierCurveBase {
/** Return poles as a linestring */
public copyPointsAsLineString(): LineString3d {
const result = LineString3d.create();
for (let i = 0; i < this._polygon.order; i++)
result.addPoint(this.getPolePoint3d(i)!);
const point = Point3d.createZero();
for (let i = 0; i < this._polygon.order; i++) {
if (this.getPolePoint3d(i, point))
result.addPoint(point);
}
return result;
}
/** Create a curve with given points.
Expand Down Expand Up @@ -155,13 +158,17 @@ export class BezierCurve3d extends BezierCurveBase {
}
/** Extend `rangeToExtend`, using candidate extrema at
* * both end points
* * any internal extrema in x,y,z
* * any internal extrema in x,y,z.
*
* Extend fails if Bezier curve order is less than 2.
*/
public extendRange(rangeToExtend: Range3d, transform?: Transform) {
const order = this.order;
if (!transform) {
this.allocateAndZeroBezierWorkData(order - 1, 0, 0);
const bezier = this._workBezier!;
const bezier = this._workBezier;
if (!bezier)
return;
this.getPolePoint3d(0, this._workPoint0);
rangeToExtend.extend(this._workPoint0);
this.getPolePoint3d(order - 1, this._workPoint0);
Expand All @@ -178,9 +185,10 @@ export class BezierCurve3d extends BezierCurveBase {
}
} else {
this.allocateAndZeroBezierWorkData(order - 1, order, 0);
const bezier = this._workBezier!;
const componentCoffs = this._workCoffsA!; // to hold transformed copy of x,y,z in turn.

const bezier = this._workBezier;
const componentCoffs = this._workCoffsA; // to hold transformed copy of x,y,z in turn.
if (!bezier || !componentCoffs)
return;
this.getPolePoint3d(0, this._workPoint0);
rangeToExtend.extendTransformedPoint(transform, this._workPoint0);
this.getPolePoint3d(order - 1, this._workPoint0);
Expand Down
30 changes: 20 additions & 10 deletions core/geometry/src/bspline/BezierCurve3dH.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ export class BezierCurve3dH extends BezierCurveBase {
* * This assumes this bezier is saturated.
* @param spacePoint point being projected
* @param detail pre-allocated detail to record (evolving) closest point.
* @returns true if an updated occurred, false if either (a) no perpendicular projections or (b) perpendiculars were not closer.
* @returns true if an updated occurred, false if either (a) no perpendicular projections or (b) perpendiculars were
* not closer or (c) Bezier order is too low.
*/
public updateClosestPointByTruePerpendicular(spacePoint: Point3d, detail: CurveLocationDetail,
testAt0: boolean = false,
Expand All @@ -230,7 +231,9 @@ export class BezierCurve3dH extends BezierCurveBase {
// unweighted !!!
const productOrder = 2 * this.order - 2;
this.allocateAndZeroBezierWorkData(productOrder, 0, 0);
const bezier = this._workBezier!;
const bezier = this._workBezier;
if (!bezier)
return false;
// closestPoint condition is:
// (spacePoint - curvePoint) DOT curveTangent = 0;
// Each product (x,y,z) of the DOT is the product of two bezier polynomials
Expand All @@ -248,9 +251,11 @@ export class BezierCurve3dH extends BezierCurveBase {
const orderB = 2 * this.order - 2; // products of component and component difference.
const productOrder = orderA + orderB - 1;
this.allocateAndZeroBezierWorkData(productOrder, orderA, orderB);
const bezier = this._workBezier!;
const workA = this._workCoffsA!;
const workB = this._workCoffsB!;
const bezier = this._workBezier;
const workA = this._workCoffsA;
const workB = this._workCoffsB;
if (!bezier || !workA || !workB)
return false;
const packedData = this._polygon.packedData;
for (let i = 0; i < 3; i++) {
// x representing loop pass: (w * spacePoint.x - curve.x(s)) * (curveDelta.x(s) * curve.w(s) - curve.x(s) * curveDelta.w(s))
Expand Down Expand Up @@ -286,12 +291,16 @@ export class BezierCurve3dH extends BezierCurveBase {
/** Extend `rangeToExtend`, using candidate extrema at
* * both end points
* * any internal extrema in x,y,z
*
* Extend fails if Bezier curve order is too low.
*/
public extendRange(rangeToExtend: Range3d, transform?: Transform) {
const order = this.order;
if (!transform) {
this.allocateAndZeroBezierWorkData(order * 2 - 2, 0, 0);
const bezier = this._workBezier!;
const bezier = this._workBezier;
if (!bezier)
return;
const data = this._polygon.packedData;
this.getPolePoint3d(0, this._workPoint0);
rangeToExtend.extend(this._workPoint0);
Expand Down Expand Up @@ -329,10 +338,11 @@ export class BezierCurve3dH extends BezierCurveBase {
}
} else {
this.allocateAndZeroBezierWorkData(order * 2 - 2, order, order);
const componentCoffs = this._workCoffsA!; // to hold transformed copy of x,y,z in turn.
const weightCoffs = this._workCoffsB!; // to hold weights
const bezier = this._workBezier!;

const componentCoffs = this._workCoffsA; // to hold transformed copy of x,y,z in turn.
const weightCoffs = this._workCoffsB; // to hold weights
const bezier = this._workBezier;
if (!bezier || !componentCoffs || !weightCoffs)
return;
this.getPolePoint3d(0, this._workPoint0);
rangeToExtend.extendTransformedPoint(transform, this._workPoint0);
this.getPolePoint3d(order - 1, this._workPoint0);
Expand Down
14 changes: 10 additions & 4 deletions core/geometry/src/bspline/BezierCurveBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,19 @@ export abstract class BezierCurveBase extends CurvePrimitive {
}
return sum;
}
/** Return the start point. (first control point) */
/**
* Return the start point (first control point).
* @returns the start point as a Point3d. If insufficiently many control points exist, returns (0,0,0).
*/
public override startPoint(result?: Point3d): Point3d {
return this.getPolePoint3d(0, result)!; // ASSUME non-trivial pole set -- if null comes back, it bubbles out
return this.getPolePoint3d(0, result) ?? Point3d.createZero();
}
/** Return the end point. (last control point) */
/**
* Return the end point (last control point).
* @returns the end point as a Point3d. If insufficiently many control points exist, returns (0,0,0).
*/
public override endPoint(result?: Point3d): Point3d {
return this.getPolePoint3d(this.order - 1, result)!; // ASSUME non-trivial pole set
return this.getPolePoint3d(this.order - 1, result) ?? Point3d.createZero();
}
/** Return the control polygon length as a quick length estimate. */
public quickLength(): number { return this.polygonLength(); }
Expand Down
12 changes: 6 additions & 6 deletions core/geometry/src/clipping/AlternatingConvexClipTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,10 @@ export class AlternatingCCTreeNodeCurveClipper {

if (this._curve instanceof LineSegment3d) {
const segment = this._curve;
let f0: number;
let f1: number;
let f0 = 0;
let f1 = 0;
if (segment.announceClipIntervals(planes, (a0: number, a1: number, _cp: CurvePrimitive) => { f0 = a0; f1 = a1; })) {
insideSegments.push(Range1d.createXX(f0!, f1!));
insideSegments.push(Range1d.createXX(f0, f1));
}
return true;

Expand All @@ -464,14 +464,14 @@ export class AlternatingCCTreeNodeCurveClipper {

} else if (this._curve instanceof LineString3d && (this._curve).points.length > 1) {
const linestring = this._curve;
let f0: number;
let f1: number;
let f0 = 0;
let f1 = 0;
const nPoints = linestring.points.length;
const df = 1.0 / (nPoints - 1);
for (let i = 0; i < nPoints - 1; i++) {
const segment = LineSegment3d.create(linestring.points[i], linestring.points[i + 1]);
if (segment.announceClipIntervals(planes, (a0: number, a1: number, _cp: CurvePrimitive) => { f0 = a0; f1 = a1; })) {
insideSegments.push(Range1d.createXX((i + f0!) * df, (i + f1!) * df));
insideSegments.push(Range1d.createXX((i + f0) * df, (i + f1) * df));
}
}
return true;
Expand Down
5 changes: 3 additions & 2 deletions core/geometry/src/clipping/ClipPlane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,11 @@ export class ClipPlane extends Plane3d implements Clipper, PolygonClipper {
public getPlane3d(): Plane3dByOriginAndUnitNormal {
const d = this._distanceFromOrigin;
// normal should be normalized, will not return undefined
return Plane3dByOriginAndUnitNormal.create(
const plane = Plane3dByOriginAndUnitNormal.create(
Point3d.create(this._inwardNormal.x * d, this._inwardNormal.y * d, this._inwardNormal.z * d),
this._inwardNormal,
)!;
);
return plane ?? Plane3dByOriginAndUnitNormal.createXYPlane();
}
/**
* Return the Point4d d form of the plane.
Expand Down
Loading
Loading