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