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