@bitruvius/geo-core 0.3.1
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.
- package/LICENSE +167 -0
- package/README.md +133 -0
- package/dist/contracts/cesium-point-decoder.d.ts +34 -0
- package/dist/contracts/decoder.d.ts +191 -0
- package/dist/contracts/feature-metadata.d.ts +161 -0
- package/dist/contracts/gltf-splat-decoder.d.ts +53 -0
- package/dist/contracts/i3s-mesh-decoder.d.ts +80 -0
- package/dist/contracts/i3s-point-decoder.d.ts +90 -0
- package/dist/contracts/mesh-codecs.d.ts +135 -0
- package/dist/contracts/mesh.d.ts +304 -0
- package/dist/contracts/renderer.d.ts +34 -0
- package/dist/contracts/viewer-adapter.d.ts +68 -0
- package/dist/dequant/formulas.d.ts +93 -0
- package/dist/dequant/stray-cull.d.ts +29 -0
- package/dist/dequant/tile-bounds.d.ts +109 -0
- package/dist/geo/ecef.d.ts +70 -0
- package/dist/geo/hard-edges.d.ts +71 -0
- package/dist/geo/mat4.d.ts +115 -0
- package/dist/geo/measure.d.ts +134 -0
- package/dist/geo/mercator.d.ts +95 -0
- package/dist/geo/model-placement.d.ts +141 -0
- package/dist/geo/pack-gltf-points-enu.d.ts +41 -0
- package/dist/geo/pack-mesh-edges.d.ts +16 -0
- package/dist/geo/pack-mesh-enu.d.ts +46 -0
- package/dist/geo/pack-point-ecef.d.ts +42 -0
- package/dist/geo/pack-point-enu.d.ts +126 -0
- package/dist/geo/plane.d.ts +38 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +1 -0
- package/dist/style/expression.d.ts +122 -0
- package/package.json +51 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WGS84 geocentric (ECEF) ↔ geodetic, plus the ECEF→ENU rotation used to rebase
|
|
3
|
+
* 3D-Tiles content near a scene anchor before handing it to a web map. Pure math
|
|
4
|
+
* (no DOM/GL); composes in f64. Reuses {@link fromBasis}/{@link fromTranslation}/
|
|
5
|
+
* {@link multiply} from `./mat4`; pairs with `./mercator`'s `placementMatrix` to
|
|
6
|
+
* complete ECEF → ENU(meters) → mercator.
|
|
7
|
+
*/
|
|
8
|
+
import type { Mat4 } from './mat4';
|
|
9
|
+
/** A 3-component vector `[x, y, z]` (ECEF or ENU meters, per context). */
|
|
10
|
+
export type Vec3 = readonly [number, number, number];
|
|
11
|
+
/** A geodetic position on the WGS84 ellipsoid. */
|
|
12
|
+
export interface Geodetic {
|
|
13
|
+
/** Longitude in degrees. */ lng: number;
|
|
14
|
+
/** Latitude in degrees. */ lat: number;
|
|
15
|
+
/** Height in meters above the WGS84 ellipsoid. */ height: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Geodetic → WGS84 geocentric (ECEF) meters.
|
|
19
|
+
* @param lngDeg - Longitude in degrees.
|
|
20
|
+
* @param latDeg - Latitude in degrees.
|
|
21
|
+
* @param h - Height in meters above the WGS84 ellipsoid.
|
|
22
|
+
* @returns ECEF `[x, y, z]` in meters.
|
|
23
|
+
*/
|
|
24
|
+
export declare function geodeticToEcef(lngDeg: number, latDeg: number, h: number): Vec3;
|
|
25
|
+
/**
|
|
26
|
+
* WGS84 geocentric (ECEF) → geodetic, via the Bowring closed-form solution.
|
|
27
|
+
* @param x - ECEF X in meters.
|
|
28
|
+
* @param y - ECEF Y in meters.
|
|
29
|
+
* @param z - ECEF Z in meters.
|
|
30
|
+
* @returns {@link Geodetic} (lng/lat degrees, height meters above the ellipsoid).
|
|
31
|
+
*/
|
|
32
|
+
export declare function ecefToGeodetic(x: number, y: number, z: number): Geodetic;
|
|
33
|
+
/**
|
|
34
|
+
* Build the matrix that maps ECEF (meters) → local East-North-Up (meters) at `anchor`:
|
|
35
|
+
* `enu = R · (p − anchor)`, where R's rows are the East / North / Up basis vectors
|
|
36
|
+
* expressed in ECEF.
|
|
37
|
+
* @param anchor - The ENU origin in ECEF meters.
|
|
38
|
+
* @param lngDeg - Anchor longitude in degrees (orients the East/North axes).
|
|
39
|
+
* @param latDeg - Anchor latitude in degrees.
|
|
40
|
+
* @returns A column-major f64 {@link Mat4} (ECEF → ENU meters).
|
|
41
|
+
*/
|
|
42
|
+
export declare function enuFromEcefMatrix(anchor: Vec3, lngDeg: number, latDeg: number): Mat4;
|
|
43
|
+
/**
|
|
44
|
+
* glTF y-up → 3D-Tiles z-up (rotation +90° about X): (x,y,z) → (x,−z,y). 3D Tiles
|
|
45
|
+
* applies this to glTF content before the tile transform; Esri's glb carries an
|
|
46
|
+
* inverse node matrix that cancels it (content stored z-up).
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export declare const Y_UP_TO_Z_UP: Mat4;
|
|
50
|
+
/**
|
|
51
|
+
* Compose the matrix that takes glb-content vertices into the shared local ENU (meters)
|
|
52
|
+
* frame: `enuFromEcef · tileTransform · (yUpToZUp · nodeMatrix)`. A renderer then
|
|
53
|
+
* applies the mercator placement once to all content already in this frame.
|
|
54
|
+
* @param enuFromEcef - ECEF → scene ENU meters (from {@link enuFromEcefMatrix}).
|
|
55
|
+
* @param tileTransform - The cumulative tile transform (tile-local → ECEF).
|
|
56
|
+
* @param nodeMatrix - The glTF node's world matrix (glb root → primitive node).
|
|
57
|
+
* @param upAxis - Up-axis of the glb CONTENT, i.e. 3D Tiles' `asset.gltfUpAxis`. `'y'` — the
|
|
58
|
+
* spec default, and the default here — applies {@link Y_UP_TO_Z_UP}; `'z'` means the content
|
|
59
|
+
* is ALREADY z-up, so the flip is skipped.
|
|
60
|
+
*
|
|
61
|
+
* Applying the flip to z-up content rotates it 90° about X **inside its tile-local frame**,
|
|
62
|
+
* about that frame's origin rather than about the geometry. For a tile whose local
|
|
63
|
+
* coordinates sit tens of km from that origin — normal for a `box` volume with a per-tile
|
|
64
|
+
* `transform` — the content is thrown tens of km sideways and tens of km below the
|
|
65
|
+
* ellipsoid, so it draws for a frame and is then buried. Publishers declaring `Z` are a
|
|
66
|
+
* minority (Esri's `3DTilesServer`), which is why this went unnoticed: the default-`Y`
|
|
67
|
+
* datasets were all correct.
|
|
68
|
+
* @returns A column-major f64 {@link Mat4} (glb-local → scene ENU meters).
|
|
69
|
+
*/
|
|
70
|
+
export declare function tileToEnu(enuFromEcef: Mat4, tileTransform: Mat4, nodeMatrix: Mat4, upAxis?: 'y' | 'z'): Mat4;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract a mesh's **hard edges** — the lines a person would draw if asked to sketch the model.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* No I3S or 3D Tiles service ships edge geometry; a building arrives as a triangle soup and the
|
|
6
|
+
* edges have to be derived from it. Two kinds count:
|
|
7
|
+
*
|
|
8
|
+
* - **Boundary** edges, used by exactly one triangle. These are the silhouette of an open surface.
|
|
9
|
+
* - **Crease** edges, where the two triangles sharing the edge meet at more than `creaseAngleDeg`.
|
|
10
|
+
* These are the corners of a box, the eaves of a roof, the sill of a window.
|
|
11
|
+
*
|
|
12
|
+
* Everything else is tessellation — the diagonal splitting a flat wall into two triangles — and
|
|
13
|
+
* drawing it is the single most obvious way edge rendering looks wrong: the model reads as a
|
|
14
|
+
* wireframe rather than as a building.
|
|
15
|
+
*
|
|
16
|
+
* Two details matter more than they look:
|
|
17
|
+
*
|
|
18
|
+
* **Welding.** Uncompressed I3S geometry (and de-indexed Draco) repeats each position per triangle,
|
|
19
|
+
* so nothing shares vertices by index. Without welding by position, *every* edge looks like a
|
|
20
|
+
* boundary edge and the entire mesh draws as a wireframe. Positions are quantized to 0.1 mm and
|
|
21
|
+
* matched on that.
|
|
22
|
+
*
|
|
23
|
+
* **Welding stops at the feature.** Terraced houses share a coincident wall. Welded across
|
|
24
|
+
* features, that wall pairs up, reads as an interior edge and vanishes, taking the line between
|
|
25
|
+
* two adjacent buildings with it. So the feature id is part of the weld identity: each feature is
|
|
26
|
+
* outlined as if it were alone.
|
|
27
|
+
*
|
|
28
|
+
* Pure and allocation-conscious: this runs in the decode worker, once per tile, never per frame.
|
|
29
|
+
*/
|
|
30
|
+
/** What to extract edges from. */
|
|
31
|
+
export interface HardEdgeInput {
|
|
32
|
+
/** 3·N vertex positions. */
|
|
33
|
+
readonly positions: Float32Array;
|
|
34
|
+
/** Triangle indices; omit for a non-indexed soup (positions are read three at a time). */
|
|
35
|
+
readonly indices?: Uint16Array | Uint32Array;
|
|
36
|
+
/** Per-VERTEX feature ordinal. When present, welding never crosses features. */
|
|
37
|
+
readonly featureIds?: Uint32Array;
|
|
38
|
+
/** Dihedral angle above which a shared edge counts as a crease. Default 25°. */
|
|
39
|
+
readonly creaseAngleDeg?: number;
|
|
40
|
+
/** Cap on emitted edges. Default 400 000. */
|
|
41
|
+
readonly maxEdges?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Extracted edges as a flat line list. */
|
|
44
|
+
export interface HardEdges {
|
|
45
|
+
/** 6·count: `[x0,y0,z0, x1,y1,z1]` per edge, in the same frame as the input positions. */
|
|
46
|
+
readonly positions: Float32Array;
|
|
47
|
+
/** Per-EDGE feature ordinal; present iff the input carried `featureIds`. Lets a renderer hide
|
|
48
|
+
* an edge along with the feature it belongs to. */
|
|
49
|
+
readonly featureIds?: Uint32Array;
|
|
50
|
+
/**
|
|
51
|
+
* 2·count original VERTEX indices, one per endpoint.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* So a caller can gather any parallel per-vertex attribute the extractor knows nothing about.
|
|
55
|
+
* The one that matters today is the baked web-mercator offset: a planet-scale tile is placed
|
|
56
|
+
* from `mercatorOffset` rather than from ENU positions, so edges built only from ENU would
|
|
57
|
+
* float away from the buildings they outline.
|
|
58
|
+
*/
|
|
59
|
+
readonly endpoints: Uint32Array;
|
|
60
|
+
readonly count: number;
|
|
61
|
+
/** True when {@link HardEdgeInput.maxEdges} cut the result short. */
|
|
62
|
+
readonly truncated: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Derive the hard edges of a triangle mesh.
|
|
66
|
+
*
|
|
67
|
+
* @param input - The geometry and extraction thresholds ({@link HardEdgeInput}).
|
|
68
|
+
* @returns The line list, or `null` when there is nothing to draw (empty, degenerate, or under
|
|
69
|
+
* one triangle) — so a caller can skip allocating and uploading an empty buffer.
|
|
70
|
+
*/
|
|
71
|
+
export declare function extractHardEdges(input: HardEdgeInput): HardEdges | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal column-major 4x4 matrix math, following the standard WebGL /
|
|
3
|
+
* gl-matrix convention: storage is column-major (`m[col*4 + row]`),
|
|
4
|
+
* `multiply(a, b)` returns `a*b`, and matrices act on column vectors on
|
|
5
|
+
* the right (`gl_Position = P * V * M * v`).
|
|
6
|
+
*
|
|
7
|
+
* Backed by **Float64Array**: mercator world coordinates (~0.5) and
|
|
8
|
+
* meter-scale offsets (~1e-8) must coexist in the same matrix, which f32
|
|
9
|
+
* cannot resolve (R3). CPU-side composition (placement, view-matrix
|
|
10
|
+
* recovery, covariance transforms) stays f64; only the final GPU uniform
|
|
11
|
+
* is downcast via `toFloat32` at the WebGL boundary.
|
|
12
|
+
*
|
|
13
|
+
* Independently implemented from textbook linear algebra — no third-party
|
|
14
|
+
* renderer code.
|
|
15
|
+
*/
|
|
16
|
+
/** A column-major 4×4 matrix: a length-16 `Float64Array` (`m[col*4 + row]`). */
|
|
17
|
+
export type Mat4 = Float64Array;
|
|
18
|
+
/** A column-major 3×3 matrix: a length-9 `Float64Array` (`m[col*3 + row]`). */
|
|
19
|
+
export type Mat3 = Float64Array;
|
|
20
|
+
/** A 4-component vector `[x, y, z, w]`. */
|
|
21
|
+
export type Vec4 = readonly [number, number, number, number];
|
|
22
|
+
/** Downcast a CPU f64 matrix to a new `Float32Array` for a WebGL uniform.
|
|
23
|
+
* @param m - The f64 matrix. @returns A new length-16 `Float32Array`. */
|
|
24
|
+
export declare function toFloat32(m: Mat4): Float32Array;
|
|
25
|
+
/** Downcast into a caller-owned `Float32Array` (no allocation) — for per-frame uniform writes.
|
|
26
|
+
* @param m - The f64 source. @param out - The length-16 destination (written in place).
|
|
27
|
+
* @returns `out`. */
|
|
28
|
+
export declare function toFloat32Into(m: Mat4, out: Float32Array): Float32Array;
|
|
29
|
+
/** Copy any array-like (e.g. a viewer's matrix) into a new f64 {@link Mat4}.
|
|
30
|
+
* @param a - A length-16 column-major array-like. @returns A new {@link Mat4}. */
|
|
31
|
+
export declare function fromArrayLike(a: ArrayLike<number>): Mat4;
|
|
32
|
+
/** @returns A new 4×4 identity {@link Mat4}. */
|
|
33
|
+
export declare function identity(): Mat4;
|
|
34
|
+
/** @param a - The matrix to copy. @returns A new {@link Mat4} with the same values. */
|
|
35
|
+
export declare function clone(a: Mat4): Mat4;
|
|
36
|
+
/** Matrix product `out = a · b` (column-major; column vectors on the right).
|
|
37
|
+
* @param a - Left matrix. @param b - Right matrix.
|
|
38
|
+
* @param out - Optional destination (defaults to a new {@link Mat4}); may not alias `a`/`b`.
|
|
39
|
+
* @returns `out`. */
|
|
40
|
+
export declare function multiply(a: Mat4, b: Mat4, out?: Mat4): Mat4;
|
|
41
|
+
/** Transpose: `out = aᵀ`.
|
|
42
|
+
* @param a - The matrix. @param out - Optional destination. @returns `out`. */
|
|
43
|
+
export declare function transpose(a: Mat4, out?: Mat4): Mat4;
|
|
44
|
+
/** Extract the upper-left 3×3 (the linear part used for covariance/normals).
|
|
45
|
+
* @param a - The 4×4 matrix. @param out - Optional length-9 {@link Mat3} destination.
|
|
46
|
+
* @returns `out`. */
|
|
47
|
+
export declare function mat3FromMat4(a: Mat4, out?: Mat3): Mat3;
|
|
48
|
+
/** Transform a 4-vector: `out = m · v` (column vector).
|
|
49
|
+
* @param m - The matrix. @param v - The {@link Vec4}. @returns The transformed `[x,y,z,w]`. */
|
|
50
|
+
export declare function multiplyVec4(m: Mat4, v: Vec4): [number, number, number, number];
|
|
51
|
+
/** Build a translation matrix.
|
|
52
|
+
* @param x - X translation. @param y - Y translation. @param z - Z translation.
|
|
53
|
+
* @returns A new {@link Mat4} translating by `(x, y, z)`. */
|
|
54
|
+
export declare function fromTranslation(x: number, y: number, z: number): Mat4;
|
|
55
|
+
/** Build a (possibly non-uniform) scaling matrix.
|
|
56
|
+
* @param x - X scale. @param y - Y scale. @param z - Z scale.
|
|
57
|
+
* @returns A new {@link Mat4} scaling by `(x, y, z)`. */
|
|
58
|
+
export declare function fromScaling(x: number, y: number, z: number): Mat4;
|
|
59
|
+
/** Build a rotation about the +X axis (right-handed: counter-clockwise when +X
|
|
60
|
+
* points at the viewer; gl-matrix convention, column-major).
|
|
61
|
+
* @param rad - The rotation angle in radians.
|
|
62
|
+
* @param out - Optional destination (defaults to a new {@link Mat4}).
|
|
63
|
+
* @returns `out`. */
|
|
64
|
+
export declare function fromRotationX(rad: number, out?: Mat4): Mat4;
|
|
65
|
+
/** Build a rotation about the +Y axis (right-handed: counter-clockwise when +Y
|
|
66
|
+
* points at the viewer; gl-matrix convention, column-major).
|
|
67
|
+
* @param rad - The rotation angle in radians.
|
|
68
|
+
* @param out - Optional destination (defaults to a new {@link Mat4}).
|
|
69
|
+
* @returns `out`. */
|
|
70
|
+
export declare function fromRotationY(rad: number, out?: Mat4): Mat4;
|
|
71
|
+
/** Build a rotation about the +Z axis (right-handed: counter-clockwise when +Z
|
|
72
|
+
* points at the viewer; gl-matrix convention, column-major).
|
|
73
|
+
* @param rad - The rotation angle in radians.
|
|
74
|
+
* @param out - Optional destination (defaults to a new {@link Mat4}).
|
|
75
|
+
* @returns `out`. */
|
|
76
|
+
export declare function fromRotationZ(rad: number, out?: Mat4): Mat4;
|
|
77
|
+
/**
|
|
78
|
+
* Full general 4×4 inverse via cofactors: `out = a⁻¹`.
|
|
79
|
+
* @param a - The matrix to invert.
|
|
80
|
+
* @param out - Optional destination (defaults to a new {@link Mat4}).
|
|
81
|
+
* @returns `out`.
|
|
82
|
+
* @throws Error `'mat4.invert: singular matrix'` when the determinant is `0` or
|
|
83
|
+
* non-finite (a uniformly tiny determinant — e.g. the mercator meter placement,
|
|
84
|
+
* det ~1e-23 — is well-conditioned and inverts fine; only true singularities throw).
|
|
85
|
+
*/
|
|
86
|
+
export declare function invert(a: Mat4, out?: Mat4): Mat4;
|
|
87
|
+
/** Apply an affine 4×4 to a point (`w = 1`).
|
|
88
|
+
* @param m - The matrix. @param p - The point `[x, y, z]`.
|
|
89
|
+
* @returns The transformed point `[x, y, z]`. */
|
|
90
|
+
export declare function transformPoint(m: Mat4, p: readonly [number, number, number]): [number, number, number];
|
|
91
|
+
/** Apply only the 3×3 linear part of an affine 4×4 to a direction (no translation).
|
|
92
|
+
* @param m - The matrix. @param d - The direction `[x, y, z]`.
|
|
93
|
+
* @returns The rotated/scaled direction `[x, y, z]` (not renormalized). */
|
|
94
|
+
export declare function transformDir(m: Mat4, d: readonly [number, number, number]): [number, number, number];
|
|
95
|
+
/**
|
|
96
|
+
* Orthographic projection (column-major), mapping the view-space box
|
|
97
|
+
* x∈[left,right], y∈[bottom,top], z∈[-far,-near] (right-handed; the view looks down −Z, so
|
|
98
|
+
* `near`/`far` are positive distances) into NDC [-1,1]³. Used to fit a directional (sun) light's
|
|
99
|
+
* frustum to a scene. Matches the gl-matrix convention.
|
|
100
|
+
*
|
|
101
|
+
* @param left - View-space left bound.
|
|
102
|
+
* @param right - View-space right bound.
|
|
103
|
+
* @param bottom - View-space bottom bound.
|
|
104
|
+
* @param top - View-space top bound.
|
|
105
|
+
* @param near - Near plane distance (positive, in front of the camera).
|
|
106
|
+
* @param far - Far plane distance (positive).
|
|
107
|
+
* @param out - Optional destination (defaults to a new {@link Mat4}).
|
|
108
|
+
* @returns `out`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function orthoInto(left: number, right: number, bottom: number, top: number, near: number, far: number, out?: Mat4): Mat4;
|
|
111
|
+
/** Build a column-major 4×4 from three basis columns + a translation column (row 3 = `[0,0,0,1]`).
|
|
112
|
+
* @param col0 - First basis column. @param col1 - Second. @param col2 - Third.
|
|
113
|
+
* @param translation - The translation column.
|
|
114
|
+
* @param out - Optional destination. @returns `out`. */
|
|
115
|
+
export declare function fromBasis(col0: readonly [number, number, number], col1: readonly [number, number, number], col2: readonly [number, number, number], translation: readonly [number, number, number], out?: Mat4): Mat4;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geodesic measurement math — distances, bearings, interpolation, and polygon
|
|
3
|
+
* area/perimeter over the WGS84 ellipsoid. Everything works in local ENU metres
|
|
4
|
+
* via the exact ECEF↔ENU primitives in `./ecef` (chord-vs-arc error is d³/24R² —
|
|
5
|
+
* ~2 mm at 10 km, ~3 m at 100 km — matching the SDK's city-scale precision
|
|
6
|
+
* philosophy; the bound is documented per function). Pure f64 math, no DOM/GL.
|
|
7
|
+
*/
|
|
8
|
+
import { type Geodetic } from './ecef';
|
|
9
|
+
/** A measured position: lng/lat in degrees, `height` in metres (default 0 where optional). */
|
|
10
|
+
export interface MeasurePoint {
|
|
11
|
+
lng: number;
|
|
12
|
+
lat: number;
|
|
13
|
+
height?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The local ENU offset (metres) of `b` relative to `a` — the workhorse the other
|
|
17
|
+
* measures build on: `[east, north, up]` in the tangent frame at `a`.
|
|
18
|
+
* @param a - The origin point.
|
|
19
|
+
* @param b - The target point.
|
|
20
|
+
* @returns `[east, north, up]` metres.
|
|
21
|
+
*/
|
|
22
|
+
export declare function enuOffset(a: MeasurePoint, b: MeasurePoint): [number, number, number];
|
|
23
|
+
/**
|
|
24
|
+
* Direct (slope) 3D distance between two points in metres — the straight chord
|
|
25
|
+
* through space, including the height difference.
|
|
26
|
+
*/
|
|
27
|
+
export declare function distance3d(a: MeasurePoint, b: MeasurePoint): number;
|
|
28
|
+
/**
|
|
29
|
+
* Horizontal (map-plane) distance in metres — the ground component, heights
|
|
30
|
+
* ignored. Chord error vs the true geodesic arc is `d³/24R²` (~2 mm at 10 km,
|
|
31
|
+
* ~3 m at 100 km).
|
|
32
|
+
*/
|
|
33
|
+
export declare function horizontalDistance(a: MeasurePoint, b: MeasurePoint): number;
|
|
34
|
+
/** Signed height difference `b.height − a.height` in metres. */
|
|
35
|
+
export declare function verticalDelta(a: MeasurePoint, b: MeasurePoint): number;
|
|
36
|
+
/**
|
|
37
|
+
* Initial compass bearing from `a` to `b`, degrees clockwise from north in `[0, 360)`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function bearingDeg(a: MeasurePoint, b: MeasurePoint): number;
|
|
40
|
+
/**
|
|
41
|
+
* Great-circle distance on the mean sphere (metres) — the long-range cross-check
|
|
42
|
+
* and the axis measure for long elevation profiles. Haversine on {@link EARTH_RADIUS}.
|
|
43
|
+
*/
|
|
44
|
+
export declare function haversineDistance(a: MeasurePoint, b: MeasurePoint): number;
|
|
45
|
+
/**
|
|
46
|
+
* Interpolate between two points at fraction `t` (0 = `a`, 1 = `b`): positions
|
|
47
|
+
* lerp in ECEF then renormalize to the surface direction (antimeridian- and
|
|
48
|
+
* pole-safe); heights lerp linearly and independently.
|
|
49
|
+
* @param a - Start point. @param b - End point. @param t - Fraction in `[0, 1]`.
|
|
50
|
+
* @returns The interpolated {@link Geodetic} (lng/lat degrees, height metres).
|
|
51
|
+
*/
|
|
52
|
+
export declare function interpolatePosition(a: MeasurePoint, b: MeasurePoint, t: number): Geodetic;
|
|
53
|
+
/**
|
|
54
|
+
* Total polyline length in metres.
|
|
55
|
+
* @param points - The vertices (≥ 2 for a nonzero result).
|
|
56
|
+
* @param mode - `'3d'` sums {@link distance3d} per segment; `'horizontal'` sums
|
|
57
|
+
* {@link horizontalDistance}.
|
|
58
|
+
*/
|
|
59
|
+
export declare function polylineLength(points: readonly MeasurePoint[], mode: '3d' | 'horizontal'): number;
|
|
60
|
+
/**
|
|
61
|
+
* Planar polygon area in square metres — the ring is projected into the ENU
|
|
62
|
+
* tangent plane at its centroid (heights ignored) and measured with the shoelace
|
|
63
|
+
* formula. Exact to city scale; the tangent-plane error grows with extent and is
|
|
64
|
+
* negligible below ~50 km. Vertex order (CW/CCW) doesn't matter (absolute value);
|
|
65
|
+
* self-intersecting rings are not supported.
|
|
66
|
+
* @param ring - The polygon vertices (unclosed; ≥ 3 for a nonzero result).
|
|
67
|
+
*/
|
|
68
|
+
export declare function polygonAreaM2(ring: readonly MeasurePoint[]): number;
|
|
69
|
+
/**
|
|
70
|
+
* A raster grid of cell CENTRES (lng/lat) inside a polygon — the sampling stations for an
|
|
71
|
+
* area-integral measure (e.g. volume = Σ over cells of fill × cellArea). Cells are square in a
|
|
72
|
+
* local planar frame at the ring's mean latitude; the long bbox axis gets ≤128 cells (≥0.25 m),
|
|
73
|
+
* and the total is capped at `maxCells` (cell size grows to fit). Pure — the caller samples a
|
|
74
|
+
* surface/DEM at the returned centres and integrates. City-scale accuracy (same tangent-plane
|
|
75
|
+
* philosophy as {@link polygonAreaM2}).
|
|
76
|
+
*
|
|
77
|
+
* The `grid` descriptor lets a caller scatter the sampled values back into a full raster (for a
|
|
78
|
+
* heatmap / contour overlay): `centers[k]` lives in row-major cell `cellIndex[k]` (`= jy·nx + ix`)
|
|
79
|
+
* of an `nx × ny` grid whose cell `(ix, jy)` centre is `[minLng+(ix+0.5)·dLng, minLat+(jy+0.5)·dLat]`
|
|
80
|
+
* (`jy` counts NORTH from the bbox's south edge). Cells outside the ring have no `centers` entry.
|
|
81
|
+
* @param ring - Polygon vertices (unclosed; ≥ 3).
|
|
82
|
+
* @param maxCells - Hard cap on the returned cell count (default 8192).
|
|
83
|
+
* @returns `centers` (cell centres strictly inside the ring), the uniform per-cell area (m²), and the
|
|
84
|
+
* `grid` descriptor (`nx`, `ny`, SW origin `minLng`/`minLat`, per-cell steps `dLng`/`dLat`, and the
|
|
85
|
+
* `cellIndex` of each centre).
|
|
86
|
+
*/
|
|
87
|
+
export declare function volumeGridCells(ring: readonly MeasurePoint[], maxCells?: number): {
|
|
88
|
+
centers: {
|
|
89
|
+
lng: number;
|
|
90
|
+
lat: number;
|
|
91
|
+
}[];
|
|
92
|
+
cellAreaM2: number;
|
|
93
|
+
grid: {
|
|
94
|
+
nx: number;
|
|
95
|
+
ny: number;
|
|
96
|
+
minLng: number;
|
|
97
|
+
minLat: number;
|
|
98
|
+
dLng: number;
|
|
99
|
+
dLat: number;
|
|
100
|
+
cellIndex: number[];
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Marching-squares iso-lines of a scalar raster, as independent line SEGMENTS in fractional grid
|
|
105
|
+
* coordinates. Grid sample `field[j·nx + i]` sits at continuous coordinate `(i, j)`; a returned
|
|
106
|
+
* point `[fi, fj]` maps to lng/lat as `[minLng+(fi+0.5)·dLng, minLat+(fj+0.5)·dLat]` (matching
|
|
107
|
+
* {@link volumeGridCells}'s cell-centre convention). Cells touching a `NaN` sample are skipped, so
|
|
108
|
+
* contours never cross the polygon boundary. Pure & allocation-light (no stitching — abutting
|
|
109
|
+
* segments render as a continuous line).
|
|
110
|
+
* @param field - Row-major `nx × ny` samples (`NaN` = no data).
|
|
111
|
+
* @param nx - Columns.
|
|
112
|
+
* @param ny - Rows.
|
|
113
|
+
* @param level - Iso value.
|
|
114
|
+
* @returns Segments `[[fi0, fj0], [fi1, fj1]]` in fractional grid coordinates.
|
|
115
|
+
*/
|
|
116
|
+
export declare function contourSegments(field: ReadonlyArray<number> | Float32Array, nx: number, ny: number, level: number): [[number, number], [number, number]][];
|
|
117
|
+
/**
|
|
118
|
+
* Choose a "nice" contour interval (a 1-2-5 multiple of `baseIntervalM`) and the iso levels spanning
|
|
119
|
+
* `(0, maxFillM]`, targeting at most `maxLines` lines. Base interval is the unit's step — 1 m
|
|
120
|
+
* (metric) or 0.3048 m (1 ft, imperial) — widened to 2/5/10/… for tall piles.
|
|
121
|
+
* @param maxFillM - Peak value of the field (metres).
|
|
122
|
+
* @param baseIntervalM - The unit's base contour step (metres).
|
|
123
|
+
* @param maxLines - Cap on the number of levels (default 12).
|
|
124
|
+
* @returns The chosen `intervalM` and the ascending `levels` (metres).
|
|
125
|
+
*/
|
|
126
|
+
export declare function contourLevels(maxFillM: number, baseIntervalM: number, maxLines?: number): {
|
|
127
|
+
intervalM: number;
|
|
128
|
+
levels: number[];
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Polygon perimeter in metres (horizontal, ring closed automatically).
|
|
132
|
+
* @param ring - The polygon vertices (unclosed).
|
|
133
|
+
*/
|
|
134
|
+
export declare function polygonPerimeterM(ring: readonly MeasurePoint[]): number;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard Web Mercator conversions, matching MapLibre's
|
|
3
|
+
* `MercatorCoordinate` exactly so this core math is interchangeable
|
|
4
|
+
* with the viewer's own conversion. Implemented from the public Web
|
|
5
|
+
* Mercator definition (not from any renderer library).
|
|
6
|
+
*
|
|
7
|
+
* Mercator unit square: x,y in [0,1] over the world; z is altitude in
|
|
8
|
+
* the same units (meters / circumferenceAtLatitude). A scene authored
|
|
9
|
+
* in local meters is placed by `placementMatrix`.
|
|
10
|
+
*/
|
|
11
|
+
import { type Mat4 } from './mat4';
|
|
12
|
+
/** Mean Earth radius (m) used for the mercator circumference. @internal */
|
|
13
|
+
export declare const EARTH_RADIUS = 6371008.8;
|
|
14
|
+
/** Earth circumference (m) at the equator (`2π·EARTH_RADIUS`). @internal */
|
|
15
|
+
export declare const EARTH_CIRCUMFERENCE: number;
|
|
16
|
+
/** A geographic anchor placing a local-meters scene in the world. */
|
|
17
|
+
export interface GeoAnchor {
|
|
18
|
+
/** Longitude in degrees. */
|
|
19
|
+
readonly lng: number;
|
|
20
|
+
/** Latitude in degrees. */
|
|
21
|
+
readonly lat: number;
|
|
22
|
+
/** Height in meters above the WGS84 ellipsoid. */
|
|
23
|
+
readonly altitude: number;
|
|
24
|
+
}
|
|
25
|
+
/** A point in MapLibre-compatible web-mercator: `x`,`y` in `[0,1]` over the world,
|
|
26
|
+
* `z` altitude in the same normalized units (meters / circumferenceAtLatitude). */
|
|
27
|
+
export interface MercatorXYZ {
|
|
28
|
+
/** Normalized mercator X in `[0,1]` (west→east). */
|
|
29
|
+
readonly x: number;
|
|
30
|
+
/** Normalized mercator Y in `[0,1]` (north→south). */
|
|
31
|
+
readonly y: number;
|
|
32
|
+
/** Normalized mercator Z (altitude in mercator units). */
|
|
33
|
+
readonly z: number;
|
|
34
|
+
}
|
|
35
|
+
/** World circumference (m) at a latitude (shrinks toward the poles by `cos lat`).
|
|
36
|
+
* @param lat - Latitude in degrees.
|
|
37
|
+
* @returns Circumference in meters. */
|
|
38
|
+
export declare function circumferenceAtLatitude(lat: number): number;
|
|
39
|
+
/** Longitude (degrees) → normalized mercator X in `[0,1]`. */
|
|
40
|
+
export declare function mercatorXfromLng(lng: number): number;
|
|
41
|
+
/** Latitude (degrees) → normalized mercator Y in `[0,1]`. */
|
|
42
|
+
export declare function mercatorYfromLat(lat: number): number;
|
|
43
|
+
/** Altitude (m above ellipsoid) → normalized mercator Z at a latitude.
|
|
44
|
+
* @param altitude - Height in meters. @param lat - Latitude in degrees. */
|
|
45
|
+
export declare function mercatorZfromAltitude(altitude: number, lat: number): number;
|
|
46
|
+
/** Normalized mercator X in `[0,1]` → longitude (degrees). Inverse of {@link mercatorXfromLng}. */
|
|
47
|
+
export declare function lngFromMercatorX(x: number): number;
|
|
48
|
+
/** Normalized mercator Y in `[0,1]` → latitude (degrees). Inverse of {@link mercatorYfromLat}
|
|
49
|
+
* (the Gudermannian). */
|
|
50
|
+
export declare function latFromMercatorY(y: number): number;
|
|
51
|
+
/** Normalized mercator Z → altitude in meters at a latitude. Inverse of {@link mercatorZfromAltitude}.
|
|
52
|
+
* @param z - Normalized mercator Z. @param lat - Latitude in degrees. */
|
|
53
|
+
export declare function altitudeFromMercatorZ(z: number, lat: number): number;
|
|
54
|
+
/** Mercator units per meter at a latitude (latitude-dependent — never a constant); the
|
|
55
|
+
* linear scale that places a local-meters scene into mercator.
|
|
56
|
+
* @param lat - Latitude in degrees. @returns Mercator units per meter. */
|
|
57
|
+
export declare function meterInMercatorCoordinateUnits(lat: number): number;
|
|
58
|
+
/** Convert lng/lat (degrees) + altitude (meters) to {@link MercatorXYZ}.
|
|
59
|
+
* @param lngLat - `{ lng, lat }` in degrees.
|
|
60
|
+
* @param altitude - Height in meters above the ellipsoid (default 0).
|
|
61
|
+
* @returns The normalized web-mercator coordinate. */
|
|
62
|
+
export declare function mercatorFromLngLat(lngLat: {
|
|
63
|
+
lng: number;
|
|
64
|
+
lat: number;
|
|
65
|
+
}, altitude?: number): MercatorXYZ;
|
|
66
|
+
/** Options for {@link placementMatrix}. */
|
|
67
|
+
export interface PlacementOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Optional basis rotation (column-major {@link Mat4}) applied BEFORE the meter
|
|
70
|
+
* scaling, to reconcile the asset's axis convention (e.g. Y-up) with the mercator
|
|
71
|
+
* frame (x east, y south, z up). Defaults to identity — i.e. the local frame is
|
|
72
|
+
* already x-east / y-south / z-up in meters.
|
|
73
|
+
*/
|
|
74
|
+
readonly basis?: Mat4;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build the model matrix that maps a local-meters scene anchored at `anchor` into
|
|
78
|
+
* web-mercator world space: `T(merc) · S(meter) · [basis]`.
|
|
79
|
+
*
|
|
80
|
+
* @remarks
|
|
81
|
+
* The meter scaling is latitude-dependent (see {@link meterInMercatorCoordinateUnits});
|
|
82
|
+
* the same linear part is what the splat covariance transform must include so ellipsoids
|
|
83
|
+
* scale with the map.
|
|
84
|
+
*
|
|
85
|
+
* @param anchor - Where the scene is anchored ({@link GeoAnchor}: lng/lat degrees, altitude meters).
|
|
86
|
+
* @param opts - Optional {@link PlacementOptions} (axis `basis`).
|
|
87
|
+
* @returns A column-major f64 {@link Mat4} (local meters → mercator).
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const model = placementMatrix({ lng: -73.99, lat: 40.75, altitude: 0 });
|
|
92
|
+
* gl.uniformMatrix4fv(uModel, false, toFloat32(model));
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function placementMatrix(anchor: GeoAnchor, opts?: PlacementOptions): Mat4;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Friendly model placement — the user-facing vocabulary that replaces raw
|
|
3
|
+
* `Mat4` basis math for placing a single local-meters model (gaussian splat,
|
|
4
|
+
* point cloud, …) on the map.
|
|
5
|
+
*
|
|
6
|
+
* A capture's coordinate frame is a format/pipeline property (SPZ-style
|
|
7
|
+
* assets are typically Y-up, LAS/BVC are Z-up), its heading/scale are
|
|
8
|
+
* per-capture tuning, and MapLibre's mercator frame quirks (Y grows SOUTH,
|
|
9
|
+
* so the frame is left-handed) are an SDK implementation detail. This module
|
|
10
|
+
* keeps each concern in its place: `ModelPlacement` exposes only
|
|
11
|
+
* degrees/meters/presets, and `placementBasis` composes the correct
|
|
12
|
+
* column-major basis — including the internal mercator Y-flip the user must
|
|
13
|
+
* never see.
|
|
14
|
+
*/
|
|
15
|
+
import { type Mat4 } from './mat4';
|
|
16
|
+
import { type GeoAnchor } from './mercator';
|
|
17
|
+
import type { LocalBounds } from '../dequant/tile-bounds';
|
|
18
|
+
export type { LocalBounds } from '../dequant/tile-bounds';
|
|
19
|
+
/**
|
|
20
|
+
* Which SOURCE axis points up in the asset's own frame — a format/pipeline
|
|
21
|
+
* property, never user matrix math. `'z'` = already Z-up (LAS/BVC native);
|
|
22
|
+
* `'y'` = glTF/SPZ-style Y-up; `'-y'` = COLMAP/3DGS Y-down; `'x' | '-x' |
|
|
23
|
+
* '-z'` cover exotic captures.
|
|
24
|
+
*/
|
|
25
|
+
export type UpAxis = 'z' | 'y' | '-y' | 'x' | '-x' | '-z';
|
|
26
|
+
/**
|
|
27
|
+
* Which local point of the model sits exactly at the `GeoAnchor` — the
|
|
28
|
+
* vertical seat is an explicit top/middle/bottom choice:
|
|
29
|
+
* `'center'` (default) = the model's center at the anchor altitude;
|
|
30
|
+
* `'bottom-center'` = base at the anchor altitude ("sits on the ground");
|
|
31
|
+
* `'top-center'` = top at the anchor altitude (hang from a height);
|
|
32
|
+
* `'origin'` = the capture's raw local origin (legacy behavior); a tuple =
|
|
33
|
+
* an explicit source-space point in meters (deterministic — skips bounds
|
|
34
|
+
* entirely; useful for pinning production placements).
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* When the bounds carry robust percentiles (`LocalBounds.p05/p50/p95` — the
|
|
38
|
+
* layers always compute them), anchoring is OUTLIER-PROOF: `'center'` is the
|
|
39
|
+
* per-axis median and bottom/top are the 5th/95th percentiles along the up
|
|
40
|
+
* axis. Gaussian-splat captures surround their subject with a huge sparse
|
|
41
|
+
* halo of background splats (±200 m around an 8 m subject is typical), so
|
|
42
|
+
* raw min/max anchoring would seat the halo, not the subject.
|
|
43
|
+
*/
|
|
44
|
+
export type AnchorPoint = 'center' | 'bottom-center' | 'top-center' | 'origin' | readonly [number, number, number];
|
|
45
|
+
/**
|
|
46
|
+
* Friendly per-capture placement — everything a user tunes to seat a model
|
|
47
|
+
* on the map, with no matrices. Shared by every single-model layer
|
|
48
|
+
* (`BvcMapLibreLayer`, `SpzMapLibreLayer`, …).
|
|
49
|
+
*/
|
|
50
|
+
export interface ModelPlacement {
|
|
51
|
+
/** Up axis of the source asset (`UpAxis`). Default `'z'` (layers may document a format-specific default). */
|
|
52
|
+
readonly upAxis?: UpAxis;
|
|
53
|
+
/** Heading in degrees clockwise from north viewed from above (0 = the model's local +y faces north).
|
|
54
|
+
* Same semantics as `GeodeticInstance.heading`. Default 0. */
|
|
55
|
+
readonly heading?: number;
|
|
56
|
+
/** Pitch in degrees about the model's local x axis (east at heading 0) — positive tips the
|
|
57
|
+
* +y (north-facing) side up. A leveling aid for off-level captures. Default 0. */
|
|
58
|
+
readonly pitch?: number;
|
|
59
|
+
/** Roll in degrees about the model's local y axis (north at heading 0) — positive lowers the
|
|
60
|
+
* +x (east-facing) side. A leveling aid for off-level captures. Default 0. */
|
|
61
|
+
readonly roll?: number;
|
|
62
|
+
/** Unitless scale: uniform number or per-axis `[x, y, z]` in the UP-FIXED frame (z = height,
|
|
63
|
+
* regardless of `ModelPlacement.upAxis`). Same shape as `GeodeticInstance.scale`. Default 1. */
|
|
64
|
+
readonly scale?: number | readonly [number, number, number];
|
|
65
|
+
/** Which local point sits at the anchor (`AnchorPoint`: top/middle/bottom,
|
|
66
|
+
* origin, or an explicit point). Default `'center'`. */
|
|
67
|
+
readonly anchorPoint?: AnchorPoint;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `diag(1, −1, 1)` — right-handed local ENU-meters → MapLibre's left-handed
|
|
71
|
+
* mercator frame (mercator Y grows SOUTH). The ONLY handedness flip in the
|
|
72
|
+
* placement composition (det −1); applied internally by
|
|
73
|
+
* `placementBasis`, never user-visible.
|
|
74
|
+
*/
|
|
75
|
+
export declare const MERCATOR_Y_FLIP: Mat4;
|
|
76
|
+
/**
|
|
77
|
+
* Pure rotations (det +1) taking each source frame to x-east / y-north /
|
|
78
|
+
* z-up. Exact signed permutations (literals, not trig — no 1e-17 residue);
|
|
79
|
+
* `'y'` equals the glTF/SPZ `Y_UP_TO_Z_UP` rotation (+90° about X).
|
|
80
|
+
*/
|
|
81
|
+
export declare const UP_AXIS_BASIS: Readonly<Record<UpAxis, Mat4>>;
|
|
82
|
+
/**
|
|
83
|
+
* The signed unit vector of an `UpAxis` in the model's SOURCE frame — the
|
|
84
|
+
* direction "height" runs along before the up-axis fix (e.g. `'y'` →
|
|
85
|
+
* `[0, 1, 0]`, `'-y'` → `[0, -1, 0]`).
|
|
86
|
+
*
|
|
87
|
+
* @param upAxis - The source up axis.
|
|
88
|
+
* @returns The signed unit source-frame up vector.
|
|
89
|
+
*/
|
|
90
|
+
export declare function upAxisVector(upAxis: UpAxis): readonly [number, number, number];
|
|
91
|
+
/**
|
|
92
|
+
* Resolve the SOURCE-space point that `placementBasis` pins to the anchor.
|
|
93
|
+
* `'bottom-center'`/`'top-center'` are sign-aware along the source axis that
|
|
94
|
+
* maps to up (e.g. `'-y'` → bottom = the source MAX y).
|
|
95
|
+
*
|
|
96
|
+
* @remarks
|
|
97
|
+
* Robust percentiles win when the bounds carry them (`p50` for centers, the
|
|
98
|
+
* up-axis `p05`/`p95` for bottom/top) — see the `AnchorPoint` remarks on
|
|
99
|
+
* outlier halos. Plain AABBs fall back to midpoint/min/max.
|
|
100
|
+
*
|
|
101
|
+
* @param bounds - The model's bounds in source coordinates (`LocalBounds`).
|
|
102
|
+
* @param upAxis - The source up axis (decides which axis top/bottom runs along).
|
|
103
|
+
* @param anchorPoint - The requested `AnchorPoint`.
|
|
104
|
+
* @returns The source-space point (meters) to place at the anchor.
|
|
105
|
+
*/
|
|
106
|
+
export declare function anchorPointSource(bounds: LocalBounds, upAxis: UpAxis, anchorPoint: AnchorPoint): [number, number, number];
|
|
107
|
+
/**
|
|
108
|
+
* Build the local→mercator basis from a friendly `ModelPlacement`:
|
|
109
|
+
*
|
|
110
|
+
* `B = MERCATOR_Y_FLIP · Rz(−heading) · Rx(pitch) · Ry(roll) · S(scale) · U(upAxis) · T(−anchorSource)`
|
|
111
|
+
*
|
|
112
|
+
* @remarks
|
|
113
|
+
* The innermost recenter translation is in SOURCE coordinates, so `B ·
|
|
114
|
+
* anchorSource = 0` exactly — heading/pitch/roll/scale pivot about the
|
|
115
|
+
* anchored point by construction. With every field defaulted and
|
|
116
|
+
* `anchorSource` at the origin, the result is byte-identical to the legacy
|
|
117
|
+
* default basis `diag(1, −1, 1)`.
|
|
118
|
+
*
|
|
119
|
+
* @param placement - The friendly placement fields (all optional; see `ModelPlacement`).
|
|
120
|
+
* @param anchorSource - The source-space point pinned to the anchor (from
|
|
121
|
+
* `anchorPointSource`); default `[0, 0, 0]`.
|
|
122
|
+
* @returns A column-major f64 `Mat4` basis for `placementMatrix`.
|
|
123
|
+
*/
|
|
124
|
+
export declare function placementBasis(placement?: ModelPlacement, anchorSource?: readonly [number, number, number]): Mat4;
|
|
125
|
+
/**
|
|
126
|
+
* The one-call model matrix every single-model layer uses:
|
|
127
|
+
* `T(merc) · S(meterPerMercUnit) · placementBasis(placement, anchorSource)`.
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* `bounds` are only needed for the `'center'` / `'bottom-center'` anchor
|
|
131
|
+
* modes; when they are not yet known (tiles still streaming) pass `null` and
|
|
132
|
+
* rebuild once they are — the fallback anchors the source origin, exactly the
|
|
133
|
+
* legacy behavior. `'origin'` and explicit-tuple anchor points never consult
|
|
134
|
+
* bounds.
|
|
135
|
+
*
|
|
136
|
+
* @param anchor - Where the model sits (`GeoAnchor`: lng/lat degrees, altitude meters).
|
|
137
|
+
* @param placement - The friendly placement (`ModelPlacement`); default all-defaults.
|
|
138
|
+
* @param bounds - The model's source-space AABB, or `null`/omitted before it is known.
|
|
139
|
+
* @returns A column-major f64 `Mat4` (source coordinates → mercator).
|
|
140
|
+
*/
|
|
141
|
+
export declare function modelPlacementMatrix(anchor: GeoAnchor, placement?: ModelPlacement, bounds?: LocalBounds | null): Mat4;
|