locar 0.1.0 → 0.1.2
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/README.md +3 -3
- package/dist/locar.es.js +168 -128
- package/dist/locar.umd.js +1 -1
- package/lib/types/locar.d.ts +316 -0
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Some of these issues, such as incorrect North on some devices, may well be to do
|
|
|
22
22
|
|
|
23
23
|
## Cross-platform Compatibility
|
|
24
24
|
|
|
25
|
-
It should now work on Chrome
|
|
25
|
+
It should now work on Chrome and Safari on iOS as well as Android, due to a number of recent iOS fixes by project contributors.
|
|
26
26
|
|
|
27
27
|
Firefox unfortunately does not appear to fully implement the Device Orientation API so is unlikely to work.
|
|
28
28
|
|
|
@@ -42,7 +42,7 @@ import * as LocAR from 'locar';
|
|
|
42
42
|
as long as you include `locar` as a dependency, e.g. in your `package.json`:
|
|
43
43
|
```
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"locar" : "^0.1.
|
|
45
|
+
"locar" : "^0.1.2",
|
|
46
46
|
"three" : "^0.175.0"
|
|
47
47
|
}
|
|
48
48
|
```
|
|
@@ -59,7 +59,7 @@ Please see [here](https://github.com/AR-js-org/locar.js/blob/master/docs/tutoria
|
|
|
59
59
|
|
|
60
60
|
## Contributors
|
|
61
61
|
|
|
62
|
-
Many thanks to all contributors to the project, in particular [Darian Elias Weiß](https://github.com/darianwwu)
|
|
62
|
+
Many thanks to all contributors to the project, in particular [Darian Elias Weiß](https://github.com/darianwwu), [Kazuhiro Matsuyama](https://github.com/ma2yama) and [Luigi Mannoni](https://github.com/luigimannoni-smartify) for providing extremely valuable iOS fixes and enhancements.
|
|
63
63
|
|
|
64
64
|
## Disclaimer
|
|
65
65
|
|
package/dist/locar.es.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { Vector3 as
|
|
3
|
-
class
|
|
1
|
+
import * as g from "three";
|
|
2
|
+
import { Vector3 as R, Euler as C, Quaternion as I, EventDispatcher as T, MathUtils as m } from "three";
|
|
3
|
+
class w {
|
|
4
4
|
/**
|
|
5
5
|
* Create a SphMercProjection.
|
|
6
6
|
*/
|
|
@@ -14,12 +14,12 @@ class T {
|
|
|
14
14
|
* @return {Array} Two-member array containing easting and northing.
|
|
15
15
|
*/
|
|
16
16
|
project(e, i) {
|
|
17
|
-
return [this.#t(e), this.#
|
|
17
|
+
return [this.#t(e), this.#l(i)];
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Unproject a Spherical Mercator easting and northing.
|
|
21
21
|
* @param {Array} projected - Two-member array containing easting and northing
|
|
22
|
-
* @return {Array} Two-member array containing longitude and latitude
|
|
22
|
+
* @return {Array} Two-member array containing longitude and latitude
|
|
23
23
|
*/
|
|
24
24
|
unproject(e) {
|
|
25
25
|
return [this.#e(e[0]), this.#s(e[1])];
|
|
@@ -27,7 +27,7 @@ class T {
|
|
|
27
27
|
#t(e) {
|
|
28
28
|
return e / 180 * this.HALF_EARTH;
|
|
29
29
|
}
|
|
30
|
-
#
|
|
30
|
+
#l(e) {
|
|
31
31
|
var i = Math.log(Math.tan((90 + e) * Math.PI / 360)) / (Math.PI / 180);
|
|
32
32
|
return i * this.HALF_EARTH / 180;
|
|
33
33
|
}
|
|
@@ -46,7 +46,7 @@ class T {
|
|
|
46
46
|
return "epsg:3857";
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
class
|
|
49
|
+
class A {
|
|
50
50
|
constructor() {
|
|
51
51
|
this.eventHandlers = {};
|
|
52
52
|
}
|
|
@@ -59,17 +59,18 @@ class P {
|
|
|
59
59
|
this.eventHandlers[e] = i;
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
* Emit an event.
|
|
62
|
+
* Emit an event.
|
|
63
63
|
* @param {string} eventName - the event to emit.
|
|
64
|
-
* @param ...params - parameters to pass to the event handler.
|
|
64
|
+
* @param ...params - parameters to pass to the event handler.
|
|
65
65
|
*/
|
|
66
66
|
emit(e, ...i) {
|
|
67
67
|
this.eventHandlers[e]?.call(this, ...i);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
class
|
|
70
|
+
class k extends A {
|
|
71
71
|
#t;
|
|
72
|
-
|
|
72
|
+
// eslint-disable-next-line no-unused-private-class-members
|
|
73
|
+
#l;
|
|
73
74
|
#e;
|
|
74
75
|
#s;
|
|
75
76
|
#c;
|
|
@@ -80,19 +81,19 @@ class L extends P {
|
|
|
80
81
|
#n;
|
|
81
82
|
/**
|
|
82
83
|
* @param {THREE.Scene} scene - The Three.js scene to use.
|
|
83
|
-
* @param {THREE.Camera} camera - The Three.js camera to use. Should usually
|
|
84
|
+
* @param {THREE.Camera} camera - The Three.js camera to use. Should usually
|
|
84
85
|
* be a THREE.PerspectiveCamera.
|
|
85
86
|
* @param {Object} options - Initialisation options for the GPS; see
|
|
86
87
|
* setGpsOptions() below.
|
|
87
88
|
* @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.
|
|
88
89
|
*/
|
|
89
90
|
constructor(e, i, t = {}, c = null) {
|
|
90
|
-
super(), this.scene = e, this.camera = i, this.#t = new
|
|
91
|
+
super(), this.scene = e, this.camera = i, this.#t = new w(), this.#e = null, this.#s = 0, this.#c = 100, this.#o = null, this.setGpsOptions(t), this.#i = null, this.#r = 0, this.#a = 0, this.#n = c;
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
93
94
|
* Set the projection to use.
|
|
94
|
-
* @param {Object} any object which includes a project() method
|
|
95
|
-
* taking longitude and latitude as arguments and returning an array
|
|
95
|
+
* @param {Object} any object which includes a project() method
|
|
96
|
+
* taking longitude and latitude as arguments and returning an array
|
|
96
97
|
* containing easting and northing.
|
|
97
98
|
*/
|
|
98
99
|
setProjection(e) {
|
|
@@ -102,7 +103,7 @@ class L extends P {
|
|
|
102
103
|
* Set the GPS options.
|
|
103
104
|
* @param {Object} object containing gpsMinDistance and/or gpsMinAccuracy
|
|
104
105
|
* properties. The former specifies the number of metres which the device
|
|
105
|
-
* must move to process a new GPS reading, and the latter specifies the
|
|
106
|
+
* must move to process a new GPS reading, and the latter specifies the
|
|
106
107
|
* minimum accuracy, in metres, for a GPS reading to be counted.
|
|
107
108
|
*/
|
|
108
109
|
setGpsOptions(e = {}) {
|
|
@@ -123,7 +124,7 @@ class L extends P {
|
|
|
123
124
|
}
|
|
124
125
|
return this.#o === null ? (this.#o = navigator.geolocation.watchPosition(
|
|
125
126
|
(e) => {
|
|
126
|
-
this.#
|
|
127
|
+
this.#h(e);
|
|
127
128
|
},
|
|
128
129
|
(e) => {
|
|
129
130
|
this.emit("gpserror", e);
|
|
@@ -151,7 +152,7 @@ class L extends P {
|
|
|
151
152
|
* ignored if lower than the specified minimum accuracy.
|
|
152
153
|
*/
|
|
153
154
|
fakeGps(e, i, t = null, c = 0) {
|
|
154
|
-
t !== null && this.setElevation(t), this.#
|
|
155
|
+
t !== null && this.setElevation(t), this.#h({
|
|
155
156
|
coords: {
|
|
156
157
|
longitude: e,
|
|
157
158
|
latitude: i,
|
|
@@ -183,23 +184,23 @@ class L extends P {
|
|
|
183
184
|
* @param {THREE.Mesh} object the object
|
|
184
185
|
* @param {number} lon - the longitude.
|
|
185
186
|
* @param {number} lat - the latitude.
|
|
186
|
-
* @param {number} elev - the elevation in metres
|
|
187
|
+
* @param {number} elev - the elevation in metres
|
|
187
188
|
* (if not specified, 0 is assigned)
|
|
188
189
|
* @param {Object} properties - properties describing the object (for example,
|
|
189
190
|
* the contents of the GeoJSON properties field).
|
|
190
191
|
*/
|
|
191
|
-
add(e, i, t, c,
|
|
192
|
-
e.properties =
|
|
192
|
+
add(e, i, t, c, h = {}) {
|
|
193
|
+
e.properties = h, this.#d(e, i, t, c), this.scene.add(e), this.#n?.sendData("/object/new", {
|
|
193
194
|
position: e.position,
|
|
194
195
|
x: e.position.x,
|
|
195
196
|
z: e.position.z,
|
|
196
197
|
session: this.#a,
|
|
197
|
-
properties:
|
|
198
|
+
properties: h
|
|
198
199
|
});
|
|
199
200
|
}
|
|
200
201
|
#d(e, i, t, c) {
|
|
201
|
-
const
|
|
202
|
-
c !== void 0 && (e.position.y = c), [e.position.x, e.position.z] =
|
|
202
|
+
const h = this.lonLatToWorldCoords(i, t);
|
|
203
|
+
c !== void 0 && (e.position.y = c), [e.position.x, e.position.z] = h;
|
|
203
204
|
}
|
|
204
205
|
/**
|
|
205
206
|
* Set the elevation (y coordinate) of the camera.
|
|
@@ -211,7 +212,7 @@ class L extends P {
|
|
|
211
212
|
#u(e, i) {
|
|
212
213
|
this.#i = this.#t.project(e, i);
|
|
213
214
|
}
|
|
214
|
-
#
|
|
215
|
+
#h(e) {
|
|
215
216
|
let i = Number.MAX_VALUE;
|
|
216
217
|
this.#r++, this.#n?.sendData("/gps/new", {
|
|
217
218
|
gpsCount: this.#r,
|
|
@@ -249,22 +250,22 @@ class L extends P {
|
|
|
249
250
|
* Taken from original A-Frame AR.js location-based components
|
|
250
251
|
*/
|
|
251
252
|
#m(e, i) {
|
|
252
|
-
const t =
|
|
253
|
-
return 2 * Math.atan2(Math.sqrt(
|
|
253
|
+
const t = g.MathUtils.degToRad(i.longitude - e.longitude), c = g.MathUtils.degToRad(i.latitude - e.latitude), h = Math.sin(c / 2) * Math.sin(c / 2) + Math.cos(g.MathUtils.degToRad(e.latitude)) * Math.cos(g.MathUtils.degToRad(i.latitude)) * (Math.sin(t / 2) * Math.sin(t / 2));
|
|
254
|
+
return 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)) * 6371e3;
|
|
254
255
|
}
|
|
255
256
|
}
|
|
256
|
-
class
|
|
257
|
+
class W extends A {
|
|
257
258
|
#t;
|
|
258
259
|
/**
|
|
259
260
|
* Create a Webcam.
|
|
260
|
-
* @param constraints {Object} - options to use for initialising the camera.
|
|
261
|
-
*
|
|
262
|
-
* @param {string} videoElementSelector - selector to obtain the HTML video
|
|
263
|
-
* element to render the webcam feed. If a falsy value (e.g. null or
|
|
261
|
+
* @param constraints {Object} - options to use for initialising the camera.
|
|
262
|
+
* This is the same constraints object as used by standard MediaDevices API.
|
|
263
|
+
* @param {string} videoElementSelector - selector to obtain the HTML video
|
|
264
|
+
* element to render the webcam feed. If a falsy value (e.g. null or
|
|
264
265
|
* undefined), a video element will be created.
|
|
265
266
|
*/
|
|
266
267
|
constructor(e = { video: { facingMode: "environment" } }, i) {
|
|
267
|
-
super(), this.sceneWebcam = new
|
|
268
|
+
super(), this.sceneWebcam = new g.Scene(), i ? this.#t = document.querySelector(i) : (this.#t = document.createElement("video"), this.#t.setAttribute("autoplay", !0), this.#t.setAttribute("playsinline", !0), this.#t.style.display = "none", document.body.appendChild(this.#t)), this.texture = new g.VideoTexture(this.#t), navigator.mediaDevices && navigator.mediaDevices.getUserMedia ? navigator.mediaDevices.getUserMedia(e).then((t) => {
|
|
268
269
|
this.#t.addEventListener("loadedmetadata", () => {
|
|
269
270
|
this.#t.setAttribute("width", this.#t.videoWidth), this.#t.setAttribute("height", this.#t.videoHeight), this.#t.play(), this.emit("webcamstarted", { texture: this.texture });
|
|
270
271
|
}), this.#t.srcObject = t;
|
|
@@ -286,25 +287,25 @@ class H extends P {
|
|
|
286
287
|
this.texture.dispose();
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
|
-
const
|
|
290
|
-
class
|
|
290
|
+
const b = "locar-device-orientation-permission-modal", D = "locar-device-orientation-permission-button", S = "locar-device-orientation-permission-message", N = "locar-device-orientation-permission-inner", L = "locar-device-orientation-permission-button-inner", P = "This immersive website requires access to your device motion sensors.", O = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, x = new R(0, 0, 1), y = new C(), H = new I(), F = new I(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
|
|
291
|
+
class V extends T {
|
|
291
292
|
/**
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
293
|
+
* Create an instance of DeviceOrientationControls.
|
|
294
|
+
* @param {Object} object - the object to attach the controls to
|
|
295
|
+
* (usually your Three.js camera)
|
|
296
|
+
* @param {Object} options - options for DeviceOrientationControls: currently accepts smoothingFactor, enablePermissionDialog
|
|
297
|
+
*/
|
|
297
298
|
constructor(e, i = {}) {
|
|
298
|
-
super(), this.eventEmitter = new
|
|
299
|
+
super(), this.eventEmitter = new A();
|
|
299
300
|
const t = this;
|
|
300
|
-
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.lastCompassY = void 0, this.lastOrientation = null, this.TWO_PI = 2 * Math.PI, this.HALF_PI = 0.5 * Math.PI, this.orientationChangeEventName = "ondeviceorientationabsolute" in window ? "deviceorientationabsolute" : "deviceorientation",
|
|
301
|
+
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.lastCompassY = void 0, this.lastOrientation = null, this.TWO_PI = 2 * Math.PI, this.HALF_PI = 0.5 * Math.PI, this.orientationChangeEventName = "ondeviceorientationabsolute" in window ? "deviceorientationabsolute" : "deviceorientation", this.smoothingFactor = i.smoothingFactor || 1, this.enablePermissionDialog = i.enablePermissionDialog ?? !0, this.enableInlineStyling = i.enableStyling ?? !0, this.preferConfirmDialog = i.preferConfirmDialog ?? !1;
|
|
301
302
|
const c = function({
|
|
302
303
|
alpha: n,
|
|
303
304
|
beta: s,
|
|
304
305
|
gamma: a,
|
|
305
306
|
webkitCompassHeading: r
|
|
306
307
|
}) {
|
|
307
|
-
if (
|
|
308
|
+
if (O) {
|
|
308
309
|
const o = 360 - r;
|
|
309
310
|
t.alphaOffset = m.degToRad(o - n), t.deviceOrientation = { alpha: n, beta: s, gamma: a, webkitCompassHeading: r };
|
|
310
311
|
} else
|
|
@@ -314,15 +315,15 @@ class x extends C {
|
|
|
314
315
|
detail: { cameraRotation: e.rotation }
|
|
315
316
|
})
|
|
316
317
|
);
|
|
317
|
-
},
|
|
318
|
-
t.screenOrientation = window.orientation || 0,
|
|
319
|
-
},
|
|
320
|
-
y.set(a, s, -r, "YXZ"), n.setFromEuler(y), n.multiply(
|
|
318
|
+
}, h = function() {
|
|
319
|
+
t.screenOrientation = window.orientation || 0, O && (t.screenOrientation === 90 ? t.orientationOffset = -t.HALF_PI : t.screenOrientation === -90 ? t.orientationOffset = t.HALF_PI : t.orientationOffset = 0);
|
|
320
|
+
}, E = function(n, s, a, r, o) {
|
|
321
|
+
y.set(a, s, -r, "YXZ"), n.setFromEuler(y), n.multiply(F), n.multiply(H.setFromAxisAngle(x, -o));
|
|
321
322
|
};
|
|
322
323
|
this.connect = function() {
|
|
323
|
-
|
|
324
|
+
h(), window.addEventListener(
|
|
324
325
|
"orientationchange",
|
|
325
|
-
|
|
326
|
+
h
|
|
326
327
|
), window.addEventListener(
|
|
327
328
|
t.orientationChangeEventName,
|
|
328
329
|
c
|
|
@@ -330,7 +331,7 @@ class x extends C {
|
|
|
330
331
|
}, this.disconnect = function() {
|
|
331
332
|
window.removeEventListener(
|
|
332
333
|
"orientationchange",
|
|
333
|
-
|
|
334
|
+
h
|
|
334
335
|
), window.removeEventListener(
|
|
335
336
|
t.orientationChangeEventName,
|
|
336
337
|
c
|
|
@@ -346,7 +347,8 @@ class x extends C {
|
|
|
346
347
|
}).catch(function(n) {
|
|
347
348
|
this.eventEmitter.emit("deviceorientationerror", {
|
|
348
349
|
code: "LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",
|
|
349
|
-
message: "Permission request for device orientation failed - AR will not work correctly"
|
|
350
|
+
message: "Permission request for device orientation failed - AR will not work correctly",
|
|
351
|
+
error: JSON.stringify(n, null, 2)
|
|
350
352
|
});
|
|
351
353
|
}) : this.eventEmitter.emit("deviceorientationerror", {
|
|
352
354
|
code: "LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",
|
|
@@ -357,22 +359,22 @@ class x extends C {
|
|
|
357
359
|
const s = t.deviceOrientation;
|
|
358
360
|
if (s) {
|
|
359
361
|
let a = s.alpha ? m.degToRad(s.alpha) + t.alphaOffset : 0, r = s.beta ? m.degToRad(s.beta) : 0, o = s.gamma ? m.degToRad(s.gamma) : 0;
|
|
360
|
-
const
|
|
362
|
+
const f = t.screenOrientation ? m.degToRad(t.screenOrientation) : 0;
|
|
361
363
|
if (t.smoothingFactor < 1) {
|
|
362
364
|
if (t.lastOrientation) {
|
|
363
|
-
const
|
|
365
|
+
const d = t.smoothingFactor;
|
|
364
366
|
a = t._getSmoothedAngle(
|
|
365
367
|
a,
|
|
366
368
|
t.lastOrientation.alpha,
|
|
367
|
-
|
|
369
|
+
d
|
|
368
370
|
), r = t._getSmoothedAngle(
|
|
369
371
|
r + Math.PI,
|
|
370
372
|
t.lastOrientation.beta,
|
|
371
|
-
|
|
373
|
+
d
|
|
372
374
|
), o = t._getSmoothedAngle(
|
|
373
375
|
o + t.HALF_PI,
|
|
374
376
|
t.lastOrientation.gamma,
|
|
375
|
-
|
|
377
|
+
d,
|
|
376
378
|
Math.PI
|
|
377
379
|
);
|
|
378
380
|
} else
|
|
@@ -383,42 +385,46 @@ class x extends C {
|
|
|
383
385
|
gamma: o
|
|
384
386
|
};
|
|
385
387
|
}
|
|
386
|
-
if (
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
if (O) {
|
|
389
|
+
const d = new I();
|
|
390
|
+
E(
|
|
391
|
+
d,
|
|
390
392
|
a,
|
|
391
393
|
t.smoothingFactor < 1 ? r - Math.PI : r,
|
|
392
394
|
t.smoothingFactor < 1 ? o - Math.PI / 2 : o,
|
|
393
|
-
|
|
395
|
+
f
|
|
394
396
|
);
|
|
395
|
-
const u = new
|
|
396
|
-
|
|
397
|
+
const u = new C().setFromQuaternion(
|
|
398
|
+
d,
|
|
397
399
|
"YXZ"
|
|
398
400
|
);
|
|
399
|
-
let
|
|
400
|
-
t.smoothingFactor < 1 && t.lastCompassY !== void 0 && (
|
|
401
|
+
let p = m.degToRad(360 - s.webkitCompassHeading);
|
|
402
|
+
t.smoothingFactor < 1 && t.lastCompassY !== void 0 && (p = t._getSmoothedAngle(
|
|
403
|
+
p,
|
|
404
|
+
t.lastCompassY,
|
|
405
|
+
t.smoothingFactor
|
|
406
|
+
)), t.lastCompassY = p, u.y = p + (t.orientationOffset || 0), d.setFromEuler(u), t.object.quaternion.copy(d);
|
|
401
407
|
} else
|
|
402
|
-
|
|
408
|
+
E(
|
|
403
409
|
t.object.quaternion,
|
|
404
|
-
|
|
410
|
+
O ? a + t.alphaOffset : a,
|
|
405
411
|
t.smoothingFactor < 1 ? r - Math.PI : r,
|
|
406
412
|
t.smoothingFactor < 1 ? o - Math.PI / 2 : o,
|
|
407
|
-
|
|
413
|
+
f
|
|
408
414
|
);
|
|
409
415
|
}
|
|
410
416
|
}, this.getCorrectedHeading = function() {
|
|
411
417
|
const { deviceOrientation: n } = t;
|
|
412
418
|
if (!n) return 0;
|
|
413
419
|
let s = 0;
|
|
414
|
-
return
|
|
420
|
+
return O ? (s = 360 - n.webkitCompassHeading, 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;
|
|
415
421
|
}, this._orderAngle = function(n, s, a = t.TWO_PI) {
|
|
416
422
|
return s > n && Math.abs(s - n) < a / 2 || n > s && Math.abs(s - n) > a / 2 ? { left: n, right: s } : { left: s, right: n };
|
|
417
423
|
}, this._getSmoothedAngle = function(n, s, a, r = t.TWO_PI) {
|
|
418
|
-
const o = t._orderAngle(n, s, r),
|
|
419
|
-
o.left = 0, o.right -=
|
|
420
|
-
let u =
|
|
421
|
-
return u +=
|
|
424
|
+
const o = t._orderAngle(n, s, r), f = o.left, d = o.right;
|
|
425
|
+
o.left = 0, o.right -= f, o.right < 0 && (o.right += r);
|
|
426
|
+
let u = d == s ? (1 - a) * o.right + a * o.left : a * o.right + (1 - a) * o.left;
|
|
427
|
+
return u += f, u >= r && (u -= r), u;
|
|
422
428
|
}, this.updateAlphaOffset = function() {
|
|
423
429
|
t.initialOffset = !1;
|
|
424
430
|
}, this.dispose = function() {
|
|
@@ -432,57 +438,91 @@ class x extends C {
|
|
|
432
438
|
}, this.getGamma = function() {
|
|
433
439
|
const { deviceOrientation: n } = t;
|
|
434
440
|
return n && n.gamma ? m.degToRad(n.gamma) : 0;
|
|
435
|
-
}, this.
|
|
436
|
-
const n = document.createElement("div")
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
441
|
+
}, this.createObtainPermissionGestureDialog = function() {
|
|
442
|
+
const n = document.createElement("div");
|
|
443
|
+
n.classList.add(b);
|
|
444
|
+
const s = document.createElement("div");
|
|
445
|
+
s.classList.add(N);
|
|
446
|
+
const a = document.createElement("div");
|
|
447
|
+
a.classList.add(S);
|
|
448
|
+
const r = document.createElement("div");
|
|
449
|
+
r.classList.add(L);
|
|
450
|
+
const o = document.createElement("button");
|
|
451
|
+
if (o.classList.add(D), document.body.appendChild(n), this.enableInlineStyling === !0) {
|
|
452
|
+
const d = {
|
|
453
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
|
|
454
|
+
display: "flex",
|
|
455
|
+
position: "fixed",
|
|
456
|
+
zIndex: 1e5,
|
|
457
|
+
justifyContent: "center",
|
|
458
|
+
alignItems: "center",
|
|
459
|
+
backgroundColor: "rgba(0,0,0,0.2)",
|
|
460
|
+
inset: 0,
|
|
461
|
+
padding: "20px"
|
|
462
|
+
}, u = {
|
|
463
|
+
backgroundColor: "rgba(220, 220, 220, 0.85)",
|
|
464
|
+
padding: "6px 0",
|
|
465
|
+
borderRadius: "10px",
|
|
466
|
+
width: "100%",
|
|
467
|
+
maxWidth: "400px"
|
|
468
|
+
}, p = {
|
|
469
|
+
padding: "10px 12px",
|
|
470
|
+
textAlign: "center",
|
|
471
|
+
fontWeight: 400,
|
|
472
|
+
fontSize: "13px",
|
|
473
|
+
display: "flex",
|
|
474
|
+
justifyContent: "center",
|
|
475
|
+
alignItems: "center"
|
|
476
|
+
}, M = {
|
|
477
|
+
display: "block",
|
|
478
|
+
textAlign: "center",
|
|
479
|
+
textDecoration: "none",
|
|
480
|
+
borderTop: "rgb(180,180,180) solid 1px"
|
|
481
|
+
}, _ = {
|
|
482
|
+
display: "block",
|
|
483
|
+
width: "100%",
|
|
484
|
+
textAlign: "center",
|
|
485
|
+
appearance: "none",
|
|
486
|
+
background: "none",
|
|
487
|
+
border: "none",
|
|
488
|
+
outline: "none",
|
|
489
|
+
padding: "10px",
|
|
490
|
+
fontWeight: 400,
|
|
491
|
+
fontSize: "16px",
|
|
492
|
+
color: "#2e7cf1",
|
|
493
|
+
cursor: "pointer"
|
|
494
|
+
};
|
|
495
|
+
for (let l in d)
|
|
496
|
+
n.style[l] = d[l];
|
|
497
|
+
for (let l in u)
|
|
498
|
+
s.style[l] = u[l];
|
|
499
|
+
for (let l in p)
|
|
500
|
+
a.style[l] = p[l];
|
|
501
|
+
for (let l in M)
|
|
502
|
+
r.style[l] = M[l];
|
|
503
|
+
for (let l in _)
|
|
504
|
+
o.style[l] = _[l];
|
|
505
|
+
}
|
|
506
|
+
n.appendChild(s), s.appendChild(a), s.appendChild(r), a.appendChild(
|
|
507
|
+
document.createTextNode(P)
|
|
508
|
+
);
|
|
509
|
+
const f = () => {
|
|
478
510
|
this.requestOrientationPermissions(), n.style.display = "none";
|
|
479
|
-
}
|
|
480
|
-
|
|
511
|
+
};
|
|
512
|
+
o.addEventListener("click", f), o.appendChild(document.createTextNode("OK")), r.appendChild(o), document.body.appendChild(n);
|
|
513
|
+
}, this.obtainPermissionGesture = function() {
|
|
514
|
+
this.preferConfirmDialog === !0 ? window.confirm(P) && this.requestOrientationPermissions() : this.createObtainPermissionGestureDialog();
|
|
481
515
|
};
|
|
482
516
|
}
|
|
483
517
|
on(e, i) {
|
|
484
518
|
this.eventEmitter.on(e, i);
|
|
485
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Initialise device orientation controls.
|
|
522
|
+
* Should be called after you have created the DeviceOrientationControls
|
|
523
|
+
* object and set up the deviceorientationgranted and deviceorientationerror
|
|
524
|
+
* event handlers.
|
|
525
|
+
*/
|
|
486
526
|
init() {
|
|
487
527
|
window.DeviceOrientationEvent === void 0 ? this.eventEmitter.emit("deviceorientationerror", {
|
|
488
528
|
code: "LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",
|
|
@@ -493,14 +533,14 @@ class x extends C {
|
|
|
493
533
|
}) : typeof window.DeviceOrientationEvent.requestPermission == "function" && this.enablePermissionDialog ? this.obtainPermissionGesture() : this.eventEmitter.emit("deviceorientationgranted", { target: this });
|
|
494
534
|
}
|
|
495
535
|
}
|
|
496
|
-
class
|
|
536
|
+
class j {
|
|
497
537
|
/**
|
|
498
538
|
* Create a ClickHandler.
|
|
499
539
|
* @param {THREE.WebGLRenderer} - The Three.js renderer on which the click
|
|
500
540
|
* events will be handled.
|
|
501
541
|
*/
|
|
502
542
|
constructor(e) {
|
|
503
|
-
this.raycaster = new
|
|
543
|
+
this.raycaster = new g.Raycaster(), this.normalisedMousePosition = new g.Vector2(null, null), e.domElement.addEventListener("click", (i) => {
|
|
504
544
|
this.normalisedMousePosition.set(
|
|
505
545
|
i.clientX / e.domElement.clientWidth * 2 - 1,
|
|
506
546
|
-(i.clientY / e.domElement.clientHeight * 2) + 1
|
|
@@ -524,13 +564,13 @@ class N {
|
|
|
524
564
|
return [];
|
|
525
565
|
}
|
|
526
566
|
}
|
|
527
|
-
const
|
|
567
|
+
const U = "0.1.2";
|
|
528
568
|
export {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
569
|
+
j as ClickHandler,
|
|
570
|
+
V as DeviceOrientationControls,
|
|
571
|
+
A as EventEmitter,
|
|
572
|
+
k as LocationBased,
|
|
573
|
+
w as SphMercProjection,
|
|
574
|
+
W as Webcam,
|
|
575
|
+
U as version
|
|
536
576
|
};
|
package/dist/locar.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(d,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],c):(d=typeof globalThis<"u"?globalThis:d||self,c(d.locar={},d.THREE))})(this,(function(d,c){"use strict";function E(u){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(u){for(const i in u)if(i!=="default"){const e=Object.getOwnPropertyDescriptor(u,i);Object.defineProperty(t,i,e.get?e:{enumerable:!0,get:()=>u[i]})}}return t.default=u,Object.freeze(t)}const v=E(c);class A{constructor(){this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}project(t,i){return[this.#t(t),this.#l(i)]}unproject(t){return[this.#e(t[0]),this.#s(t[1])]}#t(t){return t/180*this.HALF_EARTH}#l(t){var i=Math.log(Math.tan((90+t)*Math.PI/360))/(Math.PI/180);return i*this.HALF_EARTH/180}#e(t){return t/this.HALF_EARTH*180}#s(t){var i=t/this.HALF_EARTH*180;return i=180/Math.PI*(2*Math.atan(Math.exp(i*Math.PI/180))-Math.PI/2),i}getID(){return"epsg:3857"}}class y{constructor(){this.eventHandlers={}}on(t,i){this.eventHandlers[t]=i}emit(t,...i){this.eventHandlers[t]?.call(this,...i)}}class C extends y{#t;#l;#e;#s;#c;#o;#i;#r;#a;#n;constructor(t,i,e={},l=null){super(),this.scene=t,this.camera=i,this.#t=new A,this.#e=null,this.#s=0,this.#c=100,this.#o=null,this.setGpsOptions(e),this.#i=null,this.#r=0,this.#a=0,this.#n=l}setProjection(t){this.#t=t}setGpsOptions(t={}){t.gpsMinDistance!==void 0&&(this.#s=t.gpsMinDistance),t.gpsMinAccuracy!==void 0&&(this.#c=t.gpsMinAccuracy)}async startGps(){if(this.#n){const i=await(await this.#n.sendData("/gps/start",{gpsMinDistance:this.#s,gpsMinAccuracy:this.#c})).json();this.#a=i.session}return this.#o===null?(this.#o=navigator.geolocation.watchPosition(t=>{this.#h(t)},t=>{this.emit("gpserror",t)},{enableHighAccuracy:!0}),!0):!1}stopGps(){return this.#o!==null?(navigator.geolocation.clearWatch(this.#o),this.#o=null,!0):!1}fakeGps(t,i,e=null,l=0){e!==null&&this.setElevation(e),this.#h({coords:{longitude:t,latitude:i,accuracy:l}})}lonLatToWorldCoords(t,i){const e=this.#t.project(t,i);if(this.#i)e[0]-=this.#i[0],e[1]-=this.#i[1];else throw"No initial position determined";return[e[0],-e[1]]}add(t,i,e,l,m={}){t.properties=m,this.#d(t,i,e,l),this.scene.add(t),this.#n?.sendData("/object/new",{position:t.position,x:t.position.x,z:t.position.z,session:this.#a,properties:m})}#d(t,i,e,l){const m=this.lonLatToWorldCoords(i,e);l!==void 0&&(t.position.y=l),[t.position.x,t.position.z]=m}setElevation(t){this.camera.position.y=t}#u(t,i){this.#i=this.#t.project(t,i)}#h(t){let i=Number.MAX_VALUE;this.#r++,this.#n?.sendData("/gps/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,acc:t.coords.accuracy,session:this.#a}),t.coords.accuracy<=this.#c&&(this.#e===null?this.#e={latitude:t.coords.latitude,longitude:t.coords.longitude}:i=this.#m(this.#e,t.coords),i>=this.#s&&(this.#e.longitude=t.coords.longitude,this.#e.latitude=t.coords.latitude,this.#i||(this.#u(t.coords.longitude,t.coords.latitude),this.#n?.sendData("/worldorigin/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,session:this.#a,initialPosition:this.#i})),this.#d(this.camera,t.coords.longitude,t.coords.latitude),this.#n?.sendData("/gps/accepted",{gpsCount:this.#r,cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:this.#a,distMoved:i}),this.emit("gpsupdate",{position:t,distMoved:i})))}#m(t,i){const e=v.MathUtils.degToRad(i.longitude-t.longitude),l=v.MathUtils.degToRad(i.latitude-t.latitude),m=Math.sin(l/2)*Math.sin(l/2)+Math.cos(v.MathUtils.degToRad(t.latitude))*Math.cos(v.MathUtils.degToRad(i.latitude))*(Math.sin(e/2)*Math.sin(e/2));return 2*Math.atan2(Math.sqrt(m),Math.sqrt(1-m))*6371e3}}class _ extends y{#t;constructor(t={video:{facingMode:"environment"}},i){super(),this.sceneWebcam=new v.Scene,i?this.#t=document.querySelector(i):(this.#t=document.createElement("video"),this.#t.setAttribute("autoplay",!0),this.#t.setAttribute("playsinline",!0),this.#t.style.display="none",document.body.appendChild(this.#t)),this.texture=new v.VideoTexture(this.#t),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices.getUserMedia(t).then(e=>{this.#t.addEventListener("loadedmetadata",()=>{this.#t.setAttribute("width",this.#t.videoWidth),this.#t.setAttribute("height",this.#t.videoHeight),this.#t.play(),this.emit("webcamstarted",{texture:this.texture})}),this.#t.srcObject=e}).catch(e=>{this.emit("webcamerror",{code:e.name,message:e.message})}):this.emit("webcamerror",{code:"LOCAR_NO_MEDIA_DEVICES_API",message:"Media devices API not supported"})}dispose(){this.texture.dispose()}}const w=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,b=new c.Vector3(0,0,1),I=new c.Euler,D=new c.Quaternion,T=new c.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5));class L extends c.EventDispatcher{constructor(t,i={}){super(),this.eventEmitter=new y;const e=this;this.object=t,this.object.rotation.reorder("YXZ"),this.enabled=!0,this.deviceOrientation=null,this.screenOrientation=0,this.alphaOffset=0,this.orientationOffset=0,this.initialOffset=null,this.lastCompassY=void 0,this.lastOrientation=null,this.TWO_PI=2*Math.PI,this.HALF_PI=.5*Math.PI,this.orientationChangeEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation",console.log("Device Orientation Event Name:",this.orientationChangeEventName),this.smoothingFactor=i.smoothingFactor||1,this.enablePermissionDialog=i.enablePermissionDialog??!0;const l=function({alpha:n,beta:s,gamma:a,webkitCompassHeading:r}){if(w){const o=360-r;e.alphaOffset=c.MathUtils.degToRad(o-n),e.deviceOrientation={alpha:n,beta:s,gamma:a,webkitCompassHeading:r}}else n<0&&(n+=360),e.deviceOrientation={alpha:n,beta:s,gamma:a};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.rotation}}))},m=function(){e.screenOrientation=window.orientation||0,w&&(e.screenOrientation===90?e.orientationOffset=-e.HALF_PI:e.screenOrientation===-90?e.orientationOffset=e.HALF_PI:e.orientationOffset=0)},P=function(n,s,a,r,o){I.set(a,s,-r,"YXZ"),n.setFromEuler(I),n.multiply(T),n.multiply(D.setFromAxisAngle(b,-o))};this.connect=function(){m(),window.addEventListener("orientationchange",m),window.addEventListener(e.orientationChangeEventName,l),e.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",m),window.removeEventListener(e.orientationChangeEventName,l),e.enabled=!1,e.initialOffset=!1,e.deviceOrientation=null},this.requestOrientationPermissions=function(){window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(n=>{n==="granted"?this.eventEmitter.emit("deviceorientationgranted",{target:this}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_DENIED",message:"Permission for device orientation denied - AR will not work correctly"})}).catch(function(n){this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",message:"Permission request for device orientation failed - AR will not work correctly"})}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",message:"Internal error: no requestPermission() found although requestOrientationPermissions() was called - please raise an issue on GitHub"})},this.update=function({theta:n=0}={theta:0}){if(e.enabled===!1)return;const s=e.deviceOrientation;if(s){let a=s.alpha?c.MathUtils.degToRad(s.alpha)+e.alphaOffset:0,r=s.beta?c.MathUtils.degToRad(s.beta):0,o=s.gamma?c.MathUtils.degToRad(s.gamma):0;const p=e.screenOrientation?c.MathUtils.degToRad(e.screenOrientation):0;if(e.smoothingFactor<1){if(e.lastOrientation){const h=e.smoothingFactor;a=e._getSmoothedAngle(a,e.lastOrientation.alpha,h),r=e._getSmoothedAngle(r+Math.PI,e.lastOrientation.beta,h),o=e._getSmoothedAngle(o+e.HALF_PI,e.lastOrientation.gamma,h,Math.PI)}else r+=Math.PI,o+=e.HALF_PI;e.lastOrientation={alpha:a,beta:r,gamma:o}}if(w){const h=new c.Quaternion;P(h,a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,p);const f=new c.Euler().setFromQuaternion(h,"YXZ");let O=c.MathUtils.degToRad(360-s.webkitCompassHeading);e.smoothingFactor<1&&e.lastCompassY!==void 0&&(O=e._getSmoothedAngle(O,e.lastCompassY,e.smoothingFactor)),e.lastCompassY=O,f.y=O+(e.orientationOffset||0),h.setFromEuler(f),e.object.quaternion.copy(h)}else P(e.object.quaternion,w?a+e.alphaOffset:a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,p)}},this.getCorrectedHeading=function(){const{deviceOrientation:n}=e;if(!n)return 0;let s=0;return w?(s=360-n.webkitCompassHeading,e.orientationOffset&&(s+=e.orientationOffset*(180/Math.PI),s=(s+360)%360)):(n.absolute===!0||e.orientationChangeEventName,s=n.alpha?n.alpha:0,s=(360-s)%360,s<0&&(s+=360)),s},this._orderAngle=function(n,s,a=e.TWO_PI){return s>n&&Math.abs(s-n)<a/2||n>s&&Math.abs(s-n)>a/2?{left:n,right:s}:{left:s,right:n}},this._getSmoothedAngle=function(n,s,a,r=e.TWO_PI){const o=e._orderAngle(n,s,r),p=o.left,h=o.right;o.left=0,o.right-=p,o.right<0&&(o.right+=r);let f=h==s?(1-a)*o.right+a*o.left:a*o.right+(1-a)*o.left;return f+=p,f>=r&&(f-=r),f},this.updateAlphaOffset=function(){e.initialOffset=!1},this.dispose=function(){e.disconnect()},this.getAlpha=function(){const{deviceOrientation:n}=e;return n&&n.alpha?c.MathUtils.degToRad(n.alpha)+e.alphaOffset:0},this.getBeta=function(){const{deviceOrientation:n}=e;return n&&n.beta?c.MathUtils.degToRad(n.beta):0},this.getGamma=function(){const{deviceOrientation:n}=e;return n&&n.gamma?c.MathUtils.degToRad(n.gamma):0},this.obtainPermissionGesture=function(){const n=document.createElement("div"),s=document.createElement("div"),a=document.createElement("div"),r=document.createElement("div");document.body.appendChild(n);const o={display:"flex",position:"fixed",top:0,left:0,width:"100%",height:"100%",zIndex:1,backgroundColor:"rgba(0,0,0,0.6)",justifyContent:"center",alignItems:"center"},p={backgroundColor:"white",padding:"6px",borderRadius:"3px",width:"36rem",height:"24rem"},h={width:"100%",height:"70%",display:"flex",justifyContent:"center",alignItems:"center"},f={display:"inline-flex",width:"100%",height:"30%",justifyContent:"center",alignItems:"center"};for(let g in o)n.style[g]=o[g];for(let g in p)s.style[g]=p[g];for(let g in h)a.style[g]=h[g];for(let g in f)r.style[g]=f[g];n.appendChild(s),s.appendChild(a),s.appendChild(r),a.innerHTML='<div style="font-size: 24pt; margin: 1rem;">This immersive website requires access to your device motion sensors.</div>';const O=()=>{this.requestOrientationPermissions(),n.style.display="none"},M=document.createElement("button");M.addEventListener("click",O),M.style.width="50%",M.style.height="80%",M.style.fontSize="20pt",M.appendChild(document.createTextNode("OK")),r.appendChild(M),document.body.appendChild(n)}}on(t,i){this.eventEmitter.on(t,i)}init(){window.DeviceOrientationEvent===void 0?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",message:"Device orientation API not supported"}):window.isSecureContext===!1?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NO_HTTPS",message:"DeviceOrientationEvent is only available in secure contexts (https)"}):typeof window.DeviceOrientationEvent.requestPermission=="function"&&this.enablePermissionDialog?this.obtainPermissionGesture():this.eventEmitter.emit("deviceorientationgranted",{target:this})}}class R{constructor(t){this.raycaster=new v.Raycaster,this.normalisedMousePosition=new v.Vector2(null,null),t.domElement.addEventListener("click",i=>{this.normalisedMousePosition.set(i.clientX/t.domElement.clientWidth*2-1,-(i.clientY/t.domElement.clientHeight*2)+1)})}raycast(t,i){if(this.normalisedMousePosition.x!==null&&this.normalisedMousePosition.y!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,t);const e=this.raycaster.intersectObjects(i.children,!1);return this.normalisedMousePosition.set(null,null),e}return[]}}const S="0.1.0";d.ClickHandler=R,d.DeviceOrientationControls=L,d.EventEmitter=y,d.LocationBased=C,d.SphMercProjection=A,d.Webcam=_,d.version=S,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(h,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],c):(h=typeof globalThis<"u"?globalThis:h||self,c(h.locar={},h.THREE))})(this,(function(h,c){"use strict";function b(m){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(m){for(const i in m)if(i!=="default"){const e=Object.getOwnPropertyDescriptor(m,i);Object.defineProperty(t,i,e.get?e:{enumerable:!0,get:()=>m[i]})}}return t.default=m,Object.freeze(t)}const p=b(c);class A{constructor(){this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}project(t,i){return[this.#t(t),this.#l(i)]}unproject(t){return[this.#e(t[0]),this.#s(t[1])]}#t(t){return t/180*this.HALF_EARTH}#l(t){var i=Math.log(Math.tan((90+t)*Math.PI/360))/(Math.PI/180);return i*this.HALF_EARTH/180}#e(t){return t/this.HALF_EARTH*180}#s(t){var i=t/this.HALF_EARTH*180;return i=180/Math.PI*(2*Math.atan(Math.exp(i*Math.PI/180))-Math.PI/2),i}getID(){return"epsg:3857"}}class M{constructor(){this.eventHandlers={}}on(t,i){this.eventHandlers[t]=i}emit(t,...i){this.eventHandlers[t]?.call(this,...i)}}class w extends M{#t;#l;#e;#s;#c;#o;#i;#r;#a;#n;constructor(t,i,e={},l=null){super(),this.scene=t,this.camera=i,this.#t=new A,this.#e=null,this.#s=0,this.#c=100,this.#o=null,this.setGpsOptions(e),this.#i=null,this.#r=0,this.#a=0,this.#n=l}setProjection(t){this.#t=t}setGpsOptions(t={}){t.gpsMinDistance!==void 0&&(this.#s=t.gpsMinDistance),t.gpsMinAccuracy!==void 0&&(this.#c=t.gpsMinAccuracy)}async startGps(){if(this.#n){const i=await(await this.#n.sendData("/gps/start",{gpsMinDistance:this.#s,gpsMinAccuracy:this.#c})).json();this.#a=i.session}return this.#o===null?(this.#o=navigator.geolocation.watchPosition(t=>{this.#h(t)},t=>{this.emit("gpserror",t)},{enableHighAccuracy:!0}),!0):!1}stopGps(){return this.#o!==null?(navigator.geolocation.clearWatch(this.#o),this.#o=null,!0):!1}fakeGps(t,i,e=null,l=0){e!==null&&this.setElevation(e),this.#h({coords:{longitude:t,latitude:i,accuracy:l}})}lonLatToWorldCoords(t,i){const e=this.#t.project(t,i);if(this.#i)e[0]-=this.#i[0],e[1]-=this.#i[1];else throw"No initial position determined";return[e[0],-e[1]]}add(t,i,e,l,g={}){t.properties=g,this.#d(t,i,e,l),this.scene.add(t),this.#n?.sendData("/object/new",{position:t.position,x:t.position.x,z:t.position.z,session:this.#a,properties:g})}#d(t,i,e,l){const g=this.lonLatToWorldCoords(i,e);l!==void 0&&(t.position.y=l),[t.position.x,t.position.z]=g}setElevation(t){this.camera.position.y=t}#u(t,i){this.#i=this.#t.project(t,i)}#h(t){let i=Number.MAX_VALUE;this.#r++,this.#n?.sendData("/gps/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,acc:t.coords.accuracy,session:this.#a}),t.coords.accuracy<=this.#c&&(this.#e===null?this.#e={latitude:t.coords.latitude,longitude:t.coords.longitude}:i=this.#m(this.#e,t.coords),i>=this.#s&&(this.#e.longitude=t.coords.longitude,this.#e.latitude=t.coords.latitude,this.#i||(this.#u(t.coords.longitude,t.coords.latitude),this.#n?.sendData("/worldorigin/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,session:this.#a,initialPosition:this.#i})),this.#d(this.camera,t.coords.longitude,t.coords.latitude),this.#n?.sendData("/gps/accepted",{gpsCount:this.#r,cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:this.#a,distMoved:i}),this.emit("gpsupdate",{position:t,distMoved:i})))}#m(t,i){const e=p.MathUtils.degToRad(i.longitude-t.longitude),l=p.MathUtils.degToRad(i.latitude-t.latitude),g=Math.sin(l/2)*Math.sin(l/2)+Math.cos(p.MathUtils.degToRad(t.latitude))*Math.cos(p.MathUtils.degToRad(i.latitude))*(Math.sin(e/2)*Math.sin(e/2));return 2*Math.atan2(Math.sqrt(g),Math.sqrt(1-g))*6371e3}}class D extends M{#t;constructor(t={video:{facingMode:"environment"}},i){super(),this.sceneWebcam=new p.Scene,i?this.#t=document.querySelector(i):(this.#t=document.createElement("video"),this.#t.setAttribute("autoplay",!0),this.#t.setAttribute("playsinline",!0),this.#t.style.display="none",document.body.appendChild(this.#t)),this.texture=new p.VideoTexture(this.#t),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices.getUserMedia(t).then(e=>{this.#t.addEventListener("loadedmetadata",()=>{this.#t.setAttribute("width",this.#t.videoWidth),this.#t.setAttribute("height",this.#t.videoHeight),this.#t.play(),this.emit("webcamstarted",{texture:this.texture})}),this.#t.srcObject=e}).catch(e=>{this.emit("webcamerror",{code:e.name,message:e.message})}):this.emit("webcamerror",{code:"LOCAR_NO_MEDIA_DEVICES_API",message:"Media devices API not supported"})}dispose(){this.texture.dispose()}}const S="locar-device-orientation-permission-modal",T="locar-device-orientation-permission-button",N="locar-device-orientation-permission-message",R="locar-device-orientation-permission-inner",L="locar-device-orientation-permission-button-inner",P="This immersive website requires access to your device motion sensors.",I=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,x=new c.Vector3(0,0,1),_=new c.Euler,F=new c.Quaternion,U=new c.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5));class j extends c.EventDispatcher{constructor(t,i={}){super(),this.eventEmitter=new M;const e=this;this.object=t,this.object.rotation.reorder("YXZ"),this.enabled=!0,this.deviceOrientation=null,this.screenOrientation=0,this.alphaOffset=0,this.orientationOffset=0,this.initialOffset=null,this.lastCompassY=void 0,this.lastOrientation=null,this.TWO_PI=2*Math.PI,this.HALF_PI=.5*Math.PI,this.orientationChangeEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation",this.smoothingFactor=i.smoothingFactor||1,this.enablePermissionDialog=i.enablePermissionDialog??!0,this.enableInlineStyling=i.enableStyling??!0,this.preferConfirmDialog=i.preferConfirmDialog??!1;const l=function({alpha:n,beta:s,gamma:a,webkitCompassHeading:r}){if(I){const o=360-r;e.alphaOffset=c.MathUtils.degToRad(o-n),e.deviceOrientation={alpha:n,beta:s,gamma:a,webkitCompassHeading:r}}else n<0&&(n+=360),e.deviceOrientation={alpha:n,beta:s,gamma:a};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.rotation}}))},g=function(){e.screenOrientation=window.orientation||0,I&&(e.screenOrientation===90?e.orientationOffset=-e.HALF_PI:e.screenOrientation===-90?e.orientationOffset=e.HALF_PI:e.orientationOffset=0)},E=function(n,s,a,r,o){_.set(a,s,-r,"YXZ"),n.setFromEuler(_),n.multiply(U),n.multiply(F.setFromAxisAngle(x,-o))};this.connect=function(){g(),window.addEventListener("orientationchange",g),window.addEventListener(e.orientationChangeEventName,l),e.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",g),window.removeEventListener(e.orientationChangeEventName,l),e.enabled=!1,e.initialOffset=!1,e.deviceOrientation=null},this.requestOrientationPermissions=function(){window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(n=>{n==="granted"?this.eventEmitter.emit("deviceorientationgranted",{target:this}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_DENIED",message:"Permission for device orientation denied - AR will not work correctly"})}).catch(function(n){this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",message:"Permission request for device orientation failed - AR will not work correctly",error:JSON.stringify(n,null,2)})}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",message:"Internal error: no requestPermission() found although requestOrientationPermissions() was called - please raise an issue on GitHub"})},this.update=function({theta:n=0}={theta:0}){if(e.enabled===!1)return;const s=e.deviceOrientation;if(s){let a=s.alpha?c.MathUtils.degToRad(s.alpha)+e.alphaOffset:0,r=s.beta?c.MathUtils.degToRad(s.beta):0,o=s.gamma?c.MathUtils.degToRad(s.gamma):0;const v=e.screenOrientation?c.MathUtils.degToRad(e.screenOrientation):0;if(e.smoothingFactor<1){if(e.lastOrientation){const u=e.smoothingFactor;a=e._getSmoothedAngle(a,e.lastOrientation.alpha,u),r=e._getSmoothedAngle(r+Math.PI,e.lastOrientation.beta,u),o=e._getSmoothedAngle(o+e.HALF_PI,e.lastOrientation.gamma,u,Math.PI)}else r+=Math.PI,o+=e.HALF_PI;e.lastOrientation={alpha:a,beta:r,gamma:o}}if(I){const u=new c.Quaternion;E(u,a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,v);const f=new c.Euler().setFromQuaternion(u,"YXZ");let O=c.MathUtils.degToRad(360-s.webkitCompassHeading);e.smoothingFactor<1&&e.lastCompassY!==void 0&&(O=e._getSmoothedAngle(O,e.lastCompassY,e.smoothingFactor)),e.lastCompassY=O,f.y=O+(e.orientationOffset||0),u.setFromEuler(f),e.object.quaternion.copy(u)}else E(e.object.quaternion,I?a+e.alphaOffset:a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,v)}},this.getCorrectedHeading=function(){const{deviceOrientation:n}=e;if(!n)return 0;let s=0;return I?(s=360-n.webkitCompassHeading,e.orientationOffset&&(s+=e.orientationOffset*(180/Math.PI),s=(s+360)%360)):(n.absolute===!0||e.orientationChangeEventName,s=n.alpha?n.alpha:0,s=(360-s)%360,s<0&&(s+=360)),s},this._orderAngle=function(n,s,a=e.TWO_PI){return s>n&&Math.abs(s-n)<a/2||n>s&&Math.abs(s-n)>a/2?{left:n,right:s}:{left:s,right:n}},this._getSmoothedAngle=function(n,s,a,r=e.TWO_PI){const o=e._orderAngle(n,s,r),v=o.left,u=o.right;o.left=0,o.right-=v,o.right<0&&(o.right+=r);let f=u==s?(1-a)*o.right+a*o.left:a*o.right+(1-a)*o.left;return f+=v,f>=r&&(f-=r),f},this.updateAlphaOffset=function(){e.initialOffset=!1},this.dispose=function(){e.disconnect()},this.getAlpha=function(){const{deviceOrientation:n}=e;return n&&n.alpha?c.MathUtils.degToRad(n.alpha)+e.alphaOffset:0},this.getBeta=function(){const{deviceOrientation:n}=e;return n&&n.beta?c.MathUtils.degToRad(n.beta):0},this.getGamma=function(){const{deviceOrientation:n}=e;return n&&n.gamma?c.MathUtils.degToRad(n.gamma):0},this.createObtainPermissionGestureDialog=function(){const n=document.createElement("div");n.classList.add(S);const s=document.createElement("div");s.classList.add(R);const a=document.createElement("div");a.classList.add(N);const r=document.createElement("div");r.classList.add(L);const o=document.createElement("button");if(o.classList.add(T),document.body.appendChild(n),this.enableInlineStyling===!0){const u={fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",display:"flex",position:"fixed",zIndex:1e5,justifyContent:"center",alignItems:"center",backgroundColor:"rgba(0,0,0,0.2)",inset:0,padding:"20px"},f={backgroundColor:"rgba(220, 220, 220, 0.85)",padding:"6px 0",borderRadius:"10px",width:"100%",maxWidth:"400px"},O={padding:"10px 12px",textAlign:"center",fontWeight:400,fontSize:"13px",display:"flex",justifyContent:"center",alignItems:"center"},y={display:"block",textAlign:"center",textDecoration:"none",borderTop:"rgb(180,180,180) solid 1px"},C={display:"block",width:"100%",textAlign:"center",appearance:"none",background:"none",border:"none",outline:"none",padding:"10px",fontWeight:400,fontSize:"16px",color:"#2e7cf1",cursor:"pointer"};for(let d in u)n.style[d]=u[d];for(let d in f)s.style[d]=f[d];for(let d in O)a.style[d]=O[d];for(let d in y)r.style[d]=y[d];for(let d in C)o.style[d]=C[d]}n.appendChild(s),s.appendChild(a),s.appendChild(r),a.appendChild(document.createTextNode(P));const v=()=>{this.requestOrientationPermissions(),n.style.display="none"};o.addEventListener("click",v),o.appendChild(document.createTextNode("OK")),r.appendChild(o),document.body.appendChild(n)},this.obtainPermissionGesture=function(){this.preferConfirmDialog===!0?window.confirm(P)&&this.requestOrientationPermissions():this.createObtainPermissionGestureDialog()}}on(t,i){this.eventEmitter.on(t,i)}init(){window.DeviceOrientationEvent===void 0?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",message:"Device orientation API not supported"}):window.isSecureContext===!1?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NO_HTTPS",message:"DeviceOrientationEvent is only available in secure contexts (https)"}):typeof window.DeviceOrientationEvent.requestPermission=="function"&&this.enablePermissionDialog?this.obtainPermissionGesture():this.eventEmitter.emit("deviceorientationgranted",{target:this})}}class q{constructor(t){this.raycaster=new p.Raycaster,this.normalisedMousePosition=new p.Vector2(null,null),t.domElement.addEventListener("click",i=>{this.normalisedMousePosition.set(i.clientX/t.domElement.clientWidth*2-1,-(i.clientY/t.domElement.clientHeight*2)+1)})}raycast(t,i){if(this.normalisedMousePosition.x!==null&&this.normalisedMousePosition.y!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,t);const e=this.raycaster.intersectObjects(i.children,!1);return this.normalisedMousePosition.set(null,null),e}return[]}}const k="0.1.2";h.ClickHandler=q,h.DeviceOrientationControls=j,h.EventEmitter=M,h.LocationBased=w,h.SphMercProjection=A,h.Webcam=D,h.version=k,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
declare module "locar" {
|
|
2
|
+
import * as THREE from "three";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generic, small event emitter that only stores a single handler per event name.
|
|
6
|
+
*/
|
|
7
|
+
export class EventEmitter {
|
|
8
|
+
protected eventHandlers: Record<string, (...args: any[]) => void>;
|
|
9
|
+
on(eventName: string, handler: (...args: any[]) => void): void;
|
|
10
|
+
emit(eventName: string, ...params: any[]): void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Spherical Mercator (EPSG:3857) projection used by `LocationBased`.
|
|
15
|
+
*/
|
|
16
|
+
export class SphMercProjection {
|
|
17
|
+
constructor();
|
|
18
|
+
/**
|
|
19
|
+
* @returns [easting, northing]
|
|
20
|
+
*/
|
|
21
|
+
project(lon: number, lat: number): [number, number];
|
|
22
|
+
/**
|
|
23
|
+
* @returns [lon, lat]
|
|
24
|
+
*/
|
|
25
|
+
unproject(projected: [number, number]): [number, number];
|
|
26
|
+
getID(): "epsg:3857";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Options for GPS in `LocationBased`.
|
|
31
|
+
* Mirrored from `setGpsOptions(...)`
|
|
32
|
+
*/
|
|
33
|
+
export interface GpsOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Meters the device must move to accept the next GPS reading.
|
|
36
|
+
*/
|
|
37
|
+
gpsMinDistance?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Minimum accuracy in meters to accept a GPS reading.
|
|
40
|
+
*/
|
|
41
|
+
gpsMinAccuracy?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Optional logger object passed to `LocationBased` for debug.
|
|
46
|
+
*/
|
|
47
|
+
export interface ServerLogger {
|
|
48
|
+
/**
|
|
49
|
+
* Should send data to server. Implementation-specific in userland.
|
|
50
|
+
*/
|
|
51
|
+
sendData(endpoint: string, data: any): Promise<Response> | Response;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Main location AR class.
|
|
56
|
+
*/
|
|
57
|
+
export class LocationBased extends EventEmitter {
|
|
58
|
+
readonly scene: THREE.Scene;
|
|
59
|
+
readonly camera: THREE.Camera;
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
scene: THREE.Scene,
|
|
63
|
+
camera: THREE.Camera,
|
|
64
|
+
options?: GpsOptions,
|
|
65
|
+
serverLogger?: ServerLogger | null,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Set the projection (must have `project(lon,lat): [x,y]`).
|
|
70
|
+
*/
|
|
71
|
+
setProjection(proj: {
|
|
72
|
+
project(lon: number, lat: number): [number, number];
|
|
73
|
+
}): void;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Update GPS options at runtime.
|
|
77
|
+
*/
|
|
78
|
+
setGpsOptions(options?: GpsOptions): void;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Start real GPS (`navigator.geolocation.watchPosition`).
|
|
82
|
+
*/
|
|
83
|
+
startGps(): Promise<boolean> | boolean;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Stop real GPS.
|
|
87
|
+
*/
|
|
88
|
+
stopGps(): boolean;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Send a fake GPS position.
|
|
92
|
+
*/
|
|
93
|
+
fakeGps(lon: number, lat: number, elev?: number | null, acc?: number): void;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Convert lon/lat to world coordinates (needs initial position).
|
|
97
|
+
*/
|
|
98
|
+
lonLatToWorldCoords(lon: number, lat: number): [number, number];
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Add a THREE object at lon/lat/(elev) and put in the scene.
|
|
102
|
+
*/
|
|
103
|
+
add(
|
|
104
|
+
object: THREE.Object3D,
|
|
105
|
+
lon: number,
|
|
106
|
+
lat: number,
|
|
107
|
+
elev?: number,
|
|
108
|
+
properties?: Record<string, any>,
|
|
109
|
+
): void;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Set camera elevation (y).
|
|
113
|
+
*/
|
|
114
|
+
setElevation(elev: number): void;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Events:
|
|
118
|
+
* - "gpsupdate": `{ position: GeolocationPosition, distMoved: number }``
|
|
119
|
+
* - "gpserror": `GeolocationPositionError`
|
|
120
|
+
*/
|
|
121
|
+
on(
|
|
122
|
+
eventName: "gpsupdate",
|
|
123
|
+
handler: (data: {
|
|
124
|
+
position: GeolocationPosition;
|
|
125
|
+
distMoved: number;
|
|
126
|
+
}) => void,
|
|
127
|
+
): void;
|
|
128
|
+
on(
|
|
129
|
+
eventName: "gpserror",
|
|
130
|
+
handler: (error: GeolocationPositionError) => void,
|
|
131
|
+
): void;
|
|
132
|
+
on(eventName: string, handler: (...args: any[]) => void): void;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Small webcam wrapper that creates a hidden `<video>` and a `THREE.VideoTexture`.
|
|
137
|
+
*/
|
|
138
|
+
export interface WebcamStartedEvent {
|
|
139
|
+
texture: THREE.VideoTexture;
|
|
140
|
+
}
|
|
141
|
+
export interface WebcamErrorEvent {
|
|
142
|
+
code: string;
|
|
143
|
+
message: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export class Webcam extends EventEmitter {
|
|
147
|
+
/**
|
|
148
|
+
* @param constraints `MediaDevices.getUserMedia` constraints
|
|
149
|
+
* @param videoElementSelector selector for an existing `<video>`; if falsy, it creates one
|
|
150
|
+
*/
|
|
151
|
+
constructor(
|
|
152
|
+
constraints?: MediaStreamConstraints,
|
|
153
|
+
videoElementSelector?: string | null,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Texture that streams the camera feed.
|
|
158
|
+
*/
|
|
159
|
+
readonly texture: THREE.VideoTexture;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Free GPU resources.
|
|
163
|
+
*/
|
|
164
|
+
dispose(): void;
|
|
165
|
+
|
|
166
|
+
// events
|
|
167
|
+
on(
|
|
168
|
+
eventName: "webcamstarted",
|
|
169
|
+
handler: (e: WebcamStartedEvent) => void,
|
|
170
|
+
): void;
|
|
171
|
+
on(eventName: "webcamerror", handler: (e: WebcamErrorEvent) => void): void;
|
|
172
|
+
on(eventName: string, handler: (...args: any[]) => void): void;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Click handler/raycaster wrapper.
|
|
177
|
+
*/
|
|
178
|
+
export class ClickHandler {
|
|
179
|
+
constructor(renderer: THREE.WebGLRenderer);
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Cast a ray and return intersects with scene children.
|
|
183
|
+
*/
|
|
184
|
+
raycast(camera: THREE.Camera, scene: THREE.Scene): THREE.Intersection[];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface DeviceOrientationControlsOptions {
|
|
188
|
+
/**
|
|
189
|
+
* 0 < k <= 1. Lower = more smoothing.
|
|
190
|
+
*/
|
|
191
|
+
smoothingFactor?: number;
|
|
192
|
+
/**
|
|
193
|
+
* Show iOS permission dialog.
|
|
194
|
+
*/
|
|
195
|
+
enablePermissionDialog?: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Apply inline styles on the created dialog.
|
|
198
|
+
*/
|
|
199
|
+
enableStyling?: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Use `window.confirm(...)` instead of building DOM.
|
|
202
|
+
*/
|
|
203
|
+
preferConfirmDialog?: boolean;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface DeviceOrientationGrantedEvent {
|
|
207
|
+
target: DeviceOrientationControls;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface DeviceOrientationErrorEvent {
|
|
211
|
+
code: string;
|
|
212
|
+
message: string;
|
|
213
|
+
error?: string;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export class DeviceOrientationControls extends THREE.EventDispatcher {
|
|
217
|
+
/**
|
|
218
|
+
* @param object usually a `THREE.Camera`
|
|
219
|
+
*/
|
|
220
|
+
constructor(
|
|
221
|
+
object: THREE.Object3D,
|
|
222
|
+
options?: DeviceOrientationControlsOptions,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Initialize the device orientation controls. Should be called first to set up permission flow.
|
|
227
|
+
*/
|
|
228
|
+
init(): void;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Begin listening to orientation + screenorientation (must be called after permission is granted on iOS).
|
|
232
|
+
*/
|
|
233
|
+
connect(): void;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Stop listening.
|
|
237
|
+
*/
|
|
238
|
+
disconnect(): void;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* iOS: must be called in a user gesture to ask for perms.
|
|
242
|
+
*/
|
|
243
|
+
requestOrientationPermissions(): void;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Create the DOM dialog (iOS-style) and wire it to requestOrientationPermissions.
|
|
247
|
+
*/
|
|
248
|
+
createObtainPermissionGestureDialog(): void;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Choose `confirm()` vs DOM dialog.
|
|
252
|
+
*/
|
|
253
|
+
obtainPermissionGesture(): void;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Call each frame.
|
|
257
|
+
*/
|
|
258
|
+
update(args?: { theta?: number }): void;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* iOS heading correction.
|
|
262
|
+
*/
|
|
263
|
+
getCorrectedHeading(): number;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Provided in AR.js fix — forces re-evaluation of alpha offset.
|
|
267
|
+
*/
|
|
268
|
+
updateAlphaOffset(): void;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Getters (radians).
|
|
272
|
+
*/
|
|
273
|
+
getAlpha(): number;
|
|
274
|
+
getBeta(): number;
|
|
275
|
+
getGamma(): number;
|
|
276
|
+
|
|
277
|
+
dispose(): void;
|
|
278
|
+
|
|
279
|
+
on(
|
|
280
|
+
eventName: "deviceorientationgranted",
|
|
281
|
+
handler: (ev: DeviceOrientationGrantedEvent) => void,
|
|
282
|
+
): void;
|
|
283
|
+
on(
|
|
284
|
+
eventName: "deviceorientationerror",
|
|
285
|
+
handler: (ev: DeviceOrientationErrorEvent) => void,
|
|
286
|
+
): void;
|
|
287
|
+
on(eventName: string, handler: (...args: any[]) => void): void;
|
|
288
|
+
|
|
289
|
+
enabled: boolean;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Version string.
|
|
294
|
+
*/
|
|
295
|
+
export const version: string;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Default export from build (UMD/ES) is the namespace with all the above.
|
|
299
|
+
* Modeled so that:
|
|
300
|
+
* `import * as LocAR from "locar"`
|
|
301
|
+
* and
|
|
302
|
+
* `import LocAR from "locar"``
|
|
303
|
+
* both typecheck.
|
|
304
|
+
*/
|
|
305
|
+
const LocAR: {
|
|
306
|
+
EventEmitter: typeof EventEmitter;
|
|
307
|
+
SphMercProjection: typeof SphMercProjection;
|
|
308
|
+
LocationBased: typeof LocationBased;
|
|
309
|
+
Webcam: typeof Webcam;
|
|
310
|
+
ClickHandler: typeof ClickHandler;
|
|
311
|
+
DeviceOrientationControls: typeof DeviceOrientationControls;
|
|
312
|
+
version: string;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
export default LocAR;
|
|
316
|
+
}
|
package/package.json
CHANGED
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "locar",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Location-based AR from AR.js.",
|
|
5
5
|
"files": [
|
|
6
|
-
"dist"
|
|
6
|
+
"dist",
|
|
7
|
+
"lib/types"
|
|
7
8
|
],
|
|
8
9
|
"main": "./dist/locar.umd.js",
|
|
9
10
|
"module": "./dist/locar.es.js",
|
|
11
|
+
"types": "lib/types/locar.d.ts",
|
|
10
12
|
"exports": {
|
|
11
13
|
".": {
|
|
14
|
+
"types": "./lib/types/locar.d.ts",
|
|
12
15
|
"import": "./dist/locar.es.js",
|
|
13
16
|
"require": "./dist/locar.umd.js"
|
|
14
17
|
}
|
|
15
18
|
},
|
|
16
19
|
"scripts": {
|
|
17
|
-
"
|
|
20
|
+
"lint": "eslint ./lib --ext .js,.json,.css",
|
|
21
|
+
"lint:fix": "prettier --write ./lib && eslint ./lib --ext .js,.json,.css --fix",
|
|
22
|
+
"build": "npm run lint && vite build && npm pack",
|
|
18
23
|
"makedocs": "jsdoc -r lib -d docs/api"
|
|
19
24
|
},
|
|
20
25
|
"devDependencies": {
|
|
26
|
+
"@eslint/css": "^0.11.0",
|
|
27
|
+
"@eslint/js": "^9.35.0",
|
|
28
|
+
"@eslint/json": "^0.13.2",
|
|
29
|
+
"eslint": "^9.38.0",
|
|
30
|
+
"globals": "^16.4.0",
|
|
21
31
|
"jsdoc": "^4.0.4",
|
|
32
|
+
"prettier-eslint": "^16.4.2",
|
|
22
33
|
"vite": "^7.1.3"
|
|
23
34
|
},
|
|
24
35
|
"dependencies": {
|