gps-plus-slam-js 1.3.0 → 1.5.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/dist/index.d.ts CHANGED
@@ -133,6 +133,27 @@ interface RawDeviceOrientation {
133
133
  /** Whether alpha is relative to magnetic north. */
134
134
  absolute: boolean;
135
135
  }
136
+ /**
137
+ * Raw `AbsoluteOrientationSensor` (Generic Sensor API) reading — an
138
+ * Earth-referenced (magnetic ENU) device orientation, captured per GPS event as
139
+ * a GPS-independent north reference. See
140
+ * GpsPlusSlamJs_Docs/docs/2026-06-25-absolute-orientation-sensor-plan.md §5.
141
+ *
142
+ * Unlike {@link RawDeviceOrientation} (whose `absolute` flag is `false` on most
143
+ * Android browsers), this quaternion is magnetometer-fused and meaningfully
144
+ * absolute. Stored verbatim — magnetic→true correction (WMM declination) and the
145
+ * AR→north composition happen offline/at-consumption, not here.
146
+ */
147
+ interface RawAbsoluteOrientation {
148
+ /** [x,y,z,w] quaternion mapping device-frame → Earth ENU (+X East, +Y (magnetic) North, +Z Up). */
149
+ quaternion: Quaternion;
150
+ /** Reference frame the quaternion is expressed in. We capture the raw `'device'` frame. */
151
+ referenceFrame: 'device' | 'screen';
152
+ /** `screen.orientation.angle` at sample time — needed to reconstruct the `'device'` frame. */
153
+ screenAngleDeg: number;
154
+ /** Sensor reading time (epoch ms) — lets magnetometer warm-up be detected offline. */
155
+ sampleTimestamp: number;
156
+ }
136
157
  /**
137
158
  * Convert Euler angles from W3C DeviceOrientationEvent to a Quaternion.
138
159
  * Rotation order: ZXY intrinsic (per W3C DeviceOrientation spec §3.1).
@@ -166,6 +187,19 @@ interface GpsEventsState {
166
187
  gpsAccuracyMedian: number | null;
167
188
  gpsAccuracyMean: number | null;
168
189
  currentGpsPosGeoHash: string | null;
190
+ /**
191
+ * Odometry position + GPS timestamp of the last sample admitted into the
192
+ * stored history (Proposal B distance-gating). Null until the first sample
193
+ * and after any rebuild that empties the history. Always reflects the last
194
+ * *stored* sample (the only current reason to skip storing is gating itself);
195
+ * the incremental path reads it to gate the next sample, and the batch path
196
+ * recomputes it from the stored arrays — which is why both origin-jump
197
+ * handlers (which rebuild via the batch path) reset it for free. Kept
198
+ * serializable (plain tuple) so it survives Redux round-trips. Only
199
+ * meaningful when `useDistanceGating` is on.
200
+ */
201
+ lastAdmittedOdomPos: Vector3 | null;
202
+ lastAdmittedTs: number | null;
169
203
  }
170
204
  //#endregion
171
205
  //#region ../src/state/types.d.ts
@@ -221,6 +255,13 @@ interface GpsPoint extends RawGpsPoint {
221
255
  coordinates: Vector3;
222
256
  readonly weight: number;
223
257
  deviceRotation?: Quaternion;
258
+ /**
259
+ * Raw AbsoluteOrientationSensor reading captured with this GPS event — the
260
+ * GPS-independent (magnetic ENU) device orientation. Paired 1:1 with the
261
+ * event's `odomRotation`, so the offline AR→north composition is exact.
262
+ * `undefined` on recordings/devices without the sensor (most non-Chrome-Android).
263
+ */
264
+ absoluteOrientation?: RawAbsoluteOrientation;
224
265
  }
