@srsergio/taptapp-ar 1.0.20 → 1.0.22
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/dist/compiler/simple-ar.d.ts +3 -1
- package/dist/compiler/simple-ar.js +44 -24
- package/package.json +1 -1
- package/src/compiler/simple-ar.js +47 -25
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
* await ar.start();
|
|
16
16
|
*/
|
|
17
17
|
export class SimpleAR {
|
|
18
|
-
constructor({ container, targetSrc, overlay, onFound, onLost, onUpdate, cameraConfig, }: {
|
|
18
|
+
constructor({ container, targetSrc, overlay, scale, onFound, onLost, onUpdate, cameraConfig, }: {
|
|
19
19
|
container: any;
|
|
20
20
|
targetSrc: any;
|
|
21
21
|
overlay: any;
|
|
22
|
+
scale?: number | undefined;
|
|
22
23
|
onFound?: null | undefined;
|
|
23
24
|
onLost?: null | undefined;
|
|
24
25
|
onUpdate?: null | undefined;
|
|
@@ -31,6 +32,7 @@ export class SimpleAR {
|
|
|
31
32
|
container: any;
|
|
32
33
|
targetSrc: any;
|
|
33
34
|
overlay: any;
|
|
35
|
+
scaleMultiplier: number;
|
|
34
36
|
onFound: any;
|
|
35
37
|
onLost: any;
|
|
36
38
|
onUpdateCallback: any;
|
|
@@ -16,10 +16,12 @@ import { Controller } from "./controller.js";
|
|
|
16
16
|
* await ar.start();
|
|
17
17
|
*/
|
|
18
18
|
class SimpleAR {
|
|
19
|
-
constructor({ container, targetSrc, overlay,
|
|
19
|
+
constructor({ container, targetSrc, overlay, scale = 1.0, // Multiplicador de escala personalizado
|
|
20
|
+
onFound = null, onLost = null, onUpdate = null, cameraConfig = { facingMode: 'environment', width: 1280, height: 720 }, }) {
|
|
20
21
|
this.container = container;
|
|
21
22
|
this.targetSrc = targetSrc;
|
|
22
23
|
this.overlay = overlay;
|
|
24
|
+
this.scaleMultiplier = scale;
|
|
23
25
|
this.onFound = onFound;
|
|
24
26
|
this.onLost = onLost;
|
|
25
27
|
this.onUpdateCallback = onUpdate;
|
|
@@ -129,52 +131,70 @@ class SimpleAR {
|
|
|
129
131
|
return;
|
|
130
132
|
const [markerW, markerH] = this.markerDimensions[targetIndex];
|
|
131
133
|
const containerRect = this.container.getBoundingClientRect();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
// Video source dimensions (e.g., 1280x720)
|
|
135
|
+
let videoW = this.video.videoWidth;
|
|
136
|
+
let videoH = this.video.videoHeight;
|
|
137
|
+
// Detect if the video is effectively rotated (Portrait on Mobile)
|
|
138
|
+
// Browser displays video vertically but videoWidth > videoHeight
|
|
139
|
+
const isPortrait = containerRect.height > containerRect.width;
|
|
140
|
+
const isVideoLandscape = videoW > videoH;
|
|
141
|
+
const needsRotation = isPortrait && isVideoLandscape;
|
|
142
|
+
// If rotated, the effective buffer dimensions are swapped
|
|
143
|
+
const effectiveBufferW = needsRotation ? videoH : videoW;
|
|
144
|
+
const effectiveBufferH = needsRotation ? videoW : videoH;
|
|
134
145
|
// Calculate display area considering object-fit: cover
|
|
135
146
|
const containerAspect = containerRect.width / containerRect.height;
|
|
136
|
-
const
|
|
147
|
+
const bufferAspect = effectiveBufferW / effectiveBufferH;
|
|
137
148
|
let displayW, displayH, offsetX, offsetY;
|
|
138
|
-
if (containerAspect >
|
|
149
|
+
if (containerAspect > bufferAspect) {
|
|
139
150
|
displayW = containerRect.width;
|
|
140
|
-
displayH = containerRect.width /
|
|
151
|
+
displayH = containerRect.width / bufferAspect;
|
|
141
152
|
offsetX = 0;
|
|
142
153
|
offsetY = (containerRect.height - displayH) / 2;
|
|
143
154
|
}
|
|
144
155
|
else {
|
|
145
156
|
displayH = containerRect.height;
|
|
146
|
-
displayW = containerRect.height *
|
|
157
|
+
displayW = containerRect.height * bufferAspect;
|
|
147
158
|
offsetX = (containerRect.width - displayW) / 2;
|
|
148
159
|
offsetY = 0;
|
|
149
160
|
}
|
|
150
|
-
const scaleX = displayW /
|
|
151
|
-
const scaleY = displayH /
|
|
152
|
-
// Project
|
|
153
|
-
// Marker coordinates are pixels from top-left.
|
|
161
|
+
const scaleX = displayW / effectiveBufferW;
|
|
162
|
+
const scaleY = displayH / effectiveBufferH;
|
|
163
|
+
// Project marker center into camera space
|
|
154
164
|
const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
|
|
155
165
|
const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
|
|
156
166
|
const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
|
|
157
167
|
// focal length (roughly 45 degrees FOV match Controller.js)
|
|
158
|
-
const f =
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
const f = effectiveBufferH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
|
|
169
|
+
// Normalized Device Coordinates (NDC) to Screen space
|
|
170
|
+
let screenX, screenY;
|
|
171
|
+
if (needsRotation) {
|
|
172
|
+
// Map camera X/Y to rotated screen Y/X
|
|
173
|
+
// Camera X -> Screen Y, Camera Y -> Screen -X
|
|
174
|
+
screenX = offsetX + (effectiveBufferW / 2 + (ty * f / tz)) * scaleX;
|
|
175
|
+
screenY = offsetY + (effectiveBufferH / 2 - (tx * f / tz)) * scaleY;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
screenX = offsetX + (effectiveBufferW / 2 + (tx * f / tz)) * scaleX;
|
|
179
|
+
screenY = offsetY + (effectiveBufferH / 2 + (ty * f / tz)) * scaleY;
|
|
180
|
+
}
|
|
181
|
+
// Rotation: compensate for buffer rotation
|
|
182
|
+
let rotation = Math.atan2(mVT[1][0], mVT[0][0]);
|
|
183
|
+
if (needsRotation)
|
|
184
|
+
rotation -= Math.PI / 2;
|
|
165
185
|
const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
|
|
166
|
-
// Perspective scale: 1 world pixel = (f/tz) screen pixels
|
|
167
186
|
const perspectiveScale = (f / tz) * scaleX;
|
|
168
|
-
// Detect overlay intrinsic size
|
|
187
|
+
// Detect overlay intrinsic size
|
|
169
188
|
const intrinsicWidth = (this.overlay instanceof HTMLVideoElement)
|
|
170
189
|
? this.overlay.videoWidth
|
|
171
190
|
: (this.overlay instanceof HTMLImageElement ? this.overlay.naturalWidth : 0);
|
|
172
|
-
|
|
173
|
-
// If intrinsicWidth is 0 (not loaded), we fallback to a safe 1.0 scale to avoid Infinity
|
|
174
|
-
const finalScale = intrinsicWidth > 0
|
|
191
|
+
const baseScale = intrinsicWidth > 0
|
|
175
192
|
? (matrixScale * markerW * perspectiveScale) / intrinsicWidth
|
|
176
193
|
: 1.0;
|
|
194
|
+
const finalScale = baseScale * this.scaleMultiplier;
|
|
177
195
|
// Apply transform
|
|
196
|
+
this.overlay.style.width = 'auto';
|
|
197
|
+
this.overlay.style.height = 'auto';
|
|
178
198
|
this.overlay.style.position = 'absolute';
|
|
179
199
|
this.overlay.style.transformOrigin = 'center center';
|
|
180
200
|
this.overlay.style.left = '0';
|
|
@@ -183,7 +203,7 @@ class SimpleAR {
|
|
|
183
203
|
translate(${screenX}px, ${screenY}px)
|
|
184
204
|
translate(-50%, -50%)
|
|
185
205
|
scale(${finalScale})
|
|
186
|
-
rotate(${
|
|
206
|
+
rotate(${rotation}rad)
|
|
187
207
|
`;
|
|
188
208
|
}
|
|
189
209
|
}
|
package/package.json
CHANGED
|
@@ -21,6 +21,7 @@ class SimpleAR {
|
|
|
21
21
|
container,
|
|
22
22
|
targetSrc,
|
|
23
23
|
overlay,
|
|
24
|
+
scale = 1.0, // Multiplicador de escala personalizado
|
|
24
25
|
onFound = null,
|
|
25
26
|
onLost = null,
|
|
26
27
|
onUpdate = null,
|
|
@@ -29,6 +30,7 @@ class SimpleAR {
|
|
|
29
30
|
this.container = container;
|
|
30
31
|
this.targetSrc = targetSrc;
|
|
31
32
|
this.overlay = overlay;
|
|
33
|
+
this.scaleMultiplier = scale;
|
|
32
34
|
this.onFound = onFound;
|
|
33
35
|
this.onLost = onLost;
|
|
34
36
|
this.onUpdateCallback = onUpdate;
|
|
@@ -153,63 +155,84 @@ class SimpleAR {
|
|
|
153
155
|
|
|
154
156
|
const [markerW, markerH] = this.markerDimensions[targetIndex];
|
|
155
157
|
const containerRect = this.container.getBoundingClientRect();
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
|
|
159
|
+
// Video source dimensions (e.g., 1280x720)
|
|
160
|
+
let videoW = this.video.videoWidth;
|
|
161
|
+
let videoH = this.video.videoHeight;
|
|
162
|
+
|
|
163
|
+
// Detect if the video is effectively rotated (Portrait on Mobile)
|
|
164
|
+
// Browser displays video vertically but videoWidth > videoHeight
|
|
165
|
+
const isPortrait = containerRect.height > containerRect.width;
|
|
166
|
+
const isVideoLandscape = videoW > videoH;
|
|
167
|
+
const needsRotation = isPortrait && isVideoLandscape;
|
|
168
|
+
|
|
169
|
+
// If rotated, the effective buffer dimensions are swapped
|
|
170
|
+
const effectiveBufferW = needsRotation ? videoH : videoW;
|
|
171
|
+
const effectiveBufferH = needsRotation ? videoW : videoH;
|
|
158
172
|
|
|
159
173
|
// Calculate display area considering object-fit: cover
|
|
160
174
|
const containerAspect = containerRect.width / containerRect.height;
|
|
161
|
-
const
|
|
175
|
+
const bufferAspect = effectiveBufferW / effectiveBufferH;
|
|
162
176
|
|
|
163
177
|
let displayW, displayH, offsetX, offsetY;
|
|
164
178
|
|
|
165
|
-
if (containerAspect >
|
|
179
|
+
if (containerAspect > bufferAspect) {
|
|
166
180
|
displayW = containerRect.width;
|
|
167
|
-
displayH = containerRect.width /
|
|
181
|
+
displayH = containerRect.width / bufferAspect;
|
|
168
182
|
offsetX = 0;
|
|
169
183
|
offsetY = (containerRect.height - displayH) / 2;
|
|
170
184
|
} else {
|
|
171
185
|
displayH = containerRect.height;
|
|
172
|
-
displayW = containerRect.height *
|
|
186
|
+
displayW = containerRect.height * bufferAspect;
|
|
173
187
|
offsetX = (containerRect.width - displayW) / 2;
|
|
174
188
|
offsetY = 0;
|
|
175
189
|
}
|
|
176
190
|
|
|
177
|
-
const scaleX = displayW /
|
|
178
|
-
const scaleY = displayH /
|
|
191
|
+
const scaleX = displayW / effectiveBufferW;
|
|
192
|
+
const scaleY = displayH / effectiveBufferH;
|
|
179
193
|
|
|
180
|
-
// Project
|
|
181
|
-
// Marker coordinates are pixels from top-left.
|
|
194
|
+
// Project marker center into camera space
|
|
182
195
|
const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
|
|
183
196
|
const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
|
|
184
197
|
const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
|
|
185
198
|
|
|
186
199
|
// focal length (roughly 45 degrees FOV match Controller.js)
|
|
187
|
-
const f =
|
|
200
|
+
const f = effectiveBufferH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
|
|
188
201
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
const screenX = offsetX + (videoW / 2 + (tx * f / tz)) * scaleX;
|
|
192
|
-
const screenY = offsetY + (videoH / 2 - (ty * f / tz)) * scaleY;
|
|
202
|
+
// Normalized Device Coordinates (NDC) to Screen space
|
|
203
|
+
let screenX, screenY;
|
|
193
204
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
205
|
+
if (needsRotation) {
|
|
206
|
+
// Map camera X/Y to rotated screen Y/X
|
|
207
|
+
// Camera X -> Screen Y, Camera Y -> Screen -X
|
|
208
|
+
screenX = offsetX + (effectiveBufferW / 2 + (ty * f / tz)) * scaleX;
|
|
209
|
+
screenY = offsetY + (effectiveBufferH / 2 - (tx * f / tz)) * scaleY;
|
|
210
|
+
} else {
|
|
211
|
+
screenX = offsetX + (effectiveBufferW / 2 + (tx * f / tz)) * scaleX;
|
|
212
|
+
screenY = offsetY + (effectiveBufferH / 2 + (ty * f / tz)) * scaleY;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Rotation: compensate for buffer rotation
|
|
216
|
+
let rotation = Math.atan2(mVT[1][0], mVT[0][0]);
|
|
217
|
+
if (needsRotation) rotation -= Math.PI / 2;
|
|
197
218
|
|
|
198
|
-
|
|
219
|
+
const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
|
|
199
220
|
const perspectiveScale = (f / tz) * scaleX;
|
|
200
221
|
|
|
201
|
-
// Detect overlay intrinsic size
|
|
222
|
+
// Detect overlay intrinsic size
|
|
202
223
|
const intrinsicWidth = (this.overlay instanceof HTMLVideoElement)
|
|
203
224
|
? this.overlay.videoWidth
|
|
204
225
|
: (this.overlay instanceof HTMLImageElement ? this.overlay.naturalWidth : 0);
|
|
205
226
|
|
|
206
|
-
|
|
207
|
-
// If intrinsicWidth is 0 (not loaded), we fallback to a safe 1.0 scale to avoid Infinity
|
|
208
|
-
const finalScale = intrinsicWidth > 0
|
|
227
|
+
const baseScale = intrinsicWidth > 0
|
|
209
228
|
? (matrixScale * markerW * perspectiveScale) / intrinsicWidth
|
|
210
229
|
: 1.0;
|
|
211
230
|
|
|
231
|
+
const finalScale = baseScale * this.scaleMultiplier;
|
|
232
|
+
|
|
212
233
|
// Apply transform
|
|
234
|
+
this.overlay.style.width = 'auto';
|
|
235
|
+
this.overlay.style.height = 'auto';
|
|
213
236
|
this.overlay.style.position = 'absolute';
|
|
214
237
|
this.overlay.style.transformOrigin = 'center center';
|
|
215
238
|
this.overlay.style.left = '0';
|
|
@@ -218,10 +241,9 @@ class SimpleAR {
|
|
|
218
241
|
translate(${screenX}px, ${screenY}px)
|
|
219
242
|
translate(-50%, -50%)
|
|
220
243
|
scale(${finalScale})
|
|
221
|
-
rotate(${
|
|
244
|
+
rotate(${rotation}rad)
|
|
222
245
|
`;
|
|
223
246
|
}
|
|
224
|
-
|
|
225
247
|
}
|
|
226
248
|
|
|
227
249
|
export { SimpleAR };
|