|
@@ -1,11 +1,11 @@
|
1
|
1
|
/*!
|
2
|
|
- * encantAR.js version 0.3.0
|
|
2
|
+ * encantar.js version 0.3.0
|
3
|
3
|
* GPU-accelerated Augmented Reality for the web
|
4
|
4
|
* Copyright 2022-2024 Alexandre Martins <alemartf(at)gmail.com> (https://github.com/alemart)
|
5
|
5
|
* https://github.com/alemart/encantar-js
|
6
|
6
|
*
|
7
|
7
|
* @license LGPL-3.0-or-later
|
8
|
|
- * Date: 2024-10-05T04:17:49.391Z
|
|
8
|
+ * Date: 2024-10-20T01:13:15.146Z
|
9
|
9
|
*/
|
10
|
10
|
(function webpackUniversalModuleDefinition(root, factory) {
|
11
|
11
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
@@ -19538,16 +19538,21 @@ class TrainingError extends BaseError {
|
19538
|
19538
|
*/
|
19539
|
19539
|
|
19540
|
19540
|
|
19541
|
|
-/** Reference heights when in landscape mode, measured in pixels */
|
19542
|
|
-const REFERENCE_HEIGHT = {
|
|
19541
|
+/** A regex that identifies custom resolutions */
|
|
19542
|
+const CUSTOM_RESOLUTION_REGEX = /^[1-9][0-9]?[0-9][02468]p$/;
|
|
19543
|
+/** Reference heights when in landscape mode, measured in pixels, for all aliases */
|
|
19544
|
+const ALIAS_TO_HEIGHT = {
|
19543
|
19545
|
'xs': 120,
|
19544
|
|
- 'xs+': 160,
|
19545
|
|
- 'sm': 200,
|
19546
|
|
- 'sm+': 240,
|
|
19546
|
+ 'xs+': 144,
|
|
19547
|
+ 'sm': 240,
|
|
19548
|
+ 'sm+': 288,
|
19547
|
19549
|
'md': 320,
|
19548
|
19550
|
'md+': 360,
|
19549
|
19551
|
'lg': 480,
|
19550
|
19552
|
'lg+': 600,
|
|
19553
|
+ 'xl': 720,
|
|
19554
|
+ 'xl+': 900,
|
|
19555
|
+ 'xxl': 1080,
|
19551
|
19556
|
};
|
19552
|
19557
|
/**
|
19553
|
19558
|
* Convert a resolution type to a (width, height) pair
|
|
@@ -19556,26 +19561,38 @@ const REFERENCE_HEIGHT = {
|
19556
|
19561
|
* @returns size in pixels
|
19557
|
19562
|
*/
|
19558
|
19563
|
function computeResolution(resolution, aspectRatio) {
|
19559
|
|
- const referenceHeight = REFERENCE_HEIGHT[resolution];
|
|
19564
|
+ const referenceHeight = parseHeight(resolution);
|
19560
|
19565
|
let width = 0, height = 0;
|
19561
|
|
- if (referenceHeight === undefined)
|
|
19566
|
+ if (Number.isNaN(referenceHeight))
|
19562
|
19567
|
throw new IllegalArgumentError('Invalid resolution: ' + resolution);
|
19563
|
19568
|
else if (aspectRatio <= 0)
|
19564
|
19569
|
throw new IllegalArgumentError('Invalid aspect ratio: ' + aspectRatio);
|
19565
|
19570
|
if (aspectRatio >= 1) {
|
19566
|
19571
|
// landscape
|
19567
|
19572
|
height = referenceHeight;
|
19568
|
|
- width = Math.round(height * aspectRatio);
|
19569
|
|
- width -= width % 2;
|
|
19573
|
+ width = Math.floor(height * aspectRatio);
|
|
19574
|
+ width += width % 2;
|
19570
|
19575
|
}
|
19571
|
19576
|
else {
|
19572
|
19577
|
// portrait
|
19573
|
19578
|
width = referenceHeight;
|
19574
|
|
- height = Math.round(width / aspectRatio);
|
19575
|
|
- height -= height % 2;
|
|
19579
|
+ height = Math.floor(width / aspectRatio);
|
|
19580
|
+ height += height % 2;
|
19576
|
19581
|
}
|
19577
|
19582
|
return speedy_vision_default().Size(width, height);
|
19578
|
19583
|
}
|
|
19584
|
+/**
|
|
19585
|
+ * Get the height in pixels of a resolution
|
|
19586
|
+ * @param resolution resolution type
|
|
19587
|
+ * @returns the height in pixels, or NaN on error
|
|
19588
|
+ */
|
|
19589
|
+function parseHeight(resolution) {
|
|
19590
|
+ if (ALIAS_TO_HEIGHT.hasOwnProperty(resolution))
|
|
19591
|
+ return ALIAS_TO_HEIGHT[resolution];
|
|
19592
|
+ if (CUSTOM_RESOLUTION_REGEX.test(resolution))
|
|
19593
|
+ return parseInt(resolution);
|
|
19594
|
+ return Number.NaN;
|
|
19595
|
+}
|
19579
|
19596
|
|
19580
|
19597
|
;// CONCATENATED MODULE: ./src/utils/utils.ts
|
19581
|
19598
|
/*
|
|
@@ -19891,7 +19908,7 @@ class Stats {
|
19891
|
19908
|
}
|
19892
|
19909
|
}
|
19893
|
19910
|
|
19894
|
|
-;// CONCATENATED MODULE: ./src/core/stats-panel.ts
|
|
19911
|
+;// CONCATENATED MODULE: ./src/ui/stats-panel.ts
|
19895
|
19912
|
/*
|
19896
|
19913
|
* encantar.js
|
19897
|
19914
|
* GPU-accelerated Augmented Reality for the web
|
|
@@ -19915,7 +19932,6 @@ class Stats {
|
19915
|
19932
|
*/
|
19916
|
19933
|
|
19917
|
19934
|
|
19918
|
|
-
|
19919
|
19935
|
/** Update interval, in ms */
|
19920
|
19936
|
const stats_panel_UPDATE_INTERVAL = 500;
|
19921
|
19937
|
/** Icons for different power profiles */
|
|
@@ -19930,7 +19946,7 @@ const POWER_ICON = Object.freeze({
|
19930
|
19946
|
class StatsPanel {
|
19931
|
19947
|
/**
|
19932
|
19948
|
* Constructor
|
19933
|
|
- * @param parent parent element of the panel
|
|
19949
|
+ * @param viewport Viewport
|
19934
|
19950
|
*/
|
19935
|
19951
|
constructor(viewport) {
|
19936
|
19952
|
this._viewport = viewport;
|
|
@@ -20049,8 +20065,7 @@ class StatsPanel {
|
20049
|
20065
|
title.style.fontSize = '14px';
|
20050
|
20066
|
title.style.fontWeight = 'bold';
|
20051
|
20067
|
title.style.padding = '2px';
|
20052
|
|
- title.innerHTML = '✨';
|
20053
|
|
- title.innerText += 'encantar.js ' + AR.version;
|
|
20068
|
+ title.innerText = 'encantar.js ' + AR.version;
|
20054
|
20069
|
return title;
|
20055
|
20070
|
}
|
20056
|
20071
|
/**
|
|
@@ -20074,179 +20089,11 @@ class StatsPanel {
|
20074
|
20089
|
print('IN: <span class="_ar_in"></span>');
|
20075
|
20090
|
print('<br>');
|
20076
|
20091
|
print('OUT: <span class="_ar_out"></span>');
|
20077
|
|
- if (this._viewport.fullscreenAvailable) {
|
20078
|
|
- print('<br>');
|
20079
|
|
- content.appendChild(this._createFullscreenToggle());
|
20080
|
|
- }
|
20081
|
20092
|
return content;
|
20082
|
20093
|
}
|
20083
|
|
- /**
|
20084
|
|
- * Create a fullscreen toggle
|
20085
|
|
- * @returns a fullscreen toggle
|
20086
|
|
- */
|
20087
|
|
- _createFullscreenToggle() {
|
20088
|
|
- const toggle = document.createElement('a');
|
20089
|
|
- Utils.assert(this._viewport != null);
|
20090
|
|
- toggle.href = 'javascript:void(0)';
|
20091
|
|
- toggle.innerText = 'Toggle fullscreen';
|
20092
|
|
- toggle.style.color = 'white';
|
20093
|
|
- toggle.setAttribute('role', 'button');
|
20094
|
|
- toggle.addEventListener('click', () => {
|
20095
|
|
- if (!this._viewport.fullscreen) {
|
20096
|
|
- this._viewport.requestFullscreen().catch(err => {
|
20097
|
|
- alert(`Can't enable fullscreen mode. ` + err.toString());
|
20098
|
|
- });
|
20099
|
|
- }
|
20100
|
|
- else {
|
20101
|
|
- this._viewport.exitFullscreen();
|
20102
|
|
- }
|
20103
|
|
- });
|
20104
|
|
- return toggle;
|
20105
|
|
- }
|
20106
|
|
-}
|
20107
|
|
-
|
20108
|
|
-;// CONCATENATED MODULE: ./src/core/frame.ts
|
20109
|
|
-/*
|
20110
|
|
- * encantar.js
|
20111
|
|
- * GPU-accelerated Augmented Reality for the web
|
20112
|
|
- * Copyright (C) 2022-2024 Alexandre Martins <alemartf(at)gmail.com>
|
20113
|
|
- *
|
20114
|
|
- * This program is free software: you can redistribute it and/or modify
|
20115
|
|
- * it under the terms of the GNU Lesser General Public License as published
|
20116
|
|
- * by the Free Software Foundation, either version 3 of the License, or
|
20117
|
|
- * (at your option) any later version.
|
20118
|
|
- *
|
20119
|
|
- * This program is distributed in the hope that it will be useful,
|
20120
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
20121
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
20122
|
|
- * GNU Lesser General Public License for more details.
|
20123
|
|
- *
|
20124
|
|
- * You should have received a copy of the GNU Lesser General Public License
|
20125
|
|
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20126
|
|
- *
|
20127
|
|
- * frame.ts
|
20128
|
|
- * A Frame holds information used to render a single animation frame of a Session
|
20129
|
|
- */
|
20130
|
|
-/**
|
20131
|
|
- * A Frame holds information used to render a single animation frame of a Session
|
20132
|
|
- */
|
20133
|
|
-class Frame {
|
20134
|
|
- /**
|
20135
|
|
- * Constructor
|
20136
|
|
- * @param session
|
20137
|
|
- * @param results
|
20138
|
|
- */
|
20139
|
|
- constructor(session, results) {
|
20140
|
|
- this._session = session;
|
20141
|
|
- this._results = results;
|
20142
|
|
- }
|
20143
|
|
- /**
|
20144
|
|
- * The session of which this frame holds data
|
20145
|
|
- */
|
20146
|
|
- get session() {
|
20147
|
|
- return this._session;
|
20148
|
|
- }
|
20149
|
|
- /**
|
20150
|
|
- * The results of all trackers in this frame
|
20151
|
|
- */
|
20152
|
|
- get results() {
|
20153
|
|
- // we want to be able to iterate over the results of a frame multiple times
|
20154
|
|
- return this._results[Symbol.iterator]();
|
20155
|
|
- }
|
20156
|
|
-}
|
20157
|
|
-
|
20158
|
|
-;// CONCATENATED MODULE: ./src/core/time.ts
|
20159
|
|
-/*
|
20160
|
|
- * encantar.js
|
20161
|
|
- * GPU-accelerated Augmented Reality for the web
|
20162
|
|
- * Copyright (C) 2022-2024 Alexandre Martins <alemartf(at)gmail.com>
|
20163
|
|
- *
|
20164
|
|
- * This program is free software: you can redistribute it and/or modify
|
20165
|
|
- * it under the terms of the GNU Lesser General Public License as published
|
20166
|
|
- * by the Free Software Foundation, either version 3 of the License, or
|
20167
|
|
- * (at your option) any later version.
|
20168
|
|
- *
|
20169
|
|
- * This program is distributed in the hope that it will be useful,
|
20170
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
20171
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
20172
|
|
- * GNU Lesser General Public License for more details.
|
20173
|
|
- *
|
20174
|
|
- * You should have received a copy of the GNU Lesser General Public License
|
20175
|
|
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20176
|
|
- *
|
20177
|
|
- * time.ts
|
20178
|
|
- * Time utilities
|
20179
|
|
- */
|
20180
|
|
-/**
|
20181
|
|
- * Time Manager
|
20182
|
|
- */
|
20183
|
|
-class Time {
|
20184
|
|
- constructor() {
|
20185
|
|
- /** time scale */
|
20186
|
|
- this._scale = 1;
|
20187
|
|
- /** time since the start of the session, in milliseconds */
|
20188
|
|
- this._time = 0;
|
20189
|
|
- /** unscaled time since the start of the session, in milliseconds */
|
20190
|
|
- this._unscaledTime = 0;
|
20191
|
|
- /** elapsed time between the current and the previous frame, in milliseconds */
|
20192
|
|
- this._delta = 0;
|
20193
|
|
- /** time of the first update call, in milliseconds */
|
20194
|
|
- this._firstUpdate = 0;
|
20195
|
|
- /** time of the last update call, in milliseconds */
|
20196
|
|
- this._lastUpdate = Number.POSITIVE_INFINITY;
|
20197
|
|
- }
|
20198
|
|
- /**
|
20199
|
|
- * Update the Time Manager
|
20200
|
|
- * @param timestamp in milliseconds
|
20201
|
|
- * @internal
|
20202
|
|
- */
|
20203
|
|
- _update(timestamp) {
|
20204
|
|
- if (timestamp < this._lastUpdate) {
|
20205
|
|
- this._firstUpdate = this._lastUpdate = timestamp;
|
20206
|
|
- return;
|
20207
|
|
- }
|
20208
|
|
- this._delta = (timestamp - this._lastUpdate) * this._scale;
|
20209
|
|
- this._time += this._delta;
|
20210
|
|
- this._unscaledTime = timestamp - this._firstUpdate;
|
20211
|
|
- this._lastUpdate = timestamp;
|
20212
|
|
- }
|
20213
|
|
- /**
|
20214
|
|
- * Elapsed time since the start of the session, measured at the
|
20215
|
|
- * beginning of the current animation frame and given in seconds
|
20216
|
|
- */
|
20217
|
|
- get elapsed() {
|
20218
|
|
- return this._time * 0.001;
|
20219
|
|
- }
|
20220
|
|
- /**
|
20221
|
|
- * Elapsed time between the current and the previous animation
|
20222
|
|
- * frame, given in seconds
|
20223
|
|
- */
|
20224
|
|
- get delta() {
|
20225
|
|
- return this._delta * 0.001;
|
20226
|
|
- }
|
20227
|
|
- /**
|
20228
|
|
- * Time scale (defaults to 1)
|
20229
|
|
- */
|
20230
|
|
- get scale() {
|
20231
|
|
- return this._scale;
|
20232
|
|
- }
|
20233
|
|
- /**
|
20234
|
|
- * Time scale (defaults to 1)
|
20235
|
|
- */
|
20236
|
|
- set scale(scale) {
|
20237
|
|
- this._scale = Math.max(0, +scale);
|
20238
|
|
- }
|
20239
|
|
- /**
|
20240
|
|
- * Time scale independent elapsed time since the start of the session,
|
20241
|
|
- * measured at the beginning of the current animation frame and given
|
20242
|
|
- * in seconds
|
20243
|
|
- */
|
20244
|
|
- get unscaled() {
|
20245
|
|
- return this._unscaledTime * 0.001;
|
20246
|
|
- }
|
20247
|
20094
|
}
|
20248
|
20095
|
|
20249
|
|
-;// CONCATENATED MODULE: ./src/core/gizmos.ts
|
|
20096
|
+;// CONCATENATED MODULE: ./src/ui/gizmos.ts
|
20250
|
20097
|
/*
|
20251
|
20098
|
* encantar.js
|
20252
|
20099
|
* GPU-accelerated Augmented Reality for the web
|
|
@@ -20470,6 +20317,147 @@ class Gizmos {
|
20470
|
20317
|
}
|
20471
|
20318
|
}
|
20472
|
20319
|
|
|
20320
|
+;// CONCATENATED MODULE: ./src/core/frame.ts
|
|
20321
|
+/*
|
|
20322
|
+ * encantar.js
|
|
20323
|
+ * GPU-accelerated Augmented Reality for the web
|
|
20324
|
+ * Copyright (C) 2022-2024 Alexandre Martins <alemartf(at)gmail.com>
|
|
20325
|
+ *
|
|
20326
|
+ * This program is free software: you can redistribute it and/or modify
|
|
20327
|
+ * it under the terms of the GNU Lesser General Public License as published
|
|
20328
|
+ * by the Free Software Foundation, either version 3 of the License, or
|
|
20329
|
+ * (at your option) any later version.
|
|
20330
|
+ *
|
|
20331
|
+ * This program is distributed in the hope that it will be useful,
|
|
20332
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20333
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20334
|
+ * GNU Lesser General Public License for more details.
|
|
20335
|
+ *
|
|
20336
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
20337
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
20338
|
+ *
|
|
20339
|
+ * frame.ts
|
|
20340
|
+ * A Frame holds information used to render a single animation frame of a Session
|
|
20341
|
+ */
|
|
20342
|
+/**
|
|
20343
|
+ * A Frame holds information used to render a single animation frame of a Session
|
|
20344
|
+ */
|
|
20345
|
+class Frame {
|
|
20346
|
+ /**
|
|
20347
|
+ * Constructor
|
|
20348
|
+ * @param session
|
|
20349
|
+ * @param results
|
|
20350
|
+ */
|
|
20351
|
+ constructor(session, results) {
|
|
20352
|
+ this._session = session;
|
|
20353
|
+ this._results = results;
|
|
20354
|
+ }
|
|
20355
|
+ /**
|
|
20356
|
+ * The session of which this frame holds data
|
|
20357
|
+ */
|
|
20358
|
+ get session() {
|
|
20359
|
+ return this._session;
|
|
20360
|
+ }
|
|
20361
|
+ /**
|
|
20362
|
+ * The results of all trackers in this frame
|
|
20363
|
+ */
|
|
20364
|
+ get results() {
|
|
20365
|
+ // we want to be able to iterate over the results of a frame multiple times
|
|
20366
|
+ return this._results[Symbol.iterator]();
|
|
20367
|
+ }
|
|
20368
|
+}
|
|
20369
|
+
|
|
20370
|
+;// CONCATENATED MODULE: ./src/core/time.ts
|
|
20371
|
+/*
|
|
20372
|
+ * encantar.js
|
|
20373
|
+ * GPU-accelerated Augmented Reality for the web
|
|
20374
|
+ * Copyright (C) 2022-2024 Alexandre Martins <alemartf(at)gmail.com>
|
|
20375
|
+ *
|
|
20376
|
+ * This program is free software: you can redistribute it and/or modify
|
|
20377
|
+ * it under the terms of the GNU Lesser General Public License as published
|
|
20378
|
+ * by the Free Software Foundation, either version 3 of the License, or
|
|
20379
|
+ * (at your option) any later version.
|
|
20380
|
+ *
|
|
20381
|
+ * This program is distributed in the hope that it will be useful,
|
|
20382
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20383
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20384
|
+ * GNU Lesser General Public License for more details.
|
|
20385
|
+ *
|
|
20386
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
20387
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
20388
|
+ *
|
|
20389
|
+ * time.ts
|
|
20390
|
+ * Time utilities
|
|
20391
|
+ */
|
|
20392
|
+/**
|
|
20393
|
+ * Time Manager
|
|
20394
|
+ */
|
|
20395
|
+class Time {
|
|
20396
|
+ constructor() {
|
|
20397
|
+ /** time scale */
|
|
20398
|
+ this._scale = 1;
|
|
20399
|
+ /** time since the start of the session, in milliseconds */
|
|
20400
|
+ this._time = 0;
|
|
20401
|
+ /** unscaled time since the start of the session, in milliseconds */
|
|
20402
|
+ this._unscaledTime = 0;
|
|
20403
|
+ /** elapsed time between the current and the previous frame, in milliseconds */
|
|
20404
|
+ this._delta = 0;
|
|
20405
|
+ /** time of the first update call, in milliseconds */
|
|
20406
|
+ this._firstUpdate = 0;
|
|
20407
|
+ /** time of the last update call, in milliseconds */
|
|
20408
|
+ this._lastUpdate = Number.POSITIVE_INFINITY;
|
|
20409
|
+ }
|
|
20410
|
+ /**
|
|
20411
|
+ * Update the Time Manager
|
|
20412
|
+ * @param timestamp in milliseconds
|
|
20413
|
+ * @internal
|
|
20414
|
+ */
|
|
20415
|
+ _update(timestamp) {
|
|
20416
|
+ if (timestamp < this._lastUpdate) {
|
|
20417
|
+ this._firstUpdate = this._lastUpdate = timestamp;
|
|
20418
|
+ return;
|
|
20419
|
+ }
|
|
20420
|
+ this._delta = (timestamp - this._lastUpdate) * this._scale;
|
|
20421
|
+ this._time += this._delta;
|
|
20422
|
+ this._unscaledTime = timestamp - this._firstUpdate;
|
|
20423
|
+ this._lastUpdate = timestamp;
|
|
20424
|
+ }
|
|
20425
|
+ /**
|
|
20426
|
+ * Elapsed time since the start of the session, measured at the
|
|
20427
|
+ * beginning of the current animation frame and given in seconds
|
|
20428
|
+ */
|
|
20429
|
+ get elapsed() {
|
|
20430
|
+ return this._time * 0.001;
|
|
20431
|
+ }
|
|
20432
|
+ /**
|
|
20433
|
+ * Elapsed time between the current and the previous animation
|
|
20434
|
+ * frame, given in seconds
|
|
20435
|
+ */
|
|
20436
|
+ get delta() {
|
|
20437
|
+ return this._delta * 0.001;
|
|
20438
|
+ }
|
|
20439
|
+ /**
|
|
20440
|
+ * Time scale (defaults to 1)
|
|
20441
|
+ */
|
|
20442
|
+ get scale() {
|
|
20443
|
+ return this._scale;
|
|
20444
|
+ }
|
|
20445
|
+ /**
|
|
20446
|
+ * Time scale (defaults to 1)
|
|
20447
|
+ */
|
|
20448
|
+ set scale(scale) {
|
|
20449
|
+ this._scale = Math.max(0, +scale);
|
|
20450
|
+ }
|
|
20451
|
+ /**
|
|
20452
|
+ * Time scale independent elapsed time since the start of the session,
|
|
20453
|
+ * measured at the beginning of the current animation frame and given
|
|
20454
|
+ * in seconds
|
|
20455
|
+ */
|
|
20456
|
+ get unscaled() {
|
|
20457
|
+ return this._unscaledTime * 0.001;
|
|
20458
|
+ }
|
|
20459
|
+}
|
|
20460
|
+
|
20473
|
20461
|
;// CONCATENATED MODULE: ./src/utils/asap.ts
|
20474
|
20462
|
/*
|
20475
|
20463
|
* encantar.js
|
|
@@ -20592,26 +20580,11 @@ class Session extends AREventTarget {
|
20592
|
20580
|
this._gizmos = new Gizmos();
|
20593
|
20581
|
this._gizmos.visible = gizmos;
|
20594
|
20582
|
// validate the mode
|
20595
|
|
- if (mode == 'immersive') {
|
20596
|
|
- if (viewport.style != 'best-fit' && viewport.style != 'stretch') {
|
20597
|
|
- Utils.warning(`Invalid viewport style \"${viewport.style}\" for the \"${mode}\" mode`);
|
20598
|
|
- viewport.style = 'best-fit';
|
20599
|
|
- }
|
20600
|
|
- }
|
20601
|
|
- else if (mode == 'inline') {
|
20602
|
|
- if (viewport.style != 'inline') {
|
20603
|
|
- Utils.warning(`Invalid viewport style \"${viewport.style}\" for the \"${mode}\" mode`);
|
20604
|
|
- viewport.style = 'inline';
|
20605
|
|
- }
|
20606
|
|
- }
|
20607
|
|
- else
|
|
20583
|
+ if (mode != 'immersive' && mode != 'inline')
|
20608
|
20584
|
throw new IllegalArgumentError(`Invalid session mode "${mode}"`);
|
20609
|
|
- // get media
|
20610
|
|
- const media = this.media;
|
20611
|
|
- const getMediaSize = () => media.size;
|
20612
|
20585
|
// setup the viewport
|
20613
|
20586
|
this._viewport = viewport;
|
20614
|
|
- this._viewport._init(getMediaSize);
|
|
20587
|
+ this._viewport._init(() => this.media.size, mode);
|
20615
|
20588
|
// setup the main loop
|
20616
|
20589
|
this._setupUpdateLoop();
|
20617
|
20590
|
this._setupRenderLoop();
|
|
@@ -21184,16 +21157,19 @@ class ReferenceImageDatabase {
|
21184
|
21157
|
const referenceImage = referenceImages[0];
|
21185
|
21158
|
// locked database?
|
21186
|
21159
|
if (this._locked)
|
21187
|
|
- throw new IllegalOperationError(`Can't add reference image to the database: it's locked`);
|
|
21160
|
+ throw new IllegalOperationError(`Can't add reference image "${referenceImage.name}" to the database: it's locked`);
|
21188
|
21161
|
// busy loading another image?
|
21189
|
21162
|
if (this._busy)
|
21190
|
21163
|
return Utils.wait(4).then(() => this.add(referenceImages)); // try again later
|
21191
|
21164
|
// reached full capacity?
|
21192
|
21165
|
if (this.count >= this.capacity)
|
21193
|
|
- throw new IllegalOperationError(`Can't add reference image to the database: the capacity of ${this.capacity} images has been exceeded.`);
|
|
21166
|
+ throw new IllegalOperationError(`Can't add reference image "${referenceImage.name}" to the database: the capacity of ${this.capacity} images has been exceeded.`);
|
|
21167
|
+ // check if the image is valid
|
|
21168
|
+ if (!(referenceImage.image instanceof HTMLImageElement) && !(referenceImage.image instanceof HTMLCanvasElement) && !(referenceImage.image instanceof ImageBitmap))
|
|
21169
|
+ throw new IllegalArgumentError(`Can't add reference image "${referenceImage.name}" to the database: invalid image`);
|
21194
|
21170
|
// check for duplicate names
|
21195
|
21171
|
if (this._database.find(entry => entry.referenceImage.name === referenceImage.name) !== undefined)
|
21196
|
|
- throw new IllegalArgumentError(`Can't add reference image to the database: found duplicated name "${referenceImage.name}"`);
|
|
21172
|
+ throw new IllegalArgumentError(`Can't add reference image "${referenceImage.name}" to the database: found duplicated name`);
|
21197
|
21173
|
// load the media and add the reference image to the database
|
21198
|
21174
|
this._busy = true;
|
21199
|
21175
|
return speedy_vision_default().load(referenceImage.image).then(media => {
|
|
@@ -21251,7 +21227,7 @@ class ReferenceImageDatabase {
|
21251
|
21227
|
* Settings of the Image Tracker
|
21252
|
21228
|
*/
|
21253
|
21229
|
/** Default tracking resolution */
|
21254
|
|
-const DEFAULT_TRACKING_RESOLUTION = 'sm+';
|
|
21230
|
+const DEFAULT_TRACKING_RESOLUTION = 'sm';
|
21255
|
21231
|
/** Maximum number of keypoints to be stored for each reference image when in the training state */
|
21256
|
21232
|
const TRAIN_MAX_KEYPOINTS = 1024; //512;
|
21257
|
21233
|
/** Percentage relative to the screen size adjusted to the aspect ratio of the reference image */
|
|
@@ -25377,6 +25353,127 @@ class HUD {
|
25377
|
25353
|
}
|
25378
|
25354
|
}
|
25379
|
25355
|
|
|
25356
|
+;// CONCATENATED MODULE: ./src/ui/fullscreen-button.ts
|
|
25357
|
+/*
|
|
25358
|
+ * encantar.js
|
|
25359
|
+ * GPU-accelerated Augmented Reality for the web
|
|
25360
|
+ * Copyright (C) 2022-2024 Alexandre Martins <alemartf(at)gmail.com>
|
|
25361
|
+ *
|
|
25362
|
+ * This program is free software: you can redistribute it and/or modify
|
|
25363
|
+ * it under the terms of the GNU Lesser General Public License as published
|
|
25364
|
+ * by the Free Software Foundation, either version 3 of the License, or
|
|
25365
|
+ * (at your option) any later version.
|
|
25366
|
+ *
|
|
25367
|
+ * This program is distributed in the hope that it will be useful,
|
|
25368
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
25369
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
25370
|
+ * GNU Lesser General Public License for more details.
|
|
25371
|
+ *
|
|
25372
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
25373
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
25374
|
+ *
|
|
25375
|
+ * fullscreen-button.ts
|
|
25376
|
+ * A built-in fullscreen button introduced as a convenience
|
|
25377
|
+ */
|
|
25378
|
+/** Button icon to be displayed when the fullscreen mode is disabled */
|
|
25379
|
+const BUTTON_ICON_OFF = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAbUlEQVRYR+2WOQ4AIAgE5f+PVhobDZANBZAsraAwXMoqFil+f9GBj8BW8dIiKt45at/XgShStHgvmfdekwAdIIEyAmh1Z/U5ikmABPoRsLZWtt+5DUlgHgGr6qM1Pf9XnO131L7fJEQjyOqXEzjP1YAhNmUTrgAAAABJRU5ErkJggg==';
|
|
25380
|
+/** Button icon to be displayed when the fullscreen mode is enabled */
|
|
25381
|
+const BUTTON_ICON_ON = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAZElEQVRYR+2WwRIAEAhE9f8fTQ5OhtkLxbzOyc5rJSvBYcH3FwTIBKpHb5d57Nqm5o0aCIBAPgLDxSunq69APT8RCBdwezTLHjglDAEQgEC+QZR2EqqbjprHRgSB9wjwHX9LoAHP1YAhXF4Z/QAAAABJRU5ErkJggg==';
|
|
25382
|
+/** Button size, in pixels */
|
|
25383
|
+const BUTTON_SIZE = 64;
|
|
25384
|
+/** Button margin, in pixels */
|
|
25385
|
+const BUTTON_MARGIN = 24;
|
|
25386
|
+/**
|
|
25387
|
+ * Built-in fullscreen button
|
|
25388
|
+ */
|
|
25389
|
+class FullscreenButton {
|
|
25390
|
+ /**
|
|
25391
|
+ * Constructor
|
|
25392
|
+ * @param viewport Viewport
|
|
25393
|
+ */
|
|
25394
|
+ constructor(viewport) {
|
|
25395
|
+ this._viewport = viewport;
|
|
25396
|
+ this._button = this._createButton();
|
|
25397
|
+ this._boundEventHandler = this._handleFullscreenEvent.bind(this);
|
|
25398
|
+ }
|
|
25399
|
+ /**
|
|
25400
|
+ * Initialize
|
|
25401
|
+ */
|
|
25402
|
+ init() {
|
|
25403
|
+ this._viewport.hud.container.appendChild(this._button);
|
|
25404
|
+ this._viewport.addEventListener('fullscreenchange', this._boundEventHandler);
|
|
25405
|
+ }
|
|
25406
|
+ /**
|
|
25407
|
+ * Release
|
|
25408
|
+ */
|
|
25409
|
+ release() {
|
|
25410
|
+ this._viewport.removeEventListener('fullscreenchange', this._boundEventHandler);
|
|
25411
|
+ this._button.remove();
|
|
25412
|
+ }
|
|
25413
|
+ /**
|
|
25414
|
+ * Create the <button> element
|
|
25415
|
+ */
|
|
25416
|
+ _createButton() {
|
|
25417
|
+ const button = document.createElement('button');
|
|
25418
|
+ const icon = document.createElement('img');
|
|
25419
|
+ button.style.position = 'absolute';
|
|
25420
|
+ button.style.bottom = BUTTON_MARGIN + 'px';
|
|
25421
|
+ button.style.right = BUTTON_MARGIN + 'px';
|
|
25422
|
+ button.style.width = BUTTON_SIZE + 'px';
|
|
25423
|
+ button.style.height = BUTTON_SIZE + 'px';
|
|
25424
|
+ button.style.display = 'flex';
|
|
25425
|
+ button.style.alignItems = 'center';
|
|
25426
|
+ button.style.padding = '2px';
|
|
25427
|
+ button.style.opacity = '0.5';
|
|
25428
|
+ button.style.outline = 'none';
|
|
25429
|
+ button.style.cursor = 'pointer';
|
|
25430
|
+ button.draggable = false;
|
|
25431
|
+ button.style.backgroundColor = 'transparent';
|
|
25432
|
+ button.style.borderColor = 'white';
|
|
25433
|
+ button.style.borderStyle = 'solid';
|
|
25434
|
+ button.style.borderWidth = '2px';
|
|
25435
|
+ button.style.borderRadius = '8px';
|
|
25436
|
+ icon.src = BUTTON_ICON_OFF;
|
|
25437
|
+ icon.draggable = false;
|
|
25438
|
+ icon.style.display = 'inline-block';
|
|
25439
|
+ icon.style.width = '100%';
|
|
25440
|
+ icon.style.height = '100%';
|
|
25441
|
+ icon.style.imageRendering = 'pixelated';
|
|
25442
|
+ button.appendChild(icon);
|
|
25443
|
+ const highlight = () => {
|
|
25444
|
+ button.style.backgroundColor = '#ffd500';
|
|
25445
|
+ button.style.borderColor = '#ffd500';
|
|
25446
|
+ button.style.opacity = '1.0';
|
|
25447
|
+ };
|
|
25448
|
+ const dehighlight = () => {
|
|
25449
|
+ button.style.backgroundColor = 'transparent';
|
|
25450
|
+ button.style.borderColor = 'white';
|
|
25451
|
+ button.style.opacity = '0.5';
|
|
25452
|
+ };
|
|
25453
|
+ button.addEventListener('pointerdown', highlight);
|
|
25454
|
+ button.addEventListener('pointerup', dehighlight);
|
|
25455
|
+ button.addEventListener('pointerleave', dehighlight);
|
|
25456
|
+ button.addEventListener('click', () => {
|
|
25457
|
+ if (!this._viewport.fullscreen) {
|
|
25458
|
+ this._viewport.requestFullscreen().catch(err => {
|
|
25459
|
+ alert(`Can't enable the fullscreen mode. ` + err.toString());
|
|
25460
|
+ });
|
|
25461
|
+ }
|
|
25462
|
+ else {
|
|
25463
|
+ this._viewport.exitFullscreen();
|
|
25464
|
+ }
|
|
25465
|
+ });
|
|
25466
|
+ return button;
|
|
25467
|
+ }
|
|
25468
|
+ /**
|
|
25469
|
+ * Handle a fullscreenchange event
|
|
25470
|
+ */
|
|
25471
|
+ _handleFullscreenEvent(event) {
|
|
25472
|
+ const icon = this._button.querySelector('img');
|
|
25473
|
+ icon.src = this._viewport.fullscreen ? BUTTON_ICON_ON : BUTTON_ICON_OFF;
|
|
25474
|
+ }
|
|
25475
|
+}
|
|
25476
|
+
|
25380
|
25477
|
;// CONCATENATED MODULE: ./src/core/viewport.ts
|
25381
|
25478
|
/*
|
25382
|
25479
|
* encantar.js
|
|
@@ -25404,6 +25501,8 @@ class HUD {
|
25404
|
25501
|
|
25405
|
25502
|
|
25406
|
25503
|
|
|
25504
|
+
|
|
25505
|
+
|
25407
|
25506
|
/** An event emitted by a Viewport */
|
25408
|
25507
|
class ViewportEvent extends AREvent {
|
25409
|
25508
|
}
|
|
@@ -25417,6 +25516,7 @@ const DEFAULT_VIEWPORT_SETTINGS = {
|
25417
|
25516
|
resolution: 'lg',
|
25418
|
25517
|
style: 'best-fit',
|
25419
|
25518
|
canvas: null,
|
|
25519
|
+ fullscreenUI: true,
|
25420
|
25520
|
};
|
25421
|
25521
|
/** Base z-index of the children of the viewport container */
|
25422
|
25522
|
const BASE_ZINDEX = 0;
|
|
@@ -25488,11 +25588,15 @@ class ViewportCanvases {
|
25488
|
25588
|
throw new IllegalArgumentError('Not a canvas: ' + fgCanvas);
|
25489
|
25589
|
this._originalCSSTextOfForegroundCanvas = fgCanvas ? fgCanvas.style.cssText : '';
|
25490
|
25590
|
this._foregroundCanvas = this._styleCanvas(fgCanvas || this._createCanvas(initialSize), FOREGROUND_ZINDEX);
|
|
25591
|
+ this._foregroundCanvas.style.background = 'transparent';
|
25491
|
25592
|
this._backgroundCanvas = this._styleCanvas(this._createCanvas(initialSize), BACKGROUND_ZINDEX);
|
25492
|
|
- parent.appendChild(this._backgroundCanvas);
|
25493
|
|
- parent.appendChild(this._foregroundCanvas);
|
25494
|
25593
|
this._backgroundCanvas.hidden = true;
|
25495
|
25594
|
this._foregroundCanvas.hidden = true;
|
|
25595
|
+ const engineInfo = 'encantar.js ' + AR.version;
|
|
25596
|
+ this._backgroundCanvas.dataset.arEngine = engineInfo;
|
|
25597
|
+ this._foregroundCanvas.dataset.arEngine = engineInfo;
|
|
25598
|
+ parent.appendChild(this._backgroundCanvas);
|
|
25599
|
+ parent.appendChild(this._foregroundCanvas);
|
25496
|
25600
|
}
|
25497
|
25601
|
/**
|
25498
|
25602
|
* The background canvas
|
|
@@ -25555,10 +25659,24 @@ class ViewportCanvases {
|
25555
|
25659
|
class ViewportFullscreenHelper {
|
25556
|
25660
|
/**
|
25557
|
25661
|
* Constructor
|
25558
|
|
- * @param _container the container which will be put in fullscreen
|
|
25662
|
+ * @param viewport Viewport
|
|
25663
|
+ */
|
|
25664
|
+ constructor(viewport) {
|
|
25665
|
+ this._viewport = viewport;
|
|
25666
|
+ this._container = viewport.container;
|
|
25667
|
+ this._boundEventHandler = this._triggerEvent.bind(this);
|
|
25668
|
+ }
|
|
25669
|
+ /**
|
|
25670
|
+ * Initialize
|
25559
|
25671
|
*/
|
25560
|
|
- constructor(_container) {
|
25561
|
|
- this._container = _container;
|
|
25672
|
+ init() {
|
|
25673
|
+ this._container.addEventListener('fullscreenchange', this._boundEventHandler);
|
|
25674
|
+ }
|
|
25675
|
+ /**
|
|
25676
|
+ * Release
|
|
25677
|
+ */
|
|
25678
|
+ release() {
|
|
25679
|
+ this._container.removeEventListener('fullscreenchange', this._boundEventHandler);
|
25562
|
25680
|
}
|
25563
|
25681
|
/**
|
25564
|
25682
|
* Make a request to the user agent so that the viewport container is
|
|
@@ -25659,6 +25777,13 @@ class ViewportFullscreenHelper {
|
25659
|
25777
|
else
|
25660
|
25778
|
return false;
|
25661
|
25779
|
}
|
|
25780
|
+ /**
|
|
25781
|
+ * Trigger a fullscreenchange event
|
|
25782
|
+ */
|
|
25783
|
+ _triggerEvent() {
|
|
25784
|
+ const event = new ViewportEvent('fullscreenchange');
|
|
25785
|
+ this._viewport.dispatchEvent(event);
|
|
25786
|
+ }
|
25662
|
25787
|
}
|
25663
|
25788
|
/**
|
25664
|
25789
|
* Helper class to resize the viewport
|
|
@@ -25900,9 +26025,12 @@ class Viewport extends ViewportEventTarget {
|
25900
|
26025
|
this._containers = new ViewportContainers(settings.container);
|
25901
|
26026
|
this._hud = new HUD(this._subContainer, settings.hudContainer);
|
25902
|
26027
|
this._canvases = new ViewportCanvases(this._subContainer, initialSize, settings.canvas);
|
25903
|
|
- this._fullscreen = new ViewportFullscreenHelper(this.container);
|
25904
|
26028
|
this._resizer = new ViewportResizer(this);
|
25905
|
26029
|
this._resizer.setStrategyByName(this._style);
|
|
26030
|
+ this._fullscreen = new ViewportFullscreenHelper(this);
|
|
26031
|
+ this._fullscreenButton = null;
|
|
26032
|
+ if (settings.fullscreenUI && this.fullscreenAvailable)
|
|
26033
|
+ this._fullscreenButton = new FullscreenButton(this);
|
25906
|
26034
|
}
|
25907
|
26035
|
/**
|
25908
|
26036
|
* Viewport container
|
|
@@ -25919,13 +26047,16 @@ class Viewport extends ViewportEventTarget {
|
25919
|
26047
|
/**
|
25920
|
26048
|
* Set viewport style
|
25921
|
26049
|
*/
|
25922
|
|
- set style(value) {
|
|
26050
|
+ /*
|
|
26051
|
+ set style(value: ViewportStyle)
|
|
26052
|
+ {
|
25923
|
26053
|
// note: the viewport style is independent of the session mode!
|
25924
|
|
- if (value !== this._style) {
|
|
26054
|
+ if(value !== this._style) {
|
25925
|
26055
|
this._resizer.setStrategyByName(value);
|
25926
|
26056
|
this._style = value;
|
25927
|
26057
|
}
|
25928
|
26058
|
}
|
|
26059
|
+ */
|
25929
|
26060
|
/**
|
25930
|
26061
|
* HUD
|
25931
|
26062
|
*/
|
|
@@ -26002,20 +26133,45 @@ class Viewport extends ViewportEventTarget {
|
26002
|
26133
|
}
|
26003
|
26134
|
/**
|
26004
|
26135
|
* Initialize the viewport (when the session starts)
|
|
26136
|
+ * @param getMediaSize
|
|
26137
|
+ * @param sessionMode
|
26005
|
26138
|
* @internal
|
26006
|
26139
|
*/
|
26007
|
|
- _init(getMediaSize) {
|
|
26140
|
+ _init(getMediaSize, sessionMode) {
|
|
26141
|
+ var _a;
|
|
26142
|
+ // validate if the viewport style matches the session mode
|
|
26143
|
+ if (sessionMode == 'immersive') {
|
|
26144
|
+ if (this._style != 'best-fit' && this._style != 'stretch') {
|
|
26145
|
+ Utils.warning(`Invalid viewport style \"${this._style}\" for the \"${sessionMode}\" mode`);
|
|
26146
|
+ this._style = 'best-fit';
|
|
26147
|
+ this._resizer.setStrategyByName(this._style);
|
|
26148
|
+ }
|
|
26149
|
+ }
|
|
26150
|
+ else if (sessionMode == 'inline') {
|
|
26151
|
+ if (this._style != 'inline') {
|
|
26152
|
+ Utils.warning(`Invalid viewport style \"${this._style}\" for the \"${sessionMode}\" mode`);
|
|
26153
|
+ this._style = 'inline';
|
|
26154
|
+ this._resizer.setStrategyByName(this._style);
|
|
26155
|
+ }
|
|
26156
|
+ }
|
|
26157
|
+ // set the media size getter
|
26008
|
26158
|
this._mediaSize = getMediaSize;
|
|
26159
|
+ // initialize the components
|
26009
|
26160
|
this._containers.init();
|
26010
|
26161
|
this._hud._init(HUD_ZINDEX);
|
26011
|
26162
|
this._canvases.init();
|
26012
|
26163
|
this._resizer.init();
|
|
26164
|
+ this._fullscreen.init();
|
|
26165
|
+ (_a = this._fullscreenButton) === null || _a === void 0 ? void 0 : _a.init();
|
26013
|
26166
|
}
|
26014
|
26167
|
/**
|
26015
|
26168
|
* Release the viewport (when the session ends)
|
26016
|
26169
|
* @internal
|
26017
|
26170
|
*/
|
26018
|
26171
|
_release() {
|
|
26172
|
+ var _a;
|
|
26173
|
+ (_a = this._fullscreenButton) === null || _a === void 0 ? void 0 : _a.release();
|
|
26174
|
+ this._fullscreen.release();
|
26019
|
26175
|
this._resizer.release();
|
26020
|
26176
|
this._canvases.release();
|
26021
|
26177
|
this._hud._release();
|
|
@@ -26118,7 +26274,7 @@ Object.freeze(AR);
|
26118
|
26274
|
// Add Speedy Vision to global scope
|
26119
|
26275
|
((window) => window.Speedy = window.Speedy || (speedy_vision_default()))(window);
|
26120
|
26276
|
// Display a notice
|
26121
|
|
-Utils.log(`encantAR.js version ${AR.version}. ` +
|
|
26277
|
+Utils.log(`encantar.js version ${AR.version}. ` +
|
26122
|
26278
|
`GPU-accelerated Augmented Reality for the web by Alexandre Martins. ` +
|
26123
|
26279
|
"https://github.com/alemart/encantar-js");
|
26124
|
26280
|
|