locar 0.2.2 → 0.2.4

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/locar.es.js CHANGED
@@ -1,545 +1,376 @@
1
- import * as u from "three";
2
- import { Vector3 as D, Euler as T, Quaternion as O, EventDispatcher as P, MathUtils as p } from "three";
3
- class w {
4
- constructor() {
5
- this.on = (e, i) => {
6
- this.eventHandlers[e] === void 0 && (this.eventHandlers[e] = []), this.eventHandlers[e].push(i);
7
- }, this.emit = (e, ...i) => {
8
- this.eventHandlers[e]?.forEach((t) => {
9
- t(...i);
10
- });
11
- }, this.off = (e, i) => {
12
- if (this.eventHandlers[e]) {
13
- const t = this.eventHandlers[e].indexOf(i);
14
- t > -1 && this.eventHandlers[e].splice(t, 1);
15
- }
16
- }, this.eventHandlers = {};
17
- }
18
- }
19
- class j extends w {
20
- /**
21
- * Create an App object.
22
- * @param {AppOptions} - Startup options.
23
- */
24
- constructor({ cameraOptions: e, canvas: i, gpsOptions: t, videoConstraints: o, deviceOrientationOptions: l, serverLogger: m, projection: n }) {
25
- super(), this.origHfov = e?.hFov || 80;
26
- const s = 0;
27
- this.cameraFeedDimensions = null;
28
- const d = window.innerWidth / window.innerHeight;
29
- this.camera = new u.PerspectiveCamera(this.origHfov / d, d, e?.near || 1e-3, e?.far || 1e3), i ? (this.renderer = new u.WebGLRenderer({ canvas: i, alpha: !0 }), this.renderer.setClearColor(65280, s)) : (this.renderer = new u.WebGLRenderer({ alpha: !0 }), this.renderer.setClearColor(65280, s), document.body.appendChild(this.renderer.domElement)), this.renderer.setSize(window.innerWidth, window.innerHeight), this.scene = new u.Scene();
30
- const c = l || { enabled: !0 };
31
- window.addEventListener("resize", () => {
32
- this.renderer.setSize(window.innerWidth, window.innerHeight);
33
- const a = window.innerWidth / window.innerHeight;
34
- if (this.camera.aspect = a, this.cameraFeedDimensions !== null) {
35
- const h = a > 1 ? this.cameraFeedDimensions.landWidth : this.cameraFeedDimensions.landHeight, g = a > 1 ? this.cameraFeedDimensions.landHeight : this.cameraFeedDimensions.landWidth;
36
- this.#e(h, g, a);
37
- }
38
- this.camera.updateProjectionMatrix();
39
- }), this.locar = new y(this.scene, this.camera, t, m, n), this.webcam = new q(o), this.deviceOrientationControls = c.enabled === !0 ? new z(this.camera, c) : null, this.renderer.setAnimationLoop(() => {
40
- this.deviceOrientationControls?.update(), this.renderer.render(this.scene, this.camera);
41
- });
42
- }
43
- /**
44
- * Start the app.
45
- * Must be called after construction.
46
- * @returns {Promise<LocAR>}
47
- * Promise resolving with LocAR object. Rejects with object containing code and message.
48
- */
49
- start() {
50
- return new Promise((i, t) => {
51
- this.webcam.on("webcamstarted", (o) => {
52
- const l = o.videoWidth > o.videoHeight;
53
- this.cameraFeedDimensions = {
54
- landWidth: l ? o.videoWidth : o.videoHeight,
55
- landHeight: l ? o.videoHeight : o.videoWidth
56
- }, this.#e(o.videoWidth, o.videoHeight, window.innerWidth / window.innerHeight), this.camera.updateProjectionMatrix();
57
- }), this.webcam.on("webcamerror", (o) => {
58
- t({ code: o.code, message: o.message });
59
- }), this.deviceOrientationControls === null ? i(this.locar) : (this.deviceOrientationControls?.on("deviceorientationgranted", (o) => {
60
- o.target.connect(), i(this.locar);
61
- }), this.deviceOrientationControls.on("deviceorientationerror", (o) => {
62
- t({ code: o.code, message: o.message });
63
- }), this.deviceOrientationControls.init());
64
- });
65
- }
66
- #e(e, i, t) {
67
- const o = e / i;
68
- if (t < o) {
69
- const l = e * (window.innerHeight / i), m = this.origHfov * (window.innerWidth / l);
70
- this.camera.fov = m / t;
71
- } else
72
- this.camera.fov = this.origHfov / t;
73
- }
74
- }
75
- class x {
76
- /**
77
- * Create a SphMercProjection.
78
- */
79
- constructor() {
80
- this.project = (e, i) => [this.#e(e), this.#t(i)], this.unproject = (e) => [this.#i(e[0]), this.#n(e[1])], this.#e = (e) => e / 180 * this.HALF_EARTH, this.#t = (e) => {
81
- var i = Math.log(Math.tan((90 + e) * Math.PI / 360)) / (Math.PI / 180);
82
- return i * this.HALF_EARTH / 180;
83
- }, this.#i = (e) => e / this.HALF_EARTH * 180, this.#n = (e) => {
84
- var i = e / this.HALF_EARTH * 180;
85
- return i = 180 / Math.PI * (2 * Math.atan(Math.exp(i * Math.PI / 180)) - Math.PI / 2), i;
86
- }, this.getID = () => "epsg:3857", this.EARTH = 4007501668e-2, this.HALF_EARTH = 2003750834e-2;
87
- }
88
- #e;
89
- #t;
90
- #i;
91
- #n;
92
- }
93
- class y extends w {
94
- #e;
95
- #t;
96
- #i;
97
- #n;
98
- #a;
99
- #s;
100
- #d;
101
- #r;
102
- #o;
103
- /**
104
- * @param {THREE.Scene} scene - The Three.js scene to use.
105
- * @param {THREE.Camera} camera - The Three.js camera to use. Should usually
106
- * be a THREE.PerspectiveCamera.
107
- * @param {Object} options - Initialisation options for the GPS; see
108
- * setGpsOptions() below.
109
- * @param {Object} serverLogger - an object which can optionally log GPS position to a server for debugging. null by default, so no logging will be done. This object should implement a sendData() method to send data (2nd arg) to a given endpoint (1st arg). Please see source code for details. Ensure you comply with privacy laws (GDPR or equivalent) if implementing this.
110
- */
111
- constructor(e, i, t = {}, o = null, l = new x()) {
112
- super(), this.scene = e, this.camera = i, this.#e = l, this.#t = null, this.#i = 0, this.#n = 100, this.#a = null, this.setGpsOptions(t), this.#s = null, this.#d = 0, this.#r = 0, this.#o = o;
113
- }
114
- /**
115
- * Set the projection to use.
116
- * @param {Object} any object which includes a project() method
117
- * taking longitude and latitude as arguments and returning an array
118
- * containing easting and northing.
119
- */
120
- setProjection(e) {
121
- this.#e = e;
122
- }
123
- /**
124
- * Set the GPS options.
125
- * @param {Object} object containing gpsMinDistance and/or gpsMinAccuracy
126
- * properties. The former specifies the number of metres which the device
127
- * must move to process a new GPS reading, and the latter specifies the
128
- * minimum accuracy, in metres, for a GPS reading to be counted.
129
- */
130
- setGpsOptions(e = {}) {
131
- e.gpsMinDistance !== void 0 && (this.#i = e.gpsMinDistance), e.gpsMinAccuracy !== void 0 && (this.#n = e.gpsMinAccuracy);
132
- }
133
- /**
134
- * Start the GPS on a real device
135
- * @return {boolean} code indicating whether the GPS was started successfully.
136
- * GPS errors can be handled by handling the gpserror event.
137
- */
138
- async startGps() {
139
- if (this.#o) {
140
- const i = await (await this.#o.sendData("/gps/start", {
141
- gpsMinDistance: this.#i,
142
- gpsMinAccuracy: this.#n
143
- })).json();
144
- this.#r = i.session;
145
- }
146
- return this.#a === null ? (this.#a = navigator.geolocation.watchPosition(
147
- (e) => {
148
- this.#c(e);
149
- },
150
- (e) => {
151
- this.emit("gpserror", e);
152
- },
153
- {
154
- enableHighAccuracy: !0
155
- }
156
- ), !0) : !1;
157
- }
158
- /**
159
- * Stop the GPS on a real device
160
- * @return {boolean} true if the GPS was stopped, false if it could not be
161
- * stopped (i.e. it was never started).
162
- */
163
- stopGps() {
164
- return this.#a !== null ? (navigator.geolocation.clearWatch(this.#a), this.#a = null, !0) : !1;
165
- }
166
- /**
167
- * Send a fake GPS signal. Useful for testing on a desktop or laptop.
168
- * @param {number} lon - The longitude.
169
- * @param {number} lat - The latitude.
170
- * @param {number} elev - The elevation in metres. (optional, set to null
171
- * for no elevation).
172
- * @param {number} acc - The accuracy of the GPS reading in metres. May be
173
- * ignored if lower than the specified minimum accuracy.
174
- */
175
- fakeGps(e, i, t = null, o = 0) {
176
- t !== null && this.setElevation(t), this.#c({
177
- coords: {
178
- longitude: e,
179
- latitude: i,
180
- accuracy: o
181
- }
182
- });
183
- }
184
- /**
185
- * Convert longitude and latitude to three.js/WebGL world coordinates.
186
- * Uses the specified projection, and negates the northing (in typical
187
- * projections, northings increase northwards, but in the WebGL coordinate
188
- * system, we face negative z if the camera is at the origin with default
189
- * rotation).
190
- * Must not be called until an initial position is determined.
191
- * @param {number} lon - The longitude.
192
- * @param {number} lat - The latitude.
193
- * @return {Array} a two member array containing the WebGL x and z coordinates
194
- */
195
- lonLatToWorldCoords(e, i) {
196
- const t = this.#e.project(e, i);
197
- if (this.#s)
198
- t[0] -= this.#s[0], t[1] -= this.#s[1];
199
- else
200
- throw "No initial position determined";
201
- return [t[0], -t[1]];
202
- }
203
- /**
204
- * Add a new AR object at a given latitude, longitude and elevation.
205
- * @param {THREE.Mesh} object the object
206
- * @param {number} lon - the longitude.
207
- * @param {number} lat - the latitude.
208
- * @param {number} elev - the elevation in metres
209
- * (if not specified, 0 is assigned)
210
- * @param {Object} properties - properties describing the object (for example,
211
- * the contents of the GeoJSON properties field).
212
- */
213
- add(e, i, t, o, l = {}) {
214
- e.properties = l, this.#l(e, i, t, o || 0), this.scene.add(e), this.#o?.sendData("/object/new", {
215
- position: e.position,
216
- x: e.position.x,
217
- z: e.position.z,
218
- session: this.#r,
219
- properties: l
220
- });
221
- }
222
- addGeoLine(e, i, t = 1) {
223
- const o = e.map(((n) => {
224
- const [s, d] = this.lonLatToWorldCoords(n[0], n[1]);
225
- return new u.Vector3(s, n[2] || 0, d);
226
- })), l = this.#h(o, t);
227
- i.setValues({ side: u.DoubleSide });
228
- const m = new u.Mesh(l, i);
229
- return this.scene.add(m), m;
230
- }
231
- #h(e, i) {
232
- let t, o, l, m, n = 0, s = 0, d = [], c;
233
- const a = e.length - 1, h = [];
234
- for (let r = 0; r < a; r++)
235
- t = e[r + 1].x - e[r].x, o = e[r + 1].z - e[r].z, l = e[r + 1].y - e[r].y, m = Math.sqrt(t * t + l * l + o * o), n = -(o * (i / 2)) / m, s = t * (i / 2) / m, c = [
236
- e[r].x - n,
237
- e[r].y,
238
- e[r].z - s,
239
- e[r].x + n,
240
- e[r].y,
241
- e[r].z + s
242
- ], r > 0 && c.forEach((C, R) => {
243
- C = (C + d[R]) / 2;
244
- }), h.push(...c), d = [
245
- e[r + 1].x - n,
246
- e[r + 1].y,
247
- e[r + 1].z - s,
248
- e[r + 1].x + n,
249
- e[r + 1].y,
250
- e[r + 1].z + s
251
- ];
252
- h.push(e[a].x - n), h.push(e[a].y), h.push(e[a].z - s), h.push(e[a].x + n), h.push(e[a].y), h.push(e[a].z + s);
253
- let g = [];
254
- for (let r = 0; r < a; r++)
255
- g.push(r * 2, r * 2 + 1, r * 2 + 2), g.push(r * 2 + 1, r * 2 + 3, r * 2 + 2);
256
- let f = new u.BufferGeometry(), M = new Float32Array(h);
257
- return f.setIndex(g), f.setAttribute("position", new u.BufferAttribute(M, 3)), f.computeBoundingBox(), f;
258
- }
259
- #l(e, i, t, o) {
260
- const l = this.lonLatToWorldCoords(i, t);
261
- o !== void 0 && (e.position.y = o), [e.position.x, e.position.z] = l;
262
- }
263
- /**
264
- * Set the elevation (y coordinate) of the camera.
265
- * @param {number} elev - the elevation in metres.
266
- */
267
- setElevation(e) {
268
- this.camera.position.y = e;
269
- }
270
- #u(e, i) {
271
- this.#s = this.#e.project(e, i);
272
- }
273
- #c(e) {
274
- let i = Number.MAX_VALUE;
275
- this.#d++, this.#o?.sendData("/gps/new", {
276
- gpsCount: this.#d,
277
- lat: e.coords.latitude,
278
- lon: e.coords.longitude,
279
- acc: e.coords.accuracy,
280
- session: this.#r
281
- }), e.coords.accuracy <= this.#n && (this.#t === null ? this.#t = {
282
- latitude: e.coords.latitude,
283
- longitude: e.coords.longitude
284
- } : i = y.haversineDist(this.#t, e.coords), i >= this.#i && (this.#t.longitude = e.coords.longitude, this.#t.latitude = e.coords.latitude, this.#s || (this.#u(
285
- e.coords.longitude,
286
- e.coords.latitude
287
- ), this.#o?.sendData("/worldorigin/new", {
288
- gpsCount: this.#d,
289
- lat: e.coords.latitude,
290
- lon: e.coords.longitude,
291
- session: this.#r,
292
- initialPosition: this.#s
293
- })), this.#l(
294
- this.camera,
295
- e.coords.longitude,
296
- e.coords.latitude
297
- ), this.#o?.sendData("/gps/accepted", {
298
- gpsCount: this.#d,
299
- cameraX: this.camera.position.x,
300
- cameraZ: this.camera.position.z,
301
- session: this.#r,
302
- distMoved: i
303
- }), this.emit("gpsupdate", { position: e, distMoved: i })));
304
- }
305
- /**
306
- * Calculate haversine distance between two lat/lon pairs.
307
- *
308
- * Taken from original A-Frame AR.js location-based components
309
- */
310
- static haversineDist(e, i) {
311
- const t = u.MathUtils.degToRad(i.longitude - e.longitude), o = u.MathUtils.degToRad(i.latitude - e.latitude), l = Math.sin(o / 2) * Math.sin(o / 2) + Math.cos(u.MathUtils.degToRad(e.latitude)) * Math.cos(u.MathUtils.degToRad(i.latitude)) * (Math.sin(t / 2) * Math.sin(t / 2));
312
- return 2 * Math.atan2(Math.sqrt(l), Math.sqrt(1 - l)) * 6371e3;
313
- }
314
- /**
315
- * Obtain the last known GPS location.
316
- *
317
- * @return {Object} object containing latitude and longitude fields, or null if no previous GPS location.
318
- */
319
- getLastKnownLocation() {
320
- return this.#t;
321
- }
322
- }
323
- const _ = "locar-device-orientation-permission-modal", H = "locar-device-orientation-permission-button", L = "locar-device-orientation-permission-message", N = "locar-device-orientation-permission-inner", S = "locar-device-orientation-permission-button-inner", b = "This immersive website requires access to your device motion sensors.", E = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, W = new D(0, 0, 1), I = new T(), F = new O(), V = new O(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)), A = 0.5 * Math.PI;
324
- class z extends P {
325
- /**
326
- * Create an instance of DeviceOrientationControls.
327
- * @param {Object} object - the object to attach the controls to
328
- * (usually your Three.js camera)
329
- * @param {Object} options - options for DeviceOrientationControls:
330
- * currently accepts smoothingFactor (< 1), enablePermissionDialog, orientationChangeThreshold (radians)
331
- */
332
- constructor(e, i = {}) {
333
- super(), this.init = () => {
334
- window.DeviceOrientationEvent === void 0 ? this.eventEmitter.emit("deviceorientationerror", {
335
- code: "LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",
336
- message: "Device orientation API not supported"
337
- }) : window.isSecureContext === !1 ? this.eventEmitter.emit("deviceorientationerror", {
338
- code: "LOCAR_DEVICE_ORIENTATION_NO_HTTPS",
339
- message: "DeviceOrientationEvent is only available in secure contexts (https)"
340
- }) : typeof window.DeviceOrientationEvent.requestPermission == "function" && this.enablePermissionDialog ? this.obtainPermissionGesture() : this.eventEmitter.emit("deviceorientationgranted", { target: this });
341
- }, this.eventEmitter = new w();
342
- const t = this;
343
- this.object = e, this.object.rotation.reorder("YXZ"), this.enabled = !0, this.deviceOrientation = null, this.screenOrientation = 0, this.alphaOffset = 0, this.orientationOffset = 0, this.initialOffset = null, this.lastQuaternion = null, this.orientationChangeEventName = "ondeviceorientationabsolute" in window ? "deviceorientationabsolute" : "deviceorientation", this.smoothingFactor = i.smoothingFactor || 0.2, this.enablePermissionDialog = i.enablePermissionDialog ?? !0, this.enableInlineStyling = i.enableStyling ?? !0, this.preferConfirmDialog = i.preferConfirmDialog ?? !1, this.orientationChangeThreshold = i.orientationChangeThreshold ?? 0;
344
- const o = (n) => {
345
- let { alpha: s, beta: d, gamma: c, webkitCompassHeading: a } = n;
346
- if (s = s ?? 0, d = d ?? 0, c = c ?? 0, a = a ?? 0, E) {
347
- const h = 360 - a;
348
- t.alphaOffset = p.degToRad(h - s), t.deviceOrientation = { alpha: s, beta: d, gamma: c, webkitCompassHeading: a };
349
- } else
350
- s < 0 && (s += 360), t.deviceOrientation = { alpha: s, beta: d, gamma: c };
351
- window.dispatchEvent(
352
- new CustomEvent("camera-rotation-change", {
353
- detail: { cameraRotation: e.rotation }
354
- })
355
- );
356
- }, l = () => {
357
- t.screenOrientation = window.screen.orientation?.angle ?? 0, E && (t.screenOrientation === 90 ? t.orientationOffset = -A : t.screenOrientation === -90 ? t.orientationOffset = A : t.orientationOffset = 0);
358
- }, m = (n, s, d, c, a) => {
359
- I.set(d, s, -c, "YXZ"), n.setFromEuler(I), n.multiply(V), n.multiply(F.setFromAxisAngle(W, -a));
360
- };
361
- this.connect = () => {
362
- l(), window.addEventListener(
363
- "orientationchange",
364
- l
365
- ), window.addEventListener(
366
- t.orientationChangeEventName,
367
- o
368
- ), t.enabled = !0;
369
- }, this.disconnect = () => {
370
- window.removeEventListener(
371
- "orientationchange",
372
- l
373
- ), window.removeEventListener(
374
- t.orientationChangeEventName,
375
- o
376
- ), t.enabled = !1, t.initialOffset = !1, t.deviceOrientation = null, t.lastQuaternion = null;
377
- }, this.requestOrientationPermissions = () => {
378
- window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then((n) => {
379
- n === "granted" ? this.eventEmitter.emit("deviceorientationgranted", {
380
- target: this
381
- }) : this.eventEmitter.emit("deviceorientationerror", {
382
- code: "LOCAR_DEVICE_ORIENTATION_PERMISSION_DENIED",
383
- message: "Permission for device orientation denied - AR will not work correctly"
384
- });
385
- }).catch((n) => {
386
- this.eventEmitter.emit("deviceorientationerror", {
387
- code: "LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",
388
- message: "Permission request for device orientation failed - AR will not work correctly",
389
- error: JSON.stringify(n, null, 2)
390
- });
391
- }) : this.eventEmitter.emit("deviceorientationerror", {
392
- code: "LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",
393
- message: "Internal error: no requestPermission() found although requestOrientationPermissions() was called - please raise an issue on GitHub"
394
- });
395
- }, this.update = () => {
396
- if (t.enabled === !1) return;
397
- const n = t.deviceOrientation;
398
- if (n) {
399
- let s = n.alpha ? p.degToRad(n.alpha) + t.alphaOffset : 0, d = n.beta ? p.degToRad(n.beta) : 0, c = n.gamma ? p.degToRad(n.gamma) : 0;
400
- const a = t.screenOrientation ? p.degToRad(t.screenOrientation) : 0, h = new O();
401
- if (E) {
402
- m(h, s, d, c, a);
403
- const g = new T().setFromQuaternion(
404
- h,
405
- "YXZ"
406
- ), f = p.degToRad(
407
- 360 - (n.webkitCompassHeading ?? 0)
408
- );
409
- g.y = f + (t.orientationOffset || 0), h.setFromEuler(g);
410
- } else
411
- m(h, s, d, c, a);
412
- if (t.lastQuaternion && t.orientationChangeThreshold > 0 && h.angleTo(t.lastQuaternion) < t.orientationChangeThreshold)
413
- return;
414
- if (t.smoothingFactor < 1 && t.lastQuaternion) {
415
- const g = 1 - t.smoothingFactor;
416
- t.object.quaternion.slerp(h, g);
417
- } else
418
- t.object.quaternion.copy(h);
419
- t.lastQuaternion = t.object.quaternion.clone(), window.dispatchEvent(
420
- new CustomEvent("camera-rotation-change", {
421
- detail: { cameraRotation: t.object.rotation }
422
- })
423
- );
424
- }
425
- }, this.getCorrectedHeading = () => {
426
- const { deviceOrientation: n } = t;
427
- if (!n) return 0;
428
- let s = 0;
429
- return E ? (s = 360 - (n.webkitCompassHeading ?? 0), t.orientationOffset && (s += t.orientationOffset * (180 / Math.PI), s = (s + 360) % 360)) : (n.absolute === !0 || t.orientationChangeEventName, s = n.alpha ? n.alpha : 0, s = (360 - s) % 360, s < 0 && (s += 360)), s;
430
- }, this.updateAlphaOffset = () => {
431
- t.initialOffset = !1;
432
- }, this.dispose = () => {
433
- t.disconnect();
434
- }, this.getAlpha = () => {
435
- const { deviceOrientation: n } = t;
436
- return n && n.alpha ? p.degToRad(n.alpha) + t.alphaOffset : 0;
437
- }, this.getBeta = () => {
438
- const { deviceOrientation: n } = t;
439
- return n && n.beta ? p.degToRad(n.beta) : 0;
440
- }, this.getGamma = () => {
441
- const { deviceOrientation: n } = t;
442
- return n && n.gamma ? p.degToRad(n.gamma) : 0;
443
- }, this.createObtainPermissionGestureDialog = () => {
444
- const n = document.createElement("div");
445
- n.classList.add(_);
446
- const s = document.createElement("div");
447
- s.classList.add(N);
448
- const d = document.createElement("div");
449
- d.classList.add(L);
450
- const c = document.createElement("div");
451
- c.classList.add(S);
452
- const a = document.createElement("button");
453
- a.classList.add(H), document.body.appendChild(n), this.enableInlineStyling === !0 && (n.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", n.style.display = "flex", n.style.position = "fixed", n.style.zIndex = "100000", n.style.justifyContent = "center", n.style.alignItems = "center", n.style.backgroundColor = "rgba(0,0,0,0.2)", n.style.inset = "0", n.style.padding = "20px", s.style.backgroundColor = "rgba(220, 220, 220, 0.85)", s.style.padding = "6px 0", s.style.borderRadius = "10px", s.style.width = "100%", s.style.maxWidth = "400px", d.style.padding = "10px 12px", d.style.textAlign = "center", d.style.fontWeight = "400", d.style.fontSize = "13px", d.style.display = "flex", d.style.justifyContent = "center", d.style.alignItems = "center", c.style.display = "block", c.style.textAlign = "center", c.style.textDecoration = "none", c.style.borderTop = "rgb(180,180,180) solid 1px", a.style.display = "block", a.style.width = "100%", a.style.textAlign = "center", a.style.appearance = "none", a.style.background = "none", a.style.border = "none", a.style.outline = "none", a.style.padding = "10px", a.style.fontWeight = "400", a.style.fontSize = "16px", a.style.color = "#2e7cf1", a.style.cursor = "pointer"), n.appendChild(s), s.appendChild(d), s.appendChild(c), d.appendChild(
454
- document.createTextNode(b)
455
- );
456
- const h = () => {
457
- this.requestOrientationPermissions(), n.style.display = "none";
458
- };
459
- a.addEventListener("click", h), a.appendChild(document.createTextNode("OK")), c.appendChild(a), document.body.appendChild(n);
460
- }, this.obtainPermissionGesture = () => {
461
- this.preferConfirmDialog === !0 ? window.confirm(b) && this.requestOrientationPermissions() : this.createObtainPermissionGestureDialog();
462
- };
463
- }
464
- on(e, i) {
465
- this.eventEmitter.on(e, i);
466
- }
467
- }
468
- class G {
469
- /**
470
- * Create a ClickHandler.
471
- * @param {THREE.WebGLRenderer} - The Three.js renderer on which the click
472
- * events will be handled.
473
- */
474
- constructor(e) {
475
- this.raycaster = new u.Raycaster(), this.normalisedMousePosition = null, e.domElement.addEventListener("click", (i) => {
476
- this.normalisedMousePosition = new u.Vector2(
477
- i.clientX / e.domElement.clientWidth * 2 - 1,
478
- -(i.clientY / e.domElement.clientHeight * 2) + 1
479
- );
480
- });
481
- }
482
- /**
483
- * Cast a ray into the scene to detect objects.
484
- * @param {THREE.Camera} - The active Three.js camera, from which the ray
485
- * will be cast.
486
- * @param {THREE.Scene} - The active Three.js scene, which the ray will be
487
- * cast into.
488
- * @return {Array} - array of all intersected objects.
489
- */
490
- raycast(e, i) {
491
- if (this.normalisedMousePosition !== null) {
492
- this.raycaster.setFromCamera(this.normalisedMousePosition, e);
493
- const t = this.raycaster.intersectObjects(i.children, !1);
494
- return this.normalisedMousePosition = null, t;
495
- }
496
- return [];
497
- }
498
- }
499
- class q extends w {
500
- #e;
501
- /**
502
- * Create a Webcam.
503
- * @param constraints {Object} - options to use for initialising the camera.
504
- * This is the same constraints object as used by standard MediaDevices API.
505
- * @param {string} videoElementSelector - selector to obtain the HTML video
506
- * element to render the webcam feed. If a falsy value (e.g. null or
507
- * undefined), a video element will be created.
508
- */
509
- constructor(e = { video: { facingMode: "environment" } }, i) {
510
- super(), this.sceneWebcam = new u.Scene(), i ? this.#e = document.querySelector(i) : (this.#e = document.createElement("video"), this.#e.setAttribute("autoplay", "true"), this.#e.setAttribute("playsinline", "true"), this.#e.style.cssText += `
511
- width: 100%;
512
- height: 100%;
513
- object-fit: cover;
514
- background: black;
515
- position: absolute;
516
- top: 0px;
517
- left: 0px;
518
- z-index: -100;
519
- `, document.body.appendChild(this.#e)), navigator.mediaDevices && navigator.mediaDevices.getUserMedia ? navigator.mediaDevices.getUserMedia(e).then((t) => {
520
- this.#e?.addEventListener("loadedmetadata", () => {
521
- this.#e.play(), this.emit("webcamstarted", { videoWidth: this.#e.videoWidth, videoHeight: this.#e.videoHeight });
522
- }), this.#e && (this.#e.srcObject = t);
523
- }).catch((t) => {
524
- this.emit("webcamerror", {
525
- code: t.name,
526
- message: t.message
527
- });
528
- }) : this.emit("webcamerror", {
529
- code: "LOCAR_NO_MEDIA_DEVICES_API",
530
- message: "Media devices API not supported"
531
- });
532
- }
533
- getVideoDimensions() {
534
- return `w ${this.#e.videoWidth}, h ${this.#e.videoHeight}`;
535
- }
536
- }
537
- export {
538
- j as App,
539
- G as ClickHandler,
540
- z as DeviceOrientationControls,
541
- w as EventEmitter,
542
- y as LocAR,
543
- x as SphMercProjection,
544
- q as Webcam
1
+ import * as e from "three";
2
+ import { Euler as t, EventDispatcher as n, MathUtils as r, Quaternion as i, Vector3 as a } from "three";
3
+ //#region lib/three/event-emitter.ts
4
+ var o = class {
5
+ constructor() {
6
+ this.on = (e, t) => {
7
+ this.eventHandlers[e] === void 0 && (this.eventHandlers[e] = []), this.eventHandlers[e].push(t);
8
+ }, this.emit = (e, ...t) => {
9
+ this.eventHandlers[e]?.forEach((e) => {
10
+ e(...t);
11
+ });
12
+ }, this.off = (e, t) => {
13
+ if (this.eventHandlers[e]) {
14
+ let n = this.eventHandlers[e].indexOf(t);
15
+ n > -1 && this.eventHandlers[e].splice(n, 1);
16
+ }
17
+ }, this.eventHandlers = {};
18
+ }
19
+ }, s = class extends o {
20
+ constructor({ cameraOptions: t, canvas: n, gpsOptions: r, videoConstraints: i, deviceOrientationOptions: a, serverLogger: o, projection: s }) {
21
+ super(), this.origHfov = t?.hFov || 80, this.cameraFeedDimensions = null;
22
+ let c = window.innerWidth / window.innerHeight;
23
+ this.camera = new e.PerspectiveCamera(this.origHfov / c, c, t?.near || .001, t?.far || 1e3), n ? (this.renderer = new e.WebGLRenderer({
24
+ canvas: n,
25
+ alpha: !0
26
+ }), this.renderer.setClearColor(65280, 0)) : (this.renderer = new e.WebGLRenderer({ alpha: !0 }), this.renderer.setClearColor(65280, 0), document.body.appendChild(this.renderer.domElement)), this.renderer.setSize(window.innerWidth, window.innerHeight), this.scene = new e.Scene();
27
+ let u = a || { enabled: !0 };
28
+ window.addEventListener("resize", () => {
29
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
30
+ let e = window.innerWidth / window.innerHeight;
31
+ if (this.camera.aspect = e, this.cameraFeedDimensions !== null) {
32
+ let t = e > 1 ? this.cameraFeedDimensions.landWidth : this.cameraFeedDimensions.landHeight, n = e > 1 ? this.cameraFeedDimensions.landHeight : this.cameraFeedDimensions.landWidth;
33
+ this.#e(t, n, e);
34
+ }
35
+ this.camera.updateProjectionMatrix();
36
+ }), this.locar = new l(this.scene, this.camera, r, o, s), this.webcam = new w(i), this.deviceOrientationControls = u.enabled === !0 ? new S(this.camera, u) : null, this.renderer.setAnimationLoop(() => {
37
+ this.deviceOrientationControls?.update(), this.renderer.render(this.scene, this.camera);
38
+ });
39
+ }
40
+ start() {
41
+ return new Promise((e, t) => {
42
+ this.webcam.on("webcamstarted", (e) => {
43
+ let t = e.videoWidth > e.videoHeight;
44
+ this.cameraFeedDimensions = {
45
+ landWidth: t ? e.videoWidth : e.videoHeight,
46
+ landHeight: t ? e.videoHeight : e.videoWidth
47
+ }, this.#e(e.videoWidth, e.videoHeight, window.innerWidth / window.innerHeight), this.camera.updateProjectionMatrix();
48
+ }), this.webcam.on("webcamerror", (e) => {
49
+ t({
50
+ code: e.code,
51
+ message: e.message
52
+ });
53
+ }), this.deviceOrientationControls === null ? e(this.locar) : (this.deviceOrientationControls?.on("deviceorientationgranted", (t) => {
54
+ t.target.connect(), e(this.locar);
55
+ }), this.deviceOrientationControls.on("deviceorientationerror", (e) => {
56
+ t({
57
+ code: e.code,
58
+ message: e.message
59
+ });
60
+ }), this.deviceOrientationControls.init());
61
+ });
62
+ }
63
+ #e(e, t, n) {
64
+ if (n < e / t) {
65
+ let r = e * (window.innerHeight / t), i = this.origHfov * (window.innerWidth / r);
66
+ this.camera.fov = i / n;
67
+ } else this.camera.fov = this.origHfov / n;
68
+ }
69
+ }, c = class {
70
+ constructor() {
71
+ this.project = (e, t) => [this.#e(e), this.#t(t)], this.unproject = (e) => [this.#n(e[0]), this.#r(e[1])], this.#e = (e) => e / 180 * this.HALF_EARTH, this.#t = (e) => Math.log(Math.tan((90 + e) * Math.PI / 360)) / (Math.PI / 180) * this.HALF_EARTH / 180, this.#n = (e) => e / this.HALF_EARTH * 180, this.#r = (e) => {
72
+ var t = e / this.HALF_EARTH * 180;
73
+ return t = 180 / Math.PI * (2 * Math.atan(Math.exp(t * Math.PI / 180)) - Math.PI / 2), t;
74
+ }, this.getID = () => "epsg:3857", this.EARTH = 40075016.68, this.HALF_EARTH = 20037508.34;
75
+ }
76
+ #e;
77
+ #t;
78
+ #n;
79
+ #r;
80
+ }, l = class t extends o {
81
+ #e;
82
+ #t;
83
+ #n;
84
+ #r;
85
+ #i;
86
+ #a;
87
+ #o;
88
+ #s;
89
+ #c;
90
+ constructor(e, t, n = {}, r = null, i = new c()) {
91
+ super(), this.scene = e, this.camera = t, this.#e = i, this.#t = null, this.#n = 0, this.#r = 100, this.#i = null, this.setGpsOptions(n), this.#a = null, this.#o = 0, this.#s = 0, this.#c = r;
92
+ }
93
+ setProjection(e) {
94
+ this.#e = e;
95
+ }
96
+ setGpsOptions(e = {}) {
97
+ e.gpsMinDistance !== void 0 && (this.#n = e.gpsMinDistance), e.gpsMinAccuracy !== void 0 && (this.#r = e.gpsMinAccuracy);
98
+ }
99
+ async startGps() {
100
+ if (this.#c) {
101
+ let e = await (await this.#c.sendData("/gps/start", {
102
+ gpsMinDistance: this.#n,
103
+ gpsMinAccuracy: this.#r
104
+ })).json();
105
+ this.#s = e.session;
106
+ }
107
+ return this.#i === null ? (this.#i = navigator.geolocation.watchPosition((e) => {
108
+ this.#f(e);
109
+ }, (e) => {
110
+ this.emit("gpserror", e);
111
+ }, { enableHighAccuracy: !0 }), !0) : !1;
112
+ }
113
+ stopGps() {
114
+ return this.#i === null ? !1 : (navigator.geolocation.clearWatch(this.#i), this.#i = null, !0);
115
+ }
116
+ fakeGps(e, t, n = null, r = 0) {
117
+ n !== null && this.setElevation(n), this.#f({ coords: {
118
+ longitude: e,
119
+ latitude: t,
120
+ accuracy: r
121
+ } });
122
+ }
123
+ lonLatToWorldCoords(e, t) {
124
+ let n = this.#e.project(e, t);
125
+ return this.eastNorthToWorldCoords(n);
126
+ }
127
+ eastNorthToWorldCoords(e) {
128
+ if (this.#a) e[0] -= this.#a[0], e[1] -= this.#a[1];
129
+ else throw "No initial position determined";
130
+ return [e[0], -e[1]];
131
+ }
132
+ add(e, t, n, r, i = {}) {
133
+ e.properties = i, this.#u(e, t, n, r || 0), this.scene.add(e), this.#c?.sendData("/object/new", {
134
+ position: e.position,
135
+ x: e.position.x,
136
+ z: e.position.z,
137
+ session: this.#s,
138
+ properties: i
139
+ });
140
+ }
141
+ addGeoLine(t, n, r = 1) {
142
+ let i = t.map(((t) => {
143
+ let [n, r] = this.lonLatToWorldCoords(t[0], t[1]);
144
+ return new e.Vector3(n, t[2] || 0, r);
145
+ })), a = this.#l(i, r);
146
+ n.setValues({ side: e.DoubleSide });
147
+ let o = new e.Mesh(a, n);
148
+ return this.scene.add(o), o;
149
+ }
150
+ #l(t, n) {
151
+ let r, i, a, o, s = 0, c = 0, l = [], u, d = t.length - 1, f = [];
152
+ for (let e = 0; e < d; e++) r = t[e + 1].x - t[e].x, i = t[e + 1].z - t[e].z, a = t[e + 1].y - t[e].y, o = Math.sqrt(r * r + a * a + i * i), s = -(n / 2 * i) / o, c = n / 2 * r / o, u = [
153
+ t[e].x - s,
154
+ t[e].y,
155
+ t[e].z - c,
156
+ t[e].x + s,
157
+ t[e].y,
158
+ t[e].z + c
159
+ ], e > 0 && u.forEach((e, t) => {
160
+ e = (e + l[t]) / 2;
161
+ }), f.push(...u), l = [
162
+ t[e + 1].x - s,
163
+ t[e + 1].y,
164
+ t[e + 1].z - c,
165
+ t[e + 1].x + s,
166
+ t[e + 1].y,
167
+ t[e + 1].z + c
168
+ ];
169
+ f.push(t[d].x - s), f.push(t[d].y), f.push(t[d].z - c), f.push(t[d].x + s), f.push(t[d].y), f.push(t[d].z + c);
170
+ let p = [];
171
+ for (let e = 0; e < d; e++) p.push(e * 2, e * 2 + 1, e * 2 + 2), p.push(e * 2 + 1, e * 2 + 3, e * 2 + 2);
172
+ let m = new e.BufferGeometry(), h = new Float32Array(f);
173
+ return m.setIndex(p), m.setAttribute("position", new e.BufferAttribute(h, 3)), m.computeBoundingBox(), m;
174
+ }
175
+ #u(e, t, n, r) {
176
+ let i = this.lonLatToWorldCoords(t, n);
177
+ r !== void 0 && (e.position.y = r), [e.position.x, e.position.z] = i;
178
+ }
179
+ setElevation(e) {
180
+ this.camera.position.y = e;
181
+ }
182
+ #d(e, t) {
183
+ this.#a = this.#e.project(e, t);
184
+ }
185
+ #f(e) {
186
+ let n = Number.MAX_VALUE;
187
+ this.#o++, this.#c?.sendData("/gps/new", {
188
+ gpsCount: this.#o,
189
+ lat: e.coords.latitude,
190
+ lon: e.coords.longitude,
191
+ acc: e.coords.accuracy,
192
+ session: this.#s
193
+ }), e.coords.accuracy <= this.#r && (this.#t === null ? this.#t = {
194
+ latitude: e.coords.latitude,
195
+ longitude: e.coords.longitude
196
+ } : n = t.haversineDist(this.#t, e.coords), n >= this.#n && (this.#t.longitude = e.coords.longitude, this.#t.latitude = e.coords.latitude, this.#a || (this.#d(e.coords.longitude, e.coords.latitude), this.#c?.sendData("/worldorigin/new", {
197
+ gpsCount: this.#o,
198
+ lat: e.coords.latitude,
199
+ lon: e.coords.longitude,
200
+ session: this.#s,
201
+ initialPosition: this.#a
202
+ })), this.#u(this.camera, e.coords.longitude, e.coords.latitude), this.#c?.sendData("/gps/accepted", {
203
+ gpsCount: this.#o,
204
+ cameraX: this.camera.position.x,
205
+ cameraZ: this.camera.position.z,
206
+ session: this.#s,
207
+ distMoved: n
208
+ }), this.emit("gpsupdate", {
209
+ position: e,
210
+ distMoved: n
211
+ })));
212
+ }
213
+ static haversineDist(t, n) {
214
+ let r = e.MathUtils.degToRad(n.longitude - t.longitude), i = e.MathUtils.degToRad(n.latitude - t.latitude), a = Math.sin(i / 2) * Math.sin(i / 2) + Math.cos(e.MathUtils.degToRad(t.latitude)) * Math.cos(e.MathUtils.degToRad(n.latitude)) * (Math.sin(r / 2) * Math.sin(r / 2));
215
+ return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) * 6371e3;
216
+ }
217
+ getLastKnownLocation() {
218
+ return this.#t;
219
+ }
220
+ }, u = "locar-device-orientation-permission-modal", d = "locar-device-orientation-permission-button", f = "locar-device-orientation-permission-message", p = "locar-device-orientation-permission-inner", m = "locar-device-orientation-permission-button-inner", h = "This immersive website requires access to your device motion sensors.", g = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, _ = new a(0, 0, 1), v = new t(), y = new i(), b = new i(-Math.sqrt(.5), 0, 0, Math.sqrt(.5));
221
+ 2 * Math.PI;
222
+ var x = .5 * Math.PI, S = class extends n {
223
+ constructor(e, n = {}) {
224
+ super(), this.init = () => {
225
+ window.DeviceOrientationEvent === void 0 ? this.eventEmitter.emit("deviceorientationerror", {
226
+ code: "LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",
227
+ message: "Device orientation API not supported"
228
+ }) : window.isSecureContext === !1 ? this.eventEmitter.emit("deviceorientationerror", {
229
+ code: "LOCAR_DEVICE_ORIENTATION_NO_HTTPS",
230
+ message: "DeviceOrientationEvent is only available in secure contexts (https)"
231
+ }) : typeof window.DeviceOrientationEvent.requestPermission == "function" && this.enablePermissionDialog ? this.obtainPermissionGesture() : this.eventEmitter.emit("deviceorientationgranted", { target: this });
232
+ }, this.eventEmitter = new o();
233
+ let a = this;
234
+ this.object = e, this.object.rotation.reorder("YXZ"), this.enabled = !0, this.deviceOrientation = null, this.screenOrientation = 0, this.alphaOffset = 0, this.orientationOffset = 0, this.initialOffset = null, this.lastQuaternion = null, this.orientationChangeEventName = "ondeviceorientationabsolute" in window ? "deviceorientationabsolute" : "deviceorientation", this.smoothingFactor = n.smoothingFactor || .2, this.enablePermissionDialog = n.enablePermissionDialog ?? !0, this.enableInlineStyling = n.enableStyling ?? !0, this.preferConfirmDialog = n.preferConfirmDialog ?? !1, this.orientationChangeThreshold = n.orientationChangeThreshold ?? 0;
235
+ let s = (t) => {
236
+ let { alpha: n, beta: i, gamma: o, webkitCompassHeading: s } = t;
237
+ if (n ??= 0, i ??= 0, o ??= 0, s ??= 0, g) {
238
+ let e = 360 - s;
239
+ a.alphaOffset = r.degToRad(e - n), a.deviceOrientation = {
240
+ alpha: n,
241
+ beta: i,
242
+ gamma: o,
243
+ webkitCompassHeading: s
244
+ };
245
+ } else n < 0 && (n += 360), a.deviceOrientation = {
246
+ alpha: n,
247
+ beta: i,
248
+ gamma: o
249
+ };
250
+ window.dispatchEvent(new CustomEvent("camera-rotation-change", { detail: { cameraRotation: e.rotation } }));
251
+ }, c = () => {
252
+ a.screenOrientation = window.screen.orientation?.angle ?? 0, g && (a.screenOrientation === 90 ? a.orientationOffset = -x : a.screenOrientation === -90 ? a.orientationOffset = x : a.orientationOffset = 0);
253
+ }, l = (e, t, n, r, i) => {
254
+ v.set(n, t, -r, "YXZ"), e.setFromEuler(v), e.multiply(b), e.multiply(y.setFromAxisAngle(_, -i));
255
+ };
256
+ this.connect = () => {
257
+ c(), window.addEventListener("orientationchange", c), window.addEventListener(a.orientationChangeEventName, s), a.enabled = !0;
258
+ }, this.disconnect = () => {
259
+ window.removeEventListener("orientationchange", c), window.removeEventListener(a.orientationChangeEventName, s), a.enabled = !1, a.initialOffset = !1, a.deviceOrientation = null, a.lastQuaternion = null;
260
+ }, this.requestOrientationPermissions = () => {
261
+ window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then((e) => {
262
+ e === "granted" ? this.eventEmitter.emit("deviceorientationgranted", { target: this }) : this.eventEmitter.emit("deviceorientationerror", {
263
+ code: "LOCAR_DEVICE_ORIENTATION_PERMISSION_DENIED",
264
+ message: "Permission for device orientation denied - AR will not work correctly"
265
+ });
266
+ }).catch((e) => {
267
+ this.eventEmitter.emit("deviceorientationerror", {
268
+ code: "LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",
269
+ message: "Permission request for device orientation failed - AR will not work correctly",
270
+ error: JSON.stringify(e, null, 2)
271
+ });
272
+ }) : this.eventEmitter.emit("deviceorientationerror", {
273
+ code: "LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",
274
+ message: "Internal error: no requestPermission() found although requestOrientationPermissions() was called - please raise an issue on GitHub"
275
+ });
276
+ }, this.update = () => {
277
+ if (a.enabled === !1) return;
278
+ let e = a.deviceOrientation;
279
+ if (e) {
280
+ let n = e.alpha ? r.degToRad(e.alpha) + a.alphaOffset : 0, o = e.beta ? r.degToRad(e.beta) : 0, s = e.gamma ? r.degToRad(e.gamma) : 0, c = a.screenOrientation ? r.degToRad(a.screenOrientation) : 0, u = new i();
281
+ if (g) {
282
+ l(u, n, o, s, c);
283
+ let i = new t().setFromQuaternion(u, "YXZ");
284
+ i.y = r.degToRad(360 - (e.webkitCompassHeading ?? 0)) + (a.orientationOffset || 0), u.setFromEuler(i);
285
+ } else l(u, n, o, s, c);
286
+ if (a.lastQuaternion && a.orientationChangeThreshold > 0 && u.angleTo(a.lastQuaternion) < a.orientationChangeThreshold) return;
287
+ if (a.smoothingFactor < 1 && a.lastQuaternion) {
288
+ let e = 1 - a.smoothingFactor;
289
+ a.object.quaternion.slerp(u, e);
290
+ } else a.object.quaternion.copy(u);
291
+ a.lastQuaternion = a.object.quaternion.clone(), window.dispatchEvent(new CustomEvent("camera-rotation-change", { detail: { cameraRotation: a.object.rotation } }));
292
+ }
293
+ }, this.getCorrectedHeading = () => {
294
+ let { deviceOrientation: e } = a;
295
+ if (!e) return 0;
296
+ let t = 0;
297
+ return g ? (t = 360 - (e.webkitCompassHeading ?? 0), a.orientationOffset && (t += a.orientationOffset * (180 / Math.PI), t = (t + 360) % 360)) : (e.absolute === !0 || a.orientationChangeEventName, t = e.alpha ? e.alpha : 0, t = (360 - t) % 360, t < 0 && (t += 360)), t;
298
+ }, this.updateAlphaOffset = () => {
299
+ a.initialOffset = !1;
300
+ }, this.dispose = () => {
301
+ a.disconnect();
302
+ }, this.getAlpha = () => {
303
+ let { deviceOrientation: e } = a;
304
+ return e && e.alpha ? r.degToRad(e.alpha) + a.alphaOffset : 0;
305
+ }, this.getBeta = () => {
306
+ let { deviceOrientation: e } = a;
307
+ return e && e.beta ? r.degToRad(e.beta) : 0;
308
+ }, this.getGamma = () => {
309
+ let { deviceOrientation: e } = a;
310
+ return e && e.gamma ? r.degToRad(e.gamma) : 0;
311
+ }, this.createObtainPermissionGestureDialog = () => {
312
+ let e = document.createElement("div");
313
+ e.classList.add(u);
314
+ let t = document.createElement("div");
315
+ t.classList.add(p);
316
+ let n = document.createElement("div");
317
+ n.classList.add(f);
318
+ let r = document.createElement("div");
319
+ r.classList.add(m);
320
+ let i = document.createElement("button");
321
+ i.classList.add(d), document.body.appendChild(e), this.enableInlineStyling === !0 && (e.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", e.style.display = "flex", e.style.position = "fixed", e.style.zIndex = "100000", e.style.justifyContent = "center", e.style.alignItems = "center", e.style.backgroundColor = "rgba(0,0,0,0.2)", e.style.inset = "0", e.style.padding = "20px", t.style.backgroundColor = "rgba(220, 220, 220, 0.85)", t.style.padding = "6px 0", t.style.borderRadius = "10px", t.style.width = "100%", t.style.maxWidth = "400px", n.style.padding = "10px 12px", n.style.textAlign = "center", n.style.fontWeight = "400", n.style.fontSize = "13px", n.style.display = "flex", n.style.justifyContent = "center", n.style.alignItems = "center", r.style.display = "block", r.style.textAlign = "center", r.style.textDecoration = "none", r.style.borderTop = "rgb(180,180,180) solid 1px", i.style.display = "block", i.style.width = "100%", i.style.textAlign = "center", i.style.appearance = "none", i.style.background = "none", i.style.border = "none", i.style.outline = "none", i.style.padding = "10px", i.style.fontWeight = "400", i.style.fontSize = "16px", i.style.color = "#2e7cf1", i.style.cursor = "pointer"), e.appendChild(t), t.appendChild(n), t.appendChild(r), n.appendChild(document.createTextNode(h)), i.addEventListener("click", () => {
322
+ this.requestOrientationPermissions(), e.style.display = "none";
323
+ }), i.appendChild(document.createTextNode("OK")), r.appendChild(i), document.body.appendChild(e);
324
+ }, this.obtainPermissionGesture = () => {
325
+ this.preferConfirmDialog === !0 ? window.confirm(h) && this.requestOrientationPermissions() : this.createObtainPermissionGestureDialog();
326
+ };
327
+ }
328
+ on(e, t) {
329
+ this.eventEmitter.on(e, t);
330
+ }
331
+ }, C = class {
332
+ constructor(t) {
333
+ this.raycaster = new e.Raycaster(), this.normalisedMousePosition = null, t.domElement.addEventListener("click", (n) => {
334
+ this.normalisedMousePosition = new e.Vector2(n.clientX / t.domElement.clientWidth * 2 - 1, -(n.clientY / t.domElement.clientHeight * 2) + 1);
335
+ });
336
+ }
337
+ raycast(e, t) {
338
+ if (this.normalisedMousePosition !== null) {
339
+ this.raycaster.setFromCamera(this.normalisedMousePosition, e);
340
+ let n = this.raycaster.intersectObjects(t.children, !1);
341
+ return this.normalisedMousePosition = null, n;
342
+ }
343
+ return [];
344
+ }
345
+ }, w = class extends o {
346
+ #e;
347
+ #t;
348
+ constructor(t = { video: { facingMode: "environment" } }, n) {
349
+ super(), this.sceneWebcam = new e.Scene(), n ? this.#e = document.querySelector(n) : (this.#e = document.createElement("video"), this.#e.setAttribute("autoplay", "true"), this.#e.setAttribute("playsinline", "true"), this.#e.style.cssText += "\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: black;\n position: absolute;\n top: 0px;\n left: 0px;\n z-index: -100; \n ", document.body.appendChild(this.#e)), this.#t = null, navigator.mediaDevices && navigator.mediaDevices.getUserMedia ? navigator.mediaDevices.getUserMedia(t).then((e) => {
350
+ this.#t = e, this.#e?.addEventListener("loadedmetadata", () => {
351
+ this.#e.play(), this.emit("webcamstarted", {
352
+ videoWidth: this.#e.videoWidth,
353
+ videoHeight: this.#e.videoHeight
354
+ });
355
+ }), this.#e && (this.#e.srcObject = e);
356
+ }).catch((e) => {
357
+ this.emit("webcamerror", {
358
+ code: e.name,
359
+ message: e.message
360
+ });
361
+ }) : this.emit("webcamerror", {
362
+ code: "LOCAR_NO_MEDIA_DEVICES_API",
363
+ message: "Media devices API not supported"
364
+ });
365
+ }
366
+ getVideoDimensions() {
367
+ return `w ${this.#e.videoWidth}, h ${this.#e.videoHeight}`;
368
+ }
369
+ dispose() {
370
+ (this.#t?.getTracks() ?? []).forEach((e) => {
371
+ e.stop();
372
+ }), this.#t = null, this.#e?.remove(), this.#e = null;
373
+ }
545
374
  };
375
+ //#endregion
376
+ export { s as App, C as ClickHandler, S as DeviceOrientationControls, o as EventEmitter, l as LocAR, c as SphMercProjection, w as Webcam };