locar 0.1.7 → 0.1.8
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 +1 -1
- package/dist/locar.d.ts +3 -13
- package/dist/locar.es.js +141 -185
- package/dist/locar.umd.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/locar.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventDispatcher } from 'three';
|
|
2
2
|
import { Object3D } from 'three';
|
|
3
|
+
import { Quaternion } from 'three';
|
|
3
4
|
import * as THREE from 'three';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -41,12 +42,7 @@ export declare class DeviceOrientationControls extends EventDispatcher {
|
|
|
41
42
|
alphaOffset: number;
|
|
42
43
|
orientationOffset: number;
|
|
43
44
|
initialOffset: boolean | null;
|
|
44
|
-
|
|
45
|
-
lastOrientation: {
|
|
46
|
-
alpha: number;
|
|
47
|
-
beta: number;
|
|
48
|
-
gamma: number;
|
|
49
|
-
} | null;
|
|
45
|
+
lastQuaternion: Quaternion | null;
|
|
50
46
|
orientationChangeEventName: "deviceorientation" | "deviceorientationabsolute";
|
|
51
47
|
smoothingFactor: number;
|
|
52
48
|
enablePermissionDialog: boolean;
|
|
@@ -58,12 +54,6 @@ export declare class DeviceOrientationControls extends EventDispatcher {
|
|
|
58
54
|
requestOrientationPermissions: () => void;
|
|
59
55
|
update: () => void;
|
|
60
56
|
getCorrectedHeading: () => number;
|
|
61
|
-
_calcAngleWithThreshold: (a: number, b: number) => number;
|
|
62
|
-
_orderAngle: (a: number, b: number, range?: number) => {
|
|
63
|
-
left: number;
|
|
64
|
-
right: number;
|
|
65
|
-
};
|
|
66
|
-
_getSmoothedAngle: (a: number, b: number, k: number, range?: number) => number;
|
|
67
57
|
updateAlphaOffset: () => void;
|
|
68
58
|
dispose: () => void;
|
|
69
59
|
getAlpha: () => number;
|
|
@@ -251,7 +241,7 @@ export declare class SphMercProjection {
|
|
|
251
241
|
getID: () => string;
|
|
252
242
|
}
|
|
253
243
|
|
|
254
|
-
export declare const version = "0.1.
|
|
244
|
+
export declare const version = "0.1.8";
|
|
255
245
|
|
|
256
246
|
/** Class to setup the webcam. */
|
|
257
247
|
export declare class Webcam extends EventEmitter {
|
package/dist/locar.es.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { Vector3 as T, Euler as
|
|
3
|
-
class
|
|
1
|
+
import * as u from "three";
|
|
2
|
+
import { Vector3 as T, Euler as w, Quaternion as f, EventDispatcher as A, MathUtils as h } from "three";
|
|
3
|
+
class R {
|
|
4
4
|
/**
|
|
5
5
|
* Create a SphMercProjection.
|
|
6
6
|
*/
|
|
7
7
|
constructor() {
|
|
8
|
-
this.project = (e,
|
|
9
|
-
var
|
|
10
|
-
return
|
|
8
|
+
this.project = (e, i) => [this.#e(e), this.#t(i)], this.unproject = (e) => [this.#i(e[0]), this.#s(e[1])], this.#e = (e) => e / 180 * this.HALF_EARTH, this.#t = (e) => {
|
|
9
|
+
var i = Math.log(Math.tan((90 + e) * Math.PI / 360)) / (Math.PI / 180);
|
|
10
|
+
return i * this.HALF_EARTH / 180;
|
|
11
11
|
}, this.#i = (e) => e / this.HALF_EARTH * 180, this.#s = (e) => {
|
|
12
|
-
var
|
|
13
|
-
return
|
|
12
|
+
var i = e / this.HALF_EARTH * 180;
|
|
13
|
+
return i = 180 / Math.PI * (2 * Math.atan(Math.exp(i * Math.PI / 180)) - Math.PI / 2), i;
|
|
14
14
|
}, this.getID = () => "epsg:3857", this.EARTH = 4007501668e-2, this.HALF_EARTH = 2003750834e-2;
|
|
15
15
|
}
|
|
16
|
-
#t;
|
|
17
16
|
#e;
|
|
17
|
+
#t;
|
|
18
18
|
#i;
|
|
19
19
|
#s;
|
|
20
20
|
}
|
|
21
|
-
class
|
|
21
|
+
class E {
|
|
22
22
|
constructor() {
|
|
23
|
-
this.on = (e,
|
|
24
|
-
this.eventHandlers[e] === void 0 && (this.eventHandlers[e] = []), this.eventHandlers[e].push(
|
|
25
|
-
}, this.emit = (e, ...
|
|
23
|
+
this.on = (e, i) => {
|
|
24
|
+
this.eventHandlers[e] === void 0 && (this.eventHandlers[e] = []), this.eventHandlers[e].push(i);
|
|
25
|
+
}, this.emit = (e, ...i) => {
|
|
26
26
|
this.eventHandlers[e]?.forEach((t) => {
|
|
27
|
-
t(...
|
|
27
|
+
t(...i);
|
|
28
28
|
});
|
|
29
|
-
}, this.off = (e,
|
|
29
|
+
}, this.off = (e, i) => {
|
|
30
30
|
if (this.eventHandlers[e]) {
|
|
31
|
-
const t = this.eventHandlers[e].indexOf(
|
|
31
|
+
const t = this.eventHandlers[e].indexOf(i);
|
|
32
32
|
t > -1 && this.eventHandlers[e].splice(t, 1);
|
|
33
33
|
}
|
|
34
34
|
}, this.eventHandlers = {};
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
class
|
|
38
|
-
#t;
|
|
37
|
+
class H extends E {
|
|
39
38
|
#e;
|
|
39
|
+
#t;
|
|
40
40
|
#i;
|
|
41
41
|
#s;
|
|
42
42
|
#a;
|
|
@@ -52,8 +52,8 @@ class F extends y {
|
|
|
52
52
|
* setGpsOptions() below.
|
|
53
53
|
* @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.
|
|
54
54
|
*/
|
|
55
|
-
constructor(e,
|
|
56
|
-
super(), this.scene = e, this.camera =
|
|
55
|
+
constructor(e, i, t = {}, l = null) {
|
|
56
|
+
super(), this.scene = e, this.camera = i, this.#e = new R(), this.#t = null, this.#i = 0, this.#s = 100, this.#a = null, this.setGpsOptions(t), this.#n = null, this.#l = 0, this.#r = 0, this.#o = l;
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Set the projection to use.
|
|
@@ -62,7 +62,7 @@ class F extends y {
|
|
|
62
62
|
* containing easting and northing.
|
|
63
63
|
*/
|
|
64
64
|
setProjection(e) {
|
|
65
|
-
this.#
|
|
65
|
+
this.#e = e;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* Set the GPS options.
|
|
@@ -81,15 +81,15 @@ class F extends y {
|
|
|
81
81
|
*/
|
|
82
82
|
async startGps() {
|
|
83
83
|
if (this.#o) {
|
|
84
|
-
const
|
|
84
|
+
const i = await (await this.#o.sendData("/gps/start", {
|
|
85
85
|
gpsMinDistance: this.#i,
|
|
86
86
|
gpsMinAccuracy: this.#s
|
|
87
87
|
})).json();
|
|
88
|
-
this.#r =
|
|
88
|
+
this.#r = i.session;
|
|
89
89
|
}
|
|
90
90
|
return this.#a === null ? (this.#a = navigator.geolocation.watchPosition(
|
|
91
91
|
(e) => {
|
|
92
|
-
this.#
|
|
92
|
+
this.#d(e);
|
|
93
93
|
},
|
|
94
94
|
(e) => {
|
|
95
95
|
this.emit("gpserror", e);
|
|
@@ -116,12 +116,12 @@ class F extends y {
|
|
|
116
116
|
* @param {number} acc - The accuracy of the GPS reading in metres. May be
|
|
117
117
|
* ignored if lower than the specified minimum accuracy.
|
|
118
118
|
*/
|
|
119
|
-
fakeGps(e,
|
|
120
|
-
t !== null && this.setElevation(t), this.#
|
|
119
|
+
fakeGps(e, i, t = null, l = 0) {
|
|
120
|
+
t !== null && this.setElevation(t), this.#d({
|
|
121
121
|
coords: {
|
|
122
122
|
longitude: e,
|
|
123
|
-
latitude:
|
|
124
|
-
accuracy:
|
|
123
|
+
latitude: i,
|
|
124
|
+
accuracy: l
|
|
125
125
|
}
|
|
126
126
|
});
|
|
127
127
|
}
|
|
@@ -136,8 +136,8 @@ class F extends y {
|
|
|
136
136
|
* @param {number} lat - The latitude.
|
|
137
137
|
* @return {Array} a two member array containing the WebGL x and z coordinates
|
|
138
138
|
*/
|
|
139
|
-
lonLatToWorldCoords(e,
|
|
140
|
-
const t = this.#
|
|
139
|
+
lonLatToWorldCoords(e, i) {
|
|
140
|
+
const t = this.#e.project(e, i);
|
|
141
141
|
if (this.#n)
|
|
142
142
|
t[0] -= this.#n[0], t[1] -= this.#n[1];
|
|
143
143
|
else
|
|
@@ -154,18 +154,18 @@ class F extends y {
|
|
|
154
154
|
* @param {Object} properties - properties describing the object (for example,
|
|
155
155
|
* the contents of the GeoJSON properties field).
|
|
156
156
|
*/
|
|
157
|
-
add(e,
|
|
158
|
-
e.properties =
|
|
157
|
+
add(e, i, t, l, c = {}) {
|
|
158
|
+
e.properties = c, this.#c(e, i, t, l), this.scene.add(e), this.#o?.sendData("/object/new", {
|
|
159
159
|
position: e.position,
|
|
160
160
|
x: e.position.x,
|
|
161
161
|
z: e.position.z,
|
|
162
162
|
session: this.#r,
|
|
163
|
-
properties:
|
|
163
|
+
properties: c
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
-
#c(e,
|
|
167
|
-
const
|
|
168
|
-
|
|
166
|
+
#c(e, i, t, l) {
|
|
167
|
+
const c = this.lonLatToWorldCoords(i, t);
|
|
168
|
+
l !== void 0 && (e.position.y = l), [e.position.x, e.position.z] = c;
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
171
|
* Set the elevation (y coordinate) of the camera.
|
|
@@ -174,21 +174,21 @@ class F extends y {
|
|
|
174
174
|
setElevation(e) {
|
|
175
175
|
this.camera.position.y = e;
|
|
176
176
|
}
|
|
177
|
-
#
|
|
178
|
-
this.#n = this.#
|
|
177
|
+
#h(e, i) {
|
|
178
|
+
this.#n = this.#e.project(e, i);
|
|
179
179
|
}
|
|
180
|
-
#
|
|
181
|
-
let
|
|
180
|
+
#d(e) {
|
|
181
|
+
let i = Number.MAX_VALUE;
|
|
182
182
|
this.#l++, this.#o?.sendData("/gps/new", {
|
|
183
183
|
gpsCount: this.#l,
|
|
184
184
|
lat: e.coords.latitude,
|
|
185
185
|
lon: e.coords.longitude,
|
|
186
186
|
acc: e.coords.accuracy,
|
|
187
187
|
session: this.#r
|
|
188
|
-
}), e.coords.accuracy <= this.#s && (this.#
|
|
188
|
+
}), e.coords.accuracy <= this.#s && (this.#t === null ? this.#t = {
|
|
189
189
|
latitude: e.coords.latitude,
|
|
190
190
|
longitude: e.coords.longitude
|
|
191
|
-
} :
|
|
191
|
+
} : i = this.#u(this.#t, e.coords), i >= this.#i && (this.#t.longitude = e.coords.longitude, this.#t.latitude = e.coords.latitude, this.#n || (this.#h(
|
|
192
192
|
e.coords.longitude,
|
|
193
193
|
e.coords.latitude
|
|
194
194
|
), this.#o?.sendData("/worldorigin/new", {
|
|
@@ -206,17 +206,17 @@ class F extends y {
|
|
|
206
206
|
cameraX: this.camera.position.x,
|
|
207
207
|
cameraZ: this.camera.position.z,
|
|
208
208
|
session: this.#r,
|
|
209
|
-
distMoved:
|
|
210
|
-
}), this.emit("gpsupdate", { position: e, distMoved:
|
|
209
|
+
distMoved: i
|
|
210
|
+
}), this.emit("gpsupdate", { position: e, distMoved: i })));
|
|
211
211
|
}
|
|
212
212
|
/**
|
|
213
213
|
* Calculate haversine distance between two lat/lon pairs.
|
|
214
214
|
*
|
|
215
215
|
* Taken from original A-Frame AR.js location-based components
|
|
216
216
|
*/
|
|
217
|
-
#u(e,
|
|
218
|
-
const t =
|
|
219
|
-
return 2 * Math.atan2(Math.sqrt(
|
|
217
|
+
#u(e, i) {
|
|
218
|
+
const t = u.MathUtils.degToRad(i.longitude - e.longitude), l = u.MathUtils.degToRad(i.latitude - e.latitude), c = Math.sin(l / 2) * Math.sin(l / 2) + Math.cos(u.MathUtils.degToRad(e.latitude)) * Math.cos(u.MathUtils.degToRad(i.latitude)) * (Math.sin(t / 2) * Math.sin(t / 2));
|
|
219
|
+
return 2 * Math.atan2(Math.sqrt(c), Math.sqrt(1 - c)) * 6371e3;
|
|
220
220
|
}
|
|
221
221
|
/**
|
|
222
222
|
* Obtain the last known GPS location.
|
|
@@ -224,11 +224,11 @@ class F extends y {
|
|
|
224
224
|
* @return {Object} object containing latitude and longitude fields, or null if no previous GPS location.
|
|
225
225
|
*/
|
|
226
226
|
getLastKnownLocation() {
|
|
227
|
-
return this.#
|
|
227
|
+
return this.#t;
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
|
-
const
|
|
231
|
-
class q extends
|
|
230
|
+
const b = "locar-device-orientation-permission-modal", M = "locar-device-orientation-permission-button", _ = "locar-device-orientation-permission-message", D = "locar-device-orientation-permission-inner", P = "locar-device-orientation-permission-button-inner", O = "This immersive website requires access to your device motion sensors.", p = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, N = new T(0, 0, 1), y = new w(), S = new f(), x = new f(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)), I = 0.5 * Math.PI;
|
|
231
|
+
class q extends A {
|
|
232
232
|
/**
|
|
233
233
|
* Create an instance of DeviceOrientationControls.
|
|
234
234
|
* @param {Object} object - the object to attach the controls to
|
|
@@ -236,7 +236,7 @@ class q extends _ {
|
|
|
236
236
|
* @param {Object} options - options for DeviceOrientationControls:
|
|
237
237
|
* currently accepts smoothingFactor (< 1), enablePermissionDialog, orientationChangeThreshold (radians)
|
|
238
238
|
*/
|
|
239
|
-
constructor(e,
|
|
239
|
+
constructor(e, i = {}) {
|
|
240
240
|
super(), this.init = () => {
|
|
241
241
|
window.DeviceOrientationEvent === void 0 ? this.eventEmitter.emit("deviceorientationerror", {
|
|
242
242
|
code: "LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",
|
|
@@ -245,14 +245,14 @@ class q extends _ {
|
|
|
245
245
|
code: "LOCAR_DEVICE_ORIENTATION_NO_HTTPS",
|
|
246
246
|
message: "DeviceOrientationEvent is only available in secure contexts (https)"
|
|
247
247
|
}) : typeof window.DeviceOrientationEvent.requestPermission == "function" && this.enablePermissionDialog ? this.obtainPermissionGesture() : this.eventEmitter.emit("deviceorientationgranted", { target: this });
|
|
248
|
-
}, this.eventEmitter = new
|
|
248
|
+
}, this.eventEmitter = new E();
|
|
249
249
|
const t = this;
|
|
250
|
-
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.
|
|
251
|
-
const
|
|
252
|
-
let { alpha: n, beta: a, gamma: r, webkitCompassHeading: o } =
|
|
250
|
+
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 || 1, this.enablePermissionDialog = i.enablePermissionDialog ?? !0, this.enableInlineStyling = i.enableStyling ?? !0, this.preferConfirmDialog = i.preferConfirmDialog ?? !1, this.orientationChangeThreshold = i.orientationChangeThreshold ?? 0;
|
|
251
|
+
const l = (s) => {
|
|
252
|
+
let { alpha: n, beta: a, gamma: r, webkitCompassHeading: o } = s;
|
|
253
253
|
if (n = n ?? 0, a = a ?? 0, r = r ?? 0, o = o ?? 0, p) {
|
|
254
|
-
const
|
|
255
|
-
t.alphaOffset =
|
|
254
|
+
const d = 360 - o;
|
|
255
|
+
t.alphaOffset = h.degToRad(d - n), t.deviceOrientation = { alpha: n, beta: a, gamma: r, webkitCompassHeading: o };
|
|
256
256
|
} else
|
|
257
257
|
n < 0 && (n += 360), t.deviceOrientation = { alpha: n, beta: a, gamma: r };
|
|
258
258
|
window.dispatchEvent(
|
|
@@ -260,40 +260,40 @@ class q extends _ {
|
|
|
260
260
|
detail: { cameraRotation: e.rotation }
|
|
261
261
|
})
|
|
262
262
|
);
|
|
263
|
-
},
|
|
264
|
-
t.screenOrientation = window.screen.orientation?.angle ?? 0, p && (t.screenOrientation === 90 ? t.orientationOffset = -
|
|
265
|
-
},
|
|
266
|
-
|
|
263
|
+
}, c = () => {
|
|
264
|
+
t.screenOrientation = window.screen.orientation?.angle ?? 0, p && (t.screenOrientation === 90 ? t.orientationOffset = -I : t.screenOrientation === -90 ? t.orientationOffset = I : t.orientationOffset = 0);
|
|
265
|
+
}, v = (s, n, a, r, o) => {
|
|
266
|
+
y.set(a, n, -r, "YXZ"), s.setFromEuler(y), s.multiply(x), s.multiply(S.setFromAxisAngle(N, -o));
|
|
267
267
|
};
|
|
268
268
|
this.connect = () => {
|
|
269
|
-
|
|
269
|
+
c(), window.addEventListener(
|
|
270
270
|
"orientationchange",
|
|
271
|
-
|
|
271
|
+
c
|
|
272
272
|
), window.addEventListener(
|
|
273
273
|
t.orientationChangeEventName,
|
|
274
|
-
|
|
274
|
+
l
|
|
275
275
|
), t.enabled = !0;
|
|
276
276
|
}, this.disconnect = () => {
|
|
277
277
|
window.removeEventListener(
|
|
278
278
|
"orientationchange",
|
|
279
|
-
|
|
279
|
+
c
|
|
280
280
|
), window.removeEventListener(
|
|
281
281
|
t.orientationChangeEventName,
|
|
282
|
-
|
|
283
|
-
), t.enabled = !1, t.initialOffset = !1, t.deviceOrientation = null;
|
|
282
|
+
l
|
|
283
|
+
), t.enabled = !1, t.initialOffset = !1, t.deviceOrientation = null, t.lastQuaternion = null;
|
|
284
284
|
}, this.requestOrientationPermissions = () => {
|
|
285
|
-
window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then((
|
|
286
|
-
|
|
285
|
+
window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then((s) => {
|
|
286
|
+
s === "granted" ? this.eventEmitter.emit("deviceorientationgranted", {
|
|
287
287
|
target: this
|
|
288
288
|
}) : this.eventEmitter.emit("deviceorientationerror", {
|
|
289
289
|
code: "LOCAR_DEVICE_ORIENTATION_PERMISSION_DENIED",
|
|
290
290
|
message: "Permission for device orientation denied - AR will not work correctly"
|
|
291
291
|
});
|
|
292
|
-
}).catch((
|
|
292
|
+
}).catch((s) => {
|
|
293
293
|
this.eventEmitter.emit("deviceorientationerror", {
|
|
294
294
|
code: "LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",
|
|
295
295
|
message: "Permission request for device orientation failed - AR will not work correctly",
|
|
296
|
-
error: JSON.stringify(
|
|
296
|
+
error: JSON.stringify(s, null, 2)
|
|
297
297
|
});
|
|
298
298
|
}) : this.eventEmitter.emit("deviceorientationerror", {
|
|
299
299
|
code: "LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",
|
|
@@ -301,132 +301,88 @@ class q extends _ {
|
|
|
301
301
|
});
|
|
302
302
|
}, this.update = () => {
|
|
303
303
|
if (t.enabled === !1) return;
|
|
304
|
-
const
|
|
305
|
-
if (
|
|
306
|
-
let n =
|
|
307
|
-
const o = t.screenOrientation ?
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
n,
|
|
313
|
-
t.lastOrientation.alpha,
|
|
314
|
-
l
|
|
315
|
-
), a = t._getSmoothedAngle(
|
|
316
|
-
a + Math.PI,
|
|
317
|
-
t.lastOrientation.beta,
|
|
318
|
-
l
|
|
319
|
-
), r = t._getSmoothedAngle(
|
|
320
|
-
r + f,
|
|
321
|
-
t.lastOrientation.gamma,
|
|
322
|
-
l,
|
|
323
|
-
Math.PI
|
|
324
|
-
);
|
|
325
|
-
} else
|
|
326
|
-
a += Math.PI, r += f;
|
|
327
|
-
if (t.lastOrientation && (n = t._calcAngleWithThreshold(
|
|
328
|
-
n,
|
|
329
|
-
t.lastOrientation.alpha
|
|
330
|
-
), a = t._calcAngleWithThreshold(
|
|
331
|
-
a,
|
|
332
|
-
t.lastOrientation.beta
|
|
333
|
-
), r = t._calcAngleWithThreshold(
|
|
334
|
-
r,
|
|
335
|
-
t.lastOrientation.gamma
|
|
336
|
-
)), p) {
|
|
337
|
-
const l = new E();
|
|
338
|
-
O(
|
|
339
|
-
l,
|
|
340
|
-
n,
|
|
341
|
-
t.smoothingFactor < 1 ? a - Math.PI : a,
|
|
342
|
-
t.smoothingFactor < 1 ? r - Math.PI / 2 : r,
|
|
343
|
-
o
|
|
344
|
-
);
|
|
345
|
-
const v = new C().setFromQuaternion(
|
|
346
|
-
l,
|
|
304
|
+
const s = t.deviceOrientation;
|
|
305
|
+
if (s) {
|
|
306
|
+
let n = s.alpha ? h.degToRad(s.alpha) + t.alphaOffset : 0, a = s.beta ? h.degToRad(s.beta) : 0, r = s.gamma ? h.degToRad(s.gamma) : 0;
|
|
307
|
+
const o = t.screenOrientation ? h.degToRad(t.screenOrientation) : 0, d = new f();
|
|
308
|
+
if (p) {
|
|
309
|
+
v(d, n, a, r, o);
|
|
310
|
+
const g = new w().setFromQuaternion(
|
|
311
|
+
d,
|
|
347
312
|
"YXZ"
|
|
313
|
+
), C = h.degToRad(
|
|
314
|
+
360 - (s.webkitCompassHeading ?? 0)
|
|
348
315
|
);
|
|
349
|
-
|
|
350
|
-
360 - (i.webkitCompassHeading ?? 0)
|
|
351
|
-
);
|
|
352
|
-
t.smoothingFactor < 1 && t.lastCompassY !== void 0 && (d = t._getSmoothedAngle(
|
|
353
|
-
d,
|
|
354
|
-
t.lastCompassY,
|
|
355
|
-
t.smoothingFactor
|
|
356
|
-
)), t.lastCompassY = d, v.y = d + (t.orientationOffset || 0), l.setFromEuler(v), t.object.quaternion.copy(l);
|
|
316
|
+
g.y = C + (t.orientationOffset || 0), d.setFromEuler(g);
|
|
357
317
|
} else
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
318
|
+
v(d, n, a, r, o);
|
|
319
|
+
if (t.lastQuaternion && t.orientationChangeThreshold > 0 && d.angleTo(t.lastQuaternion) < t.orientationChangeThreshold)
|
|
320
|
+
return;
|
|
321
|
+
if (t.smoothingFactor < 1 && t.lastQuaternion) {
|
|
322
|
+
const g = 1 - t.smoothingFactor;
|
|
323
|
+
t.object.quaternion.slerp(d, g);
|
|
324
|
+
} else
|
|
325
|
+
t.object.quaternion.copy(d);
|
|
326
|
+
t.lastQuaternion = t.object.quaternion.clone(), window.dispatchEvent(
|
|
327
|
+
new CustomEvent("camera-rotation-change", {
|
|
328
|
+
detail: { cameraRotation: t.object.rotation }
|
|
329
|
+
})
|
|
330
|
+
);
|
|
370
331
|
}
|
|
371
332
|
}, this.getCorrectedHeading = () => {
|
|
372
|
-
const { deviceOrientation:
|
|
373
|
-
if (!
|
|
333
|
+
const { deviceOrientation: s } = t;
|
|
334
|
+
if (!s) return 0;
|
|
374
335
|
let n = 0;
|
|
375
|
-
return p ? (n = 360 - (
|
|
376
|
-
}, this._calcAngleWithThreshold = (i, n) => Math.abs(i - n) < t.orientationChangeThreshold ? n : i, this._orderAngle = (i, n, a = M) => n > i && Math.abs(n - i) < a / 2 || i > n && Math.abs(n - i) > a / 2 ? { left: i, right: n } : { left: n, right: i }, this._getSmoothedAngle = (i, n, a, r = M) => {
|
|
377
|
-
const o = t._orderAngle(i, n, r), l = o.left, v = o.right;
|
|
378
|
-
o.left = 0, o.right -= l, o.right < 0 && (o.right += r);
|
|
379
|
-
let d = v == n ? (1 - a) * o.right + a * o.left : a * o.right + (1 - a) * o.left;
|
|
380
|
-
return d += l, d >= r && (d -= r), d;
|
|
336
|
+
return p ? (n = 360 - (s.webkitCompassHeading ?? 0), t.orientationOffset && (n += t.orientationOffset * (180 / Math.PI), n = (n + 360) % 360)) : (s.absolute === !0 || t.orientationChangeEventName, n = s.alpha ? s.alpha : 0, n = (360 - n) % 360, n < 0 && (n += 360)), n;
|
|
381
337
|
}, this.updateAlphaOffset = () => {
|
|
382
338
|
t.initialOffset = !1;
|
|
383
339
|
}, this.dispose = () => {
|
|
384
340
|
t.disconnect();
|
|
385
341
|
}, this.getAlpha = () => {
|
|
386
|
-
const { deviceOrientation:
|
|
387
|
-
return
|
|
342
|
+
const { deviceOrientation: s } = t;
|
|
343
|
+
return s && s.alpha ? h.degToRad(s.alpha) + t.alphaOffset : 0;
|
|
388
344
|
}, this.getBeta = () => {
|
|
389
|
-
const { deviceOrientation:
|
|
390
|
-
return
|
|
345
|
+
const { deviceOrientation: s } = t;
|
|
346
|
+
return s && s.beta ? h.degToRad(s.beta) : 0;
|
|
391
347
|
}, this.getGamma = () => {
|
|
392
|
-
const { deviceOrientation:
|
|
393
|
-
return
|
|
348
|
+
const { deviceOrientation: s } = t;
|
|
349
|
+
return s && s.gamma ? h.degToRad(s.gamma) : 0;
|
|
394
350
|
}, this.createObtainPermissionGestureDialog = () => {
|
|
395
|
-
const
|
|
396
|
-
|
|
351
|
+
const s = document.createElement("div");
|
|
352
|
+
s.classList.add(b);
|
|
397
353
|
const n = document.createElement("div");
|
|
398
354
|
n.classList.add(D);
|
|
399
355
|
const a = document.createElement("div");
|
|
400
|
-
a.classList.add(
|
|
356
|
+
a.classList.add(_);
|
|
401
357
|
const r = document.createElement("div");
|
|
402
|
-
r.classList.add(
|
|
358
|
+
r.classList.add(P);
|
|
403
359
|
const o = document.createElement("button");
|
|
404
|
-
o.classList.add(
|
|
405
|
-
document.createTextNode(
|
|
360
|
+
o.classList.add(M), document.body.appendChild(s), this.enableInlineStyling === !0 && (s.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", s.style.display = "flex", s.style.position = "fixed", s.style.zIndex = "100000", s.style.justifyContent = "center", s.style.alignItems = "center", s.style.backgroundColor = "rgba(0,0,0,0.2)", s.style.inset = "0", s.style.padding = "20px", n.style.backgroundColor = "rgba(220, 220, 220, 0.85)", n.style.padding = "6px 0", n.style.borderRadius = "10px", n.style.width = "100%", n.style.maxWidth = "400px", a.style.padding = "10px 12px", a.style.textAlign = "center", a.style.fontWeight = "400", a.style.fontSize = "13px", a.style.display = "flex", a.style.justifyContent = "center", a.style.alignItems = "center", r.style.display = "block", r.style.textAlign = "center", r.style.textDecoration = "none", r.style.borderTop = "rgb(180,180,180) solid 1px", o.style.display = "block", o.style.width = "100%", o.style.textAlign = "center", o.style.appearance = "none", o.style.background = "none", o.style.border = "none", o.style.outline = "none", o.style.padding = "10px", o.style.fontWeight = "400", o.style.fontSize = "16px", o.style.color = "#2e7cf1", o.style.cursor = "pointer"), s.appendChild(n), n.appendChild(a), n.appendChild(r), a.appendChild(
|
|
361
|
+
document.createTextNode(O)
|
|
406
362
|
);
|
|
407
|
-
const
|
|
408
|
-
this.requestOrientationPermissions(),
|
|
363
|
+
const d = () => {
|
|
364
|
+
this.requestOrientationPermissions(), s.style.display = "none";
|
|
409
365
|
};
|
|
410
|
-
o.addEventListener("click",
|
|
366
|
+
o.addEventListener("click", d), o.appendChild(document.createTextNode("OK")), r.appendChild(o), document.body.appendChild(s);
|
|
411
367
|
}, this.obtainPermissionGesture = () => {
|
|
412
|
-
this.preferConfirmDialog === !0 ? window.confirm(
|
|
368
|
+
this.preferConfirmDialog === !0 ? window.confirm(O) && this.requestOrientationPermissions() : this.createObtainPermissionGestureDialog();
|
|
413
369
|
};
|
|
414
370
|
}
|
|
415
|
-
on(e,
|
|
416
|
-
this.eventEmitter.on(e,
|
|
371
|
+
on(e, i) {
|
|
372
|
+
this.eventEmitter.on(e, i);
|
|
417
373
|
}
|
|
418
374
|
}
|
|
419
|
-
class
|
|
375
|
+
class F {
|
|
420
376
|
/**
|
|
421
377
|
* Create a ClickHandler.
|
|
422
378
|
* @param {THREE.WebGLRenderer} - The Three.js renderer on which the click
|
|
423
379
|
* events will be handled.
|
|
424
380
|
*/
|
|
425
381
|
constructor(e) {
|
|
426
|
-
this.raycaster = new
|
|
427
|
-
this.normalisedMousePosition = new
|
|
428
|
-
|
|
429
|
-
-(
|
|
382
|
+
this.raycaster = new u.Raycaster(), this.normalisedMousePosition = null, e.domElement.addEventListener("click", (i) => {
|
|
383
|
+
this.normalisedMousePosition = new u.Vector2(
|
|
384
|
+
i.clientX / e.domElement.clientWidth * 2 - 1,
|
|
385
|
+
-(i.clientY / e.domElement.clientHeight * 2) + 1
|
|
430
386
|
);
|
|
431
387
|
});
|
|
432
388
|
}
|
|
@@ -438,17 +394,17 @@ class W {
|
|
|
438
394
|
* cast into.
|
|
439
395
|
* @return {Array} - array of all intersected objects.
|
|
440
396
|
*/
|
|
441
|
-
raycast(e,
|
|
397
|
+
raycast(e, i) {
|
|
442
398
|
if (this.normalisedMousePosition !== null) {
|
|
443
399
|
this.raycaster.setFromCamera(this.normalisedMousePosition, e);
|
|
444
|
-
const t = this.raycaster.intersectObjects(
|
|
400
|
+
const t = this.raycaster.intersectObjects(i.children, !1);
|
|
445
401
|
return this.normalisedMousePosition = null, t;
|
|
446
402
|
}
|
|
447
403
|
return [];
|
|
448
404
|
}
|
|
449
405
|
}
|
|
450
|
-
class
|
|
451
|
-
#
|
|
406
|
+
class j extends E {
|
|
407
|
+
#e;
|
|
452
408
|
/**
|
|
453
409
|
* Create a Webcam.
|
|
454
410
|
* @param constraints {Object} - options to use for initialising the camera.
|
|
@@ -457,17 +413,17 @@ class V extends y {
|
|
|
457
413
|
* element to render the webcam feed. If a falsy value (e.g. null or
|
|
458
414
|
* undefined), a video element will be created.
|
|
459
415
|
*/
|
|
460
|
-
constructor(e = { video: { facingMode: "environment" } },
|
|
461
|
-
super(), this.sceneWebcam = new
|
|
462
|
-
this.#
|
|
463
|
-
this.#
|
|
416
|
+
constructor(e = { video: { facingMode: "environment" } }, i) {
|
|
417
|
+
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.display = "none", document.body.appendChild(this.#e)), this.texture = this.#e ? new u.VideoTexture(this.#e) : null, navigator.mediaDevices && navigator.mediaDevices.getUserMedia ? navigator.mediaDevices.getUserMedia(e).then((t) => {
|
|
418
|
+
this.#e?.addEventListener("loadedmetadata", () => {
|
|
419
|
+
this.#e?.setAttribute(
|
|
464
420
|
"width",
|
|
465
|
-
this.#
|
|
466
|
-
), this.#
|
|
421
|
+
this.#e?.videoWidth.toString() ?? "0"
|
|
422
|
+
), this.#e?.setAttribute(
|
|
467
423
|
"height",
|
|
468
|
-
this.#
|
|
469
|
-
), this.#
|
|
470
|
-
}), this.#
|
|
424
|
+
this.#e?.videoHeight.toString() ?? "0"
|
|
425
|
+
), this.#e?.play(), this.emit("webcamstarted", { texture: this.texture });
|
|
426
|
+
}), this.#e && (this.#e.srcObject = t);
|
|
471
427
|
}).catch((t) => {
|
|
472
428
|
this.emit("webcamerror", {
|
|
473
429
|
code: t.name,
|
|
@@ -486,13 +442,13 @@ class V extends y {
|
|
|
486
442
|
this.texture?.dispose();
|
|
487
443
|
}
|
|
488
444
|
}
|
|
489
|
-
const
|
|
445
|
+
const k = "0.1.8";
|
|
490
446
|
export {
|
|
491
|
-
|
|
447
|
+
F as ClickHandler,
|
|
492
448
|
q as DeviceOrientationControls,
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
449
|
+
E as EventEmitter,
|
|
450
|
+
H as LocationBased,
|
|
451
|
+
R as SphMercProjection,
|
|
452
|
+
j as Webcam,
|
|
453
|
+
k as version
|
|
498
454
|
};
|
package/dist/locar.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(d,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],l):(d=typeof globalThis<"u"?globalThis:d||self,l(d.locar={},d.THREE))})(this,(function(d,l){"use strict";function _(u){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(u){for(const s in u)if(s!=="default"){const t=Object.getOwnPropertyDescriptor(u,s);Object.defineProperty(e,s,t.get?t:{enumerable:!0,get:()=>u[s]})}}return e.default=u,Object.freeze(e)}const f=_(l);class M{constructor(){this.project=(e,s)=>[this.#t(e),this.#e(s)],this.unproject=e=>[this.#i(e[0]),this.#s(e[1])],this.#t=e=>e/180*this.HALF_EARTH,this.#e=e=>{var s=Math.log(Math.tan((90+e)*Math.PI/360))/(Math.PI/180);return s*this.HALF_EARTH/180},this.#i=e=>e/this.HALF_EARTH*180,this.#s=e=>{var s=e/this.HALF_EARTH*180;return s=180/Math.PI*(2*Math.atan(Math.exp(s*Math.PI/180))-Math.PI/2),s},this.getID=()=>"epsg:3857",this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}#t;#e;#i;#s}class O{constructor(){this.on=(e,s)=>{this.eventHandlers[e]===void 0&&(this.eventHandlers[e]=[]),this.eventHandlers[e].push(s)},this.emit=(e,...s)=>{this.eventHandlers[e]?.forEach(t=>{t(...s)})},this.off=(e,s)=>{if(this.eventHandlers[e]){const t=this.eventHandlers[e].indexOf(s);t>-1&&this.eventHandlers[e].splice(t,1)}},this.eventHandlers={}}}class b extends O{#t;#e;#i;#s;#a;#n;#l;#r;#o;constructor(e,s,t={},h=null){super(),this.scene=e,this.camera=s,this.#t=new M,this.#e=null,this.#i=0,this.#s=100,this.#a=null,this.setGpsOptions(t),this.#n=null,this.#l=0,this.#r=0,this.#o=h}setProjection(e){this.#t=e}setGpsOptions(e={}){e.gpsMinDistance!==void 0&&(this.#i=e.gpsMinDistance),e.gpsMinAccuracy!==void 0&&(this.#s=e.gpsMinAccuracy)}async startGps(){if(this.#o){const s=await(await this.#o.sendData("/gps/start",{gpsMinDistance:this.#i,gpsMinAccuracy:this.#s})).json();this.#r=s.session}return this.#a===null?(this.#a=navigator.geolocation.watchPosition(e=>{this.#h(e)},e=>{this.emit("gpserror",e)},{enableHighAccuracy:!0}),!0):!1}stopGps(){return this.#a!==null?(navigator.geolocation.clearWatch(this.#a),this.#a=null,!0):!1}fakeGps(e,s,t=null,h=0){t!==null&&this.setElevation(t),this.#h({coords:{longitude:e,latitude:s,accuracy:h}})}lonLatToWorldCoords(e,s){const t=this.#t.project(e,s);if(this.#n)t[0]-=this.#n[0],t[1]-=this.#n[1];else throw"No initial position determined";return[t[0],-t[1]]}add(e,s,t,h,g={}){e.properties=g,this.#c(e,s,t,h),this.scene.add(e),this.#o?.sendData("/object/new",{position:e.position,x:e.position.x,z:e.position.z,session:this.#r,properties:g})}#c(e,s,t,h){const g=this.lonLatToWorldCoords(s,t);h!==void 0&&(e.position.y=h),[e.position.x,e.position.z]=g}setElevation(e){this.camera.position.y=e}#d(e,s){this.#n=this.#t.project(e,s)}#h(e){let s=Number.MAX_VALUE;this.#l++,this.#o?.sendData("/gps/new",{gpsCount:this.#l,lat:e.coords.latitude,lon:e.coords.longitude,acc:e.coords.accuracy,session:this.#r}),e.coords.accuracy<=this.#s&&(this.#e===null?this.#e={latitude:e.coords.latitude,longitude:e.coords.longitude}:s=this.#u(this.#e,e.coords),s>=this.#i&&(this.#e.longitude=e.coords.longitude,this.#e.latitude=e.coords.latitude,this.#n||(this.#d(e.coords.longitude,e.coords.latitude),this.#o?.sendData("/worldorigin/new",{gpsCount:this.#l,lat:e.coords.latitude,lon:e.coords.longitude,session:this.#r,initialPosition:this.#n})),this.#c(this.camera,e.coords.longitude,e.coords.latitude),this.#o?.sendData("/gps/accepted",{gpsCount:this.#l,cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:this.#r,distMoved:s}),this.emit("gpsupdate",{position:e,distMoved:s})))}#u(e,s){const t=f.MathUtils.degToRad(s.longitude-e.longitude),h=f.MathUtils.degToRad(s.latitude-e.latitude),g=Math.sin(h/2)*Math.sin(h/2)+Math.cos(f.MathUtils.degToRad(e.latitude))*Math.cos(f.MathUtils.degToRad(s.latitude))*(Math.sin(t/2)*Math.sin(t/2));return 2*Math.atan2(Math.sqrt(g),Math.sqrt(1-g))*6371e3}getLastKnownLocation(){return this.#e}}const w="locar-device-orientation-permission-modal",P="locar-device-orientation-permission-button",T="locar-device-orientation-permission-message",D="locar-device-orientation-permission-inner",S="locar-device-orientation-permission-button-inner",I="This immersive website requires access to your device motion sensors.",p=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,R=new l.Vector3(0,0,1),A=new l.Euler,N=new l.Quaternion,L=new l.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5)),C=2*Math.PI,v=.5*Math.PI;class x extends l.EventDispatcher{constructor(e,s={}){super(),this.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})},this.eventEmitter=new O;const t=this;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.orientationChangeEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation",this.smoothingFactor=s.smoothingFactor||1,this.enablePermissionDialog=s.enablePermissionDialog??!0,this.enableInlineStyling=s.enableStyling??!0,this.preferConfirmDialog=s.preferConfirmDialog??!1,this.orientationChangeThreshold=s.orientationChangeThreshold??0;const h=i=>{let{alpha:n,beta:a,gamma:r,webkitCompassHeading:o}=i;if(n=n??0,a=a??0,r=r??0,o=o??0,p){const c=360-o;t.alphaOffset=l.MathUtils.degToRad(c-n),t.deviceOrientation={alpha:n,beta:a,gamma:r,webkitCompassHeading:o}}else n<0&&(n+=360),t.deviceOrientation={alpha:n,beta:a,gamma:r};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:e.rotation}}))},g=()=>{t.screenOrientation=window.screen.orientation?.angle??0,p&&(t.screenOrientation===90?t.orientationOffset=-v:t.screenOrientation===-90?t.orientationOffset=v:t.orientationOffset=0)},E=(i,n,a,r,o)=>{A.set(a,n,-r,"YXZ"),i.setFromEuler(A),i.multiply(L),i.multiply(N.setFromAxisAngle(R,-o))};this.connect=()=>{g(),window.addEventListener("orientationchange",g),window.addEventListener(t.orientationChangeEventName,h),t.enabled=!0},this.disconnect=()=>{window.removeEventListener("orientationchange",g),window.removeEventListener(t.orientationChangeEventName,h),t.enabled=!1,t.initialOffset=!1,t.deviceOrientation=null},this.requestOrientationPermissions=()=>{window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(i=>{i==="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(i=>{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(i,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=()=>{if(t.enabled===!1)return;const i=t.deviceOrientation;if(i){let n=i.alpha?l.MathUtils.degToRad(i.alpha)+t.alphaOffset:0,a=i.beta?l.MathUtils.degToRad(i.beta):0,r=i.gamma?l.MathUtils.degToRad(i.gamma):0;const o=t.screenOrientation?l.MathUtils.degToRad(t.screenOrientation):0;if(t.smoothingFactor<1)if(t.lastOrientation){const c=t.smoothingFactor;n=t._getSmoothedAngle(n,t.lastOrientation.alpha,c),a=t._getSmoothedAngle(a+Math.PI,t.lastOrientation.beta,c),r=t._getSmoothedAngle(r+v,t.lastOrientation.gamma,c,Math.PI)}else a+=Math.PI,r+=v;if(t.lastOrientation&&(n=t._calcAngleWithThreshold(n,t.lastOrientation.alpha),a=t._calcAngleWithThreshold(a,t.lastOrientation.beta),r=t._calcAngleWithThreshold(r,t.lastOrientation.gamma)),p){const c=new l.Quaternion;E(c,n,t.smoothingFactor<1?a-Math.PI:a,t.smoothingFactor<1?r-Math.PI/2:r,o);const y=new l.Euler().setFromQuaternion(c,"YXZ");let m=l.MathUtils.degToRad(360-(i.webkitCompassHeading??0));t.smoothingFactor<1&&t.lastCompassY!==void 0&&(m=t._getSmoothedAngle(m,t.lastCompassY,t.smoothingFactor)),t.lastCompassY=m,y.y=m+(t.orientationOffset||0),c.setFromEuler(y),t.object.quaternion.copy(c)}else E(t.object.quaternion,p?n+t.alphaOffset:n,t.smoothingFactor<1?a-Math.PI:a,t.smoothingFactor<1?r-v:r,o);t.lastOrientation={alpha:n,beta:a,gamma:r}}},this.getCorrectedHeading=()=>{const{deviceOrientation:i}=t;if(!i)return 0;let n=0;return p?(n=360-(i.webkitCompassHeading??0),t.orientationOffset&&(n+=t.orientationOffset*(180/Math.PI),n=(n+360)%360)):(i.absolute===!0||t.orientationChangeEventName,n=i.alpha?i.alpha:0,n=(360-n)%360,n<0&&(n+=360)),n},this._calcAngleWithThreshold=(i,n)=>Math.abs(i-n)<t.orientationChangeThreshold?n:i,this._orderAngle=(i,n,a=C)=>n>i&&Math.abs(n-i)<a/2||i>n&&Math.abs(n-i)>a/2?{left:i,right:n}:{left:n,right:i},this._getSmoothedAngle=(i,n,a,r=C)=>{const o=t._orderAngle(i,n,r),c=o.left,y=o.right;o.left=0,o.right-=c,o.right<0&&(o.right+=r);let m=y==n?(1-a)*o.right+a*o.left:a*o.right+(1-a)*o.left;return m+=c,m>=r&&(m-=r),m},this.updateAlphaOffset=()=>{t.initialOffset=!1},this.dispose=()=>{t.disconnect()},this.getAlpha=()=>{const{deviceOrientation:i}=t;return i&&i.alpha?l.MathUtils.degToRad(i.alpha)+t.alphaOffset:0},this.getBeta=()=>{const{deviceOrientation:i}=t;return i&&i.beta?l.MathUtils.degToRad(i.beta):0},this.getGamma=()=>{const{deviceOrientation:i}=t;return i&&i.gamma?l.MathUtils.degToRad(i.gamma):0},this.createObtainPermissionGestureDialog=()=>{const i=document.createElement("div");i.classList.add(w);const n=document.createElement("div");n.classList.add(D);const a=document.createElement("div");a.classList.add(T);const r=document.createElement("div");r.classList.add(S);const o=document.createElement("button");o.classList.add(P),document.body.appendChild(i),this.enableInlineStyling===!0&&(i.style.fontFamily="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",i.style.display="flex",i.style.position="fixed",i.style.zIndex="100000",i.style.justifyContent="center",i.style.alignItems="center",i.style.backgroundColor="rgba(0,0,0,0.2)",i.style.inset="0",i.style.padding="20px",n.style.backgroundColor="rgba(220, 220, 220, 0.85)",n.style.padding="6px 0",n.style.borderRadius="10px",n.style.width="100%",n.style.maxWidth="400px",a.style.padding="10px 12px",a.style.textAlign="center",a.style.fontWeight="400",a.style.fontSize="13px",a.style.display="flex",a.style.justifyContent="center",a.style.alignItems="center",r.style.display="block",r.style.textAlign="center",r.style.textDecoration="none",r.style.borderTop="rgb(180,180,180) solid 1px",o.style.display="block",o.style.width="100%",o.style.textAlign="center",o.style.appearance="none",o.style.background="none",o.style.border="none",o.style.outline="none",o.style.padding="10px",o.style.fontWeight="400",o.style.fontSize="16px",o.style.color="#2e7cf1",o.style.cursor="pointer"),i.appendChild(n),n.appendChild(a),n.appendChild(r),a.appendChild(document.createTextNode(I));const c=()=>{this.requestOrientationPermissions(),i.style.display="none"};o.addEventListener("click",c),o.appendChild(document.createTextNode("OK")),r.appendChild(o),document.body.appendChild(i)},this.obtainPermissionGesture=()=>{this.preferConfirmDialog===!0?window.confirm(I)&&this.requestOrientationPermissions():this.createObtainPermissionGestureDialog()}}on(e,s){this.eventEmitter.on(e,s)}}class F{constructor(e){this.raycaster=new f.Raycaster,this.normalisedMousePosition=null,e.domElement.addEventListener("click",s=>{this.normalisedMousePosition=new f.Vector2(s.clientX/e.domElement.clientWidth*2-1,-(s.clientY/e.domElement.clientHeight*2)+1)})}raycast(e,s){if(this.normalisedMousePosition!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,e);const t=this.raycaster.intersectObjects(s.children,!1);return this.normalisedMousePosition=null,t}return[]}}class U extends O{#t;constructor(e={video:{facingMode:"environment"}},s){super(),this.sceneWebcam=new f.Scene,s?this.#t=document.querySelector(s):(this.#t=document.createElement("video"),this.#t.setAttribute("autoplay","true"),this.#t.setAttribute("playsinline","true"),this.#t.style.display="none",document.body.appendChild(this.#t)),this.texture=this.#t?new f.VideoTexture(this.#t):null,navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices.getUserMedia(e).then(t=>{this.#t?.addEventListener("loadedmetadata",()=>{this.#t?.setAttribute("width",this.#t?.videoWidth.toString()??"0"),this.#t?.setAttribute("height",this.#t?.videoHeight.toString()??"0"),this.#t?.play(),this.emit("webcamstarted",{texture:this.texture})}),this.#t&&(this.#t.srcObject=t)}).catch(t=>{this.emit("webcamerror",{code:t.name,message:t.message})}):this.emit("webcamerror",{code:"LOCAR_NO_MEDIA_DEVICES_API",message:"Media devices API not supported"})}dispose(){this.texture?.dispose()}}const j="0.1.7";d.ClickHandler=F,d.DeviceOrientationControls=x,d.EventEmitter=O,d.LocationBased=b,d.SphMercProjection=M,d.Webcam=U,d.version=j,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(d,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],a):(d=typeof globalThis<"u"?globalThis:d||self,a(d.locar={},d.THREE))})(this,(function(d,a){"use strict";function b(h){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(h){for(const i in h)if(i!=="default"){const t=Object.getOwnPropertyDescriptor(h,i);Object.defineProperty(e,i,t.get?t:{enumerable:!0,get:()=>h[i]})}}return e.default=h,Object.freeze(e)}const m=b(a);class y{constructor(){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=>{var i=Math.log(Math.tan((90+e)*Math.PI/360))/(Math.PI/180);return i*this.HALF_EARTH/180},this.#i=e=>e/this.HALF_EARTH*180,this.#n=e=>{var i=e/this.HALF_EARTH*180;return i=180/Math.PI*(2*Math.atan(Math.exp(i*Math.PI/180))-Math.PI/2),i},this.getID=()=>"epsg:3857",this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}#e;#t;#i;#n}class f{constructor(){this.on=(e,i)=>{this.eventHandlers[e]===void 0&&(this.eventHandlers[e]=[]),this.eventHandlers[e].push(i)},this.emit=(e,...i)=>{this.eventHandlers[e]?.forEach(t=>{t(...i)})},this.off=(e,i)=>{if(this.eventHandlers[e]){const t=this.eventHandlers[e].indexOf(i);t>-1&&this.eventHandlers[e].splice(t,1)}},this.eventHandlers={}}}class w extends f{#e;#t;#i;#n;#a;#s;#l;#r;#o;constructor(e,i,t={},c=null){super(),this.scene=e,this.camera=i,this.#e=new y,this.#t=null,this.#i=0,this.#n=100,this.#a=null,this.setGpsOptions(t),this.#s=null,this.#l=0,this.#r=0,this.#o=c}setProjection(e){this.#e=e}setGpsOptions(e={}){e.gpsMinDistance!==void 0&&(this.#i=e.gpsMinDistance),e.gpsMinAccuracy!==void 0&&(this.#n=e.gpsMinAccuracy)}async startGps(){if(this.#o){const i=await(await this.#o.sendData("/gps/start",{gpsMinDistance:this.#i,gpsMinAccuracy:this.#n})).json();this.#r=i.session}return this.#a===null?(this.#a=navigator.geolocation.watchPosition(e=>{this.#d(e)},e=>{this.emit("gpserror",e)},{enableHighAccuracy:!0}),!0):!1}stopGps(){return this.#a!==null?(navigator.geolocation.clearWatch(this.#a),this.#a=null,!0):!1}fakeGps(e,i,t=null,c=0){t!==null&&this.setElevation(t),this.#d({coords:{longitude:e,latitude:i,accuracy:c}})}lonLatToWorldCoords(e,i){const t=this.#e.project(e,i);if(this.#s)t[0]-=this.#s[0],t[1]-=this.#s[1];else throw"No initial position determined";return[t[0],-t[1]]}add(e,i,t,c,u={}){e.properties=u,this.#c(e,i,t,c),this.scene.add(e),this.#o?.sendData("/object/new",{position:e.position,x:e.position.x,z:e.position.z,session:this.#r,properties:u})}#c(e,i,t,c){const u=this.lonLatToWorldCoords(i,t);c!==void 0&&(e.position.y=c),[e.position.x,e.position.z]=u}setElevation(e){this.camera.position.y=e}#h(e,i){this.#s=this.#e.project(e,i)}#d(e){let i=Number.MAX_VALUE;this.#l++,this.#o?.sendData("/gps/new",{gpsCount:this.#l,lat:e.coords.latitude,lon:e.coords.longitude,acc:e.coords.accuracy,session:this.#r}),e.coords.accuracy<=this.#n&&(this.#t===null?this.#t={latitude:e.coords.latitude,longitude:e.coords.longitude}:i=this.#u(this.#t,e.coords),i>=this.#i&&(this.#t.longitude=e.coords.longitude,this.#t.latitude=e.coords.latitude,this.#s||(this.#h(e.coords.longitude,e.coords.latitude),this.#o?.sendData("/worldorigin/new",{gpsCount:this.#l,lat:e.coords.latitude,lon:e.coords.longitude,session:this.#r,initialPosition:this.#s})),this.#c(this.camera,e.coords.longitude,e.coords.latitude),this.#o?.sendData("/gps/accepted",{gpsCount:this.#l,cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:this.#r,distMoved:i}),this.emit("gpsupdate",{position:e,distMoved:i})))}#u(e,i){const t=m.MathUtils.degToRad(i.longitude-e.longitude),c=m.MathUtils.degToRad(i.latitude-e.latitude),u=Math.sin(c/2)*Math.sin(c/2)+Math.cos(m.MathUtils.degToRad(e.latitude))*Math.cos(m.MathUtils.degToRad(i.latitude))*(Math.sin(t/2)*Math.sin(t/2));return 2*Math.atan2(Math.sqrt(u),Math.sqrt(1-u))*6371e3}getLastKnownLocation(){return this.#t}}const C="locar-device-orientation-permission-modal",A="locar-device-orientation-permission-button",_="locar-device-orientation-permission-message",P="locar-device-orientation-permission-inner",D="locar-device-orientation-permission-button-inner",E="This immersive website requires access to your device motion sensors.",v=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,T=new a.Vector3(0,0,1),I=new a.Euler,R=new a.Quaternion,S=new a.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5)),M=.5*Math.PI;class N extends a.EventDispatcher{constructor(e,i={}){super(),this.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})},this.eventEmitter=new f;const t=this;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||1,this.enablePermissionDialog=i.enablePermissionDialog??!0,this.enableInlineStyling=i.enableStyling??!0,this.preferConfirmDialog=i.preferConfirmDialog??!1,this.orientationChangeThreshold=i.orientationChangeThreshold??0;const c=n=>{let{alpha:s,beta:r,gamma:l,webkitCompassHeading:o}=n;if(s=s??0,r=r??0,l=l??0,o=o??0,v){const g=360-o;t.alphaOffset=a.MathUtils.degToRad(g-s),t.deviceOrientation={alpha:s,beta:r,gamma:l,webkitCompassHeading:o}}else s<0&&(s+=360),t.deviceOrientation={alpha:s,beta:r,gamma:l};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:e.rotation}}))},u=()=>{t.screenOrientation=window.screen.orientation?.angle??0,v&&(t.screenOrientation===90?t.orientationOffset=-M:t.screenOrientation===-90?t.orientationOffset=M:t.orientationOffset=0)},O=(n,s,r,l,o)=>{I.set(r,s,-l,"YXZ"),n.setFromEuler(I),n.multiply(S),n.multiply(R.setFromAxisAngle(T,-o))};this.connect=()=>{u(),window.addEventListener("orientationchange",u),window.addEventListener(t.orientationChangeEventName,c),t.enabled=!0},this.disconnect=()=>{window.removeEventListener("orientationchange",u),window.removeEventListener(t.orientationChangeEventName,c),t.enabled=!1,t.initialOffset=!1,t.deviceOrientation=null,t.lastQuaternion=null},this.requestOrientationPermissions=()=>{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(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=()=>{if(t.enabled===!1)return;const n=t.deviceOrientation;if(n){let s=n.alpha?a.MathUtils.degToRad(n.alpha)+t.alphaOffset:0,r=n.beta?a.MathUtils.degToRad(n.beta):0,l=n.gamma?a.MathUtils.degToRad(n.gamma):0;const o=t.screenOrientation?a.MathUtils.degToRad(t.screenOrientation):0,g=new a.Quaternion;if(v){O(g,s,r,l,o);const p=new a.Euler().setFromQuaternion(g,"YXZ"),U=a.MathUtils.degToRad(360-(n.webkitCompassHeading??0));p.y=U+(t.orientationOffset||0),g.setFromEuler(p)}else O(g,s,r,l,o);if(t.lastQuaternion&&t.orientationChangeThreshold>0&&g.angleTo(t.lastQuaternion)<t.orientationChangeThreshold)return;if(t.smoothingFactor<1&&t.lastQuaternion){const p=1-t.smoothingFactor;t.object.quaternion.slerp(g,p)}else t.object.quaternion.copy(g);t.lastQuaternion=t.object.quaternion.clone(),window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.object.rotation}}))}},this.getCorrectedHeading=()=>{const{deviceOrientation:n}=t;if(!n)return 0;let s=0;return v?(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},this.updateAlphaOffset=()=>{t.initialOffset=!1},this.dispose=()=>{t.disconnect()},this.getAlpha=()=>{const{deviceOrientation:n}=t;return n&&n.alpha?a.MathUtils.degToRad(n.alpha)+t.alphaOffset:0},this.getBeta=()=>{const{deviceOrientation:n}=t;return n&&n.beta?a.MathUtils.degToRad(n.beta):0},this.getGamma=()=>{const{deviceOrientation:n}=t;return n&&n.gamma?a.MathUtils.degToRad(n.gamma):0},this.createObtainPermissionGestureDialog=()=>{const n=document.createElement("div");n.classList.add(C);const s=document.createElement("div");s.classList.add(P);const r=document.createElement("div");r.classList.add(_);const l=document.createElement("div");l.classList.add(D);const o=document.createElement("button");o.classList.add(A),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",r.style.padding="10px 12px",r.style.textAlign="center",r.style.fontWeight="400",r.style.fontSize="13px",r.style.display="flex",r.style.justifyContent="center",r.style.alignItems="center",l.style.display="block",l.style.textAlign="center",l.style.textDecoration="none",l.style.borderTop="rgb(180,180,180) solid 1px",o.style.display="block",o.style.width="100%",o.style.textAlign="center",o.style.appearance="none",o.style.background="none",o.style.border="none",o.style.outline="none",o.style.padding="10px",o.style.fontWeight="400",o.style.fontSize="16px",o.style.color="#2e7cf1",o.style.cursor="pointer"),n.appendChild(s),s.appendChild(r),s.appendChild(l),r.appendChild(document.createTextNode(E));const g=()=>{this.requestOrientationPermissions(),n.style.display="none"};o.addEventListener("click",g),o.appendChild(document.createTextNode("OK")),l.appendChild(o),document.body.appendChild(n)},this.obtainPermissionGesture=()=>{this.preferConfirmDialog===!0?window.confirm(E)&&this.requestOrientationPermissions():this.createObtainPermissionGestureDialog()}}on(e,i){this.eventEmitter.on(e,i)}}class L{constructor(e){this.raycaster=new m.Raycaster,this.normalisedMousePosition=null,e.domElement.addEventListener("click",i=>{this.normalisedMousePosition=new m.Vector2(i.clientX/e.domElement.clientWidth*2-1,-(i.clientY/e.domElement.clientHeight*2)+1)})}raycast(e,i){if(this.normalisedMousePosition!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,e);const t=this.raycaster.intersectObjects(i.children,!1);return this.normalisedMousePosition=null,t}return[]}}class x extends f{#e;constructor(e={video:{facingMode:"environment"}},i){super(),this.sceneWebcam=new m.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.display="none",document.body.appendChild(this.#e)),this.texture=this.#e?new m.VideoTexture(this.#e):null,navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices.getUserMedia(e).then(t=>{this.#e?.addEventListener("loadedmetadata",()=>{this.#e?.setAttribute("width",this.#e?.videoWidth.toString()??"0"),this.#e?.setAttribute("height",this.#e?.videoHeight.toString()??"0"),this.#e?.play(),this.emit("webcamstarted",{texture:this.texture})}),this.#e&&(this.#e.srcObject=t)}).catch(t=>{this.emit("webcamerror",{code:t.name,message:t.message})}):this.emit("webcamerror",{code:"LOCAR_NO_MEDIA_DEVICES_API",message:"Media devices API not supported"})}dispose(){this.texture?.dispose()}}const j="0.1.8";d.ClickHandler=L,d.DeviceOrientationControls=N,d.EventEmitter=f,d.LocationBased=w,d.SphMercProjection=y,d.Webcam=x,d.version=j,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|