locar 0.0.12 → 0.1.1

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