@navaramap/three-api 0.1.1 → 0.2.0

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 CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2026 Navara contributors
1
+ Copyright (c) 2026 MapLibre contributors
2
2
 
3
3
  This project is dual-licensed under either of
4
4
 
@@ -0,0 +1,82 @@
1
+ import { NormalizeWASMClass } from '@navaramap/core';
2
+ import { PolygonMaterial, PolylineMaterial } from '@navaramap/engine-api';
3
+ /**
4
+ * Geometry builders for standalone polygon and polyline meshes, using the
5
+ * vector layer's construction. Positions are RTE-encoded.
6
+ */
7
+ /** A polygon layer material, as a plain object. */
8
+ export type PolygonGeometryMaterial = NormalizeWASMClass<PolygonMaterial>;
9
+ /** A polyline layer material, as a plain object. */
10
+ export type PolylineGeometryMaterial = NormalizeWASMClass<PolylineMaterial>;
11
+ /** A geographic bounding box, in degrees. */
12
+ export type GeographicExtent = {
13
+ west: number;
14
+ south: number;
15
+ east: number;
16
+ north: number;
17
+ };
18
+ export type PolygonGeometryData = {
19
+ positionHigh: Float32Array;
20
+ positionLow: Float32Array;
21
+ /** Absent for a `clampToGround` polygon. */
22
+ normal?: Float32Array;
23
+ /** `xyz` = extrusion direction, `w` = 0 on the bottom cap, 1 on the top. */
24
+ scaleNormalAndCap: Float32Array;
25
+ indices: Uint32Array;
26
+ extent: GeographicExtent;
27
+ };
28
+ export type PolylineGeometryData = {
29
+ position: Float32Array;
30
+ positionHigh: Float32Array;
31
+ positionLow: Float32Array;
32
+ startHigh: Float32Array;
33
+ startLow: Float32Array;
34
+ endHigh: Float32Array;
35
+ endLow: Float32Array;
36
+ startNormal: Float32Array;
37
+ endNormalAndTextureCoordinateNormalizationX: Float32Array;
38
+ rightNormalAndTextureCoordinateNormalizationY: Float32Array;
39
+ indices: Uint32Array;
40
+ extent: GeographicExtent;
41
+ };
42
+ /** Flat `[lng, lat, height, ...]`, or ECEF `[x, y, z, ...]` when geocentric. */
43
+ export type GeometryRing = Float64Array;
44
+ /**
45
+ * Builds the geometry of one standalone polygon.
46
+ *
47
+ * @param rings - Outer ring first, then holes.
48
+ * @param geocentric - Read the positions as ECEF metres.
49
+ * @param material - A `polygon` layer material. Outline options are ignored.
50
+ * @throws When `material` does not deserialize.
51
+ * @returns `undefined` when the outer ring has fewer than three vertices or is
52
+ * collinear.
53
+ */
54
+ export declare function buildPolygonGeometry(rings: readonly GeometryRing[], geocentric: boolean, material: PolygonGeometryMaterial): PolygonGeometryData | undefined;
55
+ /**
56
+ * Builds the geometry of one standalone polyline.
57
+ *
58
+ * @param positions - Flat `[lng, lat, height, ...]`, or ECEF when `geocentric`.
59
+ * @param geocentric - Read the positions as ECEF metres.
60
+ * @param ring - Join a repeated first vertex as a seam instead of two caps.
61
+ * @param material - A `polyline` layer material.
62
+ * @throws When `material` does not deserialize.
63
+ * @returns `undefined` when fewer than two distinct positions remain.
64
+ */
65
+ export declare function buildPolylineGeometry(positions: Float64Array, geocentric: boolean, ring: boolean, material: PolylineGeometryMaterial): PolylineGeometryData | undefined;
66
+ /** A height range in metres, relative to the ellipsoid. */
67
+ export type HeightRange = {
68
+ min: number;
69
+ max: number;
70
+ };
71
+ /**
72
+ * `[min, max]` height a clamp-to-ground polygon volume over `extent` must
73
+ * span to enclose the ground heights `range`, given the flat cap triangles
74
+ * {@link buildPolygonGeometry} produces.
75
+ */
76
+ export declare function polygonGroundVolume(range: HeightRange, extent: GeographicExtent): [number, number];
77
+ /**
78
+ * `[min, max]` height a clamp-to-ground polyline volume over `extent` must
79
+ * span to enclose the ground heights `range`, given the straight segments
80
+ * {@link buildPolylineGeometry} produces.
81
+ */
82
+ export declare function polylineGroundVolume(range: HeightRange, extent: GeographicExtent): [number, number];
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { GeodeticPlacement } from './frames';
4
4
  export type { LatLngHeight, LatLng } from '@navaramap/core';
5
5
  export type { Window, Transform, CameraFrustum, Plane, Ray, Vec2, Vec3, } from '@navaramap/core';
6
6
  export type { GeodeticPlacement } from './frames';
7
+ export * from './featureGeometry';
7
8
  export * from './intersection';
8
9
  export * from './rte';
9
10
  export * from './ellipsoidGeodesic';
