@math.gl/geospatial 4.2.0-alpha.3 → 4.2.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,19 +6,15 @@
6
6
  // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md
7
7
 
8
8
  import {Vector2, Vector3, Matrix4} from '@math.gl/core';
9
- import {Plane, Ray} from '@math.gl/culling';
9
+ import {Plane} from '@math.gl/culling';
10
10
  import {Ellipsoid} from './ellipsoid';
11
11
 
12
12
  const scratchOrigin = new Vector3();
13
13
  const scratchCart3 = new Vector3();
14
14
  const scratchEastNorthUp = new Matrix4();
15
- const scratchPlane = new Plane();
16
-
17
- const scratchProjectPointOntoPlaneRay = new Ray();
18
15
  const scratchProjectPointOntoPlaneCartesian3 = new Vector3();
19
- const scratchDirection = new Vector3();
20
16
 
21
- /** A plane tangent to the WGS84 ellipsoid at the provided origin */
17
+ /** A two-dimensional east-north plane tangent to an ellipsoid. */
22
18
  export class EllipsoidTangentPlane {
23
19
  private _origin: Vector3;
24
20
  private _xAxis: Vector3;
@@ -27,45 +23,38 @@ export class EllipsoidTangentPlane {
27
23
 
28
24
  /**
29
25
  * Creates a new plane tangent to the WGS84 ellipsoid at the provided origin.
30
- * If origin is not on the surface of the ellipsoid, it's surface projection will be used.
26
+ * If origin is not on the surface of the ellipsoid, its surface projection will be used.
31
27
  *
32
- * @param {Cartesian3} origin The point on the surface of the ellipsoid where the tangent plane touches.
28
+ * @param origin A nonzero Cartesian point on or near the ellipsoid.
29
+ * @param ellipsoid The ellipsoid to which the origin belongs. Defaults to WGS84.
33
30
  */
34
- constructor(origin: number[]) {
35
- origin = Ellipsoid.WGS84.scaleToGeodeticSurface(origin, scratchOrigin);
31
+ constructor(origin: number[], ellipsoid: Ellipsoid = Ellipsoid.WGS84) {
32
+ const originOnSurface = ellipsoid.scaleToGeodeticSurface(origin, scratchOrigin);
36
33
 
37
- const eastNorthUp = Ellipsoid.WGS84.eastNorthUpToFixedFrame(origin, scratchEastNorthUp);
34
+ const eastNorthUp = ellipsoid.eastNorthUpToFixedFrame(originOnSurface, scratchEastNorthUp);
38
35
 
39
- this._origin = origin as Vector3;
36
+ this._origin = new Vector3(originOnSurface);
40
37
  this._xAxis = new Vector3(scratchCart3.from(eastNorthUp.getColumn(0)));
41
38
  this._yAxis = new Vector3(scratchCart3.from(eastNorthUp.getColumn(1)));
42
39
  const normal = new Vector3(scratchCart3.from(eastNorthUp.getColumn(2)));
43
40
 
44
- this._plane = scratchPlane.fromPointNormal(origin, normal);
41
+ this._plane = new Plane().fromPointNormal(this._origin, normal);
45
42
  }
46
43
 
47
44
  /**
48
45
  * Computes the projection of the provided 3D position onto the 2D plane, along the plane normal.
49
46
  *
50
- * @param {Vector3} cartesian The point to project.
51
- * @param {Vector2} [result] The object onto which to store the result.
52
- * @returns {Vector2} The modified result parameter or a new Cartesian2 instance if none was provided.
47
+ * @param cartesian The WGS84 Cartesian point to project.
48
+ * @param result The optional object in which to store the local east-north coordinates.
49
+ * @returns The supplied result or a new Vector2.
53
50
  */
54
51
  projectPointToNearestOnPlane(cartesian: Vector3, result?: Vector2): Vector2 {
55
52
  if (!result) result = new Vector2();
56
53
 
57
- const plane = this._plane;
58
-
59
- const ray = scratchProjectPointOntoPlaneRay;
60
- scratchProjectPointOntoPlaneRay.origin = cartesian;
61
- scratchProjectPointOntoPlaneRay.direction = scratchDirection.copy(plane.normal);
62
-
63
- let intersectionPoint = plane.intersectWithRay(ray, scratchProjectPointOntoPlaneCartesian3);
64
-
65
- if (!intersectionPoint) {
66
- ray.direction = ray.direction.negate();
67
- intersectionPoint = plane.intersectWithRay(ray, scratchProjectPointOntoPlaneCartesian3);
68
- }
54
+ const intersectionPoint = this._plane.projectPointOntoPlane(
55
+ cartesian,
56
+ scratchProjectPointOntoPlaneCartesian3
57
+ );
69
58
 
70
59
  const v = intersectionPoint.subtract(this._origin);
71
60
  const x = this._xAxis.dot(v);
@@ -76,23 +65,23 @@ export class EllipsoidTangentPlane {
76
65
  return result;
77
66
  }
78
67
 
79
- get plane() {
68
+ get plane(): Plane {
80
69
  return this._plane;
81
70
  }
82
71
 
83
- get origin() {
72
+ get origin(): Vector3 {
84
73
  return this._origin;
85
74
  }
86
75
 
87
- get xAxis() {
76
+ get xAxis(): Vector3 {
88
77
  return this._xAxis;
89
78
  }
90
79
 
91
- get yAxis() {
80
+ get yAxis(): Vector3 {
92
81
  return this._yAxis;
93
82
  }
94
83
 
95
- get zAxis() {
84
+ get zAxis(): Vector3 {
96
85
  return this._plane.normal;
97
86
  }
98
87
  }
package/src/ellipsoid.ts CHANGED
@@ -89,7 +89,10 @@ export class Ellipsoid {
89
89
  return this.radii.toString();
90
90
  }
91
91
 
92
- /** Converts the provided cartographic to Cartesian representation. */
92
+ /** Converts the provided cartographic position to Cartesian representation.
93
+ * The position is supplied as [longitude, latitude, height], where longitude and latitude
94
+ * are in degrees and height is in meters above the ellipsoid.
95
+ */
93
96
  cartographicToCartesian(cartographic: number[], result: Vector3): Vector3;
94
97
  cartographicToCartesian(cartographic: number[], result?: number[]): number[];
95
98
 
@@ -111,8 +114,10 @@ export class Ellipsoid {
111
114
  return k.to(result);
112
115
  }
113
116
 
114
- /** Converts the provided cartesian to cartographic (lng/lat/z) representation.
115
- * The cartesian is undefined at the center of the ellipsoid. */
117
+ /** Converts the provided Cartesian position to [longitude, latitude, height],
118
+ * where longitude and latitude are in degrees and height is in meters above the ellipsoid.
119
+ * The cartographic position is undefined at the center of the ellipsoid.
120
+ */
116
121
  cartesianToCartographic(cartesian: Readonly<NumericArray>, result: Vector3): Vector3;
117
122
  cartesianToCartographic(cartesian: Readonly<NumericArray>, result?: number[]): number[];
118
123
 
@@ -155,7 +160,7 @@ export class Ellipsoid {
155
160
  origin: Readonly<NumericArray>,
156
161
  result?: Matrix4
157
162
  ): Matrix4;
158
- localFrameToFixedFrame<Matrix4T>(
163
+ localFrameToFixedFrame<_Matrix4T>(
159
164
  firstAxis: AxisDirection,
160
165
  secondAxis: AxisDirection,
161
166
  thirdAxis: AxisDirection,
@@ -183,7 +188,9 @@ export class Ellipsoid {
183
188
  return scratchVector.from(cartesian).normalize().to(result);
184
189
  }
185
190
 
186
- /** Computes the normal of the plane tangent to the surface of the ellipsoid at provided position. */
191
+ /** Computes the normal of the plane tangent to the surface of the ellipsoid at the provided
192
+ * [longitude, latitude, height], where longitude and latitude are in degrees.
193
+ */
187
194
  geodeticSurfaceNormalCartographic<NumArray>(
188
195
  cartographic: Readonly<NumericArray>,
189
196
  result: NumArray
package/src/index.ts CHANGED
@@ -5,4 +5,6 @@
5
5
  export {Ellipsoid} from './ellipsoid';
6
6
  export {EllipsoidTangentPlane} from './ellipsoid-tangent-plane';
7
7
  export {LngLatRectangle} from './lng-lat-rectangle';
8
+ export {makeOBBFromRegion} from './make-obb-from-region';
9
+ export type {GeodeticRegion, MakeOBBFromRegionOptions} from './make-obb-from-region';
8
10
  export {isWGS84} from './type-utils';
@@ -55,7 +55,7 @@ export class LngLatRectangle {
55
55
  return result;
56
56
  }
57
57
 
58
- get width() {
58
+ get width(): number {
59
59
  let east = this.east;
60
60
  const west = this.west;
61
61
 
@@ -5,7 +5,7 @@
5
5
  // This file is derived from the Cesium math library under Apache 2 license
6
6
  // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md
7
7
 
8
- import {Vector2, Vector3, degrees, _MathUtils} from '@math.gl/core';
8
+ import {Matrix3, Matrix4, Vector2, Vector3, degrees, radians, _MathUtils} from '@math.gl/core';
9
9
  import {OrientedBoundingBox, Plane} from '@math.gl/culling';
10
10
  // @ts-ignore
11
11
  // eslint-disable-next-line import/no-unresolved
@@ -39,22 +39,89 @@ const scratchPlaneXAxis = new Vector3();
39
39
  const scratchHorizonCartesian = new Vector3();
40
40
  const scratchHorizonProjected = new Vector2();
41
41
  const scratchMaxY = new Vector3();
42
- const scratchMinY = new Vector3();
43
42
  const scratchZ = new Vector3();
44
43
 
45
44
  const VECTOR3_UNIT_X = new Vector3(1, 0, 0);
46
45
  const VECTOR3_UNIT_Z = new Vector3(0, 0, 1);
47
46
 
47
+ export type GeodeticRegion = readonly [
48
+ west: number,
49
+ south: number,
50
+ east: number,
51
+ north: number,
52
+ minimumHeight: number,
53
+ maximumHeight: number
54
+ ];
55
+
56
+ export type MakeOBBFromRegionOptions = {
57
+ /** Longitude and latitude units. Defaults to radians, as in 3D Tiles regions. */
58
+ units?: 'radians' | 'degrees';
59
+ /** Affine transform from ellipsoid-fixed coordinates into the caller's world space. */
60
+ transform?: Matrix4;
61
+ };
62
+
48
63
  /**
49
- * Computes an OrientedBoundingBox that bounds a region on the surface of the WGS84 ellipsoid.
50
- * There are no guarantees about the orientation of the bounding box.
64
+ * Creates a conservative oriented bounding box for a geodetic region.
51
65
  *
52
- * @param {number[]} region The cartographic region ([west, south, east, north, minimum height, maximum height])
53
- * on the surface of the ellipsoid.
54
- * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided.
66
+ * Longitudes and latitudes are radians by default. An east longitude smaller than west
67
+ * denotes the directed eastward interval crossing the antimeridian; equal longitudes denote
68
+ * zero width unless the original endpoints differ by a full turn.
69
+ * Heights use the ellipsoid's linear unit. Inputs and the supplied ellipsoid and transform are
70
+ * never mutated.
55
71
  */
56
72
  // eslint-disable-next-line max-statements
57
- export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
73
+ export function makeOBBFromRegion(
74
+ region: readonly number[],
75
+ ellipsoid: Ellipsoid = Ellipsoid.WGS84,
76
+ options: MakeOBBFromRegionOptions = {}
77
+ ): OrientedBoundingBox {
78
+ if (!region || region.length !== 6 || region.some(value => !Number.isFinite(value))) {
79
+ throw new Error('makeOBBFromRegion: region must contain exactly six finite numbers');
80
+ }
81
+ const unitScale = options.units === 'degrees' ? radians(1) : 1;
82
+ const [rawWest, rawSouth, rawEast, rawNorth, minimumHeight, maximumHeight] = region;
83
+ if (minimumHeight > maximumHeight) {
84
+ throw new Error('makeOBBFromRegion: minimumHeight must not exceed maximumHeight');
85
+ }
86
+ const south = rawSouth * unitScale;
87
+ const north = rawNorth * unitScale;
88
+ const west = normalizeLongitude(rawWest * unitScale);
89
+ let east = normalizeLongitude(rawEast * unitScale);
90
+ // Preserve a complete directed turn, which would otherwise collapse when both endpoints
91
+ // are normalized to the same longitude. Reversed boundaries retain Cesium/3D Tiles semantics.
92
+ const directedSpan = rawEast * unitScale - rawWest * unitScale;
93
+ if (directedSpan >= _MathUtils.TWO_PI - 1e-12) {
94
+ east = west + _MathUtils.TWO_PI;
95
+ }
96
+ const latitudeTolerance = 1e-12;
97
+ if (
98
+ south > north ||
99
+ south < -_MathUtils.PI_OVER_TWO - latitudeTolerance ||
100
+ north > _MathUtils.PI_OVER_TWO + latitudeTolerance
101
+ ) {
102
+ throw new Error('makeOBBFromRegion: latitude must be within [-π/2, π/2] and south ≤ north');
103
+ }
104
+ // Avoid passing a tiny out-of-range value to the ellipsoid conversion at a boundary.
105
+ const clampedSouth = Math.max(-_MathUtils.PI_OVER_TWO, Math.min(_MathUtils.PI_OVER_TWO, south));
106
+ const clampedNorth = Math.max(-_MathUtils.PI_OVER_TWO, Math.min(_MathUtils.PI_OVER_TWO, north));
107
+ return makeOBBFromNormalizedRegion(
108
+ [west, clampedSouth, east, clampedNorth, minimumHeight, maximumHeight],
109
+ ellipsoid,
110
+ options.transform
111
+ );
112
+ }
113
+
114
+ function normalizeLongitude(value: number): number {
115
+ const normalized =
116
+ ((((value + Math.PI) % _MathUtils.TWO_PI) + _MathUtils.TWO_PI) % _MathUtils.TWO_PI) - Math.PI;
117
+ return normalized === -Math.PI && value > 0 ? Math.PI : normalized;
118
+ }
119
+
120
+ function makeOBBFromNormalizedRegion(
121
+ region: GeodeticRegion,
122
+ ellipsoid: Ellipsoid,
123
+ transform?: Matrix4
124
+ ): OrientedBoundingBox {
58
125
  const obb = new OrientedBoundingBox();
59
126
 
60
127
  const [west, south, east, north, minimumHeight, maximumHeight] = region;
@@ -84,8 +151,8 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
84
151
  if (rectangle.width <= _MathUtils.PI) {
85
152
  const westDeg = degrees(west);
86
153
 
87
- const tangentPoint = Ellipsoid.WGS84.cartographicToCartesian(tangentPointCartographic);
88
- const ellipsoidTangentPlane = new EllipsoidTangentPlane(tangentPoint);
154
+ const tangentPoint = ellipsoid.cartographicToCartesian(tangentPointCartographic);
155
+ const ellipsoidTangentPlane = new EllipsoidTangentPlane(tangentPoint, ellipsoid);
89
156
 
90
157
  const latCenter = southDeg < 0.0 && northDeg > 0.0 ? 0.0 : tangentPointCartographic.y;
91
158
 
@@ -115,23 +182,23 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
115
182
  maximumHeight
116
183
  ]);
117
184
 
118
- const perimeterCartesianNC = Ellipsoid.WGS84.cartographicToCartesian(
185
+ const perimeterCartesianNC = ellipsoid.cartographicToCartesian(
119
186
  perimeterCartographicNC,
120
187
  scratchPerimeterCartesianNC
121
188
  );
122
- let perimeterCartesianNW = Ellipsoid.WGS84.cartographicToCartesian(
189
+ let perimeterCartesianNW = ellipsoid.cartographicToCartesian(
123
190
  perimeterCartographicNW,
124
191
  scratchPerimeterCartesianNW
125
192
  );
126
- const perimeterCartesianCW = Ellipsoid.WGS84.cartographicToCartesian(
193
+ const perimeterCartesianCW = ellipsoid.cartographicToCartesian(
127
194
  perimeterCartographicCW,
128
195
  scratchPerimeterCartesianCW
129
196
  );
130
- let perimeterCartesianSW = Ellipsoid.WGS84.cartographicToCartesian(
197
+ let perimeterCartesianSW = ellipsoid.cartographicToCartesian(
131
198
  perimeterCartographicSW,
132
199
  scratchPerimeterCartesianSW
133
200
  );
134
- const perimeterCartesianSC = Ellipsoid.WGS84.cartographicToCartesian(
201
+ const perimeterCartesianSC = ellipsoid.cartographicToCartesian(
135
202
  perimeterCartographicSC,
136
203
  scratchPerimeterCartesianSC
137
204
  );
@@ -164,11 +231,11 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
164
231
  minY = Math.min(perimeterProjectedSW.y, perimeterProjectedSC.y);
165
232
 
166
233
  perimeterCartographicNW.z = perimeterCartographicSW.z = minimumHeight;
167
- perimeterCartesianNW = Ellipsoid.WGS84.cartographicToCartesian(
234
+ perimeterCartesianNW = ellipsoid.cartographicToCartesian(
168
235
  perimeterCartographicNW,
169
236
  scratchPerimeterCartesianNW
170
237
  );
171
- perimeterCartesianSW = Ellipsoid.WGS84.cartographicToCartesian(
238
+ perimeterCartesianSW = ellipsoid.cartographicToCartesian(
172
239
  perimeterCartographicSW,
173
240
  scratchPerimeterCartesianSW
174
241
  );
@@ -180,18 +247,21 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
180
247
  );
181
248
  maxZ = maximumHeight;
182
249
 
183
- return fromPlaneExtents(
184
- ellipsoidTangentPlane.origin,
185
- ellipsoidTangentPlane.xAxis,
186
- ellipsoidTangentPlane.yAxis,
187
- ellipsoidTangentPlane.zAxis,
188
- minX,
189
- maxX,
190
- minY,
191
- maxY,
192
- minZ,
193
- maxZ,
194
- obb
250
+ return applyTransform(
251
+ fromPlaneExtents(
252
+ ellipsoidTangentPlane.origin,
253
+ ellipsoidTangentPlane.xAxis,
254
+ ellipsoidTangentPlane.yAxis,
255
+ ellipsoidTangentPlane.zAxis,
256
+ minX,
257
+ maxX,
258
+ minY,
259
+ maxY,
260
+ minZ,
261
+ maxZ,
262
+ obb
263
+ ),
264
+ transform
195
265
  );
196
266
  }
197
267
 
@@ -205,7 +275,7 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
205
275
  ? northDeg
206
276
  : 0.0;
207
277
 
208
- const planeOrigin = Ellipsoid.WGS84.cartographicToCartesian(
278
+ const planeOrigin = ellipsoid.cartographicToCartesian(
209
279
  [lonCenterDeg, latitudeNearestToEquator, maximumHeight],
210
280
  scratchPlaneOrigin
211
281
  );
@@ -218,7 +288,7 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
218
288
  const planeXAxis = scratchPlaneXAxis.copy(planeNormal).cross(planeYAxis);
219
289
  plane = scratchPlane.fromPointNormal(planeOrigin, planeNormal);
220
290
 
221
- const horizonCartesian = Ellipsoid.WGS84.cartographicToCartesian(
291
+ const horizonCartesian = ellipsoid.cartographicToCartesian(
222
292
  [degrees(lonCenter + _MathUtils.PI_OVER_TWO), latitudeNearestToEquator, maximumHeight],
223
293
  scratchHorizonCartesian
224
294
  );
@@ -229,16 +299,12 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
229
299
  maxX = projectedPoint.dot(planeXAxis);
230
300
  minX = -maxX;
231
301
 
232
- maxY = Ellipsoid.WGS84.cartographicToCartesian(
233
- [0.0, northDeg, fullyBelowEquator ? minimumHeight : maximumHeight],
234
- scratchMaxY
235
- ).z;
236
- minY = Ellipsoid.WGS84.cartographicToCartesian(
237
- [0.0, southDeg, fullyAboveEquator ? minimumHeight : maximumHeight],
238
- scratchMinY
239
- ).z;
302
+ const northHeight = fullyBelowEquator ? minimumHeight : maximumHeight;
303
+ const southHeight = fullyAboveEquator ? minimumHeight : maximumHeight;
304
+ maxY = latitudeZExtent(ellipsoid, northDeg, northHeight, true);
305
+ minY = latitudeZExtent(ellipsoid, southDeg, southHeight, false);
240
306
 
241
- const farZ = Ellipsoid.WGS84.cartographicToCartesian(
307
+ const farZ = ellipsoid.cartographicToCartesian(
242
308
  [eastDeg, latitudeNearestToEquator, maximumHeight],
243
309
  scratchZ
244
310
  );
@@ -246,24 +312,65 @@ export function makeOBBfromRegion(region: number[]): OrientedBoundingBox {
246
312
  minZ = plane.getPointDistance(farZ);
247
313
  maxZ = 0.0;
248
314
 
249
- return fromPlaneExtents(
250
- planeOrigin,
251
- planeXAxis,
252
- planeYAxis,
253
- planeNormal,
254
- minX,
255
- maxX,
256
- minY,
257
- maxY,
258
- minZ,
259
- maxZ,
260
- obb
315
+ return applyTransform(
316
+ fromPlaneExtents(
317
+ planeOrigin,
318
+ planeXAxis,
319
+ planeYAxis,
320
+ planeNormal,
321
+ minX,
322
+ maxX,
323
+ minY,
324
+ maxY,
325
+ minZ,
326
+ maxZ,
327
+ obb
328
+ ),
329
+ transform
261
330
  );
262
331
  }
263
332
 
264
- const scratchScale = new Vector3();
333
+ function applyTransform(obb: OrientedBoundingBox, transform?: Matrix4): OrientedBoundingBox {
334
+ if (!transform) return obb;
335
+ // A general affine transform turns an OBB into a parallelepiped. Refit its eight
336
+ // corners to an axis-aligned OBB so culling methods that assume orthogonal axes remain safe.
337
+ const axes = [0, 1, 2].map(column => obb.halfAxes.getColumn(column, new Vector3()));
338
+ const center = transform.transformAsPoint(obb.center) as number[];
339
+ const minimum = [Infinity, Infinity, Infinity];
340
+ const maximum = [-Infinity, -Infinity, -Infinity];
341
+ for (let mask = 0; mask < 8; mask++) {
342
+ const corner = new Vector3(center);
343
+ for (let axis = 0; axis < 3; axis++) {
344
+ const transformedAxis = transform.transformAsVector(axes[axis]) as number[];
345
+ corner.add(new Vector3(transformedAxis).scale((mask & (1 << axis)) === 0 ? -1 : 1));
346
+ }
347
+ for (let component = 0; component < 3; component++) {
348
+ minimum[component] = Math.min(minimum[component], corner[component]);
349
+ maximum[component] = Math.max(maximum[component], corner[component]);
350
+ }
351
+ }
352
+ obb.center.copy(minimum).add(maximum).scale(0.5);
353
+ const halfSize = new Vector3(maximum).subtract(minimum).scale(0.5);
354
+ obb.halfAxes = new Matrix3().set(halfSize.x, 0, 0, 0, halfSize.y, 0, 0, 0, halfSize.z);
355
+ return obb;
356
+ }
265
357
 
266
- /** helper function for OrientedBoundingBox.fromRegion() */
358
+ function latitudeZExtent(
359
+ ellipsoid: Ellipsoid,
360
+ latitude: number,
361
+ height: number,
362
+ maximum: boolean
363
+ ): number {
364
+ let extent = maximum ? -Infinity : Infinity;
365
+ // A triaxial ellipsoid's fixed-latitude extrema occur at a principal longitude.
366
+ for (const longitude of [0, 90, 180, 270]) {
367
+ const z = ellipsoid.cartographicToCartesian([longitude, latitude, height], scratchMaxY).z;
368
+ extent = maximum ? Math.max(extent, z) : Math.min(extent, z);
369
+ }
370
+ return extent;
371
+ }
372
+
373
+ /** Helper function for makeOBBFromRegion(). */
267
374
  // eslint-disable-next-line max-params
268
375
  function fromPlaneExtents(
269
376
  planeOrigin: Vector3,
@@ -284,20 +391,24 @@ function fromPlaneExtents(
284
391
  halfAxes.setColumn(1, planeYAxis);
285
392
  halfAxes.setColumn(2, planeZAxis);
286
393
 
287
- let centerOffset = scratchOffset;
288
- centerOffset.x = (minimumX + maximumX) / 2.0;
289
- centerOffset.y = (minimumY + maximumY) / 2.0;
290
- centerOffset.z = (minimumZ + maximumZ) / 2.0;
291
- centerOffset = halfAxes.multiplyByVector(centerOffset, scratchOffset);
292
-
293
- const scale = scratchScale;
294
- scale.x = (maximumX - minimumX) / 2.0;
295
- scale.y = (maximumY - minimumY) / 2.0;
296
- scale.z = (maximumZ - minimumZ) / 2.0;
297
-
298
- planeOrigin = center.copy(planeOrigin);
299
- result.center = planeOrigin.add(centerOffset);
300
- result.halfAxes = halfAxes.multiplyByScale(scale, halfAxes);
394
+ const centerOffset = scratchOffset
395
+ .set((minimumX + maximumX) / 2.0, (minimumY + maximumY) / 2.0, (minimumZ + maximumZ) / 2.0)
396
+ .transformByMatrix3(halfAxes);
397
+
398
+ const scaleX = (maximumX - minimumX) / 2.0;
399
+ const scaleY = (maximumY - minimumY) / 2.0;
400
+ const scaleZ = (maximumZ - minimumZ) / 2.0;
401
+ halfAxes[0] *= scaleX;
402
+ halfAxes[1] *= scaleX;
403
+ halfAxes[2] *= scaleX;
404
+ halfAxes[3] *= scaleY;
405
+ halfAxes[4] *= scaleY;
406
+ halfAxes[5] *= scaleY;
407
+ halfAxes[6] *= scaleZ;
408
+ halfAxes[7] *= scaleZ;
409
+ halfAxes[8] *= scaleZ;
410
+
411
+ center.copy(planeOrigin).add(centerOffset);
301
412
 
302
413
  return result;
303
414
  }