@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.
@@ -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, onFound = null, onLost = null, onUpdate = null, cameraConfig = { facingMode: 'environment', width: 1280, height: 720 }, }) {
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
- const videoW = this.video.videoWidth;
133
- const videoH = this.video.videoHeight;
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 videoAspect = videoW / videoH;
147
+ const bufferAspect = effectiveBufferW / effectiveBufferH;
137
148
  let displayW, displayH, offsetX, offsetY;
138
- if (containerAspect > videoAspect) {
149
+ if (containerAspect > bufferAspect) {
139
150
  displayW = containerRect.width;
140
- displayH = containerRect.width / videoAspect;
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 * videoAspect;
157
+ displayW = containerRect.height * bufferAspect;
147
158
  offsetX = (containerRect.width - displayW) / 2;
148
159
  offsetY = 0;
149
160
  }
150
- const scaleX = displayW / videoW;
151
- const scaleY = displayH / videoH;
152
- // Project the center of the marker (markerW/2, markerH/2, 0) into camera space
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 = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
159
- // Perspective projection to screen space
160
- // The estimator returns positive Z for depth, so we divide by tz directly.
161
- const screenX = offsetX + (videoW / 2 + (tx * f / tz)) * scaleX;
162
- const screenY = offsetY + (videoH / 2 - (ty * f / tz)) * scaleY;
163
- // Use the first row of mVT to determine rotation and base scale component
164
- const rotation = Math.atan2(mVT[1][0], mVT[0][0]);
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 (videoWidth for video, naturalWidth for images)
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
- // Final scale = (Target Width in Pixels on screen) / (Overlay Intrinsic Width)
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(${-rotation}rad)
206
+ rotate(${rotation}rad)
187
207
  `;
188
208
  }
189
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
4
4
  "description": "AR Compiler for Node.js and Browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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
- const videoW = this.video.videoWidth;
157
- const videoH = this.video.videoHeight;
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 videoAspect = videoW / videoH;
175
+ const bufferAspect = effectiveBufferW / effectiveBufferH;
162
176
 
163
177
  let displayW, displayH, offsetX, offsetY;
164
178
 
165
- if (containerAspect > videoAspect) {
179
+ if (containerAspect > bufferAspect) {
166
180
  displayW = containerRect.width;
167
- displayH = containerRect.width / videoAspect;
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 * videoAspect;
186
+ displayW = containerRect.height * bufferAspect;
173
187
  offsetX = (containerRect.width - displayW) / 2;
174
188
  offsetY = 0;
175
189
  }
176
190
 
177
- const scaleX = displayW / videoW;
178
- const scaleY = displayH / videoH;
191
+ const scaleX = displayW / effectiveBufferW;
192
+ const scaleY = displayH / effectiveBufferH;
179
193
 
180
- // Project the center of the marker (markerW/2, markerH/2, 0) into camera space
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 = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
200
+ const f = effectiveBufferH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
188
201
 
189
- // Perspective projection to screen space
190
- // The estimator returns positive Z for depth, so we divide by tz directly.
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
- // Use the first row of mVT to determine rotation and base scale component
195
- const rotation = Math.atan2(mVT[1][0], mVT[0][0]);
196
- const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
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
- // Perspective scale: 1 world pixel = (f/tz) screen pixels
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 (videoWidth for video, naturalWidth for images)
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
- // Final scale = (Target Width in Pixels on screen) / (Overlay Intrinsic Width)
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(${-rotation}rad)
244
+ rotate(${rotation}rad)
222
245
  `;
223
246
  }
224
-
225
247
  }
226
248
 
227
249
  export { SimpleAR };