/
GeometryQuery.ts
140 lines (137 loc) · 6.06 KB
/
GeometryQuery.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Curve
*/
import { BSpline2dNd } from "../bspline/BSplineSurface";
import { GeometryHandler } from "../geometry3d/GeometryHandler";
import { Range3d } from "../geometry3d/Range";
import { Transform } from "../geometry3d/Transform";
import { Polyface } from "../polyface/Polyface";
import { SolidPrimitive } from "../solid/SolidPrimitive";
import { CoordinateXYZ } from "./CoordinateXYZ";
import { CurveCollection } from "./CurveCollection";
import { CurvePrimitive } from "./CurvePrimitive";
import { PointString3d } from "./PointString3d";
/**
* Describes the category of a [[GeometryQuery]], enabling type-switching like:
* ```ts
* function processGeometryQuery(q: GeometryQuery): void {
* if ("solid" === q.geometryCategory)
* alert("Solid type = " + q.solidPrimitiveType); // compiler knows q is an instance of SolidPrimitive
* // ...etc...
* ```
*
* Each string maps to a particular subclass of [[GeometryQuery]]:
* - "polyface" => [[Polyface]]
* - "curvePrimitive" => [[CurvePrimitive]]
* - "curveCollection" => [[CurveCollection]]
* - "solid" => [[SolidPrimitive]]
* - "point" => [[CoordinateXYZ]]
* - "pointCollection" => [[PointString3d]]
* - "bsurf" => [[BSpline2dNd]] (which is an intermediate class shared by [[BSplineSurface3d]] and [[BSplineSurface3dH]])
*
* @see [[AnyGeometryQuery]]
* @public
*/
export type GeometryQueryCategory = "polyface" | "curvePrimitive" | "curveCollection" | "solid" | "point" | "pointCollection" | "bsurf";
/**
* Union type for subclasses of [[GeometryQuery]]. Specific subclasses can be discriminated at compile- or run-time
* using [[GeometryQuery.geometryCategory]].
* @public
*/
export type AnyGeometryQuery = Polyface | CurvePrimitive | CurveCollection | SolidPrimitive | CoordinateXYZ | PointString3d | BSpline2dNd;
/**
* Queries to be supported by Curve, Surface, and Solid objects.
* * `GeometryQuery` is an abstract base class with (abstract) methods for querying curve, solid primitive, mesh,
* and bspline surfaces.
* @public
*/
export abstract class GeometryQuery {
/** Type discriminator. */
public abstract readonly geometryCategory: GeometryQueryCategory;
/** Return the range of the entire GeometryQuery tree. */
public range(transform?: Transform, result?: Range3d): Range3d {
if (result)
result.setNull();
const range = result ? result : Range3d.createNull();
this.extendRange(range, transform);
return range;
}
/** Extend `rangeToExtend` by the range of this geometry multiplied by the `transform`. */
public abstract extendRange(rangeToExtend: Range3d, transform?: Transform): void;
/**
* Attempt to transform in place.
* * LineSegment3d, Arc3d, LineString3d, BsplineCurve3d always succeed.
* * Some geometry types may fail if scaling is non-uniform.
*/
public abstract tryTransformInPlace(transform: Transform): boolean;
/** Try to move the geometry by dx,dy,dz. */
public tryTranslateInPlace(dx: number, dy: number = 0.0, dz: number = 0.0): boolean {
return this.tryTransformInPlace(Transform.createTranslationXYZ(dx, dy, dz));
}
/** Return a transformed clone. */
public abstract cloneTransformed(transform: Transform): GeometryQuery | undefined;
/** Return a clone */
public abstract clone(): GeometryQuery | undefined;
/**
* Return GeometryQuery children for recursive queries.
* * leaf classes do not need to implement.
*/
public get children(): GeometryQuery[] | undefined {
return undefined;
}
/** Test `if (other instanceof this.Type)`. REQUIRED IN ALL CONCRETE CLASSES. */
public abstract isSameGeometryClass(other: GeometryQuery): boolean;
/**
* Test for exact structure and nearly identical geometry.
* * Leaf classes must implement.
* * Base class implementation recurses through children.
* * Base implementation is complete for classes with children and no properties.
* * Classes with both children and properties must implement for properties, call super for children.
*/
public isAlmostEqual(other: GeometryQuery): boolean {
if (this.isSameGeometryClass(other)) {
const childrenA = this.children;
const childrenB = other.children;
if (childrenA && childrenB) {
if (childrenA.length !== childrenB.length)
return false;
for (let i = 0; i < childrenA.length; i++) {
if (!childrenA[i].isAlmostEqual(childrenB[i]))
return false;
}
return true;
} else if (childrenA || childrenB) { // CurveCollections start with empty arrays for children so these null pointer cases are never reached.
return false; // plainly different
} else {
return true; // both children null; call it equal
}
}
return false;
}
/**
* Apply instance method [[isAlmostEqual]] if both are defined.
* * Both undefined returns true.
* * Single defined returns false.
*/
public static areAlmostEqual(a: GeometryQuery | undefined, b: GeometryQuery | undefined): boolean {
if (a instanceof GeometryQuery && b instanceof GeometryQuery)
return a.isAlmostEqual(b);
if ((a === undefined) && (b === undefined))
return true;
return false;
}
/**
* Double Dispatch call pattern.
* * User code implements a `GeometryHandler` with specialized methods to handle `LineSegment3d`, `Arc3d`, etc as
* relevant to its use case.
* * Each such `GeometryQuery` class implements this method as a one-line method containing the appropriate call
* such as `handler.handleLineSegment3d())`
* * This allows each type-specific method to be called without a switch or `instanceof` test.
* @param handler handler to be called by the particular geometry class
*/
public abstract dispatchToGeometryHandler(handler: GeometryHandler): any;
}