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 CHANGED
@@ -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.2",
45
+ "locar" : "^0.1.8",
46
46
  "three" : "^0.175.0"
47
47
  }
48
48
  ```
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
- lastCompassY: number | undefined;
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.7";
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 g from "three";
2
- import { Vector3 as T, Euler as C, Quaternion as E, EventDispatcher as _, MathUtils as u } from "three";
3
- class w {
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, 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) => {
9
- var s = Math.log(Math.tan((90 + e) * Math.PI / 360)) / (Math.PI / 180);
10
- return s * this.HALF_EARTH / 180;
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 s = e / this.HALF_EARTH * 180;
13
- return s = 180 / Math.PI * (2 * Math.atan(Math.exp(s * Math.PI / 180)) - Math.PI / 2), s;
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 y {
21
+ class E {
22
22
  constructor() {
23
- this.on = (e, s) => {
24
- this.eventHandlers[e] === void 0 && (this.eventHandlers[e] = []), this.eventHandlers[e].push(s);
25
- }, this.emit = (e, ...s) => {
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(...s);
27
+ t(...i);
28
28
  });
29
- }, this.off = (e, s) => {
29
+ }, this.off = (e, i) => {
30
30
  if (this.eventHandlers[e]) {
31
- const t = this.eventHandlers[e].indexOf(s);
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 F extends y {
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, s, t = {}, c = null) {
56
- super(), this.scene = e, this.camera = s, this.#t = new w(), 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 = c;
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.#t = e;
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 s = await (await this.#o.sendData("/gps/start", {
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 = s.session;
88
+ this.#r = i.session;
89
89
  }
90
90
  return this.#a === null ? (this.#a = navigator.geolocation.watchPosition(
91
91
  (e) => {
92
- this.#h(e);
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, s, t = null, c = 0) {
120
- t !== null && this.setElevation(t), this.#h({
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: s,
124
- accuracy: c
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, s) {
140
- const t = this.#t.project(e, s);
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, s, t, c, h = {}) {
158
- e.properties = h, this.#c(e, s, t, c), this.scene.add(e), this.#o?.sendData("/object/new", {
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: h
163
+ properties: c
164
164
  });
165
165
  }
166
- #c(e, s, t, c) {
167
- const h = this.lonLatToWorldCoords(s, t);
168
- c !== void 0 && (e.position.y = c), [e.position.x, e.position.z] = h;
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
- #d(e, s) {
178
- this.#n = this.#t.project(e, s);
177
+ #h(e, i) {
178
+ this.#n = this.#e.project(e, i);
179
179
  }
180
- #h(e) {
181
- let s = Number.MAX_VALUE;
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.#e === null ? this.#e = {
188
+ }), e.coords.accuracy <= this.#s && (this.#t === null ? this.#t = {
189
189
  latitude: e.coords.latitude,
190
190
  longitude: e.coords.longitude
191
- } : 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(
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: s
210
- }), this.emit("gpsupdate", { position: e, distMoved: s })));
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, s) {
218
- const t = g.MathUtils.degToRad(s.longitude - e.longitude), c = g.MathUtils.degToRad(s.latitude - e.latitude), h = Math.sin(c / 2) * Math.sin(c / 2) + Math.cos(g.MathUtils.degToRad(e.latitude)) * Math.cos(g.MathUtils.degToRad(s.latitude)) * (Math.sin(t / 2) * Math.sin(t / 2));
219
- return 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)) * 6371e3;
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.#e;
227
+ return this.#t;
228
228
  }
229
229
  }
230
- const R = "locar-device-orientation-permission-modal", P = "locar-device-orientation-permission-button", b = "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, N = new T(0, 0, 1), A = new C(), x = new E(), L = new E(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)), M = 2 * Math.PI, f = 0.5 * Math.PI;
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, s = {}) {
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 y();
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.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;
251
- const c = (i) => {
252
- let { alpha: n, beta: a, gamma: r, webkitCompassHeading: o } = i;
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 l = 360 - o;
255
- t.alphaOffset = u.degToRad(l - n), t.deviceOrientation = { alpha: n, beta: a, gamma: r, webkitCompassHeading: o };
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
- }, h = () => {
264
- t.screenOrientation = window.screen.orientation?.angle ?? 0, p && (t.screenOrientation === 90 ? t.orientationOffset = -f : t.screenOrientation === -90 ? t.orientationOffset = f : t.orientationOffset = 0);
265
- }, O = (i, n, a, r, o) => {
266
- A.set(a, n, -r, "YXZ"), i.setFromEuler(A), i.multiply(L), i.multiply(x.setFromAxisAngle(N, -o));
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
- h(), window.addEventListener(
269
+ c(), window.addEventListener(
270
270
  "orientationchange",
271
- h
271
+ c
272
272
  ), window.addEventListener(
273
273
  t.orientationChangeEventName,
274
- c
274
+ l
275
275
  ), t.enabled = !0;
276
276
  }, this.disconnect = () => {
277
277
  window.removeEventListener(
278
278
  "orientationchange",
279
- h
279
+ c
280
280
  ), window.removeEventListener(
281
281
  t.orientationChangeEventName,
282
- c
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((i) => {
286
- i === "granted" ? this.eventEmitter.emit("deviceorientationgranted", {
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((i) => {
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(i, null, 2)
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 i = t.deviceOrientation;
305
- if (i) {
306
- let n = i.alpha ? u.degToRad(i.alpha) + t.alphaOffset : 0, a = i.beta ? u.degToRad(i.beta) : 0, r = i.gamma ? u.degToRad(i.gamma) : 0;
307
- const o = t.screenOrientation ? u.degToRad(t.screenOrientation) : 0;
308
- if (t.smoothingFactor < 1)
309
- if (t.lastOrientation) {
310
- const l = t.smoothingFactor;
311
- n = t._getSmoothedAngle(
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
- let d = u.degToRad(
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
- O(
359
- t.object.quaternion,
360
- p ? n + t.alphaOffset : n,
361
- t.smoothingFactor < 1 ? a - Math.PI : a,
362
- t.smoothingFactor < 1 ? r - f : r,
363
- o
364
- );
365
- t.lastOrientation = {
366
- alpha: n,
367
- beta: a,
368
- gamma: r
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: i } = t;
373
- if (!i) return 0;
333
+ const { deviceOrientation: s } = t;
334
+ if (!s) return 0;
374
335
  let n = 0;
375
- 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;
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: i } = t;
387
- return i && i.alpha ? u.degToRad(i.alpha) + t.alphaOffset : 0;
342
+ const { deviceOrientation: s } = t;
343
+ return s && s.alpha ? h.degToRad(s.alpha) + t.alphaOffset : 0;
388
344
  }, this.getBeta = () => {
389
- const { deviceOrientation: i } = t;
390
- return i && i.beta ? u.degToRad(i.beta) : 0;
345
+ const { deviceOrientation: s } = t;
346
+ return s && s.beta ? h.degToRad(s.beta) : 0;
391
347
  }, this.getGamma = () => {
392
- const { deviceOrientation: i } = t;
393
- return i && i.gamma ? u.degToRad(i.gamma) : 0;
348
+ const { deviceOrientation: s } = t;
349
+ return s && s.gamma ? h.degToRad(s.gamma) : 0;
394
350
  }, this.createObtainPermissionGestureDialog = () => {
395
- const i = document.createElement("div");
396
- i.classList.add(R);
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(b);
356
+ a.classList.add(_);
401
357
  const r = document.createElement("div");
402
- r.classList.add(S);
358
+ r.classList.add(P);
403
359
  const o = document.createElement("button");
404
- 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(
405
- document.createTextNode(I)
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 l = () => {
408
- this.requestOrientationPermissions(), i.style.display = "none";
363
+ const d = () => {
364
+ this.requestOrientationPermissions(), s.style.display = "none";
409
365
  };
410
- o.addEventListener("click", l), o.appendChild(document.createTextNode("OK")), r.appendChild(o), document.body.appendChild(i);
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(I) && this.requestOrientationPermissions() : this.createObtainPermissionGestureDialog();
368
+ this.preferConfirmDialog === !0 ? window.confirm(O) && this.requestOrientationPermissions() : this.createObtainPermissionGestureDialog();
413
369
  };
414
370
  }
415
- on(e, s) {
416
- this.eventEmitter.on(e, s);
371
+ on(e, i) {
372
+ this.eventEmitter.on(e, i);
417
373
  }
418
374
  }
419
- class W {
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 g.Raycaster(), this.normalisedMousePosition = null, e.domElement.addEventListener("click", (s) => {
427
- this.normalisedMousePosition = new g.Vector2(
428
- s.clientX / e.domElement.clientWidth * 2 - 1,
429
- -(s.clientY / e.domElement.clientHeight * 2) + 1
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, s) {
397
+ raycast(e, i) {
442
398
  if (this.normalisedMousePosition !== null) {
443
399
  this.raycaster.setFromCamera(this.normalisedMousePosition, e);
444
- const t = this.raycaster.intersectObjects(s.children, !1);
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 V extends y {
451
- #t;
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" } }, s) {
461
- super(), this.sceneWebcam = new g.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 g.VideoTexture(this.#t) : null, navigator.mediaDevices && navigator.mediaDevices.getUserMedia ? navigator.mediaDevices.getUserMedia(e).then((t) => {
462
- this.#t?.addEventListener("loadedmetadata", () => {
463
- this.#t?.setAttribute(
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.#t?.videoWidth.toString() ?? "0"
466
- ), this.#t?.setAttribute(
421
+ this.#e?.videoWidth.toString() ?? "0"
422
+ ), this.#e?.setAttribute(
467
423
  "height",
468
- this.#t?.videoHeight.toString() ?? "0"
469
- ), this.#t?.play(), this.emit("webcamstarted", { texture: this.texture });
470
- }), this.#t && (this.#t.srcObject = t);
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 j = "0.1.7";
445
+ const k = "0.1.8";
490
446
  export {
491
- W as ClickHandler,
447
+ F as ClickHandler,
492
448
  q as DeviceOrientationControls,
493
- y as EventEmitter,
494
- F as LocationBased,
495
- w as SphMercProjection,
496
- V as Webcam,
497
- j as version
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"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locar",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Location-based AR from AR.js.",
5
5
  "files": [
6
6
  "dist"