locar 0.0.9 → 0.0.10

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
@@ -4,7 +4,7 @@ Location-based AR from AR.js.
4
4
 
5
5
  ## What is this?
6
6
 
7
- LocAR.js is an AR.js project to develop a library focused specifically on location-based augmented reality in the browser. Currently it consists of the location-based three.js code from [the main AR.js repository](https://github.com/AR-js-org/AR.js), with some minor changes to make it compatible with latest versions (0.169.0 at time of writing) of three.js.
7
+ LocAR.js is an AR.js project to develop a library focused specifically on location-based augmented reality in the browser. Currently it consists of the location-based three.js code from [the main AR.js repository](https://github.com/AR-js-org/AR.js), with some minor changes to make it compatible with latest versions (0.175.0 at time of writing) of three.js.
8
8
 
9
9
  ## Why?
10
10
 
@@ -20,8 +20,6 @@ For a while there have been some bugs and issues with location-based AR.js which
20
20
 
21
21
  Some of these issues, such as incorrect North on some devices, may well be to do with incorrect or mis-calibrated sensors but the hope is that the standalone library will help to verify this and, if certain devices have North wrong by a consistent bearing, to develop a calibration tool.
22
22
 
23
- For this reason, the focus at present will be on developing a **pure three.js version only**. When we believe that the key bugs and issues have been resolved, the plan is to then provide an A-Frame wrapper.
24
-
25
23
  ## Cross-platform Compatibility
26
24
 
27
25
  Chrome on Android is recommended. It may also work on Chrome in iOS; please file an issue if not. It will not work on Safari on iOS, due to permissions issues - **though pull requests are welcome to fix this**. Firefox unfortunately does not appear to fully implement the Device Orientation API so is unlikely to work
@@ -42,8 +40,8 @@ import * as LocAR from 'locar';
42
40
  as long as you include `locar` as a dependency, e.g. in your `package.json`:
43
41
  ```
44
42
  "dependencies": {
45
- "locar" : "^0.0.9",
46
- "three" : "^0.169.0"
43
+ "locar" : "^0.0.10",
44
+ "three" : "^0.175.0"
47
45
  }
48
46
  ```
49
47
 
package/dist/locar.es.js CHANGED
@@ -1,20 +1,20 @@
1
- var q = (a) => {
2
- throw TypeError(a);
1
+ var W = (i) => {
2
+ throw TypeError(i);
3
3
  };
4
- var S = (a, t, e) => t.has(a) || q("Cannot " + e);
5
- var n = (a, t, e) => (S(a, t, "read from private field"), e ? e.call(a) : t.get(a)), m = (a, t, e) => t.has(a) ? q("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(a) : t.set(a, e), u = (a, t, e, i) => (S(a, t, "write to private field"), i ? i.call(a, e) : t.set(a, e), e), p = (a, t, e) => (S(a, t, "access private method"), e);
6
- var W = (a, t, e, i) => ({
4
+ var S = (i, t, e) => t.has(i) || W("Cannot " + e);
5
+ var s = (i, t, e) => (S(i, t, "read from private field"), e ? e.call(i) : t.get(i)), m = (i, t, e) => t.has(i) ? W("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(i) : t.set(i, e), u = (i, t, e, n) => (S(i, t, "write to private field"), n ? n.call(i, e) : t.set(i, e), e), f = (i, t, e) => (S(i, t, "access private method"), e);
6
+ var k = (i, t, e, n) => ({
7
7
  set _(r) {
8
- u(a, t, r, e);
8
+ u(i, t, r, e);
9
9
  },
10
10
  get _() {
11
- return n(a, t, i);
11
+ return s(i, t, n);
12
12
  }
13
13
  });
14
- import * as D from "three";
15
- import { Vector3 as Z, Euler as N, Quaternion as _, EventDispatcher as B, MathUtils as C } from "three";
16
- var E, U, G, X, Q;
17
- class K {
14
+ import * as L from "three";
15
+ import { Vector3 as Y, Euler as U, Quaternion as _, EventDispatcher as Z, MathUtils as C } from "three";
16
+ var E, N, z, V, X;
17
+ class $ {
18
18
  /**
19
19
  * Create a SphMercProjection.
20
20
  */
@@ -29,7 +29,7 @@ class K {
29
29
  * @return {Array} Two-member array containing easting and northing.
30
30
  */
31
31
  project(t, e) {
32
- return [p(this, E, U).call(this, t), p(this, E, G).call(this, e)];
32
+ return [f(this, E, N).call(this, t), f(this, E, z).call(this, e)];
33
33
  }
34
34
  /**
35
35
  * Unproject a Spherical Mercator easting and northing.
@@ -37,7 +37,7 @@ class K {
37
37
  * @return {Array} Two-member array containing longitude and latitude
38
38
  */
39
39
  unproject(t) {
40
- return [p(this, E, X).call(this, t[0]), p(this, E, Q).call(this, t[1])];
40
+ return [f(this, E, V).call(this, t[0]), f(this, E, X).call(this, t[1])];
41
41
  }
42
42
  /**
43
43
  * Return the projection's ID.
@@ -47,19 +47,19 @@ class K {
47
47
  return "epsg:3857";
48
48
  }
49
49
  }
50
- E = new WeakSet(), U = function(t) {
50
+ E = new WeakSet(), N = function(t) {
51
51
  return t / 180 * this.HALF_EARTH;
52
- }, G = function(t) {
52
+ }, z = function(t) {
53
53
  var e = Math.log(Math.tan((90 + t) * Math.PI / 360)) / (Math.PI / 180);
54
54
  return e * this.HALF_EARTH / 180;
55
- }, X = function(t) {
55
+ }, V = function(t) {
56
56
  return t / this.HALF_EARTH * 180;
57
- }, Q = function(t) {
57
+ }, X = function(t) {
58
58
  var e = t / this.HALF_EARTH * 180;
59
59
  return e = 180 / Math.PI * (2 * Math.atan(Math.exp(e * Math.PI / 180)) - Math.PI / 2), e;
60
60
  };
61
- var I, O, P, x, L, A, y, b, T, M, f, F, V, j, Y;
62
- class st {
61
+ var D, A, b, H, R, P, M, T, O, w, p, j, Q, F, B;
62
+ class K {
63
63
  /**
64
64
  * @param {THREE.Scene} scene - The Three.js scene to use.
65
65
  * @param {THREE.Camera} camera - The Three.js camera to use. Should usually
@@ -68,19 +68,19 @@ class st {
68
68
  * setGpsOptions() below.
69
69
  * @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.
70
70
  */
71
- constructor(t, e, i = {}, r = null) {
72
- m(this, f);
73
- m(this, I);
74
- m(this, O);
75
- m(this, P);
76
- m(this, x);
77
- m(this, L);
71
+ constructor(t, e, n = {}, r = null) {
72
+ m(this, p);
73
+ m(this, D);
78
74
  m(this, A);
79
- m(this, y);
80
75
  m(this, b);
81
- m(this, T);
76
+ m(this, H);
77
+ m(this, R);
78
+ m(this, P);
82
79
  m(this, M);
83
- this.scene = t, this.camera = e, u(this, I, new K()), u(this, O, {}), u(this, P, null), u(this, x, 0), u(this, L, 100), u(this, A, null), this.setGpsOptions(i), u(this, y, null), u(this, b, 0), u(this, T, 0), u(this, M, r);
80
+ m(this, T);
81
+ m(this, O);
82
+ m(this, w);
83
+ this.scene = t, this.camera = e, u(this, D, new $()), u(this, A, {}), u(this, b, null), u(this, H, 0), u(this, R, 100), u(this, P, null), this.setGpsOptions(n), u(this, M, null), u(this, T, 0), u(this, O, 0), u(this, w, r);
84
84
  }
85
85
  /**
86
86
  * Set the projection to use.
@@ -89,7 +89,7 @@ class st {
89
89
  * containing easting and northing.
90
90
  */
91
91
  setProjection(t) {
92
- u(this, I, t);
92
+ u(this, D, t);
93
93
  }
94
94
  /**
95
95
  * Set the GPS options.
@@ -99,7 +99,7 @@ class st {
99
99
  * minimum accuracy, in metres, for a GPS reading to be counted.
100
100
  */
101
101
  setGpsOptions(t = {}) {
102
- t.gpsMinDistance !== void 0 && u(this, x, t.gpsMinDistance), t.gpsMinAccuracy !== void 0 && u(this, L, t.gpsMinAccuracy);
102
+ t.gpsMinDistance !== void 0 && u(this, H, t.gpsMinDistance), t.gpsMinAccuracy !== void 0 && u(this, R, t.gpsMinAccuracy);
103
103
  }
104
104
  /**
105
105
  * Start the GPS on a real device
@@ -107,19 +107,19 @@ class st {
107
107
  * GPS errors can be handled by handling the gpserror event.
108
108
  */
109
109
  async startGps() {
110
- if (n(this, M)) {
111
- const e = await (await n(this, M).sendData("/gps/start", {
112
- gpsMinDistance: n(this, x),
113
- gpsMinAccuracy: n(this, L)
110
+ if (s(this, w)) {
111
+ const e = await (await s(this, w).sendData("/gps/start", {
112
+ gpsMinDistance: s(this, H),
113
+ gpsMinAccuracy: s(this, R)
114
114
  })).json();
115
- u(this, T, e.session);
115
+ u(this, O, e.session);
116
116
  }
117
- return n(this, A) === null ? (u(this, A, navigator.geolocation.watchPosition(
117
+ return s(this, P) === null ? (u(this, P, navigator.geolocation.watchPosition(
118
118
  (t) => {
119
- p(this, f, j).call(this, t);
119
+ f(this, p, F).call(this, t);
120
120
  },
121
121
  (t) => {
122
- n(this, O).gpserror ? n(this, O).gpserror(t.code) : alert(`GPS error: code ${t.code}`);
122
+ s(this, A).gpserror ? s(this, A).gpserror(t.code) : alert(`GPS error: code ${t.code}`);
123
123
  },
124
124
  {
125
125
  enableHighAccuracy: !0
@@ -132,7 +132,7 @@ class st {
132
132
  * stopped (i.e. it was never started).
133
133
  */
134
134
  stopGps() {
135
- return n(this, A) !== null ? (navigator.geolocation.clearWatch(n(this, A)), u(this, A, null), !0) : !1;
135
+ return s(this, P) !== null ? (navigator.geolocation.clearWatch(s(this, P)), u(this, P, null), !0) : !1;
136
136
  }
137
137
  /**
138
138
  * Send a fake GPS signal. Useful for testing on a desktop or laptop.
@@ -143,8 +143,8 @@ class st {
143
143
  * @param {number} acc - The accuracy of the GPS reading in metres. May be
144
144
  * ignored if lower than the specified minimum accuracy.
145
145
  */
146
- fakeGps(t, e, i = null, r = 0) {
147
- i !== null && this.setElevation(i), p(this, f, j).call(this, {
146
+ fakeGps(t, e, n = null, r = 0) {
147
+ n !== null && this.setElevation(n), f(this, p, F).call(this, {
148
148
  coords: {
149
149
  longitude: t,
150
150
  latitude: e,
@@ -163,12 +163,12 @@ class st {
163
163
  * @return {Array} a two member array containing the WebGL x and z coordinates
164
164
  */
165
165
  lonLatToWorldCoords(t, e) {
166
- const i = n(this, I).project(t, e);
167
- if (n(this, y))
168
- i[0] -= n(this, y)[0], i[1] -= n(this, y)[1];
166
+ const n = s(this, D).project(t, e);
167
+ if (s(this, M))
168
+ n[0] -= s(this, M)[0], n[1] -= s(this, M)[1];
169
169
  else
170
170
  throw "No initial position determined";
171
- return [i[0], -i[1]];
171
+ return [n[0], -n[1]];
172
172
  }
173
173
  /**
174
174
  * Add a new AR object at a given latitude, longitude and elevation.
@@ -180,13 +180,13 @@ class st {
180
180
  * @param {Object} properties - properties describing the object (for example,
181
181
  * the contents of the GeoJSON properties field).
182
182
  */
183
- add(t, e, i, r, d = {}) {
183
+ add(t, e, n, r, d = {}) {
184
184
  var g;
185
- t.properties = d, p(this, f, F).call(this, t, e, i, r), this.scene.add(t), (g = n(this, M)) == null || g.sendData("/object/new", {
185
+ t.properties = d, f(this, p, j).call(this, t, e, n, r), this.scene.add(t), (g = s(this, w)) == null || g.sendData("/object/new", {
186
186
  position: t.position,
187
187
  x: t.position.x,
188
188
  z: t.position.z,
189
- session: n(this, T),
189
+ session: s(this, O),
190
190
  properties: d
191
191
  });
192
192
  }
@@ -211,54 +211,55 @@ class st {
211
211
  * @listens LocationBased#gpserror
212
212
  */
213
213
  on(t, e) {
214
- n(this, O)[t] = e;
214
+ s(this, A)[t] = e;
215
215
  }
216
216
  }
217
- I = new WeakMap(), O = new WeakMap(), P = new WeakMap(), x = new WeakMap(), L = new WeakMap(), A = new WeakMap(), y = new WeakMap(), b = new WeakMap(), T = new WeakMap(), M = new WeakMap(), f = new WeakSet(), F = function(t, e, i, r) {
218
- const d = this.lonLatToWorldCoords(e, i);
217
+ D = new WeakMap(), A = new WeakMap(), b = new WeakMap(), H = new WeakMap(), R = new WeakMap(), P = new WeakMap(), M = new WeakMap(), T = new WeakMap(), O = new WeakMap(), w = new WeakMap(), p = new WeakSet(), j = function(t, e, n, r) {
218
+ const d = this.lonLatToWorldCoords(e, n);
219
219
  r !== void 0 && (t.position.y = r), [t.position.x, t.position.z] = d;
220
- }, V = function(t, e) {
221
- u(this, y, n(this, I).project(t, e));
222
- }, j = function(t) {
223
- var i, r, d;
220
+ }, Q = function(t, e) {
221
+ u(this, M, s(this, D).project(t, e));
222
+ }, F = function(t) {
223
+ var n, r, d;
224
224
  let e = Number.MAX_VALUE;
225
- W(this, b)._++, (i = n(this, M)) == null || i.sendData("/gps/new", {
226
- gpsCount: n(this, b),
225
+ k(this, T)._++, (n = s(this, w)) == null || n.sendData("/gps/new", {
226
+ gpsCount: s(this, T),
227
227
  lat: t.coords.latitude,
228
228
  lon: t.coords.longitude,
229
229
  acc: t.coords.accuracy,
230
- session: n(this, T)
231
- }), t.coords.accuracy <= n(this, L) && (n(this, P) === null ? u(this, P, {
230
+ session: s(this, O)
231
+ }), t.coords.accuracy <= s(this, R) && (s(this, b) === null ? u(this, b, {
232
232
  latitude: t.coords.latitude,
233
233
  longitude: t.coords.longitude
234
- }) : e = p(this, f, Y).call(this, n(this, P), t.coords), e >= n(this, x) && (n(this, P).longitude = t.coords.longitude, n(this, P).latitude = t.coords.latitude, n(this, y) || (p(this, f, V).call(this, t.coords.longitude, t.coords.latitude), (r = n(this, M)) == null || r.sendData("/worldorigin/new", {
235
- gpsCount: n(this, b),
234
+ }) : e = f(this, p, B).call(this, s(this, b), t.coords), e >= s(this, H) && (s(this, b).longitude = t.coords.longitude, s(this, b).latitude = t.coords.latitude, s(this, M) || (f(this, p, Q).call(this, t.coords.longitude, t.coords.latitude), (r = s(this, w)) == null || r.sendData("/worldorigin/new", {
235
+ gpsCount: s(this, T),
236
236
  lat: t.coords.latitude,
237
237
  lon: t.coords.longitude,
238
- session: n(this, T),
239
- initialPosition: n(this, y)
240
- })), p(this, f, F).call(this, this.camera, t.coords.longitude, t.coords.latitude), (d = n(this, M)) == null || d.sendData("/gps/accepted", {
241
- gpsCount: n(this, b),
238
+ session: s(this, O),
239
+ initialPosition: s(this, M)
240
+ })), f(this, p, j).call(this, this.camera, t.coords.longitude, t.coords.latitude), (d = s(this, w)) == null || d.sendData("/gps/accepted", {
241
+ gpsCount: s(this, T),
242
242
  cameraX: this.camera.position.x,
243
243
  cameraZ: this.camera.position.z,
244
- session: n(this, T),
244
+ session: s(this, O),
245
245
  distMoved: e
246
- }), n(this, O).gpsupdate && n(this, O).gpsupdate(t, e)));
246
+ }), s(this, A).gpsupdate && s(this, A).gpsupdate(t, e)));
247
247
  }, /**
248
248
  * Calculate haversine distance between two lat/lon pairs.
249
249
  *
250
250
  * Taken from original A-Frame AR.js location-based components
251
251
  */
252
- Y = function(t, e) {
253
- const i = D.MathUtils.degToRad(e.longitude - t.longitude), r = D.MathUtils.degToRad(e.latitude - t.latitude), d = Math.sin(r / 2) * Math.sin(r / 2) + Math.cos(D.MathUtils.degToRad(t.latitude)) * Math.cos(D.MathUtils.degToRad(e.latitude)) * (Math.sin(i / 2) * Math.sin(i / 2));
252
+ B = function(t, e) {
253
+ const n = L.MathUtils.degToRad(e.longitude - t.longitude), r = L.MathUtils.degToRad(e.latitude - t.latitude), d = Math.sin(r / 2) * Math.sin(r / 2) + Math.cos(L.MathUtils.degToRad(t.latitude)) * Math.cos(L.MathUtils.degToRad(e.latitude)) * (Math.sin(n / 2) * Math.sin(n / 2));
254
254
  return 2 * Math.atan2(Math.sqrt(d), Math.sqrt(1 - d)) * 6371e3;
255
255
  };
256
- class ot {
256
+ class J {
257
257
  /**
258
258
  * Create a Webcam.
259
- * @options {Object} - options to use for initialising the camera. Currently
260
- * width and height properties are understood as well as onVideoStarted(),
261
- * a *required* callback which runs when the camera has been initialised.
259
+ * @param options {Object} - options to use for initialising the camera.
260
+ * Currently idealWidth and idealHeight properties are understood as well as
261
+ * onVideoStarted(), a *required* callback which runs when the camera has
262
+ * been initialised.
262
263
  * This takes a THREE.VideoTexture as an argument which can be used to set
263
264
  * the background of your three.js scene within a callback.
264
265
  * @param {string} videoElementSelector - selector to obtain the HTML video
@@ -266,21 +267,21 @@ class ot {
266
267
  * undefined), a video element will be created.
267
268
  */
268
269
  constructor(t = {}, e) {
269
- this.sceneWebcam = new D.Scene();
270
- let i;
271
- if (e ? i = document.querySelector(e) : (i = document.createElement("video"), i.setAttribute("autoplay", !0), i.setAttribute("playsinline", !0), i.style.display = "none", document.body.appendChild(i)), this.texture = new D.VideoTexture(i), navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
270
+ this.sceneWebcam = new L.Scene();
271
+ let n;
272
+ if (e ? n = document.querySelector(e) : (n = document.createElement("video"), n.setAttribute("autoplay", !0), n.setAttribute("playsinline", !0), n.style.display = "none", document.body.appendChild(n)), this.texture = new L.VideoTexture(n), navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
272
273
  const r = {
273
274
  video: {
274
- width: { ideal: t.width || 1280 },
275
- height: { ideal: t.height || 720 },
275
+ width: { ideal: t.idealWidth || 1280 },
276
+ height: { ideal: t.idealHeight || 720 },
276
277
  facingMode: "environment"
277
278
  }
278
279
  };
279
280
  navigator.mediaDevices.getUserMedia(r).then((d) => {
280
- i.addEventListener("loadedmetadata", () => {
281
+ n.addEventListener("loadedmetadata", () => {
281
282
  var g;
282
- i.setAttribute("width", i.videoWidth), i.setAttribute("height", i.videoHeight), i.play(), (g = t.onVideoStarted) == null || g.call(this, this.texture);
283
- }), i.srcObject = d;
283
+ n.setAttribute("width", n.videoWidth), n.setAttribute("height", n.videoHeight), n.play(), (g = t.onVideoStarted) == null || g.call(this, this.texture);
284
+ }), n.srcObject = d;
284
285
  }).catch((d) => {
285
286
  setTimeout(() => {
286
287
  alert(
@@ -303,8 +304,8 @@ Message: ` + d.message
303
304
  this.texture.dispose();
304
305
  }
305
306
  }
306
- const k = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, $ = new Z(0, 0, 1), z = new N(), J = new _(), tt = new _(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)), et = { type: "change" };
307
- class at extends B {
307
+ const q = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, tt = new Y(0, 0, 1), G = new U(), et = new _(), it = new _(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)), nt = { type: "change" };
308
+ class st extends Z {
308
309
  /**
309
310
  * Create an instance of DeviceOrientationControls.
310
311
  * @param {Object} object - the object to attach the controls to
@@ -314,19 +315,19 @@ class at extends B {
314
315
  super(), window.isSecureContext === !1 && console.error(
315
316
  "THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)"
316
317
  );
317
- const e = this, i = 1e-6, r = new _();
318
+ const e = this, n = 1e-6, r = new _();
318
319
  this.object = t, this.object.rotation.reorder("YXZ"), this.enabled = !0, this.deviceOrientation = null, this.screenOrientation = 0, this.alphaOffset = 0, this.initialOffset = null, this.TWO_PI = 2 * Math.PI, this.HALF_PI = 0.5 * Math.PI, this.orientationChangeEventName = "ondeviceorientationabsolute" in window ? "deviceorientationabsolute" : "deviceorientation", this.smoothingFactor = 1;
319
320
  const d = function({
320
- alpha: s,
321
+ alpha: o,
321
322
  beta: c,
322
- gamma: h,
323
- webkitCompassHeading: o
323
+ gamma: l,
324
+ webkitCompassHeading: a
324
325
  }) {
325
- if (k) {
326
- const l = 360 - o;
327
- e.alphaOffset = C.degToRad(l - s), e.deviceOrientation = { alpha: s, beta: c, gamma: h, webkitCompassHeading: o };
326
+ if (q) {
327
+ const h = 360 - a;
328
+ e.alphaOffset = C.degToRad(h - o), e.deviceOrientation = { alpha: o, beta: c, gamma: l, webkitCompassHeading: a };
328
329
  } else
329
- s < 0 && (s += 360), e.deviceOrientation = { alpha: s, beta: c, gamma: h };
330
+ o < 0 && (o += 360), e.deviceOrientation = { alpha: o, beta: c, gamma: l };
330
331
  window.dispatchEvent(
331
332
  new CustomEvent("camera-rotation-change", {
332
333
  detail: { cameraRotation: t.rotation }
@@ -334,22 +335,22 @@ class at extends B {
334
335
  );
335
336
  }, g = function() {
336
337
  e.screenOrientation = window.orientation || 0;
337
- }, R = function(s, c, h, o, l) {
338
- z.set(h, c, -o, "YXZ"), s.setFromEuler(z), s.multiply(tt), s.multiply(J.setFromAxisAngle($, -l));
338
+ }, x = function(o, c, l, a, h) {
339
+ G.set(l, c, -a, "YXZ"), o.setFromEuler(G), o.multiply(it), o.multiply(et.setFromAxisAngle(tt, -h));
339
340
  };
340
341
  this.connect = function() {
341
- g(), window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then((s) => {
342
- s === "granted" && (window.addEventListener(
342
+ g(), window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then((o) => {
343
+ o === "granted" && (window.addEventListener(
343
344
  "orientationchange",
344
345
  g
345
346
  ), window.addEventListener(
346
347
  e.orientationChangeEventName,
347
348
  d
348
349
  ));
349
- }).catch(function(s) {
350
+ }).catch(function(o) {
350
351
  console.error(
351
352
  "THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",
352
- s
353
+ o
353
354
  );
354
355
  }) : (window.addEventListener(
355
356
  "orientationchange",
@@ -366,82 +367,82 @@ class at extends B {
366
367
  e.orientationChangeEventName,
367
368
  d
368
369
  ), e.enabled = !1, e.initialOffset = !1, e.deviceOrientation = null;
369
- }, this.update = function({ theta: s = 0 } = { theta: 0 }) {
370
+ }, this.update = function({ theta: o = 0 } = { theta: 0 }) {
370
371
  if (e.enabled === !1) return;
371
372
  const c = e.deviceOrientation;
372
373
  if (c) {
373
- let h = c.alpha ? C.degToRad(c.alpha) + e.alphaOffset : 0, o = c.beta ? C.degToRad(c.beta) : 0, l = c.gamma ? C.degToRad(c.gamma) : 0;
374
- const H = e.screenOrientation ? C.degToRad(e.screenOrientation) : 0;
375
- if (k) {
374
+ let l = c.alpha ? C.degToRad(c.alpha) + e.alphaOffset : 0, a = c.beta ? C.degToRad(c.beta) : 0, h = c.gamma ? C.degToRad(c.gamma) : 0;
375
+ const I = e.screenOrientation ? C.degToRad(e.screenOrientation) : 0;
376
+ if (q) {
376
377
  const v = new _();
377
- R(v, h, o, l, H);
378
- const w = new N().setFromQuaternion(
378
+ x(v, l, a, h, I);
379
+ const y = new U().setFromQuaternion(
379
380
  v,
380
381
  "YXZ"
381
382
  );
382
- console.log(w.x, w.y, w.z), w.y = C.degToRad(
383
+ console.log(y.x, y.y, y.z), y.y = C.degToRad(
383
384
  360 - c.webkitCompassHeading
384
- ), v.setFromEuler(w), e.object.quaternion.copy(v);
385
+ ), v.setFromEuler(y), e.object.quaternion.copy(v);
385
386
  } else {
386
387
  if (this.smoothingFactor < 1) {
387
388
  if (this.lastOrientation) {
388
389
  const v = this.smoothingFactor;
389
- h = this._getSmoothedAngle(
390
- h,
390
+ l = this._getSmoothedAngle(
391
+ l,
391
392
  this.lastOrientation.alpha,
392
393
  v
393
- ), o = this._getSmoothedAngle(
394
- o + Math.PI,
394
+ ), a = this._getSmoothedAngle(
395
+ a + Math.PI,
395
396
  this.lastOrientation.beta,
396
397
  v
397
- ), l = this._getSmoothedAngle(
398
- l + this.HALF_PI,
398
+ ), h = this._getSmoothedAngle(
399
+ h + this.HALF_PI,
399
400
  this.lastOrientation.gamma,
400
401
  v,
401
402
  Math.PI
402
403
  );
403
404
  } else
404
- o += Math.PI, l += this.HALF_PI;
405
+ a += Math.PI, h += this.HALF_PI;
405
406
  this.lastOrientation = {
406
- alpha: h,
407
- beta: o,
408
- gamma: l
407
+ alpha: l,
408
+ beta: a,
409
+ gamma: h
409
410
  };
410
411
  }
411
- R(
412
+ x(
412
413
  e.object.quaternion,
413
- h + s,
414
- this.smoothingFactor < 1 ? o - Math.PI : o,
415
- this.smoothingFactor < 1 ? l - this.HALF_PI : l,
416
- H
414
+ l + o,
415
+ this.smoothingFactor < 1 ? a - Math.PI : a,
416
+ this.smoothingFactor < 1 ? h - this.HALF_PI : h,
417
+ I
417
418
  );
418
419
  }
419
- 8 * (1 - r.dot(e.object.quaternion)) > i && (r.copy(e.object.quaternion), e.dispatchEvent(et));
420
+ 8 * (1 - r.dot(e.object.quaternion)) > n && (r.copy(e.object.quaternion), e.dispatchEvent(nt));
420
421
  }
421
- }, this._orderAngle = function(s, c, h = this.TWO_PI) {
422
- return c > s && Math.abs(c - s) < h / 2 || s > c && Math.abs(c - s) > h / 2 ? { left: s, right: c } : { left: c, right: s };
423
- }, this._getSmoothedAngle = function(s, c, h, o = this.TWO_PI) {
424
- const l = this._orderAngle(s, c, o), H = l.left, v = l.right;
425
- l.left = 0, l.right -= H, l.right < 0 && (l.right += o);
426
- let w = v == c ? (1 - h) * l.right + h * l.left : h * l.right + (1 - h) * l.left;
427
- return w += H, w >= o && (w -= o), w;
422
+ }, this._orderAngle = function(o, c, l = this.TWO_PI) {
423
+ return c > o && Math.abs(c - o) < l / 2 || o > c && Math.abs(c - o) > l / 2 ? { left: o, right: c } : { left: c, right: o };
424
+ }, this._getSmoothedAngle = function(o, c, l, a = this.TWO_PI) {
425
+ const h = this._orderAngle(o, c, a), I = h.left, v = h.right;
426
+ h.left = 0, h.right -= I, h.right < 0 && (h.right += a);
427
+ let y = v == c ? (1 - l) * h.right + l * h.left : l * h.right + (1 - l) * h.left;
428
+ return y += I, y >= a && (y -= a), y;
428
429
  }, this.updateAlphaOffset = function() {
429
430
  e.initialOffset = !1;
430
431
  }, this.dispose = function() {
431
432
  e.disconnect();
432
433
  }, this.getAlpha = function() {
433
- const { deviceOrientation: s } = e;
434
- return s && s.alpha ? C.degToRad(s.alpha) + e.alphaOffset : 0;
434
+ const { deviceOrientation: o } = e;
435
+ return o && o.alpha ? C.degToRad(o.alpha) + e.alphaOffset : 0;
435
436
  }, this.getBeta = function() {
436
- const { deviceOrientation: s } = e;
437
- return s && s.beta ? C.degToRad(s.beta) : 0;
437
+ const { deviceOrientation: o } = e;
438
+ return o && o.beta ? C.degToRad(o.beta) : 0;
438
439
  }, window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? this.initPermissionDialog() : this.connect();
439
440
  }
440
441
  // Provide gesture before initialising device orientation controls
441
442
  // From PR #659 on the main AR.js repo
442
443
  // Thanks to @ma2yama
443
444
  initPermissionDialog() {
444
- const t = document.createElement("div"), e = document.createElement("div"), i = document.createElement("div"), r = document.createElement("div");
445
+ const t = document.createElement("div"), e = document.createElement("div"), n = document.createElement("div"), r = document.createElement("div");
445
446
  document.body.appendChild(t);
446
447
  const d = {
447
448
  display: "flex",
@@ -460,32 +461,32 @@ class at extends B {
460
461
  borderRadius: "3px",
461
462
  width: "36rem",
462
463
  height: "24rem"
463
- }, R = {
464
+ }, x = {
464
465
  width: "100%",
465
466
  height: "70%",
466
467
  display: "flex",
467
468
  justifyContent: "center",
468
469
  alignItems: "center"
469
- }, s = {
470
+ }, o = {
470
471
  display: "inline-flex",
471
472
  width: "100%",
472
473
  height: "30%",
473
474
  justifyContent: "center",
474
475
  alignItems: "center"
475
476
  };
476
- for (let o in d)
477
- t.style[o] = d[o];
478
- for (let o in g)
479
- e.style[o] = g[o];
480
- for (let o in R)
481
- i.style[o] = R[o];
482
- for (let o in s)
483
- r.style[o] = s[o];
484
- t.appendChild(e), e.appendChild(i), e.appendChild(r), i.innerHTML = '<div style="font-size: 24pt; margin: 1rem;">This immersive website requires access to your device motion sensors.</div>';
477
+ for (let a in d)
478
+ t.style[a] = d[a];
479
+ for (let a in g)
480
+ e.style[a] = g[a];
481
+ for (let a in x)
482
+ n.style[a] = x[a];
483
+ for (let a in o)
484
+ r.style[a] = o[a];
485
+ t.appendChild(e), e.appendChild(n), e.appendChild(r), n.innerHTML = '<div style="font-size: 24pt; margin: 1rem;">This immersive website requires access to your device motion sensors.</div>';
485
486
  const c = () => {
486
487
  this.connect(), t.style.display = "none";
487
- }, h = document.createElement("button");
488
- h.addEventListener("click", c), h.style.width = "50%", h.style.height = "80%", h.style.fontSize = "20pt", h.appendChild(document.createTextNode("OK")), r.appendChild(h), document.body.appendChild(t);
488
+ }, l = document.createElement("button");
489
+ l.addEventListener("click", c), l.style.width = "50%", l.style.height = "80%", l.style.fontSize = "20pt", l.appendChild(document.createTextNode("OK")), r.appendChild(l), document.body.appendChild(t);
489
490
  }
490
491
  }
491
492
  class rt {
@@ -495,7 +496,7 @@ class rt {
495
496
  * events will be handled.
496
497
  */
497
498
  constructor(t) {
498
- this.raycaster = new D.Raycaster(), this.normalisedMousePosition = new D.Vector2(null, null), t.domElement.addEventListener("click", (e) => {
499
+ this.raycaster = new L.Raycaster(), this.normalisedMousePosition = new L.Vector2(null, null), t.domElement.addEventListener("click", (e) => {
499
500
  this.normalisedMousePosition.set(
500
501
  e.clientX / t.domElement.clientWidth * 2 - 1,
501
502
  -(e.clientY / t.domElement.clientHeight * 2) + 1
@@ -513,18 +514,146 @@ class rt {
513
514
  raycast(t, e) {
514
515
  if (this.normalisedMousePosition.x !== null && this.normalisedMousePosition.y !== null) {
515
516
  this.raycaster.setFromCamera(this.normalisedMousePosition, t);
516
- const i = this.raycaster.intersectObjects(e.children, !1);
517
- return this.normalisedMousePosition.set(null, null), i;
517
+ const n = this.raycaster.intersectObjects(e.children, !1);
518
+ return this.normalisedMousePosition.set(null, null), n;
518
519
  }
519
520
  return [];
520
521
  }
521
522
  }
522
- const ct = "0.0.8-webcam-2";
523
+ AFRAME.registerComponent("locar-webcam", {
524
+ schema: {
525
+ idealWidth: {
526
+ type: "number",
527
+ default: 1024
528
+ },
529
+ idealHeight: {
530
+ type: "number",
531
+ default: 768
532
+ },
533
+ videoElement: {
534
+ type: "string",
535
+ default: ""
536
+ }
537
+ },
538
+ init: function() {
539
+ new J({
540
+ idealWidth: this.data.idealWidth,
541
+ idealHeight: this.data.idealHeight,
542
+ onVideoStarted: (i) => {
543
+ this.el.object3D.background = i;
544
+ }
545
+ }, this.data.videoElement || null);
546
+ }
547
+ });
548
+ AFRAME.registerComponent("locar-camera", {
549
+ schema: {
550
+ simulateLatitude: {
551
+ type: "number",
552
+ default: 0
553
+ },
554
+ simulateLongitude: {
555
+ type: "number",
556
+ default: 0
557
+ },
558
+ simulateAltitude: {
559
+ type: "number",
560
+ default: -Number.MAX_VALUE
561
+ },
562
+ positionMinAccuracy: {
563
+ type: "number",
564
+ default: 100
565
+ }
566
+ },
567
+ init: function() {
568
+ this.locar = new K(
569
+ this.el.sceneEl.object3D,
570
+ this.el.object3D
571
+ ), this.locar.on("gpsupdate", (i, t) => {
572
+ this.el.emit("gpsupdate", {
573
+ position: i,
574
+ distMoved: t
575
+ });
576
+ }), this.locar.on("gpserror", (i) => {
577
+ const t = [
578
+ "User denied access to GPS.",
579
+ "GPS satellites not available.",
580
+ "Timeout communicating with GPS satellites - try moving to a more open area."
581
+ ];
582
+ i >= 1 && i <= 3 ? alert(t[i - 1]) : alert(`Unknown geolocation error code ${i}.`);
583
+ }), this._isMobile() && (this.deviceOrientationControls = new st(this.el.object3D));
584
+ },
585
+ update: function(i) {
586
+ this.locar.setGpsOptions({
587
+ gpsMinAccuracy: this.data.positionMinAccuracy,
588
+ gpsMinDistance: this.data.gpsMinDistance
589
+ }), (this.data.simulateLatitude !== 0 || this.data.simulateLongitude !== 0) && (this.data.simulateLatitude != (i == null ? void 0 : i.simulateLatitude) || this.data.simulateLongitude != (i == null ? void 0 : i.simulateLongitude)) && (this.locar.stopGps(), this.locar.fakeGps(
590
+ this.data.simulateLongitude,
591
+ this.data.simulateLatitude
592
+ ), this.data.simulateLongitude = 0, this.data.simulateLatitude = 0), this.data.simulateAltitude > -Number.MAX_VALUE && this.locar.setElevation(this.data.simulateAltitude + 1.6);
593
+ },
594
+ play: function() {
595
+ this.locar.startGps();
596
+ },
597
+ pause: function() {
598
+ this.locar.stopGps();
599
+ },
600
+ /**
601
+ * Convert longitude and latitude to three.js/WebGL world coordinates.
602
+ * Uses the specified projection, and negates the northing (in typical
603
+ * projections, northings increase northwards, but in the WebGL coordinate
604
+ * system, we face negative z if the camera is at the origin with default
605
+ * rotation).
606
+ * @param {number} lon - The longitude.
607
+ * @param {number} lat - The latitude.
608
+ * @return {Array} a two member array containing the WebGL x and z coordinates
609
+ */
610
+ lonLatToWorldCoords: function(i, t) {
611
+ return this.locar.lonLatToWorldCoords(i, t);
612
+ },
613
+ tick: function() {
614
+ var i;
615
+ (i = this.deviceOrientationControls) == null || i.update();
616
+ },
617
+ _isMobile: function() {
618
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
619
+ navigator.userAgent
620
+ ) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1;
621
+ }
622
+ });
623
+ AFRAME.registerComponent("locar-entity-place", {
624
+ schema: {
625
+ latitude: {
626
+ type: "number",
627
+ default: 0
628
+ },
629
+ longitude: {
630
+ type: "number",
631
+ default: 0
632
+ }
633
+ },
634
+ init: function() {
635
+ const i = this.el.sceneEl.querySelector("[locar-camera]");
636
+ this.locarCamera = i.components["locar-camera"];
637
+ },
638
+ update: function(i) {
639
+ this.locarCamera || console.error("Cannot update locar-entity-place without a locar-camera component on the scene camera.");
640
+ const t = this.locarCamera.lonLatToWorldCoords(
641
+ this.data.longitude,
642
+ this.data.latitude
643
+ );
644
+ this.el.object3D.position.set(
645
+ t[0],
646
+ this.el.object3D.position.y,
647
+ t[1]
648
+ );
649
+ }
650
+ });
651
+ const ct = "0.0.10";
523
652
  export {
524
653
  rt as ClickHandler,
525
- at as DeviceOrientationControls,
526
- st as LocationBased,
527
- K as SphMercProjection,
528
- ot as Webcam,
654
+ st as DeviceOrientationControls,
655
+ K as LocationBased,
656
+ $ as SphMercProjection,
657
+ J as Webcam,
529
658
  ct as version
530
659
  };
package/dist/locar.umd.js CHANGED
@@ -1,3 +1,3 @@
1
- (function(a,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],n):(a=typeof globalThis<"u"?globalThis:a||self,n(a.locar={},a.THREE))})(this,function(a,n){"use strict";var z=a=>{throw TypeError(a)};var q=(a,n,f)=>n.has(a)||z("Cannot "+f);var s=(a,n,f)=>(q(a,n,"read from private field"),f?f.call(a):n.get(a)),v=(a,n,f)=>n.has(a)?z("Cannot add the same private member more than once"):n instanceof WeakSet?n.add(a):n.set(a,f),g=(a,n,f,p)=>(q(a,n,"write to private field"),p?p.call(a,f):n.set(a,f),f),y=(a,n,f)=>(q(a,n,"access private method"),f);var N=(a,n,f,p)=>({set _(U){g(a,n,U,f)},get _(){return s(a,n,p)}});var w,Q,G,X,V,E,D,I,S,j,L,O,_,T,P,M,W,Y,k,Z;function f(b){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(b){for(const e in b)if(e!=="default"){const i=Object.getOwnPropertyDescriptor(b,e);Object.defineProperty(t,e,i.get?i:{enumerable:!0,get:()=>b[e]})}}return t.default=b,Object.freeze(t)}const p=f(n);class U{constructor(){v(this,w);this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}project(t,e){return[y(this,w,Q).call(this,t),y(this,w,G).call(this,e)]}unproject(t){return[y(this,w,X).call(this,t[0]),y(this,w,V).call(this,t[1])]}getID(){return"epsg:3857"}}w=new WeakSet,Q=function(t){return t/180*this.HALF_EARTH},G=function(t){var e=Math.log(Math.tan((90+t)*Math.PI/360))/(Math.PI/180);return e*this.HALF_EARTH/180},X=function(t){return t/this.HALF_EARTH*180},V=function(t){var e=t/this.HALF_EARTH*180;return e=180/Math.PI*(2*Math.atan(Math.exp(e*Math.PI/180))-Math.PI/2),e};class B{constructor(t,e,i={},d=null){v(this,M);v(this,E);v(this,D);v(this,I);v(this,S);v(this,j);v(this,L);v(this,O);v(this,_);v(this,T);v(this,P);this.scene=t,this.camera=e,g(this,E,new U),g(this,D,{}),g(this,I,null),g(this,S,0),g(this,j,100),g(this,L,null),this.setGpsOptions(i),g(this,O,null),g(this,_,0),g(this,T,0),g(this,P,d)}setProjection(t){g(this,E,t)}setGpsOptions(t={}){t.gpsMinDistance!==void 0&&g(this,S,t.gpsMinDistance),t.gpsMinAccuracy!==void 0&&g(this,j,t.gpsMinAccuracy)}async startGps(){if(s(this,P)){const e=await(await s(this,P).sendData("/gps/start",{gpsMinDistance:s(this,S),gpsMinAccuracy:s(this,j)})).json();g(this,T,e.session)}return s(this,L)===null?(g(this,L,navigator.geolocation.watchPosition(t=>{y(this,M,k).call(this,t)},t=>{s(this,D).gpserror?s(this,D).gpserror(t.code):alert(`GPS error: code ${t.code}`)},{enableHighAccuracy:!0})),!0):!1}stopGps(){return s(this,L)!==null?(navigator.geolocation.clearWatch(s(this,L)),g(this,L,null),!0):!1}fakeGps(t,e,i=null,d=0){i!==null&&this.setElevation(i),y(this,M,k).call(this,{coords:{longitude:t,latitude:e,accuracy:d}})}lonLatToWorldCoords(t,e){const i=s(this,E).project(t,e);if(s(this,O))i[0]-=s(this,O)[0],i[1]-=s(this,O)[1];else throw"No initial position determined";return[i[0],-i[1]]}add(t,e,i,d,h={}){var m;t.properties=h,y(this,M,W).call(this,t,e,i,d),this.scene.add(t),(m=s(this,P))==null||m.sendData("/object/new",{position:t.position,x:t.position.x,z:t.position.z,session:s(this,T),properties:h})}setElevation(t){this.camera.position.y=t}on(t,e){s(this,D)[t]=e}}E=new WeakMap,D=new WeakMap,I=new WeakMap,S=new WeakMap,j=new WeakMap,L=new WeakMap,O=new WeakMap,_=new WeakMap,T=new WeakMap,P=new WeakMap,M=new WeakSet,W=function(t,e,i,d){const h=this.lonLatToWorldCoords(e,i);d!==void 0&&(t.position.y=d),[t.position.x,t.position.z]=h},Y=function(t,e){g(this,O,s(this,E).project(t,e))},k=function(t){var i,d,h;let e=Number.MAX_VALUE;N(this,_)._++,(i=s(this,P))==null||i.sendData("/gps/new",{gpsCount:s(this,_),lat:t.coords.latitude,lon:t.coords.longitude,acc:t.coords.accuracy,session:s(this,T)}),t.coords.accuracy<=s(this,j)&&(s(this,I)===null?g(this,I,{latitude:t.coords.latitude,longitude:t.coords.longitude}):e=y(this,M,Z).call(this,s(this,I),t.coords),e>=s(this,S)&&(s(this,I).longitude=t.coords.longitude,s(this,I).latitude=t.coords.latitude,s(this,O)||(y(this,M,Y).call(this,t.coords.longitude,t.coords.latitude),(d=s(this,P))==null||d.sendData("/worldorigin/new",{gpsCount:s(this,_),lat:t.coords.latitude,lon:t.coords.longitude,session:s(this,T),initialPosition:s(this,O)})),y(this,M,W).call(this,this.camera,t.coords.longitude,t.coords.latitude),(h=s(this,P))==null||h.sendData("/gps/accepted",{gpsCount:s(this,_),cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:s(this,T),distMoved:e}),s(this,D).gpsupdate&&s(this,D).gpsupdate(t,e)))},Z=function(t,e){const i=p.MathUtils.degToRad(e.longitude-t.longitude),d=p.MathUtils.degToRad(e.latitude-t.latitude),h=Math.sin(d/2)*Math.sin(d/2)+Math.cos(p.MathUtils.degToRad(t.latitude))*Math.cos(p.MathUtils.degToRad(e.latitude))*(Math.sin(i/2)*Math.sin(i/2));return 2*Math.atan2(Math.sqrt(h),Math.sqrt(1-h))*6371e3};class K{constructor(t={},e){this.sceneWebcam=new p.Scene;let i;if(e?i=document.querySelector(e):(i=document.createElement("video"),i.setAttribute("autoplay",!0),i.setAttribute("playsinline",!0),i.style.display="none",document.body.appendChild(i)),this.texture=new p.VideoTexture(i),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia){const d={video:{width:{ideal:t.width||1280},height:{ideal:t.height||720},facingMode:"environment"}};navigator.mediaDevices.getUserMedia(d).then(h=>{i.addEventListener("loadedmetadata",()=>{var m;i.setAttribute("width",i.videoWidth),i.setAttribute("height",i.videoHeight),i.play(),(m=t.onVideoStarted)==null||m.call(this,this.texture)}),i.srcObject=h}).catch(h=>{setTimeout(()=>{alert(`Webcam Error
2
- Name: `+h.name+`
3
- Message: `+h.message)},1e3)})}else setTimeout(()=>{alert("sorry - media devices API not supported")},1e3)}dispose(){this.texture.dispose()}}const H=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,$=new n.Vector3(0,0,1),R=new n.Euler,J=new n.Quaternion,tt=new n.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5)),et={type:"change"};class it extends n.EventDispatcher{constructor(t){super(),window.isSecureContext===!1&&console.error("THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)");const e=this,i=1e-6,d=new n.Quaternion;this.object=t,this.object.rotation.reorder("YXZ"),this.enabled=!0,this.deviceOrientation=null,this.screenOrientation=0,this.alphaOffset=0,this.initialOffset=null,this.TWO_PI=2*Math.PI,this.HALF_PI=.5*Math.PI,this.orientationChangeEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation",this.smoothingFactor=1;const h=function({alpha:o,beta:c,gamma:l,webkitCompassHeading:r}){if(H){const u=360-r;e.alphaOffset=n.MathUtils.degToRad(u-o),e.deviceOrientation={alpha:o,beta:c,gamma:l,webkitCompassHeading:r}}else o<0&&(o+=360),e.deviceOrientation={alpha:o,beta:c,gamma:l};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.rotation}}))},m=function(){e.screenOrientation=window.orientation||0},x=function(o,c,l,r,u){R.set(l,c,-r,"YXZ"),o.setFromEuler(R),o.multiply(tt),o.multiply(J.setFromAxisAngle($,-u))};this.connect=function(){m(),window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(o=>{o==="granted"&&(window.addEventListener("orientationchange",m),window.addEventListener(e.orientationChangeEventName,h))}).catch(function(o){console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",o)}):(window.addEventListener("orientationchange",m),window.addEventListener(e.orientationChangeEventName,h)),e.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",m),window.removeEventListener(e.orientationChangeEventName,h),e.enabled=!1,e.initialOffset=!1,e.deviceOrientation=null},this.update=function({theta:o=0}={theta:0}){if(e.enabled===!1)return;const c=e.deviceOrientation;if(c){let l=c.alpha?n.MathUtils.degToRad(c.alpha)+e.alphaOffset:0,r=c.beta?n.MathUtils.degToRad(c.beta):0,u=c.gamma?n.MathUtils.degToRad(c.gamma):0;const F=e.screenOrientation?n.MathUtils.degToRad(e.screenOrientation):0;if(H){const A=new n.Quaternion;x(A,l,r,u,F);const C=new n.Euler().setFromQuaternion(A,"YXZ");console.log(C.x,C.y,C.z),C.y=n.MathUtils.degToRad(360-c.webkitCompassHeading),A.setFromEuler(C),e.object.quaternion.copy(A)}else{if(this.smoothingFactor<1){if(this.lastOrientation){const A=this.smoothingFactor;l=this._getSmoothedAngle(l,this.lastOrientation.alpha,A),r=this._getSmoothedAngle(r+Math.PI,this.lastOrientation.beta,A),u=this._getSmoothedAngle(u+this.HALF_PI,this.lastOrientation.gamma,A,Math.PI)}else r+=Math.PI,u+=this.HALF_PI;this.lastOrientation={alpha:l,beta:r,gamma:u}}x(e.object.quaternion,l+o,this.smoothingFactor<1?r-Math.PI:r,this.smoothingFactor<1?u-this.HALF_PI:u,F)}8*(1-d.dot(e.object.quaternion))>i&&(d.copy(e.object.quaternion),e.dispatchEvent(et))}},this._orderAngle=function(o,c,l=this.TWO_PI){return c>o&&Math.abs(c-o)<l/2||o>c&&Math.abs(c-o)>l/2?{left:o,right:c}:{left:c,right:o}},this._getSmoothedAngle=function(o,c,l,r=this.TWO_PI){const u=this._orderAngle(o,c,r),F=u.left,A=u.right;u.left=0,u.right-=F,u.right<0&&(u.right+=r);let C=A==c?(1-l)*u.right+l*u.left:l*u.right+(1-l)*u.left;return C+=F,C>=r&&(C-=r),C},this.updateAlphaOffset=function(){e.initialOffset=!1},this.dispose=function(){e.disconnect()},this.getAlpha=function(){const{deviceOrientation:o}=e;return o&&o.alpha?n.MathUtils.degToRad(o.alpha)+e.alphaOffset:0},this.getBeta=function(){const{deviceOrientation:o}=e;return o&&o.beta?n.MathUtils.degToRad(o.beta):0},window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?this.initPermissionDialog():this.connect()}initPermissionDialog(){const t=document.createElement("div"),e=document.createElement("div"),i=document.createElement("div"),d=document.createElement("div");document.body.appendChild(t);const h={display:"flex",position:"fixed",top:0,left:0,width:"100%",height:"100%",zIndex:1,backgroundColor:"rgba(0,0,0,0.6)",justifyContent:"center",alignItems:"center"},m={backgroundColor:"white",padding:"6px",borderRadius:"3px",width:"36rem",height:"24rem"},x={width:"100%",height:"70%",display:"flex",justifyContent:"center",alignItems:"center"},o={display:"inline-flex",width:"100%",height:"30%",justifyContent:"center",alignItems:"center"};for(let r in h)t.style[r]=h[r];for(let r in m)e.style[r]=m[r];for(let r in x)i.style[r]=x[r];for(let r in o)d.style[r]=o[r];t.appendChild(e),e.appendChild(i),e.appendChild(d),i.innerHTML='<div style="font-size: 24pt; margin: 1rem;">This immersive website requires access to your device motion sensors.</div>';const c=()=>{this.connect(),t.style.display="none"},l=document.createElement("button");l.addEventListener("click",c),l.style.width="50%",l.style.height="80%",l.style.fontSize="20pt",l.appendChild(document.createTextNode("OK")),d.appendChild(l),document.body.appendChild(t)}}class nt{constructor(t){this.raycaster=new p.Raycaster,this.normalisedMousePosition=new p.Vector2(null,null),t.domElement.addEventListener("click",e=>{this.normalisedMousePosition.set(e.clientX/t.domElement.clientWidth*2-1,-(e.clientY/t.domElement.clientHeight*2)+1)})}raycast(t,e){if(this.normalisedMousePosition.x!==null&&this.normalisedMousePosition.y!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,t);const i=this.raycaster.intersectObjects(e.children,!1);return this.normalisedMousePosition.set(null,null),i}return[]}}const st="0.0.8-webcam-2";a.ClickHandler=nt,a.DeviceOrientationControls=it,a.LocationBased=B,a.SphMercProjection=U,a.Webcam=K,a.version=st,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})});
1
+ (function(r,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],n):(r=typeof globalThis<"u"?globalThis:r||self,n(r.locar={},r.THREE))})(this,function(r,n){"use strict";var Q=r=>{throw TypeError(r)};var U=(r,n,p)=>n.has(r)||Q("Cannot "+p);var s=(r,n,p)=>(U(r,n,"read from private field"),p?p.call(r):n.get(r)),y=(r,n,p)=>n.has(r)?Q("Cannot add the same private member more than once"):n instanceof WeakSet?n.add(r):n.set(r,p),m=(r,n,p,v)=>(U(r,n,"write to private field"),v?v.call(r,p):n.set(r,p),p),w=(r,n,p)=>(U(r,n,"access private method"),p);var V=(r,n,p,v)=>({set _(W){m(r,n,W,p)},get _(){return s(r,n,v)}});var b,X,B,Y,Z,_,L,E,j,I,D,A,S,T,O,M,k,$,q,K;function p(a){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const e in a)if(e!=="default"){const i=Object.getOwnPropertyDescriptor(a,e);Object.defineProperty(t,e,i.get?i:{enumerable:!0,get:()=>a[e]})}}return t.default=a,Object.freeze(t)}const v=p(n);class W{constructor(){y(this,b);this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}project(t,e){return[w(this,b,X).call(this,t),w(this,b,B).call(this,e)]}unproject(t){return[w(this,b,Y).call(this,t[0]),w(this,b,Z).call(this,t[1])]}getID(){return"epsg:3857"}}b=new WeakSet,X=function(t){return t/180*this.HALF_EARTH},B=function(t){var e=Math.log(Math.tan((90+t)*Math.PI/360))/(Math.PI/180);return e*this.HALF_EARTH/180},Y=function(t){return t/this.HALF_EARTH*180},Z=function(t){var e=t/this.HALF_EARTH*180;return e=180/Math.PI*(2*Math.atan(Math.exp(e*Math.PI/180))-Math.PI/2),e};class H{constructor(t,e,i={},h=null){y(this,M);y(this,_);y(this,L);y(this,E);y(this,j);y(this,I);y(this,D);y(this,A);y(this,S);y(this,T);y(this,O);this.scene=t,this.camera=e,m(this,_,new W),m(this,L,{}),m(this,E,null),m(this,j,0),m(this,I,100),m(this,D,null),this.setGpsOptions(i),m(this,A,null),m(this,S,0),m(this,T,0),m(this,O,h)}setProjection(t){m(this,_,t)}setGpsOptions(t={}){t.gpsMinDistance!==void 0&&m(this,j,t.gpsMinDistance),t.gpsMinAccuracy!==void 0&&m(this,I,t.gpsMinAccuracy)}async startGps(){if(s(this,O)){const e=await(await s(this,O).sendData("/gps/start",{gpsMinDistance:s(this,j),gpsMinAccuracy:s(this,I)})).json();m(this,T,e.session)}return s(this,D)===null?(m(this,D,navigator.geolocation.watchPosition(t=>{w(this,M,q).call(this,t)},t=>{s(this,L).gpserror?s(this,L).gpserror(t.code):alert(`GPS error: code ${t.code}`)},{enableHighAccuracy:!0})),!0):!1}stopGps(){return s(this,D)!==null?(navigator.geolocation.clearWatch(s(this,D)),m(this,D,null),!0):!1}fakeGps(t,e,i=null,h=0){i!==null&&this.setElevation(i),w(this,M,q).call(this,{coords:{longitude:t,latitude:e,accuracy:h}})}lonLatToWorldCoords(t,e){const i=s(this,_).project(t,e);if(s(this,A))i[0]-=s(this,A)[0],i[1]-=s(this,A)[1];else throw"No initial position determined";return[i[0],-i[1]]}add(t,e,i,h,u={}){var f;t.properties=u,w(this,M,k).call(this,t,e,i,h),this.scene.add(t),(f=s(this,O))==null||f.sendData("/object/new",{position:t.position,x:t.position.x,z:t.position.z,session:s(this,T),properties:u})}setElevation(t){this.camera.position.y=t}on(t,e){s(this,L)[t]=e}}_=new WeakMap,L=new WeakMap,E=new WeakMap,j=new WeakMap,I=new WeakMap,D=new WeakMap,A=new WeakMap,S=new WeakMap,T=new WeakMap,O=new WeakMap,M=new WeakSet,k=function(t,e,i,h){const u=this.lonLatToWorldCoords(e,i);h!==void 0&&(t.position.y=h),[t.position.x,t.position.z]=u},$=function(t,e){m(this,A,s(this,_).project(t,e))},q=function(t){var i,h,u;let e=Number.MAX_VALUE;V(this,S)._++,(i=s(this,O))==null||i.sendData("/gps/new",{gpsCount:s(this,S),lat:t.coords.latitude,lon:t.coords.longitude,acc:t.coords.accuracy,session:s(this,T)}),t.coords.accuracy<=s(this,I)&&(s(this,E)===null?m(this,E,{latitude:t.coords.latitude,longitude:t.coords.longitude}):e=w(this,M,K).call(this,s(this,E),t.coords),e>=s(this,j)&&(s(this,E).longitude=t.coords.longitude,s(this,E).latitude=t.coords.latitude,s(this,A)||(w(this,M,$).call(this,t.coords.longitude,t.coords.latitude),(h=s(this,O))==null||h.sendData("/worldorigin/new",{gpsCount:s(this,S),lat:t.coords.latitude,lon:t.coords.longitude,session:s(this,T),initialPosition:s(this,A)})),w(this,M,k).call(this,this.camera,t.coords.longitude,t.coords.latitude),(u=s(this,O))==null||u.sendData("/gps/accepted",{gpsCount:s(this,S),cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:s(this,T),distMoved:e}),s(this,L).gpsupdate&&s(this,L).gpsupdate(t,e)))},K=function(t,e){const i=v.MathUtils.degToRad(e.longitude-t.longitude),h=v.MathUtils.degToRad(e.latitude-t.latitude),u=Math.sin(h/2)*Math.sin(h/2)+Math.cos(v.MathUtils.degToRad(t.latitude))*Math.cos(v.MathUtils.degToRad(e.latitude))*(Math.sin(i/2)*Math.sin(i/2));return 2*Math.atan2(Math.sqrt(u),Math.sqrt(1-u))*6371e3};class G{constructor(t={},e){this.sceneWebcam=new v.Scene;let i;if(e?i=document.querySelector(e):(i=document.createElement("video"),i.setAttribute("autoplay",!0),i.setAttribute("playsinline",!0),i.style.display="none",document.body.appendChild(i)),this.texture=new v.VideoTexture(i),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia){const h={video:{width:{ideal:t.idealWidth||1280},height:{ideal:t.idealHeight||720},facingMode:"environment"}};navigator.mediaDevices.getUserMedia(h).then(u=>{i.addEventListener("loadedmetadata",()=>{var f;i.setAttribute("width",i.videoWidth),i.setAttribute("height",i.videoHeight),i.play(),(f=t.onVideoStarted)==null||f.call(this,this.texture)}),i.srcObject=u}).catch(u=>{setTimeout(()=>{alert(`Webcam Error
2
+ Name: `+u.name+`
3
+ Message: `+u.message)},1e3)})}else setTimeout(()=>{alert("sorry - media devices API not supported")},1e3)}dispose(){this.texture.dispose()}}const R=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,J=new n.Vector3(0,0,1),N=new n.Euler,tt=new n.Quaternion,et=new n.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5)),it={type:"change"};class z extends n.EventDispatcher{constructor(t){super(),window.isSecureContext===!1&&console.error("THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)");const e=this,i=1e-6,h=new n.Quaternion;this.object=t,this.object.rotation.reorder("YXZ"),this.enabled=!0,this.deviceOrientation=null,this.screenOrientation=0,this.alphaOffset=0,this.initialOffset=null,this.TWO_PI=2*Math.PI,this.HALF_PI=.5*Math.PI,this.orientationChangeEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation",this.smoothingFactor=1;const u=function({alpha:o,beta:l,gamma:d,webkitCompassHeading:c}){if(R){const g=360-c;e.alphaOffset=n.MathUtils.degToRad(g-o),e.deviceOrientation={alpha:o,beta:l,gamma:d,webkitCompassHeading:c}}else o<0&&(o+=360),e.deviceOrientation={alpha:o,beta:l,gamma:d};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.rotation}}))},f=function(){e.screenOrientation=window.orientation||0},x=function(o,l,d,c,g){N.set(d,l,-c,"YXZ"),o.setFromEuler(N),o.multiply(et),o.multiply(tt.setFromAxisAngle(J,-g))};this.connect=function(){f(),window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(o=>{o==="granted"&&(window.addEventListener("orientationchange",f),window.addEventListener(e.orientationChangeEventName,u))}).catch(function(o){console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",o)}):(window.addEventListener("orientationchange",f),window.addEventListener(e.orientationChangeEventName,u)),e.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",f),window.removeEventListener(e.orientationChangeEventName,u),e.enabled=!1,e.initialOffset=!1,e.deviceOrientation=null},this.update=function({theta:o=0}={theta:0}){if(e.enabled===!1)return;const l=e.deviceOrientation;if(l){let d=l.alpha?n.MathUtils.degToRad(l.alpha)+e.alphaOffset:0,c=l.beta?n.MathUtils.degToRad(l.beta):0,g=l.gamma?n.MathUtils.degToRad(l.gamma):0;const F=e.screenOrientation?n.MathUtils.degToRad(e.screenOrientation):0;if(R){const P=new n.Quaternion;x(P,d,c,g,F);const C=new n.Euler().setFromQuaternion(P,"YXZ");console.log(C.x,C.y,C.z),C.y=n.MathUtils.degToRad(360-l.webkitCompassHeading),P.setFromEuler(C),e.object.quaternion.copy(P)}else{if(this.smoothingFactor<1){if(this.lastOrientation){const P=this.smoothingFactor;d=this._getSmoothedAngle(d,this.lastOrientation.alpha,P),c=this._getSmoothedAngle(c+Math.PI,this.lastOrientation.beta,P),g=this._getSmoothedAngle(g+this.HALF_PI,this.lastOrientation.gamma,P,Math.PI)}else c+=Math.PI,g+=this.HALF_PI;this.lastOrientation={alpha:d,beta:c,gamma:g}}x(e.object.quaternion,d+o,this.smoothingFactor<1?c-Math.PI:c,this.smoothingFactor<1?g-this.HALF_PI:g,F)}8*(1-h.dot(e.object.quaternion))>i&&(h.copy(e.object.quaternion),e.dispatchEvent(it))}},this._orderAngle=function(o,l,d=this.TWO_PI){return l>o&&Math.abs(l-o)<d/2||o>l&&Math.abs(l-o)>d/2?{left:o,right:l}:{left:l,right:o}},this._getSmoothedAngle=function(o,l,d,c=this.TWO_PI){const g=this._orderAngle(o,l,c),F=g.left,P=g.right;g.left=0,g.right-=F,g.right<0&&(g.right+=c);let C=P==l?(1-d)*g.right+d*g.left:d*g.right+(1-d)*g.left;return C+=F,C>=c&&(C-=c),C},this.updateAlphaOffset=function(){e.initialOffset=!1},this.dispose=function(){e.disconnect()},this.getAlpha=function(){const{deviceOrientation:o}=e;return o&&o.alpha?n.MathUtils.degToRad(o.alpha)+e.alphaOffset:0},this.getBeta=function(){const{deviceOrientation:o}=e;return o&&o.beta?n.MathUtils.degToRad(o.beta):0},window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?this.initPermissionDialog():this.connect()}initPermissionDialog(){const t=document.createElement("div"),e=document.createElement("div"),i=document.createElement("div"),h=document.createElement("div");document.body.appendChild(t);const u={display:"flex",position:"fixed",top:0,left:0,width:"100%",height:"100%",zIndex:1,backgroundColor:"rgba(0,0,0,0.6)",justifyContent:"center",alignItems:"center"},f={backgroundColor:"white",padding:"6px",borderRadius:"3px",width:"36rem",height:"24rem"},x={width:"100%",height:"70%",display:"flex",justifyContent:"center",alignItems:"center"},o={display:"inline-flex",width:"100%",height:"30%",justifyContent:"center",alignItems:"center"};for(let c in u)t.style[c]=u[c];for(let c in f)e.style[c]=f[c];for(let c in x)i.style[c]=x[c];for(let c in o)h.style[c]=o[c];t.appendChild(e),e.appendChild(i),e.appendChild(h),i.innerHTML='<div style="font-size: 24pt; margin: 1rem;">This immersive website requires access to your device motion sensors.</div>';const l=()=>{this.connect(),t.style.display="none"},d=document.createElement("button");d.addEventListener("click",l),d.style.width="50%",d.style.height="80%",d.style.fontSize="20pt",d.appendChild(document.createTextNode("OK")),h.appendChild(d),document.body.appendChild(t)}}class nt{constructor(t){this.raycaster=new v.Raycaster,this.normalisedMousePosition=new v.Vector2(null,null),t.domElement.addEventListener("click",e=>{this.normalisedMousePosition.set(e.clientX/t.domElement.clientWidth*2-1,-(e.clientY/t.domElement.clientHeight*2)+1)})}raycast(t,e){if(this.normalisedMousePosition.x!==null&&this.normalisedMousePosition.y!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,t);const i=this.raycaster.intersectObjects(e.children,!1);return this.normalisedMousePosition.set(null,null),i}return[]}}AFRAME.registerComponent("locar-webcam",{schema:{idealWidth:{type:"number",default:1024},idealHeight:{type:"number",default:768},videoElement:{type:"string",default:""}},init:function(){new G({idealWidth:this.data.idealWidth,idealHeight:this.data.idealHeight,onVideoStarted:a=>{this.el.object3D.background=a}},this.data.videoElement||null)}}),AFRAME.registerComponent("locar-camera",{schema:{simulateLatitude:{type:"number",default:0},simulateLongitude:{type:"number",default:0},simulateAltitude:{type:"number",default:-Number.MAX_VALUE},positionMinAccuracy:{type:"number",default:100}},init:function(){this.locar=new H(this.el.sceneEl.object3D,this.el.object3D),this.locar.on("gpsupdate",(a,t)=>{this.el.emit("gpsupdate",{position:a,distMoved:t})}),this.locar.on("gpserror",a=>{const t=["User denied access to GPS.","GPS satellites not available.","Timeout communicating with GPS satellites - try moving to a more open area."];a>=1&&a<=3?alert(t[a-1]):alert(`Unknown geolocation error code ${a}.`)}),this._isMobile()&&(this.deviceOrientationControls=new z(this.el.object3D))},update:function(a){this.locar.setGpsOptions({gpsMinAccuracy:this.data.positionMinAccuracy,gpsMinDistance:this.data.gpsMinDistance}),(this.data.simulateLatitude!==0||this.data.simulateLongitude!==0)&&(this.data.simulateLatitude!=(a==null?void 0:a.simulateLatitude)||this.data.simulateLongitude!=(a==null?void 0:a.simulateLongitude))&&(this.locar.stopGps(),this.locar.fakeGps(this.data.simulateLongitude,this.data.simulateLatitude),this.data.simulateLongitude=0,this.data.simulateLatitude=0),this.data.simulateAltitude>-Number.MAX_VALUE&&this.locar.setElevation(this.data.simulateAltitude+1.6)},play:function(){this.locar.startGps()},pause:function(){this.locar.stopGps()},lonLatToWorldCoords:function(a,t){return this.locar.lonLatToWorldCoords(a,t)},tick:function(){var a;(a=this.deviceOrientationControls)==null||a.update()},_isMobile:function(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1}}),AFRAME.registerComponent("locar-entity-place",{schema:{latitude:{type:"number",default:0},longitude:{type:"number",default:0}},init:function(){const a=this.el.sceneEl.querySelector("[locar-camera]");this.locarCamera=a.components["locar-camera"]},update:function(a){this.locarCamera||console.error("Cannot update locar-entity-place without a locar-camera component on the scene camera.");const t=this.locarCamera.lonLatToWorldCoords(this.data.longitude,this.data.latitude);this.el.object3D.position.set(t[0],this.el.object3D.position.y,t[1])}});const st="0.0.10";r.ClickHandler=nt,r.DeviceOrientationControls=z,r.LocationBased=H,r.SphMercProjection=W,r.Webcam=G,r.version=st,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locar",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Location-based AR from AR.js.",
5
5
  "files": [
6
6
  "dist"
@@ -20,5 +20,8 @@
20
20
  "devDependencies": {
21
21
  "jsdoc": "^4.0.4",
22
22
  "vite": "^5.4.8"
23
+ },
24
+ "dependencies": {
25
+ "three": "^0.175.0"
23
26
  }
24
27
  }