gps-plus-slam-app-framework 1.0.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 +201 -0
- package/README.md +222 -0
- package/dist/alignment-lerper-D9BeDwZX.d.ts +29 -0
- package/dist/app-selectors-DyMzxCEC.d.ts +140 -0
- package/dist/ar/camera-blit-capture.d.ts +2 -0
- package/dist/ar/camera-blit-capture.js +253 -0
- package/dist/ar/capture-failure-tracker.d.ts +2 -0
- package/dist/ar/capture-failure-tracker.js +45 -0
- package/dist/ar/chromium-camera-access-workaround.d.ts +2 -0
- package/dist/ar/chromium-camera-access-workaround.js +32 -0
- package/dist/ar/depth-sampler.d.ts +3 -0
- package/dist/ar/depth-sampler.js +147 -0
- package/dist/ar/image-capture.d.ts +2 -0
- package/dist/ar/image-capture.js +152 -0
- package/dist/ar/index.d.ts +12 -0
- package/dist/ar/index.js +12 -0
- package/dist/ar/replay-scene.d.ts +2 -0
- package/dist/ar/replay-scene.js +325 -0
- package/dist/ar/scene-node-names.d.ts +2 -0
- package/dist/ar/scene-node-names.js +16 -0
- package/dist/ar/tracking-state.d.ts +2 -0
- package/dist/ar/tracking-state.js +164 -0
- package/dist/ar/webxr-session.d.ts +4 -0
- package/dist/ar/webxr-session.js +890 -0
- package/dist/ar/xr-camera-texture.d.ts +2 -0
- package/dist/ar/xr-camera-texture.js +39 -0
- package/dist/ar/xr-error-handler.d.ts +2 -0
- package/dist/ar/xr-error-handler.js +37 -0
- package/dist/ar-types-B-ORgk6Z.d.ts +82 -0
- package/dist/camera-blit-capture-DHuyrrxr.d.ts +108 -0
- package/dist/camera-follower-BMqPddxx.d.ts +29 -0
- package/dist/capture-failure-tracker-2Q6oI2uQ.d.ts +52 -0
- package/dist/chromium-camera-access-workaround-CU5zSKNr.d.ts +61 -0
- package/dist/community-license-key-ChZLCJyO.d.ts +21 -0
- package/dist/concurrency-p6gB30fT.d.ts +24 -0
- package/dist/css3d-renderer-manager-rgLMVAvd.d.ts +23 -0
- package/dist/depth-sampler-BNCtnEoP.d.ts +108 -0
- package/dist/failure-tracker-1sQ_Cgyw.d.ts +49 -0
- package/dist/file-system-BsQgYCU3.d.ts +128 -0
- package/dist/file-system-utils-5cWO8Xyv.d.ts +24 -0
- package/dist/format-file-size-onGq0k1Q.d.ts +24 -0
- package/dist/fused-path-DtqCSqJh.d.ts +25 -0
- package/dist/geo-types-Xx-LVcnw.d.ts +29 -0
- package/dist/gps-Ddzr2KlX.d.ts +54 -0
- package/dist/gps-compass-cubes-C-6JEnpR.d.ts +22 -0
- package/dist/gps-error-handler-VPuNOuf8.d.ts +40 -0
- package/dist/gps-event-markers-DpEy2qRd.d.ts +67 -0
- package/dist/h3-ref-point-CFhayNSZ.d.ts +59 -0
- package/dist/image-capture-2pKjYUS6.d.ts +130 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +61 -0
- package/dist/leaflet-map-overlay-CvfQXTye.d.ts +114 -0
- package/dist/lerp-utils-87NWjUOD.d.ts +20 -0
- package/dist/licensing/community-license-key.d.ts +2 -0
- package/dist/licensing/community-license-key.js +21 -0
- package/dist/licensing/index.d.ts +2 -0
- package/dist/licensing/index.js +2 -0
- package/dist/list-formatter-DKQQVLcl.d.ts +14 -0
- package/dist/logger-B81iwxx0.d.ts +74 -0
- package/dist/map-overlay-DuiZuCTx.d.ts +153 -0
- package/dist/null-storage-backend-CsWvQwN8.d.ts +11 -0
- package/dist/opfs-storage-ChOzF_Wp.d.ts +181 -0
- package/dist/opfs-storage-backend-CVcmfJHN.d.ts +11 -0
- package/dist/permission-checker-jO2ziCWC.d.ts +146 -0
- package/dist/persistence-middleware-DrptY02i.d.ts +31 -0
- package/dist/recorder-slice-fcwAXrsh.d.ts +35 -0
- package/dist/recording-coordinator-DzflP07_.d.ts +81 -0
- package/dist/recording-options-BYUFX8iU.d.ts +166 -0
- package/dist/recording-replayer-oOzPITrd.d.ts +32 -0
- package/dist/ref-point-importer-B_wn5ABk.d.ts +62 -0
- package/dist/ref-point-loader-Pt1smgHS.d.ts +117 -0
- package/dist/ref-points/h3-ref-point.d.ts +2 -0
- package/dist/ref-points/h3-ref-point.js +74 -0
- package/dist/ref-points/index.d.ts +2 -0
- package/dist/ref-points/index.js +2 -0
- package/dist/ref-points-slice-tC-Wd6XJ.d.ts +48 -0
- package/dist/reference-points-CFWy8_wj.d.ts +68 -0
- package/dist/replay-engine-DuX316ae.d.ts +103 -0
- package/dist/replay-scene-DtUm6bsr.d.ts +109 -0
- package/dist/routing-slice-BlHTM8eh.d.ts +23 -0
- package/dist/scene-node-names-DPsaE2z3.d.ts +14 -0
- package/dist/sensors/gps-error-handler.d.ts +2 -0
- package/dist/sensors/gps-error-handler.js +53 -0
- package/dist/sensors/gps.d.ts +2 -0
- package/dist/sensors/gps.js +93 -0
- package/dist/sensors/index.d.ts +4 -0
- package/dist/sensors/index.js +4 -0
- package/dist/sensors/permission-checker.d.ts +2 -0
- package/dist/sensors/permission-checker.js +397 -0
- package/dist/state/app-selectors.d.ts +2 -0
- package/dist/state/app-selectors.js +35 -0
- package/dist/state/index.d.ts +17 -0
- package/dist/state/index.js +13 -0
- package/dist/state/persistence-middleware.d.ts +2 -0
- package/dist/state/persistence-middleware.js +71 -0
- package/dist/state/recorder-slice.d.ts +2 -0
- package/dist/state/recorder-slice.js +47 -0
- package/dist/state/recording-coordinator.d.ts +2 -0
- package/dist/state/recording-coordinator.js +150 -0
- package/dist/state/recording-options.d.ts +2 -0
- package/dist/state/recording-options.js +192 -0
- package/dist/state/recording-replayer.d.ts +2 -0
- package/dist/state/recording-replayer.js +39 -0
- package/dist/state/ref-points-slice.d.ts +2 -0
- package/dist/state/ref-points-slice.js +44 -0
- package/dist/state/replay-engine.d.ts +2 -0
- package/dist/state/replay-engine.js +230 -0
- package/dist/state/routing-slice.d.ts +2 -0
- package/dist/state/routing-slice.js +13 -0
- package/dist/state/store-subscribers.d.ts +3 -0
- package/dist/state/store-subscribers.js +97 -0
- package/dist/state/store.d.ts +9 -0
- package/dist/state/store.js +71 -0
- package/dist/state/subscribe-to-selector.d.ts +2 -0
- package/dist/state/subscribe-to-selector.js +26 -0
- package/dist/storage/file-system-utils.d.ts +2 -0
- package/dist/storage/file-system-utils.js +31 -0
- package/dist/storage/file-system.d.ts +2 -0
- package/dist/storage/file-system.js +229 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.js +10 -0
- package/dist/storage/null-storage-backend.d.ts +2 -0
- package/dist/storage/null-storage-backend.js +8 -0
- package/dist/storage/opfs-storage-backend.d.ts +2 -0
- package/dist/storage/opfs-storage-backend.js +15 -0
- package/dist/storage/opfs-storage.d.ts +2 -0
- package/dist/storage/opfs-storage.js +242 -0
- package/dist/storage/ref-point-importer.d.ts +2 -0
- package/dist/storage/ref-point-importer.js +163 -0
- package/dist/storage/ref-point-loader.d.ts +2 -0
- package/dist/storage/ref-point-loader.js +248 -0
- package/dist/storage/ref-point-recovery.d.ts +28 -0
- package/dist/storage/ref-point-recovery.js +159 -0
- package/dist/storage/storage-backend.d.ts +2 -0
- package/dist/storage/storage-backend.js +1 -0
- package/dist/storage/zip-export.d.ts +2 -0
- package/dist/storage/zip-export.js +214 -0
- package/dist/storage/zip-reader.d.ts +2 -0
- package/dist/storage/zip-reader.js +190 -0
- package/dist/storage-backend-BcEItnFr.d.ts +20 -0
- package/dist/store-C7PKOj8S.d.ts +96 -0
- package/dist/store-subscribers-CuoIzB5T.d.ts +77 -0
- package/dist/subscribe-to-selector-UTZ0KkAc.d.ts +26 -0
- package/dist/test-utils/browser-mocks.d.ts +294 -0
- package/dist/test-utils/browser-mocks.js +568 -0
- package/dist/test-utils/zip-round-trip-helpers.d.ts +60 -0
- package/dist/test-utils/zip-round-trip-helpers.js +207 -0
- package/dist/three-dispose-CCBHjE_a.d.ts +30 -0
- package/dist/tracking-state-B9QfuZCg.d.ts +101 -0
- package/dist/types/ar-types.d.ts +2 -0
- package/dist/types/ar-types.js +1 -0
- package/dist/types/geo-types.d.ts +2 -0
- package/dist/types/geo-types.js +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/concurrency.d.ts +2 -0
- package/dist/utils/concurrency.js +40 -0
- package/dist/utils/failure-tracker.d.ts +2 -0
- package/dist/utils/failure-tracker.js +48 -0
- package/dist/utils/format-file-size.d.ts +2 -0
- package/dist/utils/format-file-size.js +41 -0
- package/dist/utils/fused-path.d.ts +2 -0
- package/dist/utils/fused-path.js +44 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/list-formatter.d.ts +2 -0
- package/dist/utils/list-formatter.js +17 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +226 -0
- package/dist/vis-colors-Qi2vlRGt.d.ts +63 -0
- package/dist/visualization/alignment-lerper.d.ts +2 -0
- package/dist/visualization/alignment-lerper.js +68 -0
- package/dist/visualization/camera-follower.d.ts +2 -0
- package/dist/visualization/camera-follower.js +51 -0
- package/dist/visualization/css3d-renderer-manager.d.ts +2 -0
- package/dist/visualization/css3d-renderer-manager.js +41 -0
- package/dist/visualization/gps-compass-cubes.d.ts +2 -0
- package/dist/visualization/gps-compass-cubes.js +129 -0
- package/dist/visualization/gps-event-markers.d.ts +2 -0
- package/dist/visualization/gps-event-markers.js +159 -0
- package/dist/visualization/index.d.ts +12 -0
- package/dist/visualization/index.js +12 -0
- package/dist/visualization/leaflet-map-overlay.d.ts +2 -0
- package/dist/visualization/leaflet-map-overlay.js +313 -0
- package/dist/visualization/lerp-utils.d.ts +2 -0
- package/dist/visualization/lerp-utils.js +22 -0
- package/dist/visualization/map-overlay.d.ts +2 -0
- package/dist/visualization/map-overlay.js +241 -0
- package/dist/visualization/reference-points.d.ts +2 -0
- package/dist/visualization/reference-points.js +160 -0
- package/dist/visualization/three-dispose.d.ts +2 -0
- package/dist/visualization/three-dispose.js +55 -0
- package/dist/visualization/vis-colors.d.ts +2 -0
- package/dist/visualization/vis-colors.js +74 -0
- package/dist/webxr-session-COlSrXdL.d.ts +316 -0
- package/dist/xr-camera-texture-BbukNE79.d.ts +53 -0
- package/dist/xr-error-handler-DTHfmvrl.d.ts +24 -0
- package/dist/zip-export-Rtpi75JJ.d.ts +87 -0
- package/dist/zip-reader-D7pkpX8K.d.ts +97 -0
- package/package.json +104 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/browser";
|
|
2
|
+
//#region ../src/utils/logger.ts
|
|
3
|
+
/**
|
|
4
|
+
* Configurable Logger Utility
|
|
5
|
+
*
|
|
6
|
+
* Provides a simple logging API with configurable log levels.
|
|
7
|
+
* This allows disabling verbose logging in production while keeping
|
|
8
|
+
* it available for development and debugging.
|
|
9
|
+
*
|
|
10
|
+
* Also maintains an in-memory ring buffer of recent log entries
|
|
11
|
+
* for display in the expandable log panel UI.
|
|
12
|
+
*
|
|
13
|
+
* Sentry Integration:
|
|
14
|
+
* - All log levels add Sentry breadcrumbs for debugging context
|
|
15
|
+
* - log.error() with Error objects automatically reports to Sentry
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Log levels in order of verbosity (lower = more verbose)
|
|
19
|
+
*/
|
|
20
|
+
let LogLevel = /* @__PURE__ */ function(LogLevel) {
|
|
21
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
22
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
23
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
24
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
25
|
+
LogLevel[LogLevel["SILENT"] = 4] = "SILENT";
|
|
26
|
+
return LogLevel;
|
|
27
|
+
}({});
|
|
28
|
+
/**
|
|
29
|
+
* Global log level - controls what gets logged across all loggers
|
|
30
|
+
*/
|
|
31
|
+
let globalLogLevel = 0;
|
|
32
|
+
/**
|
|
33
|
+
* Maximum number of log entries to keep in the ring buffer
|
|
34
|
+
*/
|
|
35
|
+
const LOG_BUFFER_MAX_SIZE = 100;
|
|
36
|
+
/**
|
|
37
|
+
* In-memory ring buffer of recent log entries
|
|
38
|
+
*/
|
|
39
|
+
let logBuffer = [];
|
|
40
|
+
let subscribers = [];
|
|
41
|
+
/**
|
|
42
|
+
* Serialize an Error instance to an object capturing name, message, stack,
|
|
43
|
+
* and any enumerable properties.
|
|
44
|
+
*/
|
|
45
|
+
function serializeError(error) {
|
|
46
|
+
const result = {
|
|
47
|
+
name: error.name,
|
|
48
|
+
message: error.message,
|
|
49
|
+
stack: error.stack
|
|
50
|
+
};
|
|
51
|
+
for (const key of Object.keys(error)) result[key] = error[key];
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Safely stringify an object, handling circular references.
|
|
56
|
+
* Uses a WeakSet to track seen objects and replaces cycles with a placeholder.
|
|
57
|
+
*/
|
|
58
|
+
function safeStringify(value) {
|
|
59
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
60
|
+
const replacer = (_key, val) => {
|
|
61
|
+
if (typeof val === "bigint") return val.toString();
|
|
62
|
+
if (val !== null && typeof val === "object") {
|
|
63
|
+
if (seen.has(val)) return "[Circular]";
|
|
64
|
+
seen.add(val);
|
|
65
|
+
}
|
|
66
|
+
return val;
|
|
67
|
+
};
|
|
68
|
+
try {
|
|
69
|
+
return JSON.stringify(value, replacer);
|
|
70
|
+
} catch {
|
|
71
|
+
return "[Unserializable]";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Serialize a single argument to a string for the log buffer.
|
|
76
|
+
* Special-cases Error instances and handles circular/complex objects safely.
|
|
77
|
+
*/
|
|
78
|
+
function serializeArg(arg) {
|
|
79
|
+
if (arg === null) return "null";
|
|
80
|
+
if (arg === void 0) return "undefined";
|
|
81
|
+
if (typeof arg === "string") return arg;
|
|
82
|
+
if (typeof arg === "number" || typeof arg === "boolean") return String(arg);
|
|
83
|
+
if (typeof arg === "bigint") return arg.toString();
|
|
84
|
+
if (typeof arg === "symbol") return arg.toString();
|
|
85
|
+
if (typeof arg === "function") return arg.name ? `[Function: ${arg.name}]` : "[Function]";
|
|
86
|
+
if (arg instanceof Error) return safeStringify(serializeError(arg));
|
|
87
|
+
return safeStringify(arg);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Add an entry to the log buffer and notify subscribers
|
|
91
|
+
*/
|
|
92
|
+
function addToBuffer(level, tag, args) {
|
|
93
|
+
const message = args.map((arg) => serializeArg(arg)).join(" ");
|
|
94
|
+
const entry = {
|
|
95
|
+
timestamp: Date.now(),
|
|
96
|
+
level,
|
|
97
|
+
tag,
|
|
98
|
+
message
|
|
99
|
+
};
|
|
100
|
+
logBuffer.push(entry);
|
|
101
|
+
if (logBuffer.length > LOG_BUFFER_MAX_SIZE) logBuffer.shift();
|
|
102
|
+
for (const subscriber of subscribers) try {
|
|
103
|
+
subscriber(entry);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.error("[Logger] Subscriber threw an error:", err);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get a copy of the current log buffer
|
|
110
|
+
* @returns Array of log entries (copy to prevent external mutation)
|
|
111
|
+
*/
|
|
112
|
+
function getLogBuffer() {
|
|
113
|
+
return [...logBuffer];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Clear all entries from the log buffer
|
|
117
|
+
*/
|
|
118
|
+
function clearLogBuffer() {
|
|
119
|
+
logBuffer = [];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Subscribe to new log entries
|
|
123
|
+
* @param callback - Function called when a new log entry is added
|
|
124
|
+
* @returns Unsubscribe function
|
|
125
|
+
*/
|
|
126
|
+
function subscribeToLogs(callback) {
|
|
127
|
+
subscribers.push(callback);
|
|
128
|
+
return () => {
|
|
129
|
+
subscribers = subscribers.filter((sub) => sub !== callback);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Set the global log level
|
|
134
|
+
* @param level - The minimum level to log
|
|
135
|
+
*/
|
|
136
|
+
function setGlobalLogLevel(level) {
|
|
137
|
+
globalLogLevel = level;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get the current global log level
|
|
141
|
+
*/
|
|
142
|
+
function getGlobalLogLevel() {
|
|
143
|
+
return globalLogLevel;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Create a logger with a specific tag prefix
|
|
147
|
+
* @param tag - The tag to prefix all log messages with (e.g., 'GPS', 'Storage')
|
|
148
|
+
* @returns A logger object with debug, info, warn, error methods
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const log = createLogger('GPS');
|
|
153
|
+
* log.info('Watch started'); // [GPS] Watch started
|
|
154
|
+
* log.error('Error:', err); // [GPS] Error: <error details>
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
/**
|
|
158
|
+
* Map LogLevel to Sentry breadcrumb severity.
|
|
159
|
+
* Sentry uses 'warning' (not 'warn') for the warning level.
|
|
160
|
+
*/
|
|
161
|
+
function toSentryLevel(level) {
|
|
162
|
+
switch (level) {
|
|
163
|
+
case 0: return "debug";
|
|
164
|
+
case 1: return "info";
|
|
165
|
+
case 2: return "warning";
|
|
166
|
+
case 3: return "error";
|
|
167
|
+
default: return "info";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Add a Sentry breadcrumb for debugging context.
|
|
172
|
+
* Breadcrumbs are attached to future exceptions, helping trace what happened before an error.
|
|
173
|
+
*/
|
|
174
|
+
function addSentryBreadcrumb(level, tag, args) {
|
|
175
|
+
const message = `[${tag}] ${args.map((arg) => serializeArg(arg)).join(" ")}`;
|
|
176
|
+
Sentry.addBreadcrumb({
|
|
177
|
+
category: "log",
|
|
178
|
+
level: toSentryLevel(level),
|
|
179
|
+
message
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Report a warning to Sentry as a standalone message.
|
|
184
|
+
* Called for log.warn() so warnings are independently visible in the
|
|
185
|
+
* Sentry dashboard, not only as breadcrumbs attached to later exceptions.
|
|
186
|
+
*/
|
|
187
|
+
function reportWarningToSentry(tag, args) {
|
|
188
|
+
const message = `[${tag}] ${args.map((arg) => serializeArg(arg)).join(" ")}`;
|
|
189
|
+
Sentry.captureMessage(message, "warning");
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Report Error objects to Sentry.
|
|
193
|
+
* Only called for log.error() - other levels just add breadcrumbs.
|
|
194
|
+
*/
|
|
195
|
+
function reportErrorsToSentry(args) {
|
|
196
|
+
for (const arg of args) if (arg instanceof Error) Sentry.captureException(arg);
|
|
197
|
+
}
|
|
198
|
+
function createLogger(tag) {
|
|
199
|
+
const prefix = `[${tag}]`;
|
|
200
|
+
return {
|
|
201
|
+
debug: (...args) => {
|
|
202
|
+
addToBuffer(0, tag, args);
|
|
203
|
+
addSentryBreadcrumb(0, tag, args);
|
|
204
|
+
if (globalLogLevel <= 0) console.log(prefix, ...args);
|
|
205
|
+
},
|
|
206
|
+
info: (...args) => {
|
|
207
|
+
addToBuffer(1, tag, args);
|
|
208
|
+
addSentryBreadcrumb(1, tag, args);
|
|
209
|
+
if (globalLogLevel <= 1) console.log(prefix, ...args);
|
|
210
|
+
},
|
|
211
|
+
warn: (...args) => {
|
|
212
|
+
addToBuffer(2, tag, args);
|
|
213
|
+
addSentryBreadcrumb(2, tag, args);
|
|
214
|
+
reportWarningToSentry(tag, args);
|
|
215
|
+
if (globalLogLevel <= 2) console.warn(prefix, ...args);
|
|
216
|
+
},
|
|
217
|
+
error: (...args) => {
|
|
218
|
+
addToBuffer(3, tag, args);
|
|
219
|
+
addSentryBreadcrumb(3, tag, args);
|
|
220
|
+
reportErrorsToSentry(args);
|
|
221
|
+
if (globalLogLevel <= 3) console.error(prefix, ...args);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
226
|
+
export { LogLevel, clearLogBuffer, createLogger, getGlobalLogLevel, getLogBuffer, setGlobalLogLevel, subscribeToLogs };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region ../src/visualization/vis-colors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Centralized Visualization Color Palette
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth for all semantic colors used across the app's
|
|
6
|
+
* visualization layers (Three.js 3D scene and Leaflet 2D summary map).
|
|
7
|
+
*
|
|
8
|
+
* Each entry provides both hex (number, for Three.js) and css (string,
|
|
9
|
+
* for Leaflet/CSS) formats. Some entries intentionally use different 3D/2D
|
|
10
|
+
* shades — see inline comments.
|
|
11
|
+
*
|
|
12
|
+
* Tailwind classes in index.html (e.g., bg-yellow-400) cannot be dynamically
|
|
13
|
+
* generated and must be kept in sync manually — search for "vis-colors" in
|
|
14
|
+
* the HTML legend to find them.
|
|
15
|
+
*/
|
|
16
|
+
declare const VIS_COLORS: {
|
|
17
|
+
/** Raw GPS marker — yellow */readonly RAW_GPS: {
|
|
18
|
+
readonly hex: 16776960;
|
|
19
|
+
readonly css: "#ffff00";
|
|
20
|
+
}; /** Fused VIO marker — cyan */
|
|
21
|
+
readonly FUSED_VIO: {
|
|
22
|
+
readonly hex: 65535;
|
|
23
|
+
readonly css: "#00ffff";
|
|
24
|
+
}; /** Alignment snapshot marker — red */
|
|
25
|
+
readonly ALIGNMENT_SNAPSHOT: {
|
|
26
|
+
readonly hex: 16711680;
|
|
27
|
+
readonly css: "#ff0000";
|
|
28
|
+
}; /** Prior reference point — green */
|
|
29
|
+
readonly PRIOR_REF_POINT: {
|
|
30
|
+
readonly hex: 65280;
|
|
31
|
+
readonly css: "#00ff00";
|
|
32
|
+
}; /** Current reference point — bright red in 3D, lighter red (#ff6b6b) in 2D for map visibility */
|
|
33
|
+
readonly CURRENT_REF_POINT: {
|
|
34
|
+
readonly hex: 16711680;
|
|
35
|
+
readonly css: "#ff6b6b";
|
|
36
|
+
}; /** Compass north indicator — red */
|
|
37
|
+
readonly COMPASS_NORTH: {
|
|
38
|
+
readonly hex: 16711680;
|
|
39
|
+
readonly css: "#ff0000";
|
|
40
|
+
}; /** Compass east indicator — blue */
|
|
41
|
+
readonly COMPASS_EAST: {
|
|
42
|
+
readonly hex: 255;
|
|
43
|
+
readonly css: "#0000ff";
|
|
44
|
+
}; /** Compass south indicator — dark red */
|
|
45
|
+
readonly COMPASS_SOUTH: {
|
|
46
|
+
readonly hex: 8930372;
|
|
47
|
+
readonly css: "#884444";
|
|
48
|
+
}; /** Compass west indicator — dark blue */
|
|
49
|
+
readonly COMPASS_WEST: {
|
|
50
|
+
readonly hex: 4473992;
|
|
51
|
+
readonly css: "#444488";
|
|
52
|
+
}; /** Compass up indicator — green */
|
|
53
|
+
readonly COMPASS_UP: {
|
|
54
|
+
readonly hex: 65280;
|
|
55
|
+
readonly css: "#00ff00";
|
|
56
|
+
}; /** User GPS position marker — blue */
|
|
57
|
+
readonly USER_POSITION: {
|
|
58
|
+
readonly hex: 3900150;
|
|
59
|
+
readonly css: "#3b82f6";
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
export { VIS_COLORS as t };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { clampedAlpha } from "./lerp-utils.js";
|
|
2
|
+
import * as THREE from "three";
|
|
3
|
+
//#region ../src/visualization/alignment-lerper.ts
|
|
4
|
+
/**
|
|
5
|
+
* Alignment Lerper Module
|
|
6
|
+
*
|
|
7
|
+
* Smoothly interpolates arWorldGroup.matrix toward a target alignment
|
|
8
|
+
* matrix each frame, eliminating visual jumps when the alignment solver
|
|
9
|
+
* produces a new alignment (~1 Hz).
|
|
10
|
+
*
|
|
11
|
+
* Pattern: decompose → lerp position / slerp quaternion → compose.
|
|
12
|
+
* Same lerpRate convention as camera-follower.ts (default 8 → ~90%
|
|
13
|
+
* convergence in ~0.3 s at 60 fps).
|
|
14
|
+
*
|
|
15
|
+
* The first target is applied instantly (no animation from identity).
|
|
16
|
+
*
|
|
17
|
+
* @see docs/2026-03-21-user-feedback.md Issue 4
|
|
18
|
+
*/
|
|
19
|
+
const _currentPos = new THREE.Vector3();
|
|
20
|
+
const _currentQuat = new THREE.Quaternion();
|
|
21
|
+
const _currentScale = new THREE.Vector3();
|
|
22
|
+
new THREE.Vector3();
|
|
23
|
+
new THREE.Quaternion();
|
|
24
|
+
new THREE.Vector3();
|
|
25
|
+
/**
|
|
26
|
+
* Create an AlignmentLerper that drives arWorldGroup.matrix smoothly.
|
|
27
|
+
*
|
|
28
|
+
* @param arWorldGroup The group whose matrix is interpolated.
|
|
29
|
+
* @param lerpRate Lerp speed multiplier (default 8).
|
|
30
|
+
*/
|
|
31
|
+
function createAlignmentLerper(arWorldGroup, lerpRate = 8) {
|
|
32
|
+
let hasTarget = false;
|
|
33
|
+
let isFirstTarget = true;
|
|
34
|
+
const storedTargetPos = new THREE.Vector3();
|
|
35
|
+
const storedTargetQuat = new THREE.Quaternion();
|
|
36
|
+
const storedTargetScale = new THREE.Vector3(1, 1, 1);
|
|
37
|
+
const _matrix = new THREE.Matrix4();
|
|
38
|
+
return {
|
|
39
|
+
setTarget(matrix) {
|
|
40
|
+
_matrix.fromArray(matrix);
|
|
41
|
+
_matrix.decompose(storedTargetPos, storedTargetQuat, storedTargetScale);
|
|
42
|
+
hasTarget = true;
|
|
43
|
+
},
|
|
44
|
+
update(dt) {
|
|
45
|
+
if (!hasTarget) return;
|
|
46
|
+
if (isFirstTarget) {
|
|
47
|
+
_matrix.compose(storedTargetPos, storedTargetQuat, storedTargetScale);
|
|
48
|
+
arWorldGroup.matrix.copy(_matrix);
|
|
49
|
+
arWorldGroup.matrixAutoUpdate = false;
|
|
50
|
+
arWorldGroup.updateMatrixWorld(true);
|
|
51
|
+
isFirstTarget = false;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
arWorldGroup.matrix.decompose(_currentPos, _currentQuat, _currentScale);
|
|
55
|
+
const alpha = clampedAlpha(lerpRate, dt);
|
|
56
|
+
_currentPos.lerp(storedTargetPos, alpha);
|
|
57
|
+
_currentQuat.slerp(storedTargetQuat, alpha);
|
|
58
|
+
_currentScale.lerp(storedTargetScale, alpha);
|
|
59
|
+
_matrix.compose(_currentPos, _currentQuat, _currentScale);
|
|
60
|
+
arWorldGroup.matrix.copy(_matrix);
|
|
61
|
+
arWorldGroup.matrixAutoUpdate = false;
|
|
62
|
+
arWorldGroup.updateMatrixWorld(true);
|
|
63
|
+
},
|
|
64
|
+
dispose() {}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { createAlignmentLerper };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SCENE_NODE } from "../ar/scene-node-names.js";
|
|
2
|
+
import { clampedAlpha } from "./lerp-utils.js";
|
|
3
|
+
import * as THREE from "three";
|
|
4
|
+
//#region ../src/visualization/camera-follower.ts
|
|
5
|
+
/**
|
|
6
|
+
* Camera Follower Module
|
|
7
|
+
*
|
|
8
|
+
* Creates an Object3D that tracks the camera's world position
|
|
9
|
+
* every frame via lerp, while keeping its own rotation at identity
|
|
10
|
+
* (GPS-world-aligned).
|
|
11
|
+
*
|
|
12
|
+
* The follower is placed at the scene root (outside arWorldGroup)
|
|
13
|
+
* so its world rotation stays identity regardless of the alignment
|
|
14
|
+
* matrix applied to arWorldGroup. This ensures compass cubes and
|
|
15
|
+
* the map mesh point to true N/E/S/W in the NUE GPS frame.
|
|
16
|
+
*
|
|
17
|
+
* Children of the follower (map mesh, compass cubes) stay
|
|
18
|
+
* flat / world-oriented regardless of camera rotation or
|
|
19
|
+
* orbit / FPS controls.
|
|
20
|
+
*
|
|
21
|
+
* @see docs/2026-03-12-user-feedback.md Issue 8
|
|
22
|
+
*/
|
|
23
|
+
const _worldPos = new THREE.Vector3();
|
|
24
|
+
/**
|
|
25
|
+
* Create a CameraFollower and attach it as a child of the given parent.
|
|
26
|
+
*
|
|
27
|
+
* The parent should be the scene root (not arWorldGroup) so that the
|
|
28
|
+
* follower's world rotation stays identity — ensuring compass directions
|
|
29
|
+
* align with the NUE GPS frame regardless of alignment matrix changes.
|
|
30
|
+
*
|
|
31
|
+
* @param parent Parent node — should be the scene root.
|
|
32
|
+
* @param lerpRate Lerp speed multiplier (default 8).
|
|
33
|
+
*/
|
|
34
|
+
function createCameraFollower(parent, lerpRate = 8) {
|
|
35
|
+
const node = new THREE.Object3D();
|
|
36
|
+
node.name = SCENE_NODE.CAMERA_FOLLOWER;
|
|
37
|
+
parent.add(node);
|
|
38
|
+
return {
|
|
39
|
+
object3D: node,
|
|
40
|
+
update(camera, dt) {
|
|
41
|
+
camera.getWorldPosition(_worldPos);
|
|
42
|
+
const alpha = clampedAlpha(lerpRate, dt);
|
|
43
|
+
node.position.lerp(_worldPos, alpha);
|
|
44
|
+
},
|
|
45
|
+
dispose() {
|
|
46
|
+
if (node.parent) node.parent.remove(node);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { createCameraFollower };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CSS3DRenderer } from "three/addons/renderers/CSS3DRenderer.js";
|
|
2
|
+
//#region ../src/visualization/css3d-renderer-manager.ts
|
|
3
|
+
/**
|
|
4
|
+
* Create and configure a CSS3DRenderer overlay for the given container.
|
|
5
|
+
*
|
|
6
|
+
* The CSS3D layer is positioned absolutely over the container with
|
|
7
|
+
* `pointer-events: none` so it does not intercept clicks intended
|
|
8
|
+
* for the WebGL canvas underneath.
|
|
9
|
+
*
|
|
10
|
+
* **Requirement:** The `container` element must have `position: relative`,
|
|
11
|
+
* `absolute`, `fixed`, or `sticky`. If the container is `position: static`
|
|
12
|
+
* (the CSS default), the overlay will float to the nearest positioned
|
|
13
|
+
* ancestor instead of aligning with the container.
|
|
14
|
+
*/
|
|
15
|
+
function createCss3dRendererManager(container, width, height) {
|
|
16
|
+
const cssRenderer = new CSS3DRenderer();
|
|
17
|
+
cssRenderer.setSize(width, height);
|
|
18
|
+
const el = cssRenderer.domElement;
|
|
19
|
+
el.style.position = "absolute";
|
|
20
|
+
el.style.top = "0px";
|
|
21
|
+
el.style.left = "0px";
|
|
22
|
+
el.style.pointerEvents = "none";
|
|
23
|
+
container.appendChild(el);
|
|
24
|
+
let disposed = false;
|
|
25
|
+
return {
|
|
26
|
+
render(scene, camera) {
|
|
27
|
+
if (disposed) return;
|
|
28
|
+
cssRenderer.render(scene, camera);
|
|
29
|
+
},
|
|
30
|
+
setSize(w, h) {
|
|
31
|
+
cssRenderer.setSize(w, h);
|
|
32
|
+
},
|
|
33
|
+
dispose() {
|
|
34
|
+
if (disposed) return;
|
|
35
|
+
disposed = true;
|
|
36
|
+
if (el.parentElement) el.parentElement.removeChild(el);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { createCss3dRendererManager };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { disposeObject3D } from "./three-dispose.js";
|
|
2
|
+
import { VIS_COLORS } from "./vis-colors.js";
|
|
3
|
+
import * as THREE from "three";
|
|
4
|
+
//#region ../src/visualization/gps-compass-cubes.ts
|
|
5
|
+
/**
|
|
6
|
+
* GPS Compass Cubes Module
|
|
7
|
+
*
|
|
8
|
+
* Creates colored cubes at ±1 m along each axis with text labels
|
|
9
|
+
* (N, E, S, W) to visualize GPS/NUE orientation in the 3D scene.
|
|
10
|
+
*
|
|
11
|
+
* NUE convention: X = North, Y = Up, Z = East.
|
|
12
|
+
*
|
|
13
|
+
* Designed to be added as children of a CameraFollower so they
|
|
14
|
+
* stay near the camera but always GPS-world-aligned.
|
|
15
|
+
*
|
|
16
|
+
* @see docs/2026-03-12-user-feedback.md Issue 8
|
|
17
|
+
*/
|
|
18
|
+
/** Side length of each compass cube in metres. */
|
|
19
|
+
const COMPASS_CUBE_SIZE = .1;
|
|
20
|
+
/** Distance from origin to each compass cube in metres. */
|
|
21
|
+
const COMPASS_CUBE_DISTANCE = 1;
|
|
22
|
+
const CUBE_SPECS = [
|
|
23
|
+
{
|
|
24
|
+
name: "compass-north",
|
|
25
|
+
color: VIS_COLORS.COMPASS_NORTH.hex,
|
|
26
|
+
position: [
|
|
27
|
+
1,
|
|
28
|
+
0,
|
|
29
|
+
0
|
|
30
|
+
],
|
|
31
|
+
label: "N"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "compass-east",
|
|
35
|
+
color: VIS_COLORS.COMPASS_EAST.hex,
|
|
36
|
+
position: [
|
|
37
|
+
0,
|
|
38
|
+
0,
|
|
39
|
+
1
|
|
40
|
+
],
|
|
41
|
+
label: "E"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "compass-south",
|
|
45
|
+
color: VIS_COLORS.COMPASS_SOUTH.hex,
|
|
46
|
+
position: [
|
|
47
|
+
-1,
|
|
48
|
+
0,
|
|
49
|
+
0
|
|
50
|
+
],
|
|
51
|
+
label: "S"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "compass-west",
|
|
55
|
+
color: VIS_COLORS.COMPASS_WEST.hex,
|
|
56
|
+
position: [
|
|
57
|
+
0,
|
|
58
|
+
0,
|
|
59
|
+
-1
|
|
60
|
+
],
|
|
61
|
+
label: "W"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "compass-up",
|
|
65
|
+
color: VIS_COLORS.COMPASS_UP.hex,
|
|
66
|
+
position: [
|
|
67
|
+
0,
|
|
68
|
+
1,
|
|
69
|
+
0
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
];
|
|
73
|
+
/**
|
|
74
|
+
* Create a text sprite from a canvas texture.
|
|
75
|
+
* Uses a 64×64 canvas with centered white text on transparent background.
|
|
76
|
+
*/
|
|
77
|
+
function createTextSprite(text) {
|
|
78
|
+
const canvas = document.createElement("canvas");
|
|
79
|
+
canvas.width = 64;
|
|
80
|
+
canvas.height = 64;
|
|
81
|
+
const ctx = canvas.getContext("2d");
|
|
82
|
+
if (ctx) {
|
|
83
|
+
ctx.clearRect(0, 0, 64, 64);
|
|
84
|
+
ctx.fillStyle = "#ffffff";
|
|
85
|
+
ctx.font = "bold 48px sans-serif";
|
|
86
|
+
ctx.textAlign = "center";
|
|
87
|
+
ctx.textBaseline = "middle";
|
|
88
|
+
ctx.fillText(text, 32, 32);
|
|
89
|
+
}
|
|
90
|
+
const texture = new THREE.CanvasTexture(canvas);
|
|
91
|
+
const material = new THREE.SpriteMaterial({
|
|
92
|
+
map: texture,
|
|
93
|
+
depthTest: false
|
|
94
|
+
});
|
|
95
|
+
const sprite = new THREE.Sprite(material);
|
|
96
|
+
sprite.name = `label-${text}`;
|
|
97
|
+
sprite.position.set(0, COMPASS_CUBE_SIZE * 1.5, 0);
|
|
98
|
+
sprite.scale.set(.4, .4, .4);
|
|
99
|
+
return sprite;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create compass cubes and attach them to a parent Object3D.
|
|
103
|
+
*
|
|
104
|
+
* @param parent The Object3D to anchor the compass group to
|
|
105
|
+
* (typically the CameraFollower).
|
|
106
|
+
*/
|
|
107
|
+
function createGpsCompassCubes(parent) {
|
|
108
|
+
const group = new THREE.Group();
|
|
109
|
+
group.name = "gps-compass-cubes";
|
|
110
|
+
const geometry = new THREE.BoxGeometry(COMPASS_CUBE_SIZE, COMPASS_CUBE_SIZE, COMPASS_CUBE_SIZE);
|
|
111
|
+
for (const spec of CUBE_SPECS) {
|
|
112
|
+
const material = new THREE.MeshBasicMaterial({ color: spec.color });
|
|
113
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
114
|
+
mesh.name = spec.name;
|
|
115
|
+
mesh.position.set(...spec.position);
|
|
116
|
+
if (spec.label) mesh.add(createTextSprite(spec.label));
|
|
117
|
+
group.add(mesh);
|
|
118
|
+
}
|
|
119
|
+
parent.add(group);
|
|
120
|
+
return {
|
|
121
|
+
group,
|
|
122
|
+
dispose() {
|
|
123
|
+
if (group.parent) group.parent.remove(group);
|
|
124
|
+
disposeObject3D(group);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
export { COMPASS_CUBE_DISTANCE, COMPASS_CUBE_SIZE, createGpsCompassCubes };
|