locar 0.0.3 → 0.0.7

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,6 +22,10 @@ Some of these issues, such as incorrect North on some devices, may well be to do
22
22
 
23
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
24
 
25
+ ## Cross-platform Compatibility
26
+
27
+ 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
28
+
25
29
  ## Using the library
26
30
 
27
31
  The library has been published to `npm` so you can install with:
@@ -38,13 +42,17 @@ import * as LocAR from 'locar';
38
42
  as long as you include `locar` as a dependency, e.g. in your `package.json`:
39
43
  ```
40
44
  "dependencies": {
41
- "locar" : "^0.0.3",
45
+ "locar" : "^0.0.7",
42
46
  "three" : "^0.169.0"
43
47
  }
44
48
  ```
45
49
 
46
50
  Including the library bundle directly as an import has issues with duplicate three.js imports which we have not resolved, so we recommend the approach above.
47
51
 
52
+ ## API documentation
53
+
54
+ Please see [here](https://ar-js-org.github.io/locar.js/api) for full API documentation.
55
+
48
56
  ## Tutorial
49
57
 
50
58
  Please see [here](https://github.com/AR-js-org/locar.js/blob/master/docs/tutorial/index.md) for a tutorial introducing LocAR.js.
package/dist/locar.es.js CHANGED
@@ -1,122 +1,274 @@
1
- import * as s from "three";
2
- class v {
1
+ var j = (r) => {
2
+ throw TypeError(r);
3
+ };
4
+ var F = (r, t, e) => t.has(r) || j("Cannot " + e);
5
+ var n = (r, t, e) => (F(r, t, "read from private field"), e ? e.call(r) : t.get(r)), m = (r, t, e) => t.has(r) ? j("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(r) : t.set(r, e), d = (r, t, e, i) => (F(r, t, "write to private field"), i ? i.call(r, e) : t.set(r, e), e), v = (r, t, e) => (F(r, t, "access private method"), e);
6
+ var q = (r, t, e, i) => ({
7
+ set _(o) {
8
+ d(r, t, o, e);
9
+ },
10
+ get _() {
11
+ return n(r, t, i);
12
+ }
13
+ });
14
+ import * as f from "three";
15
+ import { Vector3 as Z, Euler as N, Quaternion as _, EventDispatcher as B, MathUtils as D } from "three";
16
+ var P, U, G, X, Q;
17
+ class K {
18
+ /**
19
+ * Create a SphMercProjection.
20
+ */
3
21
  constructor() {
22
+ m(this, P);
4
23
  this.EARTH = 4007501668e-2, this.HALF_EARTH = 2003750834e-2;
5
24
  }
6
- project(e, t) {
7
- return [this.lonToSphMerc(e), this.latToSphMerc(t)];
8
- }
9
- unproject(e) {
10
- return [this.sphMercToLon(e[0]), this.sphMercToLat(e[1])];
11
- }
12
- lonToSphMerc(e) {
13
- return e / 180 * this.HALF_EARTH;
14
- }
15
- latToSphMerc(e) {
16
- var t = Math.log(Math.tan((90 + e) * Math.PI / 360)) / (Math.PI / 180);
17
- return t * this.HALF_EARTH / 180;
18
- }
19
- sphMercToLon(e) {
20
- return e / this.HALF_EARTH * 180;
25
+ /**
26
+ * Project a longitude and latitude into Spherical Mercator.
27
+ * @param {number} lon - the longitude.
28
+ * @param {number} lat - the latitude.
29
+ * @return {Array} Two-member array containing easting and northing.
30
+ */
31
+ project(t, e) {
32
+ return [v(this, P, U).call(this, t), v(this, P, G).call(this, e)];
21
33
  }
22
- sphMercToLat(e) {
23
- var t = e / this.HALF_EARTH * 180;
24
- return t = 180 / Math.PI * (2 * Math.atan(Math.exp(t * Math.PI / 180)) - Math.PI / 2), t;
34
+ /**
35
+ * Unproject a Spherical Mercator easting and northing.
36
+ * @param {Array} projected - Two-member array containing easting and northing
37
+ * @return {Array} Two-member array containing longitude and latitude
38
+ */
39
+ unproject(t) {
40
+ return [v(this, P, X).call(this, t[0]), v(this, P, Q).call(this, t[1])];
25
41
  }
42
+ /**
43
+ * Return the projection's ID.
44
+ * @return {string} The value "epsg:3857".
45
+ */
26
46
  getID() {
27
47
  return "epsg:3857";
28
48
  }
29
49
  }
30
- class w {
31
- constructor(e, t, i = {}) {
32
- this._scene = e, this._camera = t, this._proj = new v(), this._eventHandlers = {}, this._lastCoords = null, this._gpsMinDistance = 0, this._gpsMinAccuracy = 100, this._watchPositionId = null, this.setGpsOptions(i), this.initialPosition = null;
50
+ P = new WeakSet(), U = function(t) {
51
+ return t / 180 * this.HALF_EARTH;
52
+ }, G = function(t) {
53
+ var e = Math.log(Math.tan((90 + t) * Math.PI / 360)) / (Math.PI / 180);
54
+ return e * this.HALF_EARTH / 180;
55
+ }, X = function(t) {
56
+ return t / this.HALF_EARTH * 180;
57
+ }, Q = function(t) {
58
+ var e = t / this.HALF_EARTH * 180;
59
+ return e = 180 / Math.PI * (2 * Math.atan(Math.exp(e * Math.PI / 180)) - Math.PI / 2), e;
60
+ };
61
+ var R, A, C, L, x, b, E, I, T, O, p, S, V, W, Y;
62
+ class st {
63
+ /**
64
+ * @param {THREE.Scene} scene - The Three.js scene to use.
65
+ * @param {THREE.Camera} camera - The Three.js camera to use. Should usually
66
+ * be a THREE.PerspectiveCamera.
67
+ * @param {Object} options - Initialisation options for the GPS; see
68
+ * setGpsOptions() below.
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
+ */
71
+ constructor(t, e, i = {}, o = null) {
72
+ m(this, p);
73
+ m(this, R);
74
+ m(this, A);
75
+ m(this, C);
76
+ m(this, L);
77
+ m(this, x);
78
+ m(this, b);
79
+ m(this, E);
80
+ m(this, I);
81
+ m(this, T);
82
+ m(this, O);
83
+ this.scene = t, this.camera = e, d(this, R, new K()), d(this, A, {}), d(this, C, null), d(this, L, 0), d(this, x, 100), d(this, b, null), this.setGpsOptions(i), d(this, E, null), d(this, I, 0), d(this, T, 0), d(this, O, o);
33
84
  }
34
- setProjection(e) {
35
- this._proj = e;
85
+ /**
86
+ * 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
89
+ * containing easting and northing.
90
+ */
91
+ setProjection(t) {
92
+ d(this, R, t);
36
93
  }
37
- setGpsOptions(e = {}) {
38
- e.gpsMinDistance !== void 0 && (this._gpsMinDistance = e.gpsMinDistance), e.gpsMinAccuracy !== void 0 && (this._gpsMinAccuracy = e.gpsMinAccuracy);
94
+ /**
95
+ * Set the GPS options.
96
+ * @param {Object} object containing gpsMinDistance and/or gpsMinAccuracy
97
+ * 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
99
+ * minimum accuracy, in metres, for a GPS reading to be counted.
100
+ */
101
+ setGpsOptions(t = {}) {
102
+ t.gpsMinDistance !== void 0 && d(this, L, t.gpsMinDistance), t.gpsMinAccuracy !== void 0 && d(this, x, t.gpsMinAccuracy);
39
103
  }
40
- startGps() {
41
- return this._watchPositionId === null ? (this._watchPositionId = navigator.geolocation.watchPosition(
42
- (e) => {
43
- this._gpsReceived(e);
104
+ /**
105
+ * Start the GPS on a real device
106
+ * @return {boolean} code indicating whether the GPS was started successfully.
107
+ * GPS errors can be handled by handling the gpserror event.
108
+ */
109
+ async startGps() {
110
+ if (n(this, O)) {
111
+ const e = await (await n(this, O).sendData("/gps/start", {
112
+ gpsMinDistance: n(this, L),
113
+ gpsMinAccuracy: n(this, x)
114
+ })).json();
115
+ d(this, T, e.session);
116
+ }
117
+ return n(this, b) === null ? (d(this, b, navigator.geolocation.watchPosition(
118
+ (t) => {
119
+ v(this, p, W).call(this, t);
44
120
  },
45
- (e) => {
46
- this._eventHandlers.gpserror ? this._eventHandlers.gpserror(e.code) : alert(`GPS error: code ${e.code}`);
121
+ (t) => {
122
+ n(this, A).gpserror ? n(this, A).gpserror(t.code) : alert(`GPS error: code ${t.code}`);
47
123
  },
48
124
  {
49
125
  enableHighAccuracy: !0
50
126
  }
51
- ), !0) : !1;
127
+ )), !0) : !1;
52
128
  }
129
+ /**
130
+ * Stop the GPS on a real device
131
+ * @return {boolean} true if the GPS was stopped, false if it could not be
132
+ * stopped (i.e. it was never started).
133
+ */
53
134
  stopGps() {
54
- return this._watchPositionId !== null ? (navigator.geolocation.clearWatch(this._watchPositionId), this._watchPositionId = null, !0) : !1;
135
+ return n(this, b) !== null ? (navigator.geolocation.clearWatch(n(this, b)), d(this, b, null), !0) : !1;
55
136
  }
56
- fakeGps(e, t, i = null, n = 0) {
57
- i !== null && this.setElevation(i), this._gpsReceived({
137
+ /**
138
+ * Send a fake GPS signal. Useful for testing on a desktop or laptop.
139
+ * @param {number} lon - The longitude.
140
+ * @param {number} lat - The latitude.
141
+ * @param {number} elev - The elevation in metres. (optional, set to null
142
+ * for no elevation).
143
+ * @param {number} acc - The accuracy of the GPS reading in metres. May be
144
+ * ignored if lower than the specified minimum accuracy.
145
+ */
146
+ fakeGps(t, e, i = null, o = 0) {
147
+ i !== null && this.setElevation(i), v(this, p, W).call(this, {
58
148
  coords: {
59
- longitude: e,
60
- latitude: t,
61
- accuracy: n
149
+ longitude: t,
150
+ latitude: e,
151
+ accuracy: o
62
152
  }
63
153
  });
64
154
  }
65
- lonLatToWorldCoords(e, t) {
66
- const i = this._proj.project(e, t);
67
- if (this.initialPosition)
68
- i[0] -= this.initialPosition[0], i[1] -= this.initialPosition[1];
155
+ /**
156
+ * Convert longitude and latitude to three.js/WebGL world coordinates.
157
+ * Uses the specified projection, and negates the northing (in typical
158
+ * projections, northings increase northwards, but in the WebGL coordinate
159
+ * system, we face negative z if the camera is at the origin with default
160
+ * rotation).
161
+ * @param {number} lon - The longitude.
162
+ * @param {number} lat - The latitude.
163
+ * @return {Array} a two member array containing the WebGL x and z coordinates
164
+ */
165
+ lonLatToWorldCoords(t, e) {
166
+ const i = n(this, R).project(t, e);
167
+ if (n(this, E))
168
+ i[0] -= n(this, E)[0], i[1] -= n(this, E)[1];
69
169
  else
70
170
  throw "No initial position determined";
71
171
  return [i[0], -i[1]];
72
172
  }
73
- add(e, t, i, n, a = {}) {
74
- e.properties = a, this.setWorldPosition(e, t, i, n), this._scene.add(e);
75
- }
76
- setWorldPosition(e, t, i, n) {
77
- const a = this.lonLatToWorldCoords(t, i);
78
- n !== void 0 && (e.position.y = n), [e.position.x, e.position.z] = a;
79
- }
80
- setElevation(e) {
81
- this._camera.position.y = e;
82
- }
83
- on(e, t) {
84
- this._eventHandlers[e] = t;
85
- }
86
- setWorldOrigin(e, t) {
87
- this.initialPosition = this._proj.project(e, t);
173
+ /**
174
+ * Add a new AR object at a given latitude, longitude and elevation.
175
+ * @param {THREE.Mesh} object the object
176
+ * @param {number} lon - the longitude.
177
+ * @param {number} lat - the latitude.
178
+ * @param {number} elev - the elevation in metres
179
+ * (if not specified, 0 is assigned)
180
+ * @param {Object} properties - properties describing the object (for example,
181
+ * the contents of the GeoJSON properties field).
182
+ */
183
+ add(t, e, i, o, u = {}) {
184
+ var g;
185
+ t.properties = u, v(this, p, S).call(this, t, e, i, o), this.scene.add(t), (g = n(this, O)) == 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: u
191
+ });
88
192
  }
89
- _gpsReceived(e) {
90
- let t = Number.MAX_VALUE;
91
- console.log(`_gpsReceived(): position ${e.coords.latitude},${e.coords.longitude}, accuracy ${e.coords.accuracy}, accuracy limit ${this._gpsMinAccuracy}`), e.coords.accuracy <= this._gpsMinAccuracy && (this._lastCoords === null ? this._lastCoords = {
92
- latitude: e.coords.latitude,
93
- longitude: e.coords.longitude
94
- } : t = this._haversineDist(this._lastCoords, e.coords), t >= this._gpsMinDistance && (this._lastCoords.longitude = e.coords.longitude, this._lastCoords.latitude = e.coords.latitude, this.initialPosition || this.setWorldOrigin(
95
- e.coords.longitude,
96
- e.coords.latitude
97
- ), this.setWorldPosition(
98
- this._camera,
99
- e.coords.longitude,
100
- e.coords.latitude
101
- ), this._eventHandlers.gpsupdate && this._eventHandlers.gpsupdate(e, t)));
193
+ /**
194
+ * Set the elevation (y coordinate) of the camera.
195
+ * @param {number} elev - the elevation in metres.
196
+ */
197
+ setElevation(t) {
198
+ this.camera.position.y = t;
102
199
  }
103
200
  /**
104
- * Calculate haversine distance between two lat/lon pairs.
105
- *
106
- * Taken from original A-Frame AR.js location-based components
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
107
212
  */
108
- _haversineDist(e, t) {
109
- const i = s.MathUtils.degToRad(t.longitude - e.longitude), n = s.MathUtils.degToRad(t.latitude - e.latitude), a = Math.sin(n / 2) * Math.sin(n / 2) + Math.cos(s.MathUtils.degToRad(e.latitude)) * Math.cos(s.MathUtils.degToRad(t.latitude)) * (Math.sin(i / 2) * Math.sin(i / 2));
110
- return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) * 6371e3;
213
+ on(t, e) {
214
+ n(this, A)[t] = e;
111
215
  }
112
216
  }
113
- class E {
114
- constructor(e, t, i) {
115
- this.renderer = e, this.renderer.autoClear = !1, this.sceneWebcam = new s.Scene();
116
- let n;
117
- t === void 0 ? (n = document.createElement("video"), n.setAttribute("autoplay", !0), n.setAttribute("playsinline", !0), n.style.display = "none", document.body.appendChild(n)) : n = document.querySelector(t), this.geom = new s.PlaneGeometry(), this.texture = new s.VideoTexture(n), this.material = new s.MeshBasicMaterial({ map: this.texture });
118
- const a = new s.Mesh(this.geom, this.material);
119
- if (this.sceneWebcam.add(a), this.cameraWebcam = new s.OrthographicCamera(
217
+ R = new WeakMap(), A = new WeakMap(), C = new WeakMap(), L = new WeakMap(), x = new WeakMap(), b = new WeakMap(), E = new WeakMap(), I = new WeakMap(), T = new WeakMap(), O = new WeakMap(), p = new WeakSet(), S = function(t, e, i, o) {
218
+ const u = this.lonLatToWorldCoords(e, i);
219
+ o !== void 0 && (t.position.y = o), [t.position.x, t.position.z] = u;
220
+ }, V = function(t, e) {
221
+ d(this, E, n(this, R).project(t, e));
222
+ }, W = function(t) {
223
+ var i, o, u;
224
+ let e = Number.MAX_VALUE;
225
+ q(this, I)._++, (i = n(this, O)) == null || i.sendData("/gps/new", {
226
+ gpsCount: n(this, I),
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, x) && (n(this, C) === null ? d(this, C, {
232
+ latitude: t.coords.latitude,
233
+ longitude: t.coords.longitude
234
+ }) : e = v(this, p, Y).call(this, n(this, C), t.coords), e >= n(this, L) && (n(this, C).longitude = t.coords.longitude, n(this, C).latitude = t.coords.latitude, n(this, E) || (v(this, p, V).call(this, t.coords.longitude, t.coords.latitude), (o = n(this, O)) == null || o.sendData("/worldorigin/new", {
235
+ gpsCount: n(this, I),
236
+ lat: t.coords.latitude,
237
+ lon: t.coords.longitude,
238
+ session: n(this, T),
239
+ initialPosition: n(this, E)
240
+ })), v(this, p, S).call(this, this.camera, t.coords.longitude, t.coords.latitude), (u = n(this, O)) == null || u.sendData("/gps/accepted", {
241
+ gpsCount: n(this, I),
242
+ cameraX: this.camera.position.x,
243
+ cameraZ: this.camera.position.z,
244
+ session: n(this, T),
245
+ distMoved: e
246
+ }), n(this, A).gpsupdate && n(this, A).gpsupdate(t, e)));
247
+ }, /**
248
+ * Calculate haversine distance between two lat/lon pairs.
249
+ *
250
+ * Taken from original A-Frame AR.js location-based components
251
+ */
252
+ Y = function(t, e) {
253
+ const i = f.MathUtils.degToRad(e.longitude - t.longitude), o = f.MathUtils.degToRad(e.latitude - t.latitude), u = Math.sin(o / 2) * Math.sin(o / 2) + Math.cos(f.MathUtils.degToRad(t.latitude)) * Math.cos(f.MathUtils.degToRad(e.latitude)) * (Math.sin(i / 2) * Math.sin(i / 2));
254
+ return 2 * Math.atan2(Math.sqrt(u), Math.sqrt(1 - u)) * 6371e3;
255
+ };
256
+ class ot {
257
+ /**
258
+ * Create a WebcamRenderer.
259
+ * @param {THREE.WebGLRenderer} renderer - the Three.js renderer.
260
+ * @param {string} videoElementSelector - selector to obtain the HTML video
261
+ * element to render the webcam feed. If a falsy value (e.g. null or
262
+ * undefined), a video element will be created.
263
+ * @options {Object} - options to use for initialising the camera. Currently
264
+ * width and height properties are understood.
265
+ */
266
+ constructor(t, e, i) {
267
+ this.renderer = t, this.renderer.autoClear = !1, this.sceneWebcam = new f.Scene();
268
+ let o;
269
+ e ? o = document.querySelector(e) : (o = document.createElement("video"), o.setAttribute("autoplay", !0), o.setAttribute("playsinline", !0), o.style.display = "none", document.body.appendChild(o)), this.geom = new f.PlaneGeometry(), this.texture = new f.VideoTexture(o), this.material = new f.MeshBasicMaterial({ map: this.texture });
270
+ const u = new f.Mesh(this.geom, this.material);
271
+ if (this.sceneWebcam.add(u), this.cameraWebcam = new f.OrthographicCamera(
120
272
  -0.5,
121
273
  0.5,
122
274
  0.5,
@@ -124,21 +276,21 @@ class E {
124
276
  0,
125
277
  10
126
278
  ), navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
127
- const r = {
279
+ const g = {
128
280
  video: {
129
281
  width: (i == null ? void 0 : i.width) || 1280,
130
282
  height: (i == null ? void 0 : i.height) || 720,
131
283
  facingMode: "environment"
132
284
  }
133
285
  };
134
- navigator.mediaDevices.getUserMedia(r).then((c) => {
135
- console.log("using the webcam successfully..."), n.srcObject = c, n.play();
136
- }).catch((c) => {
286
+ navigator.mediaDevices.getUserMedia(g).then((w) => {
287
+ console.log("using the webcam successfully..."), o.srcObject = w, o.play();
288
+ }).catch((w) => {
137
289
  setTimeout(() => {
138
290
  alert(
139
291
  `Webcam Error
140
- Name: ` + c.name + `
141
- Message: ` + c.message
292
+ Name: ` + w.name + `
293
+ Message: ` + w.message
142
294
  );
143
295
  }, 1e3);
144
296
  });
@@ -147,67 +299,241 @@ Message: ` + c.message
147
299
  alert("sorry - media devices API not supported");
148
300
  }, 1e3);
149
301
  }
302
+ /**
303
+ * Update the webcam.
304
+ * Should be called from your Three.js rendering (animation) function.
305
+ */
150
306
  update() {
151
307
  this.renderer.clear(), this.renderer.render(this.sceneWebcam, this.cameraWebcam), this.renderer.clearDepth();
152
308
  }
309
+ /**
310
+ * Free up the memory associated with the webcam.
311
+ * Should be called when your application closes.
312
+ */
153
313
  dispose() {
154
314
  this.material.dispose(), this.texture.dispose(), this.geom.dispose();
155
315
  }
156
316
  }
157
- const p = new s.Vector3(0, 0, 1), m = new s.Euler(), M = new s.Quaternion(), f = new s.Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)), _ = { type: "change" };
158
- class y extends s.EventDispatcher {
159
- constructor(e) {
160
- super(), window.isSecureContext === !1 && console.error("THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)");
161
- const t = this, i = 1e-6, n = new s.Quaternion();
162
- this.object = e, this.object.rotation.reorder("YXZ"), this.enabled = !0, this.deviceOrientation = {}, this.screenOrientation = 0, this.alphaOffset = 0, this.deviceOrientationEventName = "ondeviceorientationabsolute" in window ? "deviceorientationabsolute" : "deviceorientation";
163
- const a = function(o) {
164
- t.deviceOrientation = o;
165
- }, r = function() {
166
- t.screenOrientation = window.orientation || 0;
167
- }, c = function(o, l, h, u, g) {
168
- m.set(h, l, -u, "YXZ"), o.setFromEuler(m), o.multiply(f), o.multiply(M.setFromAxisAngle(p, -g));
317
+ const k = navigator.userAgent.match(/iPhone|iPad|iPod/i), $ = 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" };
318
+ class at extends B {
319
+ /**
320
+ * Create an instance of DeviceOrientationControls.
321
+ * @param {Object} object - the object to attach the controls to
322
+ * (usually your Three.js camera)
323
+ */
324
+ constructor(t) {
325
+ super(), window.isSecureContext === !1 && console.error(
326
+ "THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)"
327
+ );
328
+ const e = this, i = 1e-6, o = new _();
329
+ 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;
330
+ const u = function({
331
+ alpha: s,
332
+ beta: c,
333
+ gamma: h,
334
+ webkitCompassHeading: a
335
+ }) {
336
+ if (k) {
337
+ const l = 360 - a;
338
+ e.alphaOffset = D.degToRad(l - s), e.deviceOrientation = { alpha: s, beta: c, gamma: h, webkitCompassHeading: a };
339
+ } else
340
+ s < 0 && (s += 360), e.deviceOrientation = { alpha: s, beta: c, gamma: h };
341
+ window.dispatchEvent(
342
+ new CustomEvent("camera-rotation-change", {
343
+ detail: { cameraRotation: t.rotation }
344
+ })
345
+ );
346
+ }, g = function() {
347
+ e.screenOrientation = window.orientation || 0;
348
+ }, w = function(s, c, h, a, l) {
349
+ z.set(h, c, -a, "YXZ"), s.setFromEuler(z), s.multiply(tt), s.multiply(J.setFromAxisAngle($, -l));
169
350
  };
170
351
  this.connect = function() {
171
- r(), window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then(function(o) {
172
- o == "granted" && (window.addEventListener("orientationchange", r), window.addEventListener(t.deviceOrientationEventName, a));
173
- }).catch(function(o) {
174
- console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:", o);
175
- }) : (window.addEventListener("orientationchange", r), window.addEventListener(t.deviceOrientationEventName, a)), t.enabled = !0;
352
+ g(), window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? window.DeviceOrientationEvent.requestPermission().then((s) => {
353
+ s === "granted" && (window.addEventListener(
354
+ "orientationchange",
355
+ g
356
+ ), window.addEventListener(
357
+ e.orientationChangeEventName,
358
+ u
359
+ ));
360
+ }).catch(function(s) {
361
+ console.error(
362
+ "THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",
363
+ s
364
+ );
365
+ }) : (window.addEventListener(
366
+ "orientationchange",
367
+ g
368
+ ), window.addEventListener(
369
+ e.orientationChangeEventName,
370
+ u
371
+ )), e.enabled = !0;
176
372
  }, this.disconnect = function() {
177
- window.removeEventListener("orientationchange", r), window.removeEventListener(t.deviceOrientationEventName, a), t.enabled = !1;
178
- }, this.update = function() {
179
- if (t.enabled === !1) return;
180
- const o = t.deviceOrientation;
181
- if (o) {
182
- const l = o.alpha ? s.MathUtils.degToRad(o.alpha) + t.alphaOffset : 0, h = o.beta ? s.MathUtils.degToRad(o.beta) : 0, u = o.gamma ? s.MathUtils.degToRad(o.gamma) : 0, g = t.screenOrientation ? s.MathUtils.degToRad(t.screenOrientation) : 0;
183
- c(t.object.quaternion, l, h, u, g), 8 * (1 - n.dot(t.object.quaternion)) > i && (n.copy(t.object.quaternion), t.dispatchEvent(_));
373
+ window.removeEventListener(
374
+ "orientationchange",
375
+ g
376
+ ), window.removeEventListener(
377
+ e.orientationChangeEventName,
378
+ u
379
+ ), e.enabled = !1, e.initialOffset = !1, e.deviceOrientation = null;
380
+ }, this.update = function({ theta: s = 0 } = { theta: 0 }) {
381
+ if (e.enabled === !1) return;
382
+ const c = e.deviceOrientation;
383
+ if (c) {
384
+ let h = c.alpha ? D.degToRad(c.alpha) + e.alphaOffset : 0, a = c.beta ? D.degToRad(c.beta) : 0, l = c.gamma ? D.degToRad(c.gamma) : 0;
385
+ const H = e.screenOrientation ? D.degToRad(e.screenOrientation) : 0;
386
+ if (k) {
387
+ const y = new _();
388
+ w(y, h, a, l, H);
389
+ const M = new N().setFromQuaternion(
390
+ y,
391
+ "YXZ"
392
+ );
393
+ console.log(M.x, M.y, M.z), M.y = D.degToRad(
394
+ 360 - c.webkitCompassHeading
395
+ ), y.setFromEuler(M), e.object.quaternion.copy(y);
396
+ } else {
397
+ if (this.smoothingFactor < 1) {
398
+ if (this.lastOrientation) {
399
+ const y = this.smoothingFactor;
400
+ h = this._getSmoothedAngle(
401
+ h,
402
+ this.lastOrientation.alpha,
403
+ y
404
+ ), a = this._getSmoothedAngle(
405
+ a + Math.PI,
406
+ this.lastOrientation.beta,
407
+ y
408
+ ), l = this._getSmoothedAngle(
409
+ l + this.HALF_PI,
410
+ this.lastOrientation.gamma,
411
+ y,
412
+ Math.PI
413
+ );
414
+ } else
415
+ a += Math.PI, l += this.HALF_PI;
416
+ this.lastOrientation = {
417
+ alpha: h,
418
+ beta: a,
419
+ gamma: l
420
+ };
421
+ }
422
+ w(
423
+ e.object.quaternion,
424
+ h + s,
425
+ this.smoothingFactor < 1 ? a - Math.PI : a,
426
+ this.smoothingFactor < 1 ? l - this.HALF_PI : l,
427
+ H
428
+ );
429
+ }
430
+ 8 * (1 - o.dot(e.object.quaternion)) > i && (o.copy(e.object.quaternion), e.dispatchEvent(et));
184
431
  }
432
+ }, this._orderAngle = function(s, c, h = this.TWO_PI) {
433
+ 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 };
434
+ }, this._getSmoothedAngle = function(s, c, h, a = this.TWO_PI) {
435
+ const l = this._orderAngle(s, c, a), H = l.left, y = l.right;
436
+ l.left = 0, l.right -= H, l.right < 0 && (l.right += a);
437
+ let M = y == c ? (1 - h) * l.right + h * l.left : h * l.right + (1 - h) * l.left;
438
+ return M += H, M >= a && (M -= a), M;
439
+ }, this.updateAlphaOffset = function() {
440
+ e.initialOffset = !1;
185
441
  }, this.dispose = function() {
186
- t.disconnect();
187
- }, this.connect();
442
+ e.disconnect();
443
+ }, this.getAlpha = function() {
444
+ const { deviceOrientation: s } = e;
445
+ return s && s.alpha ? D.degToRad(s.alpha) + e.alphaOffset : 0;
446
+ }, this.getBeta = function() {
447
+ const { deviceOrientation: s } = e;
448
+ return s && s.beta ? D.degToRad(s.beta) : 0;
449
+ }, window.DeviceOrientationEvent !== void 0 && typeof window.DeviceOrientationEvent.requestPermission == "function" ? this.initPermissionDialog() : this.connect();
450
+ }
451
+ // Provide gesture before initialising device orientation controls
452
+ // From PR #659 on the main AR.js repo
453
+ // Thanks to @ma2yama
454
+ initPermissionDialog() {
455
+ const t = document.createElement("div"), e = document.createElement("div"), i = document.createElement("div"), o = document.createElement("div");
456
+ document.body.appendChild(t);
457
+ const u = {
458
+ display: "flex",
459
+ position: "fixed",
460
+ top: 0,
461
+ left: 0,
462
+ width: "100%",
463
+ height: "100%",
464
+ zIndex: 1,
465
+ backgroundColor: "rgba(0,0,0,0.6)",
466
+ justifyContent: "center",
467
+ alignItems: "center"
468
+ }, g = {
469
+ backgroundColor: "white",
470
+ padding: "6px",
471
+ borderRadius: "3px",
472
+ width: "36rem",
473
+ height: "24rem"
474
+ }, w = {
475
+ width: "100%",
476
+ height: "70%",
477
+ display: "flex",
478
+ justifyContent: "center",
479
+ alignItems: "center"
480
+ }, s = {
481
+ display: "inline-flex",
482
+ width: "100%",
483
+ height: "30%",
484
+ justifyContent: "center",
485
+ alignItems: "center"
486
+ };
487
+ for (let a in u)
488
+ t.style[a] = u[a];
489
+ for (let a in g)
490
+ e.style[a] = g[a];
491
+ for (let a in w)
492
+ i.style[a] = w[a];
493
+ for (let a in s)
494
+ o.style[a] = s[a];
495
+ t.appendChild(e), e.appendChild(i), e.appendChild(o), i.innerHTML = '<div style="font-size: 24pt; margin: 1rem;">This immersive website requires access to your device motion sensors.</div>';
496
+ const c = () => {
497
+ this.connect(), t.style.display = "none";
498
+ }, h = document.createElement("button");
499
+ h.addEventListener("click", c), h.style.width = "50%", h.style.height = "80%", h.style.fontSize = "20pt", h.appendChild(document.createTextNode("OK")), o.appendChild(h), document.body.appendChild(t);
188
500
  }
189
501
  }
190
- class P {
191
- constructor(e) {
192
- this.raycaster = new s.Raycaster(), this.normalisedMousePosition = new s.Vector2(null, null), e.domElement.addEventListener("click", (t) => {
502
+ class rt {
503
+ /**
504
+ * Create a ClickHandler.
505
+ * @param {THREE.WebGLRenderer} - The Three.js renderer on which the click
506
+ * events will be handled.
507
+ */
508
+ constructor(t) {
509
+ this.raycaster = new f.Raycaster(), this.normalisedMousePosition = new f.Vector2(null, null), t.domElement.addEventListener("click", (e) => {
193
510
  this.normalisedMousePosition.set(
194
- t.clientX / e.domElement.clientWidth * 2 - 1,
195
- -(t.clientY / e.domElement.clientHeight * 2) + 1
511
+ e.clientX / t.domElement.clientWidth * 2 - 1,
512
+ -(e.clientY / t.domElement.clientHeight * 2) + 1
196
513
  );
197
514
  });
198
515
  }
199
- raycast(e, t) {
516
+ /**
517
+ * Cast a ray into the scene to detect objects.
518
+ * @param {THREE.Camera} - The active Three.js camera, from which the ray
519
+ * will be cast.
520
+ * @param {THREE.Scene} - The active Three.js scene, which the ray will be
521
+ * cast into.
522
+ * @return {Array} - array of all intersected objects.
523
+ */
524
+ raycast(t, e) {
200
525
  if (this.normalisedMousePosition.x !== null && this.normalisedMousePosition.y !== null) {
201
- this.raycaster.setFromCamera(this.normalisedMousePosition, e);
202
- const i = this.raycaster.intersectObjects(t.children, !1);
526
+ this.raycaster.setFromCamera(this.normalisedMousePosition, t);
527
+ const i = this.raycaster.intersectObjects(e.children, !1);
203
528
  return this.normalisedMousePosition.set(null, null), i;
204
529
  }
205
530
  return [];
206
531
  }
207
532
  }
208
533
  export {
209
- P as ClickHandler,
210
- y as DeviceOrientationControls,
211
- w as LocationBased,
212
- E as WebcamRenderer
534
+ rt as ClickHandler,
535
+ at as DeviceOrientationControls,
536
+ st as LocationBased,
537
+ K as SphMercProjection,
538
+ ot as WebcamRenderer
213
539
  };
package/dist/locar.umd.js CHANGED
@@ -1,3 +1,3 @@
1
- (function(r,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],h):(r=typeof globalThis<"u"?globalThis:r||self,h(r.locar={},r.THREE))})(this,function(r,h){"use strict";function v(c){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(c){for(const t in c)if(t!=="default"){const i=Object.getOwnPropertyDescriptor(c,t);Object.defineProperty(e,t,i.get?i:{enumerable:!0,get:()=>c[t]})}}return e.default=c,Object.freeze(e)}const n=v(h);class M{constructor(){this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}project(e,t){return[this.lonToSphMerc(e),this.latToSphMerc(t)]}unproject(e){return[this.sphMercToLon(e[0]),this.sphMercToLat(e[1])]}lonToSphMerc(e){return e/180*this.HALF_EARTH}latToSphMerc(e){var t=Math.log(Math.tan((90+e)*Math.PI/360))/(Math.PI/180);return t*this.HALF_EARTH/180}sphMercToLon(e){return e/this.HALF_EARTH*180}sphMercToLat(e){var t=e/this.HALF_EARTH*180;return t=180/Math.PI*(2*Math.atan(Math.exp(t*Math.PI/180))-Math.PI/2),t}getID(){return"epsg:3857"}}class _{constructor(e,t,i={}){this._scene=e,this._camera=t,this._proj=new M,this._eventHandlers={},this._lastCoords=null,this._gpsMinDistance=0,this._gpsMinAccuracy=100,this._watchPositionId=null,this.setGpsOptions(i),this.initialPosition=null}setProjection(e){this._proj=e}setGpsOptions(e={}){e.gpsMinDistance!==void 0&&(this._gpsMinDistance=e.gpsMinDistance),e.gpsMinAccuracy!==void 0&&(this._gpsMinAccuracy=e.gpsMinAccuracy)}startGps(){return this._watchPositionId===null?(this._watchPositionId=navigator.geolocation.watchPosition(e=>{this._gpsReceived(e)},e=>{this._eventHandlers.gpserror?this._eventHandlers.gpserror(e.code):alert(`GPS error: code ${e.code}`)},{enableHighAccuracy:!0}),!0):!1}stopGps(){return this._watchPositionId!==null?(navigator.geolocation.clearWatch(this._watchPositionId),this._watchPositionId=null,!0):!1}fakeGps(e,t,i=null,s=0){i!==null&&this.setElevation(i),this._gpsReceived({coords:{longitude:e,latitude:t,accuracy:s}})}lonLatToWorldCoords(e,t){const i=this._proj.project(e,t);if(this.initialPosition)i[0]-=this.initialPosition[0],i[1]-=this.initialPosition[1];else throw"No initial position determined";return[i[0],-i[1]]}add(e,t,i,s,a={}){e.properties=a,this.setWorldPosition(e,t,i,s),this._scene.add(e)}setWorldPosition(e,t,i,s){const a=this.lonLatToWorldCoords(t,i);s!==void 0&&(e.position.y=s),[e.position.x,e.position.z]=a}setElevation(e){this._camera.position.y=e}on(e,t){this._eventHandlers[e]=t}setWorldOrigin(e,t){this.initialPosition=this._proj.project(e,t)}_gpsReceived(e){let t=Number.MAX_VALUE;console.log(`_gpsReceived(): position ${e.coords.latitude},${e.coords.longitude}, accuracy ${e.coords.accuracy}, accuracy limit ${this._gpsMinAccuracy}`),e.coords.accuracy<=this._gpsMinAccuracy&&(this._lastCoords===null?this._lastCoords={latitude:e.coords.latitude,longitude:e.coords.longitude}:t=this._haversineDist(this._lastCoords,e.coords),t>=this._gpsMinDistance&&(this._lastCoords.longitude=e.coords.longitude,this._lastCoords.latitude=e.coords.latitude,this.initialPosition||this.setWorldOrigin(e.coords.longitude,e.coords.latitude),this.setWorldPosition(this._camera,e.coords.longitude,e.coords.latitude),this._eventHandlers.gpsupdate&&this._eventHandlers.gpsupdate(e,t)))}_haversineDist(e,t){const i=n.MathUtils.degToRad(t.longitude-e.longitude),s=n.MathUtils.degToRad(t.latitude-e.latitude),a=Math.sin(s/2)*Math.sin(s/2)+Math.cos(n.MathUtils.degToRad(e.latitude))*Math.cos(n.MathUtils.degToRad(t.latitude))*(Math.sin(i/2)*Math.sin(i/2));return 2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a))*6371e3}}class w{constructor(e,t,i){this.renderer=e,this.renderer.autoClear=!1,this.sceneWebcam=new n.Scene;let s;t===void 0?(s=document.createElement("video"),s.setAttribute("autoplay",!0),s.setAttribute("playsinline",!0),s.style.display="none",document.body.appendChild(s)):s=document.querySelector(t),this.geom=new n.PlaneGeometry,this.texture=new n.VideoTexture(s),this.material=new n.MeshBasicMaterial({map:this.texture});const a=new n.Mesh(this.geom,this.material);if(this.sceneWebcam.add(a),this.cameraWebcam=new n.OrthographicCamera(-.5,.5,.5,-.5,0,10),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia){const d={video:{width:(i==null?void 0:i.width)||1280,height:(i==null?void 0:i.height)||720,facingMode:"environment"}};navigator.mediaDevices.getUserMedia(d).then(l=>{console.log("using the webcam successfully..."),s.srcObject=l,s.play()}).catch(l=>{setTimeout(()=>{alert(`Webcam Error
2
- Name: `+l.name+`
3
- Message: `+l.message)},1e3)})}else setTimeout(()=>{alert("sorry - media devices API not supported")},1e3)}update(){this.renderer.clear(),this.renderer.render(this.sceneWebcam,this.cameraWebcam),this.renderer.clearDepth()}dispose(){this.material.dispose(),this.texture.dispose(),this.geom.dispose()}}const y=new n.Vector3(0,0,1),f=new n.Euler,E=new n.Quaternion,P=new n.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5)),b={type:"change"};class O extends n.EventDispatcher{constructor(e){super(),window.isSecureContext===!1&&console.error("THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)");const t=this,i=1e-6,s=new n.Quaternion;this.object=e,this.object.rotation.reorder("YXZ"),this.enabled=!0,this.deviceOrientation={},this.screenOrientation=0,this.alphaOffset=0,this.deviceOrientationEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation";const a=function(o){t.deviceOrientation=o},d=function(){t.screenOrientation=window.orientation||0},l=function(o,u,g,m,p){f.set(g,u,-m,"YXZ"),o.setFromEuler(f),o.multiply(P),o.multiply(E.setFromAxisAngle(y,-p))};this.connect=function(){d(),window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(function(o){o=="granted"&&(window.addEventListener("orientationchange",d),window.addEventListener(t.deviceOrientationEventName,a))}).catch(function(o){console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",o)}):(window.addEventListener("orientationchange",d),window.addEventListener(t.deviceOrientationEventName,a)),t.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",d),window.removeEventListener(t.deviceOrientationEventName,a),t.enabled=!1},this.update=function(){if(t.enabled===!1)return;const o=t.deviceOrientation;if(o){const u=o.alpha?n.MathUtils.degToRad(o.alpha)+t.alphaOffset:0,g=o.beta?n.MathUtils.degToRad(o.beta):0,m=o.gamma?n.MathUtils.degToRad(o.gamma):0,p=t.screenOrientation?n.MathUtils.degToRad(t.screenOrientation):0;l(t.object.quaternion,u,g,m,p),8*(1-s.dot(t.object.quaternion))>i&&(s.copy(t.object.quaternion),t.dispatchEvent(b))}},this.dispose=function(){t.disconnect()},this.connect()}}class T{constructor(e){this.raycaster=new n.Raycaster,this.normalisedMousePosition=new n.Vector2(null,null),e.domElement.addEventListener("click",t=>{this.normalisedMousePosition.set(t.clientX/e.domElement.clientWidth*2-1,-(t.clientY/e.domElement.clientHeight*2)+1)})}raycast(e,t){if(this.normalisedMousePosition.x!==null&&this.normalisedMousePosition.y!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,e);const i=this.raycaster.intersectObjects(t.children,!1);return this.normalisedMousePosition.set(null,null),i}return[]}}r.ClickHandler=T,r.DeviceOrientationControls=O,r.LocationBased=_,r.WebcamRenderer=w,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})});
1
+ (function(a,i){typeof exports=="object"&&typeof module<"u"?i(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],i):(a=typeof globalThis<"u"?globalThis:a||self,i(a.locar={},a.THREE))})(this,function(a,i){"use strict";var H=a=>{throw TypeError(a)};var U=(a,i,f)=>i.has(a)||H("Cannot "+f);var n=(a,i,f)=>(U(a,i,"read from private field"),f?f.call(a):i.get(a)),v=(a,i,f)=>i.has(a)?H("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(a):i.set(a,f),u=(a,i,f,m)=>(U(a,i,"write to private field"),m?m.call(a,f):i.set(a,f),f),y=(a,i,f)=>(U(a,i,"access private method"),f);var N=(a,i,f,m)=>({set _(W){u(a,i,W,f)},get _(){return n(a,i,m)}});var w,Q,G,X,B,S,I,_,j,x,L,O,T,E,P,M,q,V,R,Y;function f(b){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(b){for(const e in b)if(e!=="default"){const s=Object.getOwnPropertyDescriptor(b,e);Object.defineProperty(t,e,s.get?s:{enumerable:!0,get:()=>b[e]})}}return t.default=b,Object.freeze(t)}const m=f(i);class W{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,B).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},B=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 Z{constructor(t,e,s={},r=null){v(this,M);v(this,S);v(this,I);v(this,_);v(this,j);v(this,x);v(this,L);v(this,O);v(this,T);v(this,E);v(this,P);this.scene=t,this.camera=e,u(this,S,new W),u(this,I,{}),u(this,_,null),u(this,j,0),u(this,x,100),u(this,L,null),this.setGpsOptions(s),u(this,O,null),u(this,T,0),u(this,E,0),u(this,P,r)}setProjection(t){u(this,S,t)}setGpsOptions(t={}){t.gpsMinDistance!==void 0&&u(this,j,t.gpsMinDistance),t.gpsMinAccuracy!==void 0&&u(this,x,t.gpsMinAccuracy)}async startGps(){if(n(this,P)){const e=await(await n(this,P).sendData("/gps/start",{gpsMinDistance:n(this,j),gpsMinAccuracy:n(this,x)})).json();u(this,E,e.session)}return n(this,L)===null?(u(this,L,navigator.geolocation.watchPosition(t=>{y(this,M,R).call(this,t)},t=>{n(this,I).gpserror?n(this,I).gpserror(t.code):alert(`GPS error: code ${t.code}`)},{enableHighAccuracy:!0})),!0):!1}stopGps(){return n(this,L)!==null?(navigator.geolocation.clearWatch(n(this,L)),u(this,L,null),!0):!1}fakeGps(t,e,s=null,r=0){s!==null&&this.setElevation(s),y(this,M,R).call(this,{coords:{longitude:t,latitude:e,accuracy:r}})}lonLatToWorldCoords(t,e){const s=n(this,S).project(t,e);if(n(this,O))s[0]-=n(this,O)[0],s[1]-=n(this,O)[1];else throw"No initial position determined";return[s[0],-s[1]]}add(t,e,s,r,g={}){var p;t.properties=g,y(this,M,q).call(this,t,e,s,r),this.scene.add(t),(p=n(this,P))==null||p.sendData("/object/new",{position:t.position,x:t.position.x,z:t.position.z,session:n(this,E),properties:g})}setElevation(t){this.camera.position.y=t}on(t,e){n(this,I)[t]=e}}S=new WeakMap,I=new WeakMap,_=new WeakMap,j=new WeakMap,x=new WeakMap,L=new WeakMap,O=new WeakMap,T=new WeakMap,E=new WeakMap,P=new WeakMap,M=new WeakSet,q=function(t,e,s,r){const g=this.lonLatToWorldCoords(e,s);r!==void 0&&(t.position.y=r),[t.position.x,t.position.z]=g},V=function(t,e){u(this,O,n(this,S).project(t,e))},R=function(t){var s,r,g;let e=Number.MAX_VALUE;N(this,T)._++,(s=n(this,P))==null||s.sendData("/gps/new",{gpsCount:n(this,T),lat:t.coords.latitude,lon:t.coords.longitude,acc:t.coords.accuracy,session:n(this,E)}),t.coords.accuracy<=n(this,x)&&(n(this,_)===null?u(this,_,{latitude:t.coords.latitude,longitude:t.coords.longitude}):e=y(this,M,Y).call(this,n(this,_),t.coords),e>=n(this,j)&&(n(this,_).longitude=t.coords.longitude,n(this,_).latitude=t.coords.latitude,n(this,O)||(y(this,M,V).call(this,t.coords.longitude,t.coords.latitude),(r=n(this,P))==null||r.sendData("/worldorigin/new",{gpsCount:n(this,T),lat:t.coords.latitude,lon:t.coords.longitude,session:n(this,E),initialPosition:n(this,O)})),y(this,M,q).call(this,this.camera,t.coords.longitude,t.coords.latitude),(g=n(this,P))==null||g.sendData("/gps/accepted",{gpsCount:n(this,T),cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:n(this,E),distMoved:e}),n(this,I).gpsupdate&&n(this,I).gpsupdate(t,e)))},Y=function(t,e){const s=m.MathUtils.degToRad(e.longitude-t.longitude),r=m.MathUtils.degToRad(e.latitude-t.latitude),g=Math.sin(r/2)*Math.sin(r/2)+Math.cos(m.MathUtils.degToRad(t.latitude))*Math.cos(m.MathUtils.degToRad(e.latitude))*(Math.sin(s/2)*Math.sin(s/2));return 2*Math.atan2(Math.sqrt(g),Math.sqrt(1-g))*6371e3};class K{constructor(t,e,s){this.renderer=t,this.renderer.autoClear=!1,this.sceneWebcam=new m.Scene;let r;e?r=document.querySelector(e):(r=document.createElement("video"),r.setAttribute("autoplay",!0),r.setAttribute("playsinline",!0),r.style.display="none",document.body.appendChild(r)),this.geom=new m.PlaneGeometry,this.texture=new m.VideoTexture(r),this.material=new m.MeshBasicMaterial({map:this.texture});const g=new m.Mesh(this.geom,this.material);if(this.sceneWebcam.add(g),this.cameraWebcam=new m.OrthographicCamera(-.5,.5,.5,-.5,0,10),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia){const p={video:{width:(s==null?void 0:s.width)||1280,height:(s==null?void 0:s.height)||720,facingMode:"environment"}};navigator.mediaDevices.getUserMedia(p).then(C=>{console.log("using the webcam successfully..."),r.srcObject=C,r.play()}).catch(C=>{setTimeout(()=>{alert(`Webcam Error
2
+ Name: `+C.name+`
3
+ Message: `+C.message)},1e3)})}else setTimeout(()=>{alert("sorry - media devices API not supported")},1e3)}update(){this.renderer.clear(),this.renderer.render(this.sceneWebcam,this.cameraWebcam),this.renderer.clearDepth()}dispose(){this.material.dispose(),this.texture.dispose(),this.geom.dispose()}}const k=navigator.userAgent.match(/iPhone|iPad|iPod/i),$=new i.Vector3(0,0,1),z=new i.Euler,J=new i.Quaternion,tt=new i.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5)),et={type:"change"};class it extends i.EventDispatcher{constructor(t){super(),window.isSecureContext===!1&&console.error("THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)");const e=this,s=1e-6,r=new i.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 g=function({alpha:o,beta:h,gamma:l,webkitCompassHeading:c}){if(k){const d=360-c;e.alphaOffset=i.MathUtils.degToRad(d-o),e.deviceOrientation={alpha:o,beta:h,gamma:l,webkitCompassHeading:c}}else o<0&&(o+=360),e.deviceOrientation={alpha:o,beta:h,gamma:l};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.rotation}}))},p=function(){e.screenOrientation=window.orientation||0},C=function(o,h,l,c,d){z.set(l,h,-c,"YXZ"),o.setFromEuler(z),o.multiply(tt),o.multiply(J.setFromAxisAngle($,-d))};this.connect=function(){p(),window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(o=>{o==="granted"&&(window.addEventListener("orientationchange",p),window.addEventListener(e.orientationChangeEventName,g))}).catch(function(o){console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",o)}):(window.addEventListener("orientationchange",p),window.addEventListener(e.orientationChangeEventName,g)),e.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",p),window.removeEventListener(e.orientationChangeEventName,g),e.enabled=!1,e.initialOffset=!1,e.deviceOrientation=null},this.update=function({theta:o=0}={theta:0}){if(e.enabled===!1)return;const h=e.deviceOrientation;if(h){let l=h.alpha?i.MathUtils.degToRad(h.alpha)+e.alphaOffset:0,c=h.beta?i.MathUtils.degToRad(h.beta):0,d=h.gamma?i.MathUtils.degToRad(h.gamma):0;const F=e.screenOrientation?i.MathUtils.degToRad(e.screenOrientation):0;if(k){const A=new i.Quaternion;C(A,l,c,d,F);const D=new i.Euler().setFromQuaternion(A,"YXZ");console.log(D.x,D.y,D.z),D.y=i.MathUtils.degToRad(360-h.webkitCompassHeading),A.setFromEuler(D),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),c=this._getSmoothedAngle(c+Math.PI,this.lastOrientation.beta,A),d=this._getSmoothedAngle(d+this.HALF_PI,this.lastOrientation.gamma,A,Math.PI)}else c+=Math.PI,d+=this.HALF_PI;this.lastOrientation={alpha:l,beta:c,gamma:d}}C(e.object.quaternion,l+o,this.smoothingFactor<1?c-Math.PI:c,this.smoothingFactor<1?d-this.HALF_PI:d,F)}8*(1-r.dot(e.object.quaternion))>s&&(r.copy(e.object.quaternion),e.dispatchEvent(et))}},this._orderAngle=function(o,h,l=this.TWO_PI){return h>o&&Math.abs(h-o)<l/2||o>h&&Math.abs(h-o)>l/2?{left:o,right:h}:{left:h,right:o}},this._getSmoothedAngle=function(o,h,l,c=this.TWO_PI){const d=this._orderAngle(o,h,c),F=d.left,A=d.right;d.left=0,d.right-=F,d.right<0&&(d.right+=c);let D=A==h?(1-l)*d.right+l*d.left:l*d.right+(1-l)*d.left;return D+=F,D>=c&&(D-=c),D},this.updateAlphaOffset=function(){e.initialOffset=!1},this.dispose=function(){e.disconnect()},this.getAlpha=function(){const{deviceOrientation:o}=e;return o&&o.alpha?i.MathUtils.degToRad(o.alpha)+e.alphaOffset:0},this.getBeta=function(){const{deviceOrientation:o}=e;return o&&o.beta?i.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"),s=document.createElement("div"),r=document.createElement("div");document.body.appendChild(t);const g={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"},C={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 g)t.style[c]=g[c];for(let c in p)e.style[c]=p[c];for(let c in C)s.style[c]=C[c];for(let c in o)r.style[c]=o[c];t.appendChild(e),e.appendChild(s),e.appendChild(r),s.innerHTML='<div style="font-size: 24pt; margin: 1rem;">This immersive website requires access to your device motion sensors.</div>';const h=()=>{this.connect(),t.style.display="none"},l=document.createElement("button");l.addEventListener("click",h),l.style.width="50%",l.style.height="80%",l.style.fontSize="20pt",l.appendChild(document.createTextNode("OK")),r.appendChild(l),document.body.appendChild(t)}}class nt{constructor(t){this.raycaster=new m.Raycaster,this.normalisedMousePosition=new m.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 s=this.raycaster.intersectObjects(e.children,!1);return this.normalisedMousePosition.set(null,null),s}return[]}}a.ClickHandler=nt,a.DeviceOrientationControls=it,a.LocationBased=Z,a.SphMercProjection=W,a.WebcamRenderer=K,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locar",
3
- "version": "0.0.3",
3
+ "version": "0.0.7",
4
4
  "description": "Location-based AR from AR.js.",
5
5
  "files": [
6
6
  "dist"
@@ -14,9 +14,11 @@
14
14
  }
15
15
  },
16
16
  "scripts": {
17
- "build": "vite build && npm pack"
17
+ "build": "vite build && npm pack",
18
+ "makedocs": "jsdoc -r lib -d docs/api"
18
19
  },
19
20
  "devDependencies": {
21
+ "jsdoc": "^4.0.4",
20
22
  "vite": "^5.4.8"
21
23
  }
22
24
  }