@@ -49,6 +50,14 @@ export declare function convertScreenToWorld(windowObject: WindowObject, camera:
49
50
  * @returns Unit normal vector pointing outward from the ellipsoid surface
50
51
  */
51
52
  export declare function geodeticSurfaceNormal(lle: LatLngHeight): Vector3;
53
+ /**
54
+ * Projects a Cartesian position onto the WGS84 ellipsoid surface along the
55
+ * geodetic normal.
56
+ * @param xyz - Cartesian Vector3 in Earth-Centered Earth-Fixed (ECEF) coordinates
57
+ * @returns The surface position in ECEF coordinates, or undefined when the
58
+ * position is too close to the ellipsoid's center to project
59
+ */
60
+ export declare function scaleToGeodeticSurface(xyz: Vector3): Vector3 | undefined;
52
61
  /**
53
62
  * Creates a local East-North-Up (ENU) reference frame transformation matrix at a position.
54
63
  * @param origin - Origin position in ECEF coordinates
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import e, { CameraFrustum as t, EllipsoidGeodesic as n, LLE as r, Ray as i, Transform as a, Vec2 as o, Vec3 as s, Window as c, angleToDegree as l, angleToRadian as u, eastNorthUpToFixedFrame as d, encodePosition as f, geodeticSurfaceNormal as p, geodeticToXyz as m, getHeightFromEllipsoid as h, getPickRay as g, getPlaneFromPointNormal as _, getRayPlaneIntersection as v, getWGS84Eccentricity as y, getWGS84EccentricitySquared as b, getWGS84Flattening as x, getWGS84SemiMajorAxis as S, getWGS84SemiMinorAxis as C, northEastDownToFixedFrame as ee, northUpEastToFixedFrame as te, northWestUpToFixedFrame as w, screenToWorld as T, worldToScreen as E, xyzToGeodetic as D } from "@navaramap/engine-api";
1
+ import e, { CameraFrustum as t, EllipsoidGeodesic as n, LLE as r, Ray as i, Transform as a, Vec2 as o, Vec3 as s, Window as c, angleToDegree as l, angleToRadian as u, buildPolygonGeometry as d, buildPolylineGeometry as f, eastNorthUpToFixedFrame as p, encodePosition as m, geodeticSurfaceNormal as h, geodeticToXyz as g, getHeightFromEllipsoid as _, getPickRay as v, getPlaneFromPointNormal as y, getRayPlaneIntersection as b, getWGS84Eccentricity as ee, getWGS84EccentricitySquared as te, getWGS84Flattening as ne, getWGS84SemiMajorAxis as re, getWGS84SemiMinorAxis as ie, northEastDownToFixedFrame as ae, northUpEastToFixedFrame as oe, northWestUpToFixedFrame as x, polygonGroundVolume as S, polylineGroundVolume as C, scaleToGeodeticSurface as w, screenToWorld as T, worldToScreen as E, xyzToGeodetic as D } from "@navaramap/engine-api";
2
2
  import { Euler as O, MathUtils as k, Matrix4 as A, Plane as j, Ray as M, Vector2 as N, Vector3 as P } from "three";
3
3
  //#region src/frames.ts
4
4
  function F() {
@@ -13,44 +13,118 @@ function I(e, t) {
13
13
  return n;
14
14
  }
15
15
  //#endregion
16
+ //#region src/featureGeometry.ts
17
+ function L(e) {
18
+ return {
19
+ west: l(e.west),
20
+ south: l(e.south),
21
+ east: l(e.east),
22
+ north: l(e.north)
23
+ };
24
+ }
25
+ function R(e) {
26
+ let t = 0;
27
+ for (let n of e) t += n.length;
28
+ let n = new Float64Array(t), r = 0;
29
+ for (let t of e) n.set(t, r), r += t.length;
30
+ return n;
31
+ }
32
+ function z(e) {
33
+ let t = new Uint32Array(e.length);
34
+ for (let n = 0; n < e.length; n++) t[n] = e[n].length / 3;
35
+ return t;
36
+ }
37
+ function B(e, t, n) {
38
+ let r = d(R(e), z(e), t, n);
39
+ if (r) try {
40
+ let e = r.position_3d_high(), t = r.position_3d_low(), n = r.extent;
41
+ if (!e || !t || !n) throw Error("buildPolygonGeometry: incomplete RTE geometry");
42
+ let i = r.scale_normal_and_cap();
43
+ if (!i) throw Error("buildPolygonGeometry: missing extrusion attribute");
44
+ return {
45
+ positionHigh: e,
46
+ positionLow: t,
47
+ normal: r.normal(),
48
+ scaleNormalAndCap: i,
49
+ indices: r.indices(),
50
+ extent: L(n)
51
+ };
52
+ } finally {
53
+ r.free();
54
+ }
55
+ }
56
+ function V(e, t, n, r) {
57
+ let i = f(e, t, n, r);
58
+ if (i) try {
59
+ let e = i.position_high(), t = i.position_low(), n = i.start_high(), r = i.start_low(), a = i.end_high(), o = i.end_low(), s = i.start_normals(), c = i.end_normal_and_texture_coordinate_normalization_x(), l = i.extent;
60
+ if (!e || !t || !n || !r || !a || !o || !s || !c || !l) throw Error("buildPolylineGeometry: incomplete RTE geometry");
61
+ return {
62
+ position: i.position(),
63
+ positionHigh: e,
64
+ positionLow: t,
65
+ startHigh: n,
66
+ startLow: r,
67
+ endHigh: a,
68
+ endLow: o,
69
+ startNormal: s,
70
+ endNormalAndTextureCoordinateNormalizationX: c,
71
+ rightNormalAndTextureCoordinateNormalizationY: i.right_normal_and_texture_coordinate_normalization_y(),
72
+ indices: i.indices(),
73
+ extent: L(l)
74
+ };
75
+ } finally {
76
+ i.free();
77
+ }
78
+ }
79
+ function H(e, t, n) {
80
+ let r = e(t.min, t.max, u(n.west), u(n.south), u(n.east), u(n.north));
81
+ return [r[0], r[1]];
82
+ }
83
+ function U(e, t) {
84
+ return H(S, e, t);
85
+ }
86
+ function W(e, t) {
87
+ return H(C, e, t);
88
+ }
89
+ //#endregion
16
90
  //#region src/intersection.ts
17
- function L(e, t) {
18
- let n = new s(e.x, e.y, e.z), r = new s(t.x, t.y, t.z), i = _(n, r), a = new P(i.normal.x, i.normal.y, i.normal.z), o = new j(a, -i.distance);
91
+ function G(e, t) {
92
+ let n = new s(e.x, e.y, e.z), r = new s(t.x, t.y, t.z), i = y(n, r), a = new P(i.normal.x, i.normal.y, i.normal.z), o = new j(a, -i.distance);
19
93
  return i.free(), o;
20
94
  }
21
- function R(e, n, r) {
95
+ function K(e, n, r) {
22
96
  let i = new c(e.width, e.height, e.pixelRatio);
23
97
  i.width *= i.pixelRatio, i.height *= i.pixelRatio;
24
- let s = new a(n.position.x, n.position.y, n.position.z, n.quaternion.x, n.quaternion.y, n.quaternion.z, n.quaternion.w, n.scale.x, n.scale.y, n.scale.z), l = new t(n.near, n.far, u(n.fov), n.aspect), d = g(i, s, l, new o(r.x, r.y)), f = new P(d.origin.x, d.origin.y, d.origin.z), p = new P(d.direction.x, d.direction.y, d.direction.z), m = new M(f, p);
98
+ let s = new a(n.position.x, n.position.y, n.position.z, n.quaternion.x, n.quaternion.y, n.quaternion.z, n.quaternion.w, n.scale.x, n.scale.y, n.scale.z), l = new t(n.near, n.far, u(n.fov), n.aspect), d = v(i, s, l, new o(r.x, r.y)), f = new P(d.origin.x, d.origin.y, d.origin.z), p = new P(d.direction.x, d.direction.y, d.direction.z), m = new M(f, p);
25
99
  return d.free(), m;
26
100
  }
27
- function z(e, t) {
28
- let n = new i(new s(e.origin.x, e.origin.y, e.origin.z), new s(e.direction.x, e.direction.y, e.direction.z)), r = _(new s(t.normal.x * -t.constant, t.normal.y * -t.constant, t.normal.z * -t.constant), new s(t.normal.x, t.normal.y, t.normal.z)), a = v(n, r);
101
+ function q(e, t) {
102
+ let n = new i(new s(e.origin.x, e.origin.y, e.origin.z), new s(e.direction.x, e.direction.y, e.direction.z)), r = y(new s(t.normal.x * -t.constant, t.normal.y * -t.constant, t.normal.z * -t.constant), new s(t.normal.x, t.normal.y, t.normal.z)), a = b(n, r);
29
103
  if (a) {
30
104
  let e = new P(a.x, a.y, a.z);
31
105
  return a.free(), e;
32
106
  }
33
107
  }
34
- function B(e) {
108
+ function se(e) {
35
109
  let t = new s(e.x, e.y, e.z);
36
- return h(t);
110
+ return _(t);
37
111
  }
38
112
  //#endregion
39
113
  //#region src/rte.ts
40
- var V = Object.freeze({ value: 1 });
41
- function H(e, t = new P(), n = new P()) {
42
- let r = f(e.x, e.y, e.z);
114
+ var ce = Object.freeze({ value: 1 });
115
+ function le(e, t = new P(), n = new P()) {
116
+ let r = m(e.x, e.y, e.z);
43
117
  return t.set(r.high.x, r.high.y, r.high.z), n.set(r.low.x, r.low.y, r.low.z), r.free(), {
44
118
  high: t,
45
119
  low: n
46
120
  };
47
121
  }
48
- var ne = (e, t, n = new A()) => {
122
+ var ue = (e, t, n = new A()) => {
49
123
  n.multiplyMatrices(t, e);
50
124
  let r = n.elements;
51
125
  return r[12] = 0, r[13] = 0, r[14] = 0, n;
52
126
  };
53
- function U(e, t, n = new P(), r = new A()) {
127
+ function de(e, t, n = new P(), r = new A()) {
54
128
  r.multiplyMatrices(e, t), n.setFromMatrixPosition(r);
55
129
  let i = r.elements;
56
130
  return i[12] = 0, i[13] = 0, i[14] = 0, {
@@ -58,14 +132,14 @@ function U(e, t, n = new P(), r = new A()) {
58
132
  rotationScale: r
59
133
  };
60
134
  }
61
- var W = new A(), G = new P(), K = (e, t) => {
62
- W.copy(t).invert(), G.copy(e).applyMatrix4(W);
63
- let n = f(G.x, G.y, G.z), r = n.high, i = n.low, a = {
135
+ var J = new A(), Y = new P(), fe = (e, t) => {
136
+ J.copy(t).invert(), Y.copy(e).applyMatrix4(J);
137
+ let n = m(Y.x, Y.y, Y.z), r = n.high, i = n.low, a = {
64
138
  high: new P(r.x, r.y, r.z),
65
139
  low: new P(i.x, i.y, i.z)
66
140
  };
67
141
  return n.free(), a;
68
- }, q = class {
142
+ }, pe = class {
69
143
  _raw;
70
144
  constructor(e, t) {
71
145
  this._raw = new n(new r(u(e.lat), u(e.lng), e.height), new r(u(t.lat), u(t.lng), t.height));
@@ -121,14 +195,14 @@ var W = new A(), G = new P(), K = (e, t) => {
121
195
  };
122
196
  //#endregion
123
197
  //#region src/index.ts
124
- async function J() {
198
+ async function me() {
125
199
  await e();
126
200
  }
127
- function Y(e) {
128
- let t = m(new r(u(e.lat), u(e.lng), e.height)), n = new P(t.x, t.y, t.z);
201
+ function X(e) {
202
+ let t = g(new r(u(e.lat), u(e.lng), e.height)), n = new P(t.x, t.y, t.z);
129
203
  return t.free(), n;
130
204
  }
131
- function X(e) {
205
+ function he(e) {
132
206
  let t = new s(e.x, e.y, e.z), n = D(t), r = {
133
207
  lat: l(n.lat),
134
208
  lng: l(n.lng),
@@ -136,13 +210,13 @@ function X(e) {
136
210
  };
137
211
  return n.free(), r;
138
212
  }
139
- function Z(e) {
213
+ function ge(e) {
140
214
  return u(e);
141
215
  }
142
- function re(e) {
216
+ function _e(e) {
143
217
  return l(e);
144
218
  }
145
- function ie(e, n, r) {
219
+ function ve(e, n, r) {
146
220
  let i = new c(e.width, e.height, e.pixelRatio);
147
221
  i.width *= i.pixelRatio, i.height *= i.pixelRatio;
148
222
  let s = new a(n.position.x, n.position.y, n.position.z, n.quaternion.x, n.quaternion.y, n.quaternion.z, n.quaternion.w, n.scale.x, n.scale.y, n.scale.z), l = new t(n.near, n.far, u(n.fov), n.aspect), d = T(i, s, l, new o(r.x, r.y));
@@ -150,37 +224,43 @@ function ie(e, n, r) {
150
224
  let f = new P(d.x, d.y, d.z);
151
225
  return d.free(), f;
152
226
  }
153
- function ae(e) {
154
- let t = p(new r(u(e.lat), u(e.lng), e.height)), n = new P(t.x, t.y, t.z);
227
+ function ye(e) {
228
+ let t = h(new r(u(e.lat), u(e.lng), e.height)), n = new P(t.x, t.y, t.z);
229
+ return t.free(), n;
230
+ }
231
+ function be(e) {
232
+ let t = w(new s(e.x, e.y, e.z));
233
+ if (!t) return;
234
+ let n = new P(t.x, t.y, t.z);
155
235
  return t.free(), n;
156
236
  }
157
- function oe(e) {
158
- let t = new s(e.x, e.y, e.z), n = d(t);
237
+ function xe(e) {
238
+ let t = new s(e.x, e.y, e.z), n = p(t);
159
239
  return new A().fromArray(n);
160
240
  }
161
- function se(e) {
162
- let t = new s(e.x, e.y, e.z), n = ee(t);
241
+ function Se(e) {
242
+ let t = new s(e.x, e.y, e.z), n = ae(t);
163
243
  return new A().fromArray(n);
164
244
  }
165
- function Q(e) {
166
- let t = new s(e.x, e.y, e.z), n = te(t);
245
+ function Z(e) {
246
+ let t = new s(e.x, e.y, e.z), n = oe(t);
167
247
  return new A().fromArray(n);
168
248
  }
169
- function ce(e) {
170
- let t = new s(e.x, e.y, e.z), n = w(t);
249
+ function Ce(e) {
250
+ let t = new s(e.x, e.y, e.z), n = x(t);
171
251
  return new A().fromArray(n);
172
252
  }
173
- function $(e) {
174
- return Q(e).multiply(F());
253
+ function Q(e) {
254
+ return Z(e).multiply(F());
175
255
  }
176
- function le(e) {
177
- return I($(Y({
256
+ function $(e) {
257
+ return I(Q(X({
178
258
  lng: e.lng,
179
259
  lat: e.lat,
180
260
  height: e.height ?? 0
181
261
  })), e);
182
262
  }
183
- function ue(e, n, r) {
263
+ function we(e, n, r) {
184
264
  let i = new c(e.width, e.height, e.pixelRatio);
185
265
  i.width *= i.pixelRatio, i.height *= i.pixelRatio;
186
266
  let o = new a(n.position.x, n.position.y, n.position.z, n.quaternion.x, n.quaternion.y, n.quaternion.z, n.quaternion.w, n.scale.x, n.scale.y, n.scale.z), l = new t(n.near, n.far, u(n.fov), n.aspect), d = E(i, o, l, new s(r.x, r.y, r.z));
@@ -188,20 +268,20 @@ function ue(e, n, r) {
188
268
  let f = new N(d.x, d.y);
189
269
  return d.free(), f;
190
270
  }
191
- function de() {
192
- return S();
271
+ function Te() {
272
+ return re();
193
273
  }
194
- function fe() {
195
- return C();
274
+ function Ee() {
275
+ return ie();
196
276
  }
197
- function pe() {
198
- return b();
277
+ function De() {
278
+ return te();
199
279
  }
200
- function me() {
201
- return x();
280
+ function Oe() {
281
+ return ne();
202
282
  }
203
- function he() {
204
- return y();
283
+ function ke() {
284
+ return ee();
205
285
  }
206
286
  //#endregion
207
- export { q as EllipsoidGeodesic, V as RTE_ONE_UNIFORM, K as calcCameraPosition, ne as calcModelMatrixRTE, U as composeWorldMatrixForRTE, ie as convertScreenToWorld, ue as convertWorldToScreen, Z as degreeToRadian, oe as eastNorthUpToFixedFrame, H as encodePositionRTE, ae as geodeticSurfaceNormal, Y as geodeticToVector3, B as getHeightFromEllipsoid, R as getPickRay, L as getPlaneFromPointNormal, z as getRayPlaneIntersection, he as getWGS84Eccentricity, pe as getWGS84EccentricitySquared, me as getWGS84Flattening, de as getWGS84SemiMajorAxis, fe as getWGS84SemiMinorAxis, le as headingPitchRollToFixedFrame, J as initNavaraApi, se as northEastDownToFixedFrame, Q as northUpEastToFixedFrame, ce as northWestUpToFixedFrame, re as radianToDegree, X as vector3ToGeodetic, $ as westUpNorthToFixedFrame };
287
+ export { pe as EllipsoidGeodesic, ce as RTE_ONE_UNIFORM, B as buildPolygonGeometry, V as buildPolylineGeometry, fe as calcCameraPosition, ue as calcModelMatrixRTE, de as composeWorldMatrixForRTE, ve as convertScreenToWorld, we as convertWorldToScreen, ge as degreeToRadian, xe as eastNorthUpToFixedFrame, le as encodePositionRTE, ye as geodeticSurfaceNormal, X as geodeticToVector3, se as getHeightFromEllipsoid, K as getPickRay, G as getPlaneFromPointNormal, q as getRayPlaneIntersection, ke as getWGS84Eccentricity, De as getWGS84EccentricitySquared, Oe as getWGS84Flattening, Te as getWGS84SemiMajorAxis, Ee as getWGS84SemiMinorAxis, $ as headingPitchRollToFixedFrame, me as initNavaraApi, Se as northEastDownToFixedFrame, Z as northUpEastToFixedFrame, Ce as northWestUpToFixedFrame, U as polygonGroundVolume, W as polylineGroundVolume, _e as radianToDegree, be as scaleToGeodeticSurface, he as vector3ToGeodetic, Q as westUpNorthToFixedFrame };
@@ -1 +1 @@
1
- (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("@navaramap/engine-api"),require("three")):typeof define==`function`&&define.amd?define([`exports`,`@navaramap/engine-api`,`three`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.NavaraThreeApi={},e._navaramap_engine_api,e.three))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(t||!e||!e.__esModule||!c.call(e,`default`)?i(n,`default`,{value:e,enumerable:!0}):n,e)))(t,1);function u(){return new n.Matrix4().makeRotationY(Math.PI/2)}function d(e,t){let r=new n.Matrix4().multiplyMatrices(e,new n.Matrix4().makeRotationFromEuler(new n.Euler(-n.MathUtils.degToRad(t.pitch??0),-n.MathUtils.degToRad(t.heading??0),n.MathUtils.degToRad(t.roll??0),`YXZ`)));if(t.scale!==void 0){let e=t.scale;r.scale(typeof e==`number`?new n.Vector3(e,e,e):new n.Vector3(e.x,e.y,e.z))}return r}function f(e,r){let i=new t.Vec3(e.x,e.y,e.z),a=new t.Vec3(r.x,r.y,r.z),o=(0,t.getPlaneFromPointNormal)(i,a),s=new n.Vector3(o.normal.x,o.normal.y,o.normal.z),c=new n.Plane(s,-o.distance);return o.free(),c}function p(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),s=new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),c=(0,t.getPickRay)(a,o,s,new t.Vec2(i.x,i.y)),l=new n.Vector3(c.origin.x,c.origin.y,c.origin.z),u=new n.Vector3(c.direction.x,c.direction.y,c.direction.z),d=new n.Ray(l,u);return c.free(),d}function m(e,r){let i=new t.Ray(new t.Vec3(e.origin.x,e.origin.y,e.origin.z),new t.Vec3(e.direction.x,e.direction.y,e.direction.z)),a=(0,t.getPlaneFromPointNormal)(new t.Vec3(r.normal.x*-r.constant,r.normal.y*-r.constant,r.normal.z*-r.constant),new t.Vec3(r.normal.x,r.normal.y,r.normal.z)),o=(0,t.getRayPlaneIntersection)(i,a);if(o){let e=new n.Vector3(o.x,o.y,o.z);return o.free(),e}}function h(e){let n=new t.Vec3(e.x,e.y,e.z);return(0,t.getHeightFromEllipsoid)(n)}var g=Object.freeze({value:1});function _(e,r=new n.Vector3,i=new n.Vector3){let a=(0,t.encodePosition)(e.x,e.y,e.z);return r.set(a.high.x,a.high.y,a.high.z),i.set(a.low.x,a.low.y,a.low.z),a.free(),{high:r,low:i}}var v=(e,t,r=new n.Matrix4)=>{r.multiplyMatrices(t,e);let i=r.elements;return i[12]=0,i[13]=0,i[14]=0,r};function y(e,t,r=new n.Vector3,i=new n.Matrix4){i.multiplyMatrices(e,t),r.setFromMatrixPosition(i);let a=i.elements;return a[12]=0,a[13]=0,a[14]=0,{position:r,rotationScale:i}}var b=new n.Matrix4,x=new n.Vector3,S=(e,r)=>{b.copy(r).invert(),x.copy(e).applyMatrix4(b);let i=(0,t.encodePosition)(x.x,x.y,x.z),a=i.high,o=i.low,s={high:new n.Vector3(a.x,a.y,a.z),low:new n.Vector3(o.x,o.y,o.z)};return i.free(),s},C=class{_raw;constructor(e,n){this._raw=new t.EllipsoidGeodesic(new t.LLE((0,t.angleToRadian)(e.lat),(0,t.angleToRadian)(e.lng),e.height),new t.LLE((0,t.angleToRadian)(n.lat),(0,t.angleToRadian)(n.lng),n.height))}get distance(){return this._raw.distance}get converged(){return this._raw.converged}get startHeading(){return(0,t.angleToDegree)(this._raw.start_heading)}get endHeading(){return(0,t.angleToDegree)(this._raw.end_heading)}get start(){let e=this._raw.start;return{lat:(0,t.angleToDegree)(e.lat),lng:(0,t.angleToDegree)(e.lng),height:e.height}}get end(){let e=this._raw.end;return{lat:(0,t.angleToDegree)(e.lat),lng:(0,t.angleToDegree)(e.lng),height:e.height}}interpolatePoints(e){let n=this._raw.interpolateGeodeticPoints(e??null),r=[];for(let e of n)r.push({lat:(0,t.angleToDegree)(e.lat),lng:(0,t.angleToDegree)(e.lng),height:e.height}),e.free();return r}interpolateDistance(e){let n=this._raw.interpolateDistance(e),r={lat:(0,t.angleToDegree)(n.lat),lng:(0,t.angleToDegree)(n.lng),height:n.height};return n.free(),r}dispose(){this._raw.free()}};async function w(){await(0,t.default)()}function T(e){let r=(0,t.geodeticToXyz)(new t.LLE((0,t.angleToRadian)(e.lat),(0,t.angleToRadian)(e.lng),e.height)),i=new n.Vector3(r.x,r.y,r.z);return r.free(),i}function E(e){let n=new t.Vec3(e.x,e.y,e.z),r=(0,t.xyzToGeodetic)(n),i={lat:(0,t.angleToDegree)(r.lat),lng:(0,t.angleToDegree)(r.lng),height:r.height};return r.free(),i}function D(e){return(0,t.angleToRadian)(e)}function O(e){return(0,t.angleToDegree)(e)}function k(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),s=new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),c=(0,t.screenToWorld)(a,o,s,new t.Vec2(i.x,i.y));if(!c)return;let l=new n.Vector3(c.x,c.y,c.z);return c.free(),l}function A(e){let r=(0,t.geodeticSurfaceNormal)(new t.LLE((0,t.angleToRadian)(e.lat),(0,t.angleToRadian)(e.lng),e.height)),i=new n.Vector3(r.x,r.y,r.z);return r.free(),i}function j(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.eastNorthUpToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function M(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.northEastDownToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function N(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.northUpEastToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function P(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.northWestUpToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function F(e){return N(e).multiply(u())}function I(e){return d(F(T({lng:e.lng,lat:e.lat,height:e.height??0})),e)}function L(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),s=new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),c=(0,t.worldToScreen)(a,o,s,new t.Vec3(i.x,i.y,i.z));if(!c)return;let l=new n.Vector2(c.x,c.y);return c.free(),l}function R(){return(0,t.getWGS84SemiMajorAxis)()}function z(){return(0,t.getWGS84SemiMinorAxis)()}function B(){return(0,t.getWGS84EccentricitySquared)()}function V(){return(0,t.getWGS84Flattening)()}function H(){return(0,t.getWGS84Eccentricity)()}e.EllipsoidGeodesic=C,e.RTE_ONE_UNIFORM=g,e.calcCameraPosition=S,e.calcModelMatrixRTE=v,e.composeWorldMatrixForRTE=y,e.convertScreenToWorld=k,e.convertWorldToScreen=L,e.degreeToRadian=D,e.eastNorthUpToFixedFrame=j,e.encodePositionRTE=_,e.geodeticSurfaceNormal=A,e.geodeticToVector3=T,e.getHeightFromEllipsoid=h,e.getPickRay=p,e.getPlaneFromPointNormal=f,e.getRayPlaneIntersection=m,e.getWGS84Eccentricity=H,e.getWGS84EccentricitySquared=B,e.getWGS84Flattening=V,e.getWGS84SemiMajorAxis=R,e.getWGS84SemiMinorAxis=z,e.headingPitchRollToFixedFrame=I,e.initNavaraApi=w,e.northEastDownToFixedFrame=M,e.northUpEastToFixedFrame=N,e.northWestUpToFixedFrame=P,e.radianToDegree=O,e.vector3ToGeodetic=E,e.westUpNorthToFixedFrame=F});
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("@navaramap/engine-api"),require("three")):typeof define==`function`&&define.amd?define([`exports`,`@navaramap/engine-api`,`three`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.NavaraThreeApi={},e._navaramap_engine_api,e.three))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(t||!e||!e.__esModule||!c.call(e,`default`)?i(n,`default`,{value:e,enumerable:!0}):n,e)))(t,1);function u(){return new n.Matrix4().makeRotationY(Math.PI/2)}function d(e,t){let r=new n.Matrix4().multiplyMatrices(e,new n.Matrix4().makeRotationFromEuler(new n.Euler(-n.MathUtils.degToRad(t.pitch??0),-n.MathUtils.degToRad(t.heading??0),n.MathUtils.degToRad(t.roll??0),`YXZ`)));if(t.scale!==void 0){let e=t.scale;r.scale(typeof e==`number`?new n.Vector3(e,e,e):new n.Vector3(e.x,e.y,e.z))}return r}function f(e){return{west:(0,t.angleToDegree)(e.west),south:(0,t.angleToDegree)(e.south),east:(0,t.angleToDegree)(e.east),north:(0,t.angleToDegree)(e.north)}}function p(e){let t=0;for(let n of e)t+=n.length;let n=new Float64Array(t),r=0;for(let t of e)n.set(t,r),r+=t.length;return n}function m(e){let t=new Uint32Array(e.length);for(let n=0;n<e.length;n++)t[n]=e[n].length/3;return t}function h(e,n,r){let i=(0,t.buildPolygonGeometry)(p(e),m(e),n,r);if(i)try{let e=i.position_3d_high(),t=i.position_3d_low(),n=i.extent;if(!e||!t||!n)throw Error(`buildPolygonGeometry: incomplete RTE geometry`);let r=i.scale_normal_and_cap();if(!r)throw Error(`buildPolygonGeometry: missing extrusion attribute`);return{positionHigh:e,positionLow:t,normal:i.normal(),scaleNormalAndCap:r,indices:i.indices(),extent:f(n)}}finally{i.free()}}function g(e,n,r,i){let a=(0,t.buildPolylineGeometry)(e,n,r,i);if(a)try{let e=a.position_high(),t=a.position_low(),n=a.start_high(),r=a.start_low(),i=a.end_high(),o=a.end_low(),s=a.start_normals(),c=a.end_normal_and_texture_coordinate_normalization_x(),l=a.extent;if(!e||!t||!n||!r||!i||!o||!s||!c||!l)throw Error(`buildPolylineGeometry: incomplete RTE geometry`);return{position:a.position(),positionHigh:e,positionLow:t,startHigh:n,startLow:r,endHigh:i,endLow:o,startNormal:s,endNormalAndTextureCoordinateNormalizationX:c,rightNormalAndTextureCoordinateNormalizationY:a.right_normal_and_texture_coordinate_normalization_y(),indices:a.indices(),extent:f(l)}}finally{a.free()}}function _(e,n,r){let i=e(n.min,n.max,(0,t.angleToRadian)(r.west),(0,t.angleToRadian)(r.south),(0,t.angleToRadian)(r.east),(0,t.angleToRadian)(r.north));return[i[0],i[1]]}function v(e,n){return _(t.polygonGroundVolume,e,n)}function y(e,n){return _(t.polylineGroundVolume,e,n)}function b(e,r){let i=new t.Vec3(e.x,e.y,e.z),a=new t.Vec3(r.x,r.y,r.z),o=(0,t.getPlaneFromPointNormal)(i,a),s=new n.Vector3(o.normal.x,o.normal.y,o.normal.z),c=new n.Plane(s,-o.distance);return o.free(),c}function x(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),s=new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),c=(0,t.getPickRay)(a,o,s,new t.Vec2(i.x,i.y)),l=new n.Vector3(c.origin.x,c.origin.y,c.origin.z),u=new n.Vector3(c.direction.x,c.direction.y,c.direction.z),d=new n.Ray(l,u);return c.free(),d}function S(e,r){let i=new t.Ray(new t.Vec3(e.origin.x,e.origin.y,e.origin.z),new t.Vec3(e.direction.x,e.direction.y,e.direction.z)),a=(0,t.getPlaneFromPointNormal)(new t.Vec3(r.normal.x*-r.constant,r.normal.y*-r.constant,r.normal.z*-r.constant),new t.Vec3(r.normal.x,r.normal.y,r.normal.z)),o=(0,t.getRayPlaneIntersection)(i,a);if(o){let e=new n.Vector3(o.x,o.y,o.z);return o.free(),e}}function C(e){let n=new t.Vec3(e.x,e.y,e.z);return(0,t.getHeightFromEllipsoid)(n)}var w=Object.freeze({value:1});function T(e,r=new n.Vector3,i=new n.Vector3){let a=(0,t.encodePosition)(e.x,e.y,e.z);return r.set(a.high.x,a.high.y,a.high.z),i.set(a.low.x,a.low.y,a.low.z),a.free(),{high:r,low:i}}var E=(e,t,r=new n.Matrix4)=>{r.multiplyMatrices(t,e);let i=r.elements;return i[12]=0,i[13]=0,i[14]=0,r};function D(e,t,r=new n.Vector3,i=new n.Matrix4){i.multiplyMatrices(e,t),r.setFromMatrixPosition(i);let a=i.elements;return a[12]=0,a[13]=0,a[14]=0,{position:r,rotationScale:i}}var O=new n.Matrix4,k=new n.Vector3,A=(e,r)=>{O.copy(r).invert(),k.copy(e).applyMatrix4(O);let i=(0,t.encodePosition)(k.x,k.y,k.z),a=i.high,o=i.low,s={high:new n.Vector3(a.x,a.y,a.z),low:new n.Vector3(o.x,o.y,o.z)};return i.free(),s},j=class{_raw;constructor(e,n){this._raw=new t.EllipsoidGeodesic(new t.LLE((0,t.angleToRadian)(e.lat),(0,t.angleToRadian)(e.lng),e.height),new t.LLE((0,t.angleToRadian)(n.lat),(0,t.angleToRadian)(n.lng),n.height))}get distance(){return this._raw.distance}get converged(){return this._raw.converged}get startHeading(){return(0,t.angleToDegree)(this._raw.start_heading)}get endHeading(){return(0,t.angleToDegree)(this._raw.end_heading)}get start(){let e=this._raw.start;return{lat:(0,t.angleToDegree)(e.lat),lng:(0,t.angleToDegree)(e.lng),height:e.height}}get end(){let e=this._raw.end;return{lat:(0,t.angleToDegree)(e.lat),lng:(0,t.angleToDegree)(e.lng),height:e.height}}interpolatePoints(e){let n=this._raw.interpolateGeodeticPoints(e??null),r=[];for(let e of n)r.push({lat:(0,t.angleToDegree)(e.lat),lng:(0,t.angleToDegree)(e.lng),height:e.height}),e.free();return r}interpolateDistance(e){let n=this._raw.interpolateDistance(e),r={lat:(0,t.angleToDegree)(n.lat),lng:(0,t.angleToDegree)(n.lng),height:n.height};return n.free(),r}dispose(){this._raw.free()}};async function M(){await(0,t.default)()}function N(e){let r=(0,t.geodeticToXyz)(new t.LLE((0,t.angleToRadian)(e.lat),(0,t.angleToRadian)(e.lng),e.height)),i=new n.Vector3(r.x,r.y,r.z);return r.free(),i}function P(e){let n=new t.Vec3(e.x,e.y,e.z),r=(0,t.xyzToGeodetic)(n),i={lat:(0,t.angleToDegree)(r.lat),lng:(0,t.angleToDegree)(r.lng),height:r.height};return r.free(),i}function F(e){return(0,t.angleToRadian)(e)}function I(e){return(0,t.angleToDegree)(e)}function L(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),s=new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),c=(0,t.screenToWorld)(a,o,s,new t.Vec2(i.x,i.y));if(!c)return;let l=new n.Vector3(c.x,c.y,c.z);return c.free(),l}function R(e){let r=(0,t.geodeticSurfaceNormal)(new t.LLE((0,t.angleToRadian)(e.lat),(0,t.angleToRadian)(e.lng),e.height)),i=new n.Vector3(r.x,r.y,r.z);return r.free(),i}function z(e){let r=(0,t.scaleToGeodeticSurface)(new t.Vec3(e.x,e.y,e.z));if(!r)return;let i=new n.Vector3(r.x,r.y,r.z);return r.free(),i}function B(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.eastNorthUpToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function V(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.northEastDownToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function H(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.northUpEastToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function U(e){let r=new t.Vec3(e.x,e.y,e.z),i=(0,t.northWestUpToFixedFrame)(r);return new n.Matrix4().fromArray(i)}function W(e){return H(e).multiply(u())}function G(e){return d(W(N({lng:e.lng,lat:e.lat,height:e.height??0})),e)}function K(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),s=new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),c=(0,t.worldToScreen)(a,o,s,new t.Vec3(i.x,i.y,i.z));if(!c)return;let l=new n.Vector2(c.x,c.y);return c.free(),l}function q(){return(0,t.getWGS84SemiMajorAxis)()}function J(){return(0,t.getWGS84SemiMinorAxis)()}function Y(){return(0,t.getWGS84EccentricitySquared)()}function X(){return(0,t.getWGS84Flattening)()}function Z(){return(0,t.getWGS84Eccentricity)()}e.EllipsoidGeodesic=j,e.RTE_ONE_UNIFORM=w,e.buildPolygonGeometry=h,e.buildPolylineGeometry=g,e.calcCameraPosition=A,e.calcModelMatrixRTE=E,e.composeWorldMatrixForRTE=D,e.convertScreenToWorld=L,e.convertWorldToScreen=K,e.degreeToRadian=F,e.eastNorthUpToFixedFrame=B,e.encodePositionRTE=T,e.geodeticSurfaceNormal=R,e.geodeticToVector3=N,e.getHeightFromEllipsoid=C,e.getPickRay=x,e.getPlaneFromPointNormal=b,e.getRayPlaneIntersection=S,e.getWGS84Eccentricity=Z,e.getWGS84EccentricitySquared=Y,e.getWGS84Flattening=X,e.getWGS84SemiMajorAxis=q,e.getWGS84SemiMinorAxis=J,e.headingPitchRollToFixedFrame=G,e.initNavaraApi=M,e.northEastDownToFixedFrame=V,e.northUpEastToFixedFrame=H,e.northWestUpToFixedFrame=U,e.polygonGroundVolume=v,e.polylineGroundVolume=y,e.radianToDegree=I,e.scaleToGeodeticSurface=z,e.vector3ToGeodetic=P,e.westUpNorthToFixedFrame=W});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@navaramap/three-api",
3
3
  "type": "module",
4
- "version": "0.1.1",
4
+ "version": "0.2.0",
5
5
  "main": "dist/index.umd.cjs",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  "license": "MIT OR Apache-2.0",
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "git+https://github.com/reearth/navara.git",
15
+ "url": "git+https://github.com/maplibre/navara.git",
16
16
  "directory": "web/navara_three_api"
17
17
  },
18
18
  "publishConfig": {
@@ -22,11 +22,11 @@
22
22
  "three": ">=0.183.0"
23
23
  },
24
24
  "dependencies": {
25
- "@navaramap/core": "0.1.1",
26
- "@navaramap/engine-api": "0.1.1"
25
+ "@navaramap/core": "0.2.0",
26
+ "@navaramap/engine-api": "0.2.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@types/three": "0.185.1",
29
+ "@types/three": "0.185.4",
30
30
  "three": "0.185.1"
31
31
  },
32
32
  "scripts": {
@@ -0,0 +1,255 @@
1
+ import type { NormalizeWASMClass } from "@navaramap/core";
2
+ import {
3
+ buildPolygonGeometry as buildPolygonGeometryWasm,
4
+ buildPolylineGeometry as buildPolylineGeometryWasm,
5
+ polygonGroundVolume as polygonGroundVolumeWasm,
6
+ polylineGroundVolume as polylineGroundVolumeWasm,
7
+ angleToDegree,
8
+ angleToRadian,
9
+ type PolygonMaterial,
10
+ type PolylineMaterial,
11
+ } from "@navaramap/engine-api";
12
+
13
+ /**
14
+ * Geometry builders for standalone polygon and polyline meshes, using the
15
+ * vector layer's construction. Positions are RTE-encoded.
16
+ */
17
+
18
+ /** A polygon layer material, as a plain object. */
19
+ export type PolygonGeometryMaterial = NormalizeWASMClass<PolygonMaterial>;
20
+
21
+ /** A polyline layer material, as a plain object. */
22
+ export type PolylineGeometryMaterial = NormalizeWASMClass<PolylineMaterial>;
23
+
24
+ /** A geographic bounding box, in degrees. */
25
+ export type GeographicExtent = {
26
+ west: number;
27
+ south: number;
28
+ east: number;
29
+ north: number;
30
+ };
31
+
32
+ export type PolygonGeometryData = {
33
+ positionHigh: Float32Array;
34
+ positionLow: Float32Array;
35
+ /** Absent for a `clampToGround` polygon. */
36
+ normal?: Float32Array;
37
+ /** `xyz` = extrusion direction, `w` = 0 on the bottom cap, 1 on the top. */
38
+ scaleNormalAndCap: Float32Array;
39
+ indices: Uint32Array;
40
+ extent: GeographicExtent;
41
+ };
42
+
43
+ export type PolylineGeometryData = {
44
+ position: Float32Array;
45
+ positionHigh: Float32Array;
46
+ positionLow: Float32Array;
47
+ startHigh: Float32Array;
48
+ startLow: Float32Array;
49
+ endHigh: Float32Array;
50
+ endLow: Float32Array;
51
+ startNormal: Float32Array;
52
+ endNormalAndTextureCoordinateNormalizationX: Float32Array;
53
+ rightNormalAndTextureCoordinateNormalizationY: Float32Array;
54
+ indices: Uint32Array;
55
+ extent: GeographicExtent;
56
+ };
57
+
58
+ /** Flat `[lng, lat, height, ...]`, or ECEF `[x, y, z, ...]` when geocentric. */
59
+ export type GeometryRing = Float64Array;
60
+
61
+ function toDegrees(extent: {
62
+ west: number;
63
+ south: number;
64
+ east: number;
65
+ north: number;
66
+ }): GeographicExtent {
67
+ return {
68
+ west: angleToDegree(extent.west),
69
+ south: angleToDegree(extent.south),
70
+ east: angleToDegree(extent.east),
71
+ north: angleToDegree(extent.north),
72
+ };
73
+ }
74
+
75
+ /** Concatenates rings; the builder splits them by `ringLengths`. */
76
+ function concatRings(rings: readonly GeometryRing[]): Float64Array {
77
+ let total = 0;
78
+ for (const ring of rings) total += ring.length;
79
+
80
+ const coordinates = new Float64Array(total);
81
+ let offset = 0;
82
+ for (const ring of rings) {
83
+ coordinates.set(ring, offset);
84
+ offset += ring.length;
85
+ }
86
+ return coordinates;
87
+ }
88
+
89
+ function ringLengths(rings: readonly GeometryRing[]): Uint32Array {
90
+ const lengths = new Uint32Array(rings.length);
91
+ for (let i = 0; i < rings.length; i++) {
92
+ lengths[i] = rings[i].length / 3;
93
+ }
94
+ return lengths;
95
+ }
96
+
97
+ /**
98
+ * Builds the geometry of one standalone polygon.
99
+ *
100
+ * @param rings - Outer ring first, then holes.
101
+ * @param geocentric - Read the positions as ECEF metres.
102
+ * @param material - A `polygon` layer material. Outline options are ignored.
103
+ * @throws When `material` does not deserialize.
104
+ * @returns `undefined` when the outer ring has fewer than three vertices or is
105
+ * collinear.
106
+ */
107
+ export function buildPolygonGeometry(
108
+ rings: readonly GeometryRing[],
109
+ geocentric: boolean,
110
+ material: PolygonGeometryMaterial,
111
+ ): PolygonGeometryData | undefined {
112
+ const built = buildPolygonGeometryWasm(
113
+ concatRings(rings),
114
+ ringLengths(rings),
115
+ geocentric,
116
+ material,
117
+ );
118
+ if (!built) return undefined;
119
+
120
+ try {
121
+ const positionHigh = built.position_3d_high();
122
+ const positionLow = built.position_3d_low();
123
+ const extent = built.extent;
124
+ if (!positionHigh || !positionLow || !extent) {
125
+ throw new Error("buildPolygonGeometry: incomplete RTE geometry");
126
+ }
127
+
128
+ const scaleNormalAndCap = built.scale_normal_and_cap();
129
+ if (!scaleNormalAndCap) {
130
+ throw new Error("buildPolygonGeometry: missing extrusion attribute");
131
+ }
132
+
133
+ return {
134
+ positionHigh,
135
+ positionLow,
136
+ normal: built.normal(),
137
+ scaleNormalAndCap,
138
+ indices: built.indices(),
139
+ extent: toDegrees(extent),
140
+ };
141
+ } finally {
142
+ built.free();
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Builds the geometry of one standalone polyline.
148
+ *
149
+ * @param positions - Flat `[lng, lat, height, ...]`, or ECEF when `geocentric`.
150
+ * @param geocentric - Read the positions as ECEF metres.
151
+ * @param ring - Join a repeated first vertex as a seam instead of two caps.
152
+ * @param material - A `polyline` layer material.
153
+ * @throws When `material` does not deserialize.
154
+ * @returns `undefined` when fewer than two distinct positions remain.
155
+ */
156
+ export function buildPolylineGeometry(
157
+ positions: Float64Array,
158
+ geocentric: boolean,
159
+ ring: boolean,
160
+ material: PolylineGeometryMaterial,
161
+ ): PolylineGeometryData | undefined {
162
+ const built = buildPolylineGeometryWasm(
163
+ positions,
164
+ geocentric,
165
+ ring,
166
+ material,
167
+ );
168
+ if (!built) return undefined;
169
+
170
+ try {
171
+ const positionHigh = built.position_high();
172
+ const positionLow = built.position_low();
173
+ const startHigh = built.start_high();
174
+ const startLow = built.start_low();
175
+ const endHigh = built.end_high();
176
+ const endLow = built.end_low();
177
+ const startNormal = built.start_normals();
178
+ const endNormal = built.end_normal_and_texture_coordinate_normalization_x();
179
+ const extent = built.extent;
180
+ if (
181
+ !positionHigh ||
182
+ !positionLow ||
183
+ !startHigh ||
184
+ !startLow ||
185
+ !endHigh ||
186
+ !endLow ||
187
+ !startNormal ||
188
+ !endNormal ||
189
+ !extent
190
+ ) {
191
+ throw new Error("buildPolylineGeometry: incomplete RTE geometry");
192
+ }
193
+
194
+ return {
195
+ position: built.position(),
196
+ positionHigh,
197
+ positionLow,
198
+ startHigh,
199
+ startLow,
200
+ endHigh,
201
+ endLow,
202
+ startNormal,
203
+ endNormalAndTextureCoordinateNormalizationX: endNormal,
204
+ rightNormalAndTextureCoordinateNormalizationY:
205
+ built.right_normal_and_texture_coordinate_normalization_y(),
206
+ indices: built.indices(),
207
+ extent: toDegrees(extent),
208
+ };
209
+ } finally {
210
+ built.free();
211
+ }
212
+ }
213
+
214
+ /** A height range in metres, relative to the ellipsoid. */
215
+ export type HeightRange = { min: number; max: number };
216
+
217
+ function groundVolume(
218
+ build: typeof polygonGroundVolumeWasm,
219
+ range: HeightRange,
220
+ extent: GeographicExtent,
221
+ ): [number, number] {
222
+ const heights = build(
223
+ range.min,
224
+ range.max,
225
+ angleToRadian(extent.west),
226
+ angleToRadian(extent.south),
227
+ angleToRadian(extent.east),
228
+ angleToRadian(extent.north),
229
+ );
230
+ return [heights[0], heights[1]];
231
+ }
232
+
233
+ /**
234
+ * `[min, max]` height a clamp-to-ground polygon volume over `extent` must
235
+ * span to enclose the ground heights `range`, given the flat cap triangles
236
+ * {@link buildPolygonGeometry} produces.
237
+ */
238
+ export function polygonGroundVolume(
239
+ range: HeightRange,
240
+ extent: GeographicExtent,
241
+ ): [number, number] {
242
+ return groundVolume(polygonGroundVolumeWasm, range, extent);
243
+ }
244
+
245
+ /**
246
+ * `[min, max]` height a clamp-to-ground polyline volume over `extent` must
247
+ * span to enclose the ground heights `range`, given the straight segments
248
+ * {@link buildPolylineGeometry} produces.
249
+ */
250
+ export function polylineGroundVolume(
251
+ range: HeightRange,
252
+ extent: GeographicExtent,
253
+ ): [number, number] {
254
+ return groundVolume(polylineGroundVolumeWasm, range, extent);
255
+ }
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ import initApi, {
13
13
  screenToWorld,
14
14
  worldToScreen,
15
15
  geodeticSurfaceNormal as nvGeodeticSurfaceNormal,
16
+ scaleToGeodeticSurface as nvScaleToGeodeticSurface,
16
17
  eastNorthUpToFixedFrame as nvEastNorthUpToFixedFrame,
17
18
  northEastDownToFixedFrame as nvNorthEastDownToFixedFrame,
18
19
  northUpEastToFixedFrame as nvNorthUpEastToFixedFrame,
@@ -43,6 +44,7 @@ export type {
43
44
  } from "@navaramap/core";
44
45
  export type { GeodeticPlacement } from "./frames";
45
46
 
47
+ export * from "./featureGeometry";
46
48
  export * from "./intersection";
47
49
  export * from "./rte";
48
50
  export * from "./ellipsoidGeodesic";
@@ -173,6 +175,23 @@ export function geodeticSurfaceNormal(lle: LatLngHeight): Vector3 {
173
175
  return result;
174
176
  }
175
177
 
178
+ /**
179
+ * Projects a Cartesian position onto the WGS84 ellipsoid surface along the
180
+ * geodetic normal.
181
+ * @param xyz - Cartesian Vector3 in Earth-Centered Earth-Fixed (ECEF) coordinates
182
+ * @returns The surface position in ECEF coordinates, or undefined when the
183
+ * position is too close to the ellipsoid's center to project
184
+ */
185
+ export function scaleToGeodeticSurface(xyz: Vector3): Vector3 | undefined {
186
+ const pos = nvScaleToGeodeticSurface(new Vec3(xyz.x, xyz.y, xyz.z));
187
+ if (!pos) {
188
+ return undefined;
189
+ }
190
+ const result = new Vector3(pos.x, pos.y, pos.z);
191
+ pos.free();
192
+ return result;
193
+ }
194
+
176
195
  /**
177
196
  * Creates a local East-North-Up (ENU) reference frame transformation matrix at a position.
178
197
  * @param origin - Origin position in ECEF coordinates