diff --git a/common/api/core-geometry.api.md b/common/api/core-geometry.api.md index 2066357e362b..fac3c9e6662e 100644 --- a/common/api/core-geometry.api.md +++ b/common/api/core-geometry.api.md @@ -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; @@ -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; @@ -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 @@ -2994,8 +3003,11 @@ 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; @@ -3003,10 +3015,15 @@ export abstract class IndexedXYZCollection { 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; @@ -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; 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 @@ -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; @@ -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; } @@ -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; @@ -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; @@ -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 @@ -5497,130 +5525,6 @@ export class RuledSweep extends SolidPrimitive { tryTransformInPlace(transform: Transform): boolean; } -// @alpha -export class Sample { - static addAuxDataScalarChannel(data: PolyfaceData, channelIndex: number, name: string | undefined, inputName: string | undefined, input0: number, inputStep: number, numInput: number, dataType: AuxChannelDataType, scalarFunction: (input: number, xyz: Point3d) => number): void; - static readonly angle: Angle[]; - static readonly angleSweep: AngleSweep[]; - static appendPhases(linestring: LineString3d, numPhase: number, ...vectors: Vector3d[]): void; - static appendSawTooth(points: Point3d[], dxLow: number, riseX: number, riseY: number, dxHigh: number, numPhase: number): Point3d[]; - static appendSplits(points: Point3d[], target: Point3d, numSplit: number, includeTarget: boolean): void; - static appendVariableSawTooth(points: Point3d[], dxLow: number, riseX: number, riseY: number, dxHigh: number, numPhase: number, xFactor: number): Point3d[]; - static convertPointsToSegments(points: Point3d[], forceClosure?: boolean): LineSegment3d[]; - static createAllGeometryQueryTypes(): GeometryQuery[]; - static createAnnulusPolyline(edgesPerQuadrant: number, center: Point3d, r0: number, r1: number, theta0: Angle, theta1: Angle, addClosure: boolean): Point3d[]; - static createArcRegions(): Loop[]; - static createArcs(radiusRatio?: number, sweep?: AngleSweep): Arc3d[]; - static createArcStrokes(edgesPerQuadrant: number, center: Point3d, r0: number, theta0: Angle, theta1: Angle, addClosure?: boolean, z?: number): Point3d[]; - static createBagOfCurves(): BagOfCurves[]; - static createBidirectionalSawtooth(origin: Point3d, dxLow: number, riseX: number, riseY: number, dxHigh: number, numPhaseOutbound: number, dyFinal: number, dxLowReturn: number, riseXReturn: number, riseYReturn: number, dxHighReturn: number): Point3d[]; - static createBoxes(capped?: boolean): Box[]; - static createBspline3dHArcs(): BSplineCurve3dH[]; - static createBspline3dHCurves(): BSplineCurve3dH[]; - static createBsplineArc90SectionToXYZWArrays(center: Point3d, axes: Matrix3d, radius0: number, radius90: number, applyWeightsToXYZ: boolean): number[][]; - static createBsplineCurveHelices(radius: number, height: number, numTurns: number, numSamplesPerTurn: number): BSplineCurve3d[]; - static createBsplineCurves(includeMultipleKnots?: boolean): BSplineCurve3d[]; - static createCappedArcLoop(radius: number, startDegrees: number, endDegrees: number): Loop; - static createCappedArcPath(radius: number, startDegrees: number, endDegrees: number): Path; - static createCappedArcPrimitives(radius: number, startDegrees: number, endDegrees: number): CurvePrimitive[]; - static createCenteredBoxEdges(ax?: number, ay?: number, az?: number, cx?: number, cy?: number, cz?: number, geometry?: GeometryQuery[]): GeometryQuery[]; - static createCenteredRectangleXY(cx: number, cy: number, ax: number, ay: number, z?: number): Point3d[]; - static createClipPlanes(): ClipPlane[]; - static createClipPlaneSets(): UnionOfConvexClipPlaneSets[]; - static createClosedSolidSampler(capped: boolean, rotationAngle?: Angle): SolidPrimitive[]; - static createConeBsplineSurface(centerA: Point3d, centerB: Point3d, radiusA: number, radiusB: number, numSection: number): BSplineSurface3dH | undefined; - static createCones(): Cone[]; - static createCurveChainWithDistanceIndex(): CurveChainWithDistanceIndex[]; - static createCutPie(x0: number, y0: number, radius: number, sweep: AngleSweep, numRadialEdges: number, numArcEdges: number, addClosure?: boolean): Point3d[]; - static createEllipsoids(): Sphere[]; - static createFractalDiamondConvexPattern(numRecursion: number, perpendicularFactor: number): Point3d[]; - static createFractalHatReversingPattern(numRecursion: number, perpendicularFactor: number): Point3d[]; - static createFractalLMildConcavePatter(numRecursion: number, perpendicularFactor: number): Point3d[]; - static createFractalLReversingPattern(numRecursion: number, perpendicularFactor: number): Point3d[]; - static createFractalSquareReversingPattern(numRecursion: number, perpendicularFactor: number): Point3d[]; - static createGridPointsOnEllipsoid(transform: Transform, numLatitudeStep: number, numLongitudeStep: number, latitudeSweep?: AngleSweep, longitudeSweep?: AngleSweep): Point3d[]; - static createGrowableArrayCirclePoints(radius: number, numEdge: number, closed?: boolean, centerX?: number, centerY?: number, data?: GrowableXYZArray): GrowableXYZArray; - static createGrowableArrayCountedSteps(a0: number, delta: number, n: number): GrowableFloat64Array; - static createHelixPoints(completeTurns: number, numPoints: number, placement?: Transform): Point3d[]; - static createInterpolatedPoints(point0: Point3d, point1: Point3d, numPoints: number, result?: Point3d[], index0?: number, index1?: number): Point3d[]; - static createInvertibleTransforms(): Transform[]; - static createLineArcPaths(): Path[]; - static createLineStrings(): LineString3d[]; - static createLShapedPolygon(x0: number, y0: number, ax: number, ay: number, bx: number, by: number, z?: number): Point3d[]; - static createManyArcs(skewFactors?: number[]): Arc3d[]; - static createMap4ds(): Map4d[]; - static createMatrix3dArray(): Matrix3d[]; - static createMatrix4ds(includeIrregular?: boolean): Matrix4d[]; - static createMeshFromFrankeSurface(size: number, options?: StrokeOptions, scales?: number[]): IndexedPolyface | undefined; - static createMeshInAnnulus(edgesPerQuadrant: number, center: Point3d, r0: number, r1: number, theta0: Angle, theta1: Angle): IndexedPolyface | undefined; - static createMessyRigidTransform(fixedPoint?: Point3d): Transform; - static createMixedBsplineCurves(): BSplineCurve3dBase[]; - static createNonZeroVectors(): Vector3d[]; - static createPartialTorusAroundZ(majorRadius: number, majorSweep: Angle, minorRadius: number, minorStart: Angle, minorEnd: Angle): RotationalSweep; - static createPlane(x: number, y: number, z: number, u: number, v: number, w: number): Plane3dByOriginAndUnitNormal; - static createPoint2dLattice(low: number, step: number, high: number): Point2d[]; - static createPoint3dLattice(low: number, step: number, high: number): Point3d[]; - static createPointsByIndexFunctions(numInterval: number, fx: SteppedIndexFunction, fy: SteppedIndexFunction, fz?: SteppedIndexFunction): Point3d[]; - static createPointSineWave(origin: XYAndZ | undefined, numInterval?: number, xStep?: number, a?: number, thetaSweep?: AngleSweep, b?: number, betaSweep?: AngleSweep): Point3d[]; - static createPseudoTorusBsplineSurface(radiusU: number, radiusV: number, numU: number, numV: number, orderU: number, orderV: number): BSplineSurface3d | undefined; - static createRange3ds(): Range3d[]; - static createRangeEdges(range: Range3d): BagOfCurves | undefined; - static createRay(x: number, y: number, z: number, u: number, v: number, w: number): Ray3d; - static createRectangle(x0: number, y0: number, x1: number, y1: number, z?: number, closed?: boolean): Point3d[]; - static createRectangleInRange2d(range: Range2d, z?: number, closed?: boolean): Point3d[]; - static createRectangleXY(x0: number, y0: number, ax: number, ay: number, z?: number): Point3d[]; - static createRecursiveFractalPolygon(poles: Point3d[], pattern: Point2d[], numRecursion: number, perpendicularFactor: number): Point3d[]; - static createRegularPolygon(cx: number, cy: number, cz: number, angle0: Angle, r: number, numPoint: number, close: boolean): Point3d[]; - static createRigidAxes(): Matrix3d[]; - static createRigidTransforms(distanceScale?: number): Transform[]; - static createRosePoint2d(theta: number, a: number): Point2d; - static createRosePoint3d(theta: number, a: number, z?: number): Point3d; - static createRuledSweeps(includeParityRegion?: boolean, includeBagOfCurves?: boolean): RuledSweep[]; - static createScaleSkewMatrix3d(): Matrix3d[]; - static createSimpleIndexedPolyfaces(gridMultiplier: number): IndexedPolyface[]; - static createSimpleLinearSweeps(): LinearSweep[]; - static createSimpleLoops(): Loop[]; - static createSimpleParityRegions(includeBCurves?: boolean): ParityRegion[]; - static createSimplePaths(withGaps?: boolean): Path[]; - static createSimplePointStrings(): PointString3d[]; - static createSimpleRotationalSweeps(): RotationalSweep[]; - static createSimpleTransitionSpirals(): TransitionSpiral3d[]; - static createSimpleUnions(): UnionRegion[]; - static createSimpleXYPointLoops(): Point3d[][]; - static createSingularMatrix3d(): Matrix3d[]; - static createSmoothCurvePrimitives(size?: number): CurvePrimitive[]; - static createSpheres(includeEllipsoidal?: boolean): Sphere[]; - static createSquareWave(origin: Point3d, dx0: number, dy: number, dx1: number, numPhase: number, dyReturn: number): Point3d[]; - static createSquareWavePath(numTooth: number, dxA: number, dxB: number, yA: number, yB: number, structure: number): Path; - static createStar(cx: number, cy: number, cz: number, r0: number, r1: number | undefined, numPoint: number, close: boolean, theta0?: Angle): Point3d[]; - static createStarsInStars(rA0: number, rA1: number, numAPoint: number, rB0: number, rB1: number, numBPoint: number, rC: number, numC: number, close: boolean): Point3d[][]; - static createTorusPipes(): TorusPipe[]; - static createTriangleWithSplitEdges(numSplitAB: number, numSplitBC: number, numSplitCA: number, wrap?: boolean, xyzA?: Point3d, xyzB?: Point3d, xyzC?: Point3d): Point3d[]; - static createTriangularUnitGridPolyface(origin: Point3d, vectorX: Vector3d, vectorY: Vector3d, numXVertices: number, numYVertices: number, createParams?: boolean, createNormals?: boolean, createColors?: boolean, triangulate?: boolean): IndexedPolyface; - static createTwistingBezier(order: number, x0: number, y0: number, r: number, thetaStepper: AngleSweep, phiStepper: AngleSweep, weightInterval?: Segment1d): CurvePrimitive | undefined; - static createUnitCircle(numPoints: number): Point3d[]; - static createVerticalStaggerPolygon(dy1: number, dy2: number, dy3: number, dy4: number, ax: number, ay: number, dx1: number, dx4: number): Point3d[]; - static createWeightedXYGridBsplineSurface(numU: number, numV: number, orderU: number, orderV: number, weight00?: number, weight10?: number, weight01?: number, weight11?: number): BSplineSurface3dH | undefined; - static createXYGrid(numU: number, numV: number, dX?: number, dY?: number): Point3d[]; - static createXYGridBsplineSurface(numU: number, numV: number, orderU: number, orderV: number): BSplineSurface3d | undefined; - static createZigZag(start: Point3d | Point3d[], steps: Vector3d[], numStroke: number): Point3d[]; - // @deprecated (undocumented) - static creatVerticalStaggerPolygon(dy1: number, dy2: number, dy3: number, dy4: number, ax: number, ay: number, dx1: number, dx4: number): Point3d[]; - static readonly lineSegment3d: LineSegment3d[]; - static nonConvexQuadSimpleFractal(numRecursion: number, perpendicularFactor: number): Point3d[]; - static readonly plane3dByOriginAndUnitNormal: Plane3dByOriginAndUnitNormal[]; - static readonly point2d: Point2d[]; - static readonly point3d: Point3d[]; - static readonly point4d: Point4d[]; - static pushMove(data: Point3d[], dx: number, dy: number, dz?: number): void; - static readonly range1d: Range1d[]; - static readonly range2d: Range2d[]; - static readonly range3d: Range3d[]; - static readonly ray3d: Ray3d[]; - static sweepXZLineStringToMeshWithHoles(xzPoints: number[][], ySweep: number, acceptFunction: (x0: number, y0: number) => boolean): IndexedPolyface; - static readonly vector2d: Vector2d[]; -} - // @public export interface SectionSequenceWithPlanes { mesh?: IndexedPolyface; @@ -5845,17 +5749,6 @@ export enum StandardViewIndex { Top = 1 } -// @alpha -export type SteppedIndexFunction = (i: number, n: number) => number; - -// @alpha -export class SteppedIndexFunctionFactory { - static createConstant(value?: number): SteppedIndexFunction; - static createCosine(amplitude: number, sweep?: AngleSweep, f0?: number): SteppedIndexFunction; - static createLinear(a: number, f0?: number): SteppedIndexFunction; - static createSine(amplitude: number, sweep?: AngleSweep, f0?: number): SteppedIndexFunction; -} - // @alpha export type StringifiedClipVector = ClipVector & { readonly clipString: string; diff --git a/common/api/summary/core-geometry.exports.csv b/common/api/summary/core-geometry.exports.csv index 37be4d514bf1..ae4845f8e8f2 100644 --- a/common/api/summary/core-geometry.exports.csv +++ b/common/api/summary/core-geometry.exports.csv @@ -258,7 +258,6 @@ public;class;RegionOps public;class;ReusableObjectCache public;class;RotationalSweep public;class;RuledSweep -alpha;class;Sample public;interface;SectionSequenceWithPlanes public;class;Segment1d public;namespace;SerializationHelpers @@ -272,8 +271,6 @@ public;type;SortableEdgeCluster public;class;SpacePolygonTriangulation public;class;Sphere public;enum;StandardViewIndex -alpha;type;SteppedIndexFunction -alpha;class;SteppedIndexFunctionFactory alpha;type;StringifiedClipVector alpha;namespace;StringifiedClipVector public;class;StrokeCountMap diff --git a/common/changes/@itwin/core-geometry/saeed-torabi-fix-lint-issues_2025-09-11-20-13.json b/common/changes/@itwin/core-geometry/saeed-torabi-fix-lint-issues_2025-09-11-20-13.json new file mode 100644 index 000000000000..9d7216f28509 --- /dev/null +++ b/common/changes/@itwin/core-geometry/saeed-torabi-fix-lint-issues_2025-09-11-20-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-geometry", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/core-geometry" +} \ No newline at end of file diff --git a/core/frontend/src/test/render/primitives/Primitives.test.ts b/core/frontend/src/test/render/primitives/Primitives.test.ts index d3015e24ce86..ec1402e50691 100644 --- a/core/frontend/src/test/render/primitives/Primitives.test.ts +++ b/core/frontend/src/test/render/primitives/Primitives.test.ts @@ -3,8 +3,8 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { describe, expect, it } from "vitest"; -import { LineString3d, Loop, Point3d, Range3d, Sample, Transform, Vector3d } from "@itwin/core-geometry"; import { GraphicParams } from "@itwin/core-common"; +import { Geometry as Geom, IndexedPolyface, LineString3d, Loop, Point3d, Range3d, Transform, Vector3d } from "@itwin/core-geometry"; import { DisplayParams } from "../../../common/internal/render/DisplayParams"; import { GeometryList } from "../../../common/internal/render/GeometryList"; import { Geometry } from "../../../common/internal/render/GeometryPrimitives"; @@ -15,6 +15,64 @@ function verifyGeometryQueries(g: Geometry, doDecimate: boolean = false, doVerte expect(g.doVertexCluster()).toBe(doVertexCluster); expect(g.part() !== undefined).toBe(hasPart); } +function createUnitCircle(numPoints: number): Point3d[] { + const points: Point3d[] = []; + const dTheta = Geom.safeDivideFraction(Math.PI * 2, numPoints - 1, 0.0); + for (let i = 0; i + 1 < numPoints; i++) { + const theta = i * dTheta; + points.push(Point3d.create(Math.cos(theta), Math.sin(theta), 0.0)); + } + points.push(points[0].clone()); + return points; +} +function createTriangularUnitGridPolyface( + origin: Point3d, vectorX: Vector3d, vectorY: Vector3d, + numXVertices: number, numYVertices: number, createParams: boolean = false, createNormals: boolean = false, +): IndexedPolyface { + const mesh = IndexedPolyface.create(createNormals, createParams); + const normal = vectorX.crossProduct(vectorY); + if (createNormals) { + normal.normalizeInPlace(); + mesh.addNormalXYZ(normal.x, normal.y, normal.z); // use XYZ to help coverage count + } + // push to coordinate arrays + for (let j = 0; j < numYVertices; j++) { + for (let i = 0; i < numXVertices; i++) { + mesh.addPoint(origin.plus2Scaled(vectorX, i, vectorY, j)); + if (createParams) + mesh.addParamUV(i, j); + } + } + for (let j = 0; j + 1 < numYVertices; j++) { + for (let i = 0; i + 1 < numXVertices; i++) { + const vertex00 = numXVertices * j + i; + const vertex10 = vertex00 + 1; + const vertex01 = vertex00 + numXVertices; + const vertex11 = vertex01 + 1; + // push quad + mesh.addPointIndex(vertex00, true); + mesh.addPointIndex(vertex10, true); + mesh.addPointIndex(vertex11, true); + mesh.addPointIndex(vertex01, true); + // param indexing matches points + if (createParams) { + mesh.addParamIndex(vertex00); + mesh.addParamIndex(vertex10); + mesh.addParamIndex(vertex11); + mesh.addParamIndex(vertex01); + } + if (createNormals) { + mesh.addNormalIndex(0); + mesh.addNormalIndex(0); + mesh.addNormalIndex(0); + mesh.addNormalIndex(0); + } + mesh.terminateFacet(false); + } + } + return mesh; +} + describe("ToleranceRatio", () => { it("ToleranceRatio works as expected", () => { expect(ToleranceRatio.vertex).toBe(0.1); @@ -78,8 +136,8 @@ describe("GeometryList", () => { const gp = new GraphicParams(); const dp = DisplayParams.createForLinear(gp); const origin = Point3d.create(1, 2, 3); - const polyface = Sample.createTriangularUnitGridPolyface(origin, - Vector3d.create(1, 0, 0), Vector3d.create(0, 2, 0), 4, 5, true, true, false); + const polyface = createTriangularUnitGridPolyface(origin, + Vector3d.create(1, 0, 0), Vector3d.create(0, 2, 0), 4, 5, true, true); const polyfaceG0 = Geometry.createFromPolyface(polyface, Transform.createIdentity(), polyface.range(), dp, undefined); glist0.push(polyfaceG0); verifyGeometryQueries(polyfaceG0, false, true, false); // maybe this has to change someday? @@ -93,7 +151,7 @@ describe("GeometryList", () => { expect(glist0.isEmpty).toBe(true); const gp = new GraphicParams(); const dp = DisplayParams.createForLinear(gp); - const loop = Loop.create(LineString3d.create(Sample.createUnitCircle(5))); + const loop = Loop.create(LineString3d.create(createUnitCircle(5))); const loopG0 = Geometry.createFromLoop(loop, Transform.createIdentity(), loop.range(), dp, false, undefined); glist0.push(loopG0); verifyGeometryQueries(loopG0, false, true, false); // maybe this has to change someday? diff --git a/core/geometry/src/bspline/BSplineCurve.ts b/core/geometry/src/bspline/BSplineCurve.ts index 05a2f64c16bd..410f2fd839db 100644 --- a/core/geometry/src/bspline/BSplineCurve.ts +++ b/core/geometry/src/bspline/BSplineCurve.ts @@ -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. @@ -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); diff --git a/core/geometry/src/bspline/BSplineCurveOps.ts b/core/geometry/src/bspline/BSplineCurveOps.ts index 75e6f18d4ace..660d459d3f77 100644 --- a/core/geometry/src/bspline/BSplineCurveOps.ts +++ b/core/geometry/src/bspline/BSplineCurveOps.ts @@ -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) } diff --git a/core/geometry/src/bspline/BezierCurve3d.ts b/core/geometry/src/bspline/BezierCurve3d.ts index 917bf0e54daf..33b5fa2570f4 100644 --- a/core/geometry/src/bspline/BezierCurve3d.ts +++ b/core/geometry/src/bspline/BezierCurve3d.ts @@ -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. @@ -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); @@ -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); diff --git a/core/geometry/src/bspline/BezierCurve3dH.ts b/core/geometry/src/bspline/BezierCurve3dH.ts index 52c008b076c4..ac35d13a8270 100644 --- a/core/geometry/src/bspline/BezierCurve3dH.ts +++ b/core/geometry/src/bspline/BezierCurve3dH.ts @@ -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, @@ -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 @@ -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)) @@ -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); @@ -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); diff --git a/core/geometry/src/bspline/BezierCurveBase.ts b/core/geometry/src/bspline/BezierCurveBase.ts index 8de0e6dd0b4d..ffe781f1acf2 100644 --- a/core/geometry/src/bspline/BezierCurveBase.ts +++ b/core/geometry/src/bspline/BezierCurveBase.ts @@ -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(); } diff --git a/core/geometry/src/clipping/AlternatingConvexClipTree.ts b/core/geometry/src/clipping/AlternatingConvexClipTree.ts index 142bb01f340c..3bbd6383b4b2 100644 --- a/core/geometry/src/clipping/AlternatingConvexClipTree.ts +++ b/core/geometry/src/clipping/AlternatingConvexClipTree.ts @@ -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; @@ -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; diff --git a/core/geometry/src/clipping/ClipPlane.ts b/core/geometry/src/clipping/ClipPlane.ts index 8a7dd26152a0..85d180b47eef 100644 --- a/core/geometry/src/clipping/ClipPlane.ts +++ b/core/geometry/src/clipping/ClipPlane.ts @@ -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. diff --git a/core/geometry/src/clipping/ClipPrimitive.ts b/core/geometry/src/clipping/ClipPrimitive.ts index de254df2d3a4..eff9e1c64f8b 100644 --- a/core/geometry/src/clipping/ClipPrimitive.ts +++ b/core/geometry/src/clipping/ClipPrimitive.ts @@ -269,11 +269,13 @@ export class ClipPrimitive implements Clipper { * (b) finite distance from origin. */ public containsZClip(): boolean { - if (this.fetchClipPlanesRef() !== undefined) - for (const convexSet of this._clipPlanes!.convexSets) + const clipPlanes = this.fetchClipPlanesRef(); + if (clipPlanes !== undefined) { + for (const convexSet of clipPlanes.convexSets) for (const plane of convexSet.planes) if (Math.abs(plane.inwardNormalRef.z) > 1.0e-6 && Math.abs(plane.distance) !== Number.MAX_VALUE) return true; + } return false; } /** @@ -553,7 +555,7 @@ export class ClipShape extends ClipPrimitive { blockPoints[1].x = blockPoints[2].x = high.x; blockPoints[0].y = blockPoints[1].y = blockPoints[4].y = low.y; blockPoints[2].y = blockPoints[3].y = high.y; - return ClipShape.createShape( + const shape = ClipShape.createShape( blockPoints, (ClipMaskXYZRangePlanes.None !== (clipMask & ClipMaskXYZRangePlanes.ZLow)) ? low.z : undefined, (ClipMaskXYZRangePlanes.None !== (clipMask & ClipMaskXYZRangePlanes.ZHigh)) ? high.z : undefined, @@ -561,7 +563,10 @@ export class ClipShape extends ClipPrimitive { isMask, invisible, result, - )!; + ); + // ClipShape.createShape only returns undefined if blockPoints.length < 3 which is not the case here + // so we shape is always defined and we never return the empty ClipShape + return shape ?? ClipShape.createEmpty(); } /** Creates a new ClipShape with undefined members and a polygon points array of zero length. */ public static createEmpty( @@ -610,6 +615,14 @@ export class ClipShape extends ClipPrimitive { this.parsePolygonPlanes(set, this._polygon, this.isMask); return true; } + private pushPlanes(planes: Array, dst: ClipPlane[]): boolean { + for (const plane of planes) { + if (!plane) + return false; + dst.push(plane); + } + return true; + } /** * Given a start and end point, populate the given UnionOfConvexClipPlaneSets with ConvexClipPlaneSets * defining the bounded region of linear planes. Returns true if successful. @@ -619,36 +632,38 @@ export class ClipShape extends ClipPrimitive { ): boolean { // Handles the degenerate case of 2 distinct points (used by select by line). const normal = start.vectorTo(end); - if (normal.magnitude() === 0.0) + if (!normal.normalize(normal)) return false; - normal.normalize(normal); const convexSet = ConvexClipPlaneSet.createEmpty(); if (cameraFocalLength === undefined) { const perpendicular = Vector2d.create(-normal.y, normal.x); - convexSet.planes.push( - ClipPlane.createNormalAndPoint(Vector3d.create(normal.x, normal.y), Point3d.createFrom(start), this._invisible)!, - ); - convexSet.planes.push( - ClipPlane.createNormalAndPoint(Vector3d.create(-normal.x, -normal.y), Point3d.createFrom(end), this._invisible)!, - ); - convexSet.planes.push( - ClipPlane.createNormalAndPoint(Vector3d.create(perpendicular.x, perpendicular.y), Point3d.createFrom(start), this._invisible)!, - ); - convexSet.planes.push( - ClipPlane.createNormalAndPoint(Vector3d.create(-perpendicular.x, -perpendicular.y), Point3d.createFrom(start), this._invisible)!, - ); + if (!this.pushPlanes([ + ClipPlane.createNormalAndPoint(Vector3d.create(normal.x, normal.y), Point3d.createFrom(start), this._invisible), + ClipPlane.createNormalAndPoint(Vector3d.create(-normal.x, -normal.y), Point3d.createFrom(end), this._invisible), + ClipPlane.createNormalAndPoint(Vector3d.create(perpendicular.x, perpendicular.y), Point3d.createFrom(start), this._invisible), + ClipPlane.createNormalAndPoint(Vector3d.create(-perpendicular.x, -perpendicular.y), Point3d.createFrom(start), this._invisible), + ], convexSet.planes)) + return false; } else { const start3d = Point3d.create(start.x, start.y, -cameraFocalLength); const end3d = Point3d.create(end.x, end.y, -cameraFocalLength); const vecEnd3d = Vector3d.createFrom(end3d); const perpendicular = vecEnd3d.crossProduct(Vector3d.createFrom(start3d)).normalize(); - let endNormal = Vector3d.createFrom(start3d).crossProduct(perpendicular!).normalize(); - convexSet.planes.push(ClipPlane.createNormalAndDistance(perpendicular!, 0.0, this._invisible)!); - convexSet.planes.push(ClipPlane.createNormalAndDistance(endNormal!, 0.0, this._invisible)!); - perpendicular!.negate(); - endNormal = vecEnd3d.crossProduct(perpendicular!).normalize(); - convexSet.planes.push(ClipPlane.createNormalAndDistance(perpendicular!, 0.0, this._invisible)!); - convexSet.planes.push(ClipPlane.createNormalAndDistance(endNormal!, 0.0, this._invisible)!); + if (undefined === perpendicular) + return false; + let endNormal = Vector3d.createFrom(start3d).crossProduct(perpendicular).normalize(); + if (undefined === endNormal || !this.pushPlanes([ + ClipPlane.createNormalAndDistance(perpendicular, 0.0, this._invisible), + ClipPlane.createNormalAndDistance(endNormal, 0.0, this._invisible), + ], convexSet.planes)) + return false; + perpendicular.negate(); + endNormal = vecEnd3d.crossProduct(perpendicular).normalize(); + if (undefined === endNormal || !this.pushPlanes([ + ClipPlane.createNormalAndDistance(perpendicular, 0.0, this._invisible), + ClipPlane.createNormalAndDistance(endNormal, 0.0, this._invisible), + ], convexSet.planes)) + return false; } convexSet.addZClipPlanes(this._invisible, this._zLow, this._zHigh); set.addConvexSet(convexSet); @@ -693,35 +708,49 @@ export class ClipShape extends ClipPrimitive { const nextPerpendicular = PolyEdge.makeUnitPerpendicularToBisector(edge, nextEdge, reverse); // Create three-sided fans from each edge. Note we could define the correct region // with only two planes for edge, but cannot then designate the "interior" status of the edges accurately. - - if (previousPerpendicular) - convexSet.planes.push(ClipPlane.createNormalAndPoint(previousPerpendicular, edge.pointA, this._invisible, true)!); - convexSet.planes.push(ClipPlane.createNormalAndPoint(edge.normal, edge.pointB, this._invisible, false)!); - if (nextPerpendicular) - convexSet.planes.push(ClipPlane.createNormalAndPoint(nextPerpendicular, nextEdge.pointA, this._invisible, true)!); + if (undefined === previousPerpendicular || !this.pushPlanes([ + ClipPlane.createNormalAndPoint(previousPerpendicular, edge.pointA, this._invisible, true), + ], convexSet.planes)) + return false; + if (!this.pushPlanes([ + ClipPlane.createNormalAndPoint(edge.normal, edge.pointB, this._invisible, false), + ], convexSet.planes)) + return false; + if (undefined === nextPerpendicular || !this.pushPlanes([ + ClipPlane.createNormalAndPoint(nextPerpendicular, nextEdge.pointA, this._invisible, true), + ], convexSet.planes)) + return false; set.addConvexSet(convexSet); set.addOutsideZClipSets(this._invisible, this._zLow, this._zHigh); } } else { const convexSet = ConvexClipPlaneSet.createEmpty(); if (cameraFocalLength === undefined) { - for (const edge of edges) - convexSet.planes.push(ClipPlane.createNormalAndPoint(Vector3d.create(edge.normal.x, edge.normal.y), edge.pointA)!); + for (const edge of edges) { + if (!this.pushPlanes([ + ClipPlane.createNormalAndPoint(Vector3d.create(edge.normal.x, edge.normal.y), edge.pointA), + ], convexSet.planes)) + return false; + } } else { - if (reverse) - for (const edge of edges) - convexSet.planes.push( - ClipPlane.createNormalAndDistance( - Vector3d.createFrom(edge.pointA).crossProduct(Vector3d.createFrom(edge.pointB)).normalize()!, 0.0, - )!, - ); - else - for (const edge of edges) - convexSet.planes.push( - ClipPlane.createNormalAndDistance( - Vector3d.createFrom(edge.pointB).crossProduct(Vector3d.createFrom(edge.pointA)).normalize()!, 0.0, - )!, - ); + let crossProduct: Vector3d | undefined; + if (reverse) { + for (const edge of edges) { + crossProduct = Vector3d.createFrom(edge.pointA).crossProduct(Vector3d.createFrom(edge.pointB)).normalize(); + if (undefined === crossProduct || !this.pushPlanes([ + ClipPlane.createNormalAndDistance(crossProduct, 0.0), + ], convexSet.planes)) + return false; + } + } else { + for (const edge of edges) { + crossProduct = Vector3d.createFrom(edge.pointB).crossProduct(Vector3d.createFrom(edge.pointA)).normalize(); + if (undefined === crossProduct || !this.pushPlanes([ + ClipPlane.createNormalAndDistance(crossProduct, 0.0), + ], convexSet.planes)) + return false; + } + } } convexSet.addZClipPlanes(this._invisible, this._zLow, this._zHigh); set.addConvexSet(convexSet); diff --git a/core/geometry/src/clipping/ClipVector.ts b/core/geometry/src/clipping/ClipVector.ts index 00a473ccfb71..2590251e463e 100644 --- a/core/geometry/src/clipping/ClipVector.ts +++ b/core/geometry/src/clipping/ClipVector.ts @@ -243,8 +243,10 @@ export class ClipVector implements Clipper { let invTrans = Transform.createIdentity(); if (firstClipShape.transformValid && clip.transformValid) { - fwdTrans = clip.transformFromClip!.clone(); - invTrans = firstClipShape.transformToClip!.clone(); + if (undefined === clip.transformFromClip || undefined === firstClipShape.transformToClip) + return []; + fwdTrans = clip.transformFromClip.clone(); + invTrans = firstClipShape.transformToClip.clone(); } deltaTrans.setFrom(invTrans.multiplyTransformTransform(fwdTrans)); } @@ -255,13 +257,13 @@ export class ClipVector implements Clipper { if (clip.polygon !== undefined) { clipM = ClipMaskXYZRangePlanes.XAndY; - if (clip.zHighValid) { + if (clip.zHighValid && undefined !== clip.zHigh) { clipM = clipM | ClipMaskXYZRangePlanes.ZHigh; - zFront = clip.zHigh!; + zFront = clip.zHigh; } - if (clip.zLowValid) { + if (clip.zLowValid && undefined !== clip.zLow) { clipM = clipM | ClipMaskXYZRangePlanes.ZLow; - zBack = clip.zLow!; + zBack = clip.zLow; } for (const point of clip.polygon) @@ -274,8 +276,8 @@ export class ClipVector implements Clipper { retVal.push(clipM); retVal.push(zBack); retVal.push(zFront); - if (transform && firstClipShape) - transform.setFrom(firstClipShape.transformFromClip!); + if (transform && firstClipShape && undefined !== firstClipShape.transformFromClip) + transform.setFrom(firstClipShape.transformFromClip); return retVal; } /** Sets this ClipVector and all of its members to the visibility specified. */ diff --git a/core/geometry/src/clipping/ConvexClipPlaneSet.ts b/core/geometry/src/clipping/ConvexClipPlaneSet.ts index ed928366be4f..4030bdab176f 100644 --- a/core/geometry/src/clipping/ConvexClipPlaneSet.ts +++ b/core/geometry/src/clipping/ConvexClipPlaneSet.ts @@ -130,21 +130,22 @@ export class ConvexClipPlaneSet implements Clipper, PolygonClipper { highZ: boolean = true, ): ConvexClipPlaneSet { const result = ConvexClipPlaneSet.createEmpty(); + let clipPlane: ClipPlane | undefined; - if (lowX) - result.planes.push(ClipPlane.createNormalAndPointXYZXYZ(1, 0, 0, range.low.x, 0, 0)!); - if (highX) - result.planes.push(ClipPlane.createNormalAndPointXYZXYZ(-1, 0, 0, range.high.x, 0, 0)!); + if (lowX && (clipPlane = ClipPlane.createNormalAndPointXYZXYZ(1, 0, 0, range.low.x, 0, 0)) !== undefined) + result.planes.push(clipPlane); + if (highX && (clipPlane = ClipPlane.createNormalAndPointXYZXYZ(-1, 0, 0, range.high.x, 0, 0)) !== undefined) + result.planes.push(clipPlane); - if (lowY) - result.planes.push(ClipPlane.createNormalAndPointXYZXYZ(0, 1, 0, 0, range.low.y, 0)!); - if (highY) - result.planes.push(ClipPlane.createNormalAndPointXYZXYZ(0, -1, 0, 0, range.high.y, 0)!); + if (lowY && (clipPlane = ClipPlane.createNormalAndPointXYZXYZ(0, 1, 0, 0, range.low.y, 0)) !== undefined) + result.planes.push(clipPlane); + if (highY && (clipPlane = ClipPlane.createNormalAndPointXYZXYZ(0, -1, 0, 0, range.high.y, 0)) !== undefined) + result.planes.push(clipPlane); - if (lowZ) - result.planes.push(ClipPlane.createNormalAndPointXYZXYZ(0, 0, 1, 0, 0, range.low.z)!); - if (highZ) - result.planes.push(ClipPlane.createNormalAndPointXYZXYZ(0, 0, -1, 0, 0, range.high.z)!); + if (lowZ && (clipPlane = ClipPlane.createNormalAndPointXYZXYZ(0, 0, 1, 0, 0, range.low.z)) !== undefined) + result.planes.push(clipPlane); + if (highZ && (clipPlane = ClipPlane.createNormalAndPointXYZXYZ(0, 0, -1, 0, 0, range.high.z)) !== undefined) + result.planes.push(clipPlane); return result; } @@ -248,9 +249,9 @@ export class ConvexClipPlaneSet implements Clipper, PolygonClipper { let x1, y1, nx, ny; frustum._planes.length = 0; const z0 = points.getZAtUncheckedPointIndex(i0); // z for planes can stay fixed - const planeNormal = points.crossProductIndexIndexIndex(0, 2, 1)!; - ClipPlane.createNormalAndPointXYZXYZ(planeNormal.x, planeNormal.y, planeNormal.z, x0, y0, z0, false, false, planeOfPolygon); - if (planeNormal.normalizeInPlace()) { + const planeNormal = points.crossProductIndexIndexIndex(0, 2, 1); + if (planeNormal?.normalizeInPlace()) { + ClipPlane.createNormalAndPointXYZXYZ(planeNormal.x, planeNormal.y, planeNormal.z, x0, y0, z0, false, false, planeOfPolygon); for (let i1 = 0; i1 < n; i1++, x0 = x1, y0 = y1) { x1 = points.getXAtUncheckedPointIndex(i1); y1 = points.getYAtUncheckedPointIndex(i1); @@ -709,7 +710,7 @@ export class ConvexClipPlaneSet implements Clipper, PolygonClipper { * @param transform (optional) transform to apply to the accepted points. * @param testContainment if true, test each point to see if it is within the convex set. (send false if confident * that the convex set is rectilinear set such as a slab. Send true if chiseled corners are possible). - * @returns number of points. + * @returns number of points. If computation fails, return 0. */ public computePlanePlanePlaneIntersections( points: Point3d[] | undefined, @@ -730,7 +731,9 @@ export class ConvexClipPlaneSet implements Clipper, PolygonClipper { allPlanes[k].inwardNormalRef.x, allPlanes[k].inwardNormalRef.y, allPlanes[k].inwardNormalRef.z, normalRows); if (normalRows.computeCachedInverse(false)) { - const xyz = normalRows.multiplyInverseXYZAsPoint3d(allPlanes[i].distance, allPlanes[j].distance, allPlanes[k].distance)!; + const xyz = normalRows.multiplyInverseXYZAsPoint3d(allPlanes[i].distance, allPlanes[j].distance, allPlanes[k].distance); + if (undefined === xyz) + return 0; if (!testContainment || this.isPointOnOrInside(xyz, Geometry.smallMetricDistance)) { numPoints++; if (transform) @@ -761,10 +764,13 @@ export class ConvexClipPlaneSet implements Clipper, PolygonClipper { * @param zHigh high z value. The plane clips out points with z above this. */ public addZClipPlanes(invisible: boolean, zLow?: number, zHigh?: number) { - if (zLow !== undefined) - this._planes.push(ClipPlane.createNormalAndDistance(Vector3d.create(0, 0, 1), zLow, invisible)!); - if (zHigh !== undefined) - this._planes.push(ClipPlane.createNormalAndDistance(Vector3d.create(0, 0, -1), -zHigh, invisible)!); + let clipPlane: ClipPlane | undefined; + if (zLow !== undefined && + (clipPlane = ClipPlane.createNormalAndDistance(Vector3d.create(0, 0, 1), zLow, invisible)) !== undefined) + this._planes.push(clipPlane); + if (zHigh !== undefined && + (clipPlane = ClipPlane.createNormalAndDistance(Vector3d.create(0, 0, -1), -zHigh, invisible)) !== undefined) + this._planes.push(clipPlane); } /** * Implement appendPolygonClip, as defined in interface PolygonClipper. @@ -812,7 +818,8 @@ export class ConvexClipPlaneSet implements Clipper, PolygonClipper { for (visitor.reset(); visitor.moveToNextFacet();) { if (undefined !== PolygonOps.areaNormalGo(visitor.point, normal)) { normal.scaleInPlace(scale); - if (undefined !== Plane3dByOriginAndUnitNormal.create(visitor.point.front()!, normal, plane)) + const front = visitor.point.front(); + if (undefined !== front && undefined !== Plane3dByOriginAndUnitNormal.create(front, normal, plane)) result.addPlaneToConvexSet(plane); } } diff --git a/core/geometry/src/clipping/internalContexts/LineStringOffsetClipperContext.ts b/core/geometry/src/clipping/internalContexts/LineStringOffsetClipperContext.ts index b9e504006626..ed8983fd0224 100644 --- a/core/geometry/src/clipping/internalContexts/LineStringOffsetClipperContext.ts +++ b/core/geometry/src/clipping/internalContexts/LineStringOffsetClipperContext.ts @@ -168,7 +168,10 @@ export class LineStringOffsetClipperContext { const context = new LineStringOffsetClipperContext(positiveOffsetLeft, positiveOffsetRight); const result = UnionOfConvexClipPlaneSets.createEmpty(); if (points.length > 1) { - const closed = Geometry.isSmallMetricDistance(points.distanceIndexIndex(0, points.length - 1)!); + const distance = points.distanceIndexIndex(0, points.length - 1); + if (undefined === distance) + return result; + const closed = Geometry.isSmallMetricDistance(distance); for (let i = 0; i + 1 < points.length; i++) { const unitVectorA = this.createUnit(points, i - 1, closed); const unitVectorB = this.createUnit(points, i, closed); diff --git a/core/geometry/src/core-geometry.ts b/core/geometry/src/core-geometry.ts index 94e9021162df..088e90ffaf57 100644 --- a/core/geometry/src/core-geometry.ts +++ b/core/geometry/src/core-geometry.ts @@ -257,6 +257,5 @@ export * from "./polyface/TaggedNumericData"; export * from "./topology/SpaceTriangulation"; export * from "./serialization/IModelJsonSchema"; export * from "./serialization/DeepCompare"; -export * from "./serialization/GeometrySamples"; export * from "./serialization/SerializationHelpers"; export { BentleyGeometryFlatBuffer } from "./serialization/BentleyGeometryFlatBuffer"; diff --git a/core/geometry/src/curve/Arc3d.ts b/core/geometry/src/curve/Arc3d.ts index f8a9642f1893..1ba87373905a 100644 --- a/core/geometry/src/curve/Arc3d.ts +++ b/core/geometry/src/curve/Arc3d.ts @@ -512,8 +512,8 @@ export class Arc3d extends CurvePrimitive implements BeJSONFunctions { const center = start.plusScaled(vector0, radius); // reverse the A-to-center vector and bring it up to scale vector0.scaleInPlace(-radius); - const vector90 = tangentAtStart.scaleToLength(Math.abs(radius))!; // cannot fail; prior unitCrossProduct would have failed first - return Arc3d.create(center, vector0, vector90, AngleSweep.create(sweep)); + const vector90 = tangentAtStart.scaleToLength(Math.abs(radius)); // cannot fail; prior unitCrossProduct would have failed first + return (vector90 !== undefined) ? Arc3d.create(center, vector0, vector90, AngleSweep.create(sweep)) : undefined; } /** * Create a circular arc defined by start and end points and radius. @@ -942,7 +942,7 @@ export class Arc3d extends CurvePrimitive implements BeJSONFunctions { const arcToView = Matrix3d.createColumns(this.matrixRef.getColumn(0), this.matrixRef.getColumn(1), options.vectorToEye); centerToLocalPoint = arcToView.multiplyInverse(centerToPoint); } else { - centerToLocalPoint = this.matrixRef.multiplyInverse(centerToPoint)!; + centerToLocalPoint = this.matrixRef.multiplyInverse(centerToPoint); } if (centerToLocalPoint === undefined) return; diff --git a/core/geometry/src/curve/CurveChainWithDistanceIndex.ts b/core/geometry/src/curve/CurveChainWithDistanceIndex.ts index 319800bff1e5..0a478fc8da2c 100644 --- a/core/geometry/src/curve/CurveChainWithDistanceIndex.ts +++ b/core/geometry/src/curve/CurveChainWithDistanceIndex.ts @@ -103,6 +103,7 @@ export class PathFragment { /** * Convert distance to local fraction and apply that to interpolate between the stored curve fractions. * Note that proportional calculation does NOT account for non-uniform parameterization in the child curve. + * Return 0 if conversion failed. */ public chainDistanceToInterpolatedChildFraction(distance: number): number { return Geometry.inverseInterpolate( @@ -112,7 +113,7 @@ export class PathFragment { this.chainDistance1, distance, this.childFraction0, - )!; // the interval must have nonzero length so division should be safe + ) ?? 0; } /** * Convert the given chainDistance to a fraction along this childCurve using `moveSignedDistanceFromFraction`. @@ -582,10 +583,13 @@ export class CurveChainWithDistanceIndex extends CurvePrimitive { * @param fraction fractional position along the geometry. * @param result optional receiver for the result. * @returns a ray whose origin is the curve point and direction is the derivative with respect to the fraction. + * If calculation failed, a zero ray will be returned. */ public fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d { const distanceAlongPath = fraction * this._totalLength; - const fragment = this.chainDistanceToFragment(distanceAlongPath, true)!; + const fragment = this.chainDistanceToFragment(distanceAlongPath, true); + if (undefined === fragment) + return Ray3d.createZero(); const childFraction = fragment.chainDistanceToAccurateChildFraction(distanceAlongPath, true); result = fragment.childCurve.fractionToPointAndDerivative(childFraction, result); // Recall the standard arclength formula s(t) for the curve C = C(t), with derivative s'(t) = ||C'||. @@ -606,11 +610,13 @@ export class CurveChainWithDistanceIndex extends CurvePrimitive { * @param fraction fractional position on the curve * @param result optional receiver for the result. * @returns a ray whose origin is the curve point and direction is the normalized derivative with respect to - * the fraction. + * the fraction. If calculation failed, a zero ray will be returned. */ public override fractionToPointAndUnitTangent(fraction: number, result?: Ray3d): Ray3d { const distanceAlongPath = fraction * this._totalLength; - const fragment = this.chainDistanceToFragment(distanceAlongPath, true)!; + const fragment = this.chainDistanceToFragment(distanceAlongPath, true); + if (undefined === fragment) + return Ray3d.createZero(); const childFraction = fragment.chainDistanceToAccurateChildFraction(distanceAlongPath, true); result = fragment.childCurve.fractionToPointAndDerivative(childFraction, result); result.direction.normalizeInPlace(); @@ -628,7 +634,9 @@ export class CurveChainWithDistanceIndex extends CurvePrimitive { fraction: number, result?: Plane3dByOriginAndVectors, ): Plane3dByOriginAndVectors | undefined { const distanceAlongPath = fraction * this._totalLength; - const fragment = this.chainDistanceToFragment(distanceAlongPath, true)!; + const fragment = this.chainDistanceToFragment(distanceAlongPath, true); + if (undefined === fragment) + return undefined; const childFraction = fragment.chainDistanceToAccurateChildFraction(distanceAlongPath, true); result = fragment.childCurve.fractionToPointAnd2Derivatives(childFraction, result); if (!result) @@ -689,13 +697,16 @@ export class CurveChainWithDistanceIndex extends CurvePrimitive { * * See `CurvePrimitive.moveSignedDistanceFromFraction` for parameter details. * * The returned location directly identifies fractional position along the CurveChainWithDistanceIndex and * has pointer to an additional detail for the child curve. + * * If calculation failed, return default CurveLocationDetail via `CurveLocationDetail.create()`. */ public override moveSignedDistanceFromFraction( startFraction: number, signedDistance: number, allowExtension: boolean, result?: CurveLocationDetail, ): CurveLocationDetail { const distanceA = startFraction * this._totalLength; const distanceB = distanceA + signedDistance; - const fragmentB = this.chainDistanceToFragment(distanceB, true)!; + const fragmentB = this.chainDistanceToFragment(distanceB, true); + if (undefined === fragmentB) + return CurveLocationDetail.create(); const childDetail = fragmentB.childCurve.moveSignedDistanceFromFraction( fragmentB.childFraction0, distanceB - fragmentB.chainDistance0, allowExtension, result?.childDetail, ); // local detail related to the child curve diff --git a/core/geometry/src/curve/CurveCollection.ts b/core/geometry/src/curve/CurveCollection.ts index 19c411311840..f167e4f9df88 100644 --- a/core/geometry/src/curve/CurveCollection.ts +++ b/core/geometry/src/curve/CurveCollection.ts @@ -89,8 +89,11 @@ export abstract class CurveCollection extends GeometryQuery { const detailB = new CurveLocationDetail(); if (this.children !== undefined) { for (const child of this.children) { - if (child.closestPoint(spacePoint, false, detailB)) - detailA = result = CurveLocationDetail.chooseSmallerA(detailA, detailB)!.clone(result); + if (child.closestPoint(spacePoint, false, detailB)) { + const smaller = CurveLocationDetail.chooseSmallerA(detailA, detailB); + if (undefined !== smaller) + detailA = result = smaller.clone(result); + } } } return detailA; @@ -598,8 +601,11 @@ export class BagOfCurves extends CurveCollection { const detailB = new CurveLocationDetail(); if (this.children !== undefined) { for (const child of this.children) { - if (child.closestPoint(spacePoint, extend, detailB)) - detailA = result = CurveLocationDetail.chooseSmallerA(detailA, detailB)!.clone(result); + if (child.closestPoint(spacePoint, extend, detailB)) { + const smaller = CurveLocationDetail.chooseSmallerA(detailA, detailB); + if (undefined !== smaller) + detailA = result = smaller.clone(result); + } } } return detailA; diff --git a/core/geometry/src/curve/CurveFactory.ts b/core/geometry/src/curve/CurveFactory.ts index 428835bc3124..67511573e97a 100644 --- a/core/geometry/src/curve/CurveFactory.ts +++ b/core/geometry/src/curve/CurveFactory.ts @@ -156,14 +156,14 @@ export class CurveFactory { const n = points.length; if (n <= 1) return undefined; - const pointA = points.getPoint3dAtCheckedPointIndex(0)!; - const pointB = points.getPoint3dAtCheckedPointIndex(1)!; + const pointA = points.getPoint3dAtUncheckedPointIndex(0); + const pointB = points.getPoint3dAtUncheckedPointIndex(1); // remark: n=2 and n=3 cases should fall out from loop logic const blendArray: ArcBlendData[] = []; // build one-sided blends at each end . . blendArray.push({ fraction10: 0.0, fraction12: 0.0, point: pointA.clone() }); for (let i = 1; i + 1 < n; i++) { - const pointC = points.getPoint3dAtCheckedPointIndex(i + 1)!; + const pointC = points.getPoint3dAtUncheckedPointIndex(i + 1); let thisRadius = 0; if (Array.isArray(radius)) { if (i < radius.length) @@ -634,7 +634,9 @@ export class CurveFactory { const altitudeSpiralEnd = midPlanePerpendicularVector.dotProductStartEnd(startPoint, spiralARefLength.endPoint()); const scaleFactor = altitudeB / altitudeSpiralEnd; const spiralA = IntegratedSpiral3d.createFrom4OutOf5(spiralType, 0.0, undefined, - Angle.createRadians(0), Angle.createRadians(spiralTurnRadians), referenceLength * scaleFactor, undefined, frameA)!; + Angle.createRadians(0), Angle.createRadians(spiralTurnRadians), referenceLength * scaleFactor, undefined, frameA); + if (undefined === spiralA) + return undefined; const distanceAB = vectorAB.magnitude(); const vectorBC = Vector3d.createStartEnd(shoulderPoint, targetPoint); vectorBC.scaleToLength(distanceAB, vectorBC); @@ -642,7 +644,9 @@ export class CurveFactory { const axesC = Matrix3d.createRotationAroundAxisIndex(AxisIndex.Z, Angle.createRadians(radiansBC + Math.PI)); const frameC = Transform.createRefs(pointC, axesC); const spiralC = IntegratedSpiral3d.createFrom4OutOf5(spiralType, - 0, -spiralA.radius01.x1, Angle.zero(), undefined, spiralA.curveLength(), Segment1d.create(1, 0), frameC)!; + 0, -spiralA.radius01.x1, Angle.zero(), undefined, spiralA.curveLength(), Segment1d.create(1, 0), frameC); + if (undefined === spiralC) + return undefined; return [spiralA, spiralC]; } return undefined; @@ -693,7 +697,9 @@ export class CurveFactory { spiralType, 0, undefined, Angle.zero(), Angle.createRadians(spiralTurnRadians), spiralLength, undefined, frameA, - )!; + ); + if (undefined === spiralAB) + return undefined; const axesB = Matrix3d.createRotationAroundAxisIndex(AxisIndex.Z, Angle.createRadians(radiansCB)); const frameBOrigin = pointC.interpolate(xFractionCB, pointB); const frameB = Transform.createRefs(frameBOrigin, axesB); @@ -701,7 +707,9 @@ export class CurveFactory { spiralType, 0, undefined, Angle.zero(), Angle.createRadians(-spiralTurnRadians), spiralLength, undefined, frameB, - )!; + ); + if (undefined === spiralBC) + return undefined; return [spiralAB, spiralBC]; } } @@ -740,9 +748,11 @@ export class CurveFactory { const radiusA = sideA * Math.abs(arcRadius); const radiusB = sideB * Math.abs(arcRadius); const spiralA = IntegratedSpiral3d.createFrom4OutOf5(spiralType, - 0, radiusA, Angle.zero(), undefined, lengthA, undefined, Transform.createIdentity())!; + 0, radiusA, Angle.zero(), undefined, lengthA, undefined, Transform.createIdentity()); const spiralB = IntegratedSpiral3d.createFrom4OutOf5(spiralType, - 0, radiusB, Angle.zero(), undefined, lengthB, undefined, Transform.createIdentity())!; + 0, radiusB, Angle.zero(), undefined, lengthB, undefined, Transform.createIdentity()); + if (undefined === spiralA || undefined === spiralB) + return undefined; const spiralEndA = spiralA.fractionToPointAndUnitTangent(1.0); const spiralEndB = spiralB.fractionToPointAndUnitTangent(1.0); // From the end of spiral, step away to arc center (and this is in local coordinates of each spiral) @@ -770,7 +780,9 @@ export class CurveFactory { const sweep = rayA1.direction.angleToXY(rayB0.direction); if (radiusA < 0) sweep.setRadians(- sweep.radians); - const arc = CurveFactory.createArcPointTangentRadius(rayA1.origin, rayA1.direction, radiusA, undefined, sweep)!; + const arc = CurveFactory.createArcPointTangentRadius(rayA1.origin, rayA1.direction, radiusA, undefined, sweep); + if (undefined === arc) + return undefined; return [spiralA, arc, spiralB]; } return undefined; diff --git a/core/geometry/src/curve/CurveLocationDetail.ts b/core/geometry/src/curve/CurveLocationDetail.ts index 3810735b9027..d19c0242c354 100644 --- a/core/geometry/src/curve/CurveLocationDetail.ts +++ b/core/geometry/src/curve/CurveLocationDetail.ts @@ -116,6 +116,10 @@ export class CurveLocationDetail { public get hasFraction1(): boolean { return this.fraction1 !== undefined; } + /** Test if this detail defines an interval. Preferable to [[CurveLocationDetail.hasFraction1]]. */ + public isInterval(): this is {fraction1: number, point1: Point3d} { + return this.fraction1 !== undefined && this.point1 !== undefined; + } /** Test if this is an isolated point. This is true if intervalRole is any of (undefined, isolated, isolatedAtVertex). */ public get isIsolated(): boolean { return this.intervalRole === undefined @@ -374,11 +378,14 @@ export class CurveLocationDetail { } } /** - * Return the fraction where f falls between fraction and fraction1. - * * ASSUME fraction1 defined + * Return the fraction where f falls between `fraction` and `fraction1`. + * Returns `defaultFraction` if calculation fails. + * * ASSUME fraction1 defined. */ public inverseInterpolateFraction(f: number, defaultFraction: number = 0): number { - const a = Geometry.inverseInterpolate01(this.fraction, this.fraction1!, f); + if (this.fraction1 === undefined) + return defaultFraction; + const a = Geometry.inverseInterpolate01(this.fraction, this.fraction1, f); if (a === undefined) return defaultFraction; return a; diff --git a/core/geometry/src/curve/CurvePrimitive.ts b/core/geometry/src/curve/CurvePrimitive.ts index ee062c62c005..d1faf0923234 100644 --- a/core/geometry/src/curve/CurvePrimitive.ts +++ b/core/geometry/src/curve/CurvePrimitive.ts @@ -188,7 +188,9 @@ export abstract class CurvePrimitive extends GeometryQuery { * @param fraction fractional position on the curve */ public fractionToCurvature(fraction: number): number | undefined { - const data = this.fractionToPointAnd2Derivatives(fraction)!; + const data = this.fractionToPointAnd2Derivatives(fraction); + if (!data) + return undefined; const cross = data.vectorU.crossProduct(data.vectorV); const a = cross.magnitude(); const b = data.vectorU.magnitude(); diff --git a/core/geometry/src/curve/LineString3d.ts b/core/geometry/src/curve/LineString3d.ts index cd235343abf8..15889e155723 100644 --- a/core/geometry/src/curve/LineString3d.ts +++ b/core/geometry/src/curve/LineString3d.ts @@ -178,7 +178,13 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public static createCapture(points: GrowableXYZArray): LineString3d { return new LineString3d(points); } - /** Create a linestring from `XAndY` points, with a specified z applied to all. */ + /** + * Create a linestring from `XAndY` points, with a specified z applied to all. + * @param points array of 2D points. + * @param z the z-coordinate to apply to all points. + * @param enforceClosure set the coordinates of the last point to those of the first point if both points are + * within [[Geometry.smallMetricDistance]]. + */ public static createXY(points: XAndY[], z: number, enforceClosure: boolean = false): LineString3d { const result = new LineString3d(); const xyz = result._points; @@ -186,12 +192,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { xyz.pushXYZ(xy.x, xy.y, z); } if (enforceClosure && points.length > 1) { - const distance = xyz.distanceIndexIndex(0, xyz.length - 1); - if (distance !== undefined && distance !== 0.0) { + const distance = xyz.distanceUncheckedIndexIndex(0, xyz.length - 1); + if (distance !== 0.0) { if (Geometry.isSameCoordinate(0, distance)) { - xyz.pop(); // nonzero but small distance -- to be replaced by point 0 exactly. - const xyzA = xyz.front(); - xyz.push(xyzA!); + xyz.transferFromGrowableXYZArray(points.length - 1, xyz, 0); } } } @@ -526,12 +530,15 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (n === 1) return Point3d.createFrom(this._points.getPoint3dAtUncheckedPointIndex(0), result); const df = 1.0 / (n - 1); - if (fraction <= df) - return this._points.interpolate(0, fraction / df, 1, result)!; - if (fraction + df >= 1.0) - return this._points.interpolate(n - 1, (1.0 - fraction) / df, n - 2, result)!; + if (fraction <= df) { + return this._points.interpolate(0, fraction / df, 1, result) ?? Point3d.createZero(); + } + if (fraction + df >= 1.0) { + const interpolatedPoint = this._points.interpolate(n - 1, (1.0 - fraction) / df, n - 2, result); + return interpolatedPoint ?? Point3d.createZero(); + } const index0 = Math.floor(fraction / df); - return this._points.interpolate(index0, (fraction - index0 * df) / df, index0 + 1, result)!; + return this._points.interpolate(index0, (fraction - index0 * df) / df, index0 + 1, result) ?? Point3d.createZero(); } /** * Evaluate a point a fractional position and derivative with respect to fraction along this linestring. @@ -633,7 +640,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public globalFractionToSegmentIndexAndLocalFraction(globalFraction: number): { index: number, fraction: number } { return LineString3d.mapGlobalToLocalFraction(globalFraction, this._points.length - 1); } - /** Return a frenet frame, using nearby points to estimate a plane. */ + /** + * Return a frenet frame, using nearby points to estimate a plane. + * Return a zero transform if the input calculation fails. + */ public override fractionToFrenetFrame(fraction: number, result?: Transform): Transform { const n = this._points.length; if (n <= 1) { @@ -641,10 +651,15 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { return Transform.createTranslation(this._points.getPoint3dAtUncheckedPointIndex(0), result); return Transform.createIdentity(result); } - if (n === 2) + if (n === 2) { + const vec = this._points.vectorIndexIndex(0, 1); + if (undefined === vec) + return Transform.createZero(); return Transform.createRefs( this._points.interpolate(0, fraction, 1), - Matrix3d.createRigidHeadsUp(this._points.vectorIndexIndex(0, 1)!, AxisOrder.XYZ)); + Matrix3d.createRigidHeadsUp(vec, AxisOrder.XYZ), + ); + } /** 3 or more points. */ const numSegment = n - 1; const df = 1.0 / numSegment; @@ -660,8 +675,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { baseIndex = Math.floor(fraction / df); localFraction = fraction * numSegment - baseIndex; } - const origin = this._points.interpolate(baseIndex, localFraction, baseIndex + 1)!; - const vectorA = this._points.vectorIndexIndex(baseIndex, baseIndex + 1)!; + const origin = this._points.interpolate(baseIndex, localFraction, baseIndex + 1); + const vectorA = this._points.vectorIndexIndex(baseIndex, baseIndex + 1); + if (undefined === vectorA || undefined === origin) + return Transform.createZero(); // tricky stuff to handle colinear points. But if vectorA is zero it is still a mess . .. const normal = Vector3d.create(); const workVector = Vector3d.create(); @@ -690,6 +707,13 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { return this._points.getPoint3dAtUncheckedPointIndex(i, result); return undefined; } + /** + * Return the point for the given index. + * * This method does not check for index validity. Use [[LineString3d.pointAt]] to have validity test. + */ + public uncheckedPointAt(i: number, result?: Point3d): Point3d { + return this._points.getPoint3dAtUncheckedPointIndex(i, result); + } /** If i and j are both valid indices, return the vector from point i to point j */ public vectorBetween(i: number, j: number, result?: Vector3d): Vector3d | undefined { return this._points.vectorIndexIndex(i, j, result); @@ -762,8 +786,12 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public override curveLength(): number { return this._points.sumLengths(); } - /** Sum the lengths of segments between fractional positions on a linestring. */ + /** + * Sum the lengths of segments between fractional positions on a linestring. + * Return 0 if inputs are invalid or calculation fails. + */ public override curveLengthBetweenFractions(fraction0: number, fraction1: number): number { + // TODO: lint fix changes in this function must be verified const numSegments = this._points.length - 1; if (fraction1 === fraction0 || numSegments < 1) return 0.0; @@ -777,16 +805,25 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const localFraction1 = scaledFraction1 - index1; if (index0 > index1) { // the interval is entirely within a single segment - return Math.abs(scaledFraction1 - scaledFraction0) * this._points.distanceIndexIndex(index0 - 1, index0)!; + const distance = this._points.distanceIndexIndex(index0 - 1, index0); + if (undefined !== distance) + return Math.abs(scaledFraction1 - scaledFraction0) * distance; } else { // there is leading partial interval, 0 or more complete segments, and a trailing partial interval. // (either or both partial may be zero length) - let sum = localFraction0 * this._points.distanceIndexIndex(index0 - 1, index0)! - + localFraction1 * (this._points.distanceIndexIndex(index1, index1 + 1))!; - for (let i = index0; i < index1; i++) - sum += this._points.distanceIndexIndex(i, i + 1)!; - return sum; + const distance0 = this._points.distanceIndexIndex(index0 - 1, index0); + const distance1 = this._points.distanceIndexIndex(index1, index1 + 1); + if (undefined !== distance0 && undefined !== distance1) { + let sum = localFraction0 * distance0 + localFraction1 * distance1; + for (let i = index0; i < index1; i++) { + const d = this._points.distanceIndexIndex(i, i + 1); + if (undefined !== d) + sum += d; + } + return sum; + } } + return 0.0; } /** Compute the range of points between fractional positions on the linestring. */ public override rangeBetweenFractions(fraction0: number, fraction1: number, transform?: Transform): Range3d { @@ -820,10 +857,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { * * Find the segment that contains the start fraction * * Move point-by-point from that position to the start or end (respectively for negative or positive signedDistance) * * Optionally extrapolate - * @param startFraction - * @param signedDistance - * @param allowExtension - * @param result + * If calculation failed, return default CurveLocationDetail via `CurveLocationDetail.create()`. */ public override moveSignedDistanceFromFraction( startFraction: number, signedDistance: number, allowExtension: false, result?: CurveLocationDetail, @@ -832,10 +866,11 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const scaledFraction = startFraction * numSegments; let leftPointIndex = Geometry.restrictToInterval(Math.floor(scaledFraction), 0, numSegments - 1); // lower point index on active segment. const localFraction = scaledFraction - leftPointIndex; - const point0 = this._points.interpolate(leftPointIndex, localFraction, leftPointIndex + 1, LineString3d._workPointA)!; + const point0 = this._points.interpolate(leftPointIndex, localFraction, leftPointIndex + 1, LineString3d._workPointA); const point1 = LineString3d._workPointB; + if (undefined === point0) + return CurveLocationDetail.create(); const context = new MoveByDistanceContext(point0, startFraction, signedDistance); - if (signedDistance > 0.0) { for (; leftPointIndex <= numSegments;) { leftPointIndex++; @@ -1009,7 +1044,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { let add = true; const addFraction = (fraction !== undefined) && (this._fractions !== undefined); if (n > 0) { - if (addFraction && Geometry.isSmallRelative(fraction - this._fractions!.back())) + if (addFraction && undefined !== this._fractions && Geometry.isSmallRelative(fraction - this._fractions.back())) add = false; if (point.isAlmostEqual(this._points.getPoint3dAtUncheckedPointIndex(n - 1))) add = false; @@ -1224,7 +1259,8 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (options && options.hasMaxEdgeLength) { numStroke = 0; for (let i = 1; i < numPoints; i++) { - numStroke += options.applyMaxEdgeLength(1, this._points.distanceIndexIndex(i - 1, i)!); + const distance = this._points.distanceUncheckedIndexIndex(i - 1, i); + numStroke += options.applyMaxEdgeLength(1, distance); } } return numStroke; @@ -1239,7 +1275,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const applyOptions = options !== undefined && options.hasMaxEdgeLength; const myData = StrokeCountMap.createWithCurvePrimitiveAndOptionalParent(this, parentStrokeMap, []); for (let i = 1; i < numPoints; i++) { - const segmentLength = this._points.distanceIndexIndex(i - 1, i)!; + const segmentLength = this._points.distanceUncheckedIndexIndex(i - 1, i); const numStrokeOnSegment = applyOptions ? options.applyMaxEdgeLength(1, segmentLength) : 1; myData.addToCountAndLength(numStrokeOnSegment, segmentLength); } @@ -1357,10 +1393,19 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public getIndexedSegment(index: number, result?: LineSegment3d): LineSegment3d | undefined { if (index >= 0 && index + 1 < this._points.length) return LineSegment3d.create( - this._points.getPoint3dAtCheckedPointIndex(index)!, this._points.getPoint3dAtCheckedPointIndex(index + 1)!, result, + this._points.getPoint3dAtUncheckedPointIndex(index), this._points.getPoint3dAtUncheckedPointIndex(index + 1), result, ); return undefined; } + /** + * Return (if possible) a specific segment of the linestring. + * * This method does not check for index validity. Use [[LineString3d.getIndexedSegment]] to have validity test. + */ + public getUncheckedIndexedSegment(index: number, result?: LineSegment3d): LineSegment3d { + return LineSegment3d.create( + this._points.getPoint3dAtUncheckedPointIndex(index), this._points.getPoint3dAtUncheckedPointIndex(index + 1), result, + ); + } /** * Whether the start and end points are defined and within tolerance. * * Does not check for planarity or degeneracy. @@ -1371,8 +1416,8 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (!this._points.length) return false; if (xyOnly) - return this._points.almostEqualXYIndexIndex(0, this._points.length - 1, tolerance)!; // we know the indices are valid - return this._points.almostEqualIndexIndex(0, this._points.length - 1, tolerance)!; + return this._points.almostEqualXYUncheckedIndexIndex(0, this._points.length - 1, tolerance); + return this._points.almostEqualUncheckedIndexIndex(0, this._points.length - 1, tolerance); } /** Returns true if first and last points are within metric tolerance. */ public get isPhysicallyClosed(): boolean { @@ -1408,11 +1453,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const fraction = i / m; const outputFraction = segmentMap.fractionToA(fraction); destLinestring.addPoint(pointA.interpolate(fraction, pointB, pointC)); - if (needFractions) - destLinestring._fractions!.push((outputFraction)); - if (needDerivatives) - destLinestring._derivatives!.push(vectorAB); - + if (needFractions && undefined !== destLinestring._fractions) + destLinestring._fractions.push((outputFraction)); + if (needDerivatives && undefined !== destLinestring._derivatives) + destLinestring._derivatives.push(vectorAB); } } } diff --git a/core/geometry/src/curve/Path.ts b/core/geometry/src/curve/Path.ts index 7e5a451d31db..1a20300b80cb 100644 --- a/core/geometry/src/curve/Path.ts +++ b/core/geometry/src/curve/Path.ts @@ -93,8 +93,11 @@ export class Path extends CurveChain { const child = this.children[i]; // head only extends at start; tail, only at end. NOTE: child may be both head and tail! const mode0 = (i === 0) ? CurveExtendOptions.resolveVariantCurveExtendParameterToCurveExtendMode(extend, 0) : CurveExtendMode.None; const mode1 = (i === this.children.length - 1) ? CurveExtendOptions.resolveVariantCurveExtendParameterToCurveExtendMode(extend, 1) : CurveExtendMode.None; - if (child.closestPoint(spacePoint, [mode0, mode1], detailB)) - detailA = result = CurveLocationDetail.chooseSmallerA(detailA, detailB)!.clone(result); + if (child.closestPoint(spacePoint, [mode0, mode1], detailB)) { + const smaller = CurveLocationDetail.chooseSmallerA(detailA, detailB); + if (undefined !== smaller) + detailA = result = smaller.clone(result); + } } } return detailA; diff --git a/core/geometry/src/curve/Query/InOutTests.ts b/core/geometry/src/curve/Query/InOutTests.ts index b5ae03bb5db8..70af1601d3fc 100644 --- a/core/geometry/src/curve/Query/InOutTests.ts +++ b/core/geometry/src/curve/Query/InOutTests.ts @@ -38,7 +38,7 @@ export class PointInOnOutContext { let plane: Plane3dByOriginAndUnitNormal; const xy = Point3d.create(x, y); for (let radians = 0.0; Math.abs(radians) < 6.0; radians = -1.2313 * (radians + 0.3212897)) { - plane = Plane3dByOriginAndUnitNormal.createXYAngle(x, y, Angle.createRadians(radians))!; + plane = Plane3dByOriginAndUnitNormal.createXYAngle(x, y, Angle.createRadians(radians)); const normal = plane.getNormalRef(); const intersections: CurveLocationDetail[] = []; for (const cp of loop.children) { diff --git a/core/geometry/src/curve/Query/PlanarSubdivision.ts b/core/geometry/src/curve/Query/PlanarSubdivision.ts index 1bf0aa47688f..cd547d9d8540 100644 --- a/core/geometry/src/curve/Query/PlanarSubdivision.ts +++ b/core/geometry/src/curve/Query/PlanarSubdivision.ts @@ -133,6 +133,13 @@ export class PlanarSubdivision { // otherwise, these single-primitive loops are missing from the graph detailByPrimitive.splitAndAppendMissingClosedPrimitives(primitives, mergeTolerance); } + // these helper lambdas assume exactly one of the paired details is on the input curve + const getFractionOnCurve = (pair: CurveLocationDetailPair, curve: CurvePrimitive): number => { + return (pair.detailA.curve === curve) ? pair.detailA.fraction : pair.detailB.fraction; + }; + const getDetailOnCurve = (pair: CurveLocationDetailPair, curve: CurvePrimitive): CurveLocationDetail => { + return (pair.detailA.curve === curve) ? pair.detailA : pair.detailB; + }; const graph = new HalfEdgeGraph(); for (const entry of detailByPrimitive.primitiveToPair.entries()) { const p = entry[0]; @@ -140,24 +147,27 @@ export class PlanarSubdivision { const details = entry[1].reduce((accumulator: CurveLocationDetailPair[], detailPair) => { if (!detailPair.detailA.hasFraction1) return [...accumulator, detailPair]; - const detail = getDetailOnCurve(detailPair, p)!; - const detail0 = CurveLocationDetail.createCurveFractionPoint(p, detail.fraction, detail.point); - const detail1 = CurveLocationDetail.createCurveFractionPoint(p, detail.fraction1!, detail.point1!); - return [ - ...accumulator, - CurveLocationDetailPair.createCapture(detail0, detail0), - CurveLocationDetailPair.createCapture(detail1, detail1), - ]; + const detail = getDetailOnCurve(detailPair, p); + if (detail.isInterval()) { + const detail0 = CurveLocationDetail.createCurveFractionPoint(p, detail.fraction, detail.point); + const detail1 = CurveLocationDetail.createCurveFractionPoint(p, detail.fraction1, detail.point1); + return [ + ...accumulator, + CurveLocationDetailPair.createCapture(detail0, detail0), + CurveLocationDetailPair.createCapture(detail1, detail1), + ]; + } + return [...accumulator, detailPair]; }, []); // lexical sort on p intersection fraction details.sort((pairA: CurveLocationDetailPair, pairB: CurveLocationDetailPair) => { - const fractionA = getFractionOnCurve(pairA, p)!; - const fractionB = getFractionOnCurve(pairB, p)!; + const fractionA = getFractionOnCurve(pairA, p); + const fractionB = getFractionOnCurve(pairB, p); return fractionA - fractionB; }); let last = { point: p.startPoint(), fraction: 0.0 }; for (const detailPair of details) { - const detail = getDetailOnCurve(detailPair, p)!; + const detail = getDetailOnCurve(detailPair, p); const detailFraction = Geometry.restrictToInterval(detail.fraction, 0, 1); // truncate fraction, but don't snap point; clustering happens later last = this.addHalfEdge(graph, p, last.point, last.fraction, detail.point, detailFraction, mergeTolerance); } @@ -257,7 +267,7 @@ export class PlanarSubdivision { private static createCurveInEdge(edge: HalfEdge, z?: number): CurvePrimitive | undefined { let result: CurvePrimitive | undefined; const info = this.extractGeometryFromEdge(edge); - if (info && info.detail.curve && info.detail.fraction1) { + if (info && info.detail.curve && info.detail.hasFraction1) { const f0 = info.reversed ? info.detail.fraction1 : info.detail.fraction; const f1 = info.reversed ? info.detail.fraction : info.detail.fraction1; result = info.detail.curve.clonePartialCurve(f0, f1); @@ -431,4 +441,4 @@ export namespace PlanarSubdivision { */ z?: number; } -} \ No newline at end of file +} diff --git a/core/geometry/src/curve/Query/StrokeCountChain.ts b/core/geometry/src/curve/Query/StrokeCountChain.ts index f5df166eb37e..9f94d36504f2 100644 --- a/core/geometry/src/curve/Query/StrokeCountChain.ts +++ b/core/geometry/src/curve/Query/StrokeCountChain.ts @@ -324,9 +324,11 @@ export class StrokeCountSection { for (let pass = 0; ; pass++) { if (!callback.startPass(pass)) break; - for (let sectionIndex = 0; sectionIndex < numSection; sectionIndex++) - if (!callback.visit(pass, sections[sectionIndex].chains[chainIndex].maps[primitiveIndex].componentData![componentIndex])) + for (let sectionIndex = 0; sectionIndex < numSection; sectionIndex++) { + const componentData = sections[sectionIndex].chains[chainIndex].maps[primitiveIndex].componentData; + if (undefined === componentData || !callback.visit(pass, componentData[componentIndex])) return false; + } if (!callback.endPass(pass)) return false; } diff --git a/core/geometry/src/curve/RegionMomentsXY.ts b/core/geometry/src/curve/RegionMomentsXY.ts index 668f83ac70fe..8d76d3715d6b 100644 --- a/core/geometry/src/curve/RegionMomentsXY.ts +++ b/core/geometry/src/curve/RegionMomentsXY.ts @@ -41,7 +41,9 @@ export class RegionMomentsXY extends NullGeometryHandler { * * The triangle with vertices: origin, arc start, arc end. */ public override handleArc3d(arc: Arc3d): void { - const momentData = this._activeMomentData!; + const momentData = this._activeMomentData; + if (undefined === momentData) + return; const sweepRadians = arc.sweep.sweepRadians; const alphaRadians = sweepRadians * 0.5; let s = Math.sin(alphaRadians); @@ -74,12 +76,16 @@ export class RegionMomentsXY extends NullGeometryHandler { } /** Accumulate integrals over the (triangular) areas from the origin to each line segment. */ public override handleLineString3d(ls: LineString3d): void { - const momentData = this._activeMomentData!; + const momentData = this._activeMomentData; + if (undefined === momentData) + return; momentData.accumulateTriangleToLineStringMomentsXY(undefined, ls.packedPoints); } /** Accumulate integrals over the (triangular) area from the origin to this line segment. */ public override handleLineSegment3d(segment: LineSegment3d): void { - const momentData = this._activeMomentData!; + const momentData = this._activeMomentData; + if (undefined === momentData) + return; segment.startPoint(this._point0); segment.endPoint(this._point1); momentData.accumulateTriangleMomentsXY(undefined, this._point0, this._point1); diff --git a/core/geometry/src/curve/RegionOps.ts b/core/geometry/src/curve/RegionOps.ts index 85d8e91d1652..c17a60065cd8 100644 --- a/core/geometry/src/curve/RegionOps.ts +++ b/core/geometry/src/curve/RegionOps.ts @@ -200,13 +200,16 @@ export class RegionOps { */ public static centroidAreaNormal(region: AnyRegion, result?: Ray3d): Ray3d | undefined { const localToWorld = FrameBuilder.createRightHandedFrame(undefined, region); - if (!localToWorld) + if (undefined === localToWorld) return undefined; const normal = localToWorld.matrix.columnZ(result?.direction); const regionIsXY = normal.isParallelTo(Vector3d.unitZ(), true); let regionXY: AnyRegion | undefined = region; if (!regionIsXY) { // rotate the region to be parallel to the xy-plane - regionXY = region.cloneTransformed(localToWorld.inverse()!) as AnyRegion | undefined; + const inverse = localToWorld.inverse(); + if (!inverse) + return undefined; + regionXY = region.cloneTransformed(inverse) as AnyRegion | undefined; if (!regionXY) return undefined; } @@ -748,7 +751,10 @@ export class RegionOps { } else if (data instanceof IndexedXYZCollection) { let dataToUse; if (requireClosurePoint && data.length === 5) { - if (!Geometry.isSmallMetricDistance(data.distanceIndexIndex(0, 4)!)) + const distance = data.distanceIndexIndex(0, 4); + if (undefined === distance) + return undefined; + if (!Geometry.isSmallMetricDistance(distance)) return undefined; dataToUse = data; } else if (!requireClosurePoint && data.length === 4) { @@ -761,9 +767,11 @@ export class RegionOps { if (dataToUse.length < (requireClosurePoint ? 5 : 4)) return undefined; } - const vector01 = dataToUse.vectorIndexIndex(0, 1)!; - const vector03 = dataToUse.vectorIndexIndex(0, 3)!; - const vector12 = dataToUse.vectorIndexIndex(1, 2)!; + const vector01 = dataToUse.vectorIndexIndex(0, 1); + const vector03 = dataToUse.vectorIndexIndex(0, 3); + const vector12 = dataToUse.vectorIndexIndex(1, 2); + if (undefined === vector01 || undefined === vector03 || undefined === vector12) + return undefined; const normalVector = vector01.crossProduct(vector03); if (normalVector.normalizeInPlace() && vector12.isAlmostEqual(vector03) diff --git a/core/geometry/src/curve/RegionOpsClassificationSweeps.ts b/core/geometry/src/curve/RegionOpsClassificationSweeps.ts index 462c18a3dc02..7d511d7cf97d 100644 --- a/core/geometry/src/curve/RegionOpsClassificationSweeps.ts +++ b/core/geometry/src/curve/RegionOpsClassificationSweeps.ts @@ -478,6 +478,7 @@ export class RegionBooleanContext implements RegionOpsFaceToFaceSearchCallbacks this.addConnectives(); } private _workSegment?: LineSegment3d; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion private static _bridgeDirection = Vector3d.createNormalized(1.0, -0.12328974132467)!; // magic unit direction to minimize vertex hits /** * The sweep operations require access to all geometry by edge crossings and face walk. @@ -604,10 +605,10 @@ export class RegionBooleanContext implements RegionOpsFaceToFaceSearchCallbacks doomedA.edgeTag instanceof CurveLocationDetail && doomedA.sortData !== undefined && doomedB.edgeTag instanceof CurveLocationDetail && doomedB.sortData !== undefined && doomedA.edgeTag.curve === doomedB.edgeTag.curve && - doomedA.edgeTag.hasFraction1 && doomedB.edgeTag.hasFraction1 && + doomedA.edgeTag.isInterval() && doomedB.edgeTag.isInterval() && doomedA.sortData * doomedB.sortData < 0 && - ((doomedA.sortData > 0 && Geometry.isSmallRelative(doomedA.edgeTag.fraction - doomedB.edgeTag.fraction1!)) || - (doomedA.sortData < 0 && Geometry.isSmallRelative(doomedA.edgeTag.fraction1! - doomedB.edgeTag.fraction))) + ((doomedA.sortData > 0 && Geometry.isSmallRelative(doomedA.edgeTag.fraction - doomedB.edgeTag.fraction1)) || + (doomedA.sortData < 0 && Geometry.isSmallRelative(doomedA.edgeTag.fraction1 - doomedB.edgeTag.fraction))) ) { const survivorA = HalfEdge.healEdge(doomedA, false); if (survivorA) { @@ -737,9 +738,9 @@ export class RegionBooleanContext implements RegionOpsFaceToFaceSearchCallbacks if (data instanceof RegionGroupMember) return updateRegionGroupMemberState(data); - if (data instanceof CurveLocationDetail) { + if (data instanceof CurveLocationDetail && undefined !== data.curve) { // We trust that the caller has linked from the graph node to a curve which has a RegionGroupMember as its parent. - const member = data.curve!.parent; + const member = data.curve.parent; if (member instanceof RegionGroupMember) return updateRegionGroupMemberState(member); } @@ -794,15 +795,15 @@ function areaUnderPartialCurveXY( trapezoidArea = -(xyEnd.x - xyStart.x) * (0.5 * (xyStart.y + xyEnd.y) - referencePoint.y); } let areaToChord = 0.0; - if (detail && detail.curve && detail.hasFraction1) { + if (detail && detail.curve && detail.isInterval()) { if (detail.curve instanceof LineSegment3d) { // nothing to do for a line segment } else if (detail.curve instanceof Arc3d) { - areaToChord = detail.curve.areaToChordXY(detail.fraction, detail.fraction1!); + areaToChord = detail.curve.areaToChordXY(detail.fraction, detail.fraction1); } else { - const partial = detail.curve.clonePartialCurve(detail.fraction, detail.fraction1!); - areaToChord = partial ? - RegionOps.computeXYArea(Loop.create(partial, LineSegment3d.create(detail.point1!, detail.point))) ?? 0 + const partial = detail.curve.clonePartialCurve(detail.fraction, detail.fraction1); + areaToChord = (partial) ? + RegionOps.computeXYArea(Loop.create(partial, LineSegment3d.create(detail.point1, detail.point))) ?? 0 : 0; } } diff --git a/core/geometry/src/curve/internalContexts/AnnounceTangentStrokeHandler.ts b/core/geometry/src/curve/internalContexts/AnnounceTangentStrokeHandler.ts index 2d910a6cebef..aef39e88e94d 100644 --- a/core/geometry/src/curve/internalContexts/AnnounceTangentStrokeHandler.ts +++ b/core/geometry/src/curve/internalContexts/AnnounceTangentStrokeHandler.ts @@ -169,7 +169,10 @@ export class AnnounceTangentStrokeHandler extends NewtonRtoRStrokeHandler implem return true; } private announceRay(fraction: number, data: Ray3d): void { - this._functionB = this.evaluateFunction(data)!; + const value = this.evaluateFunction(data); + if (value === undefined) + return; + this._functionB = value; this._fractionB = fraction; if (this._numThisCurve++ > 0) // after the first stroke point, a stroke segment is defined, so we have an interval this.searchInterval(); diff --git a/core/geometry/src/curve/internalContexts/ClosestPointStrokeHandler.ts b/core/geometry/src/curve/internalContexts/ClosestPointStrokeHandler.ts index b4893c3fbb1d..8de433f0be2e 100644 --- a/core/geometry/src/curve/internalContexts/ClosestPointStrokeHandler.ts +++ b/core/geometry/src/curve/internalContexts/ClosestPointStrokeHandler.ts @@ -168,7 +168,10 @@ export class ClosestPointStrokeHandler extends NewtonRtoRStrokeHandler implement return true; } private announceRay(fraction: number, data: Ray3d): void { - this._functionB = this.evaluateFunction(data)!; + const value = this.evaluateFunction(data); + if (value === undefined) + return; + this._functionB = value; this._fractionB = fraction; if (this._numThisCurve++ > 0) // after the first stroke point, a stroke segment is defined, so we have an interval this.searchInterval(); diff --git a/core/geometry/src/curve/internalContexts/CurveCurveCloseApproachXY.ts b/core/geometry/src/curve/internalContexts/CurveCurveCloseApproachXY.ts index faec86c38ece..2b746a0e3c9b 100644 --- a/core/geometry/src/curve/internalContexts/CurveCurveCloseApproachXY.ts +++ b/core/geometry/src/curve/internalContexts/CurveCurveCloseApproachXY.ts @@ -159,14 +159,17 @@ export class CurveCurveCloseApproachXY extends RecurseToCurvesGeometryHandler { ): void { let globalFractionA, globalFractionB; let globalFractionA1, globalFractionB1; - const isInterval = intervalDetails !== undefined && - intervalDetails.detailA.hasFraction1 && - intervalDetails.detailB.hasFraction1; - if (isInterval) { + let isInterval = false; + if (undefined !== intervalDetails && + intervalDetails.detailA.isInterval() && + intervalDetails.detailB.isInterval() && + intervalDetails.detailA.fraction1 && + intervalDetails.detailB.fraction1) { + isInterval = true; globalFractionA = Geometry.interpolate(fractionA0, intervalDetails.detailA.fraction, fractionA1); globalFractionB = Geometry.interpolate(fractionB0, intervalDetails.detailB.fraction, fractionB1); - globalFractionA1 = Geometry.interpolate(fractionA0, intervalDetails.detailA.fraction1!, fractionA1); - globalFractionB1 = Geometry.interpolate(fractionB0, intervalDetails.detailB.fraction1!, fractionB1); + globalFractionA1 = Geometry.interpolate(fractionA0, intervalDetails.detailA.fraction1, fractionA1); + globalFractionB1 = Geometry.interpolate(fractionB0, intervalDetails.detailB.fraction1, fractionB1); } else { globalFractionA = globalFractionA1 = Geometry.interpolate(fractionA0, localFractionA, fractionA1); globalFractionB = globalFractionB1 = Geometry.interpolate(fractionB0, localFractionB, fractionB1); @@ -665,7 +668,9 @@ export class CurveCurveCloseApproachXY extends RecurseToCurvesGeometryHandler { private dispatchArcArc(cpA: Arc3d, cpB: Arc3d, reversed: boolean): void { const rangeA = cpA.range(); const rangeB = cpB.range(); - rangeA.expandInPlace(this._maxDistanceToAccept!); + if (undefined === this._maxDistanceToAccept) + return; + rangeA.expandInPlace(this._maxDistanceToAccept); if (!rangeB.intersectsRangeXY(rangeA)) return; // 1) endpoints to endpoints or endpoints projection to the other curve @@ -721,7 +726,9 @@ export class CurveCurveCloseApproachXY extends RecurseToCurvesGeometryHandler { public computeArcLineString(arcA: Arc3d, lsB: LineString3d, reversed: boolean): any { const rangeA = arcA.range(); const rangeB = lsB.range(); - rangeA.expandInPlace(this._maxDistanceToAccept!); + if (undefined === this._maxDistanceToAccept) + return; + rangeA.expandInPlace(this._maxDistanceToAccept); if (!rangeB.intersectsRangeXY(rangeA)) return; const pointB0 = CurveCurveCloseApproachXY._workPointBB0; @@ -823,7 +830,9 @@ export class CurveCurveCloseApproachXY extends RecurseToCurvesGeometryHandler { private computeLineStringLineString(lsA: LineString3d, lsB: LineString3d, reversed: boolean): void { const rangeA = lsA.range(); const rangeB = lsB.range(); - rangeA.expandInPlace(this._maxDistanceToAccept!); + if (undefined === this._maxDistanceToAccept) + return; + rangeA.expandInPlace(this._maxDistanceToAccept); if (!rangeB.intersectsRangeXY(rangeA)) return; let bitB0: number; @@ -849,7 +858,7 @@ export class CurveCurveCloseApproachXY extends RecurseToCurvesGeometryHandler { rangeA1.setNull(); rangeA1.extendPoint(pointA0); rangeA1.extendPoint(pointA1); - rangeA1.expandInPlace(this._maxDistanceToAccept!); + rangeA1.expandInPlace(this._maxDistanceToAccept); if (rangeA1.intersectsRangeXY(rangeB)) { lsB.pointAt(0, pointB0); bitB0 = this.classifyBitsPointRangeXY(pointB0.x, pointB0.y, rangeA1); diff --git a/core/geometry/src/curve/internalContexts/CurveCurveIntersectXY.ts b/core/geometry/src/curve/internalContexts/CurveCurveIntersectXY.ts index 6b74f25a80a2..a99490e6fdc4 100644 --- a/core/geometry/src/curve/internalContexts/CurveCurveIntersectXY.ts +++ b/core/geometry/src/curve/internalContexts/CurveCurveIntersectXY.ts @@ -169,14 +169,17 @@ export class CurveCurveIntersectXY extends RecurseToCurvesGeometryHandler { ): void { let globalFractionA, globalFractionB; let globalFractionA1, globalFractionB1; - const isInterval = intervalDetails !== undefined && - intervalDetails.detailA.hasFraction1 && - intervalDetails.detailB.hasFraction1; - if (isInterval) { + let isInterval = false; + if (undefined !== intervalDetails && + intervalDetails.detailA.isInterval() && + intervalDetails.detailB.isInterval() && + intervalDetails.detailA.fraction1 && + intervalDetails.detailB.fraction1) { + isInterval = true; globalFractionA = Geometry.interpolate(fractionA0, intervalDetails.detailA.fraction, fractionA1); globalFractionB = Geometry.interpolate(fractionB0, intervalDetails.detailB.fraction, fractionB1); - globalFractionA1 = Geometry.interpolate(fractionA0, intervalDetails.detailA.fraction1!, fractionA1); - globalFractionB1 = Geometry.interpolate(fractionB0, intervalDetails.detailB.fraction1!, fractionB1); + globalFractionA1 = Geometry.interpolate(fractionA0, intervalDetails.detailA.fraction1, fractionA1); + globalFractionB1 = Geometry.interpolate(fractionB0, intervalDetails.detailB.fraction1, fractionB1); } else { globalFractionA = globalFractionA1 = Geometry.interpolate(fractionA0, localFractionA, fractionA1); globalFractionB = globalFractionB1 = Geometry.interpolate(fractionB0, localFractionB, fractionB1); @@ -293,14 +296,16 @@ export class CurveCurveIntersectXY extends RecurseToCurvesGeometryHandler { extendB1: boolean, reversed: boolean, ): void { + if (undefined === this._worldToLocalPerspective) + return; const hA0 = CurveCurveIntersectXY._workPointA0H; const hA1 = CurveCurveIntersectXY._workPointA1H; const hB0 = CurveCurveIntersectXY._workPointB0H; const hB1 = CurveCurveIntersectXY._workPointB1H; - this._worldToLocalPerspective!.multiplyPoint3d(pointA0, 1, hA0); - this._worldToLocalPerspective!.multiplyPoint3d(pointA1, 1, hA1); - this._worldToLocalPerspective!.multiplyPoint3d(pointB0, 1, hB0); - this._worldToLocalPerspective!.multiplyPoint3d(pointB1, 1, hB1); + this._worldToLocalPerspective.multiplyPoint3d(pointA0, 1, hA0); + this._worldToLocalPerspective.multiplyPoint3d(pointA1, 1, hA1); + this._worldToLocalPerspective.multiplyPoint3d(pointB0, 1, hB0); + this._worldToLocalPerspective.multiplyPoint3d(pointB1, 1, hB1); const fractionAB = SmallSystem.lineSegment3dHXYTransverseIntersectionUnbounded(hA0, hA1, hB0, hB1); if (fractionAB !== undefined) { const fractionA = fractionAB.x; diff --git a/core/geometry/src/curve/internalContexts/PolygonOffsetContext.ts b/core/geometry/src/curve/internalContexts/PolygonOffsetContext.ts index 941cd1bbf75f..7ec55e4ea461 100644 --- a/core/geometry/src/curve/internalContexts/PolygonOffsetContext.ts +++ b/core/geometry/src/curve/internalContexts/PolygonOffsetContext.ts @@ -495,10 +495,14 @@ export class PolygonWireOffsetContext { Joint.collectStrokesFromChain(joint0, chain, numPoints); // compute offset corners (by extension/trim) const n = chain.packedPoints.length; if (n > 1) { - if (chain.packedPoints.front()!.isAlmostEqual(chain.packedPoints.back()!)) - return Loop.create(chain); - else - return Path.create(chain); + const front = chain.packedPoints.front(); + const back = chain.packedPoints.back(); + if (undefined !== front && undefined !== back) { + if (front.isAlmostEqual(back)) + return Loop.create(chain); + else + return Path.create(chain); + } } return undefined; } diff --git a/core/geometry/src/curve/spiral/DirectSpiral3d.ts b/core/geometry/src/curve/spiral/DirectSpiral3d.ts index adfec9f1ee78..cb285812e67c 100644 --- a/core/geometry/src/curve/spiral/DirectSpiral3d.ts +++ b/core/geometry/src/curve/spiral/DirectSpiral3d.ts @@ -95,6 +95,7 @@ export class DirectSpiral3d extends TransitionSpiral3d { } /** * Compute stroke data in an interval. + * * Strokes is not computed if suitable stroke data is not available. * @param strokes strokes to clear and refill. * @param fraction0 start fraction * @param fraction1 end fraction @@ -105,7 +106,9 @@ export class DirectSpiral3d extends TransitionSpiral3d { strokes.clear(); strokes.ensureEmptyUVParams(); strokes.ensureEmptyFractions(); - const distances = strokes.packedUVParams!; + const distances = strokes.packedUVParams; + if (undefined === distances) + return; const nominalIntervalLength = Math.abs(fractionB - fractionA) * this._nominalL1; for (let i = 0; i <= numInterval; i++) { const fraction = Geometry.interpolate(fractionA, i / numInterval, fractionB); @@ -481,11 +484,16 @@ export class DirectSpiral3d extends TransitionSpiral3d { && Geometry.isSameCoordinate(0.0, this.localToWorld.matrix.dotColumnX(plane.getNormalRef())) && Geometry.isSameCoordinate(0.0, this.localToWorld.matrix.dotColumnY(plane.getNormalRef())); } - /** Return quick length of the spiral. - * The tangent vector of a true clothoid is length 1 everywhere, so simple proportion of nominalL1 is a good approximation. + /** + * Return quick length of the spiral. + * The tangent vector of a true clothoid is length 1 everywhere, so simple proportion of nominalL1 is a good + * approximation. + * * Return 0 if suitable stroke data is not available. */ public quickLength() { - const distanceData = this._globalStrokes.packedUVParams!; + const distanceData = this._globalStrokes.packedUVParams; + if (undefined === distanceData) + return 0; const n = distanceData.length; return distanceData.getYAtUncheckedPointIndex(n - 1); } diff --git a/core/geometry/src/curve/spiral/IntegratedSpiral3d.ts b/core/geometry/src/curve/spiral/IntegratedSpiral3d.ts index c1aeaa8e3e5d..a4d7393d2360 100644 --- a/core/geometry/src/curve/spiral/IntegratedSpiral3d.ts +++ b/core/geometry/src/curve/spiral/IntegratedSpiral3d.ts @@ -226,13 +226,15 @@ export class IntegratedSpiral3d extends TransitionSpiral3d { return undefined; if (fractionInterval === undefined) fractionInterval = Segment1d.create(0, 1); + if (!data.bearing0 || !data.bearing1 || !data.curveLength) + return undefined; return new IntegratedSpiral3d( spiralType, evaluator, Segment1d.create(data.radius0, data.radius1), - AngleSweep.createStartEnd(data.bearing0!, data.bearing1!), + AngleSweep.createStartEnd(data.bearing0, data.bearing1), fractionInterval ? fractionInterval.clone() : Segment1d.create(0, 1), - localToWorld, data.curveLength!, data1); + localToWorld, data.curveLength, data1); } /** Copy all defining data from another spiral. */ public setFrom(other: IntegratedSpiral3d): IntegratedSpiral3d { @@ -339,12 +341,17 @@ export class IntegratedSpiral3d extends TransitionSpiral3d { this._activeStrokes = this._globalStrokes.clone(); this._activeStrokes.reverseInPlace(); } - /** Evaluate curve point with respect to fraction. */ + /** + * Evaluate curve point with respect to fraction. + * If calculation failed, a zero Point3d is returned. + */ public fractionToPoint(activeFraction: number, result?: Point3d): Point3d { const targetGlobalFraction = this.activeFractionInterval.fractionToPoint(activeFraction); const numStrokes = this._globalStrokes.packedPoints.length - 1; if (activeFraction > 1.0) { - result = this._globalStrokes.packedPoints.back(result)!; + result = this._globalStrokes.packedPoints.back(result); + if (undefined === result) + result = Point3d.createZero(); const integrationStep = 1.0 / numStrokes; let currentGlobalFraction = 1.0; let nextGlobalFraction = currentGlobalFraction + integrationStep; @@ -355,7 +362,9 @@ export class IntegratedSpiral3d extends TransitionSpiral3d { } this.fullSpiralIncrementalIntegral(result, currentGlobalFraction, targetGlobalFraction, true); } else if (activeFraction < 0.0) { - result = this._globalStrokes.packedPoints.front(result)!; + result = this._globalStrokes.packedPoints.front(result); + if (undefined === result) + result = Point3d.createZero(); const integrationStep = 1.0 / numStrokes; let currentGlobalFraction = 0.0; let nextGlobalFraction = currentGlobalFraction - integrationStep; diff --git a/core/geometry/src/geometry3d/Ellipsoid.ts b/core/geometry/src/geometry3d/Ellipsoid.ts index 8388df01b742..733a0dbf1284 100644 --- a/core/geometry/src/geometry3d/Ellipsoid.ts +++ b/core/geometry/src/geometry3d/Ellipsoid.ts @@ -182,7 +182,7 @@ export class Ellipsoid implements Clipper { public static createCenterMatrixRadii(center: Point3d, axes: Matrix3d | undefined, radiusX: number, radiusY: number, radiusZ: number): Ellipsoid { let scaledAxes; if (axes === undefined) - scaledAxes = Matrix3d.createScale(radiusX, radiusY, radiusZ)!; + scaledAxes = Matrix3d.createScale(radiusX, radiusY, radiusZ); else scaledAxes = axes.scaleColumns(radiusX, radiusY, radiusZ); return new Ellipsoid(Transform.createOriginAndMatrix(center, scaledAxes)); @@ -382,7 +382,7 @@ export class Ellipsoid implements Clipper { SphereImplicit.radiansToUnitSphereXYZ(thetaBRadians, phiBRadians, this._workUnitVectorB); const sweepAngle = this._workUnitVectorA.angleTo(this._workUnitVectorB); // the unit vectors (on unit sphere) are never 0, so this cannot fail. - const matrix = Matrix3d.createRigidFromColumns(this._workUnitVectorA, this._workUnitVectorB, AxisOrder.XYZ)!; + const matrix = Matrix3d.createRigidFromColumns(this._workUnitVectorA, this._workUnitVectorB, AxisOrder.XYZ); if (matrix !== undefined) { const matrix1 = this._transform.matrix.multiplyMatrixMatrix(matrix); return Arc3d.create(this._transform.getOrigin(), matrix1.columnX(), matrix1.columnY(), @@ -527,16 +527,21 @@ export class Ellipsoid implements Clipper { * @param angleA start point of arc (given as angles on this ellipsoid) * @param intermediateNormalFraction * @param angleB end point of arc (given as angles on this ellipsoid) + * @returns arc in the plane defined by the normal at the intermediate point. If calculation fails, return an + * arc with zero center and zero vectors. */ public sectionArcWithIntermediateNormal( angleA: LongitudeLatitudeNumber, intermediateNormalFraction: number, angleB: LongitudeLatitudeNumber): Arc3d { - const normalA = this.radiansToUnitNormalRay(angleA.longitudeRadians, angleA.latitudeRadians)!; - const normalB = this.radiansToUnitNormalRay(angleB.longitudeRadians, angleB.latitudeRadians)!; + const defaultArc = Arc3d.create(Point3d.createZero(), Vector3d.createZero(), Vector3d.createZero()); + const normalA = this.radiansToUnitNormalRay(angleA.longitudeRadians, angleA.latitudeRadians); + const normalB = this.radiansToUnitNormalRay(angleB.longitudeRadians, angleB.latitudeRadians); + if (!normalA || !normalB) + return defaultArc; const normal = normalA.direction.interpolate(intermediateNormalFraction, normalB.direction); const arc = this.createSectionArcPointPointVectorInPlane(angleA, angleB, normal); - return arc!; + return arc ?? defaultArc; } /** diff --git a/core/geometry/src/geometry3d/FrustumAnimation.ts b/core/geometry/src/geometry3d/FrustumAnimation.ts index c01b739c39cc..852ea8e45bb6 100644 --- a/core/geometry/src/geometry3d/FrustumAnimation.ts +++ b/core/geometry/src/geometry3d/FrustumAnimation.ts @@ -78,10 +78,14 @@ export class SmoothTransformBetweenFrusta { const rigidA = Transform.createOriginAndMatrix(localToWorldA.origin, Matrix3d.createRigidFromMatrix3d(localToWorldA.matrix, AxisOrder.ZXY)); const rigidB = Transform.createOriginAndMatrix(localToWorldB.origin, Matrix3d.createRigidFromMatrix3d(localToWorldB.matrix, AxisOrder.ZXY)); if (rigidA.matrix.computeCachedInverse(true) && rigidB.matrix.computeCachedInverse(true)) { - const spinMatrix = rigidB.matrix.multiplyMatrixMatrixInverse(rigidA.matrix)!; + const spinMatrix = rigidB.matrix.multiplyMatrixMatrixInverse(rigidA.matrix); + if (!spinMatrix) + return undefined; const spinAxis = spinMatrix.getAxisAndAngleOfRotation(); - const localCornerA = rigidA.multiplyInversePoint3dArray(cornerA)!; - const localCornerB = rigidB.multiplyInversePoint3dArray(cornerB)!; + const localCornerA = rigidA.multiplyInversePoint3dArray(cornerA); + const localCornerB = rigidB.multiplyInversePoint3dArray(cornerB); + if (!localCornerA || !localCornerB) + return undefined; /** Is this a pure rotation -- i.e. no clip volume resizing for camera or clip changes */ if (preferSimpleRotation && Point3dArray.isAlmostEqual(localCornerA, localCornerB) && !spinAxis.angle.isAlmostZero) { // world vectors @@ -95,8 +99,10 @@ export class SmoothTransformBetweenFrusta { const spinCenter = chordMidPoint.plusScaled(bisector, alpha); const rigidA1 = Transform.createOriginAndMatrix(spinCenter, rigidA.matrix); const rigidB1 = Transform.createOriginAndMatrix(spinCenter, rigidB.matrix); - const localCornerA1 = rigidA1.multiplyInversePoint3dArray(cornerA)!; - const localCornerB1 = rigidB1.multiplyInversePoint3dArray(cornerB)!; + const localCornerA1 = rigidA1.multiplyInversePoint3dArray(cornerA); + const localCornerB1 = rigidB1.multiplyInversePoint3dArray(cornerB); + if (!localCornerA1 || !localCornerB1) + return undefined; return new SmoothTransformBetweenFrusta(rigidA1, localCornerA1, rigidB1, localCornerB1, spinAxis.axis, spinAxis.angle); } @@ -121,11 +127,14 @@ export class SmoothTransformBetweenFrusta { /** * After initialization, call this for various intermediate fractions. * The returned corner points are in world coordinates "between" start and end positions. + * If calculation fails, an empty array is returned. */ public fractionToWorldCorners(fraction: number, result?: Point3d[]): Point3d[] { const corners = this.interpolateLocalCorners(fraction, result); const fractionalRotation = Matrix3d.createRotationAroundVector(this._rotationAxis, - this._rotationAngle.cloneScaled(fraction))!; + this._rotationAngle.cloneScaled(fraction)); + if (!fractionalRotation) + return []; const axes0 = this._localToWorldA.matrix; const fractionalAxes = fractionalRotation.multiplyMatrixMatrix(axes0); const fractionalOrigin = this._localToWorldA.getOrigin().interpolate(fraction, this._localToWorldB.origin); diff --git a/core/geometry/src/geometry3d/GrowableXYZArray.ts b/core/geometry/src/geometry3d/GrowableXYZArray.ts index 51831865d36d..9a46a91bb645 100644 --- a/core/geometry/src/geometry3d/GrowableXYZArray.ts +++ b/core/geometry/src/geometry3d/GrowableXYZArray.ts @@ -586,7 +586,9 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection { const nDouble = this.float64Length; if (!matrix.computeCachedInverse(true)) return false; - const coffs = matrix.inverseCoffs!; + const coffs = matrix.inverseCoffs; + if (undefined === coffs) + return false; const tol = Geometry.smallFloatingPoint; let x = 0; let y = 0; @@ -800,6 +802,13 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection { public vectorIndexIndex(i: number, j: number, result?: Vector3d): Vector3d | undefined { if (!this.isIndexValid(i) || !this.isIndexValid(j)) return undefined; + return this.vectorUncheckedIndexIndex(i, j, result); + } + /** + * Compute a vector from index origin i to indexed target j. + * This method does not check for index validity. Use [[GrowableXYZArray.vectorIndexIndex]] to have validity test. + */ + public vectorUncheckedIndexIndex(i: number, j: number, result?: Vector3d): Vector3d { const data = this._data; i = 3 * i; j = 3 * j; @@ -810,38 +819,48 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection { result, ); } - /** Compute a vector from origin to indexed target j. */ public vectorXYAndZIndex(origin: XYAndZ, j: number, result?: Vector3d): Vector3d | undefined { - if (this.isIndexValid(j)) { - const data = this._data; - j = 3 * j; - return Vector3d.create( - data[j] - origin.x, + if (this.isIndexValid(j)) + return this.vectorXYAndZUncheckedIndex(origin, j, result); + return undefined; + } + /** + * Compute a vector from origin to indexed target j. + * * This method does not check for index validity. Use [[GrowableXYZArray.vectorXYAndZIndex]] to have validity test. + */ + public vectorXYAndZUncheckedIndex(origin: XYAndZ, j: number, result?: Vector3d): Vector3d { + const data = this._data; + j = 3 * j; + return Vector3d.create( + data[j] - origin.x, data[j + 1] - origin.y, data[j + 2] - origin.z, result, ); - } - return undefined; } /** Compute the cross product of vectors from from indexed origin to indexed targets i and j. */ public crossProductIndexIndexIndex(originIndex: number, targetAIndex: number, targetBIndex: number, result?: Vector3d): Vector3d | undefined { - if (this.isIndexValid(originIndex) && this.isIndexValid(targetAIndex) && this.isIndexValid(targetBIndex)) { - const i = originIndex * 3; - const j = targetAIndex * 3; - const k = targetBIndex * 3; - const data = this._data; - return Geometry.crossProductXYZXYZ( - data[j] - data[i], data[j + 1] - data[i + 1], data[j + 2] - data[i + 2], - data[k] - data[i], data[k + 1] - data[i + 1], data[k + 2] - data[i + 2], - result, - ); - } + if (this.isIndexValid(originIndex) && this.isIndexValid(targetAIndex) && this.isIndexValid(targetBIndex)) + return this.crossProductUncheckedIndexIndexIndex(originIndex, targetAIndex, targetBIndex, result); return undefined; } - + /** + * Compute the cross product of vectors from from indexed origin to indexed targets i and j. + * * This method does not check for index validity. Use [[ GrowableXYZArray.vectorXYAndZIndex]] to have validity test. + */ + public crossProductUncheckedIndexIndexIndex(originIndex: number, targetAIndex: number, targetBIndex: number, result?: Vector3d): Vector3d { + const i = originIndex * 3; + const j = targetAIndex * 3; + const k = targetBIndex * 3; + const data = this._data; + return Geometry.crossProductXYZXYZ( + data[j] - data[i], data[j + 1] - data[i + 1], data[j + 2] - data[i + 2], + data[k] - data[i], data[k + 1] - data[i + 1], data[k + 2] - data[i + 2], + result, + ); + } /** Compute the dot product of pointIndex with [x,y,z] */ public evaluateUncheckedIndexDotProductXYZ(pointIndex: number, x: number, y: number, z: number): number { const i = pointIndex * 3; @@ -918,15 +937,23 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection { * @param j second point index */ public distanceSquaredIndexIndex(i: number, j: number): number | undefined { - if (this.isIndexValid(i) && this.isIndexValid(j)) { - const i0 = 3 * i; - const j0 = 3 * j; - return Geometry.hypotenuseSquaredXYZ( - this._data[j0] - this._data[i0], - this._data[j0 + 1] - this._data[i0 + 1], - this._data[j0 + 2] - this._data[i0 + 2]); - } + if (this.isIndexValid(i) && this.isIndexValid(j)) + return this.distanceSquaredUncheckedIndexIndex(i, j); return undefined; + } + /** + * Return distance squared between indicated points. + * * This method does not check for index validity. Use [[GrowableXYZArray.distanceSquaredIndexIndex]] to have validity test. + * @param i first point index + * @param j second point index + */ + public distanceSquaredUncheckedIndexIndex(i: number, j: number): number { + const i0 = 3 * i; + const j0 = 3 * j; + return Geometry.hypotenuseSquaredXYZ( + this._data[j0] - this._data[i0], + this._data[j0 + 1] - this._data[i0 + 1], + this._data[j0 + 2] - this._data[i0 + 2]); } /** * Return distance between indicated points. @@ -934,15 +961,24 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection { * @param j second point index */ public distanceIndexIndex(i: number, j: number): number | undefined { - if (this.isIndexValid(i) && this.isIndexValid(j)) { + if (this.isIndexValid(i) && this.isIndexValid(j)) + return this.distanceUncheckedIndexIndex(i, j); + return undefined; + } + /** + * Return distance between indicated points. + * * This method does not check for index validity. Use [[distanceIndexIndex]] to have validity test. + * @param i first point index + * @param j second point index + */ + public distanceUncheckedIndexIndex(i: number, j: number): number { const i0 = 3 * i; const j0 = 3 * j; return Geometry.hypotenuseXYZ( this._data[j0] - this._data[i0], this._data[j0 + 1] - this._data[i0 + 1], - this._data[j0 + 2] - this._data[i0 + 2]); - } - return undefined; + this._data[j0 + 2] - this._data[i0 + 2], + ); } /** Return the distance between points in distinct arrays. */ public static distanceBetweenPointsIn2Arrays(arrayA: GrowableXYZArray, i: number, arrayB: GrowableXYZArray, j: number): number | undefined { @@ -1047,7 +1083,7 @@ export class GrowableXYZArray extends IndexedReadWriteXYZCollection { * @param tolerance */ public static removeClosure(points: IndexedReadWriteXYZCollection, tolerance: number = Geometry.smallMetricDistance) { - while (points.length > 1 && points.distanceIndexIndex(0, points.length - 1)! < tolerance) + while (points.length > 1 && points.distanceUncheckedIndexIndex(0, points.length - 1) < tolerance) points.pop(); } /** diff --git a/core/geometry/src/geometry3d/IndexedXYZCollection.ts b/core/geometry/src/geometry3d/IndexedXYZCollection.ts index ec4e680c59ed..b6b9a408effc 100644 --- a/core/geometry/src/geometry3d/IndexedXYZCollection.ts +++ b/core/geometry/src/geometry3d/IndexedXYZCollection.ts @@ -54,9 +54,9 @@ export abstract class IndexedXYZCollection { public abstract getPoint3dAtCheckedPointIndex(index: number, result?: Point3d): Point3d | undefined; /** * Return the point at `index` as a strongly typed Point3d, without checking the point index validity. + * * Use [[IndexedXYZCollection.getPoint3dAtCheckedPointIndex]] to have index validity test. * @param index index of point within the array * @param result caller-allocated destination - * @returns undefined if the index is out of bounds */ public abstract getPoint3dAtUncheckedPointIndex(index: number, result?: Point3d): Point3d; /** @@ -74,6 +74,15 @@ export abstract class IndexedXYZCollection { * @returns undefined if either index is out of bounds */ public abstract vectorIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined; + /** + * Return a vector from the point at `indexA` to the point at `indexB` + * * This method does not check for index validity. Use [[IndexedXYZCollection.vectorIndexIndex]] to have validity test. + * @param indexA index of point within the array + * @param indexB index of point within the array + * @param result caller-allocated vector. + * @returns undefined if either index is out of bounds + */ + public abstract vectorUncheckedIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d; /** * Return a vector from `origin` to the point at `indexB` * @param origin origin for vector @@ -82,6 +91,14 @@ export abstract class IndexedXYZCollection { * @returns undefined if index is out of bounds */ public abstract vectorXYAndZIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d | undefined; + /** + * Return a vector from `origin` to the point at `indexB` + * * This method does not check for index validity. Use [[IndexedXYZCollection.vectorXYAndZIndex]] to have validity test. + * @param origin origin for vector + * @param indexB index of point within the array + * @param result caller-allocated vector. + */ + public abstract vectorXYAndZUncheckedIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d ; /** * Return a vector from the point at `indexA` to `target` * @param indexA index of point within the array @@ -120,6 +137,18 @@ export abstract class IndexedXYZCollection { public dotProductIndexIndexXYAndZ(origin: number, indexA: number, targetB: XYAndZ): number | undefined { if (origin < 0 || origin >= this.length || indexA < 0 || indexA >= this.length) return undefined; + return this.dotProductUncheckedIndexIndexXYAndZ(origin, indexA, targetB); + } + /** + * Return the dot product of the vectors from the point at `origin` to the point at `indexA` and to `targetB`. + * * This method does not check for index validity. Use [[IndexedXYZCollection.dotProductIndexIndexXYAndZ]] to have + * validity test. + * @param origin index of point within the array; origin of both vectors + * @param indexA index of point within the array; target of the first vector + * @param targetB target for second vector + * @returns undefined if index is out of bounds + */ + public dotProductUncheckedIndexIndexXYAndZ(origin: number, indexA: number, targetB: XYAndZ): number { const x0 = this.getXAtUncheckedPointIndex(origin); const y0 = this.getYAtUncheckedPointIndex(origin); const z0 = this.getZAtUncheckedPointIndex(origin); @@ -186,6 +215,13 @@ export abstract class IndexedXYZCollection { * @param index1 second point index */ public abstract distanceSquaredIndexIndex(index0: number, index1: number): number | undefined; + /** + * Return distance squared between indicated points. + * * This method does not check for index validity. Use [[IndexedXYZCollection.distanceSquaredIndexIndex]] to have validity test. + * @param index0 first point index + * @param index1 second point index + */ + public abstract distanceSquaredUncheckedIndexIndex(index0: number, index1: number): number; /** * Return distance squared between the point at index0 and target. * @param index0 first point index @@ -194,6 +230,15 @@ export abstract class IndexedXYZCollection { public distanceSquaredIndexXYAndZ(index0: number, target: XYAndZ): number | undefined { if (index0 < 0 || index0 >= this.length) return undefined; + return this.distanceSquaredUncheckedIndexXYAndZ(index0, target); + } + /** + * Return distance squared between the point at index0 and target. + * * This method does not check for index validity. Use [[IndexedXYZCollection.distanceSquaredIndexXYAndZ]] to have validity test. + * @param index0 first point index + * @param target second point + */ + public distanceSquaredUncheckedIndexXYAndZ(index0: number, target: XYAndZ): number { return Geometry.hypotenuseSquaredXYZ( target.x - this.getXAtUncheckedPointIndex(index0), target.y - this.getYAtUncheckedPointIndex(index0), @@ -205,6 +250,13 @@ export abstract class IndexedXYZCollection { * @param index1 second point index */ public abstract distanceIndexIndex(index0: number, index1: number): number | undefined; + /** + * Return distance between indicated points. + * * This method does not check for index validity. Use [[IndexedXYZCollection.distanceIndexIndex]] to have validity test. + * @param index0 first point index + * @param index1 second point index + */ + public abstract distanceUncheckedIndexIndex(index0: number, index1: number): number; /** * Test if index is valid for an xyz within this array. * @param index xyz index to test. @@ -293,11 +345,21 @@ export abstract class IndexedXYZCollection { public interpolateIndexIndex(index0: number, fraction: number, index1: number, result?: Point3d): Point3d | undefined { if (index0 < 0 || index0 >= this.length || index1 < 0 || index1 >= this.length) return undefined; + return this.interpolateUncheckedIndexIndex(index0, fraction, index1, result); + } + /** + * Interpolate the points at the given indices. + * * This method does not check for index validity. Use [[IndexedXYZCollection.interpolateIndexIndex]] to have validity test. + * @param index0 index of point p0 within the array + * @param fraction fraction f such that returned point is p0 + f * (p1 - p0) + * @param index1 index of point p1 within the array + * @param result optional caller-allocated result to fill and return + */ + public interpolateUncheckedIndexIndex(index0: number, fraction: number, index1: number, result?: Point3d): Point3d { return Point3d.create(Geometry.interpolate(this.getXAtUncheckedPointIndex(index0), fraction, this.getXAtUncheckedPointIndex(index1)), Geometry.interpolate(this.getYAtUncheckedPointIndex(index0), fraction, this.getYAtUncheckedPointIndex(index1)), Geometry.interpolate(this.getZAtUncheckedPointIndex(index0), fraction, this.getZAtUncheckedPointIndex(index1)), result); } - /** access x of indexed point */ public abstract getXAtUncheckedPointIndex(pointIndex: number): number; @@ -328,12 +390,26 @@ export abstract class IndexedXYZCollection { return undefined; return this.getPoint3dAtUncheckedPointIndex(0, result); } + /** + * Return the first point. This function does not check if `this` is empty. + * * Use [[IndexedXYZCollection.front]] to have the check. + */ + public frontUnchecked(result?: Point3d): Point3d { + return this.getPoint3dAtUncheckedPointIndex(0, result); + } /** Return the last point, or undefined if the array is empty. */ public back(result?: Point3d): Point3d | undefined { if (this.length === 0) return undefined; return this.getPoint3dAtUncheckedPointIndex(this.length - 1, result); } + /** + * Return the last point. This function does not check if `this` is empty. + * * Use [[IndexedXYZCollection.back]] to have the check. + */ + public backUnchecked(result?: Point3d): Point3d { + return this.getPoint3dAtUncheckedPointIndex(this.length - 1, result); + } /** * Test whether the indexed points are equal within tolerance. * @param index0 index of first point @@ -344,6 +420,17 @@ export abstract class IndexedXYZCollection { public almostEqualIndexIndex(index0: number, index1: number, tolerance = Geometry.smallMetricDistance): boolean | undefined { if (index0 < 0 || index0 >= this.length || index1 < 0 || index1 >= this.length) return undefined; + return this.almostEqualUncheckedIndexIndex(index0, index1, tolerance); + } + /** + * Test whether the indexed points are equal within tolerance. + * * This method does not check for index validity. Use [[IndexedXYZCollection.almostEqualIndexIndex]] to have validity test. + * @param index0 index of first point + * @param index1 index of second point + * @param tolerance max coordinate difference to be considered equal. For exact test, pass 0. Defaults to `Geometry.smallMetricDistance`. + * @returns whether the points are equal within tolerance. + */ + public almostEqualUncheckedIndexIndex(index0: number, index1: number, tolerance = Geometry.smallMetricDistance): boolean { return Geometry.isSameCoordinate(this.getXAtUncheckedPointIndex(index0), this.getXAtUncheckedPointIndex(index1), tolerance) && Geometry.isSameCoordinate(this.getYAtUncheckedPointIndex(index0), this.getYAtUncheckedPointIndex(index1), tolerance) && Geometry.isSameCoordinate(this.getZAtUncheckedPointIndex(index0), this.getZAtUncheckedPointIndex(index1), tolerance); @@ -358,6 +445,17 @@ export abstract class IndexedXYZCollection { public almostEqualXYIndexIndex(index0: number, index1: number, tolerance = Geometry.smallMetricDistance): boolean | undefined { if (index0 < 0 || index0 >= this.length || index1 < 0 || index1 >= this.length) return undefined; + return this.almostEqualXYUncheckedIndexIndex(index0, index1, tolerance); + } + /** + * Test whether the xy-coordinates of the indexed points are equal within tolerance. The z-coordinates are ignored. + * * This method does not check for index validity. Use [[IndexedXYZCollection.almostEqualXYIndexIndex]] to have validity test. + * @param index0 index of first point + * @param index1 index of second point + * @param tolerance max coordinate difference to be considered equal. For exact test, pass 0. Defaults to `Geometry.smallMetricDistance`. + * @returns whether the xy-coordinates of the points are equal within tolerance, or `undefined` if either index is invalid. + */ + public almostEqualXYUncheckedIndexIndex(index0: number, index1: number, tolerance = Geometry.smallMetricDistance): boolean { return Geometry.isSameCoordinate(this.getXAtUncheckedPointIndex(index0), this.getXAtUncheckedPointIndex(index1), tolerance) && Geometry.isSameCoordinate(this.getYAtUncheckedPointIndex(index0), this.getYAtUncheckedPointIndex(index1), tolerance); } diff --git a/core/geometry/src/geometry3d/Matrix3d.ts b/core/geometry/src/geometry3d/Matrix3d.ts index 169b56706a56..c586fb0c8651 100644 --- a/core/geometry/src/geometry3d/Matrix3d.ts +++ b/core/geometry/src/geometry3d/Matrix3d.ts @@ -493,11 +493,12 @@ export class Matrix3d implements BeJSONFunctions { * This is for use by matrix * matrix multiplications which need to be sure the member is there to be * filled with method-specific content. */ - private createInverseCoffsWithZeros() { + private createInverseCoffsWithZeros(): this is { inverseCoffs: Float64Array, inverseState: InverseMatrixState } { if (!this.inverseCoffs) { this.inverseState = InverseMatrixState.unknown; this.inverseCoffs = new Float64Array(9); } + return this.inverseCoffs !== undefined && this.inverseState !== undefined; } /** * Copy the transpose of the coffs to the inverseCoffs. @@ -553,10 +554,11 @@ export class Matrix3d implements BeJSONFunctions { for (let i = 0; i < 9; i++) this.coffs[i] = other.coffs[i]; if (other.inverseState === InverseMatrixState.inverseStored && other.inverseCoffs !== undefined) { - this.createInverseCoffsWithZeros(); - for (let i = 0; i < 9; i++) - this.inverseCoffs![i] = other.inverseCoffs[i]; - this.inverseState = InverseMatrixState.inverseStored; + if (this.createInverseCoffsWithZeros()) { + for (let i = 0; i < 9; i++) + this.inverseCoffs[i] = other.inverseCoffs[i]; + this.inverseState = InverseMatrixState.inverseStored; + } } else if (other.inverseState !== InverseMatrixState.inverseStored) { this.inverseState = other.inverseState; } else { // This is reached when other says stored but does not have coffs. This should not happen. @@ -1324,7 +1326,10 @@ export class Matrix3d implements BeJSONFunctions { } else if (!scaleXIsZero && !scaleYIsZero) { // rank 2 column[0].scaleInPlace(1 / scale.x); column[1].scaleInPlace(1 / scale.y); - column[2] = column[0].unitCrossProduct(column[1], column[2])!; + const crossProduct = column[0].unitCrossProduct(column[1], column[2]); + if (!crossProduct) + return false; + column[2] = crossProduct; matrixV.setColumns(column[0], column[1], column[2]); } else if (!scaleXIsZero) { // rank 1 matrixV = Matrix3d.createRigidHeadsUp(column[0], AxisOrder.XYZ, matrixV); // preserve column0 @@ -2091,10 +2096,9 @@ export class Matrix3d implements BeJSONFunctions { f: (factorA: Float64Array, factorB: Float64Array, result: Float64Array) => void, coffA?: Float64Array, coffB?: Float64Array, ): void { - if (coffA && coffB) { - this.createInverseCoffsWithZeros(); + if (coffA && coffB && this.createInverseCoffsWithZeros()) { this.inverseState = InverseMatrixState.inverseStored; - f(coffA, coffB, this.inverseCoffs!); // call function f (which is provided by user) to compute the inverse. + f(coffA, coffB, this.inverseCoffs); // call function f (which is provided by user) to compute the inverse. } else { this.inverseState = InverseMatrixState.unknown; } @@ -2130,7 +2134,7 @@ export class Matrix3d implements BeJSONFunctions { if (!other.computeCachedInverse(true)) return undefined; result = result ? result : new Matrix3d(); - PackedMatrix3dOps.multiplyMatrixMatrix(this.coffs, other.inverseCoffs!, Matrix3d._productBuffer); + PackedMatrix3dOps.multiplyMatrixMatrix(this.coffs, other.inverseCoffs, Matrix3d._productBuffer); if (this.inverseState === InverseMatrixState.inverseStored) result.finishInverseCoffs((a, b, _result) => PackedMatrix3dOps.multiplyMatrixMatrix(a, b, _result), other.coffs, this.inverseCoffs); else @@ -2145,8 +2149,8 @@ export class Matrix3d implements BeJSONFunctions { public multiplyMatrixInverseMatrix(other: Matrix3d, result?: Matrix3d): Matrix3d | undefined { if (!this.computeCachedInverse(true)) return undefined; + PackedMatrix3dOps.multiplyMatrixMatrix(this.inverseCoffs, other.coffs, Matrix3d._productBuffer); result = result ? result : new Matrix3d(); - PackedMatrix3dOps.multiplyMatrixMatrix(this.inverseCoffs!, other.coffs, Matrix3d._productBuffer); if (other.inverseState === InverseMatrixState.inverseStored) result.finishInverseCoffs((a, b, _result) => PackedMatrix3dOps.multiplyMatrixMatrix(a, b, _result), other.inverseCoffs, this.coffs); else @@ -2257,18 +2261,20 @@ export class Matrix3d implements BeJSONFunctions { if (result === this) { // swap the contents of this.coffs and this.inverseCoffs PackedMatrix3dOps.copy(this.coffs, Matrix3d._productBuffer); - PackedMatrix3dOps.copy(this.inverseCoffs!, this.coffs); - PackedMatrix3dOps.copy(Matrix3d._productBuffer, this.inverseCoffs!); + PackedMatrix3dOps.copy(this.inverseCoffs, this.coffs); + PackedMatrix3dOps.copy(Matrix3d._productBuffer, this.inverseCoffs); return result; } if (result === undefined) { result = Matrix3d.createIdentity(); } - result.createInverseCoffsWithZeros(); - PackedMatrix3dOps.copy(this.coffs, result.inverseCoffs!); - PackedMatrix3dOps.copy(this.inverseCoffs!, result.coffs); - result.inverseState = this.inverseState; - return result; + if (result.createInverseCoffsWithZeros()) { + PackedMatrix3dOps.copy(this.coffs, result.inverseCoffs); + PackedMatrix3dOps.copy(this.inverseCoffs, result.coffs); + result.inverseState = this.inverseState; + return result; + } + return undefined; } /** * Take the dot product of a row (specified by `rowStartA`) of `coffA` and `columnStartB` of `coffB`. @@ -2407,38 +2413,40 @@ export class Matrix3d implements BeJSONFunctions { * recompute the inverse. * @returns return `true` if the inverse is computed. Return `false` if matrix is singular. */ - public computeCachedInverse(useCacheIfAvailable: boolean): boolean { + public computeCachedInverse(useCacheIfAvailable: boolean): this is { inverseCoffs: Float64Array} { if (useCacheIfAvailable && Matrix3d.useCachedInverse && this.inverseState !== InverseMatrixState.unknown) { Matrix3d.numUseCache++; return this.inverseState === InverseMatrixState.inverseStored; } this.inverseState = InverseMatrixState.unknown; - this.createInverseCoffsWithZeros(); - const coffs = this.coffs; - const inverseCoffs = this.inverseCoffs!; - /** - * We calculate the inverse using cross products. - * Math details can be found at docs/learning/matrix/Matrix.md - * [ A ] - * In summary, if M = [ B ] then inverse of M = (1/det)[BxC CxA AxB] where - * [ C ] - * det is the determinant of matrix M (which is equal to "A dot BxC"). - */ - Matrix3d.indexedRowCrossProduct(coffs, 3, 6, inverseCoffs, 0); // BxC - Matrix3d.indexedRowCrossProduct(coffs, 6, 0, inverseCoffs, 1); // CxA - Matrix3d.indexedRowCrossProduct(coffs, 0, 3, inverseCoffs, 2); // AxB - Matrix3d.numComputeCache++; - const det = Matrix3d.rowColumnDot(coffs, 0, inverseCoffs, 0); // A dot BxC - if (det === 0.0) { - this.inverseState = InverseMatrixState.singular; - this.inverseCoffs = undefined; - return false; + if (this.createInverseCoffsWithZeros()) { + const coffs = this.coffs; + const inverseCoffs = this.inverseCoffs; + /** + * We calculate the inverse using cross products. + * Math details can be found at docs/learning/matrix/Matrix.md + * [ A ] + * In summary, if M = [ B ] then inverse of M = (1/det)[BxC CxA AxB] where + * [ C ] + * det is the determinant of matrix M (which is equal to "A dot BxC"). + */ + Matrix3d.indexedRowCrossProduct(coffs, 3, 6, inverseCoffs, 0); // BxC + Matrix3d.indexedRowCrossProduct(coffs, 6, 0, inverseCoffs, 1); // CxA + Matrix3d.indexedRowCrossProduct(coffs, 0, 3, inverseCoffs, 2); // AxB + Matrix3d.numComputeCache++; + const det = Matrix3d.rowColumnDot(coffs, 0, inverseCoffs, 0); // A dot BxC + if (det === 0.0) { + this.inverseState = InverseMatrixState.singular; + } else { + const f = 1.0 / det; + for (let i = 0; i < 9; i++) + inverseCoffs[i] *= f; + this.inverseState = InverseMatrixState.inverseStored; + return true; + } } - const f = 1.0 / det; - for (let i = 0; i < 9; i++) - inverseCoffs[i] *= f; - this.inverseState = InverseMatrixState.inverseStored; - return true; + this.inverseCoffs = undefined; + return false; } /** * Convert a (row,column) index pair to the single index within flattened array of 9 numbers in row-major-order diff --git a/core/geometry/src/geometry3d/Point3dArrayCarrier.ts b/core/geometry/src/geometry3d/Point3dArrayCarrier.ts index 278ed5ba513f..a006d6fba719 100644 --- a/core/geometry/src/geometry3d/Point3dArrayCarrier.ts +++ b/core/geometry/src/geometry3d/Point3dArrayCarrier.ts @@ -96,8 +96,19 @@ export class Point3dArrayCarrier extends IndexedReadWriteXYZCollection { */ public vectorIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined { if (this.isValidIndex(indexA) && this.isValidIndex(indexB)) - return Vector3d.createStartEnd(this.data[indexA], this.data[indexB], result); + return this.vectorUncheckedIndexIndex(indexA, indexB, result); return undefined; + } + /** + * Return a vector from the point at indexA to the point at indexB + * * This method does not check for index validity. Use [[Point3dArrayCarrier.vectorIndexIndex]] to have validity test. + * @param indexA index of point within the array + * @param indexB index of point within the array + * @param result caller-allocated vector. + * @returns undefined if either index is out of bounds + */ + public vectorUncheckedIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d { + return Vector3d.createStartEnd(this.data[indexA], this.data[indexB], result); } /** * Return a vector from given origin to point at indexB @@ -108,9 +119,19 @@ export class Point3dArrayCarrier extends IndexedReadWriteXYZCollection { */ public vectorXYAndZIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d | undefined { if (this.isValidIndex(indexB)) - return Vector3d.createStartEnd(origin, this.data[indexB], result); + return this.vectorXYAndZUncheckedIndex(origin, indexB, result); return undefined; } + /** + * Return a vector from given origin to point at indexB + * * This method does not check for index validity. Use [[Point3dArrayCarrier.vectorXYAndZIndex]] to have validity test. + * @param origin origin for vector + * @param indexB index of point within the array + * @param result caller-allocated vector. + */ + public vectorXYAndZUncheckedIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d { + return Vector3d.createStartEnd(origin, this.data[indexB], result); + } /** * Return the cross product of vectors from origin to points at indexA and indexB * @param origin origin for vector @@ -223,10 +244,19 @@ export class Point3dArrayCarrier extends IndexedReadWriteXYZCollection { */ public distanceSquaredIndexIndex(index0: number, index1: number): number | undefined { const n = this.data.length; - if (index0 >= 0 && index0 < n && index1 >= 0 && index1 < n) { - return this.data[index0].distanceSquared(this.data[index1]); - } + if (index0 >= 0 && index0 < n && index1 >= 0 && index1 < n) + return this.distanceSquaredUncheckedIndexIndex(index0, index1); return undefined; + } + /** + * Return distance squared between indicated points. + * * This method does not check for index validity. Use [[Point3dArrayCarrier.distanceSquaredIndexIndex]] to have + * validity test. + * @param index0 first point index + * @param index1 second point index + */ + public distanceSquaredUncheckedIndexIndex(index0: number, index1: number): number { + return this.data[index0].distanceSquared(this.data[index1]); } /** * Return distance between indicated points. @@ -236,9 +266,18 @@ export class Point3dArrayCarrier extends IndexedReadWriteXYZCollection { public distanceIndexIndex(index0: number, index1: number): number | undefined { const n = this.data.length; if (index0 >= 0 && index0 < n && index1 >= 0 && index1 < n) { - return this.data[index0].distance(this.data[index1]); + return this.distanceUncheckedIndexIndex(index0, index1); } return undefined; + } + /** + * Return distance between indicated points. + * * This method does not check for index validity. Use [[Point3dArrayCarrier.distanceIndexIndex]] to have validity test. + * @param index0 first point index + * @param index1 second point index + */ + public distanceUncheckedIndexIndex(index0: number, index1: number): number { + return this.data[index0].distance(this.data[index1]); } /** Adjust index into range by modulo with the length. */ public override cyclicIndex(i: number): number { diff --git a/core/geometry/src/geometry3d/PointStreaming.ts b/core/geometry/src/geometry3d/PointStreaming.ts index edafdc6f0822..6190aa15c8f5 100644 --- a/core/geometry/src/geometry3d/PointStreaming.ts +++ b/core/geometry/src/geometry3d/PointStreaming.ts @@ -40,8 +40,8 @@ export class PointStreamXYZXYZHandlerBase extends PointStreamXYZHandlerBase { private _y0?: number; private _z0?: number; public override handleXYZ(x: number, y: number, z: number): void { - if (this._x0 !== undefined) - this.handleXYZXYZ(this._x0, this._y0!, this._z0!, x, y, z); + if (this._x0 !== undefined && this._y0 !== undefined && this._z0 !== undefined) + this.handleXYZXYZ(this._x0, this._y0, this._z0, x, y, z); this._x0 = x; this._y0 = y; this._z0 = z; diff --git a/core/geometry/src/geometry3d/PolygonOps.ts b/core/geometry/src/geometry3d/PolygonOps.ts index 207267a4ab1e..1fe520dcab99 100644 --- a/core/geometry/src/geometry3d/PolygonOps.ts +++ b/core/geometry/src/geometry3d/PolygonOps.ts @@ -178,7 +178,7 @@ export class CutLoop { public static createCaptureWithReturnEdge(xyz: GrowableXYZArray): CutLoop { const result = new CutLoop(xyz); if (xyz.length >= 2) - result.edge = Ray3d.createStartEnd(xyz.front()!, xyz.back()!); + result.edge = Ray3d.createStartEnd(xyz.frontUnchecked(), xyz.backUnchecked()); return result; } /** @@ -188,8 +188,10 @@ export class CutLoop { * * Hence sorting on the coordinates puts loops in left-to-right order by the their edge vector leftmost point. */ public setSortCoordinates(ray: Ray3d) { - this.sortDelta = this.edge!.direction.dotProduct(ray.direction); - const a = ray.dotProductToPoint(this.edge!.origin); + if (!this.edge) + return; + this.sortDelta = this.edge.direction.dotProduct(ray.direction); + const a = ray.dotProductToPoint(this.edge.origin); if (this.sortDelta >= 0) { this.sortCoordinate0 = a; this.sortCoordinate1 = a + this.sortDelta; @@ -230,11 +232,11 @@ export class CutLoop { /** Return first point coordinates. * * For type checking, assume array is not empty. */ - public front(result?: Point3d): Point3d { return this.xyz.front(result)!; } + public front(result?: Point3d): Point3d { return this.xyz.frontUnchecked(result); } /** Return last point coordinates. * * For type checking, assume array is not empty. */ - public back(result?: Point3d): Point3d { return this.xyz.back(result)!; } + public back(result?: Point3d): Point3d { return this.xyz.backUnchecked(result); } } /** @@ -284,7 +286,7 @@ export class CutLoopMergeContext { */ private sortInputs() { if (this.inputLoops.length > 0 && this.inputLoops[0].xyz.length > 0) { - const point0 = this.inputLoops[0].xyz.front()!; + const point0 = this.inputLoops[0].xyz.frontUnchecked(); const workPoint = Point3d.create(); const point1 = Point3d.create(); // point0 could be in the middle. Find the most distant point ... @@ -564,9 +566,11 @@ export class PolygonOps { } const n = points.length; if (n === 3) { - const normal = points.crossProductIndexIndexIndex(0, 1, 2, result?.direction)!; + const normal = points.crossProductIndexIndexIndex(0, 1, 2, result?.direction); + if (!normal || normal.isAlmostZero) + return undefined; const a = 0.5 * normal.magnitude(); - const centroid = points.getPoint3dAtCheckedPointIndex(0, result?.origin)!; + const centroid = points.getPoint3dAtUncheckedPointIndex(0, result?.origin); points.accumulateScaledXYZ(1, 1.0, centroid); points.accumulateScaledXYZ(2, 1.0, centroid); centroid.scaleInPlace(1.0 / 3.0); @@ -583,7 +587,7 @@ export class PolygonOps { points.accumulateCrossProductIndexIndexIndex(0, i - 1, i, areaNormal); } areaNormal.normalizeInPlace(); - const origin = points.getPoint3dAtCheckedPointIndex(0)!; + const origin = points.getPoint3dAtUncheckedPointIndex(0); const vector0 = Vector3d.create(); const vector1 = Vector3d.create(); points.vectorXYAndZIndex(origin, 1, vector0); @@ -712,7 +716,7 @@ export class PolygonOps { const placement = PolygonOps._matrixA; const matrixAB = PolygonOps._matrixB; const matrixABC = PolygonOps._matrixC; - const vectorOrigin = points.vectorXYAndZIndex(origin, 0, PolygonOps._vectorOrigin)!; + const vectorOrigin = points.vectorXYAndZUncheckedIndex(origin, 0, PolygonOps._vectorOrigin); const numPoints = points.length; let detJ = 0; for (let i2 = 2; i2 < numPoints; i2++) { @@ -757,7 +761,7 @@ export class PolygonOps { let signedTruncatedPrismVolumeTimes6 = 0.0; const h0 = facetPoints.evaluateUncheckedIndexPlaneAltitude(0, plane); for (let i = 1; i + 1 < facetPoints.length; i++) { - const triangleNormal = facetPoints.crossProductIndexIndexIndex(0, i, i + 1, options?.workVector)!; + const triangleNormal = facetPoints.crossProductUncheckedIndexIndexIndex(0, i, i + 1, options?.workVector); const hA = facetPoints.evaluateUncheckedIndexPlaneAltitude(i, plane); const hB = facetPoints.evaluateUncheckedIndexPlaneAltitude(i + 1, plane); const signedProjectedTriangleAreaTimes2 = triangleNormal.dotProductXYZ(plane.normalX(), plane.normalY(), plane.normalZ()); @@ -1010,7 +1014,7 @@ export class PolygonOps { let numPoints = polygon.length; while (numPoints > 1) { - if (polygon.distanceSquaredIndexIndex(0, numPoints - 1)! > distTol2) + if (polygon.distanceSquaredUncheckedIndexIndex(0, numPoints - 1) > distTol2) break; --numPoints; // ignore closure point } @@ -1041,10 +1045,10 @@ export class PolygonOps { let iEdgeEnd = iEdgeStart + 1; if (iEdgeEnd === numPoints) iEdgeEnd = 0; - uDotU = polygon.distanceSquaredIndexIndex(iEdgeStart, iEdgeEnd)!; + uDotU = polygon.distanceSquaredUncheckedIndexIndex(iEdgeStart, iEdgeEnd); if (uDotU > distTol2) { // nontrivial edge - vDotV = polygon.distanceSquaredIndexXYAndZ(iEdgeStart, testPoint)!; - const uDotV = polygon.dotProductIndexIndexXYAndZ(iEdgeStart, iEdgeEnd, testPoint)!; + vDotV = polygon.distanceSquaredUncheckedIndexXYAndZ(iEdgeStart, testPoint); + const uDotV = polygon.dotProductUncheckedIndexIndexXYAndZ(iEdgeStart, iEdgeEnd, testPoint); edgeParam = uDotV / uDotU; // param of projection of testPoint onto edge [iEdgeStart, iEdgeEnd] isValid = true; } @@ -1091,7 +1095,7 @@ export class PolygonOps { // update candidate (to edge start) only if testPoint projected beyond previous edge end polygon.getPoint3dAtUncheckedPointIndex(iBase, result.point); result.a = Math.sqrt(distToStart2); - polygon.crossProductIndexIndexIndex(iBase, iPrev, iNext, result.v)!; + polygon.crossProductIndexIndexIndex(iBase, iPrev, iNext, result.v); result.code = PolygonLocation.OnPolygonVertex; result.closestEdgeIndex = iBase; result.closestEdgeParam = 0.0; @@ -1138,7 +1142,7 @@ export class PolygonOps { // update candidate polygon.interpolateIndexIndex(iBase, projData.edgeParam, iNext, result.point); result.a = Math.sqrt(projDist2); - polygon.crossProductIndexIndexXYAndZ(iBase, iNext, testPoint, result.v)!; + polygon.crossProductIndexIndexXYAndZ(iBase, iNext, testPoint, result.v); result.code = projData.edgeParam < 1.0 ? PolygonLocation.OnPolygonEdgeInterior : PolygonLocation.OnPolygonVertex;; result.closestEdgeIndex = iBase; result.closestEdgeParam = projData.edgeParam; @@ -1170,8 +1174,10 @@ export class PolygonOps { if (!(polygon instanceof IndexedXYZCollection)) return this.closestPoint(new Point3dArrayCarrier(polygon), testPoint, tolerance, result); if (!this.unitNormal(polygon, this._normal)) - return PolygonLocationDetail.create(result); // invalid - const polygonPlane = this._workPlane = Plane3dByOriginAndUnitNormal.createXYZUVW(polygon.getXAtUncheckedPointIndex(0), polygon.getYAtUncheckedPointIndex(0), polygon.getZAtUncheckedPointIndex(0), this._normal.x, this._normal.y, this._normal.z, this._workPlane)!; + return PolygonLocationDetail.create(result); // invalid + const polygonPlane = this._workPlane = Plane3dByOriginAndUnitNormal.createXYZUVW(polygon.getXAtUncheckedPointIndex(0), polygon.getYAtUncheckedPointIndex(0), polygon.getZAtUncheckedPointIndex(0), this._normal.x, this._normal.y, this._normal.z, this._workPlane); + if (!polygonPlane) + return PolygonLocationDetail.create(result); // invalid const planePoint = this._workXYZ = polygonPlane.projectPointToPlane(testPoint, this._workXYZ); result = this.closestPointOnBoundary(polygon, planePoint, tolerance, result); if (result.isValid) { @@ -1228,7 +1234,9 @@ export class PolygonOps { polygon.getZAtUncheckedPointIndex(0), this._normal.x, this._normal.y, this._normal.z, this._workPlane, - )!; + ); + if (!this._workPlane) + return PolygonLocationDetail.create(result); // invalid const intersectionPoint = this._workXYZ = Point3d.createZero(this._workXYZ); const rayParam = ray.intersectionWithPlane(this._workPlane, intersectionPoint); if (undefined === rayParam) @@ -1300,7 +1308,7 @@ export class PolygonOps { ): Point2d { const i0 = edgeStartVertexIndex % polygon.length; const i1 = (i0 + 1) % polygon.length; - polygon.vectorIndexIndex(i0, i1, edgeOutwardUnitNormal)!.unitPerpendicularXY(edgeOutwardUnitNormal).negate(edgeOutwardUnitNormal); // z is zero + polygon.vectorUncheckedIndexIndex(i0, i1, edgeOutwardUnitNormal).unitPerpendicularXY(edgeOutwardUnitNormal).negate(edgeOutwardUnitNormal); // z is zero const hypDeltaX = polygon.getXAtUncheckedPointIndex(i0) - point.x; const hypDeltaY = polygon.getYAtUncheckedPointIndex(i0) - point.y; let projDist = Geometry.dotProductXYXY(hypDeltaX, hypDeltaY, edgeOutwardUnitNormal.x, edgeOutwardUnitNormal.y); @@ -1390,9 +1398,21 @@ export class PolygonOps { return undefined; const localToWorld = this._workMatrix3d = Matrix3d.createRigidHeadsUp(this._normal, AxisOrder.ZXY, this._workMatrix3d); const polygonXY = new GrowableXYZArray(n); - for (let i = 0; i < n; ++i) - polygonXY.push(localToWorld.multiplyInverseXYZAsPoint3d(polygon.getXAtUncheckedPointIndex(i), polygon.getYAtUncheckedPointIndex(i), polygon.getZAtUncheckedPointIndex(i), this._workXYZ)!); - const pointXY = this._workXYZ = localToWorld.multiplyInverseXYZAsPoint3d(point.x, point.y, point.z, this._workXYZ)!; + for (let i = 0; i < n; ++i) { + const pt = localToWorld.multiplyInverseXYZAsPoint3d( + polygon.getXAtUncheckedPointIndex(i), + polygon.getYAtUncheckedPointIndex(i), + polygon.getZAtUncheckedPointIndex(i), + this._workXYZ, + ); + if (!pt) + return undefined; + polygonXY.push(pt); + } + const pointXY = localToWorld.multiplyInverseXYZAsPoint3d(point.x, point.y, point.z, this._workXYZ); + if (!pointXY) + return undefined; + this._workXYZ = pointXY; // now we know polygon orientation is ccw, its last edge has positive length, and we can ignore z-coords let iPrev = n - 1; const outwardUnitNormalOfLastEdge = this._vector0; @@ -1752,8 +1772,12 @@ export class IndexedXYZCollectionPolygonOps { return; // Simple cases: 2 loops . . . if (loops.inputLoops.length === 2) { - // if edges are in the same direction, it must be a pair of unrelated loop . . . - if (loops.inputLoops[0].edge!.direction.dotProduct(loops.inputLoops[1].edge!.direction) > 0) { + const edge0 = loops.inputLoops[0].edge; + const edge1 = loops.inputLoops[1].edge; + if (!edge0 || !edge1) + return; + // if edges are in the same direction, it must be a pair of unrelated loop + if (edge0.direction.dotProduct(edge1.direction) > 0) { loops.outputLoops.push(loops.inputLoops[0]); loops.outputLoops.push(loops.inputLoops[1]); return; diff --git a/core/geometry/src/geometry3d/PolylineCompressionByEdgeOffset.ts b/core/geometry/src/geometry3d/PolylineCompressionByEdgeOffset.ts index 4a7f0b837297..728056cc3351 100644 --- a/core/geometry/src/geometry3d/PolylineCompressionByEdgeOffset.ts +++ b/core/geometry/src/geometry3d/PolylineCompressionByEdgeOffset.ts @@ -76,7 +76,7 @@ export class PolylineCompressionContext { let distanceSquared; let s; let i; - this._source.vectorIndexIndex(i0, i1, PolylineCompressionContext._vector01)!; + this._source.vectorUncheckedIndexIndex(i0, i1, PolylineCompressionContext._vector01); const denominator = PolylineCompressionContext._vector01.magnitudeSquared(); for (let index = index0 + 1; index < index1; index++) { i = this._source.cyclicIndex(index); @@ -152,14 +152,14 @@ export class PolylineCompressionContext { dest.clear(); const n = source.length; if (n === 1) { - dest.push(source.getPoint3dAtCheckedPointIndex(0)!); + dest.push(source.getPoint3dAtUncheckedPointIndex(0)); return; } const context = new PolylineCompressionContext(source, dest, chordTolerance); // Do compression on inclusive interval from indexA to indexB, with indices interpreted cyclically if closed let indexA = 0; let indexB = n - 1; - if (n > 2 && !keepSeam && source.distanceIndexIndex(0, n - 1)! <= chordTolerance) { + if (n > 2 && !keepSeam && source.distanceUncheckedIndexIndex(0, n - 1) <= chordTolerance) { // cyclic data. It is possible that the wrap point itself has to be seen as an internal point. // do the search from point index where there is a large triangle . .. const maxCrossProductIndex = context.indexOfMaxCrossProduct(0, n - 1); @@ -185,7 +185,7 @@ export class PolylineCompressionContext { let lastAcceptedIndex = 0; // back up from final point .. let indexB = n - 1; - while (indexB > 0 && data.distanceIndexIndex(indexB - 1, n - 1)! <= maxEdgeLength) + while (indexB > 0 && data.distanceUncheckedIndexIndex(indexB - 1, n - 1) <= maxEdgeLength) indexB--; if (indexB === 0) { // Theres only one point there. @@ -197,7 +197,7 @@ export class PolylineCompressionContext { data.moveIndexToIndex(n - 1, indexB); let candidateIndex = lastAcceptedIndex + 1; while (candidateIndex <= indexB) { - const d = data.distanceIndexIndex(lastAcceptedIndex, candidateIndex)!; + const d = data.distanceUncheckedIndexIndex(lastAcceptedIndex, candidateIndex); if (d > maxEdgeLength) { data.moveIndexToIndex(candidateIndex, lastAcceptedIndex + 1); lastAcceptedIndex++; diff --git a/core/geometry/src/geometry3d/PolylineOps.ts b/core/geometry/src/geometry3d/PolylineOps.ts index 5d129825f821..cd6f6f61e095 100644 --- a/core/geometry/src/geometry3d/PolylineOps.ts +++ b/core/geometry/src/geometry3d/PolylineOps.ts @@ -246,8 +246,12 @@ export class PolylineOps { const bisectorPlanes: Plane3dByOriginAndUnitNormal[] = []; const point0 = packedPoints[0]; const point1 = packedPoints[1]; - const unit01 = Vector3d.createNormalizedStartEnd(point0, point1)!; - const perpendicular0 = Plane3dByOriginAndUnitNormal.create(point0, unit01)!; + const unit01 = Vector3d.createNormalizedStartEnd(point0, point1); + if (!unit01) + return undefined; + const perpendicular0 = Plane3dByOriginAndUnitNormal.create(point0, unit01); + if (!perpendicular0) + return undefined; const perpendicular1 = Plane3dByOriginAndUnitNormal.createXYPlane(); // FIRST point gets simple perpendicular bisectorPlanes.push(perpendicular0.clone()); @@ -264,7 +268,10 @@ export class PolylineOps { } } // LAST point gets simple perpendicular inherited from last pass - bisectorPlanes.push(Plane3dByOriginAndUnitNormal.create(packedPoints[packedPoints.length - 1], perpendicular0.getNormalRef())!); + const plane = Plane3dByOriginAndUnitNormal.create(packedPoints[packedPoints.length - 1], perpendicular0.getNormalRef()); + if (!plane) + return undefined; + bisectorPlanes.push(plane); // reset end planes to their average plane, but leave them alone if the closure point is a cusp const lastIndex = bisectorPlanes.length - 1; if (lastIndex > 0 && wrapIfPhysicallyClosed) { @@ -275,7 +282,10 @@ export class PolylineOps { const newBisectorPlane = Plane3dByOriginAndUnitNormal.create(firstPlane.getOriginRef(), newBisectorNormal); if (undefined !== newBisectorPlane) { bisectorPlanes[0] = newBisectorPlane; - bisectorPlanes[lastIndex] = Plane3dByOriginAndUnitNormal.create(lastPlane.getOriginRef(), newBisectorNormal)!; + const p = Plane3dByOriginAndUnitNormal.create(lastPlane.getOriginRef(), newBisectorNormal); + if (!p) + return undefined; + bisectorPlanes[lastIndex] = p; } } } diff --git a/core/geometry/src/geometry3d/Range.ts b/core/geometry/src/geometry3d/Range.ts index b5ef8c2e81aa..7b9157700df0 100644 --- a/core/geometry/src/geometry3d/Range.ts +++ b/core/geometry/src/geometry3d/Range.ts @@ -429,7 +429,7 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions const origin = transform.origin; if (!transform.matrix.computeCachedInverse(true)) return false; - const coffs = transform.matrix.inverseCoffs!; + const coffs = transform.matrix.inverseCoffs; const xx = x - origin.x; const yy = y - origin.y; const zz = z - origin.z; diff --git a/core/geometry/src/geometry3d/Ray3d.ts b/core/geometry/src/geometry3d/Ray3d.ts index 4433d999ed0c..8fbef82f2be5 100644 --- a/core/geometry/src/geometry3d/Ray3d.ts +++ b/core/geometry/src/geometry3d/Ray3d.ts @@ -228,13 +228,15 @@ export class Ray3d implements BeJSONFunctions { public cloneInverseTransformed(transform: Transform, result?: Ray3d): Ray3d | undefined { if (!transform.computeCachedInverse(true)) return undefined; - return Ray3d.create( - transform.multiplyInversePoint3d(this.origin, result?.origin)!, - transform.matrix.multiplyInverseXYZAsVector3d( - this.direction.x, this.direction.y, this.direction.z, result?.direction, - )!, - result, + const origin = transform.multiplyInversePoint3d(this.origin, result?.origin); + if (!origin) + return undefined; + const direction = transform.matrix.multiplyInverseXYZAsVector3d( + this.direction.x, this.direction.y, this.direction.z, result?.direction, ); + if (!direction) + return undefined; + return Ray3d.create(origin, direction, result); } /** Apply a transform in place. */ public transformInPlace(transform: Transform) { diff --git a/core/geometry/src/geometry3d/SortablePolygon.ts b/core/geometry/src/geometry3d/SortablePolygon.ts index 17add5e21a2f..ca8fb3861265 100644 --- a/core/geometry/src/geometry3d/SortablePolygon.ts +++ b/core/geometry/src/geometry3d/SortablePolygon.ts @@ -116,14 +116,13 @@ class PolygonCarrier extends SimpleRegionCarrier { } } public constructInteriorPointNearEdge(edgeIndex: number, fractionAlong: number): Point3d | undefined { - if (edgeIndex + 1 < this.data.length) { - const ray = Ray3d.createCapture( - this.data.interpolateIndexIndex(edgeIndex, fractionAlong, edgeIndex + 1)!, - this.data.vectorIndexIndex(edgeIndex, edgeIndex + 1)! - ); - return this.constructInteriorPoint(ray); - } - return undefined; + if (edgeIndex < 0 || edgeIndex + 1 >= this.data.length) + return undefined; + const ray = Ray3d.createCapture( + this.data.interpolateUncheckedIndexIndex(edgeIndex, fractionAlong, edgeIndex + 1), + this.data.vectorUncheckedIndexIndex(edgeIndex, edgeIndex + 1) + ); + return this.constructInteriorPoint(ray); } } @@ -285,11 +284,18 @@ export class SortablePolygon { loopData._loopCarrier.reverseForAreaSign(1.0); loopData.outputSetIndex = outputSets.length; outputSets.push([]); - outputSets[loopData.outputSetIndex].push(loopData._loopCarrier.grabPolygon()!); + const polygon = loopData._loopCarrier.grabPolygon(); + ///// I AM NOT SURE THIS IS THE CORRECT FIX + if (polygon) + outputSets[loopData.outputSetIndex].push(polygon); } else { loopData._loopCarrier.reverseForAreaSign(-1.0); - const outputSetIndex = loops[parentIndex!].outputSetIndex!; - outputSets[outputSetIndex].push(loopData._loopCarrier.grabPolygon()!); + let outputSetIndex : number | undefined; + if (undefined !== parentIndex) + outputSetIndex = loops[parentIndex].outputSetIndex; + const polygon = loopData._loopCarrier.grabPolygon(); + if (undefined !== outputSetIndex && polygon) + outputSets[outputSetIndex].push(polygon); } } return outputSets; @@ -307,7 +313,7 @@ export class SortablePolygon { if (!candidateData.isHole) { candidateData._loopCarrier.reverseForAreaSign(1.0); - const candidateLoop = candidateData._loopCarrier.grabLoop()!; + const candidateLoop = candidateData._loopCarrier.grabLoop(); let candidateParityRegion: ParityRegion | undefined; // find all directly contained children . . . for (let childIndex = candidateIndex + 1; childIndex < numLoops; childIndex++) { diff --git a/core/geometry/src/geometry3d/Transform.ts b/core/geometry/src/geometry3d/Transform.ts index d9ee31f2004d..558b6239407a 100644 --- a/core/geometry/src/geometry3d/Transform.ts +++ b/core/geometry/src/geometry3d/Transform.ts @@ -605,14 +605,16 @@ export class Transform implements BeJSONFunctions { return result; } result = []; - for (const point of points) - result.push( - this._matrix.multiplyInverseXYZAsPoint3d( - point.x - originX, - point.y - originY, - point.z - originZ, - )!, + for (const point of points) { + const pt = this._matrix.multiplyInverseXYZAsPoint3d( + point.x - originX, + point.y - originY, + point.z - originZ, ); + if (!pt) + return undefined; + result.push(pt); + } return result; } /** diff --git a/core/geometry/src/numerics/ClusterableArray.ts b/core/geometry/src/numerics/ClusterableArray.ts index 7f1fd79f84f9..8fb80af56390 100644 --- a/core/geometry/src/numerics/ClusterableArray.ts +++ b/core/geometry/src/numerics/ClusterableArray.ts @@ -421,8 +421,8 @@ export class ClusterableArray extends GrowableBlockedArray { currentClusterIndex++; numThisCluster = 0; } else { - if (numThisCluster === 0) // This is the first encounter with a new cluster - result.growablePackedPoints!.pushFromGrowableXYZArray(source, k); + if (numThisCluster === 0 && result.growablePackedPoints) // This is the first encounter with a new cluster + result.growablePackedPoints.pushFromGrowableXYZArray(source, k); result.oldToNew[k] = currentClusterIndex; numThisCluster++; } diff --git a/core/geometry/src/polyface/AuxData.ts b/core/geometry/src/polyface/AuxData.ts index d2778703f22e..616fe75514b9 100644 --- a/core/geometry/src/polyface/AuxData.ts +++ b/core/geometry/src/polyface/AuxData.ts @@ -9,7 +9,6 @@ // import { Point2d } from "./Geometry2d"; import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; import { Point3d } from "../geometry3d/Point3dVector3d"; import { NumberArray } from "../geometry3d/PointHelpers"; // import { Geometry } from "./Geometry"; @@ -251,7 +250,6 @@ export class PolyfaceAuxData { * @returns true if the channels were all successfully transformed. */ public tryTransformInPlace(transform: Transform): boolean { - let inverseRot: Matrix3d | undefined; const rot = transform.matrix; const det = rot.determinant(); const scale = Math.pow(Math.abs(det), 1 / 3) * (det >= 0 ? 1 : -1); @@ -264,16 +262,14 @@ export class PolyfaceAuxData { case AuxChannelDataType.Distance: { for (let i = 0; i < data.values.length; i++) data.values[i] *= scale; - break; } case AuxChannelDataType.Normal: { - inverseRot = inverseRot ?? rot.inverse(); + const inverseRot = rot.inverse(); if (!inverseRot) - return false; - + return false; transformPoints(data.values, (point) => { - inverseRot!.multiplyTransposeVectorInPlace(point); + inverseRot.multiplyTransposeVectorInPlace(point); const dot = point.magnitudeSquared(); const tol = 1.0e-15; // cf. GrowableXYZArray.multiplyAndRenormalizeMatrix3dInverseTransposeInPlace if (dot > tol && Math.abs(dot - 1.0) > tol ) { // only renormalize if magnitude is not near 0 or 1 diff --git a/core/geometry/src/polyface/GreedyTriangulationBetweenLineStrings.ts b/core/geometry/src/polyface/GreedyTriangulationBetweenLineStrings.ts index 6b64853d135f..14920017a010 100644 --- a/core/geometry/src/polyface/GreedyTriangulationBetweenLineStrings.ts +++ b/core/geometry/src/polyface/GreedyTriangulationBetweenLineStrings.ts @@ -277,7 +277,7 @@ function resolveToNoDuplicates(data: IndexedXYZCollection, tolerance = Geometry. let hasDuplicates = false; const n = data.length; for (let i = 0; i + 1 < n; i++) { - if (data.distanceIndexIndex(i, i + 1)! <= tolerance) { + if (data.distanceUncheckedIndexIndex(i, i + 1) <= tolerance) { hasDuplicates = true; break; } @@ -288,13 +288,13 @@ function resolveToNoDuplicates(data: IndexedXYZCollection, tolerance = Geometry. result.pushXYZ(data.getXAtUncheckedPointIndex(0), data.getYAtUncheckedPointIndex(0), data.getZAtUncheckedPointIndex(0)); let i0 = 0; for (let i = 1; i < n; i++) { - if (data.distanceIndexIndex(i0, i)! > tolerance) { + if (data.distanceUncheckedIndexIndex(i0, i) > tolerance) { result.pushXYZ(data.getXAtUncheckedPointIndex(i), data.getYAtUncheckedPointIndex(i), data.getZAtUncheckedPointIndex(i)); i0 = i; } } /** enforce exact closure if original was closed. */ - if (data.distanceIndexIndex(0, n - 1)! <= tolerance) { + if (data.distanceUncheckedIndexIndex(0, n - 1) <= tolerance) { result.pop(); result.pushFromGrowableXYZArray(result, 0); } diff --git a/core/geometry/src/polyface/Polyface.ts b/core/geometry/src/polyface/Polyface.ts index f9ccb81e84ac..c8eae18f5617 100644 --- a/core/geometry/src/polyface/Polyface.ts +++ b/core/geometry/src/polyface/Polyface.ts @@ -289,7 +289,7 @@ export class IndexedPolyface extends Polyface { // more info can be found at geo if (undefined !== this.data.normal && undefined !== source.data.normal && undefined !== source.data.normalIndex) { const startOfNewNormals = this.data.normal.length; for (let i = 0; i < source.data.normal.length; i++) { - const sourceNormal = source.data.normal.getVector3dAtCheckedVectorIndex(i)!; + const sourceNormal = source.data.normal.getVector3dAtUncheckedVectorIndex(i); if (transform) transform.multiplyVector(sourceNormal, sourceNormal); if (reversed) @@ -427,7 +427,9 @@ export class IndexedPolyface extends Polyface { // more info can be found at geo public addNormal(normal: Vector3d, priorIndexA?: number, priorIndexB?: number): number { // check if `normal` is duplicate of `dataNormal` at index `i` const normalIsDuplicate = (i: number) => { - const distance = this.data.normal!.distanceIndexToPoint(i, normal); + if (!this.data.normal) + return false; + const distance = this.data.normal.distanceIndexToPoint(i, normal); return distance !== undefined && Geometry.isSmallMetricDistance(distance); }; if (this.data.normal !== undefined) { @@ -669,7 +671,8 @@ export class IndexedPolyface extends Polyface { // more info can be found at geo if (setParamRange && visitor.param !== undefined) visitor.param.extendRange(faceData.paramRange); } while (visitor.moveToNextFacet() && visitor.currentReadIndex() < endFacetIndex); - if (paramDefined && !(this.data.param!.length === 0) && faceData.paramDistanceRange.isNull) + const param = this.data.param; + if (paramDefined && param && param.length !== 0 && faceData.paramDistanceRange.isNull) faceData.setParamDistanceRangeFromNewFaceData(this, facetStart, endFacetIndex); this.data.face.push(faceData); const faceDataIndex = this.data.face.length - 1; diff --git a/core/geometry/src/polyface/PolyfaceBuilder.ts b/core/geometry/src/polyface/PolyfaceBuilder.ts index 21ce0881fb08..be31caa31c94 100644 --- a/core/geometry/src/polyface/PolyfaceBuilder.ts +++ b/core/geometry/src/polyface/PolyfaceBuilder.ts @@ -149,15 +149,15 @@ class FacetSector { public static computeNormalsAlongRuleLine(sectorA: FacetSector, sectorB: FacetSector) { // We expect that if a sector's sectionDerivative is defined, then so is its normal. If a normal is undefined, the // crossProduct returns an object that goes unused---not good, but the garbage collector will clean it up. - if (sectorA.sectionDerivative && sectorB.sectionDerivative) { + if (sectorA.sectionDerivative && sectorB.sectionDerivative && sectorA.normal && sectorB.normal) { const vectorAB = FacetSector._edgeVector; Vector3d.createStartEnd(sectorA.xyz, sectorB.xyz, vectorAB); sectorA.sectionDerivative.crossProduct(vectorAB, sectorA.normal); sectorB.sectionDerivative.crossProduct(vectorAB, sectorB.normal); - sectorA.normal!.normalizeInPlace(); - sectorB.normal!.normalizeInPlace(); - FacetSector.suppressSmallUnitVectorComponents(sectorA.normal!); - FacetSector.suppressSmallUnitVectorComponents(sectorB.normal!); + sectorA.normal.normalizeInPlace(); + sectorB.normal.normalizeInPlace(); + FacetSector.suppressSmallUnitVectorComponents(sectorA.normal); + FacetSector.suppressSmallUnitVectorComponents(sectorB.normal); } } } @@ -295,10 +295,12 @@ export class PolyfaceBuilder extends NullGeometryHandler { if (toggle) this.toggleReversedFacetFlag(); const index0 = this.addPoint(conePoint); - let index1 = this.findOrAddPointInLineString(ls, 0)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let index1 = this.findOrAddPointInLineString(ls, 0)!; // never undefined because index is valid let index2 = 0; for (let i = 1; i < n; i++) { - index2 = this.findOrAddPointInLineString(ls, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index2 = this.findOrAddPointInLineString(ls, i)!; // never undefined because index is valid this.addIndexedTrianglePointIndexes(index0, index1, index2); index1 = index2; } @@ -320,10 +322,12 @@ export class PolyfaceBuilder extends NullGeometryHandler { let normal; let normalIndex; if (this._options.needNormals) { - normal = ls.quickUnitNormal(PolyfaceBuilder._workVectorFindOrAdd)!; - if (toggle) - normal.scaleInPlace(-1.0); - normalIndex = this._polyface.addNormal(normal); + normal = ls.quickUnitNormal(PolyfaceBuilder._workVectorFindOrAdd); + if (normal) { + if (toggle) + normal.scaleInPlace(-1.0); + normalIndex = this._polyface.addNormal(normal); + } } const needParams = this._options.needParams; const packedUV = needParams ? ls.packedUVParams : undefined; @@ -331,22 +335,28 @@ export class PolyfaceBuilder extends NullGeometryHandler { let paramIndex1 = -1; let paramIndex2 = -1; if (packedUV) { - paramIndex0 = this.addParamInGrowableXYArray(packedUV, 0)!; - paramIndex1 = this.addParamInGrowableXYArray(packedUV, 1)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + paramIndex0 = this.addParamInGrowableXYArray(packedUV, 0)!; // never undefined because packedUV is defined and index is valid + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + paramIndex1 = this.addParamInGrowableXYArray(packedUV, 1)!; // never undefined because packedUV is defined and index is valid } - const pointIndex0 = this.findOrAddPointInLineString(ls, 0)!; - let pointIndex1 = this.findOrAddPointInLineString(ls, 1)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const pointIndex0 = this.findOrAddPointInLineString(ls, 0)!; // never undefined because index is valid + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let pointIndex1 = this.findOrAddPointInLineString(ls, 1)!; // never undefined because index is valid let pointIndex2 = 0; let numEdge = n; if (ls.isPhysicallyClosed) numEdge--; for (let i = 2; i < numEdge; i++, pointIndex1 = pointIndex2, paramIndex1 = paramIndex2) { - pointIndex2 = this.findOrAddPointInLineString(ls, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + pointIndex2 = this.findOrAddPointInLineString(ls, i)!; // never undefined because index is valid this.addIndexedTrianglePointIndexes(pointIndex0, pointIndex1, pointIndex2, false); if (normalIndex !== undefined) this.addIndexedTriangleNormalIndexes(normalIndex, normalIndex, normalIndex); if (packedUV) { - paramIndex2 = this.addParamInGrowableXYArray(packedUV, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + paramIndex2 = this.addParamInGrowableXYArray(packedUV, i)!; // never undefined because packedUV is defined and index is valid this.addIndexedTriangleParamIndexes(paramIndex0, paramIndex1, paramIndex2); } this._polyface.terminateFacet(); @@ -521,9 +531,9 @@ export class PolyfaceBuilder extends NullGeometryHandler { const needParams = this.options.needParams; const needNormals = this.options.needNormals; const needColors = this.options.needColors; - let param0: Point2d, param1: Point2d, param2: Point2d, param3: Point2d; - let normal0: Vector3d, normal1: Vector3d, normal2: Vector3d, normal3: Vector3d; - let color0: number, color1: number, color2: number, color3: number; + let param0 = Point2d.createZero(), param1 = Point2d.createZero(), param2 = Point2d.createZero(), param3 = Point2d.createZero(); + let normal0 = Vector3d.createZero(), normal1 = Vector3d.createZero(), normal2 = Vector3d.createZero(), normal3 = Vector3d.createZero(); + let color0 = 0, color1 = 0, color2 = 0, color3 = 0; if (needParams) { if (params !== undefined && params.length > 3) { param0 = params[0]; @@ -561,6 +571,8 @@ export class PolyfaceBuilder extends NullGeometryHandler { color1 = colors[1]; color2 = colors[2]; color3 = colors[3]; + } else { + throw new Error("PolyfaceBuilder.addQuadFacet: need colors but insufficient input"); } } if (this._options.shouldTriangulate) { @@ -571,28 +583,28 @@ export class PolyfaceBuilder extends NullGeometryHandler { if (vectorAC.magnitude() >= vectorBD.magnitude()) { this.addTriangleFacet( [points[0], points[1], points[2]], - needParams ? [param0!, param1!, param2!] : undefined, - needNormals ? [normal0!, normal1!, normal2!] : undefined, - needColors ? [color0!, color1!, color2!] : undefined, + needParams ? [param0, param1, param2] : undefined, + needNormals ? [normal0, normal1, normal2] : undefined, + needColors ? [color0, color1, color2] : undefined, ); this.addTriangleFacet( [points[0], points[2], points[3]], - needParams ? [param0!, param2!, param3!] : undefined, - needNormals ? [normal0!, normal2!, normal3!] : undefined, - needColors ? [color0!, color2!, color3!] : undefined, + needParams ? [param0, param2, param3] : undefined, + needNormals ? [normal0, normal2, normal3] : undefined, + needColors ? [color0, color2, color3] : undefined, ); } else { this.addTriangleFacet( [points[0], points[1], points[3]], - needParams ? [param0!, param1!, param3!] : undefined, - needNormals ? [normal0!, normal1!, normal3!] : undefined, - needColors ? [color0!, color1!, color3!] : undefined, + needParams ? [param0, param1, param3] : undefined, + needNormals ? [normal0, normal1, normal3] : undefined, + needColors ? [color0, color1, color3] : undefined, ); this.addTriangleFacet( [points[1], points[2], points[3]], - needParams ? [param1!, param2!, param3!] : undefined, - needNormals ? [normal1!, normal2!, normal3!] : undefined, - needColors ? [color1!, color2!, color3!] : undefined, + needParams ? [param1, param2, param3] : undefined, + needNormals ? [normal1, normal2, normal3] : undefined, + needColors ? [color1, color2, color3] : undefined, ); } return; @@ -600,26 +612,26 @@ export class PolyfaceBuilder extends NullGeometryHandler { let idx0, idx1, idx2, idx3; // add params if needed if (needParams) { - idx0 = this._polyface.addParam(param0!); - idx1 = this._polyface.addParam(param1!); - idx2 = this._polyface.addParam(param2!); - idx3 = this._polyface.addParam(param3!); + idx0 = this._polyface.addParam(param0); + idx1 = this._polyface.addParam(param1); + idx2 = this._polyface.addParam(param2); + idx3 = this._polyface.addParam(param3); this.addIndexedQuadParamIndexes(idx0, idx1, idx3, idx2); } // add normals if needed if (needNormals) { - idx0 = this._polyface.addNormal(normal0!); - idx1 = this._polyface.addNormal(normal1!); - idx2 = this._polyface.addNormal(normal2!); - idx3 = this._polyface.addNormal(normal3!); + idx0 = this._polyface.addNormal(normal0); + idx1 = this._polyface.addNormal(normal1); + idx2 = this._polyface.addNormal(normal2); + idx3 = this._polyface.addNormal(normal3); this.addIndexedQuadNormalIndexes(idx0, idx1, idx3, idx2); } // add colors if needed if (needColors) { - idx0 = this._polyface.addColor(color0!); - idx1 = this._polyface.addColor(color1!); - idx2 = this._polyface.addColor(color2!); - idx3 = this._polyface.addColor(color3!); + idx0 = this._polyface.addColor(color0); + idx1 = this._polyface.addColor(color1); + idx2 = this._polyface.addColor(color2); + idx3 = this._polyface.addColor(color3); this.addIndexedQuadColorIndexes(idx0, idx1, idx3, idx2); } // add point and point indexes last (terminates the facet) @@ -714,9 +726,9 @@ export class PolyfaceBuilder extends NullGeometryHandler { let idx2: number; let point0, point1, point2; if (points instanceof GrowableXYZArray) { - point0 = points.getPoint3dAtCheckedPointIndex(0)!; - point1 = points.getPoint3dAtCheckedPointIndex(1)!; - point2 = points.getPoint3dAtCheckedPointIndex(2)!; + point0 = points.getPoint3dAtUncheckedPointIndex(0); + point1 = points.getPoint3dAtUncheckedPointIndex(1); + point2 = points.getPoint3dAtUncheckedPointIndex(2); } else { point0 = points[0]; point1 = points[1]; @@ -912,10 +924,14 @@ export class PolyfaceBuilder extends NullGeometryHandler { /** * Add facets between lineStrings with matched point counts. * * Indices of points, normals, and uv parameters are pre-stored in the linestrings. + * * `pointIndices`, `normalIndices`, and `paramIndices` (if used) for both linestrings must be defined and + * have matched index counts. */ public addBetweenLineStringsWithStoredIndices(lineStringA: LineString3d, lineStringB: LineString3d): void { - const pointA = lineStringA.pointIndices!; - const pointB = lineStringB.pointIndices!; + const pointA = lineStringA.pointIndices; + const pointB = lineStringB.pointIndices; + if (!pointA || !pointB || pointA.length < 1 || pointA.length !== pointB.length) + return; let normalA: GrowableFloat64Array | undefined = lineStringA.normalIndices; let normalB: GrowableFloat64Array | undefined = lineStringB.normalIndices; if (!this._options.needNormals) { @@ -991,15 +1007,19 @@ export class PolyfaceBuilder extends NullGeometryHandler { if (curves instanceof LineString3d) { const pointA = curves.points; const numPoints = pointA.length; - let indexA0 = this.findOrAddPointInLineString(curves, 0, transformA)!; - let indexB0 = this.findOrAddPointInLineString(curves, 0, transformB)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let indexA0 = this.findOrAddPointInLineString(curves, 0, transformA)!; // never undefined because index is valid + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let indexB0 = this.findOrAddPointInLineString(curves, 0, transformB)!; // never undefined because index is valid const indexA00 = indexA0; const indexB00 = indexB0; let indexA1 = 0; let indexB1 = 0; for (let i = 1; i < numPoints; i++) { - indexA1 = this.findOrAddPointInLineString(curves, i, transformA)!; - indexB1 = this.findOrAddPointInLineString(curves, i, transformB)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + indexA1 = this.findOrAddPointInLineString(curves, i, transformA)!; // never undefined because index is valid + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + indexB1 = this.findOrAddPointInLineString(curves, i, transformB)!; // never undefined because index is valid this.addIndexedQuadPointIndexes(indexA0, indexA1, indexB0, indexB1); indexA0 = indexA1; indexB0 = indexB1; @@ -1081,8 +1101,8 @@ export class PolyfaceBuilder extends NullGeometryHandler { const sizes = surface.maxIsoParametricDistance(); this.addUVGridBody(surface, numU, numV, Segment1d.create(0, sizes.x), Segment1d.create(0, sizes.y)); this.toggleReversedFacetFlag(); - if (surface.capped && thetaFraction < 1.0) { - const centerFrame = surface.getConstructiveFrame()!; + const centerFrame = surface.getConstructiveFrame(); + if (centerFrame && surface.capped && thetaFraction < 1.0) { const minorRadius = surface.getMinorRadius(); const majorRadius = surface.getMajorRadius(); const a = 2 * minorRadius; @@ -1136,7 +1156,7 @@ export class PolyfaceBuilder extends NullGeometryHandler { let indexB1 = 0; const n = contour.numPoints(); for (let i = 0; i < n; i++) { - pointA = contour.pointAt(i, pointA)!; + pointA = contour.uncheckedPointAt(i, pointA); pointB = pointA.plus(vector, pointB); indexA1 = this.addPoint(pointA); indexB1 = this.addPoint(pointB); @@ -1228,53 +1248,64 @@ export class PolyfaceBuilder extends NullGeometryHandler { } private createIndicesInLineString(ls: LineString3d, vParam: number, transform?: Transform) { const n = ls.numPoints(); + if (n === 0) + return; const pointIndices = ls.ensureEmptyPointIndices(); - const index0 = this.findOrAddPointInLineString(ls, 0, transform); - pointIndices.push(index0!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const index0 = this.findOrAddPointInLineString(ls, 0, transform)!; // never undefined because index is valid + pointIndices.push(index0); if (n > 1) { let indexA = index0; let indexB: number | undefined; for (let i = 1; i + 1 < n; i++) { - indexB = this.findOrAddPointInLineString(ls, i, transform, indexA); - pointIndices.push(indexB!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + indexB = this.findOrAddPointInLineString(ls, i, transform, indexA)!; // never undefined because index is valid + pointIndices.push(indexB); indexA = indexB; } // assume last point can only repeat back to zero - indexB = this.findOrAddPointInLineString(ls, n - 1, transform, index0); - pointIndices.push(indexB!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + indexB = this.findOrAddPointInLineString(ls, n - 1, transform, index0)!; // never undefined because index is valid + pointIndices.push(indexB); } if (this._options.needNormals && ls.packedSurfaceNormals !== undefined) { const normalIndices = ls.ensureEmptyNormalIndices(); - const normalIndex0 = this.findOrAddNormalInLineString(ls, 0, transform); - normalIndices.push(normalIndex0!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const normalIndex0 = this.findOrAddNormalInLineString(ls, 0, transform)!; // never undefined because index is valid + normalIndices.push(normalIndex0); if (n > 1) { let normalIndexA = normalIndex0; let normalIndexB: number | undefined; for (let i = 1; i + 1 < n; i++) { - normalIndexB = this.findOrAddNormalInLineString(ls, i, transform, normalIndexA); - normalIndices.push(normalIndexB!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + normalIndexB = this.findOrAddNormalInLineString(ls, i, transform, normalIndexA)!; // never undefined because index is valid + normalIndices.push(normalIndexB); normalIndexA = normalIndexB; } // assume last point can only repeat back to zero - normalIndexB = this.findOrAddNormalInLineString(ls, n - 1, transform, normalIndex0, normalIndexA); - normalIndices.push(normalIndexB!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + normalIndexB = this.findOrAddNormalInLineString(ls, n - 1, transform, normalIndex0, normalIndexA)!; // never undefined because index is valid + normalIndices.push(normalIndexB); } } if (this._options.needParams && ls.packedUVParams !== undefined) { const uvIndices = ls.ensureEmptyUVIndices(); - const uvIndex0 = this.findOrAddParamInLineString(ls, 0, vParam); - uvIndices.push(uvIndex0!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const uvIndex0 = this.findOrAddParamInLineString(ls, 0, vParam)!; // never undefined because index is valid + uvIndices.push(uvIndex0); if (n > 1) { let uvIndexA = uvIndex0; let uvIndexB: number | undefined; for (let i = 1; i + 1 < n; i++) { - uvIndexB = this.findOrAddParamInLineString(ls, i, vParam, uvIndexA); - uvIndices.push(uvIndexB!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + uvIndexB = this.findOrAddParamInLineString(ls, i, vParam, uvIndexA)!; // never undefined because index is valid + uvIndices.push(uvIndexB); uvIndexA = uvIndexB; } // assume last point can only repeat back to zero - uvIndexB = this.findOrAddParamInLineString(ls, n - 1, vParam, uvIndexA, uvIndex0); - uvIndices.push(uvIndexB!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + uvIndexB = this.findOrAddParamInLineString(ls, n - 1, vParam, uvIndexA, uvIndex0)!; // never undefined because index is valid + uvIndices.push(uvIndexB); } } } @@ -1456,18 +1487,18 @@ export class PolyfaceBuilder extends NullGeometryHandler { public addPolygonGrowableXYZArray(points: GrowableXYZArray): void { // don't use trailing points that match start point let numPointsToUse = points.length; - while (numPointsToUse > 2 && Geometry.isSmallMetricDistance(points.distanceIndexIndex(0, numPointsToUse - 1)!)) + while (numPointsToUse > 2 && Geometry.isSmallMetricDistance(points.distanceUncheckedIndexIndex(0, numPointsToUse - 1))) numPointsToUse--; // strip trailing duplicates - while (numPointsToUse > 2 && Geometry.isSmallMetricDistance(points.distanceIndexIndex(numPointsToUse - 2, numPointsToUse - 1)!)) + while (numPointsToUse > 2 && Geometry.isSmallMetricDistance(points.distanceUncheckedIndexIndex(numPointsToUse - 2, numPointsToUse - 1))) numPointsToUse--; // ignore triangles for which the height is less than smallMetricDistance times length. // sum of edge lengths is twice the perimeter. If it is flat that's twice the largest base dimension. // cross product magnitude is twice the area. if (numPointsToUse === 3) { - const cross = points.crossProductIndexIndexIndex(0, 1, 2)!; + const cross = points.crossProductUncheckedIndexIndexIndex(0, 1, 2); const q = cross.magnitude(); - const p = points.distanceIndexIndex(0, 1)! + points.distanceIndexIndex(0, 2)! + points.distanceIndexIndex(1, 2)!; + const p = points.distanceUncheckedIndexIndex(0, 1) + points.distanceUncheckedIndexIndex(0, 2) + points.distanceUncheckedIndexIndex(1, 2); if (q < Geometry.smallMetricDistance * p) numPointsToUse = 0; } @@ -1475,12 +1506,14 @@ export class PolyfaceBuilder extends NullGeometryHandler { let index = 0; if (!this._reversed) { for (let i = 0; i < numPointsToUse; i++) { - index = this.findOrAddPointInGrowableXYZArray(points, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.findOrAddPointInGrowableXYZArray(points, i)!; // never undefined because index is valid this._polyface.addPointIndex(index); } } else { for (let i = numPointsToUse; --i >= 0;) { - index = this.findOrAddPointInGrowableXYZArray(points, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.findOrAddPointInGrowableXYZArray(points, i)!; // never undefined because index is valid this._polyface.addPointIndex(index); } } @@ -1508,7 +1541,7 @@ export class PolyfaceBuilder extends NullGeometryHandler { ): void { // don't use trailing points that match start point let numPointsToUse = points.length; - while (numPointsToUse > 1 && Geometry.isSmallMetricDistance(points.distanceIndexIndex(0, numPointsToUse - 1)!)) + while (numPointsToUse > 1 && Geometry.isSmallMetricDistance(points.distanceUncheckedIndexIndex(0, numPointsToUse - 1))) numPointsToUse--; let index = 0; if (normals && normals.length < numPointsToUse) @@ -1521,14 +1554,17 @@ export class PolyfaceBuilder extends NullGeometryHandler { edgeVisible = undefined; if (!this._reversed) { for (let i = 0; i < numPointsToUse; i++) { - index = this.findOrAddPointInGrowableXYZArray(points, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.findOrAddPointInGrowableXYZArray(points, i)!; // never undefined because index is valid this._polyface.addPointIndex(index, edgeVisible ? edgeVisible[i] : true); if (normals) { - index = this.findOrAddNormalInGrowableXYZArray(normals, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.findOrAddNormalInGrowableXYZArray(normals, i)!; // never undefined because index is valid this._polyface.addNormalIndex(index); } if (params) { - index = this.addParamInGrowableXYArray(params, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.addParamInGrowableXYArray(params, i)!; // never undefined because index is valid this._polyface.addParamIndex(index); } if (colors) { @@ -1538,14 +1574,17 @@ export class PolyfaceBuilder extends NullGeometryHandler { } } else { for (let i = numPointsToUse; --i >= 0;) { - index = this.findOrAddPointInGrowableXYZArray(points, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.findOrAddPointInGrowableXYZArray(points, i)!; // never undefined because index is valid this._polyface.addPointIndex(index); if (normals) { - index = this.findOrAddNormalInGrowableXYZArray(normals, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.findOrAddNormalInGrowableXYZArray(normals, i)!; // never undefined because index is valid this._polyface.addNormalIndex(index); } if (params) { - index = this.addParamInGrowableXYArray(params, i)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + index = this.addParamInGrowableXYArray(params, i)!; // never undefined because index is valid this._polyface.addParamIndex(index); } if (colors) { @@ -1773,7 +1812,9 @@ export class PolyfaceBuilder extends NullGeometryHandler { if (!localToWorld) localToWorld = FrameBuilder.createFrameWithCCWPolygon(points); if (localToWorld) { - const localPoints = localToWorld.multiplyInversePoint3dArray(points)!; + const localPoints = localToWorld.multiplyInversePoint3dArray(points); + if (!localPoints) + return undefined; const areaXY = PolygonOps.areaXY(localPoints); if (areaXY < 0.0) localPoints.reverse(); @@ -1851,24 +1892,24 @@ export class PolyfaceBuilder extends NullGeometryHandler { for (let v = 0; v <= numV; v++) { // evaluate new points xyzIndex1.clear(); - if (needNormals) - normalIndex1!.clear(); - if (needParams) - paramIndex1!.clear(); + if (needNormals && normalIndex1) + normalIndex1.clear(); + if (needParams && paramIndex1) + paramIndex1.clear(); for (let u = 0; u <= numU; u++) { const uFrac = u * du; const vFrac = v * dv; surface.uvFractionToPointAndTangents(uFrac, vFrac, plane); xyzIndex1.push(this._polyface.addPoint(plane.origin)); - if (needNormals) { + if (needNormals && normalIndex1) { plane.vectorU.crossProduct(plane.vectorV, normal); normal.normalizeInPlace(); if (reverse) normal.scaleInPlace(-1.0); - normalIndex1!.push(this._polyface.addNormal(normal)); + normalIndex1.push(this._polyface.addNormal(normal)); } - if (needParams) - paramIndex1!.push( + if (needParams && paramIndex1) + paramIndex1.push( this._polyface.addParam( Point2d.create(uMap ? uMap.fractionToPoint(uFrac) : uFrac, vMap ? vMap.fractionToPoint(vFrac) : vFrac, uv), ), @@ -1882,30 +1923,30 @@ export class PolyfaceBuilder extends NullGeometryHandler { xyzIndex1.atUncheckedIndex(u), xyzIndex1.atUncheckedIndex(u + 1), false, ); - if (needNormals) + if (needNormals && normalIndex0 && normalIndex1) this.addIndexedQuadNormalIndexes( - normalIndex0!.atUncheckedIndex(u), normalIndex0!.atUncheckedIndex(u + 1), - normalIndex1!.atUncheckedIndex(u), normalIndex1!.atUncheckedIndex(u + 1), + normalIndex0.atUncheckedIndex(u), normalIndex0.atUncheckedIndex(u + 1), + normalIndex1.atUncheckedIndex(u), normalIndex1.atUncheckedIndex(u + 1), ); - if (needParams) + if (needParams && paramIndex0 && paramIndex1) this.addIndexedQuadParamIndexes( - paramIndex0!.atUncheckedIndex(u), paramIndex0!.atUncheckedIndex(u + 1), - paramIndex1!.atUncheckedIndex(u), paramIndex1!.atUncheckedIndex(u + 1), + paramIndex0.atUncheckedIndex(u), paramIndex0.atUncheckedIndex(u + 1), + paramIndex1.atUncheckedIndex(u), paramIndex1.atUncheckedIndex(u + 1), ); this._polyface.terminateFacet(); } else { this.addIndexedTrianglePointIndexes( xyzIndex0.atUncheckedIndex(u), xyzIndex0.atUncheckedIndex(u + 1), xyzIndex1.atUncheckedIndex(u), false); - if (needNormals) + if (needNormals && normalIndex0 && normalIndex1) this.addIndexedTriangleNormalIndexes( - normalIndex0!.atUncheckedIndex(u), normalIndex0!.atUncheckedIndex(u + 1), - normalIndex1!.atUncheckedIndex(u), + normalIndex0.atUncheckedIndex(u), normalIndex0.atUncheckedIndex(u + 1), + normalIndex1.atUncheckedIndex(u), ); - if (needParams) + if (needParams && paramIndex0 && paramIndex1) this.addIndexedTriangleParamIndexes( - paramIndex0!.atUncheckedIndex(u), paramIndex0!.atUncheckedIndex(u + 1), - paramIndex1!.atUncheckedIndex(u), + paramIndex0.atUncheckedIndex(u), paramIndex0.atUncheckedIndex(u + 1), + paramIndex1.atUncheckedIndex(u), ); this._polyface.terminateFacet(); this.addIndexedTrianglePointIndexes( @@ -1914,17 +1955,17 @@ export class PolyfaceBuilder extends NullGeometryHandler { xyzIndex1.atUncheckedIndex(u + 1), false, ); - if (needNormals) + if (needNormals && normalIndex0 && normalIndex1) this.addIndexedTriangleNormalIndexes( - normalIndex1!.atUncheckedIndex(u), - normalIndex0!.atUncheckedIndex(u + 1), - normalIndex1!.atUncheckedIndex(u + 1), + normalIndex1.atUncheckedIndex(u), + normalIndex0.atUncheckedIndex(u + 1), + normalIndex1.atUncheckedIndex(u + 1), ); - if (needParams) + if (needParams && paramIndex0 && paramIndex1) this.addIndexedTriangleParamIndexes( - paramIndex1!.atUncheckedIndex(u), - paramIndex0!.atUncheckedIndex(u + 1), - paramIndex1!.atUncheckedIndex(u + 1), + paramIndex1.atUncheckedIndex(u), + paramIndex0.atUncheckedIndex(u + 1), + paramIndex1.atUncheckedIndex(u + 1), ); this._polyface.terminateFacet(); } @@ -2065,8 +2106,8 @@ export class PolyfaceBuilder extends NullGeometryHandler { if (undefined === xyz) return false; vertices.push(xyz); - if (undefined !== colors) { - const color = this._polyface.data.getColor(this._polyface.data.colorIndex![indices[i]]); + if (undefined !== colors && undefined !== this._polyface.data.colorIndex) { + const color = this._polyface.data.getColor(this._polyface.data.colorIndex[indices[i]]); if (undefined === color) return false; colors.push(color); diff --git a/core/geometry/src/polyface/PolyfaceClip.ts b/core/geometry/src/polyface/PolyfaceClip.ts index 36ed795d7670..51b9677097d6 100644 --- a/core/geometry/src/polyface/PolyfaceClip.ts +++ b/core/geometry/src/polyface/PolyfaceClip.ts @@ -101,14 +101,17 @@ export class PolyfaceClip { public static clipPolyfaceClipPlaneWithClosureFace(source: Polyface | PolyfaceVisitor, clipper: ClipPlane, insideClip: boolean = true, buildClosureFaces: boolean = true): IndexedPolyface { return this.clipPolyfaceClipPlane(source, clipper, insideClip, buildClosureFaces); } - /** Clip each facet of polyface to the ClipPlane. + /** + * Clip each facet of polyface to the ClipPlane. * * Return all surviving clip as a new mesh. * * WARNING: The new mesh is "points only" -- parameters, normals, etc are not interpolated + * * Returns a default empty polyface if clipping failed. */ public static clipPolyfaceClipPlane(source: Polyface | PolyfaceVisitor, clipper: ClipPlane, insideClip: boolean = true, buildClosureFaces: boolean = false): IndexedPolyface { const builders = ClippedPolyfaceBuilders.create(insideClip, !insideClip, buildClosureFaces); this.clipPolyfaceInsideOutside(source, clipper, builders); - return builders.claimPolyface(insideClip ? 0 : 1, true)!; + const polyface = builders.claimPolyface(insideClip ? 0 : 1, true); + return polyface ?? IndexedPolyface.create(); } /** @@ -304,9 +307,11 @@ export class PolyfaceClip { for (visitor.reset(); visitor.moveToNextFacet();) { for (const chainContext of chainContexts) { const plane = chainContext.plane; + if (!plane) + continue; facetPoints.clear(); facetPoints.pushFrom(visitor.point); - IndexedXYZCollectionPolygonOps.clipConvexPolygonInPlace(plane!, facetPoints, workPoints); + IndexedXYZCollectionPolygonOps.clipConvexPolygonInPlace(plane, facetPoints, workPoints); chainContext.addSegmentsOnPlane(facetPoints, true); } } @@ -376,12 +381,14 @@ export class PolyfaceClip { /** * Gather loops out of the ChainMergeContext. Add to destination arrays. - * @param chainContext ASSUMED TO HAVE A PLANE + * @param chainContext ASSUMED TO HAVE A PLANE; OTHERWISE THE METHOD DOES NOTHING * @param destination */ private static addClosureFacets(chainContext: ChainMergeContext, destination: ClippedPolyfaceBuilders, cache: GrowableXYZArrayCache): void { const clipper = chainContext.convexClipper; - const plane = chainContext.plane!; + const plane = chainContext.plane; + if (!plane) + return; const outwardNormal = this.evaluateInwardPlaneNormal(plane, -1.0); chainContext.clusterAndMergeVerticesXYZ(); const loops = chainContext.collectMaximalGrowableXYZArrays(); @@ -563,7 +570,9 @@ export class PolyfaceClip { const xyFrustum = ConvexClipPlaneSet.createEmpty(); const below = new GrowableXYZArray(10); const above = new GrowableXYZArray(10); - const planeOfFacet = ClipPlane.createNormalAndPointXYZXYZ(0, 0, 1, 0, 0, 0)!; + const planeOfFacet = ClipPlane.createNormalAndPointXYZXYZ(0, 0, 1, 0, 0, 0); + if (!planeOfFacet) + return; const altitudeRange = Range1d.createNull(); for (visitorB.reset(); visitorB.moveToNextFacet();) { diff --git a/core/geometry/src/polyface/PolyfaceData.ts b/core/geometry/src/polyface/PolyfaceData.ts index 6ccbb83eb5f3..fda0357fd853 100644 --- a/core/geometry/src/polyface/PolyfaceData.ts +++ b/core/geometry/src/polyface/PolyfaceData.ts @@ -596,12 +596,15 @@ export class PolyfaceData { public compress(tolerance: number = Geometry.smallMetricDistance): void { // more info can be found at geometry/internaldocs/Polyface.md const packedPoints = ClusterableArray.clusterGrowablePoint3dArray(this.point, tolerance); - this.point = packedPoints.growablePackedPoints!; + let growablePackedPoints = packedPoints.growablePackedPoints; + if (!growablePackedPoints) + return; + this.point = growablePackedPoints; packedPoints.updateIndices(this.pointIndex); // for now, normals, params, and colors use the default tolerance for clustering if (this.normalIndex && this.normal) { const packedNormals = ClusterableArray.clusterGrowablePoint3dArray(this.normal); - this.normal = packedNormals.growablePackedPoints!; + this.normal = packedNormals.growablePackedPoints; packedNormals.updateIndices(this.normalIndex); } if (this.paramIndex && this.param) { @@ -623,7 +626,10 @@ export class PolyfaceData { } else if (3 === dataSize) { const blockedData = GrowableXYZArray.create(this.auxData.channels[0].data[0].values); const packedData = ClusterableArray.clusterGrowablePoint3dArray(blockedData); - this.auxData.channels[0].data[0].values = NumberArray.create(packedData.growablePackedPoints!.float64Data()); + growablePackedPoints = packedData.growablePackedPoints; + if (!growablePackedPoints) + return; + this.auxData.channels[0].data[0].values = NumberArray.create(growablePackedPoints.float64Data()); packedData.updateIndices(this.auxData.indices); } } diff --git a/core/geometry/src/polyface/PolyfaceQuery.ts b/core/geometry/src/polyface/PolyfaceQuery.ts index b33de7f0ba7a..4b24cd686f20 100644 --- a/core/geometry/src/polyface/PolyfaceQuery.ts +++ b/core/geometry/src/polyface/PolyfaceQuery.ts @@ -919,8 +919,11 @@ export class PolyfaceQuery { context: SweepLineStringToFacetContext, visitor: PolyfaceVisitor, announce: AnnounceDrapePanel, ): Promise { let workCount = 0; + let clientPolyface: Polyface | undefined; while ((workCount < this.asyncWorkLimit) && visitor.moveToNextFacet()) { - workCount += context.projectToPolygon(visitor.point, announce, visitor.clientPolyface()!, visitor.currentReadIndex()); + clientPolyface = visitor.clientPolyface(); + if (clientPolyface) + workCount += context.projectToPolygon(visitor.point, announce, clientPolyface, visitor.currentReadIndex()); } return workCount; } @@ -1583,11 +1586,14 @@ export class PolyfaceQuery { /** * Clone the facets, inserting vertices (within edges) where points not part of each facet's vertex indices * impinge within edges. + * If clone failed, a default empty IndexedPolyface is returned. */ public static cloneWithTVertexFixup(polyface: Polyface): IndexedPolyface { const oldFacetVisitor = polyface.createVisitor(1); // this is to visit the existing facets const newFacetVisitor = polyface.createVisitor(0); // this is to build the new facets - const rangeSearcher = XYPointBuckets.create(polyface.data.point, 30)!; + const rangeSearcher = XYPointBuckets.create(polyface.data.point, 30); + if (!rangeSearcher) + return IndexedPolyface.create(); const builder = PolyfaceBuilder.create(); const edgeRange = Range3d.createNull(); const point0 = Point3d.create(); diff --git a/core/geometry/src/polyface/RangeTree/LineString3dRangeTreeContext.ts b/core/geometry/src/polyface/RangeTree/LineString3dRangeTreeContext.ts index ae5889ebc433..d2b5df107bb2 100644 --- a/core/geometry/src/polyface/RangeTree/LineString3dRangeTreeContext.ts +++ b/core/geometry/src/polyface/RangeTree/LineString3dRangeTreeContext.ts @@ -57,7 +57,9 @@ export class LineString3dRangeTreeContext { public static createCapture(points: Point3d[] | LineString3d, maxChildPerNode: number = 4, maxAppDataPerLeaf: number = 4): LineString3dRangeTreeContext | undefined { const linestring = points instanceof LineString3d ? points : LineString3d.createPoints(points); const rangeTreeRoot = RangeTreeOps.createByIndexSplits( - ((index: number): Range3d => { return Range3d.create(linestring.pointAt(index)!, linestring.pointAt(index + 1)!); }), + ((index: number): Range3d => { + return Range3d.create(linestring.uncheckedPointAt(index), linestring.uncheckedPointAt(index + 1)); + }), ((index: number): number => { return index; }), linestring.numPoints() - 1, // number of segments maxChildPerNode, diff --git a/core/geometry/src/polyface/RangeTree/RangeTreeSearchHandlers.ts b/core/geometry/src/polyface/RangeTree/RangeTreeSearchHandlers.ts index c9e035d4fbee..ebde46b6706e 100644 --- a/core/geometry/src/polyface/RangeTree/RangeTreeSearchHandlers.ts +++ b/core/geometry/src/polyface/RangeTree/RangeTreeSearchHandlers.ts @@ -221,7 +221,7 @@ export class SingleTreeSearchHandlerForClosestPointOnLineString3d extends Single private _workSegment?: LineSegment3d; /** Test a segment indexed in the range tree as candidate for "closest" */ public override processAppData(candidateIndex: number): void { - const segment = this._workSegment = this.context.lineString.getIndexedSegment(candidateIndex, this._workSegment)!; + const segment = this._workSegment = this.context.lineString.getIndexedSegment(candidateIndex, this._workSegment); if (segment) { const cld = segment.closestPoint(this.spacePoint, false); LineString3d.convertLocalToGlobalDetail(cld, candidateIndex, this.context.lineString.numEdges(), this.context.lineString); @@ -282,13 +282,19 @@ export class TwoTreeSearchHandlerForLineString3dLineString3dCloseApproach extend } private static _workSegmentA?: LineSegment3d; private static _workSegmentB?: LineSegment3d; - /** Compute and test the closest approach between two segments, given their indices. */ + /** + * Compute and test the closest approach between two segments, given their indices. + * * If indices are out of range, simply return. + */ public override processAppDataPair(indexA: number, indexB: number): void { + if (indexA < 0 || indexA >= this.contextA.lineString.points.length || + indexB < 0 || indexB >= this.contextB.lineString.points.length) + return; this.contextA.numPointTest++; const segA = TwoTreeSearchHandlerForLineString3dLineString3dCloseApproach._workSegmentA = - this.contextA.lineString.getIndexedSegment(indexA, TwoTreeSearchHandlerForLineString3dLineString3dCloseApproach._workSegmentA)!; + this.contextA.lineString.getUncheckedIndexedSegment(indexA, TwoTreeSearchHandlerForLineString3dLineString3dCloseApproach._workSegmentA); const segB = TwoTreeSearchHandlerForLineString3dLineString3dCloseApproach._workSegmentB = - this.contextB.lineString.getIndexedSegment(indexB, TwoTreeSearchHandlerForLineString3dLineString3dCloseApproach._workSegmentB)!; + this.contextB.lineString.getUncheckedIndexedSegment(indexB, TwoTreeSearchHandlerForLineString3dLineString3dCloseApproach._workSegmentB); const cldPair = LineSegment3d.closestApproach(segA, false, segB, false); if (cldPair && this.searchState.isNewMinOrTrigger(cldPair.detailA.a)) { LineString3d.convertLocalToGlobalDetail(cldPair.detailA, indexA, this.contextA.lineString.numEdges(), this.contextA.lineString); diff --git a/core/geometry/src/polyface/multiclip/BuildAverageNormalsContext.ts b/core/geometry/src/polyface/multiclip/BuildAverageNormalsContext.ts index 7163a4b252a6..3278fec97890 100644 --- a/core/geometry/src/polyface/multiclip/BuildAverageNormalsContext.ts +++ b/core/geometry/src/polyface/multiclip/BuildAverageNormalsContext.ts @@ -153,7 +153,9 @@ export class BuildAverageNormalsContext { } // emplace the indices for (const sector of sectors) { - polyface.data.normalIndex.push(sector.sectorClusterData!.index); + if (!sector.sectorClusterData) + continue; + polyface.data.normalIndex.push(sector.sectorClusterData.index); } } /** diff --git a/core/geometry/src/polyface/multiclip/OffsetMeshContext.ts b/core/geometry/src/polyface/multiclip/OffsetMeshContext.ts index 2eb28d2e8c38..39a8c143b3ee 100644 --- a/core/geometry/src/polyface/multiclip/OffsetMeshContext.ts +++ b/core/geometry/src/polyface/multiclip/OffsetMeshContext.ts @@ -1007,7 +1007,10 @@ export class OffsetMeshContext { OffsetMeshContext.stringDebugFunction(""); OffsetMeshContext.stringDebugFunction(` VERTEX LOOP ${JSON.stringify(vertexSeed.getPoint3d().toJSON())} `); vertexSeed.sumAroundVertex( - (node: HalfEdge) => { OffsetMeshContext.stringDebugFunction!(this.inspectMasks(node, false, true)); return 0; }); + (node: HalfEdge) => { + OffsetMeshContext.stringDebugFunction?.(this.inspectMasks(node, false, true)); + return 0; + }); } // Take care of the easiest vertices directly . . . note that this returns from the lambda, not computeOffsetFacetIntersections if (this.assignOffsetByAverageNormalAroundVertex(vertexSeed, maxAllowedNormalDeviationRadians, averageNormalData, distance)) diff --git a/core/geometry/src/polyface/multiclip/SweepLineStringToFacetContext.ts b/core/geometry/src/polyface/multiclip/SweepLineStringToFacetContext.ts index c63964effe2e..5d901b946f9a 100644 --- a/core/geometry/src/polyface/multiclip/SweepLineStringToFacetContext.ts +++ b/core/geometry/src/polyface/multiclip/SweepLineStringToFacetContext.ts @@ -186,7 +186,9 @@ export class ClipSweptLineStringContext { if (localToWorldMatrix === undefined) localToWorldMatrix = Matrix3d.createIdentity(); const localToWorld = Transform.createOriginAndMatrix(point, localToWorldMatrix); - const worldToLocal = localToWorld.inverse()!; + const worldToLocal = localToWorld.inverse(); + if (!worldToLocal) + return undefined; const localRange = xyz.getRange(worldToLocal); for (let i = 1; i < xyz.length; i++) { xyz.getPoint3dAtUncheckedPointIndex(i, newPoint); diff --git a/core/geometry/src/serialization/BGFBAccessors.ts b/core/geometry/src/serialization/BGFBAccessors.ts index f921355b483c..25fa71dba9f0 100644 --- a/core/geometry/src/serialization/BGFBAccessors.ts +++ b/core/geometry/src/serialization/BGFBAccessors.ts @@ -7,6 +7,7 @@ import { flatbuffers } from "flatbuffers"; /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/explicit-member-accessibility */ /* eslint-disable @itwin/prefer-get */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ // cspell:word flatbuffers, Akima /** diff --git a/core/geometry/src/serialization/BGFBReader.ts b/core/geometry/src/serialization/BGFBReader.ts index 6b9ddd6d164b..7a0d5af3870f 100644 --- a/core/geometry/src/serialization/BGFBReader.ts +++ b/core/geometry/src/serialization/BGFBReader.ts @@ -176,13 +176,15 @@ export class BGFBReader { /** * Extract a bspline curve * @param variant read position in the flat buffer. + * Return undefined if the spiral cannot be constructed. */ public readTransitionSpiral(header: BGFBAccessors.TransitionSpiral): TransitionSpiral3d | undefined { const detailHeader = header.detail(); if (detailHeader) { const directDetailHeader = header.directDetail(); - const _extraDataArray = header.extraDataArray(); const spiralTypeName = DgnSpiralTypeQueries.typeCodeToString(detailHeader.spiralType()); + if (!spiralTypeName) + return undefined; const curvature0 = detailHeader.curvature0(); const curvature1 = detailHeader.curvature1(); const bearing0Radians = detailHeader.bearing0Radians(); @@ -208,7 +210,7 @@ export class BGFBReader { const arcLength = TransitionSpiral3d.radiusRadiusSweepRadiansToArcLength(radius0, radius1, bearing1Radians - bearing0Radians); const directSpiral = DirectSpiral3d.createFromLengthAndRadius( - spiralTypeName!, + spiralTypeName, radius0, radius1, Angle.createRadians(bearing0Radians), Angle.createRadians(bearing1Radians), @@ -224,29 +226,44 @@ export class BGFBReader { /** * Extract a curve primitive * @param variant read position in the flat buffer. + * Return undefined if the curve primitive cannot be constructed. */ public readCurvePrimitiveFromVariant(variant: BGFBAccessors.VariantGeometry): CurvePrimitive | undefined { const geometryType = variant.geometryType(); if (geometryType === BGFBAccessors.VariantGeometryUnion.tagLineSegment) { const offsetToLineSegment = variant.geometry(new BGFBAccessors.LineSegment()); - const offsetToCoordinates = offsetToLineSegment!.segment(); + if (!offsetToLineSegment) + return undefined; + const offsetToCoordinates = offsetToLineSegment.segment(); + if (!offsetToCoordinates) + return undefined; return LineSegment3d.createXYZXYZ( - offsetToCoordinates!.point0X(), offsetToCoordinates!.point0Y(), offsetToCoordinates!.point0Z(), - offsetToCoordinates!.point1X(), offsetToCoordinates!.point1Y(), offsetToCoordinates!.point1Z()); + offsetToCoordinates.point0X(), offsetToCoordinates.point0Y(), offsetToCoordinates.point0Z(), + offsetToCoordinates.point1X(), offsetToCoordinates.point1Y(), offsetToCoordinates.point1Z()); } else if (geometryType === BGFBAccessors.VariantGeometryUnion.tagEllipticArc) { const offsetToEllipticArc = variant.geometry(new BGFBAccessors.EllipticArc()); - const offsetToCoordinates = offsetToEllipticArc!.arc()!; + if (!offsetToEllipticArc) + return undefined; + const offsetToCoordinates = offsetToEllipticArc.arc(); + if (!offsetToCoordinates) + return undefined; return Arc3d.createXYZXYZXYZ( offsetToCoordinates.centerX(), offsetToCoordinates.centerY(), offsetToCoordinates.centerZ(), offsetToCoordinates.vector0X(), offsetToCoordinates.vector0Y(), offsetToCoordinates.vector0Z(), offsetToCoordinates.vector90X(), offsetToCoordinates.vector90Y(), offsetToCoordinates.vector90Z(), AngleSweep.createStartSweepRadians(offsetToCoordinates.startRadians(), offsetToCoordinates?.sweepRadians())); } else if (geometryType === BGFBAccessors.VariantGeometryUnion.tagLineString) { - const offsetToLineString = variant.geometry(new BGFBAccessors.LineString())!; + const offsetToLineString = variant.geometry(new BGFBAccessors.LineString()); + if (!offsetToLineString) + return undefined; const numCoordinates = offsetToLineString.pointsLength(); const result = LineString3d.create(); for (let i = 0; i + 2 < numCoordinates; i += 3) { - result.packedPoints.pushXYZ(offsetToLineString.points(i)!, offsetToLineString.points(i + 1)!, offsetToLineString.points(i + 2)!); + const p0 = offsetToLineString.points(i); + const p1 = offsetToLineString.points(i + 1); + const p2 = offsetToLineString.points(i + 2); + if (p0 !== null && p1 !== null && p2 !== null) + result.packedPoints.pushXYZ(p0, p1, p2); } return result; } else if (geometryType === BGFBAccessors.VariantGeometryUnion.tagBsplineCurve) { @@ -271,15 +288,22 @@ export class BGFBReader { /** * Extract a curve primitive * @param variant read position in the flat buffer. + * Return undefined if the point string cannot be constructed. */ public readPointStringFromVariant(variant: BGFBAccessors.VariantGeometry): PointString3d | undefined { const geometryType = variant.geometryType(); if (geometryType === BGFBAccessors.VariantGeometryUnion.tagPointString) { - const offsetToLineString = variant.geometry(new BGFBAccessors.PointString())!; + const offsetToLineString = variant.geometry(new BGFBAccessors.PointString()); + if (!offsetToLineString) + return undefined; const numCoordinates = offsetToLineString.pointsLength(); const result = PointString3d.create(); for (let i = 0; i + 2 < numCoordinates; i += 3) { - result.points.push(Point3d.create(offsetToLineString.points(i)!, offsetToLineString.points(i + 1)!, offsetToLineString.points(i + 2)!)); + const p0 = offsetToLineString.points(i); + const p1 = offsetToLineString.points(i + 1); + const p2 = offsetToLineString.points(i + 2); + if (p0 !== null && p1 !== null && p2 !== null) + result.points.push(Point3d.create(p0, p1, p2)); } return result; } @@ -567,11 +591,14 @@ export class BGFBReader { /** * Extract a curve collection * @param variant read position in the flat buffer. + * Return undefined if the curve collection cannot be constructed. */ public readCurveCollectionFromVariantGeometry(variant: BGFBAccessors.VariantGeometry): CurveCollection | undefined { const geometryType = variant.geometryType(); if (geometryType === BGFBAccessors.VariantGeometryUnion.tagCurveVector) { - const cvTable = variant.geometry(new BGFBAccessors.CurveVector())!; + const cvTable = variant.geometry(new BGFBAccessors.CurveVector()); + if (!cvTable) + return undefined; return this.readCurveCollectionFromCurveVectorTable(cvTable); } return undefined; @@ -579,12 +606,17 @@ export class BGFBReader { /** * Extract a curve collection * @param variant read position in the flat buffer. + * Return undefined if the solid primitive cannot be constructed. */ public readSolidPrimitiveFromVariant(variant: BGFBAccessors.VariantGeometry): SolidPrimitive | undefined { const geometryType = variant.geometryType(); if (geometryType === BGFBAccessors.VariantGeometryUnion.tagDgnBox) { const header = variant.geometry(new BGFBAccessors.DgnBox()); - const detail = header!.detail()!; + if (!header) + return undefined; + const detail = header.detail(); + if (!detail) + return undefined; return Box.createDgnBox( Point3d.create(detail.baseOriginX(), detail.baseOriginY(), detail.baseOriginZ()), Vector3d.create(detail.vectorXX(), detail.vectorXY(), detail.vectorXZ()), @@ -594,8 +626,14 @@ export class BGFBReader { detail.capped()); } if (geometryType === BGFBAccessors.VariantGeometryUnion.tagDgnSphere) { const header = variant.geometry(new BGFBAccessors.DgnSphere()); - const detail = header!.detail()!; - const lToWDetail = detail.localToWorld()!; + if (!header) + return undefined; + const detail = header.detail(); + if (!detail) + return undefined; + const lToWDetail = detail.localToWorld(); + if (!lToWDetail) + return undefined; const localToWorld = Transform.createRowValues( lToWDetail.axx(), lToWDetail.axy(), lToWDetail.axz(), lToWDetail.axw(), lToWDetail.ayx(), lToWDetail.ayy(), lToWDetail.ayz(), lToWDetail.ayw(), @@ -605,7 +643,11 @@ export class BGFBReader { detail.capped()); } if (geometryType === BGFBAccessors.VariantGeometryUnion.tagDgnCone) { const header = variant.geometry(new BGFBAccessors.DgnCone()); - const detail = header!.detail()!; + if (!header) + return undefined; + const detail = header.detail(); + if (!detail) + return undefined; const centerA = Point3d.create(detail.centerAX(), detail.centerAY(), detail.centerAZ()); const centerB = Point3d.create(detail.centerBX(), detail.centerBY(), detail.centerBZ()); const vector0 = Vector3d.create(detail.vector0X(), detail.vector0Y(), detail.vector0Z()); @@ -614,8 +656,12 @@ export class BGFBReader { const radiusB = detail.radiusB(); return Cone.createBaseAndTarget(centerA, centerB, vector0, vector90, radiusA, radiusB, detail.capped()); } if (geometryType === BGFBAccessors.VariantGeometryUnion.tagDgnTorusPipe) { - const header = variant.geometry(new BGFBAccessors.DgnTorusPipe())!; - const detail = header.detail()!; + const header = variant.geometry(new BGFBAccessors.DgnTorusPipe()); + if (!header) + return undefined; + const detail = header.detail(); + if (!detail) + return undefined; const center = Point3d.create(detail.centerX(), detail.centerY(), detail.centerZ()); const vectorX = Vector3d.create(detail.vectorXX(), detail.vectorXY(), detail.vectorXZ()); const vectorY = Vector3d.create(detail.vectorYX(), detail.vectorYY(), detail.vectorYZ()); @@ -624,7 +670,9 @@ export class BGFBReader { const minorRadius = detail.minorRadius(); return TorusPipe.createDgnTorusPipe(center, vectorX, vectorY, majorRadius, minorRadius, Angle.createRadians(sweepRadians), detail.capped()); } if (geometryType === BGFBAccessors.VariantGeometryUnion.tagDgnExtrusion) { - const header = variant.geometry(new BGFBAccessors.DgnExtrusion())!; + const header = variant.geometry(new BGFBAccessors.DgnExtrusion()); + if (!header) + return undefined; const dVector = new BGFBAccessors.DVector3d(); header.extrusionVector(dVector); const extrusionVector = Vector3d.create(dVector.x(), dVector.y(), dVector.z()); @@ -634,7 +682,9 @@ export class BGFBReader { return LinearSweep.create(contour, extrusionVector, header.capped()); } } if (geometryType === BGFBAccessors.VariantGeometryUnion.tagDgnRotationalSweep) { - const header = variant.geometry(new BGFBAccessors.DgnRotationalSweep())!; + const header = variant.geometry(new BGFBAccessors.DgnRotationalSweep()); + if (!header) + return undefined; const dAxis = new BGFBAccessors.DRay3d(); header.axis(dAxis); const axis = Ray3d.createXYZUVW(dAxis.x(), dAxis.y(), dAxis.z(), dAxis.ux(), dAxis.uy(), dAxis.uz()); @@ -646,7 +696,9 @@ export class BGFBReader { return RotationalSweep.create(contour, axis, sweepAngle, header.capped()); } } if (geometryType === BGFBAccessors.VariantGeometryUnion.tagDgnRuledSweep) { - const header = variant.geometry(new BGFBAccessors.DgnRuledSweep())!; + const header = variant.geometry(new BGFBAccessors.DgnRuledSweep()); + if (!header) + return undefined; const numCurves = header.curvesLength(); const contours: CurveCollection[] = []; for (let i = 0; i < numCurves; i++) { @@ -666,6 +718,7 @@ export class BGFBReader { /** * Extract any geometry type or array of geometry. * @param variant read position in the flat buffer. + * Return undefined if the geometry cannot be read. */ public readGeometryQueryFromVariant(variant: BGFBAccessors.VariantGeometry): GeometryQuery | GeometryQuery[] | undefined { const rootType = variant.geometryType(); @@ -697,8 +750,10 @@ export class BGFBReader { case BGFBAccessors.VariantGeometryUnion.tagVectorOfVariantGeometry: { const geometry: GeometryQuery[] = []; const offsetToVectorOfVariantGeometry = variant.geometry(new BGFBAccessors.VectorOfVariantGeometry()); - for (let i = 0; i < offsetToVectorOfVariantGeometry!.membersLength(); i++) { - const child = offsetToVectorOfVariantGeometry!.members(i); + if (!offsetToVectorOfVariantGeometry) + return undefined; + for (let i = 0; i < offsetToVectorOfVariantGeometry.membersLength(); i++) { + const child = offsetToVectorOfVariantGeometry.members(i); if (child !== null) { const childGeometry = this.readGeometryQueryFromVariant(child); if (childGeometry instanceof GeometryQuery) { diff --git a/core/geometry/src/serialization/BGFBWriter.ts b/core/geometry/src/serialization/BGFBWriter.ts index 45a15ce5919a..760a28f3fb7b 100644 --- a/core/geometry/src/serialization/BGFBWriter.ts +++ b/core/geometry/src/serialization/BGFBWriter.ts @@ -309,7 +309,9 @@ export class BGFBWriter { return this.writeAkimaCurve3dAsFBVariantGeometry(curvePrimitive); } else if (curvePrimitive instanceof IntegratedSpiral3d) { const placement = curvePrimitive.localToWorld; - const typeCode = DgnSpiralTypeQueries.stringToTypeCode(curvePrimitive.spiralType, true)!; + const typeCode = DgnSpiralTypeQueries.stringToTypeCode(curvePrimitive.spiralType, true); + if (undefined === typeCode) + return undefined; const spiralDetailOffset = BGFBAccessors.TransitionSpiralDetail.createTransitionSpiralDetail(this.builder, placement.matrix.coffs[0], placement.matrix.coffs[1], placement.matrix.coffs[2], placement.origin.x, placement.matrix.coffs[3], placement.matrix.coffs[4], placement.matrix.coffs[5], placement.origin.y, @@ -335,7 +337,9 @@ export class BGFBWriter { const nominalLength = curvePrimitive.nominalL1; const bearing0Radians = 0.0; const bearing1Radians = TransitionSpiral3d.radiusRadiusLengthToSweepRadians(radius0, radius1, nominalLength); - const typeCode = DgnSpiralTypeQueries.stringToTypeCode(curvePrimitive.spiralType, true)!; + const typeCode = DgnSpiralTypeQueries.stringToTypeCode(curvePrimitive.spiralType, true); + if (undefined === typeCode) + return undefined; const spiralDetailOffset = BGFBAccessors.TransitionSpiralDetail.createTransitionSpiralDetail(this.builder, placement.matrix.coffs[0], placement.matrix.coffs[1], placement.matrix.coffs[2], placement.origin.x, placement.matrix.coffs[3], placement.matrix.coffs[4], placement.matrix.coffs[5], placement.origin.y, @@ -423,7 +427,9 @@ export class BGFBWriter { const carrierOffset = BGFBAccessors.DgnTorusPipe.createDgnTorusPipe(this.builder, detailOffset); return BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors.VariantGeometryUnion.tagDgnTorusPipe, carrierOffset, 0); } else if (solid instanceof LinearSweep) { - const baseCurveOffset = this.writeCurveCollectionAsFBCurveVector(solid.getSweepContourRef().getCurves())!; + const baseCurveOffset = this.writeCurveCollectionAsFBCurveVector(solid.getSweepContourRef().getCurves()); + if (undefined === baseCurveOffset) + return undefined; const sweepVector = solid.cloneSweepVector(); // const sweepVectorOffset = BGFBAccessors.DVector3d.createDVector3d(this.builder, sweepVector.x, sweepVector.y, sweepVector.z); // const carrierOffset = BGFBAccessors.DgnExtrusion.createDgnExtrusion(this.builder, contourOffset, sweepVectorOffset, solid.capped); @@ -439,7 +445,9 @@ export class BGFBWriter { return BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors.VariantGeometryUnion.tagDgnExtrusion, dgnExtrusionOffset, 0); } else if (solid instanceof RotationalSweep) { - const baseCurveOffset = this.writeCurveCollectionAsFBCurveVector(solid.getSweepContourRef().getCurves())!; + const baseCurveOffset = this.writeCurveCollectionAsFBCurveVector(solid.getSweepContourRef().getCurves()); + if (undefined === baseCurveOffset) + return undefined; const axis = solid.cloneAxisRay(); const sweepAngle = solid.getSweep(); // const sweepVectorOffset = BGFBAccessors.DVector3d.createDVector3d(this.builder, sweepVector.x, sweepVector.y, sweepVector.z); @@ -486,7 +494,9 @@ export class BGFBWriter { if (channel instanceof AuxChannel) { const channelDataOffsets: number[] = []; for (const channelData of channel.data) { - channelDataOffsets.push(this.writePolyfaceAuxChannelDataAsFBVariantGeometry(channelData)!); + const channelDataOffset = this.writePolyfaceAuxChannelDataAsFBVariantGeometry(channelData); + if (channelDataOffset !== undefined) + channelDataOffsets.push(channelDataOffset); } const valuesOffset = BGFBAccessors.PolyfaceAuxChannel.createDataVector(this.builder, channelDataOffsets); const nameOffset = channel.name ? this.builder.createString(channel.name) : 0; @@ -503,8 +513,11 @@ export class BGFBWriter { public writePolyfaceAuxDataAsFBVariantGeometry(mesh: IndexedPolyface, data: PolyfaceAuxData): number | undefined { if (data instanceof PolyfaceAuxData) { const channelOffsets: number[] = []; - for (const channel of data.channels) - channelOffsets.push(this.writePolyfaceAuxChannelAsFBVariantGeometry(channel)!); + for (const channel of data.channels) { + const channelOffset = this.writePolyfaceAuxChannelAsFBVariantGeometry(channel); + if (channelOffset !== undefined) + channelOffsets.push(channelOffset); + } const channelOffsetsOffset = BGFBAccessors.PolyfaceAuxChannel.createDataVector(this.builder, channelOffsets); const indexArray: number[] = []; @@ -578,8 +591,11 @@ export class BGFBWriter { paramOffset = BGFBAccessors.Polyface.createPointVector(this.builder, numberArray); } - if (mesh.data.auxData) - auxDataOffset = this.writePolyfaceAuxDataAsFBVariantGeometry(mesh, mesh.data.auxData)!; + if (mesh.data.auxData) { + const offset = this.writePolyfaceAuxDataAsFBVariantGeometry(mesh, mesh.data.auxData); + if (offset) + auxDataOffset = offset; + } if (mesh.data.taggedNumericData) taggedNumericDataOffset = this.writeTaggedNumericDataArray(mesh.data.taggedNumericData); diff --git a/core/geometry/src/serialization/IModelJsonSchema.ts b/core/geometry/src/serialization/IModelJsonSchema.ts index be344e49b0f3..c9d757e61fea 100644 --- a/core/geometry/src/serialization/IModelJsonSchema.ts +++ b/core/geometry/src/serialization/IModelJsonSchema.ts @@ -831,7 +831,7 @@ export namespace IModelJson { } /** Parse `transitionSpiral` content (right side) to TransitionSpiral3d. */ public static parseTransitionSpiral(data?: TransitionSpiralProps): TransitionSpiral3d | undefined { - const axes = Reader.parseOrientation(data, true)!; + const axes = Reader.parseOrientation(data, true); const origin = Reader.parsePoint3dProperty(data, "origin"); // the create method will juggle any 4 out of these 5 inputs to define the other .. const startBearing = Reader.parseAngleProperty(data, "startBearing"); @@ -847,7 +847,7 @@ export namespace IModelJson { interval = Reader.parseSegment1dProperty(data, "fractionInterval", undefined); if (!interval) interval = Reader.parseSegment1dProperty(data, "activeInterval", undefined); - const spiralType = Reader.parseStringProperty(data, "type", "clothoid")!; + const spiralType = Reader.parseStringProperty(data, "type", "clothoid"); // REMARK: Our job is to parse and pass data along -- inscrutable validation happens in the implementation classes . . . if (origin) { let candidate: TransitionSpiral3d | undefined; @@ -860,6 +860,8 @@ export namespace IModelJson { Transform.createOriginAndMatrix(origin, axes)); if (candidate) return candidate; + if (undefined === spiralType) + return undefined; candidate = DirectSpiral3d.createFromLengthAndRadius( spiralType, startRadius, endRadius, @@ -1169,8 +1171,9 @@ export namespace IModelJson { const topX = Reader.parseNumberProperty(json, "topX", baseX); const topY = Reader.parseNumberProperty(json, "topY", baseY); const height = Reader.parseNumberProperty(json, "height", baseX); - const axes = Reader.parseOrientation(json, true)!; - + const axes = Reader.parseOrientation(json, true); + if (!axes) + return undefined; if (origin && !topOrigin && height) topOrigin = Matrix3d.xyzPlusMatrixTimesXYZ(origin, axes, Vector3d.create(0, 0, height)); @@ -1216,13 +1219,13 @@ export namespace IModelJson { } /** Parse TorusPipe props to TorusPipe instance. */ public static parseTorusPipe(json?: TorusPipeProps): TorusPipe | undefined { - const axes = Reader.parseOrientation(json, true)!; // force frame to be pure rotation (no scale or mirror)! + const axes = Reader.parseOrientation(json, true); // force frame to be pure rotation (no scale or mirror)! const center = Reader.parsePoint3dProperty(json, "center"); const radiusA = Reader.parseNumberProperty(json, "majorRadius"); const radiusB = Reader.parseNumberProperty(json, "minorRadius"); const sweepAngle = Reader.parseAngleProperty(json, "sweepAngle", undefined); - const capped = Reader.parseBooleanProperty(json, "capped", false)!; - if (center + const capped = Reader.parseBooleanProperty(json, "capped", false); + if (capped !== undefined && axes !== undefined && center && radiusA !== undefined && radiusB !== undefined) { return TorusPipe.createDgnTorusPipe(center, axes.columnX(), axes.columnY(), @@ -1701,7 +1704,9 @@ export namespace IModelJson { }, }; - const outBox = out.box!; + const outBox = out.box; + if (!outBox) + return undefined; Writer.insertXYOrientation(outBox, box.getVectorX(), box.getVectorY(), true); if (!Geometry.isSameCoordinate(box.getTopX(), box.getBaseX())) outBox.topX = box.getTopX(); @@ -1716,8 +1721,10 @@ export namespace IModelJson { const contents: AuxDataProps = { indices: [], channels: [] }; const visitor = pf.createVisitor(0); while (visitor.moveToNextFacet()) { - for (let i = 0; i < visitor.indexCount; i++) - contents.indices.push(visitor.auxData!.indices[i] + 1); + for (let i = 0; i < visitor.indexCount; i++) { + if (visitor.auxData) + contents.indices.push(visitor.auxData.indices[i] + 1); + } contents.indices.push(0); // facet terminator. } for (const inChannel of auxData.channels) { @@ -1805,7 +1812,8 @@ export namespace IModelJson { edgeMateIndex = []; if (!SerializationHelpers.announceUncompressedZeroBasedReflexiveIndices(pf.data.edgeMateIndex, pf.facetStart, SerializationHelpers.EdgeMateIndex.BlockSeparator, SerializationHelpers.EdgeMateIndex.NoEdgeMate, - (i: number) => edgeMateIndex!.push(i), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (i: number) => (edgeMateIndex!).push(i), // edgeMateIndex is defined if we are here )) { edgeMateIndex = undefined; } diff --git a/core/geometry/src/serialization/SerializationHelpers.ts b/core/geometry/src/serialization/SerializationHelpers.ts index 3622258c66d3..a7aa4c119b12 100644 --- a/core/geometry/src/serialization/SerializationHelpers.ts +++ b/core/geometry/src/serialization/SerializationHelpers.ts @@ -222,9 +222,11 @@ export namespace SerializationHelpers { function convertBSplineCurveDataArrays(data: BSplineCurveData, options?: BSplineDataOptions) { if (undefined !== options?.jsonPoles) { const packedPoles = data.poles instanceof Float64Array; - if (options.jsonPoles && packedPoles) - data.poles = NumberArray.unpack2d(data.poles as Float64Array, data.dim)!; - else if (!options.jsonPoles && !packedPoles) + if (options.jsonPoles && packedPoles) { + const poles = NumberArray.unpack2d(data.poles as Float64Array, data.dim); + if (poles) + data.poles = poles; + } else if (!options.jsonPoles && !packedPoles) data.poles = NumberArray.pack(data.poles as number[][]); if (data.weights) { @@ -248,9 +250,11 @@ export namespace SerializationHelpers { function convertBSplineSurfaceDataArrays(data: BSplineSurfaceData, options?: BSplineDataOptions) { if (undefined !== options?.jsonPoles) { const packedPoles = data.poles instanceof Float64Array; - if (options.jsonPoles && packedPoles) - data.poles = NumberArray.unpack3d(data.poles as Float64Array, data.uParams.numPoles, data.dim)!; - else if (!options.jsonPoles && !packedPoles) + if (options.jsonPoles && packedPoles) { + const poles = NumberArray.unpack3d(data.poles as Float64Array, data.uParams.numPoles, data.dim); + if (poles) + data.poles = poles; + } else if (!options.jsonPoles && !packedPoles) data.poles = NumberArray.pack(data.poles as number[][][]); if (data.weights) { diff --git a/core/geometry/src/solid/Cone.ts b/core/geometry/src/solid/Cone.ts index 5594707a46a0..11e18e5f0ad0 100644 --- a/core/geometry/src/solid/Cone.ts +++ b/core/geometry/src/solid/Cone.ts @@ -262,8 +262,10 @@ export class Cone extends SolidPrimitive implements UVSurface, UVSurfaceIsoParam } /** Extend `rangeToExtend` so it includes this `Cone` instance. */ public extendRange(rangeToExtend: Range3d, transform?: Transform): void { - const arc0 = this.constantVSection(0.0)!; - const arc1 = this.constantVSection(1.0)!; + const arc0 = this.constantVSection(0.0); + const arc1 = this.constantVSection(1.0); + if (!arc0 || !arc1) + return; arc0.extendRange(rangeToExtend, transform); arc1.extendRange(rangeToExtend, transform); } @@ -307,13 +309,16 @@ export class Cone extends SolidPrimitive implements UVSurface, UVSurfaceIsoParam * Directional distance query * * u direction is around longitude circle at maximum distance from axis. * * v direction is on a line of longitude between the latitude limits. + * If calculation failed, a zero vector is returned. */ public maxIsoParametricDistance(): Vector2d { const vectorX = this._localToWorld.matrix.columnX(); const vectorY = this._localToWorld.matrix.columnY(); const columnZ = this._localToWorld.matrix.columnZ(); - const xyNormal = vectorX.unitCrossProduct(vectorY)!; + const xyNormal = vectorX.unitCrossProduct(vectorY); + if (!xyNormal) + return Vector2d.createZero(); const hZ = xyNormal.dotProduct(columnZ); const zSkewVector = columnZ.plusScaled(xyNormal, hZ); const zSkewDistance = zSkewVector.magnitudeXY(); diff --git a/core/geometry/src/test/Checker.ts b/core/geometry/src/test/Checker.ts index 016e107274f0..98955ec9691b 100644 --- a/core/geometry/src/test/Checker.ts +++ b/core/geometry/src/test/Checker.ts @@ -554,7 +554,10 @@ export class Checker { const range = g.range(); if (!range.isNull && range.maxAbs() <= maxCoordinate) { - Checker._cache.push(g.clone()!); + const gClone = g.clone(); + if (!gClone) + return; + Checker._cache.push(gClone); Checker._cache[Checker._cache.length - 1].tryTransformInPlace(Checker._transform); } } diff --git a/core/geometry/src/test/GeometryCoreTestIO.ts b/core/geometry/src/test/GeometryCoreTestIO.ts index 985ccd5c4598..18f74928d23c 100644 --- a/core/geometry/src/test/GeometryCoreTestIO.ts +++ b/core/geometry/src/test/GeometryCoreTestIO.ts @@ -285,7 +285,9 @@ export class GeometryCoreTestIO { const centers = []; for (visitor.reset(); visitor.moveToNextFacet();) { centers.length = 0; - const centroid = PolygonOps.centroidAreaNormal(visitor.point)!; + const centroid = PolygonOps.centroidAreaNormal(visitor.point); + if (!centroid) + continue; for (let i = 0; i < visitor.point.length; i++) { visitor.point.getPoint3dAtUncheckedPointIndex(i, xyz); const distanceToCentroid = xyz.distance(centroid.getOriginRef()); @@ -480,8 +482,8 @@ export class GeometryCoreTestIO { } } else if (data instanceof CurveLocationDetail) { if (data.hasFraction1) { - if (data.curve) { - const partialCurve = data.curve.clonePartialCurve(data.fraction, data.fraction1!); + if (data.curve && undefined !== data.fraction1) { + const partialCurve = data.curve.clonePartialCurve(data.fraction, data.fraction1); if (partialCurve) { const curveB = CurveChainWireOffsetContext.createSingleOffsetPrimitiveXY(partialCurve, 0.6 * markerSize); this.captureGeometry(collection, curveB, dx, dy, dz); diff --git a/core/geometry/src/serialization/GeometrySamples.ts b/core/geometry/src/test/GeometrySamples.ts similarity index 99% rename from core/geometry/src/serialization/GeometrySamples.ts rename to core/geometry/src/test/GeometrySamples.ts index 8d1204b442af..22abd45d9add 100644 --- a/core/geometry/src/serialization/GeometrySamples.ts +++ b/core/geometry/src/test/GeometrySamples.ts @@ -3,9 +3,6 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -/** @packageDocumentation - * @module Serialization - */ import { BezierCurve3d } from "../bspline/BezierCurve3d"; import { BezierCurve3dH } from "../bspline/BezierCurve3dH"; import { BSplineCurve3d, BSplineCurve3dBase } from "../bspline/BSplineCurve"; @@ -65,6 +62,8 @@ import { SolidPrimitive } from "../solid/SolidPrimitive"; import { Sphere } from "../solid/Sphere"; import { TorusPipe } from "../solid/TorusPipe"; +/* eslint-disable @typescript-eslint/no-non-null-assertion */ + /** * Function to be called to obtain function value at (i,n), for * * n fixed over many calls diff --git a/core/geometry/src/test/SimpleFactory.ts b/core/geometry/src/test/SimpleFactory.ts index dd4c5087ac15..1a069588843b 100644 --- a/core/geometry/src/test/SimpleFactory.ts +++ b/core/geometry/src/test/SimpleFactory.ts @@ -5,6 +5,8 @@ import * as g from "../core-geometry"; +/* eslint-disable @typescript-eslint/no-non-null-assertion */ + // GeometryCoreTestIO.consoleLog("========================="); // GeometryCoreTestIO.consoleLog("Standalone Output"); // GeometryCoreTestIO.consoleLog("========================="); diff --git a/core/geometry/src/test/bspline/AkimaCurve3d.test.ts b/core/geometry/src/test/bspline/AkimaCurve3d.test.ts index 1f3d7eb4ee46..4c9909114832 100644 --- a/core/geometry/src/test/bspline/AkimaCurve3d.test.ts +++ b/core/geometry/src/test/bspline/AkimaCurve3d.test.ts @@ -5,7 +5,7 @@ import { describe, expect, it } from "vitest"; import { Checker } from "../Checker"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { AkimaCurve3d } from "../../bspline/AkimaCurve3d"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { GeometryQuery } from "../../curve/GeometryQuery"; diff --git a/core/geometry/src/test/bspline/BSplineSurface.test.ts b/core/geometry/src/test/bspline/BSplineSurface.test.ts index 82291b8f7694..c20beadd3da6 100644 --- a/core/geometry/src/test/bspline/BSplineSurface.test.ts +++ b/core/geometry/src/test/bspline/BSplineSurface.test.ts @@ -12,7 +12,7 @@ import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAn import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { IModelJson } from "../../serialization/IModelJsonSchema"; diff --git a/core/geometry/src/test/bspline/BsplineCurve.test.ts b/core/geometry/src/test/bspline/BsplineCurve.test.ts index 14dcbb4ab527..10bfc2ed76df 100644 --- a/core/geometry/src/test/bspline/BsplineCurve.test.ts +++ b/core/geometry/src/test/bspline/BsplineCurve.test.ts @@ -29,7 +29,7 @@ import { NumberArray, Point3dArray } from "../../geometry3d/PointHelpers"; import { Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; import { Point4d } from "../../geometry4d/Point4d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/bspline/ConvexPolygon2d.test.ts b/core/geometry/src/test/bspline/ConvexPolygon2d.test.ts index eb7801ce6597..9711886f1f70 100644 --- a/core/geometry/src/test/bspline/ConvexPolygon2d.test.ts +++ b/core/geometry/src/test/bspline/ConvexPolygon2d.test.ts @@ -14,7 +14,7 @@ import { Point3d } from "../../geometry3d/Point3dVector3d"; import { PolygonOps } from "../../geometry3d/PolygonOps"; import { Ray2d } from "../../geometry3d/Ray2d"; import { ConvexPolygon2d } from "../../numerics/ConvexPolygon2d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/bspline/InterpolationCurve3d.test.ts b/core/geometry/src/test/bspline/InterpolationCurve3d.test.ts index 19cdb04c50ee..55b76830b303 100644 --- a/core/geometry/src/test/bspline/InterpolationCurve3d.test.ts +++ b/core/geometry/src/test/bspline/InterpolationCurve3d.test.ts @@ -9,7 +9,7 @@ import { GeometryQuery } from "../../curve/GeometryQuery"; import { LineString3d } from "../../curve/LineString3d"; import { Angle } from "../../geometry3d/Angle"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { testGeometryQueryRoundTrip } from "../serialization/FlatBuffer.test"; diff --git a/core/geometry/src/test/clipping/AlternatingConvexClipTree.test.ts b/core/geometry/src/test/clipping/AlternatingConvexClipTree.test.ts index 3fd9036da0ee..dc10421bed48 100644 --- a/core/geometry/src/test/clipping/AlternatingConvexClipTree.test.ts +++ b/core/geometry/src/test/clipping/AlternatingConvexClipTree.test.ts @@ -24,7 +24,7 @@ import { Range3d } from "../../geometry3d/Range"; import { GrowableXYZArrayCache } from "../../geometry3d/ReusableObjectCache"; import { Transform } from "../../geometry3d/Transform"; import { UsageSums } from "../../numerics/UsageSums"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker, SaveAndRestoreCheckTransform } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/clipping/ClipNode.test.ts b/core/geometry/src/test/clipping/ClipNode.test.ts index 1b4d610b3862..51d0465230b2 100644 --- a/core/geometry/src/test/clipping/ClipNode.test.ts +++ b/core/geometry/src/test/clipping/ClipNode.test.ts @@ -19,7 +19,7 @@ import { Loop } from "../../curve/Loop"; import { Geometry } from "../../Geometry"; import { AngleSweep } from "../../geometry3d/AngleSweep"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { IModelJson } from "../../serialization/IModelJsonSchema"; diff --git a/core/geometry/src/test/clipping/ClipPlanes.test.ts b/core/geometry/src/test/clipping/ClipPlanes.test.ts index c02a609007dc..d04e62d3b853 100644 --- a/core/geometry/src/test/clipping/ClipPlanes.test.ts +++ b/core/geometry/src/test/clipping/ClipPlanes.test.ts @@ -31,7 +31,7 @@ import { Matrix4d } from "../../geometry4d/Matrix4d"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { ClippedPolyfaceBuilders, PolyfaceClip } from "../../polyface/PolyfaceClip"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Box } from "../../solid/Box"; import { Cone } from "../../solid/Cone"; import { LinearSweep } from "../../solid/LinearSweep"; diff --git a/core/geometry/src/test/clipping/ClipPrimitives.test.ts b/core/geometry/src/test/clipping/ClipPrimitives.test.ts index 3a256e0d7dc4..81b8ba143cd1 100644 --- a/core/geometry/src/test/clipping/ClipPrimitives.test.ts +++ b/core/geometry/src/test/clipping/ClipPrimitives.test.ts @@ -22,7 +22,7 @@ import { Transform } from "../../geometry3d/Transform"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { ClippedPolyfaceBuilders, PolyfaceClip } from "../../polyface/PolyfaceClip"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Cone } from "../../solid/Cone"; import { HalfEdge, HalfEdgeGraph, HalfEdgeMask } from "../../topology/Graph"; import { Triangulator } from "../../topology/Triangulation"; diff --git a/core/geometry/src/test/clipping/ClipVector.test.ts b/core/geometry/src/test/clipping/ClipVector.test.ts index cb2fc27c8242..f1dc6c68e103 100644 --- a/core/geometry/src/test/clipping/ClipVector.test.ts +++ b/core/geometry/src/test/clipping/ClipVector.test.ts @@ -20,7 +20,7 @@ import { Range3d } from "../../geometry3d/Range"; import { GrowableXYZArrayCache } from "../../geometry3d/ReusableObjectCache"; import { Transform } from "../../geometry3d/Transform"; import { Matrix4d } from "../../geometry4d/Matrix4d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { prettyPrint } from "../testFunctions"; diff --git a/core/geometry/src/test/clipping/CutFill.test.ts b/core/geometry/src/test/clipping/CutFill.test.ts index 5e69d55881b3..3ab60156f9a4 100644 --- a/core/geometry/src/test/clipping/CutFill.test.ts +++ b/core/geometry/src/test/clipping/CutFill.test.ts @@ -11,7 +11,7 @@ import { ClippedPolyfaceBuilders, PolyfaceClip } from "../../polyface/PolyfaceCl import { Range3d } from "../../geometry3d/Range"; import { IndexedPolyface, Polyface } from "../../polyface/Polyface"; import { ConvexClipPlaneSet } from "../../clipping/ConvexClipPlaneSet"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; diff --git a/core/geometry/src/test/clipping/PolyfaceClip.test.ts b/core/geometry/src/test/clipping/PolyfaceClip.test.ts index 40ab0b9de527..7434ece5b1aa 100644 --- a/core/geometry/src/test/clipping/PolyfaceClip.test.ts +++ b/core/geometry/src/test/clipping/PolyfaceClip.test.ts @@ -38,7 +38,7 @@ import { IndexedPolyface, Polyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { ClippedPolyfaceBuilders, PolyfaceClip } from "../../polyface/PolyfaceClip"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Box } from "../../solid/Box"; import { LinearSweep } from "../../solid/LinearSweep"; diff --git a/core/geometry/src/test/clipping/XYOffsetAsClipper.test.ts b/core/geometry/src/test/clipping/XYOffsetAsClipper.test.ts index f657e218d588..91c13d58ffc6 100644 --- a/core/geometry/src/test/clipping/XYOffsetAsClipper.test.ts +++ b/core/geometry/src/test/clipping/XYOffsetAsClipper.test.ts @@ -27,7 +27,7 @@ import { Transform } from "../../geometry3d/Transform"; import { IndexedPolyface } from "../../polyface/Polyface"; import { ClippedPolyfaceBuilders, PolyfaceClip } from "../../polyface/PolyfaceClip"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { SweepContour } from "../../solid/SweepContour"; import { HalfEdgeGraphMerge, VertexNeighborhoodSortData } from "../../topology/Merging"; diff --git a/core/geometry/src/test/curve/Arc3d.test.ts b/core/geometry/src/test/curve/Arc3d.test.ts index c53aa7c07418..dabd1805a01a 100644 --- a/core/geometry/src/test/curve/Arc3d.test.ts +++ b/core/geometry/src/test/curve/Arc3d.test.ts @@ -28,7 +28,7 @@ import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Range1d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; import { SmallSystem } from "../../numerics/SmallSystem"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { prettyPrint } from "../testFunctions"; diff --git a/core/geometry/src/test/curve/BuildingCodeOffsetOps.ts b/core/geometry/src/test/curve/BuildingCodeOffsetOps.ts index 24370c0df935..87032a06721d 100644 --- a/core/geometry/src/test/curve/BuildingCodeOffsetOps.ts +++ b/core/geometry/src/test/curve/BuildingCodeOffsetOps.ts @@ -16,6 +16,8 @@ import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { AnalyticRoots } from "../../numerics/Polynomials"; import { SmallSystem } from "../../numerics/SmallSystem"; +/* eslint-disable @typescript-eslint/no-non-null-assertion */ + /** * Assorted static methods for constructing fragmentary and complete curves for offsets from linestrings. * * Primary method is the static edgeByEdgeOffsetFromPoints diff --git a/core/geometry/src/test/curve/Curve.test.ts b/core/geometry/src/test/curve/Curve.test.ts index 38e508653f5d..30511c418d3a 100644 --- a/core/geometry/src/test/curve/Curve.test.ts +++ b/core/geometry/src/test/curve/Curve.test.ts @@ -38,7 +38,7 @@ import { Segment1d } from "../../geometry3d/Segment1d"; import { Transform } from "../../geometry3d/Transform"; import { Point4d } from "../../geometry4d/Point4d"; import { Newton1dUnboundedApproximateDerivative, NewtonEvaluatorRtoR } from "../../numerics/Newton"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { RuledSweep } from "../../solid/RuledSweep"; import { Sphere } from "../../solid/Sphere"; diff --git a/core/geometry/src/test/curve/CurveChainWithDistanceIndex.test.ts b/core/geometry/src/test/curve/CurveChainWithDistanceIndex.test.ts index e32c25f9949f..9c9cf0699e31 100644 --- a/core/geometry/src/test/curve/CurveChainWithDistanceIndex.test.ts +++ b/core/geometry/src/test/curve/CurveChainWithDistanceIndex.test.ts @@ -18,7 +18,7 @@ import { Geometry } from "../../Geometry"; import { Angle } from "../../geometry3d/Angle"; import { AngleSweep } from "../../geometry3d/AngleSweep"; import { Point3d } from "../../geometry3d/Point3dVector3d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/curve/CurveCollection.test.ts b/core/geometry/src/test/curve/CurveCollection.test.ts index 42d72cc74f57..ebf99fb21e91 100644 --- a/core/geometry/src/test/curve/CurveCollection.test.ts +++ b/core/geometry/src/test/curve/CurveCollection.test.ts @@ -26,7 +26,7 @@ import { AngleSweep } from "../../geometry3d/AngleSweep"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/curve/CurveCurveIntersectXY.test.ts b/core/geometry/src/test/curve/CurveCurveIntersectXY.test.ts index 35d4f6f13195..08c25e8d4d2d 100644 --- a/core/geometry/src/test/curve/CurveCurveIntersectXY.test.ts +++ b/core/geometry/src/test/curve/CurveCurveIntersectXY.test.ts @@ -27,7 +27,7 @@ import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Transform } from "../../geometry3d/Transform"; import { Map4d } from "../../geometry4d/Map4d"; import { Matrix4d } from "../../geometry4d/Matrix4d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/curve/CurveFactory.test.ts b/core/geometry/src/test/curve/CurveFactory.test.ts index c76bd70112ae..72039e533b2b 100644 --- a/core/geometry/src/test/curve/CurveFactory.test.ts +++ b/core/geometry/src/test/curve/CurveFactory.test.ts @@ -30,7 +30,7 @@ import { Point4d } from "../../geometry4d/Point4d"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { RuledSweep } from "../../solid/RuledSweep"; import { Checker } from "../Checker"; diff --git a/core/geometry/src/test/curve/EmitStrokableParts.test.ts b/core/geometry/src/test/curve/EmitStrokableParts.test.ts index bce6e4c28125..f2a3dc6c1870 100644 --- a/core/geometry/src/test/curve/EmitStrokableParts.test.ts +++ b/core/geometry/src/test/curve/EmitStrokableParts.test.ts @@ -11,7 +11,7 @@ import { FrameBuilder } from "../../geometry3d/FrameBuilder"; import { IStrokeHandler } from "../../geometry3d/GeometryHandler"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; // import { Point3d, Vector3d, Transform, Matrix3d, Range1d } from "../PointVector"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; // import { Geometry } from "../Geometry"; import { Checker } from "../Checker"; diff --git a/core/geometry/src/test/curve/GeometryQuery.test.ts b/core/geometry/src/test/curve/GeometryQuery.test.ts index 83812b847bd7..7257e1be5c54 100644 --- a/core/geometry/src/test/curve/GeometryQuery.test.ts +++ b/core/geometry/src/test/curve/GeometryQuery.test.ts @@ -24,7 +24,7 @@ import { GeometryHandler, NullGeometryHandler, RecurseToCurvesGeometryHandler } import { Vector3d } from "../../geometry3d/Point3dVector3d"; import { Ray3d } from "../../geometry3d/Ray3d"; import { IndexedPolyface } from "../../polyface/Polyface"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Box } from "../../solid/Box"; import { Cone } from "../../solid/Cone"; import { LinearSweep } from "../../solid/LinearSweep"; diff --git a/core/geometry/src/test/curve/LineSegment3d.test.ts b/core/geometry/src/test/curve/LineSegment3d.test.ts index e902488142d0..eefa7c7b6af4 100644 --- a/core/geometry/src/test/curve/LineSegment3d.test.ts +++ b/core/geometry/src/test/curve/LineSegment3d.test.ts @@ -14,7 +14,7 @@ import { Matrix3d } from "../../geometry3d/Matrix3d"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Range1d, Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/curve/PointString3d.test.ts b/core/geometry/src/test/curve/PointString3d.test.ts index de5c8ba7820f..906490ae3759 100644 --- a/core/geometry/src/test/curve/PointString3d.test.ts +++ b/core/geometry/src/test/curve/PointString3d.test.ts @@ -9,7 +9,7 @@ import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAn import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Point3dArray } from "../../geometry3d/PointHelpers"; import { Range3d } from "../../geometry3d/Range"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; function exercisePointString3d(ck: Checker, lsA: PointString3d) { diff --git a/core/geometry/src/test/curve/Region.test.ts b/core/geometry/src/test/curve/Region.test.ts index c8c99ea0f519..0d3769ebcd9d 100644 --- a/core/geometry/src/test/curve/Region.test.ts +++ b/core/geometry/src/test/curve/Region.test.ts @@ -9,7 +9,7 @@ import { LineString3d } from "../../curve/LineString3d"; import { Loop } from "../../curve/Loop"; import { ParityRegion } from "../../curve/ParityRegion"; import { UnionRegion } from "../../curve/UnionRegion"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; describe("Regions", () => { diff --git a/core/geometry/src/test/curve/RegionBoolean.test.ts b/core/geometry/src/test/curve/RegionBoolean.test.ts index da699b374e11..52bc0e31f16a 100644 --- a/core/geometry/src/test/curve/RegionBoolean.test.ts +++ b/core/geometry/src/test/curve/RegionBoolean.test.ts @@ -39,7 +39,7 @@ import { Transform } from "../../geometry3d/Transform"; import { PolyfaceVisitor } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { DuplicateFacetClusterSelector, PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { LinearSweep } from "../../solid/LinearSweep"; import { HalfEdgeGraph } from "../../topology/Graph"; diff --git a/core/geometry/src/test/curve/TransitionSpiral3d.test.ts b/core/geometry/src/test/curve/TransitionSpiral3d.test.ts index b49f36a4d671..335bac0e254b 100644 --- a/core/geometry/src/test/curve/TransitionSpiral3d.test.ts +++ b/core/geometry/src/test/curve/TransitionSpiral3d.test.ts @@ -35,7 +35,7 @@ import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Segment1d } from "../../geometry3d/Segment1d"; import { Transform } from "../../geometry3d/Transform"; import { Quadrature } from "../../numerics/Quadrature"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/geometry3d/GrowableArray.test.ts b/core/geometry/src/test/geometry3d/GrowableArray.test.ts index 3508889e1123..723d7a8c0c37 100644 --- a/core/geometry/src/test/geometry3d/GrowableArray.test.ts +++ b/core/geometry/src/test/geometry3d/GrowableArray.test.ts @@ -18,7 +18,7 @@ import { Point3dArray } from "../../geometry3d/PointHelpers"; import { Transform } from "../../geometry3d/Transform"; import { ClusterableArray } from "../../numerics/ClusterableArray"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { prettyPrint } from "../testFunctions"; diff --git a/core/geometry/src/test/geometry3d/GrowableXYArray.test.ts b/core/geometry/src/test/geometry3d/GrowableXYArray.test.ts index eb0d244476cf..f05d41d11452 100644 --- a/core/geometry/src/test/geometry3d/GrowableXYArray.test.ts +++ b/core/geometry/src/test/geometry3d/GrowableXYArray.test.ts @@ -15,7 +15,7 @@ import { Point3dArrayCarrier } from "../../geometry3d/Point3dArrayCarrier"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Range2d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { XAndY } from "../../geometry3d/XYZProps"; diff --git a/core/geometry/src/test/geometry3d/Matrix3d.test.ts b/core/geometry/src/test/geometry3d/Matrix3d.test.ts index e6d23d7841f2..76d754cdc125 100644 --- a/core/geometry/src/test/geometry3d/Matrix3d.test.ts +++ b/core/geometry/src/test/geometry3d/Matrix3d.test.ts @@ -12,7 +12,7 @@ import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; import { XYAndZ } from "../../geometry3d/XYZProps"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/geometry3d/PlaneAltitudeEvaluators.test.ts b/core/geometry/src/test/geometry3d/PlaneAltitudeEvaluators.test.ts index 97ce57275124..cdf82c847b77 100644 --- a/core/geometry/src/test/geometry3d/PlaneAltitudeEvaluators.test.ts +++ b/core/geometry/src/test/geometry3d/PlaneAltitudeEvaluators.test.ts @@ -10,7 +10,7 @@ import { Matrix3d } from "../../geometry3d/Matrix3d"; import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Checker } from "../Checker"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Point4d } from "../../geometry4d/Point4d"; import { ClipPlane } from "../../clipping/ClipPlane"; import { Plane3d } from "../../geometry3d/Plane3d"; diff --git a/core/geometry/src/test/geometry3d/Point2dVector2d.test.ts b/core/geometry/src/test/geometry3d/Point2dVector2d.test.ts index c659fef908ff..34f2abacef65 100644 --- a/core/geometry/src/test/geometry3d/Point2dVector2d.test.ts +++ b/core/geometry/src/test/geometry3d/Point2dVector2d.test.ts @@ -9,7 +9,7 @@ import { Angle } from "../../geometry3d/Angle"; import { Point2dArrayCarrier } from "../../geometry3d/Point2dArrayCarrier"; import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; import { Point3d } from "../../geometry3d/Point3dVector3d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import * as bsiChecker from "../Checker"; describe("Point2d", () => { diff --git a/core/geometry/src/test/geometry3d/Point3dVector3d.test.ts b/core/geometry/src/test/geometry3d/Point3dVector3d.test.ts index 1bf9398e1193..c7512c0bf44b 100644 --- a/core/geometry/src/test/geometry3d/Point3dVector3d.test.ts +++ b/core/geometry/src/test/geometry3d/Point3dVector3d.test.ts @@ -10,7 +10,7 @@ import { Point3dArrayCarrier } from "../../geometry3d/Point3dArrayCarrier"; import { Point3d, Vector3d, XYZ } from "../../geometry3d/Point3dVector3d"; import { Ray3d } from "../../geometry3d/Ray3d"; import { XYZProps } from "../../geometry3d/XYZProps"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import * as bsiChecker from "../Checker"; // cSpell:words Jcross CCWXY CWXY diff --git a/core/geometry/src/test/geometry3d/PointHelper.test.ts b/core/geometry/src/test/geometry3d/PointHelper.test.ts index 0be46ccc9767..e7ccbe44284b 100644 --- a/core/geometry/src/test/geometry3d/PointHelper.test.ts +++ b/core/geometry/src/test/geometry3d/PointHelper.test.ts @@ -27,7 +27,7 @@ import { XAndY, XYZProps } from "../../geometry3d/XYZProps"; import { Matrix4d } from "../../geometry4d/Matrix4d"; import { MomentData } from "../../geometry4d/MomentData"; import { Point4d } from "../../geometry4d/Point4d"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { HalfEdgeGraph } from "../../topology/Graph"; import { HalfEdgeGraphSearch } from "../../topology/HalfEdgeGraphSearch"; import { Triangulator } from "../../topology/Triangulation"; diff --git a/core/geometry/src/test/geometry3d/PolygonOps.test.ts b/core/geometry/src/test/geometry3d/PolygonOps.test.ts index 2f4b15f283f5..c32dde118037 100644 --- a/core/geometry/src/test/geometry3d/PolygonOps.test.ts +++ b/core/geometry/src/test/geometry3d/PolygonOps.test.ts @@ -22,7 +22,7 @@ import { Range2d, Range3d } from "../../geometry3d/Range"; import { Ray3d } from "../../geometry3d/Ray3d"; import { SortablePolygon } from "../../geometry3d/SortablePolygon"; import { Transform } from "../../geometry3d/Transform"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/geometry3d/PolylineCompression.test.ts b/core/geometry/src/test/geometry3d/PolylineCompression.test.ts index 535b47128245..f8db7ff992c8 100644 --- a/core/geometry/src/test/geometry3d/PolylineCompression.test.ts +++ b/core/geometry/src/test/geometry3d/PolylineCompression.test.ts @@ -11,7 +11,7 @@ import { Point3dArray } from "../../geometry3d/PointHelpers"; import { PolygonOps } from "../../geometry3d/PolygonOps"; import { PolylineOps } from "../../geometry3d/PolylineOps"; import { Range3d } from "../../geometry3d/Range"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/geometry3d/Range3d.test.ts b/core/geometry/src/test/geometry3d/Range3d.test.ts index c5db297ed4f1..acee14d76e63 100644 --- a/core/geometry/src/test/geometry3d/Range3d.test.ts +++ b/core/geometry/src/test/geometry3d/Range3d.test.ts @@ -14,7 +14,7 @@ import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Range1d, Range2d, Range3d, RangeBase } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; import { SineCosinePolynomial } from "../../numerics/Polynomials"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; import { prettyPrint } from "../testFunctions"; diff --git a/core/geometry/src/test/geometry3d/RangeTree.test.ts b/core/geometry/src/test/geometry3d/RangeTree.test.ts index e9d0545a7509..bef4b3084057 100644 --- a/core/geometry/src/test/geometry3d/RangeTree.test.ts +++ b/core/geometry/src/test/geometry3d/RangeTree.test.ts @@ -27,7 +27,7 @@ import { LineString3dRangeTreeContext } from "../../polyface/RangeTree/LineStrin import { Point3dArrayRangeTreeContext } from "../../polyface/RangeTree/Point3dArrayRangeTreeContext"; import { PolyfaceRangeTreeContext } from "../../polyface/RangeTree/PolyfaceRangeTreeContext"; import { RangeTreeNode, RangeTreeOps, SingleTreeSearchHandler, TwoTreeSearchHandler } from "../../polyface/RangeTree/RangeTreeNode"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { LinearSweep } from "../../solid/LinearSweep"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/geometry3d/Transform.test.ts b/core/geometry/src/test/geometry3d/Transform.test.ts index 96a6bcc38b0b..b2367f5b8071 100644 --- a/core/geometry/src/test/geometry3d/Transform.test.ts +++ b/core/geometry/src/test/geometry3d/Transform.test.ts @@ -9,7 +9,7 @@ import { Matrix3d } from "../../geometry3d/Matrix3d"; import { Point2d } from "../../geometry3d/Point2dVector2d"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Transform } from "../../geometry3d/Transform"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/geometry3d/ViewBox.test.ts b/core/geometry/src/test/geometry3d/ViewBox.test.ts index 0f9dc810c3bd..5078d40cb539 100644 --- a/core/geometry/src/test/geometry3d/ViewBox.test.ts +++ b/core/geometry/src/test/geometry3d/ViewBox.test.ts @@ -15,7 +15,7 @@ import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { Transform } from "../../geometry3d/Transform"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/geometry4d/MomentData.test.ts b/core/geometry/src/test/geometry4d/MomentData.test.ts index 70598ec33014..690cbfe7baa4 100644 --- a/core/geometry/src/test/geometry4d/MomentData.test.ts +++ b/core/geometry/src/test/geometry4d/MomentData.test.ts @@ -19,7 +19,7 @@ import { Matrix3d } from "../../geometry3d/Matrix3d"; import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Transform } from "../../geometry3d/Transform"; import { MomentData } from "../../geometry4d/MomentData"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/numerics/BandedSystem.test.ts b/core/geometry/src/test/numerics/BandedSystem.test.ts index f3580a344d88..f27ac664d9e3 100644 --- a/core/geometry/src/test/numerics/BandedSystem.test.ts +++ b/core/geometry/src/test/numerics/BandedSystem.test.ts @@ -10,7 +10,7 @@ import { AngleSweep } from "../../geometry3d/AngleSweep"; import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; import { Point3d } from "../../geometry3d/Point3dVector3d"; import { BandedSystem } from "../../numerics/BandedSystem"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/numerics/Bezier.test.ts b/core/geometry/src/test/numerics/Bezier.test.ts index 749d265841bd..2f63c5eee659 100644 --- a/core/geometry/src/test/numerics/Bezier.test.ts +++ b/core/geometry/src/test/numerics/Bezier.test.ts @@ -12,10 +12,10 @@ import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Range3d } from "../../geometry3d/Range"; import { Segment1d } from "../../geometry3d/Segment1d"; import { Transform } from "../../geometry3d/Transform"; -// import { Sample } from "../serialization/GeometrySamples"; +// import { Sample } from "../GeometrySamples"; import { BezierCoffs, Order2Bezier, Order3Bezier, Order4Bezier, Order5Bezier, UnivariateBezier } from "../../numerics/BezierPolynomials"; import { PascalCoefficients } from "../../numerics/PascalCoefficients"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Box } from "../../solid/Box"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/polyface/DrapeLinestring.test.ts b/core/geometry/src/test/polyface/DrapeLinestring.test.ts index a8c9653042c2..2ccb3e255ed4 100644 --- a/core/geometry/src/test/polyface/DrapeLinestring.test.ts +++ b/core/geometry/src/test/polyface/DrapeLinestring.test.ts @@ -16,7 +16,7 @@ import { Transform } from "../../geometry3d/Transform"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery, SweepLineStringToFacetsOptions } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/polyface/FacetFaceData.test.ts b/core/geometry/src/test/polyface/FacetFaceData.test.ts index 008a3d8efde5..b08e89c660b8 100644 --- a/core/geometry/src/test/polyface/FacetFaceData.test.ts +++ b/core/geometry/src/test/polyface/FacetFaceData.test.ts @@ -7,7 +7,7 @@ import { GeometryQuery } from "../../curve/GeometryQuery"; import { Point2d } from "../../geometry3d/Point2dVector2d"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { FacetFaceData } from "../../polyface/FacetFaceData"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/polyface/GreedyTriangulationBetweenLineStrings.test.ts b/core/geometry/src/test/polyface/GreedyTriangulationBetweenLineStrings.test.ts index d6a78e03a91e..8b8bb963b76b 100644 --- a/core/geometry/src/test/polyface/GreedyTriangulationBetweenLineStrings.test.ts +++ b/core/geometry/src/test/polyface/GreedyTriangulationBetweenLineStrings.test.ts @@ -11,7 +11,7 @@ import { Angle } from "../../geometry3d/Angle"; import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; import { Point3d } from "../../geometry3d/Point3dVector3d"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/polyface/OffsetMeshContext.test.ts b/core/geometry/src/test/polyface/OffsetMeshContext.test.ts index 5f3a6917249c..45a28c701fc0 100644 --- a/core/geometry/src/test/polyface/OffsetMeshContext.test.ts +++ b/core/geometry/src/test/polyface/OffsetMeshContext.test.ts @@ -14,7 +14,7 @@ import { GeometryQuery } from "../../curve/GeometryQuery"; import { Checker } from "../Checker"; import { IndexedPolyface } from "../../polyface/Polyface"; import { RFunctions } from "./DrapeLinestring.test"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Geometry } from "../../Geometry"; import { Angle } from "../../geometry3d/Angle"; import { HalfEdge, HalfEdgeGraph, HalfEdgeMask } from "../../topology/Graph"; diff --git a/core/geometry/src/test/polyface/Polyface.test.ts b/core/geometry/src/test/polyface/Polyface.test.ts index a46160cb4e52..215ea83ea208 100644 --- a/core/geometry/src/test/polyface/Polyface.test.ts +++ b/core/geometry/src/test/polyface/Polyface.test.ts @@ -39,7 +39,7 @@ import { IndexedPolyface, Polyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceData } from "../../polyface/PolyfaceData"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Box } from "../../solid/Box"; import { Cone } from "../../solid/Cone"; diff --git a/core/geometry/src/test/polyface/PolyfaceData.test.ts b/core/geometry/src/test/polyface/PolyfaceData.test.ts index b659f13fd03a..7406da09f955 100644 --- a/core/geometry/src/test/polyface/PolyfaceData.test.ts +++ b/core/geometry/src/test/polyface/PolyfaceData.test.ts @@ -7,7 +7,7 @@ import { describe, expect, it } from "vitest"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceData } from "../../polyface/PolyfaceData"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; describe("PolyfaceData", () => { diff --git a/core/geometry/src/test/polyface/PolyfaceQuery.test.ts b/core/geometry/src/test/polyface/PolyfaceQuery.test.ts index 2c49f4d240b0..8e1b1761a003 100644 --- a/core/geometry/src/test/polyface/PolyfaceQuery.test.ts +++ b/core/geometry/src/test/polyface/PolyfaceQuery.test.ts @@ -39,7 +39,7 @@ import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { ClippedPolyfaceBuilders, PolyfaceClip } from "../../polyface/PolyfaceClip"; import { PolyfaceData } from "../../polyface/PolyfaceData"; import { DuplicateFacetClusterSelector, PolyfaceQuery, SweepLineStringToFacetsOptions } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Box } from "../../solid/Box"; import { Cone } from "../../solid/Cone"; diff --git a/core/geometry/src/test/polyface/SearchableSetOfRange2d.test.ts b/core/geometry/src/test/polyface/SearchableSetOfRange2d.test.ts index dff484a7634c..e74d72958f37 100644 --- a/core/geometry/src/test/polyface/SearchableSetOfRange2d.test.ts +++ b/core/geometry/src/test/polyface/SearchableSetOfRange2d.test.ts @@ -21,7 +21,7 @@ import { GriddedRaggedRange2dSetWithOverflow } from "../../polyface/multiclip/Gr import { LinearSearchRange2dArray } from "../../polyface/multiclip/LinearSearchRange2dArray"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/polyface/XYPointBuckets.test.ts b/core/geometry/src/test/polyface/XYPointBuckets.test.ts index b24d654257b7..01fa8d0e5d17 100644 --- a/core/geometry/src/test/polyface/XYPointBuckets.test.ts +++ b/core/geometry/src/test/polyface/XYPointBuckets.test.ts @@ -9,7 +9,7 @@ import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Range2d } from "../../geometry3d/Range"; import { XYIndexGrid, XYPointBuckets } from "../../polyface/multiclip/XYPointBuckets"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/serialization/BSIJSON.test.ts b/core/geometry/src/test/serialization/BSIJSON.test.ts index 810a525e1fd2..f7ce8934c09f 100644 --- a/core/geometry/src/test/serialization/BSIJSON.test.ts +++ b/core/geometry/src/test/serialization/BSIJSON.test.ts @@ -36,7 +36,7 @@ import { Matrix4d } from "../../geometry4d/Matrix4d"; import { Point4d } from "../../geometry4d/Point4d"; import { Complex } from "../../numerics/Complex"; import { IndexedPolyface } from "../../polyface/Polyface"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/serialization/FlatBuffer.test.ts b/core/geometry/src/test/serialization/FlatBuffer.test.ts index f73cf4530756..e74bd5905007 100644 --- a/core/geometry/src/test/serialization/FlatBuffer.test.ts +++ b/core/geometry/src/test/serialization/FlatBuffer.test.ts @@ -23,7 +23,7 @@ import { TaggedNumericData } from "../../polyface/TaggedNumericData"; import { BentleyGeometryFlatBuffer } from "../../serialization/BentleyGeometryFlatBuffer"; import { BGFBAccessors } from "../../serialization/BGFBAccessors"; import { DeepCompare } from "../../serialization/DeepCompare"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { SolidPrimitive } from "../../solid/SolidPrimitive"; import { Checker } from "../Checker"; diff --git a/core/geometry/src/test/serialization/IModelJson.test.ts b/core/geometry/src/test/serialization/IModelJson.test.ts index db6567f46c1a..1730e0f9ef30 100644 --- a/core/geometry/src/test/serialization/IModelJson.test.ts +++ b/core/geometry/src/test/serialization/IModelJson.test.ts @@ -13,7 +13,7 @@ import { Path } from "../../curve/Path"; import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; import { IndexedPolyface } from "../../polyface/Polyface"; import { DeepCompare } from "../../serialization/DeepCompare"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Box } from "../../solid/Box"; import { Checker } from "../Checker"; diff --git a/core/geometry/src/test/solid/Solid.test.ts b/core/geometry/src/test/solid/Solid.test.ts index 4e7540c53669..5aca0605ed8f 100644 --- a/core/geometry/src/test/solid/Solid.test.ts +++ b/core/geometry/src/test/solid/Solid.test.ts @@ -26,7 +26,7 @@ import { Transform } from "../../geometry3d/Transform"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { Box } from "../../solid/Box"; import { Cone } from "../../solid/Cone"; import { LinearSweep } from "../../solid/LinearSweep"; diff --git a/core/geometry/src/test/topology/ChainCollector.test.ts b/core/geometry/src/test/topology/ChainCollector.test.ts index 810280a06529..03f4a461a4a5 100644 --- a/core/geometry/src/test/topology/ChainCollector.test.ts +++ b/core/geometry/src/test/topology/ChainCollector.test.ts @@ -19,7 +19,7 @@ import { Geometry } from "../../Geometry"; import { PolygonOps } from "../../geometry3d/PolygonOps"; import { PolylineOps } from "../../geometry3d/PolylineOps"; import { Range3d } from "../../geometry3d/Range"; -import { Sample, SteppedIndexFunctionFactory } from "../../serialization/GeometrySamples"; +import { Sample, SteppedIndexFunctionFactory } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/topology/CurveWireOffset.test.ts b/core/geometry/src/test/topology/CurveWireOffset.test.ts index 42d3cadaa334..7d9e9034c619 100644 --- a/core/geometry/src/test/topology/CurveWireOffset.test.ts +++ b/core/geometry/src/test/topology/CurveWireOffset.test.ts @@ -18,7 +18,7 @@ import { RegionOps } from "../../curve/RegionOps"; import { Matrix3d } from "../../geometry3d/Matrix3d"; import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Transform } from "../../geometry3d/Transform"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { Checker } from "../Checker"; import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; diff --git a/core/geometry/src/test/topology/HalfEdgeGraphSearch.test.ts b/core/geometry/src/test/topology/HalfEdgeGraphSearch.test.ts index 136c816e5c95..67b374517d83 100644 --- a/core/geometry/src/test/topology/HalfEdgeGraphSearch.test.ts +++ b/core/geometry/src/test/topology/HalfEdgeGraphSearch.test.ts @@ -12,7 +12,7 @@ import { Transform } from "../../geometry3d/Transform"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { HalfEdge, HalfEdgeGraph, HalfEdgeMask } from "../../topology/Graph"; import { HalfEdgeGraphSearch, HalfEdgeMaskTester } from "../../topology/HalfEdgeGraphSearch"; import { Checker } from "../Checker"; diff --git a/core/geometry/src/test/topology/HalfEdgeGraphSpineContext.test.ts b/core/geometry/src/test/topology/HalfEdgeGraphSpineContext.test.ts index 18c3031e78cc..1e86abfe1ecc 100644 --- a/core/geometry/src/test/topology/HalfEdgeGraphSpineContext.test.ts +++ b/core/geometry/src/test/topology/HalfEdgeGraphSpineContext.test.ts @@ -17,7 +17,7 @@ import { Matrix3d } from "../../geometry3d/Matrix3d"; import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Transform } from "../../geometry3d/Transform"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { HalfEdge, HalfEdgeGraph } from "../../topology/Graph"; import { HalfEdgeGraphSpineContext } from "../../topology/HalfEdgeGraphSpineContext"; diff --git a/core/geometry/src/test/topology/InsertAndRetriangulateContext.test.ts b/core/geometry/src/test/topology/InsertAndRetriangulateContext.test.ts index deec612362c1..8ef6b65d504d 100644 --- a/core/geometry/src/test/topology/InsertAndRetriangulateContext.test.ts +++ b/core/geometry/src/test/topology/InsertAndRetriangulateContext.test.ts @@ -13,7 +13,7 @@ import { Point3dArray } from "../../geometry3d/PointHelpers"; import { PolygonOps } from "../../geometry3d/PolygonOps"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { HalfEdgeGraph } from "../../topology/Graph"; import { HalfEdgePositionDetail, HalfEdgeTopo } from "../../topology/HalfEdgePositionDetail"; import { InsertAndRetriangulateContext, InsertedVertexZOptions } from "../../topology/InsertAndRetriangulateContext"; diff --git a/core/geometry/src/test/topology/Merge.test.ts b/core/geometry/src/test/topology/Merge.test.ts index 9fba5a5b43c9..4a1797f36fc1 100644 --- a/core/geometry/src/test/topology/Merge.test.ts +++ b/core/geometry/src/test/topology/Merge.test.ts @@ -11,7 +11,7 @@ import { Matrix3d } from "../../geometry3d/Matrix3d"; import { Point3d } from "../../geometry3d/Point3dVector3d"; import { Transform } from "../../geometry3d/Transform"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { HalfEdge, HalfEdgeGraph } from "../../topology/Graph"; import { HalfEdgeGraphSearch } from "../../topology/HalfEdgeGraphSearch"; import { HalfEdgePriorityQueueWithPartnerArray } from "../../topology/HalfEdgePriorityQueue"; diff --git a/core/geometry/src/test/topology/RegionOps.test.ts b/core/geometry/src/test/topology/RegionOps.test.ts index 05adfa2e6415..e156840b0e89 100644 --- a/core/geometry/src/test/topology/RegionOps.test.ts +++ b/core/geometry/src/test/topology/RegionOps.test.ts @@ -44,7 +44,7 @@ import { Range2d, Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { HalfEdgeGraph } from "../../topology/Graph"; import { HalfEdgeGraphMerge } from "../../topology/Merging"; diff --git a/core/geometry/src/test/topology/RegularizeFace.test.ts b/core/geometry/src/test/topology/RegularizeFace.test.ts index df054d49f0b4..51ee55cbeb35 100644 --- a/core/geometry/src/test/topology/RegularizeFace.test.ts +++ b/core/geometry/src/test/topology/RegularizeFace.test.ts @@ -19,7 +19,7 @@ import { Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; import { IndexedPolyface } from "../../polyface/Polyface"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { HalfEdge, HalfEdgeGraph, HalfEdgeMask } from "../../topology/Graph"; import { HalfEdgeGraphSearch, HalfEdgeMaskTester } from "../../topology/HalfEdgeGraphSearch"; import { HalfEdgeGraphOps } from "../../topology/Merging"; diff --git a/core/geometry/src/test/topology/Triangulator.test.ts b/core/geometry/src/test/topology/Triangulator.test.ts index 86b93477b225..d759e6a6dd39 100644 --- a/core/geometry/src/test/topology/Triangulator.test.ts +++ b/core/geometry/src/test/topology/Triangulator.test.ts @@ -23,7 +23,7 @@ import { Range3d } from "../../geometry3d/Range"; import { Transform } from "../../geometry3d/Transform"; import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; -import { Sample } from "../../serialization/GeometrySamples"; +import { Sample } from "../GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; import { SweepContour } from "../../solid/SweepContour"; import { HalfEdge, HalfEdgeGraph, HalfEdgeMask } from "../../topology/Graph"; diff --git a/core/geometry/src/test/topology/Voronoi.test.ts b/core/geometry/src/test/topology/Voronoi.test.ts index fd40896357be..37233d539116 100644 --- a/core/geometry/src/test/topology/Voronoi.test.ts +++ b/core/geometry/src/test/topology/Voronoi.test.ts @@ -30,8 +30,8 @@ import { IndexedPolyface } from "../../polyface/Polyface"; import { HalfEdgeGraphSearch, PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; import { Point3dArrayRangeTreeContext } from "../../polyface/RangeTree/Point3dArrayRangeTreeContext"; -import { Sample } from "../../serialization/GeometrySamples"; import { IModelJson } from "../../serialization/IModelJsonSchema"; +import { Sample } from "../../test/GeometrySamples"; import { HalfEdge, HalfEdgeGraph, HalfEdgeMask, HalfEdgeToBooleanFunction } from "../../topology/Graph"; import { HalfEdgeGraphOps } from "../../topology/Merging"; import { Triangulator } from "../../topology/Triangulation"; diff --git a/core/geometry/src/topology/ChainMerge.ts b/core/geometry/src/topology/ChainMerge.ts index 1a8a082ef6db..55ab640bf331 100644 --- a/core/geometry/src/topology/ChainMerge.ts +++ b/core/geometry/src/topology/ChainMerge.ts @@ -171,9 +171,14 @@ export class ChainMergeContext { private primarySortKey(node: HalfEdge): number { return this._options.primarySortDirection.dotProductXYZ(node.x, node.y, node.z); } - /** Return difference of sortData members as sort comparison */ + /** + * Return difference of sortData members as sort comparison. + * sortData members must be defined. Otherwise, return 0. + */ private static nodeCompareSortData(nodeA: HalfEdge, nodeB: HalfEdge): number { - return nodeA.sortData! - nodeB.sortData!; + if (nodeA.sortData === undefined || nodeB.sortData === undefined) + return 0; + return nodeA.sortData - nodeB.sortData; } /** test if nodeA is a dangling edge end (i.e. edges around vertex equal 1, but detect it without walking all the way around. */ private static isIsolatedEnd(nodeA: HalfEdge): boolean { @@ -204,13 +209,15 @@ export class ChainMergeContext { const n = sortArray.length; for (let i0 = 0; i0 < n; i0++) { const node0 = sortArray[i0]; - const qMin = node0.sortData!; + const qMin = node0.sortData; + if (undefined === qMin) + continue; const qMax = qMin + xyzTolerance; if (ChainMergeContext.isIsolatedEnd(node0)) { for (let i1 = i0 + 1; i1 < n; i1++) { const node1 = sortArray[i1]; if (ChainMergeContext.isIsolatedEnd(node1)) { - if (node1.sortData! > qMax) + if (undefined !== node1.sortData && node1.sortData > qMax) break; if (node0.distanceXYZ(node1) <= xyzTolerance) { HalfEdge.pinch(node0, node1); diff --git a/core/geometry/src/topology/Graph.ts b/core/geometry/src/topology/Graph.ts index db14697b2b72..00e9413ccc5c 100644 --- a/core/geometry/src/topology/Graph.ts +++ b/core/geometry/src/topology/Graph.ts @@ -1460,7 +1460,8 @@ export class HalfEdgeGraph { private _maskManager: MaskManager; public constructor() { this.allHalfEdges = []; - this._maskManager = MaskManager.create(HalfEdgeMask.ALL_GRAB_DROP_MASKS)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this._maskManager = MaskManager.create(HalfEdgeMask.ALL_GRAB_DROP_MASKS)!; // guaranteed to not return undefined } /** * Ask for a mask (from the graph's free pool) for caller's use. diff --git a/core/geometry/src/topology/HalfEdgeGraphSearch.ts b/core/geometry/src/topology/HalfEdgeGraphSearch.ts index dca694fc3886..e193ac920b15 100644 --- a/core/geometry/src/topology/HalfEdgeGraphSearch.ts +++ b/core/geometry/src/topology/HalfEdgeGraphSearch.ts @@ -172,7 +172,9 @@ export class HalfEdgeGraphSearch { // the seed face is arbitrarily assigned the parity mask HalfEdgeGraphSearch.pushAndMaskAllNodesInFace(faceSeed, allMasks, stack, faces); while (stack.length > 0) { - const p = stack.pop()!; + const p = stack.pop(); + if (!p) + break; const mate = p.edgeMate; if (!mate) continue; diff --git a/core/geometry/src/topology/HalfEdgePointInGraphSearch.ts b/core/geometry/src/topology/HalfEdgePointInGraphSearch.ts index 7dfca5591359..5162538bb120 100644 --- a/core/geometry/src/topology/HalfEdgePointInGraphSearch.ts +++ b/core/geometry/src/topology/HalfEdgePointInGraphSearch.ts @@ -67,7 +67,9 @@ export class PointSearchContext { public reAimFromEdge( edgeHit: HalfEdgePositionDetail, ray: Ray3d, targetDistance: number, ): HalfEdgePositionDetail { - const nodeA = edgeHit.node!; + const nodeA = edgeHit.node; + if (undefined === nodeA) + return this.panic(); const dataA = NodeXYZUV.createNodeAndRayOrigin(nodeA, ray); const dataB = NodeXYZUV.createNodeAndRayOrigin(nodeA.edgeMate, ray); const sideA = -dataA.classifyV(0.0, this._tol); @@ -132,9 +134,11 @@ export class PointSearchContext { ): HalfEdgePositionDetail { assert(ray.origin.isExactEqual(vertexHit)); const vertexNode = vertexHit.node; - let outboundEdge = vertexNode!; + let outboundEdge = vertexNode; // lambda to handle the case where the target definitively lies in the same direction as outboundEdge const advancePositionAlongOutboundEdge = (rayParam: number): boolean => { + if (undefined === outboundEdge) + return false; if (Math.abs(rayParam - targetDistance) <= this._tol) { // direct hit at far end of outBoundEdge vertexHit.resetAsVertex(outboundEdge.faceSuccessor).setITag(1); } else if (rayParam > targetDistance) { // direct hit within outBoundEdge @@ -147,6 +151,8 @@ export class PointSearchContext { return true; }; do { + if (!outboundEdge) + return this.panic(); // examine the sector at the outboundEdge node; if ray lies in this sector, return updated detail const data0 = NodeXYZUV.createNodeAndRayOrigin(outboundEdge.faceSuccessor, ray); const data1 = NodeXYZUV.createNodeAndRayOrigin(outboundEdge.facePredecessor, ray); @@ -235,9 +241,13 @@ export class PointSearchContext { lastBefore.setFrom(vertexHit); return RayClassification.TargetOnVertex; } - if (u1 > targetDistance && u1 < firstAfter.getDTag()!) + const fTag = firstAfter.getDTag(); + const lTag = lastBefore.getDTag(); + if (undefined === fTag || undefined === lTag) + return RayClassification.NoHits; + if (u1 > targetDistance && u1 < fTag) firstAfter.setFrom(vertexHit); - if (u1 < targetDistance && u1 > lastBefore.getDTag()!) + if (u1 < targetDistance && u1 > lTag) lastBefore.setFrom(vertexHit); } else if (v0 * v1 < 0.0) { // ray crosses edge const edgeFraction = -v0 / (v1 - v0); @@ -249,9 +259,13 @@ export class PointSearchContext { lastBefore.setFrom(edgeHit); return RayClassification.TargetOnEdge; } - if (rayFraction > targetDistance && rayFraction < firstAfter.getDTag()!) + const fTag = firstAfter.getDTag(); + const lTag = lastBefore.getDTag(); + if (undefined === fTag || undefined === lTag) + return RayClassification.NoHits; + if (rayFraction > targetDistance && rayFraction < fTag) firstAfter.setFrom(edgeHit); - if (rayFraction < targetDistance && rayFraction > lastBefore.getDTag()!) + if (rayFraction < targetDistance && rayFraction > lTag) lastBefore.setFrom(edgeHit); } data0.setFrom(data1); diff --git a/core/geometry/src/topology/HalfEdgePriorityQueue.ts b/core/geometry/src/topology/HalfEdgePriorityQueue.ts index 6d810b6cd50d..5d400ae89e24 100644 --- a/core/geometry/src/topology/HalfEdgePriorityQueue.ts +++ b/core/geometry/src/topology/HalfEdgePriorityQueue.ts @@ -31,7 +31,9 @@ export class HalfEdgePriorityQueueWithPartnerArray { public popQueueToArray(): HalfEdge | undefined { if (this.priorityQueue.isEmpty) return undefined; - const x = this.priorityQueue.pop()!; + const x = this.priorityQueue.pop(); + if (!x) + return undefined; this.activeEdges.push(x); return x; } @@ -43,7 +45,9 @@ export class HalfEdgePriorityQueueWithPartnerArray { public popArrayToArrayIndex(i: number) { const n = this.activeEdges.length; if (i < n) { - const x = this.activeEdges.pop()!; + const x = this.activeEdges.pop(); + if (!x) + return; this.activeEdges[i] = x; } } diff --git a/core/geometry/src/topology/InsertAndRetriangulateContext.ts b/core/geometry/src/topology/InsertAndRetriangulateContext.ts index d2b008e60f27..4f664e12659d 100644 --- a/core/geometry/src/topology/InsertAndRetriangulateContext.ts +++ b/core/geometry/src/topology/InsertAndRetriangulateContext.ts @@ -55,7 +55,7 @@ export class InsertAndRetriangulateContext { private constructor(graph: HalfEdgeGraph, tolerance: number) { this._graph = graph; - this._edgeSet = MarkedEdgeSet.create(graph)!; + this._edgeSet = MarkedEdgeSet.create(graph) as MarkedEdgeSet; this._searcher = HalfEdgePositionDetail.create(); this._tolerance = tolerance; } @@ -101,19 +101,21 @@ export class InsertAndRetriangulateContext { const xyzC = Point3d.create(); let fractionC; let distanceC; + let tag: number | undefined; for (const nodeA of this._graph.allHalfEdges) { const nodeB = nodeA.faceSuccessor; fractionC = SmallSystem.lineSegment3dXYClosestPointUnbounded(nodeA, nodeB, xyz); - if (fractionC !== undefined) { + tag = position.getDTag(); + if (tag !== undefined && fractionC !== undefined) { if (fractionC > 1.0) { distanceC = xyz.distanceXY(nodeB); - if (distanceC < position.getDTag()!) { + if (distanceC < tag) { position.resetAsVertex(nodeB); position.setDTag(distanceC); } } else if (fractionC < 0.0) { distanceC = xyz.distanceXY(nodeA); - if (distanceC < position.getDTag()!) { + if (distanceC < tag) { position.resetAsVertex(nodeA); position.setDTag(distanceC); } @@ -121,7 +123,7 @@ export class InsertAndRetriangulateContext { nodeA.fractionToPoint3d(fractionC, xyzC); distanceC = xyz.distanceXY(xyzC); - if (distanceC < position.getDTag()!) { + if (distanceC < tag) { position.resetAtEdgeAndFraction(nodeA, fractionC); } } @@ -134,9 +136,11 @@ export class InsertAndRetriangulateContext { const position = HalfEdgePositionDetail.create(); position.setDTag(Number.MAX_VALUE); let distanceA; + let tag : number | undefined; for (const nodeA of this._graph.allHalfEdges) { distanceA = xyz.distanceXY(nodeA); - if (distanceA < position.getDTag()!) { + tag = position.getDTag(); + if (undefined !== tag && distanceA < tag) { position.resetAsVertex(nodeA); position.setDTag(distanceA); } @@ -245,7 +249,10 @@ export class InsertAndRetriangulateContext { } } else if (this._searcher.isEdge) { // insert point into the graph by splitting its containing edge - const newA = this._graph.splitEdgeAtFraction(this._searcher.node, this._searcher.edgeFraction!); + const edgeFraction = this._searcher.edgeFraction; + if (undefined === edgeFraction) + return false; + const newA = this._graph.splitEdgeAtFraction(this._searcher.node, edgeFraction); const newB = newA.vertexPredecessor; this.updateZAroundVertex(newA, point, InsertedVertexZOptions.Replace); // always replace this.retriangulateFromBaseVertex(newA); @@ -290,7 +297,11 @@ export class InsertAndRetriangulateContext { } else if (movingPosition.isFace) { const lastBefore = HalfEdgePositionDetail.create(); const firstAfter = HalfEdgePositionDetail.create(); - const rc = psc.reAimAroundFace(movingPosition.node!, ray, ray.a!, lastBefore, firstAfter); + const node = movingPosition.node; + const a = ray.a; + if (!node || undefined === a) + return false; + const rc = psc.reAimAroundFace(node, ray, a, lastBefore, firstAfter); // reAimAroundFace returns lots of cases in `lastBefore` switch (rc) { case RayClassification.NoHits: { @@ -330,11 +341,17 @@ export class InsertAndRetriangulateContext { } } } else if (movingPosition.isEdge) { - psc.reAimFromEdge(movingPosition, ray, ray.a!); + const a = ray.a; + if (undefined === a) + return false; + psc.reAimFromEdge(movingPosition, ray, a); if (movingPosition.isUnclassified) break; } else if (movingPosition.isVertex) { - psc.reAimFromVertex(movingPosition, ray, ray.a!); + const a = ray.a; + if (undefined === a) + return false; + psc.reAimFromVertex(movingPosition, ray, a); if (movingPosition.isUnclassified) break; } diff --git a/core/geometry/src/topology/RegularizeFace.ts b/core/geometry/src/topology/RegularizeFace.ts index 0b89f9980ceb..0c20e01b56bb 100644 --- a/core/geometry/src/topology/RegularizeFace.ts +++ b/core/geometry/src/topology/RegularizeFace.ts @@ -234,8 +234,8 @@ export class RegularizationContext { private downwardConnectionFromBottomPeak(node: HalfEdge): HalfEdge | undefined { let connectTo; const upFunction = (a: HalfEdge, b: HalfEdge) => HalfEdgeGraphOps.compareNodesYXUp(a, b); - const upEdgeBase = this.findTopVisibleEdge(node, this.upEdges, 1.0)!; - const downEdgeBase = this.findTopVisibleEdge(node, this.downEdges, -1.0)!; + const upEdgeBase = this.findTopVisibleEdge(node, this.upEdges, 1.0); + const downEdgeBase = this.findTopVisibleEdge(node, this.downEdges, -1.0); connectTo = this.updateMaxNode(connectTo, upEdgeBase, upFunction); if (downEdgeBase) connectTo = this.updateMaxNode(connectTo, downEdgeBase.faceSuccessor, upFunction); diff --git a/core/geometry/src/topology/Triangulation.ts b/core/geometry/src/topology/Triangulation.ts index 1b6a45be5138..93f1b61a0eab 100644 --- a/core/geometry/src/topology/Triangulation.ts +++ b/core/geometry/src/topology/Triangulation.ts @@ -118,10 +118,13 @@ export class Triangulator { /** * * Visit each node of the graph array * * If a flip would be possible, test the results of flipping using circumcircle condition - * * If revealed to be an improvement, conduct the flip, mark involved nodes as unvisited, and repeat until all nodes are visited + * * If revealed to be an improvement, conduct the flip, mark involved nodes as unvisited, and repeat until all nodes are visited. + * * @returns number of edges flipped. */ public static flipTriangles(graph: HalfEdgeGraph): number { - const edgeSet = MarkedEdgeSet.create(graph)!; + const edgeSet = MarkedEdgeSet.create(graph); + if (!edgeSet) + return 0; for (const node of graph.allHalfEdges) edgeSet.addToSet(node); const numFlip = this.flipTrianglesInEdgeSet(graph, edgeSet); @@ -1090,7 +1093,9 @@ class AssembleXYZXYZChains extends PointStreamXYZXYZHandlerBase { this._baseNode = this._nodeC; this._nodeB = this._baseNode.faceSuccessor; } else { - HalfEdge.pinch(this._nodeB!, this._nodeC); + if (!this._nodeB) + return; + HalfEdge.pinch(this._nodeB, this._nodeC); this._nodeB = this._nodeC.faceSuccessor; } }