225
266
  interface GpsMarker {
226
267
  id: string;
@@ -323,6 +364,12 @@ interface RecordGpsEventPayload {
323
364
  odomRotation: Quaternion;
324
365
  rawGpsPoint: RawGpsPoint;
325
366
  rawDeviceOrientation?: RawDeviceOrientation;
367
+ /**
368
+ * Raw AbsoluteOrientationSensor reading at GPS-event time (Phase 1 capture).
369
+ * Optional ⇒ old recordings remain valid untouched; the field is simply absent
370
+ * on devices/platforms without the sensor.
371
+ */
372
+ rawAbsoluteOrientation?: RawAbsoluteOrientation;
326
373
  }
327
374
  interface OdometryTrackingRestartedPayload {
328
375
  lastValidOdomPos: Vector3;
@@ -495,6 +542,106 @@ declare const getOpenStreetMapLink: (coord: LatLong, zoomLevel?: number) => stri
495
542
  */
496
543
  declare const fusedGpsFromOdom: (alignmentMatrix: Matrix4, odomPosition: Vector3, zeroRef: LatLong) => LatLongAlt;
497
544
  //#endregion
545
+ //#region ../src/math/magnetic-declination.d.ts
546
+ /**
547
+ * Pure, dependency-free World Magnetic Model 2025 (WMM2025) magnetic-declination
548
+ * synthesis.
549
+ *
550
+ * Sign convention (WMM): declination D is the angle of the horizontal magnetic
551
+ * field measured clockwise (EAST) from geographic (true) north, EAST-positive.
552
+ * To convert a compass (magnetic) heading to a true heading:
553
+ *
554
+ * trueHeading = normalize(magneticHeading + D)
555
+ *
556
+ * Example: a magnetic heading of 90 deg at a location with D = +5 deg (5 deg
557
+ * EAST) corresponds to a true heading of 95 deg.
558
+ *
559
+ * The Gauss coefficients below are the official WMM2025 model (epoch 2025.0,
560
+ * valid 2025.0-2030.0), transcribed verbatim from the NOAA-distributed
561
+ * WMM.COF (header "2025.0 WMM-2025 11/13/2024"). Source mirror:
562
+ * https://github.com/boxpet/pygeomag (pygeomag/wmm/WMM_2025.COF)
563
+ * Acceptance oracle (official NOAA test values):
564
+ * https://www.ncei.noaa.gov/sites/default/files/2025-02/WMM2025testvalues.pdf
565
+ *
566
+ * The synthesis follows the standard WMM technical-report algorithm: decimal
567
+ * year -> time-adjusted Gauss coefficients -> geodetic-to-geocentric mapping
568
+ * (WGS-84) -> Schmidt semi-normalized associated Legendre functions ->
569
+ * spherical-harmonic field components (X north, Y east, Z down) ->
570
+ * D = atan2(Y, X).
571
+ */
572
+ /** Model epoch (decimal year) of WMM2025. */
573
+ declare const WMM2025_EPOCH = 2025;
574
+ /** Validity window of WMM2025 (decimal years), inclusive lower / exclusive upper. */
575
+ declare const WMM2025_VALID_FROM = 2025;
576
+ declare const WMM2025_VALID_TO = 2030;
577
+ /**
578
+ * Magnetic declination in degrees, EAST-positive (WMM convention).
579
+ * D = atan2(Y, X) where X is geodetic-north and Y is geodetic-east field.
580
+ *
581
+ * @param latDeg geodetic latitude in degrees (-90..90)
582
+ * @param lonDeg geodetic longitude in degrees (-180..180)
583
+ * @param opts.altitudeKm height above WGS-84 ellipsoid in km (default 0)
584
+ * @param opts.date Date or epoch-millis; defaults to a fixed in-model decimal year
585
+ * @returns declination in degrees, or NaN if inputs are non-finite.
586
+ */
587
+ declare function magneticDeclinationDeg(latDeg: number, lonDeg: number, opts?: {
588
+ altitudeKm?: number;
589
+ date?: Date | number;
590
+ }): number;
591
+ /**
592
+ * Convert a magnetic heading to a true heading:
593
+ * trueHeading = normalize(magneticDeg + D)
594
+ * where D is the magnetic declination (EAST-positive). Result is in [0, 360).
595
+ */
596
+ declare function magneticToTrueHeadingDeg(magneticDeg: number, latDeg: number, lonDeg: number, opts?: {
597
+ altitudeKm?: number;
598
+ date?: Date | number;
599
+ }): number;
600
+ //#endregion
601
+ //#region ../src/math/orientation-heading.d.ts
602
+ /**
603
+ * Magnetic heading in degrees (0 = North, 90 = East, clockwise) from a
604
+ * device→ENU quaternion, derived by rotating the device camera-forward (−Z)
605
+ * into ENU and taking its horizontal bearing.
606
+ *
607
+ * Returns `null` when the camera points within ~5° of straight up/down, where
608
+ * the horizontal projection is too small for a stable bearing (the v3 guard).
609
+ *
610
+ * This is an *indicative* read-out (the exact forward axis depends on how the
611
+ * phone is held); the rigorous AR→north comparison is {@link arNorthBearingDeg}.
612
+ */
613
+ declare function magneticHeadingFromEnuQuat(q: Quaternion): number | null;
614
+ /**
615
+ * The composed AR→ENU rotation: maps a vector expressed in the AR (WebXR)
616
+ * frame into the magnetic ENU frame, using **only** the sensor reading and the
617
+ * AR pose — never GPS.
618
+ *
619
+ * q(AR→ENU) = q(device→ENU)_sensor ∘ q(device→AR)⁻¹_arPose
620
+ *
621
+ * This is the GPS-independent estimate of the alignment's own rotation (plan §3).
622
+ * Both inputs ride in the same recorded GPS event, so pairing is exact.
623
+ */
624
+ declare function arToEnuRotation(qSensorDeviceToEnu: Quaternion, qArPoseDeviceToAr: Quaternion): Quaternion;
625
+ /**
626
+ * Magnetic bearing (deg, 0 = North, 90 = East, clockwise) that the AR frame's
627
+ * north axis (−Z) points toward, computed independently of GPS from the sensor
628
+ * quaternion + the AR pose. This is the "AR→north" yaw the Phase-2 harness
629
+ * compares against the GPS-derived alignment matrix's yaw (plan §3, §4c kernel 4).
630
+ *
631
+ * Returns `null` if the AR-north axis maps near-vertical in ENU (degenerate,
632
+ * e.g. a physically impossible pose); horizontal otherwise.
633
+ *
634
+ * NOTE: this is a **magnetic** bearing. Correct to true north via the WMM
635
+ * declination module before comparing against a geographic-ENU alignment.
636
+ */
637
+ declare function arNorthBearingDeg(qSensorDeviceToEnu: Quaternion, qArPoseDeviceToAr: Quaternion): number | null;
638
+ /**
639
+ * Signed smallest-magnitude difference between two bearings, in (−180, 180].
640
+ * Positive = `a` is clockwise (east) of `b`. Useful for the AR→north vs
641
+ * alignment-yaw discrepancy (plan §3) and the Phase-4 trust comparisons.
642
+ */
643
+ declare function bearingDeltaDeg(a: number, b: number): number;
644
+ //#endregion
498
645
  //#region ../src/state/selectors.d.ts
499
646
  /** Returns the 4×4 alignment matrix, or null if not yet computed. */
500
647
  declare const getAlignmentMatrix: (state: GpsSlamState) => Matrix4 | null;
@@ -534,4 +681,4 @@ declare function validateLicenseKey(key: string): LicenseValidationResult;
534
681
  //#region ../src/index.d.ts
535
682
  declare const LIB_VERSION = "1.0.0";
536
683
  //#endregion
537
- export { type Add2dImagePayload, type ArElementsState, type ArImageCapture, type ArLoopClosureDetectedPayload, type AreaPolygon, type CreateGpsSlamStoreOptions, type DetectedArPlane, type EventArea, type FloorDetection, type GpsElementsState, type GpsLine, type GpsMarker, type GpsModel, type GpsPoint, type GpsSlamDispatch, type GpsSlamState, type GpsSlamStore, type HeatArea, type HeatMapTile, IDENTITY_MATRIX4, IDENTITY_QUATERNION, LIB_VERSION, type LatLong, type LatLongAlt, type LicenseValidationResult, type Matrix4, type OdometryPath, type OdometryTrackingRestartedPayload, type Quaternion, type RawDeviceOrientation, type RawGpsPoint, type RecordGpsEventPayload, type RootState, type Vector3, type Vector4, ZERO_VECTOR3, add2dImage, addArea, addCornerToEventArea, addHeatMapArea, addLine, addMarker, addOrUpdateArPlane, addToHeatMaps, anchorEventAreaInWorldSpace, arElementsReducer, arLoopClosureDetected, calcGeoHash, calcGpsCoords, calcQuadKey, calcRelativeCoordsInMeters, cloneMatrix4, cloneQuaternion, cloneVector3, correctArDriftForEventArea, createGpsSlamStore, distanceInMeters, distanceInMetersRelative, enuQuaternionToNUE, eulerToQuaternion, finishEventArea, fromMatrix4, fromQuaternion, fromVector3, fusedGpsFromOdom, geoHashToLatLong, getAlignmentMatrix, getAlignmentRotation, getGoogleMapsDirectionsLink, getGoogleMapsLink, getGpsAccuracyStats, getGpsPositions, getOdometryPositions, getOdometryRotations, getOpenStreetMapLink, getZeroReference, gpsDataReducer, gpsElementsReducer, invertQuaternion, isIdentityMatrix4, isIdentityQuaternion, isNearIdentityQuaternion, multiplyQuaternions, normalizeQuaternion, nueQuaternionToENU, nueQuaternionToWebXR, nueToWebXR, odometryTrackingRestarted, quadKeyToLatLong, quaternionMagnitude, quaternionsEquivalent, recordGpsEvent, recordPhoneHeight, resetArElements, resetGpsElements, sanitizeForDevTools, setZeroPos, startEventArea, toEarthCenteredCoordinates, toMatrix4, toQuaternion, toVector3, validateLicenseKey, webxrQuaternionToNUE, webxrToNUE };
684
+ export { type Add2dImagePayload, type ArElementsState, type ArImageCapture, type ArLoopClosureDetectedPayload, type AreaPolygon, type CreateGpsSlamStoreOptions, type DetectedArPlane, type EventArea, type FloorDetection, type GpsElementsState, type GpsLine, type GpsMarker, type GpsModel, type GpsPoint, type GpsSlamDispatch, type GpsSlamState, type GpsSlamStore, type HeatArea, type HeatMapTile, IDENTITY_MATRIX4, IDENTITY_QUATERNION, LIB_VERSION, type LatLong, type LatLongAlt, type LicenseValidationResult, type Matrix4, type OdometryPath, type OdometryTrackingRestartedPayload, type Quaternion, type RawAbsoluteOrientation, type RawDeviceOrientation, type RawGpsPoint, type RecordGpsEventPayload, type RootState, type Vector3, type Vector4, WMM2025_EPOCH, WMM2025_VALID_FROM, WMM2025_VALID_TO, ZERO_VECTOR3, add2dImage, addArea, addCornerToEventArea, addHeatMapArea, addLine, addMarker, addOrUpdateArPlane, addToHeatMaps, anchorEventAreaInWorldSpace, arElementsReducer, arLoopClosureDetected, arNorthBearingDeg, arToEnuRotation, bearingDeltaDeg, calcGeoHash, calcGpsCoords, calcQuadKey, calcRelativeCoordsInMeters, cloneMatrix4, cloneQuaternion, cloneVector3, correctArDriftForEventArea, createGpsSlamStore, distanceInMeters, distanceInMetersRelative, enuQuaternionToNUE, eulerToQuaternion, finishEventArea, fromMatrix4, fromQuaternion, fromVector3, fusedGpsFromOdom, geoHashToLatLong, getAlignmentMatrix, getAlignmentRotation, getGoogleMapsDirectionsLink, getGoogleMapsLink, getGpsAccuracyStats, getGpsPositions, getOdometryPositions, getOdometryRotations, getOpenStreetMapLink, getZeroReference, gpsDataReducer, gpsElementsReducer, invertQuaternion, isIdentityMatrix4, isIdentityQuaternion, isNearIdentityQuaternion, magneticDeclinationDeg, magneticHeadingFromEnuQuat, magneticToTrueHeadingDeg, multiplyQuaternions, normalizeQuaternion, nueQuaternionToENU, nueQuaternionToWebXR, nueToWebXR, odometryTrackingRestarted, quadKeyToLatLong, quaternionMagnitude, quaternionsEquivalent, recordGpsEvent, recordPhoneHeight, resetArElements, resetGpsElements, sanitizeForDevTools, setZeroPos, startEventArea, toEarthCenteredCoordinates, toMatrix4, toQuaternion, toVector3, validateLicenseKey, webxrQuaternionToNUE, webxrToNUE };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /*! gps-plus-slam-js | (c) 2026 cs-util-com | UNLICENSED — see EULA.md */
2
- import{configureStore as e,createSlice as t}from"@reduxjs/toolkit";import{ed25519 as n}from"@noble/curves/ed25519.js";import{hexToBytes as r}from"@noble/curves/utils.js";import{mat4 as i,quat as a,vec3 as o,vec4 as s}from"gl-matrix";import{castDraft as c}from"immer";let l=!1;function u(){l=!0}function d(){if(!l)throw Error(`gps-plus-slam-js: license not activated. Construct your store via createRecorderStore() from gps-plus-slam-app-framework, or pass a valid licenseKey to createGpsSlamStore(). See EULA §3.`)}function f(e){return((...t)=>(d(),e(...t)))}function p(e){let t=((...t)=>(d(),e(...t)));return Object.assign(t,e),t.toString=()=>e.type,t}function m(e){return(t,n)=>(d(),e(t,n))}function h(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=`=`.repeat((4-t.length%4)%4),r=atob(t+n);return Uint8Array.from(r,e=>e.charCodeAt(0))}function g(e){let t=new Date,i=e.indexOf(`.`);if(i<=0||i>=e.length-1)throw Error(`Invalid license key format: expected "payload.signature" with base64url-encoded parts.`);let a,o;try{a=h(e.substring(0,i)),o=h(e.substring(i+1))}catch{throw Error(`Invalid license key format: base64url decoding failed.`)}let s=r(`dfe5c62120b7a0ce962b17907a15b27cf1b056e6619bf6ce55e6e094b322a470`);if(!n.verify(o,a,s))throw Error(`Invalid license key: signature verification failed.`);let c=JSON.parse(new TextDecoder().decode(a));if(typeof c!=`object`||!c)throw Error(`Invalid license key: payload must be a JSON object.`);let{type:l,exp:d}=c;if(l!==`community`)throw Error(`Invalid license key: "type" must be "community".`);if(typeof d!=`number`||!Number.isFinite(d))throw Error(`Invalid license key: "exp" must be a finite number (Unix timestamp in seconds).`);if(d<Math.floor(t.getTime()/1e3)){let e=new Date(d*1e3);throw Error(`License key expired on ${e.toISOString()}. Update to the latest version for a renewed community key.`)}return u(),{type:l,expiresAt:new Date(d*1e3)}}const _=180/Math.PI,v=e=>{let t=e[0],n=e[1],r=e[2],i=e[3],a=2*(i*t+n*r),o=1-2*(t*t+n*n),s=Math.atan2(a,o)*_,c=2*(i*n-r*t),l=Math.abs(c)>=1?Math.sign(c)*90:Math.asin(c)*_,u=2*(i*r+t*n),d=1-2*(n*n+r*r);return{pitch:l,yaw:Math.atan2(u,d)*_,roll:s}},y=1/298.257223563,b=y*(2-y),x=40075016.6856/360,S=39940652.7422/360,ee=Math.PI/180,C=180/Math.PI,w=`0123456789bcdefghjkmnpqrstuvwxyz`,T=[16,8,4,2,1],E=(e,t,n)=>Math.min(Math.max(e,t),n),te=e=>E(e,-180,180),D=e=>e*ee,ne=e=>e*C,re=e=>{if(!Number.isFinite(e))throw RangeError(`Coordinate values must be finite numbers`);return String(e)},ie=e=>({lat:re(e.lat),lon:re(e.lon)}),ae=(e,t=0)=>{let n=D(e.lat),r=D(e.lon),i=Math.sin(n),a=Math.cos(n),s=Math.sin(r),c=Math.cos(r),l=6378137/Math.sqrt(1-b*i*i),u=(l+t)*a*c,d=(l+t)*a*s,f=(l*(1-b)+t)*i;return o.fromValues(u,d,f)},oe=f((e,t)=>{if(t<1||t>23)throw RangeError(`levelOfDetail must be between 1 and 23 (inclusive)`);let n=E(e.lat,-85.05112878,85.05112878),r=te(e.lon),i=Math.sin(D(n)),a=256*2**t,o=(r+180)/360*a,s=(.5-Math.log((1+i)/(1-i))/(4*Math.PI))*a,c=2**t-1,l=E(Math.floor(o/256),0,c),u=E(Math.floor(s/256),0,c),d=``;for(let e=t;e>0;--e){let t=0,n=1<<e-1;(l&n)!==0&&(t+=1),(u&n)!==0&&(t+=2),d+=t.toString()}return d}),se=f(e=>{if(!/^[0-3]+$/.test(e))throw Error(`QuadKey must consist of digits 0-3`);let t=e.length;if(t===0)throw Error(`QuadKey must not be empty`);let n=0,r=0;for(let i=0;i<t;i+=1){let a=Number.parseInt(e[i],10),o=1<<t-i-1;a&1&&(n+=o),a&2&&(r+=o)}let i=256*2**t,a=(n+.5)*256,o=(r+.5)*256,s=a/i,c=o/i,l=s*360-180;return{lat:ne(Math.atan(Math.sinh(Math.PI*(1-2*c)))),lon:l}}),ce=f((e,t)=>{if(t<=0)throw RangeError(`precision must be greater than 0`);let n=-90,r=90,i=-180,a=180,o=``,s=0,c=0,l=!0;for(;o.length<t;){if(l){let t=(i+a)/2;e.lon>=t?(c|=T[s],i=t):a=t}else{let t=(n+r)/2;e.lat>=t?(c|=T[s],n=t):r=t}l=!l,s<4?s+=1:(o+=w[c],s=0,c=0)}return o}),le=f(e=>{if(e.length===0)throw Error(`geoHash must not be empty`);let t=-90,n=90,r=-180,i=180,a=!0;for(let o of e){let e=w.indexOf(o);if(e===-1)throw Error(`Invalid geohash character: ${o}`);for(let o of T){if(a){let t=(r+i)/2;(e&o)===0?i=t:r=t}else{let r=(t+n)/2;(e&o)===0?n=r:t=r}a=!a}}return{lat:(t+n)/2,lon:(r+i)/2}}),ue=f((e,t)=>{let n=D(e.lat),r=D(t.lat),i=r-n,a=D(t.lon-e.lon),o=Math.sin(i/2),s=Math.sin(a/2),c=o*o+Math.cos(n)*Math.cos(r)*s*s,l=Math.min(1,Math.max(0,c));return 6371008.8*(2*Math.atan2(Math.sqrt(l),Math.sqrt(1-l)))}),de=f((e,t)=>{let n=O(e,t);return Math.hypot(n[0],n[2])}),O=f((e,t,n=0,r=0)=>{let i=(t.lon-e.lon)*x*Math.cos(D(e.lat)),a=(t.lat-e.lat)*S,s=n-r;return o.fromValues(a,s,i)}),k=f((e,t)=>{let n=t[0],r=t[2];return{lat:n/S+e.lat,lon:r/(x*Math.cos(D(e.lat)))+e.lon}}),fe=f((e,t=0)=>ae(e,t)),A=e=>{let{lat:t,lon:n}=ie(e);return`${t}%2C${n}`},pe=f(e=>`https://www.google.com/maps/search/?api=1&query=${A(e)}`),me=f((e,t)=>`https://www.google.com/maps/dir/?api=1&origin=${A(t)}&destination=${A(e)}`),he=f((e,t=19)=>{let{lat:n,lon:r}=ie(e);return`https://www.openstreetmap.org/query?lat=${n}&lon=${r}#map=${t}/${n}/${r}`}),ge=f((e,t,n)=>{let r=i.fromValues(...e),a=o.fromValues(t[0],t[1],t[2]),s=o.create();o.transformMat4(s,a,r);let{lat:c,lon:l}=k(n,s);return{lat:c,lon:l,altitude:s[1]}}),_e=()=>({points:[]}),ve=()=>({gpsMarkers:[],gpsLines:[],areas:[],heatMap:{},heatAreas:{}}),j=()=>({arPlanes:{},floorDetections:[],eventAreas:[],currentEventAreaId:null,currentEventAreaPoints:[]}),M=e=>o.fromValues(e[0],e[1],e[2]),N=e=>a.fromValues(e[0],e[1],e[2],e[3]),ye=e=>i.fromValues(...e),P=e=>[e[0],e[1],e[2]],F=e=>[e[0],e[1],e[2],e[3]],I=e=>[e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]],L=e=>[e[0],e[1],e[2]],be=e=>[e[0],e[1],e[2],e[3]],xe=e=>[...e],R=Object.freeze([0,0,0,1]),z=Object.freeze([0,0,0]),B=Object.freeze([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),V=e=>{let t=Ce(e);if(t===0)return R;let n=1/t;return[e[0]*n,e[1]*n,e[2]*n,e[3]*n]},Se=e=>{for(let t=0;t<16;t++)if(e[t]!==B[t])return!1;return!0},Ce=e=>Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]+e[3]*e[3]),we=e=>e[0]===0&&e[1]===0&&e[2]===0&&e[3]===1,Te=(e,t)=>Math.abs(e[0])<t&&Math.abs(e[1])<t&&Math.abs(e[2])<t&&Math.abs(e[3]-1)<t,Ee=(e,t,n=1e-10)=>{let r=Math.abs(e[0]-t[0])<n&&Math.abs(e[1]-t[1])<n&&Math.abs(e[2]-t[2])<n&&Math.abs(e[3]-t[3])<n,i=Math.abs(e[0]+t[0])<n&&Math.abs(e[1]+t[1])<n&&Math.abs(e[2]+t[2])<n&&Math.abs(e[3]+t[3])<n;return r||i},De=(e,t)=>{let n=a.create();return a.multiply(n,e,t),[n[0],n[1],n[2],n[3]]},Oe=e=>{let t=a.create();return a.invert(t,e),[t[0],t[1],t[2],t[3]]},H=e=>[-e[2],e[1],e[0]],ke=e=>[e[2],e[1],-e[0]],U=e=>[-e[2],e[1],e[0],e[3]],Ae=e=>[e[2],e[1],-e[0],e[3]],W=e=>[e[1],e[2],e[0],e[3]],je=e=>[e[2],e[0],e[1],e[3]],G=(e,t,n)=>{if(!Number.isFinite(e)||!Number.isFinite(t)||!Number.isFinite(n))throw Error(`eulerToQuaternion: non-finite input (alpha=${e}, beta=${t}, gamma=${n})`);let r=e*Math.PI/180,i=t*Math.PI/180,o=n*Math.PI/180,s=a.create(),c=a.create(),l=a.create();a.setAxisAngle(s,[0,0,1],r),a.setAxisAngle(c,[1,0,0],i),a.setAxisAngle(l,[0,1,0],o);let u=a.create();return a.multiply(u,s,c),a.multiply(u,u,l),[u[0],u[1],u[2],u[3]]},Me=t({name:`arElements`,initialState:j(),reducers:{recordPhoneHeight:(e,t)=>{e.floorDetections.push(t.payload)},addOrUpdateArPlane:(e,t)=>{let n=t.payload;if(n.parentPlaneId){delete e.arPlanes[n.planeId];return}e.arPlanes[n.planeId]=c(n)},startEventArea:(e,t)=>{e.currentEventAreaId=t.payload.id,e.currentEventAreaPoints=[]},addCornerToEventArea:(e,t)=>{e.currentEventAreaId&&e.currentEventAreaPoints.push(c(L(t.payload.position)))},correctArDriftForEventArea:(e,t)=>{let n=t.payload.drift;for(let t of e.currentEventAreaPoints)t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]},finishEventArea:(e,t)=>{e.currentEventAreaId&&(e.eventAreas.push({id:e.currentEventAreaId,label:t.payload.label,polygon:[],isInArSpace:!0,arPoints:e.currentEventAreaPoints}),e.currentEventAreaId=null,e.currentEventAreaPoints=[])},anchorEventAreaInWorldSpace:(e,t)=>{let{areaId:n,alignmentMatrix:r,zeroRef:i}=t.payload,a=e.eventAreas.findIndex(e=>e.id===n);if(a===-1)return;let s=e.eventAreas[a];if(!s.isInArSpace||!s.arPoints)throw Error(`The area ${s.id} is already anchored in world space`);let c=ye(r),l=s.arPoints.map(e=>{let t=M(e);return k(i,o.transformMat4(o.create(),t,c))});e.eventAreas[a]={...s,isInArSpace:!1,polygon:l,arPoints:void 0}},resetArElements:()=>j()}}),{recordPhoneHeight:Ne,addOrUpdateArPlane:Pe,startEventArea:Fe,addCornerToEventArea:Ie,correctArDriftForEventArea:Le,finishEventArea:Re,anchorEventAreaInWorldSpace:ze,resetArElements:Be}=Me.actions,Ve=p(Ne),He=p(Pe),Ue=p(Fe),We=p(Ie),Ge=p(Le),Ke=p(Re),qe=p(ze),Je=p(Be),Ye=m(Me.reducer);function Xe(e,t,n,r){let{inCentroid:i,refCentroid:a,refVec:s,tmp:c}=r;o.set(i,0,0,0),o.set(a,0,0,0);let l=0;for(let n=0;n<e.length;n++){let r=t[n][3];o.scaleAndAdd(i,i,e[n],r),K(s,t[n]),o.scaleAndAdd(a,a,s,r),l+=r}if(l===0)throw Error(`Weights must not sum to zero when solving Kabsch`);let u=1/l;if(o.scale(i,i,u),o.scale(a,a,u),!n)return{inCentroid:i,refCentroid:a,scaleRatio:1};let d=0,f=0;for(let n=0;n<e.length;n++)o.subtract(c,e[n],i),d+=o.length(c),K(s,t[n]),o.subtract(c,s,a),f+=o.length(c);return{inCentroid:i,refCentroid:a,scaleRatio:d===0?1:f/d}}function Ze(e,t,n,r,a,s,c){let{negCentroid:l,scaleVec:u}=c;return i.fromRotationTranslation(e,r,n),s&&(o.set(u,a,a,a),i.scale(e,e,u)),o.negate(l,t),i.translate(e,e,l),e}function Qe(e,t,n={}){let r=e.length;if(r!==t.length)throw Error(`Length of the point lists was not equal: ${r} vs ${t.length}`);if(r===0)return i.create();let s=n.solveRotation??!0,c=n.solveScale??!1,l=n.ignoreYAxisForRotation??!1,u=n.rotationIterations??9,{inCentroid:d,refCentroid:f,scaleRatio:p}=Xe(e,t,c,{inCentroid:o.create(),refCentroid:o.create(),refVec:o.create(),tmp:o.create()}),m=a.create();s?new $e(l).solve(e,t,d,f,m,u):a.identity(m);let h={negCentroid:o.create(),scaleVec:o.create()};return Ze(i.create(),d,f,m,p,c,h)}var $e=class e{ignoreYAxis;static twoPi=Math.PI*2;static basisX=o.fromValues(1,0,0);static basisY=o.fromValues(0,1,0);static basisZ=o.fromValues(0,0,1);constructor(e){this.ignoreYAxis=e}solve(t,n,r,i,o,s){let c=this.transposeMultSubtract(t,n,r,i);if(this.ignoreYAxis){let t=c[0][0],n=c[0][2],r=c[2][0],i=c[2][2],s=Math.atan2(r-n,t+i);a.setAxisAngle(o,e.basisY,s)}else this.extractRotation(c,o,s)}transposeMultSubtract(e,t,n,r){let i=[o.create(),o.create(),o.create()],a=o.clone(n),s=o.clone(r);this.ignoreYAxis&&(a[1]=0,s[1]=0);let c=o.create(),l=o.create(),u=o.create();for(let n=0;n<e.length;n++){o.copy(c,e[n]),K(u,t[n]),this.ignoreYAxis&&(c[1]=0,u[1]=0);let r=t[n][3];o.subtract(c,c,a),o.scale(c,c,r),o.subtract(l,u,s),i[0][0]+=c[0]*l[0],i[1][0]+=c[1]*l[0],i[2][0]+=c[2]*l[0],i[0][1]+=c[0]*l[1],i[1][1]+=c[1]*l[1],i[2][1]+=c[2]*l[1],i[0][2]+=c[0]*l[2],i[1][2]+=c[1]*l[2],i[2][2]+=c[2]*l[2]}return i}extractRotation(t,n,r){let i=[o.create(),o.create(),o.create()],s=o.create(),c=o.create(),l=o.create();for(let u=0;u<r;u++){this.fillMatrixFromQuaternion(n,i),o.cross(s,i[0],t[0]),o.cross(l,i[1],t[1]),o.add(s,s,l),o.cross(l,i[2],t[2]),o.add(s,s,l);let r=o.dot(i[0],t[0])+o.dot(i[1],t[1])+o.dot(i[2],t[2])+1e-9;r=Math.abs(r),o.scale(s,s,1/r);let u=o.length(s);if(u<1e-9)break;o.scale(c,s,1/u);let d=u%e.twoPi,f=a.create();a.setAxisAngle(f,c,d),a.multiply(n,f,n),a.normalize(n,n)}}fillMatrixFromQuaternion(t,n){o.transformQuat(n[0],e.basisX,t),o.transformQuat(n[1],e.basisY,t),o.transformQuat(n[2],e.basisZ,t)}};const K=(e,t)=>(e[0]=t[0],e[1]=t[1],e[2]=t[2],e);function et(e,t,n,r,i,a,o=Math.random){let s=Array.from(e);if(n>s.length)throw RangeError(`minSampleSize must be smaller than the number of elements: minSampleSize=${n}, elements=${s.length}`);return rt(s,tt(s,t,n,r,i,a,o),i,a)}function tt(e,t,n,r,i,a,o){let s=null,c=Array(n),l=new Uint8Array(e.length);for(let u=0;u<r;u+=1){it(e,n,o,c,l);let{inliers:r,outliers:u}=nt(e,l,i(c),a);if(s===null||n+r.length>=t){let e=[...c,...r],t=i(e),n=q(t,`createModel must assign totalModelError for comparison during RANSAC`);(s===null||n<s.error)&&(t.inliers=e,t.outliers=u,s={model:t,error:n})}}return s?.model??null}function nt(e,t,n,r){let i=[],a=[];for(let o=0;o<e.length;o+=1){if(t?.[o])continue;let s=e[o];(r(n,s)?i:a).push(s)}return{inliers:i,outliers:a}}function rt(e,t,n,r){let i=t??n(e);q(i,`RANSAC failed to produce a model with totalModelError`);let{inliers:a,outliers:o}=nt(e,null,i,r);return a.length>0&&(i=n(a),q(i,`createModel must assign totalModelError for final RANSAC model`)),i.inliers=a,i.outliers=o,i}function q(e,t){let{totalModelError:n}=e;if(n==null)throw Error(t);return n}function it(e,t,n,r,i){i.fill(0);let a=0;for(;a<t;){let t=Math.floor(n()*e.length);i[t]||(i[t]=1,r[a]=e[t],a+=1)}}const at={useOnlyRecentData:!1,recentSeconds:180,includeTimeWeight:!0,weightByTimeFactor:2500,ignoreYAxisForRotation:!0,useRansac:!1,ransacInlierRatio:.95,ransacSampleRatio:.3,ransacMaxIterations:230,ransacErrorTolerance:1,geohashPrecision:8,geohashWindowSize:10,gpsAccuracyExponent:.1,altitudeAlignmentMode:`separate1D`,altitudeAggregator:`mean`,altitudeSampleSet:`all`,gpsVerticalAccuracyExponent:5},ot=e=>e.map(e=>L(e)),st=e=>e.map(e=>V(e)),ct=e=>e.map(Nt),lt=e=>e.map(e=>[e[0],e[1],e[2],e[3]]),J=e=>Array.isArray(e)?e:P(e),Y=e=>Array.isArray(e)?e:F(e),ut=(e,t,n,r)=>({odometryPosOffset:L(J(e)),odometryRotOffset:V(Y(t)),latestLoopClosureFixPointPos:n?L(J(n)):null,latestLoopClosureFixPointRot:r?V(Y(r)):null}),dt=(e,t,n,r)=>({odometryPositions:[],odometryRotations:[],gpsPositions:[],gpsPositionsVec4:[],alignmentMatrix:[...B],alignmentRotation:[...R],alignmentTranslation:[...z],alignmentRotationInDegree:[...z],...ut(e,t,n,r),gpsAccuracyMedian:null,gpsAccuracyMean:null,currentGpsPosGeoHash:null}),ft=()=>dt(z,R,null,null),pt=(e,t,n,r)=>{let i=e.map(e=>J(e)),a=t.map(e=>V(Y(e))),o=n.map(Nt),s=r.gpsAccuracyExponent;for(let e of o)e.weight=X(e.latLongAccuracy,s);let c=o.map(jt),l=r.includeTimeWeight?Dt(c,o,r.weightByTimeFactor):c,u=r.useOnlyRecentData?Tt(o,r.recentSeconds):0,d=i.slice(u).map(M),f=o.slice(u);return{allPositionTuples:i,allRotationTuples:a,allGpsPoints:o,allVec4Tuples:l,solverPositionsTyped:d,solverGpsPoints:f,solverWeightedVec4Tuples:l.slice(u),solverVerticalWeights:Et(f,r)}},mt=e=>{let{odometryPositions:t,odometryRotations:n,gpsPoints:r,odometryPosOffset:i=z,odometryRotOffset:s=R,latestLoopClosureFixPointPos:c=null,latestLoopClosureFixPointRot:l=null,alignmentConfig:u,random:d}=e;if(t.length!==n.length||t.length!==r.length)throw Error(`GpsEvents requires odometry positions, rotations, and gps points to have identical lengths`);let f={...at,...u??{}};if(t.length===0)return dt(i,s,c,l);let{allPositionTuples:p,allRotationTuples:m,allGpsPoints:h,allVec4Tuples:g,solverPositionsTyped:_,solverGpsPoints:v,solverWeightedVec4Tuples:y,solverVerticalWeights:b}=pt(t,n,r,f),x=vt(_,y,b,f,d??Math.random),S=a.create(),ee=o.create(),C=o.create();gt(x.matrix,S,ee,C);let{mean:w,median:T}=kt(h),E=Ot(r,f.geohashPrecision,f.geohashWindowSize);return{odometryPositions:ot(p),odometryRotations:st(m),gpsPositions:ct(h),gpsPositionsVec4:lt(g),alignmentMatrix:I(x.matrix),alignmentRotation:F(S),alignmentTranslation:P(ee),alignmentRotationInDegree:P(C),...ut(i,s,c,l),gpsAccuracyMedian:T,gpsAccuracyMean:w,currentGpsPosGeoHash:E}},X=(e,t)=>e!=null&&e>0?1/Math.max(e,1)**+t:1,ht=(e,t,n,r,i,s)=>{let c=e.odometryPositions,l=e.odometryRotations,u=e.gpsPositions,d=e.gpsPositionsVec4;c.push(J(t)),l.push(V(Y(n)));let f=Nt(r);u.push(f);let p={...at,...i??{}};f.weight=X(f.latLongAccuracy,p.gpsAccuracyExponent);let m=jt(f);if(p.includeTimeWeight){d.push(m);let e=Dt(d,u,p.weightByTimeFactor);for(let t=0;t<e.length;t++)d[t]=e[t]}else d.push(m);let h=0;p.useOnlyRecentData&&(h=Tt(u,p.recentSeconds));let g=vt(c.slice(h).map(M),d.slice(h),Et(u.slice(h),p),p,s??Math.random);e.alignmentMatrix=I(g.matrix);let _=a.create(),v=o.create(),y=o.create();gt(g.matrix,_,v,y),e.alignmentRotation=F(_),e.alignmentTranslation=P(v),e.alignmentRotationInDegree=P(y);let{mean:b,median:x}=kt(u);e.gpsAccuracyMean=b,e.gpsAccuracyMedian=x,e.currentGpsPosGeoHash=Ot(u,p.geohashPrecision,p.geohashWindowSize)},gt=(e,t,n,r)=>{i.getRotation(t,e),a.normalize(t,t),i.getTranslation(n,e),At(t,r)},Z=o.create(),_t=o.create(),vt=(e,t,n,r,a)=>{if(e.length===0)return{matrix:i.create(),meanError:0};let o=e.length,c=[];for(let r=0;r<o;r+=1){let i=t[r];c.push({odom:e[r],ref:s.fromValues(i[0],i[1],i[2],i[3]),verticalWeight:n[r]??1})}let l,u,d;if(r.useRansac&&c.length>=3){let e=c.length,t=Math.max(3,Math.min(e,Math.ceil(e*r.ransacSampleRatio))),n=et(c,Math.max(t,Math.min(e,Math.ceil(e*r.ransacInlierRatio))),t,r.ransacMaxIterations,e=>St(e,r.ignoreYAxisForRotation),(e,t)=>Ct(e,t)<e.meanAlignmentError+r.ransacErrorTolerance,a);l=n.alignmentMatrix,u=n.meanAlignmentError,n.inliers!==void 0&&(d=Array.from(n.inliers))}else{let e=St(c,r.ignoreYAxisForRotation);l=e.alignmentMatrix,u=e.meanAlignmentError}return r.altitudeAlignmentMode===`separate1D`&&yt(l,c,d,r),{matrix:l,meanError:u}},yt=(e,t,n,r)=>{let i=r.altitudeSampleSet===`horizontalInliers`&&n!==void 0&&n.length>0?n:t;if(i.length===0)return;let a=Array(i.length),o=Array(i.length);for(let e=0;e<i.length;e+=1){let t=i[e];a[e]=t.ref[1]-t.odom[1];let n=t.verticalWeight;o[e]=n>0&&Number.isFinite(n)?n:0}e[13]=r.altitudeAggregator===`median`?xt(a,o):bt(a,o)},bt=(e,t)=>{let n=0,r=0;for(let i=0;i<e.length;i+=1)n+=e[i]*t[i],r+=t[i];if(r>0&&Number.isFinite(r))return n/r;let i=0;for(let t=0;t<e.length;t+=1)i+=e[t];return e.length===0?0:i/e.length},xt=(e,t)=>{if(e.length===0)return 0;if(e.length===1)return e[0];let n=e.map((e,t)=>t);n.sort((t,n)=>e[t]-e[n]);let r=0;for(let e=0;e<t.length;e+=1)r+=t[e];if(!(r>0)||!Number.isFinite(r)){let t=n.map(t=>e[t]);return t[Math.floor((t.length-1)/2)]}let i=r/2,a=0;for(let r=0;r<n.length;r+=1)if(a+=t[n[r]],a>=i)return e[n[r]];return e[n[n.length-1]]},St=(e,t)=>{let n=Array.from(e),r=n.length,i=Array(r),a=Array(r);for(let e=0;e<r;e+=1)i[e]=n[e].odom,a[e]=n[e].ref;let s=Qe(i,a,{ignoreYAxisForRotation:t}),c=0;for(let e=0;e<r;e+=1)o.transformMat4(Z,i[e],s),c+=wt(Z,n[e].ref);return{alignmentMatrix:s,meanAlignmentError:r===0?0:c/r,totalModelError:c}},Ct=(e,t)=>(o.transformMat4(Z,t.odom,e.alignmentMatrix),wt(Z,t.ref)),wt=(e,t)=>(o.set(_t,t[0],t[1],t[2]),o.squaredDistance(e,_t)),Tt=(e,t)=>{if(e.length===0||t<=0)return 0;let n=e[e.length-1].timestamp-t*1e3;for(let t=0;t<e.length;t+=1)if(e[t].timestamp>=n)return t;return 0},Et=(e,t)=>{let n=t.gpsVerticalAccuracyExponent,r=e.map(e=>{let t=e.altitudeAccuracy;return X(t!=null&&Number.isFinite(t)&&t>0?t:e.latLongAccuracy,n)});if(!t.includeTimeWeight||e.length<2)return r;let i=e[e.length-1].timestamp,a=e[0].timestamp,o=Math.max(1,i-a),s=t.weightByTimeFactor;return r.map((t,n)=>{if(!(t>0&&Number.isFinite(t)))return 0;let r=s*((i-e[n].timestamp)/o)+1;return 1/(1/t+r)})},Dt=(e,t,n)=>{if(e.length<2)return e.map(e=>[e[0],e[1],e[2],e[3]]);let r=t[t.length-1].timestamp,i=t[0].timestamp,a=Math.max(1,r-i);return e.map((e,i)=>{let o=n*((r-t[i].timestamp)/a)+1,s=t[i].weight;if(!(s>0&&Number.isFinite(s)))return[e[0],e[1],e[2],0];let c=1/(1/s+o);return[e[0],e[1],e[2],c]})},Q=new Map,Ot=(e,t,n)=>{if(e.length===0)return null;let r=Math.max(0,e.length-n);Q.clear();for(let n=r;n<e.length;n+=1){let r=e[n],i=ce(Mt(r),t),a=Q.get(i);a?(a.count+=1,a.index=n):Q.set(i,{count:1,index:n})}let i=null,a=-1,o=-1;for(let[e,{count:t,index:n}]of Q)(t>a||t===a&&n>o)&&(i=e,a=t,o=n);return i},kt=e=>{let t=e.map(e=>e.latLongAccuracy).filter(e=>typeof e==`number`&&Number.isFinite(e));if(t.length===0)return{mean:null,median:null};let n=t.reduce((e,t)=>e+t,0)/t.length,r=t.slice().sort((e,t)=>e-t),i=Math.floor(r.length/2);return{mean:n,median:r.length%2==0?(r[i-1]+r[i])/2:r[i]}},At=(e,t)=>{let n=e[0],r=e[1],i=e[2],a=e[3],s=2*(a*n+r*i),c=1-2*(n*n+r*r),l=Math.atan2(s,c),u=2*(a*r-i*n),d;d=Math.abs(u)>=1?Math.PI/2*Math.sign(u):Math.asin(u);let f=2*(a*i+n*r),p=1-2*(r*r+i*i),m=Math.atan2(f,p),h=180/Math.PI,g=t??o.create();return g[0]=l*h,g[1]=d*h,g[2]=m*h,g},jt=e=>[e.coordinates[0],e.coordinates[1],e.coordinates[2],e.weight>0&&Number.isFinite(e.weight)?e.weight:1],Mt=e=>({lat:e.latitude,lon:e.longitude}),Nt=e=>({...e,zeroRef:{lat:e.zeroRef.lat,lon:e.zeroRef.lon},coordinates:[e.coordinates[0],e.coordinates[1],e.coordinates[2]],timestamp:e.timestamp,deviceRotation:e.deviceRotation?V(Y(e.deviceRotation)):void 0}),Pt=({lat:e,lon:t})=>{if(!Number.isFinite(e))throw Error(`Invalid latitude value: ${e}`);if(!Number.isFinite(t))throw Error(`Invalid longitude value: ${t}`);if(e<-90||e>90)throw Error(`Invalid latitude range: ${e}`);if(t<-180||t>180)throw Error(`Invalid longitude range: ${t}`)},Ft=o.fromValues(1,1,1),It=(e,t)=>[e[0]+t[0],e[1]+t[1],e[2]+t[2]],Lt=(e,t,n)=>{let r=O(t,{lat:e.latitude,lon:e.longitude},e.altitude??0,0),i=n?.alpha!=null&&n?.beta!=null&&n?.gamma!=null?W(G(n.alpha,n.beta,n.gamma)):void 0;return{...e,zeroRef:t,coordinates:P(r),weight:1,deviceRotation:i}},Rt=(e,t,n)=>{if(e)return G(e.alpha,e.beta,e.gamma);if(t)return t;throw Error(`Sensor rotation missing for ${n}: neither raw orientation nor legacy quaternion provided`)},zt=e=>{let t=a.invert(a.create(),e);if(!t)throw Error(`Encountered non-invertible quaternion`);return t},Bt=e=>{let t=zt(a.normalize(a.create(),N(e.lastSensorRot))),n=a.normalize(a.create(),N(e.lastValidOdomRot)),r=a.multiply(a.create(),t,n),i=a.normalize(a.create(),N(e.newSensorRot)),o=a.multiply(a.create(),i,r),s=a.normalize(a.create(),N(e.newOdomRot)),c=a.multiply(a.create(),o,s);return a.normalize(c,c),F(c)},Vt=(e,t)=>{let n=i.create();return i.fromRotationTranslationScale(n,a.normalize(a.create(),N(t)),M(e),Ft),n},Ht=(e,t,n)=>{let r=o.create(),s=o.create(),c=a.create(),l=a.create();i.getTranslation(r,e),i.getTranslation(s,t),i.getRotation(c,e),i.getRotation(l,t);let u=o.create();o.lerp(u,r,s,n);let d=a.create();return a.slerp(d,c,l,n),i.fromRotationTranslation(i.create(),d,u)},Ut=(e,t)=>{if(e.length===0)return[];let n=e[e.length-1],r=i.invert(i.create(),n);if(!r)throw Error(`End pose matrix is not invertible`);let a=i.multiply(i.create(),t,r),o=i.create(),s=e.length-1;return e.map((e,t)=>{let n=Ht(o,a,s<=0?1:t/s);return i.multiply(i.create(),n,e)})},Wt=(e,t,n)=>{for(let r=e.length-1;r>=0;--r)if(n(e[r],t))return r;return-1},Gt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2],Kt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2]&&e[3]===t[3],qt=t({name:`gpsData`,initialState:null,reducers:{setZeroPos:{reducer:(e,t)=>e===null?{zero:t.payload,gpsEvents:ft(),odometryPath:_e()}:e,prepare:e=>(Pt(e),{payload:e})},recordGpsEvent:(e,t)=>{if(!e)return e;let{odomPosition:n,odomRotation:r,rawGpsPoint:i,rawDeviceOrientation:a}=t.payload,o=Lt(i,e.zero,a);ht(e.gpsEvents,H(n),U(r),o)},odometryTrackingRestarted:(e,t)=>{if(!e)return e;let n=It(e.gpsEvents.odometryPosOffset,H(t.payload.lastValidOdomPos)),r=Rt(t.payload.lastSensorOrientation,t.payload.lastSensorRot,`lastSensor`),i=Rt(t.payload.newSensorOrientation,t.payload.newSensorRot,`newSensor`);e.gpsEvents=c(mt({odometryPositions:[],odometryRotations:[],gpsPoints:[],odometryPosOffset:n,odometryRotOffset:Bt({...t.payload,lastValidOdomRot:U(t.payload.lastValidOdomRot),newOdomRot:U(t.payload.newOdomRot),lastSensorRot:W(r),newSensorRot:W(i)}),latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},arLoopClosureDetected:(e,t)=>{if(!e)return e;let n=e.gpsEvents;if(n.odometryPositions.length===0)return e;let r=n.latestLoopClosureFixPointPos?Wt(n.odometryPositions,n.latestLoopClosureFixPointPos,Gt):0,s=n.latestLoopClosureFixPointRot?Wt(n.odometryRotations,n.latestLoopClosureFixPointRot,Kt):0;if(r<0||s<0)throw Error(`Loop closure fix point could not be located in history`);if(r!==s)throw Error(`Loop closure fix point indices for position and rotation diverged`);let l=n.odometryPositions.slice(0,r).map(e=>L(e)),u=n.odometryRotations.slice(0,r).map(e=>V(e)),d=n.odometryPositions.slice(r).map(e=>L(e)),f=n.odometryRotations.slice(r).map(e=>V(e)),p=d.map((e,t)=>Vt(e,f[t])),m=Vt(H(t.payload.lastPos),V(U(t.payload.lastRot)));p.push(m);let h=Ut(p,Vt(H(t.payload.newPos),V(U(t.payload.newRot)))),g=[],_=[],v=o.create();for(let e=0;e<h.length-1;e+=1){let t=h[e],n=o.create();i.getTranslation(n,t);let r=a.create();if(i.getRotation(r,t),a.normalize(r,r),i.getScaling(v,t),Math.hypot(v[0]-1,v[1]-1,v[2]-1)>.05)throw Error(`Unexpected scale drift encountered during loop closure correction`);g.push(P(n)),_.push(F(r))}e.gpsEvents=c(mt({odometryPositions:l.concat(g),odometryRotations:u.concat(_),gpsPoints:n.gpsPositions,odometryPosOffset:n.odometryPosOffset,odometryRotOffset:n.odometryRotOffset,latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},add2dImage:(e,t)=>{if(!e)return e;e.odometryPath.points.push(c({imageFile:t.payload.imageFile,screenRotation:t.payload.screenRotation,position:H(t.payload.position),rotation:V(U(t.payload.rotation)),capturedAt:t.payload.capturedAt,width:t.payload.width,height:t.payload.height}))}}}),{setZeroPos:Jt,recordGpsEvent:Yt,odometryTrackingRestarted:Xt,arLoopClosureDetected:Zt,add2dImage:Qt}=qt.actions,$t=p(Jt),en=p(Yt),tn=p(Xt),nn=p(Zt),rn=p(Qt),an=m(qt.reducer),on=t({name:`gpsElements`,initialState:ve(),reducers:{addMarker:(e,t)=>{e.gpsMarkers.push(t.payload)},addLine:(e,t)=>{e.gpsLines.push(t.payload)},addArea:(e,t)=>{e.areas.push(t.payload)},addToHeatMaps:(e,t)=>{let{category:n,tiles:r}=t.payload,i=e.heatMap[n]??{};e.heatMap[n]={...i,...r}},addHeatMapArea:(e,t)=>{let n=t.payload,r=n.category;if(e.heatAreas[r]||(e.heatAreas[r]={}),e.heatAreas[r][n.geoHash])throw Error(`HeatArea collision detected for GeoHash: ${n.geoHash}`);e.heatAreas[r][n.geoHash]=n},resetGpsElements:()=>ve()}}),{addMarker:sn,addLine:cn,addArea:ln,addToHeatMaps:un,addHeatMapArea:dn,resetGpsElements:fn}=on.actions,pn=p(sn),mn=p(cn),hn=p(ln),gn=p(un),_n=p(dn),vn=p(fn),yn=m(on.reducer),bn=()=>({gpsData:null,gpsElements:ve(),arElements:j()}),xn=e=>{let t=Array.from({length:Math.min(e.length,4)},(t,n)=>e[n].toFixed(2));e.length>4&&t.push(`…`);let n=`[${t.join(`, `)}]`;if(e.length===4){let{pitch:t,yaw:r,roll:i}=v(e);return`${n} (pitch=${t.toFixed(0)}°, yaw=${r.toFixed(0)}°, roll=${i.toFixed(0)}°)`}return n},Sn=e=>(e.length===3||e.length===4||e.length===16)&&e.every(e=>typeof e==`number`),$=(e,t=0)=>{if(t>10||e==null)return e;if(Array.isArray(e))return Sn(e)?xn(e):e.map(e=>$(e,t+1));if(typeof e==`object`){let n={};for(let[r,i]of Object.entries(e))n[r]=$(i,t+1);return n}return e},Cn=(t={})=>{let{preloadedState:n,enableDevToolsSanitizers:r=!0,enableDevChecks:i=!0,licenseKey:a}=t;if(a==null)throw Error(`License key required. Pass options.licenseKey or use createRecorderStore() from gps-plus-slam-app-framework. See EULA §3.`);g(a);let o=bn(),s=n?{gpsData:n.gpsData??o.gpsData,gpsElements:n.gpsElements??o.gpsElements,arElements:n.arElements??o.arElements}:void 0;return e({reducer:{gpsData:an,gpsElements:yn,arElements:Ye},preloadedState:s,middleware:e=>e({serializableCheck:i,immutableCheck:i}),devTools:r?{actionSanitizer:$,stateSanitizer:$}:!0})},wn=[],Tn=[],En=[],Dn={median:null,mean:null},On=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentMatrix}),kn=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentRotation}),An=f(function(e){return e.gpsData?.gpsEvents?.odometryPositions??wn}),jn=f(function(e){return e.gpsData?.gpsEvents?.odometryRotations??Tn}),Mn=f(function(e){return e.gpsData?.gpsEvents?.gpsPositions??En}),Nn=f(function(e){return e.gpsData?.zero??null}),Pn=f(function(e){let t=e.gpsData?.gpsEvents?.gpsAccuracyMedian??null,n=e.gpsData?.gpsEvents?.gpsAccuracyMean??null;return t===null&&n===null?Dn:{median:t,mean:n}}),Fn=`1.0.0`;export{B as IDENTITY_MATRIX4,R as IDENTITY_QUATERNION,Fn as LIB_VERSION,z as ZERO_VECTOR3,rn as add2dImage,hn as addArea,We as addCornerToEventArea,_n as addHeatMapArea,mn as addLine,pn as addMarker,He as addOrUpdateArPlane,gn as addToHeatMaps,qe as anchorEventAreaInWorldSpace,Ye as arElementsReducer,nn as arLoopClosureDetected,ce as calcGeoHash,k as calcGpsCoords,oe as calcQuadKey,O as calcRelativeCoordsInMeters,xe as cloneMatrix4,be as cloneQuaternion,L as cloneVector3,Ge as correctArDriftForEventArea,Cn as createGpsSlamStore,ue as distanceInMeters,de as distanceInMetersRelative,W as enuQuaternionToNUE,G as eulerToQuaternion,Ke as finishEventArea,ye as fromMatrix4,N as fromQuaternion,M as fromVector3,ge as fusedGpsFromOdom,le as geoHashToLatLong,On as getAlignmentMatrix,kn as getAlignmentRotation,me as getGoogleMapsDirectionsLink,pe as getGoogleMapsLink,Pn as getGpsAccuracyStats,Mn as getGpsPositions,An as getOdometryPositions,jn as getOdometryRotations,he as getOpenStreetMapLink,Nn as getZeroReference,an as gpsDataReducer,yn as gpsElementsReducer,Oe as invertQuaternion,Se as isIdentityMatrix4,we as isIdentityQuaternion,Te as isNearIdentityQuaternion,De as multiplyQuaternions,V as normalizeQuaternion,je as nueQuaternionToENU,Ae as nueQuaternionToWebXR,ke as nueToWebXR,tn as odometryTrackingRestarted,se as quadKeyToLatLong,Ce as quaternionMagnitude,Ee as quaternionsEquivalent,en as recordGpsEvent,Ve as recordPhoneHeight,Je as resetArElements,vn as resetGpsElements,$ as sanitizeForDevTools,$t as setZeroPos,Ue as startEventArea,fe as toEarthCenteredCoordinates,I as toMatrix4,F as toQuaternion,P as toVector3,g as validateLicenseKey,U as webxrQuaternionToNUE,H as webxrToNUE};
2
+ import{configureStore as e,createSlice as t}from"@reduxjs/toolkit";import{ed25519 as n}from"@noble/curves/ed25519.js";import{hexToBytes as r}from"@noble/curves/utils.js";import{mat4 as i,quat as a,vec3 as o,vec4 as s}from"gl-matrix";import{castDraft as c}from"immer";let l=!1;function u(){l=!0}function d(){if(!l)throw Error(`gps-plus-slam-js: license not activated. Construct your store via createRecorderStore() from gps-plus-slam-app-framework, or pass a valid licenseKey to createGpsSlamStore(). See EULA §3.`)}function f(e){return((...t)=>(d(),e(...t)))}function p(e){let t=((...t)=>(d(),e(...t)));return Object.assign(t,e),t.toString=()=>e.type,t}function m(e){return(t,n)=>(d(),e(t,n))}function h(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=`=`.repeat((4-t.length%4)%4),r=atob(t+n);return Uint8Array.from(r,e=>e.charCodeAt(0))}function g(e){let t=new Date,i=e.indexOf(`.`);if(i<=0||i>=e.length-1)throw Error(`Invalid license key format: expected "payload.signature" with base64url-encoded parts.`);let a,o;try{a=h(e.substring(0,i)),o=h(e.substring(i+1))}catch{throw Error(`Invalid license key format: base64url decoding failed.`)}let s=r(`dfe5c62120b7a0ce962b17907a15b27cf1b056e6619bf6ce55e6e094b322a470`);if(!n.verify(o,a,s))throw Error(`Invalid license key: signature verification failed.`);let c=JSON.parse(new TextDecoder().decode(a));if(typeof c!=`object`||!c)throw Error(`Invalid license key: payload must be a JSON object.`);let{type:l,exp:d}=c;if(l!==`community`)throw Error(`Invalid license key: "type" must be "community".`);if(typeof d!=`number`||!Number.isFinite(d))throw Error(`Invalid license key: "exp" must be a finite number (Unix timestamp in seconds).`);if(d<Math.floor(t.getTime()/1e3)){let e=new Date(d*1e3);throw Error(`License key expired on ${e.toISOString()}. Update to the latest version for a renewed community key.`)}return u(),{type:l,expiresAt:new Date(d*1e3)}}const _=180/Math.PI,v=e=>{let t=e[0],n=e[1],r=e[2],i=e[3],a=2*(i*t+n*r),o=1-2*(t*t+n*n),s=Math.atan2(a,o)*_,c=2*(i*n-r*t),l=Math.abs(c)>=1?Math.sign(c)*90:Math.asin(c)*_,u=2*(i*r+t*n),d=1-2*(n*n+r*r);return{pitch:l,yaw:Math.atan2(u,d)*_,roll:s}},y=1/298.257223563,b=y*(2-y),x=40075016.6856/360,S=39940652.7422/360,C=Math.PI/180,w=180/Math.PI,T=`0123456789bcdefghjkmnpqrstuvwxyz`,E=[16,8,4,2,1],D=(e,t,n)=>Math.min(Math.max(e,t),n),O=e=>D(e,-180,180),k=e=>e*C,A=e=>e*w,j=e=>{if(!Number.isFinite(e))throw RangeError(`Coordinate values must be finite numbers`);return String(e)},M=e=>({lat:j(e.lat),lon:j(e.lon)}),ee=(e,t=0)=>{let n=k(e.lat),r=k(e.lon),i=Math.sin(n),a=Math.cos(n),s=Math.sin(r),c=Math.cos(r),l=6378137/Math.sqrt(1-b*i*i),u=(l+t)*a*c,d=(l+t)*a*s,f=(l*(1-b)+t)*i;return o.fromValues(u,d,f)},te=f((e,t)=>{if(t<1||t>23)throw RangeError(`levelOfDetail must be between 1 and 23 (inclusive)`);let n=D(e.lat,-85.05112878,85.05112878),r=O(e.lon),i=Math.sin(k(n)),a=256*2**t,o=(r+180)/360*a,s=(.5-Math.log((1+i)/(1-i))/(4*Math.PI))*a,c=2**t-1,l=D(Math.floor(o/256),0,c),u=D(Math.floor(s/256),0,c),d=``;for(let e=t;e>0;--e){let t=0,n=1<<e-1;(l&n)!==0&&(t+=1),(u&n)!==0&&(t+=2),d+=t.toString()}return d}),ne=f(e=>{if(!/^[0-3]+$/.test(e))throw Error(`QuadKey must consist of digits 0-3`);let t=e.length;if(t===0)throw Error(`QuadKey must not be empty`);let n=0,r=0;for(let i=0;i<t;i+=1){let a=Number.parseInt(e[i],10),o=1<<t-i-1;a&1&&(n+=o),a&2&&(r+=o)}let i=256*2**t,a=(n+.5)*256,o=(r+.5)*256,s=a/i,c=o/i,l=s*360-180;return{lat:A(Math.atan(Math.sinh(Math.PI*(1-2*c)))),lon:l}}),re=f((e,t)=>{if(t<=0)throw RangeError(`precision must be greater than 0`);let n=-90,r=90,i=-180,a=180,o=``,s=0,c=0,l=!0;for(;o.length<t;){if(l){let t=(i+a)/2;e.lon>=t?(c|=E[s],i=t):a=t}else{let t=(n+r)/2;e.lat>=t?(c|=E[s],n=t):r=t}l=!l,s<4?s+=1:(o+=T[c],s=0,c=0)}return o}),ie=f(e=>{if(e.length===0)throw Error(`geoHash must not be empty`);let t=-90,n=90,r=-180,i=180,a=!0;for(let o of e){let e=T.indexOf(o);if(e===-1)throw Error(`Invalid geohash character: ${o}`);for(let o of E){if(a){let t=(r+i)/2;(e&o)===0?i=t:r=t}else{let r=(t+n)/2;(e&o)===0?n=r:t=r}a=!a}}return{lat:(t+n)/2,lon:(r+i)/2}}),ae=f((e,t)=>{let n=k(e.lat),r=k(t.lat),i=r-n,a=k(t.lon-e.lon),o=Math.sin(i/2),s=Math.sin(a/2),c=o*o+Math.cos(n)*Math.cos(r)*s*s,l=Math.min(1,Math.max(0,c));return 6371008.8*(2*Math.atan2(Math.sqrt(l),Math.sqrt(1-l)))}),oe=f((e,t)=>{let n=N(e,t);return Math.hypot(n[0],n[2])}),N=f((e,t,n=0,r=0)=>{let i=(t.lon-e.lon)*x*Math.cos(k(e.lat)),a=(t.lat-e.lat)*S,s=n-r;return o.fromValues(a,s,i)}),P=f((e,t)=>{let n=t[0],r=t[2];return{lat:n/S+e.lat,lon:r/(x*Math.cos(k(e.lat)))+e.lon}}),se=f((e,t=0)=>ee(e,t)),F=e=>{let{lat:t,lon:n}=M(e);return`${t}%2C${n}`},ce=f(e=>`https://www.google.com/maps/search/?api=1&query=${F(e)}`),le=f((e,t)=>`https://www.google.com/maps/dir/?api=1&origin=${F(t)}&destination=${F(e)}`),ue=f((e,t=19)=>{let{lat:n,lon:r}=M(e);return`https://www.openstreetmap.org/query?lat=${n}&lon=${r}#map=${t}/${n}/${r}`}),de=f((e,t,n)=>{let r=i.fromValues(...e),a=o.fromValues(t[0],t[1],t[2]),s=o.create();o.transformMat4(s,a,r);let{lat:c,lon:l}=P(n,s);return{lat:c,lon:l,altitude:s[1]}}),fe=()=>({points:[]}),pe=()=>({gpsMarkers:[],gpsLines:[],areas:[],heatMap:{},heatAreas:{}}),me=()=>({arPlanes:{},floorDetections:[],eventAreas:[],currentEventAreaId:null,currentEventAreaPoints:[]}),I=e=>o.fromValues(e[0],e[1],e[2]),L=e=>a.fromValues(e[0],e[1],e[2],e[3]),he=e=>i.fromValues(...e),R=e=>[e[0],e[1],e[2]],z=e=>[e[0],e[1],e[2],e[3]],ge=e=>[e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]],B=e=>[e[0],e[1],e[2]],_e=e=>[e[0],e[1],e[2],e[3]],ve=e=>[...e],V=Object.freeze([0,0,0,1]),H=Object.freeze([0,0,0]),ye=Object.freeze([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),U=e=>{let t=xe(e);if(t===0)return V;let n=1/t;return[e[0]*n,e[1]*n,e[2]*n,e[3]*n]},be=e=>{for(let t=0;t<16;t++)if(e[t]!==ye[t])return!1;return!0},xe=e=>Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]+e[3]*e[3]),Se=e=>e[0]===0&&e[1]===0&&e[2]===0&&e[3]===1,Ce=(e,t)=>Math.abs(e[0])<t&&Math.abs(e[1])<t&&Math.abs(e[2])<t&&Math.abs(e[3]-1)<t,we=(e,t,n=1e-10)=>{let r=Math.abs(e[0]-t[0])<n&&Math.abs(e[1]-t[1])<n&&Math.abs(e[2]-t[2])<n&&Math.abs(e[3]-t[3])<n,i=Math.abs(e[0]+t[0])<n&&Math.abs(e[1]+t[1])<n&&Math.abs(e[2]+t[2])<n&&Math.abs(e[3]+t[3])<n;return r||i},Te=(e,t)=>{let n=a.create();return a.multiply(n,e,t),[n[0],n[1],n[2],n[3]]},Ee=e=>{let t=a.create();return a.invert(t,e),[t[0],t[1],t[2],t[3]]},W=e=>[-e[2],e[1],e[0]],De=e=>[e[2],e[1],-e[0]],G=e=>[-e[2],e[1],e[0],e[3]],Oe=e=>[e[2],e[1],-e[0],e[3]],K=e=>[e[1],e[2],e[0],e[3]],ke=e=>[e[2],e[0],e[1],e[3]],Ae=(e,t,n)=>{if(!Number.isFinite(e)||!Number.isFinite(t)||!Number.isFinite(n))throw Error(`eulerToQuaternion: non-finite input (alpha=${e}, beta=${t}, gamma=${n})`);let r=e*Math.PI/180,i=t*Math.PI/180,o=n*Math.PI/180,s=a.create(),c=a.create(),l=a.create();a.setAxisAngle(s,[0,0,1],r),a.setAxisAngle(c,[1,0,0],i),a.setAxisAngle(l,[0,1,0],o);let u=a.create();return a.multiply(u,s,c),a.multiply(u,u,l),[u[0],u[1],u[2],u[3]]},je=t({name:`arElements`,initialState:me(),reducers:{recordPhoneHeight:(e,t)=>{e.floorDetections.push(t.payload)},addOrUpdateArPlane:(e,t)=>{let n=t.payload;if(n.parentPlaneId){delete e.arPlanes[n.planeId];return}e.arPlanes[n.planeId]=c(n)},startEventArea:(e,t)=>{e.currentEventAreaId=t.payload.id,e.currentEventAreaPoints=[]},addCornerToEventArea:(e,t)=>{e.currentEventAreaId&&e.currentEventAreaPoints.push(c(B(t.payload.position)))},correctArDriftForEventArea:(e,t)=>{let n=t.payload.drift;for(let t of e.currentEventAreaPoints)t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]},finishEventArea:(e,t)=>{e.currentEventAreaId&&(e.eventAreas.push({id:e.currentEventAreaId,label:t.payload.label,polygon:[],isInArSpace:!0,arPoints:e.currentEventAreaPoints}),e.currentEventAreaId=null,e.currentEventAreaPoints=[])},anchorEventAreaInWorldSpace:(e,t)=>{let{areaId:n,alignmentMatrix:r,zeroRef:i}=t.payload,a=e.eventAreas.findIndex(e=>e.id===n);if(a===-1)return;let s=e.eventAreas[a];if(!s.isInArSpace||!s.arPoints)throw Error(`The area ${s.id} is already anchored in world space`);let c=he(r),l=s.arPoints.map(e=>{let t=I(e);return P(i,o.transformMat4(o.create(),t,c))});e.eventAreas[a]={...s,isInArSpace:!1,polygon:l,arPoints:void 0}},resetArElements:()=>me()}}),{recordPhoneHeight:Me,addOrUpdateArPlane:Ne,startEventArea:Pe,addCornerToEventArea:Fe,correctArDriftForEventArea:Ie,finishEventArea:Le,anchorEventAreaInWorldSpace:Re,resetArElements:ze}=je.actions,Be=p(Me),Ve=p(Ne),He=p(Pe),Ue=p(Fe),We=p(Ie),Ge=p(Le),Ke=p(Re),qe=p(ze),Je=m(je.reducer);function Ye(e,t,n,r){let{inCentroid:i,refCentroid:a,refVec:s,tmp:c}=r;o.set(i,0,0,0),o.set(a,0,0,0);let l=0;for(let n=0;n<e.length;n++){let r=t[n][3];o.scaleAndAdd(i,i,e[n],r),$e(s,t[n]),o.scaleAndAdd(a,a,s,r),l+=r}if(l===0)throw Error(`Weights must not sum to zero when solving Kabsch`);let u=1/l;if(o.scale(i,i,u),o.scale(a,a,u),!n)return{inCentroid:i,refCentroid:a,scaleRatio:1};let d=0,f=0;for(let n=0;n<e.length;n++)o.subtract(c,e[n],i),d+=o.length(c),$e(s,t[n]),o.subtract(c,s,a),f+=o.length(c);return{inCentroid:i,refCentroid:a,scaleRatio:d===0?1:f/d}}function Xe(e,t,n,r,a,s,c){let{negCentroid:l,scaleVec:u}=c;return i.fromRotationTranslation(e,r,n),s&&(o.set(u,a,a,a),i.scale(e,e,u)),o.negate(l,t),i.translate(e,e,l),e}function Ze(e,t,n={}){let r=e.length;if(r!==t.length)throw Error(`Length of the point lists was not equal: ${r} vs ${t.length}`);if(r===0)return i.create();let s=n.solveRotation??!0,c=n.solveScale??!1,l=n.ignoreYAxisForRotation??!1,u=n.rotationIterations??9,{inCentroid:d,refCentroid:f,scaleRatio:p}=Ye(e,t,c,{inCentroid:o.create(),refCentroid:o.create(),refVec:o.create(),tmp:o.create()}),m=a.create();s?new Qe(l).solve(e,t,d,f,m,u):a.identity(m);let h={negCentroid:o.create(),scaleVec:o.create()};return Xe(i.create(),d,f,m,p,c,h)}var Qe=class e{ignoreYAxis;static twoPi=Math.PI*2;static basisX=o.fromValues(1,0,0);static basisY=o.fromValues(0,1,0);static basisZ=o.fromValues(0,0,1);constructor(e){this.ignoreYAxis=e}solve(t,n,r,i,o,s){let c=this.transposeMultSubtract(t,n,r,i);if(this.ignoreYAxis){let t=c[0][0],n=c[0][2],r=c[2][0],i=c[2][2],s=Math.atan2(r-n,t+i);a.setAxisAngle(o,e.basisY,s)}else this.extractRotation(c,o,s)}transposeMultSubtract(e,t,n,r){let i=[o.create(),o.create(),o.create()],a=o.clone(n),s=o.clone(r);this.ignoreYAxis&&(a[1]=0,s[1]=0);let c=o.create(),l=o.create(),u=o.create();for(let n=0;n<e.length;n++){o.copy(c,e[n]),$e(u,t[n]),this.ignoreYAxis&&(c[1]=0,u[1]=0);let r=t[n][3];o.subtract(c,c,a),o.scale(c,c,r),o.subtract(l,u,s),i[0][0]+=c[0]*l[0],i[1][0]+=c[1]*l[0],i[2][0]+=c[2]*l[0],i[0][1]+=c[0]*l[1],i[1][1]+=c[1]*l[1],i[2][1]+=c[2]*l[1],i[0][2]+=c[0]*l[2],i[1][2]+=c[1]*l[2],i[2][2]+=c[2]*l[2]}return i}extractRotation(t,n,r){let i=[o.create(),o.create(),o.create()],s=o.create(),c=o.create(),l=o.create();for(let u=0;u<r;u++){this.fillMatrixFromQuaternion(n,i),o.cross(s,i[0],t[0]),o.cross(l,i[1],t[1]),o.add(s,s,l),o.cross(l,i[2],t[2]),o.add(s,s,l);let r=o.dot(i[0],t[0])+o.dot(i[1],t[1])+o.dot(i[2],t[2])+1e-9;r=Math.abs(r),o.scale(s,s,1/r);let u=o.length(s);if(u<1e-9)break;o.scale(c,s,1/u);let d=u%e.twoPi,f=a.create();a.setAxisAngle(f,c,d),a.multiply(n,f,n),a.normalize(n,n)}}fillMatrixFromQuaternion(t,n){o.transformQuat(n[0],e.basisX,t),o.transformQuat(n[1],e.basisY,t),o.transformQuat(n[2],e.basisZ,t)}};const $e=(e,t)=>(e[0]=t[0],e[1]=t[1],e[2]=t[2],e);function et(e,t,n,r,i,a,o=Math.random){let s=Array.from(e);if(n>s.length)throw RangeError(`minSampleSize must be smaller than the number of elements: minSampleSize=${n}, elements=${s.length}`);return rt(s,tt(s,t,n,r,i,a,o),i,a)}function tt(e,t,n,r,i,a,o){let s=null,c=Array(n),l=new Uint8Array(e.length);for(let u=0;u<r;u+=1){at(e,n,o,c,l);let{inliers:r,outliers:u}=nt(e,l,i(c),a);if(s===null||n+r.length>=t){let e=[...c,...r],t=i(e),n=it(t,`createModel must assign totalModelError for comparison during RANSAC`);(s===null||n<s.error)&&(t.inliers=e,t.outliers=u,s={model:t,error:n})}}return s?.model??null}function nt(e,t,n,r){let i=[],a=[];for(let o=0;o<e.length;o+=1){if(t?.[o])continue;let s=e[o];(r(n,s)?i:a).push(s)}return{inliers:i,outliers:a}}function rt(e,t,n,r){let i=t??n(e);it(i,`RANSAC failed to produce a model with totalModelError`);let{inliers:a,outliers:o}=nt(e,null,i,r);return a.length>0&&(i=n(a),it(i,`createModel must assign totalModelError for final RANSAC model`)),i.inliers=a,i.outliers=o,i}function it(e,t){let{totalModelError:n}=e;if(n==null)throw Error(t);return n}function at(e,t,n,r,i){i.fill(0);let a=0;for(;a<t;){let t=Math.floor(n()*e.length);i[t]||(i[t]=1,r[a]=e[t],a+=1)}}const ot={useOnlyRecentData:!1,recentSeconds:180,includeTimeWeight:!0,weightByTimeFactor:2500,ignoreYAxisForRotation:!0,useRansac:!1,ransacInlierRatio:.95,ransacSampleRatio:.3,ransacMaxIterations:230,ransacErrorTolerance:1,geohashPrecision:8,geohashWindowSize:10,gpsAccuracyExponent:.1,altitudeAlignmentMode:`separate1D`,altitudeAggregator:`mean`,altitudeSampleSet:`all`,gpsVerticalAccuracyExponent:5,useOutlierRejection:!1,outlierThresholdMeters:10,outlierRetainRatio:.6,useDistanceGating:!1,minSampleDistanceMeters:1,maxSampleIntervalMs:3e3},st=e=>e.map(e=>B(e)),ct=e=>e.map(e=>U(e)),lt=e=>e.map(Rt),ut=e=>e.map(e=>[e[0],e[1],e[2],e[3]]),q=e=>Array.isArray(e)?e:R(e),J=e=>Array.isArray(e)?e:z(e),dt=(e,t,n,r)=>({odometryPosOffset:B(q(e)),odometryRotOffset:U(J(t)),latestLoopClosureFixPointPos:n?B(q(n)):null,latestLoopClosureFixPointRot:r?U(J(r)):null}),ft=(e,t,n,r)=>({odometryPositions:[],odometryRotations:[],gpsPositions:[],gpsPositionsVec4:[],alignmentMatrix:[...ye],alignmentRotation:[...V],alignmentTranslation:[...H],alignmentRotationInDegree:[...H],...dt(e,t,n,r),gpsAccuracyMedian:null,gpsAccuracyMean:null,currentGpsPosGeoHash:null,lastAdmittedOdomPos:null,lastAdmittedTs:null}),pt=()=>ft(H,V,null,null),mt=(e,t,n,r)=>{let i=e.map(e=>q(e)),a=t.map(e=>U(J(e))),o=n.map(Rt),s=r.gpsAccuracyExponent;for(let e of o)e.weight=vt(e.latLongAccuracy,s);let c=o.map(It),l=r.includeTimeWeight?Mt(c,o,r.weightByTimeFactor):c,u=r.useOnlyRecentData?At(o,r.recentSeconds):0,d=i.slice(u).map(I),f=o.slice(u);return{allPositionTuples:i,allRotationTuples:a,allGpsPoints:o,allVec4Tuples:l,solverPositionsTyped:d,solverGpsPoints:f,solverWeightedVec4Tuples:l.slice(u),solverVerticalWeights:jt(f,r)}},ht=(e,t,n,r,i)=>{if(e==null||t==null)return!0;let a=n[0]-e[0],o=n[2]-e[2],s=i.minSampleDistanceMeters*i.minSampleDistanceMeters;return a*a+o*o>=s||r-t>=i.maxSampleIntervalMs},gt=(e,t,n,r)=>{if(!r.useDistanceGating)return{odometryPositions:e,odometryRotations:t,gpsPoints:n};let i=[],a=[],o=[],s=null,c=null;for(let l=0;l<n.length;l+=1){let u=q(e[l]),d=n[l].timestamp;ht(s,c,u,d,r)&&(i.push(e[l]),a.push(t[l]),o.push(n[l]),s=u,c=d)}return{odometryPositions:i,odometryRotations:a,gpsPoints:o}},_t=e=>{let{odometryPositions:t,odometryRotations:n,gpsPoints:r,odometryPosOffset:i=H,odometryRotOffset:s=V,latestLoopClosureFixPointPos:c=null,latestLoopClosureFixPointRot:l=null,alignmentConfig:u,random:d}=e;if(t.length!==n.length||t.length!==r.length)throw Error(`GpsEvents requires odometry positions, rotations, and gps points to have identical lengths`);let f={...ot,...u??{}};if(t.length===0)return ft(i,s,c,l);let p=gt(t,n,r,f),{allPositionTuples:m,allRotationTuples:h,allGpsPoints:g,allVec4Tuples:_,solverPositionsTyped:v,solverGpsPoints:y,solverWeightedVec4Tuples:b,solverVerticalWeights:x}=mt(p.odometryPositions,p.odometryRotations,p.gpsPoints,f),S=St(v,b,x,f,d??Math.random),C=a.create(),w=o.create(),T=o.create();bt(S.matrix,C,w,T);let{mean:E,median:D}=Pt(g),O=Nt(p.gpsPoints,f.geohashPrecision,f.geohashWindowSize),k=m.length-1,A=k>=0?B(m[k]):null,j=k>=0?p.gpsPoints[k]?.timestamp??null:null;return{odometryPositions:st(m),odometryRotations:ct(h),gpsPositions:lt(g),gpsPositionsVec4:ut(_),alignmentMatrix:ge(S.matrix),alignmentRotation:z(C),alignmentTranslation:R(w),alignmentRotationInDegree:R(T),...dt(i,s,c,l),gpsAccuracyMedian:D,gpsAccuracyMean:E,currentGpsPosGeoHash:O,lastAdmittedOdomPos:A,lastAdmittedTs:j}},vt=(e,t)=>e!=null&&e>0?1/Math.max(e,1)**+t:1,yt=(e,t,n,r,i,s)=>{let c={...ot,...i??{}},l=q(t);if(c.useDistanceGating&&!ht(e.lastAdmittedOdomPos,e.lastAdmittedTs,l,r.timestamp,c))return;let u=e.odometryPositions,d=e.odometryRotations,f=e.gpsPositions,p=e.gpsPositionsVec4;u.push(l),d.push(U(J(n)));let m=Rt(r);f.push(m),m.weight=vt(m.latLongAccuracy,c.gpsAccuracyExponent);let h=It(m);if(c.includeTimeWeight){p.push(h);let e=Mt(p,f,c.weightByTimeFactor);for(let t=0;t<e.length;t++)p[t]=e[t]}else p.push(h);let g=0;c.useOnlyRecentData&&(g=At(f,c.recentSeconds));let _=St(u.slice(g).map(I),p.slice(g),jt(f.slice(g),c),c,s??Math.random);e.alignmentMatrix=ge(_.matrix);let v=a.create(),y=o.create(),b=o.create();bt(_.matrix,v,y,b),e.alignmentRotation=z(v),e.alignmentTranslation=R(y),e.alignmentRotationInDegree=R(b);let{mean:x,median:S}=Pt(f);e.gpsAccuracyMean=x,e.gpsAccuracyMedian=S,e.currentGpsPosGeoHash=Nt(f,c.geohashPrecision,c.geohashWindowSize),e.lastAdmittedOdomPos=B(l),e.lastAdmittedTs=r.timestamp},bt=(e,t,n,r)=>{i.getRotation(t,e),a.normalize(t,t),i.getTranslation(n,e),Ft(t,r)},Y=o.create(),xt=o.create(),St=(e,t,n,r,a)=>{if(e.length===0)return{matrix:i.create(),meanError:0};let o=e.length,c=[];for(let r=0;r<o;r+=1){let i=t[r];c.push({odom:e[r],ref:s.fromValues(i[0],i[1],i[2],i[3]),verticalWeight:n[r]??1})}let l,u,d;if(r.useRansac&&c.length>=3){let e=c.length,t=Math.max(3,Math.min(e,Math.ceil(e*r.ransacSampleRatio))),n=et(c,Math.max(t,Math.min(e,Math.ceil(e*r.ransacInlierRatio))),t,r.ransacMaxIterations,e=>X(e,r.ignoreYAxisForRotation),(e,t)=>Et(e,t)<e.meanAlignmentError+r.ransacErrorTolerance,a);l=n.alignmentMatrix,u=n.meanAlignmentError,n.inliers!==void 0&&(d=Array.from(n.inliers))}else if(r.useOutlierRejection&&c.length>=4){let{model:e,inliers:t}=kt(c,r);l=e.alignmentMatrix,u=e.meanAlignmentError,d=t}else{let e=X(c,r.ignoreYAxisForRotation);l=e.alignmentMatrix,u=e.meanAlignmentError}return r.altitudeAlignmentMode===`separate1D`&&Ct(l,c,d,r),{matrix:l,meanError:u}},Ct=(e,t,n,r)=>{let i=r.altitudeSampleSet===`horizontalInliers`&&n!==void 0&&n.length>0?n:t;if(i.length===0)return;let a=Array(i.length),o=Array(i.length);for(let e=0;e<i.length;e+=1){let t=i[e];a[e]=t.ref[1]-t.odom[1];let n=t.verticalWeight;o[e]=n>0&&Number.isFinite(n)?n:0}e[13]=r.altitudeAggregator===`median`?Tt(a,o):wt(a,o)},wt=(e,t)=>{let n=0,r=0;for(let i=0;i<e.length;i+=1)n+=e[i]*t[i],r+=t[i];if(r>0&&Number.isFinite(r))return n/r;let i=0;for(let t=0;t<e.length;t+=1)i+=e[t];return e.length===0?0:i/e.length},Tt=(e,t)=>{if(e.length===0)return 0;if(e.length===1)return e[0];let n=e.map((e,t)=>t);n.sort((t,n)=>e[t]-e[n]);let r=0;for(let e=0;e<t.length;e+=1)r+=t[e];if(!(r>0)||!Number.isFinite(r)){let t=n.map(t=>e[t]);return t[Math.floor((t.length-1)/2)]}let i=r/2,a=0;for(let r=0;r<n.length;r+=1)if(a+=t[n[r]],a>=i)return e[n[r]];return e[n[n.length-1]]},X=(e,t)=>{let n=Array.from(e),r=n.length,i=Array(r),a=Array(r);for(let e=0;e<r;e+=1)i[e]=n[e].odom,a[e]=n[e].ref;let s=Ze(i,a,{ignoreYAxisForRotation:t}),c=0;for(let e=0;e<r;e+=1)o.transformMat4(Y,i[e],s),c+=Dt(Y,n[e].ref);return{alignmentMatrix:s,meanAlignmentError:r===0?0:c/r,totalModelError:c}},Et=(e,t)=>(o.transformMat4(Y,t.odom,e.alignmentMatrix),Dt(Y,t.ref)),Dt=(e,t)=>(o.set(xt,t[0],t[1],t[2]),o.squaredDistance(e,xt)),Ot=(e,t)=>{o.transformMat4(Y,t.odom,e.alignmentMatrix);let n=Y[0]-t.ref[0],r=Y[2]-t.ref[2];return n*n+r*r},kt=(e,t)=>{let n=e.length,r=Math.max(3,Math.ceil(t.outlierRetainRatio*n)),i=t.outlierThresholdMeters*t.outlierThresholdMeters,a=Array.from(e),o=X(a,t.ignoreYAxisForRotation);for(;a.length>r;){let e=-1,n=-1;for(let t=0;t<a.length;t+=1){let r=Ot(o,a[t]);r>n&&(n=r,e=t)}if(n<=i||e<0)break;a=a.filter((t,n)=>n!==e),o=X(a,t.ignoreYAxisForRotation)}return{model:o,inliers:a}},At=(e,t)=>{if(e.length===0||t<=0)return 0;let n=e[e.length-1].timestamp-t*1e3;for(let t=0;t<e.length;t+=1)if(e[t].timestamp>=n)return t;return 0},jt=(e,t)=>{let n=t.gpsVerticalAccuracyExponent,r=e.map(e=>{let t=e.altitudeAccuracy;return vt(t!=null&&Number.isFinite(t)&&t>0?t:e.latLongAccuracy,n)});if(!t.includeTimeWeight||e.length<2)return r;let i=e[e.length-1].timestamp,a=e[0].timestamp,o=Math.max(1,i-a),s=t.weightByTimeFactor;return r.map((t,n)=>{if(!(t>0&&Number.isFinite(t)))return 0;let r=s*((i-e[n].timestamp)/o)+1;return 1/(1/t+r)})},Mt=(e,t,n)=>{if(e.length<2)return e.map(e=>[e[0],e[1],e[2],e[3]]);let r=t[t.length-1].timestamp,i=t[0].timestamp,a=Math.max(1,r-i);return e.map((e,i)=>{let o=n*((r-t[i].timestamp)/a)+1,s=t[i].weight;if(!(s>0&&Number.isFinite(s)))return[e[0],e[1],e[2],0];let c=1/(1/s+o);return[e[0],e[1],e[2],c]})},Z=new Map,Nt=(e,t,n)=>{if(e.length===0)return null;let r=Math.max(0,e.length-n);Z.clear();for(let n=r;n<e.length;n+=1){let r=e[n],i=re(Lt(r),t),a=Z.get(i);a?(a.count+=1,a.index=n):Z.set(i,{count:1,index:n})}let i=null,a=-1,o=-1;for(let[e,{count:t,index:n}]of Z)(t>a||t===a&&n>o)&&(i=e,a=t,o=n);return i},Pt=e=>{let t=e.map(e=>e.latLongAccuracy).filter(e=>typeof e==`number`&&Number.isFinite(e));if(t.length===0)return{mean:null,median:null};let n=t.reduce((e,t)=>e+t,0)/t.length,r=t.slice().sort((e,t)=>e-t),i=Math.floor(r.length/2);return{mean:n,median:r.length%2==0?(r[i-1]+r[i])/2:r[i]}},Ft=(e,t)=>{let n=e[0],r=e[1],i=e[2],a=e[3],s=2*(a*n+r*i),c=1-2*(n*n+r*r),l=Math.atan2(s,c),u=2*(a*r-i*n),d;d=Math.abs(u)>=1?Math.PI/2*Math.sign(u):Math.asin(u);let f=2*(a*i+n*r),p=1-2*(r*r+i*i),m=Math.atan2(f,p),h=180/Math.PI,g=t??o.create();return g[0]=l*h,g[1]=d*h,g[2]=m*h,g},It=e=>[e.coordinates[0],e.coordinates[1],e.coordinates[2],e.weight>0&&Number.isFinite(e.weight)?e.weight:1],Lt=e=>({lat:e.latitude,lon:e.longitude}),Rt=e=>({...e,zeroRef:{lat:e.zeroRef.lat,lon:e.zeroRef.lon},coordinates:[e.coordinates[0],e.coordinates[1],e.coordinates[2]],timestamp:e.timestamp,deviceRotation:e.deviceRotation?U(J(e.deviceRotation)):void 0,absoluteOrientation:e.absoluteOrientation?{...e.absoluteOrientation,quaternion:[e.absoluteOrientation.quaternion[0],e.absoluteOrientation.quaternion[1],e.absoluteOrientation.quaternion[2],e.absoluteOrientation.quaternion[3]]}:void 0}),zt=({lat:e,lon:t})=>{if(!Number.isFinite(e))throw Error(`Invalid latitude value: ${e}`);if(!Number.isFinite(t))throw Error(`Invalid longitude value: ${t}`);if(e<-90||e>90)throw Error(`Invalid latitude range: ${e}`);if(t<-180||t>180)throw Error(`Invalid longitude range: ${t}`)},Bt=o.fromValues(1,1,1),Vt=(e,t)=>[e[0]+t[0],e[1]+t[1],e[2]+t[2]],Ht=(e,t,n,r)=>{let i=N(t,{lat:e.latitude,lon:e.longitude},e.altitude??0,0),a=n?.alpha!=null&&n?.beta!=null&&n?.gamma!=null?K(Ae(n.alpha,n.beta,n.gamma)):void 0;return{...e,zeroRef:t,coordinates:R(i),weight:1,deviceRotation:a,absoluteOrientation:r}},Ut=(e,t,n)=>{if(e)return Ae(e.alpha,e.beta,e.gamma);if(t)return t;throw Error(`Sensor rotation missing for ${n}: neither raw orientation nor legacy quaternion provided`)},Wt=e=>{let t=a.invert(a.create(),e);if(!t)throw Error(`Encountered non-invertible quaternion`);return t},Gt=e=>{let t=Wt(a.normalize(a.create(),L(e.lastSensorRot))),n=a.normalize(a.create(),L(e.lastValidOdomRot)),r=a.multiply(a.create(),t,n),i=a.normalize(a.create(),L(e.newSensorRot)),o=a.multiply(a.create(),i,r),s=a.normalize(a.create(),L(e.newOdomRot)),c=a.multiply(a.create(),o,s);return a.normalize(c,c),z(c)},Kt=(e,t)=>{let n=i.create();return i.fromRotationTranslationScale(n,a.normalize(a.create(),L(t)),I(e),Bt),n},qt=(e,t,n)=>{let r=o.create(),s=o.create(),c=a.create(),l=a.create();i.getTranslation(r,e),i.getTranslation(s,t),i.getRotation(c,e),i.getRotation(l,t);let u=o.create();o.lerp(u,r,s,n);let d=a.create();return a.slerp(d,c,l,n),i.fromRotationTranslation(i.create(),d,u)},Jt=(e,t)=>{if(e.length===0)return[];let n=e[e.length-1],r=i.invert(i.create(),n);if(!r)throw Error(`End pose matrix is not invertible`);let a=i.multiply(i.create(),t,r),o=i.create(),s=e.length-1;return e.map((e,t)=>{let n=qt(o,a,s<=0?1:t/s);return i.multiply(i.create(),n,e)})},Yt=(e,t,n)=>{for(let r=e.length-1;r>=0;--r)if(n(e[r],t))return r;return-1},Xt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2],Zt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2]&&e[3]===t[3],Qt=t({name:`gpsData`,initialState:null,reducers:{setZeroPos:{reducer:(e,t)=>e===null?{zero:t.payload,gpsEvents:pt(),odometryPath:fe()}:e,prepare:e=>(zt(e),{payload:e})},recordGpsEvent:(e,t)=>{if(!e)return e;let{odomPosition:n,odomRotation:r,rawGpsPoint:i,rawDeviceOrientation:a,rawAbsoluteOrientation:o}=t.payload,s=Ht(i,e.zero,a,o);yt(e.gpsEvents,W(n),G(r),s)},odometryTrackingRestarted:(e,t)=>{if(!e)return e;let n=Vt(e.gpsEvents.odometryPosOffset,W(t.payload.lastValidOdomPos)),r=Ut(t.payload.lastSensorOrientation,t.payload.lastSensorRot,`lastSensor`),i=Ut(t.payload.newSensorOrientation,t.payload.newSensorRot,`newSensor`);e.gpsEvents=c(_t({odometryPositions:[],odometryRotations:[],gpsPoints:[],odometryPosOffset:n,odometryRotOffset:Gt({...t.payload,lastValidOdomRot:G(t.payload.lastValidOdomRot),newOdomRot:G(t.payload.newOdomRot),lastSensorRot:K(r),newSensorRot:K(i)}),latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},arLoopClosureDetected:(e,t)=>{if(!e)return e;let n=e.gpsEvents;if(n.odometryPositions.length===0)return e;let r=n.latestLoopClosureFixPointPos?Yt(n.odometryPositions,n.latestLoopClosureFixPointPos,Xt):0,s=n.latestLoopClosureFixPointRot?Yt(n.odometryRotations,n.latestLoopClosureFixPointRot,Zt):0;if(r<0||s<0)throw Error(`Loop closure fix point could not be located in history`);if(r!==s)throw Error(`Loop closure fix point indices for position and rotation diverged`);let l=n.odometryPositions.slice(0,r).map(e=>B(e)),u=n.odometryRotations.slice(0,r).map(e=>U(e)),d=n.odometryPositions.slice(r).map(e=>B(e)),f=n.odometryRotations.slice(r).map(e=>U(e)),p=d.map((e,t)=>Kt(e,f[t])),m=Kt(W(t.payload.lastPos),U(G(t.payload.lastRot)));p.push(m);let h=Jt(p,Kt(W(t.payload.newPos),U(G(t.payload.newRot)))),g=[],_=[],v=o.create();for(let e=0;e<h.length-1;e+=1){let t=h[e],n=o.create();i.getTranslation(n,t);let r=a.create();if(i.getRotation(r,t),a.normalize(r,r),i.getScaling(v,t),Math.hypot(v[0]-1,v[1]-1,v[2]-1)>.05)throw Error(`Unexpected scale drift encountered during loop closure correction`);g.push(R(n)),_.push(z(r))}e.gpsEvents=c(_t({odometryPositions:l.concat(g),odometryRotations:u.concat(_),gpsPoints:n.gpsPositions,odometryPosOffset:n.odometryPosOffset,odometryRotOffset:n.odometryRotOffset,latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},add2dImage:(e,t)=>{if(!e)return e;e.odometryPath.points.push(c({imageFile:t.payload.imageFile,screenRotation:t.payload.screenRotation,position:W(t.payload.position),rotation:U(G(t.payload.rotation)),capturedAt:t.payload.capturedAt,width:t.payload.width,height:t.payload.height}))}}}),{setZeroPos:$t,recordGpsEvent:en,odometryTrackingRestarted:tn,arLoopClosureDetected:nn,add2dImage:rn}=Qt.actions,an=p($t),on=p(en),sn=p(tn),cn=p(nn),ln=p(rn),un=m(Qt.reducer),dn=t({name:`gpsElements`,initialState:pe(),reducers:{addMarker:(e,t)=>{e.gpsMarkers.push(t.payload)},addLine:(e,t)=>{e.gpsLines.push(t.payload)},addArea:(e,t)=>{e.areas.push(t.payload)},addToHeatMaps:(e,t)=>{let{category:n,tiles:r}=t.payload,i=e.heatMap[n]??{};e.heatMap[n]={...i,...r}},addHeatMapArea:(e,t)=>{let n=t.payload,r=n.category;if(e.heatAreas[r]||(e.heatAreas[r]={}),e.heatAreas[r][n.geoHash])throw Error(`HeatArea collision detected for GeoHash: ${n.geoHash}`);e.heatAreas[r][n.geoHash]=n},resetGpsElements:()=>pe()}}),{addMarker:fn,addLine:pn,addArea:mn,addToHeatMaps:hn,addHeatMapArea:gn,resetGpsElements:_n}=dn.actions,vn=p(fn),yn=p(pn),bn=p(mn),xn=p(hn),Sn=p(gn),Cn=p(_n),wn=m(dn.reducer),Tn=()=>({gpsData:null,gpsElements:pe(),arElements:me()}),En=e=>{let t=Array.from({length:Math.min(e.length,4)},(t,n)=>e[n].toFixed(2));e.length>4&&t.push(`…`);let n=`[${t.join(`, `)}]`;if(e.length===4){let{pitch:t,yaw:r,roll:i}=v(e);return`${n} (pitch=${t.toFixed(0)}°, yaw=${r.toFixed(0)}°, roll=${i.toFixed(0)}°)`}return n},Dn=e=>(e.length===3||e.length===4||e.length===16)&&e.every(e=>typeof e==`number`),Q=(e,t=0)=>{if(t>10||e==null)return e;if(Array.isArray(e))return Dn(e)?En(e):e.map(e=>Q(e,t+1));if(typeof e==`object`){let n={};for(let[r,i]of Object.entries(e))n[r]=Q(i,t+1);return n}return e},On=(t={})=>{let{preloadedState:n,enableDevToolsSanitizers:r=!0,enableDevChecks:i=!0,licenseKey:a}=t;if(a==null)throw Error(`License key required. Pass options.licenseKey or use createRecorderStore() from gps-plus-slam-app-framework. See EULA §3.`);g(a);let o=Tn(),s=n?{gpsData:n.gpsData??o.gpsData,gpsElements:n.gpsElements??o.gpsElements,arElements:n.arElements??o.arElements}:void 0;return e({reducer:{gpsData:un,gpsElements:wn,arElements:Je},preloadedState:s,middleware:e=>e({serializableCheck:i,immutableCheck:i}),devTools:r?{actionSanitizer:Q,stateSanitizer:Q}:!0})},kn=Math.PI/180,An=180/Math.PI,jn=2025,Mn=2025,Nn=2030,Pn=6378137;Pn*(1-1/298.257223563);const Fn=[[1,0,-29351.8,0,12,0],[1,1,-1410.8,4545.4,9.7,-21.5],[2,0,-2556.6,0,-11.6,0],[2,1,2951.1,-3133.6,-5.2,-27.7],[2,2,1649.3,-815.1,-8,-12.1],[3,0,1361,0,-1.3,0],[3,1,-2404.1,-56.6,-4.2,4],[3,2,1243.8,237.5,.4,-.3],[3,3,453.6,-549.5,-15.6,-4.1],[4,0,895,0,-1.6,0],[4,1,799.5,278.6,-2.4,-1.1],[4,2,55.7,-133.9,-6,4.1],[4,3,-281.1,212,5.6,1.6],[4,4,12.1,-375.6,-7,-4.4],[5,0,-233.2,0,.6,0],[5,1,368.9,45.4,1.4,-.5],[5,2,187.2,220.2,0,2.2],[5,3,-138.7,-122.9,.6,.4],[5,4,-142,43,2.2,1.7],[5,5,20.9,106.1,.9,1.9],[6,0,64.4,0,-.2,0],[6,1,63.8,-18.4,-.4,.3],[6,2,76.9,16.8,.9,-1.6],[6,3,-115.7,48.8,1.2,-.4],[6,4,-40.9,-59.8,-.9,.9],[6,5,14.9,10.9,.3,.7],[6,6,-60.7,72.7,.9,.9],[7,0,79.5,0,-0,0],[7,1,-77,-48.9,-.1,.6],[7,2,-8.8,-14.4,-.1,.5],[7,3,59.3,-1,.5,-.8],[7,4,15.8,23.4,-.1,0],[7,5,2.5,-7.4,-.8,-1],[7,6,-11.1,-25.1,-.8,.6],[7,7,14.2,-2.3,.8,-.2],[8,0,23.2,0,-.1,0],[8,1,10.8,7.1,.2,-.2],[8,2,-17.5,-12.6,0,.5],[8,3,2,11.4,.5,-.4],[8,4,-21.7,-9.7,-.1,.4],[8,5,16.9,12.7,.3,-.5],[8,6,15,.7,.2,-.6],[8,7,-16.8,-5.2,-0,.3],[8,8,.9,3.9,.2,.2],[9,0,4.6,0,-0,0],[9,1,7.8,-24.8,-.1,-.3],[9,2,3,12.2,.1,.3],[9,3,-.2,8.3,.3,-.3],[9,4,-2.5,-3.3,-.3,.3],[9,5,-13.1,-5.2,0,.2],[9,6,2.4,7.2,.3,-.1],[9,7,8.6,-.6,-.1,-.2],[9,8,-8.7,.8,.1,.4],[9,9,-12.9,10,-.1,.1],[10,0,-1.3,0,.1,0],[10,1,-6.4,3.3,0,0],[10,2,.2,0,.1,-0],[10,3,2,2.4,.1,-.2],[10,4,-1,5.3,-0,.1],[10,5,-.6,-9.1,-.3,-.1],[10,6,-.9,.4,0,.1],[10,7,1.5,-4.2,-.1,0],[10,8,.9,-3.8,-.1,-.1],[10,9,-2.7,.9,-0,.2],[10,10,-3.9,-9.1,-0,-0],[11,0,2.9,0,0,0],[11,1,-1.5,0,-0,-0],[11,2,-2.5,2.9,0,.1],[11,3,2.4,-.6,0,-0],[11,4,-.6,.2,0,.1],[11,5,-.1,.5,-.1,-0],[11,6,-.6,-.3,0,-0],[11,7,-.1,-1.2,-0,.1],[11,8,1.1,-1.7,-.1,-0],[11,9,-1,-2.9,-.1,0],[11,10,-.2,-1.8,-.1,0],[11,11,2.6,-2.3,-.1,0],[12,0,-2,0,0,0],[12,1,-.2,-1.3,0,-0],[12,2,.3,.7,-0,0],[12,3,1.2,1,-0,-.1],[12,4,-1.3,-1.4,-0,.1],[12,5,.6,-0,-0,-0],[12,6,.6,.6,.1,-0],[12,7,.5,-.1,-0,-0],[12,8,-.1,.8,0,0],[12,9,-.4,.1,0,-0],[12,10,-.2,-1,-.1,-0],[12,11,-1.3,.1,-0,0],[12,12,-.7,.2,-.1,-.1]];function In(){let e=()=>Array.from({length:13},()=>Array(13).fill(0)),t={g:e(),h:e(),gdot:e(),hdot:e()};for(let[e,n,r,i,a,o]of Fn)t.g[e][n]=r,t.h[e][n]=i,t.gdot[e][n]=a,t.hdot[e][n]=o;return t}const $=In();function Ln(){let e=Array(169).fill(0),t=Array(169).fill(0),n=Array(13).fill(0),r=Array(13).fill(0),i=Array(169).fill(0),a=Array(169).fill(0),o=(e,t)=>e*13+t;for(let e=1;e<=12;e++)for(let t=0;t<=e;t++){let n=$.g[e][t],r=$.h[e][t],s=$.gdot[e][t],c=$.hdot[e][t];i[o(t,e)]=n,a[o(t,e)]=s,t!==0&&(i[o(e,t-1)]=r,a[o(e,t-1)]=c)}e[o(0,0)]=1;for(let s=1;s<=12;s++){e[o(0,s)]=e[o(0,s-1)]*(2*s-1)/s;let c=2;for(let n=0,r=(s-n+1)/1;r>0;r--,n+=1){if(t[o(n,s)]=((s-1)*(s-1)-n*n)/((2*s-1)*(2*s-3)),n>0){let t=(s-n+1)*c/(s+n);e[o(n,s)]=e[o(n-1,s)]*Math.sqrt(t),c=1,i[o(s,n-1)]=e[o(n,s)]*i[o(s,n-1)],a[o(s,n-1)]=e[o(n,s)]*a[o(s,n-1)]}i[o(n,s)]=e[o(n,s)]*i[o(n,s)],a[o(n,s)]=e[o(n,s)]*a[o(n,s)]}n[s]=s+1,r[s]=s}return t[o(1,1)]=0,r[0]=0,{snorm:e,k:t,fn:n,fm:r,c:i,cd:a,idx:o}}const Rn=Ln();function zn(e){return e%4==0&&e%100!=0||e%400==0?366:365}function Bn(e){let t=e.getUTCFullYear(),n=Date.UTC(t,0,1);return t+Math.floor((e.getTime()-n)/864e5)/zn(t)}let Vn=!1;function Hn(e){return e===void 0?2027.5:typeof e==`number`?e>1e4?Bn(new Date(e)):e:Bn(e)}function Un(e,t,n){let r=Pn/1e3,i=6356752.314245179/1e3,a=r*r,o=i*i,s=a-o,c=a*a;c-o*o;let l=t*t,u=n*n,d=Math.sqrt(a-s*l),f=e*d,p=(f+a)/(f+o)*((f+a)/(f+o)),m=t/Math.sqrt(p*u+l),h=Math.sqrt(1-m*m),g=Math.sqrt(e*e+2*f+(c-22083078973482.75*l)/(d*d)),_=Math.sqrt(a*u+o*l);return{r:g,ct:m,st:h,ca:(e+_)/g,sa:s/_*t*n/g}}function Wn(e,t,n,r,i,a,o,s){if(i===a){e[r(a,i)]=o*e[r(a-1,i-1)],t[r(a,i)]=o*t[r(a-1,i-1)]+s*e[r(a-1,i-1)];return}if(i===1&&a===0){e[r(a,i)]=s*e[r(a,i-1)],t[r(a,i)]=s*t[r(a,i-1)]-o*e[r(a,i-1)];return}a>i-2&&(e[r(a,i-2)]=0,t[r(a,i-2)]=0),e[r(a,i)]=s*e[r(a,i-1)]-n[r(a,i)]*e[r(a,i-2)],t[r(a,i)]=s*t[r(a,i-1)]-o*e[r(a,i-1)]-n[r(a,i)]*t[r(a,i-2)]}function Gn(e,t,n,r){let i=r-jn;(r<2025||r>=2030)&&!Vn&&(Vn=!0,console.warn(`magneticDeclinationDeg: date ${r.toFixed(3)} is outside the WMM2025 validity window [${Mn}, ${Nn}); using linear secular extrapolation (accuracy degrades).`));let{snorm:a,k:o,fn:s,fm:c,c:l,cd:u,idx:d}=Rn,f=t*kn,p=e*kn,m=Math.sin(f),h=Math.sin(p),g=Math.cos(f),_=Math.cos(p),v=Array(13).fill(0),y=Array(13).fill(0);v[0]=0,y[0]=1,v[1]=m,y[1]=g;let{r:b,ct:x,st:S,ca:C,sa:w}=Un(n,h,_);for(let e=2;e<=12;e++)v[e]=m*y[e-1]+g*v[e-1],y[e]=g*y[e-1]-m*v[e-1];let T=6371.2/b,E=T*T,D=0,O=0,k=0,A=a.slice(),j=Array(169).fill(0);for(let e=1;e<=12;e++){E*=T;for(let t=0,n=(e+t+1)/1;n>0;n--,t+=1){Wn(A,j,o,d,e,t,S,x);let n=l[d(t,e)]+i*u[d(t,e)],r=E*A[d(t,e)],a,f;if(t===0)a=n*y[t],f=n*v[t];else{let r=l[d(e,t-1)]+i*u[d(e,t-1)];a=n*y[t]+r*v[t],f=n*v[t]-r*y[t]}D-=E*a*j[d(t,e)],O+=c[t]*f*r,k+=s[e]*a*r}}let M;return M=S===0?0:O/S,{x:-D*C-k*w,y:M,z:D*w-k*C}}function Kn(e){let t=e%360;return t<0?t+360:t}function qn(e,t,n){if(!Number.isFinite(e)||!Number.isFinite(t))return NaN;let r=n?.altitudeKm??0;if(!Number.isFinite(r))return NaN;let{x:i,y:a}=Gn(e,t,r,Hn(n?.date));return Math.atan2(a,i)*An}function Jn(e,t,n,r){return Kn(e+qn(t,n,r))}const Yn=.08,Xn=180/Math.PI,Zn=e=>(e%360+360)%360,Qn=(e,t)=>{let[n,r,i,a]=e,[o,s,c]=t,l=2*(r*c-i*s),u=2*(i*o-n*c),d=2*(n*s-r*o);return[o+a*l+(r*d-i*u),s+a*u+(i*l-n*d),c+a*d+(n*u-r*l)]};function $n(e){let[t,n,r,i]=e,a=-2*(t*r+i*n),o=-2*(n*r-i*t);return Math.hypot(a,o)<Yn?null:Zn(Math.atan2(a,o)*Xn)}const er=[0,0,-1];function tr(e,t){return Te(e,Ee(t))}function nr(e,t){let[n,r]=Qn(tr(e,t),er);return Math.hypot(n,r)<Yn?null:Zn(Math.atan2(n,r)*Xn)}function rr(e,t){let n=((e-t+180)%360+360)%360-180;return n===-180&&(n=180),n}const ir=[],ar=[],or=[],sr={median:null,mean:null},cr=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentMatrix}),lr=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentRotation}),ur=f(function(e){return e.gpsData?.gpsEvents?.odometryPositions??ir}),dr=f(function(e){return e.gpsData?.gpsEvents?.odometryRotations??ar}),fr=f(function(e){return e.gpsData?.gpsEvents?.gpsPositions??or}),pr=f(function(e){return e.gpsData?.zero??null}),mr=f(function(e){let t=e.gpsData?.gpsEvents?.gpsAccuracyMedian??null,n=e.gpsData?.gpsEvents?.gpsAccuracyMean??null;return t===null&&n===null?sr:{median:t,mean:n}}),hr=`1.0.0`;export{ye as IDENTITY_MATRIX4,V as IDENTITY_QUATERNION,hr as LIB_VERSION,jn as WMM2025_EPOCH,Mn as WMM2025_VALID_FROM,Nn as WMM2025_VALID_TO,H as ZERO_VECTOR3,ln as add2dImage,bn as addArea,Ue as addCornerToEventArea,Sn as addHeatMapArea,yn as addLine,vn as addMarker,Ve as addOrUpdateArPlane,xn as addToHeatMaps,Ke as anchorEventAreaInWorldSpace,Je as arElementsReducer,cn as arLoopClosureDetected,nr as arNorthBearingDeg,tr as arToEnuRotation,rr as bearingDeltaDeg,re as calcGeoHash,P as calcGpsCoords,te as calcQuadKey,N as calcRelativeCoordsInMeters,ve as cloneMatrix4,_e as cloneQuaternion,B as cloneVector3,We as correctArDriftForEventArea,On as createGpsSlamStore,ae as distanceInMeters,oe as distanceInMetersRelative,K as enuQuaternionToNUE,Ae as eulerToQuaternion,Ge as finishEventArea,he as fromMatrix4,L as fromQuaternion,I as fromVector3,de as fusedGpsFromOdom,ie as geoHashToLatLong,cr as getAlignmentMatrix,lr as getAlignmentRotation,le as getGoogleMapsDirectionsLink,ce as getGoogleMapsLink,mr as getGpsAccuracyStats,fr as getGpsPositions,ur as getOdometryPositions,dr as getOdometryRotations,ue as getOpenStreetMapLink,pr as getZeroReference,un as gpsDataReducer,wn as gpsElementsReducer,Ee as invertQuaternion,be as isIdentityMatrix4,Se as isIdentityQuaternion,Ce as isNearIdentityQuaternion,qn as magneticDeclinationDeg,$n as magneticHeadingFromEnuQuat,Jn as magneticToTrueHeadingDeg,Te as multiplyQuaternions,U as normalizeQuaternion,ke as nueQuaternionToENU,Oe as nueQuaternionToWebXR,De as nueToWebXR,sn as odometryTrackingRestarted,ne as quadKeyToLatLong,xe as quaternionMagnitude,we as quaternionsEquivalent,on as recordGpsEvent,Be as recordPhoneHeight,qe as resetArElements,Cn as resetGpsElements,Q as sanitizeForDevTools,an as setZeroPos,He as startEventArea,se as toEarthCenteredCoordinates,ge as toMatrix4,z as toQuaternion,R as toVector3,g as validateLicenseKey,G as webxrQuaternionToNUE,W as webxrToNUE};
@@ -35,6 +35,6 @@
35
35
  * @see docs/2026-04-25-private-key-security-plan.md — secret-backed renewal
36
36
  * @see ../../GpsPlusSlamJs_Docs/docs/2026-05-01-community-key-resign-cross-repo-issue.md
37
37
  */
38
- declare const COMMUNITY_LICENSE_KEY = "eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODEyOTEyMTg2fQ.eRcW8Abz6alOWXkRQGyA4w3s_n9lMkO_cjbmHBB0fx18675sCPDom2DL4Vu17k7Mgk9sn5T3HTHoo5BwIs_IBQ";
38
+ declare const COMMUNITY_LICENSE_KEY = "eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODEzOTA4MDgzfQ.zzBeiFZSpkdY22kZKTL5WuexQ_MZOe0o7KTmuv7p873-66tZYWIjYUPkRXt4LI_XAJFpmeCPxlEpvpw1pqCgBw";
39
39
  //#endregion
40
40
  export { COMMUNITY_LICENSE_KEY };
@@ -1,2 +1,2 @@
1
1
  /*! gps-plus-slam-js | (c) 2026 cs-util-com | UNLICENSED — see EULA.md */
2
- const e=`eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODEyOTEyMTg2fQ.eRcW8Abz6alOWXkRQGyA4w3s_n9lMkO_cjbmHBB0fx18675sCPDom2DL4Vu17k7Mgk9sn5T3HTHoo5BwIs_IBQ`;export{e as COMMUNITY_LICENSE_KEY};
2
+ const e=`eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODEzOTA4MDgzfQ.zzBeiFZSpkdY22kZKTL5WuexQ_MZOe0o7KTmuv7p873-66tZYWIjYUPkRXt4LI_XAJFpmeCPxlEpvpw1pqCgBw`;export{e as COMMUNITY_LICENSE_KEY};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gps-plus-slam-js",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "TypeScript library for real-time GPS + AR odometry alignment.",
5
5
  "author": "cs-util-com",
6
6
  "keywords": [