@srsergio/taptapp-ar 1.0.25 → 1.0.27

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,32 +15,49 @@
15
15
  * await ar.start();
16
16
  */
17
17
  export class SimpleAR {
18
+ /**
19
+ * @param {Object} options
20
+ * @param {HTMLElement} options.container
21
+ * @param {string|string[]} options.targetSrc
22
+ * @param {HTMLElement} options.overlay
23
+ * @param {number} [options.scale=1.0]
24
+ * @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onFound]
25
+ * @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onLost]
26
+ * @param {((data: {targetIndex: number, worldMatrix: number[]}) => void) | null} [options.onUpdate]
27
+ * @param {Object} [options.cameraConfig]
28
+ */
18
29
  constructor({ container, targetSrc, overlay, scale, onFound, onLost, onUpdate, cameraConfig, }: {
19
- container: any;
20
- targetSrc: any;
21
- overlay: any;
30
+ container: HTMLElement;
31
+ targetSrc: string | string[];
32
+ overlay: HTMLElement;
22
33
  scale?: number | undefined;
23
- onFound?: null | undefined;
24
- onLost?: null | undefined;
25
- onUpdate?: null | undefined;
26
- cameraConfig?: {
27
- facingMode: string;
28
- width: number;
29
- height: number;
30
- } | undefined;
34
+ onFound?: ((data: {
35
+ targetIndex: number;
36
+ }) => void | Promise<void>) | null | undefined;
37
+ onLost?: ((data: {
38
+ targetIndex: number;
39
+ }) => void | Promise<void>) | null | undefined;
40
+ onUpdate?: ((data: {
41
+ targetIndex: number;
42
+ worldMatrix: number[];
43
+ }) => void) | null | undefined;
44
+ cameraConfig?: Object | undefined;
31
45
  });
32
- container: any;
33
- targetSrc: any;
34
- overlay: any;
46
+ container: HTMLElement;
47
+ targetSrc: string | string[];
48
+ overlay: HTMLElement;
35
49
  scaleMultiplier: number;
36
- onFound: any;
37
- onLost: any;
38
- onUpdateCallback: any;
39
- cameraConfig: {
40
- facingMode: string;
41
- width: number;
42
- height: number;
43
- };
50
+ onFound: ((data: {
51
+ targetIndex: number;
52
+ }) => void | Promise<void>) | null;
53
+ onLost: ((data: {
54
+ targetIndex: number;
55
+ }) => void | Promise<void>) | null;
56
+ onUpdateCallback: ((data: {
57
+ targetIndex: number;
58
+ worldMatrix: number[];
59
+ }) => void) | null;
60
+ cameraConfig: Object;
44
61
  video: HTMLVideoElement | null;
45
62
  controller: Controller | null;
46
63
  isTracking: boolean;
@@ -16,6 +16,17 @@ import { Controller } from "./controller.js";
16
16
  * await ar.start();
17
17
  */
18
18
  class SimpleAR {
19
+ /**
20
+ * @param {Object} options
21
+ * @param {HTMLElement} options.container
22
+ * @param {string|string[]} options.targetSrc
23
+ * @param {HTMLElement} options.overlay
24
+ * @param {number} [options.scale=1.0]
25
+ * @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onFound]
26
+ * @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onLost]
27
+ * @param {((data: {targetIndex: number, worldMatrix: number[]}) => void) | null} [options.onUpdate]
28
+ * @param {Object} [options.cameraConfig]
29
+ */
19
30
  constructor({ container, targetSrc, overlay, scale = 1.0, // Multiplicador de escala personalizado
20
31
  onFound = null, onLost = null, onUpdate = null, cameraConfig = { facingMode: 'environment', width: 1280, height: 720 }, }) {
21
32
  this.container = container;
@@ -45,6 +56,7 @@ class SimpleAR {
45
56
  const targets = Array.isArray(this.targetSrc) ? this.targetSrc : [this.targetSrc];
46
57
  const result = await this.controller.addImageTargets(targets);
47
58
  this.markerDimensions = result.dimensions; // [ [w1, h1], [w2, h2], ... ]
59
+ console.log("Targets loaded. Dimensions:", this.markerDimensions);
48
60
  this.controller.processVideo(this.video);
49
61
  return this;
50
62
  }
@@ -136,38 +148,49 @@ class SimpleAR {
136
148
  const isPortrait = containerRect.height > containerRect.width;
137
149
  const isVideoLandscape = videoW > videoH;
138
150
  const needsRotation = isPortrait && isVideoLandscape;
139
- // The tracker uses 1280x720. Focal length is based on 720 (sensor height).
140
- const f = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
141
- // Project Center of the marker into camera space
151
+ // Current display dimensions of the video (accounting for rotation)
152
+ const vW = needsRotation ? videoH : videoW;
153
+ const vH = needsRotation ? videoW : videoH;
154
+ // Robust "object-fit: cover" scale calculation
155
+ const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
156
+ const displayW = vW * perspectiveScale;
157
+ const displayH = vH * perspectiveScale;
158
+ const offsetX = (containerRect.width - displayW) / 2;
159
+ const offsetY = (containerRect.height - displayH) / 2;
160
+ // The tracker uses focal length based on height dimension in tracker space
161
+ const f = (videoH / 2) / Math.tan((45.0 * Math.PI / 180) / 2);
162
+ // Center of the marker in camera space (marker coordinates origin at top-left)
142
163
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
143
164
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
144
165
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
145
- let screenX, screenY, rotation, perspectiveScale;
166
+ let screenX, screenY, rotation;
146
167
  if (needsRotation) {
147
- // PORTRAIT MOBILE:
148
- // Browser rotates 1280 (W) -> vertical, 720 (H) -> horizontal
149
- const scale = containerRect.height / videoW;
150
- const displayW = videoH * scale;
151
- const offsetX = (containerRect.width - displayW) / 2;
152
- // Mapping: Buffer +X (Right) -> Screen +Y (Down), Buffer +Y (Down) -> Screen -X (Left)
153
- screenX = offsetX + (displayW / 2 - (ty * f / tz) * scale);
154
- screenY = (containerRect.height / 2 + (tx * f / tz) * scale);
168
+ const bufferOffsetX = (tx * f / tz);
169
+ const bufferOffsetY = (ty * f / tz);
170
+ screenX = offsetX + (displayW / 2) - (bufferOffsetY * perspectiveScale);
171
+ screenY = offsetY + (displayH / 2) + (bufferOffsetX * perspectiveScale);
155
172
  rotation = Math.atan2(mVT[1][0], mVT[0][0]) - Math.PI / 2;
156
- perspectiveScale = scale;
157
173
  }
158
174
  else {
159
- // LANDSCAPE / LAPTOP:
160
- const scale = containerRect.width / videoW;
161
- const displayH = videoH * scale;
162
- const offsetY = (containerRect.height - displayH) / 2;
163
- screenX = (containerRect.width / 2 + (tx * f / tz) * scale);
164
- screenY = offsetY + (displayH / 2 + (ty * f / tz) * scale);
175
+ const bufferOffsetX = (tx * f / tz);
176
+ const bufferOffsetY = (ty * f / tz);
177
+ screenX = offsetX + (displayW / 2) + (bufferOffsetX * perspectiveScale);
178
+ screenY = offsetY + (displayH / 2) + (bufferOffsetY * perspectiveScale);
165
179
  rotation = Math.atan2(mVT[1][0], mVT[0][0]);
166
- perspectiveScale = scale;
167
180
  }
168
- // Final Scale
169
181
  const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
170
182
  const finalScale = (f / tz) * perspectiveScale * matrixScale * this.scaleMultiplier;
183
+ // DEBUG LOGS
184
+ if (window.AR_DEBUG) {
185
+ console.log('--- AR POSITION DEBUG ---');
186
+ console.log('Container:', containerRect.width, 'x', containerRect.height);
187
+ console.log('Video:', videoW, 'x', videoH, 'needsRotation:', needsRotation);
188
+ console.log('PerspectiveScale (Cover):', perspectiveScale);
189
+ console.log('Display:', displayW, 'x', displayH, 'Offsets:', offsetX, offsetY);
190
+ console.log('Projection (tx, ty, tz):', tx.toFixed(2), ty.toFixed(2), tz.toFixed(2));
191
+ console.log('Screen Coords:', screenX.toFixed(2), screenY.toFixed(2));
192
+ console.log('Final Scale:', finalScale.toFixed(4), '(MatrixScale:', matrixScale.toFixed(4), ')');
193
+ }
171
194
  // Apply
172
195
  this.overlay.style.width = `${markerW}px`;
173
196
  this.overlay.style.height = 'auto';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "AR Compiler for Node.js and Browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,6 +17,17 @@ import { Controller } from "./controller.js";
17
17
  * await ar.start();
18
18
  */
19
19
  class SimpleAR {
20
+ /**
21
+ * @param {Object} options
22
+ * @param {HTMLElement} options.container
23
+ * @param {string|string[]} options.targetSrc
24
+ * @param {HTMLElement} options.overlay
25
+ * @param {number} [options.scale=1.0]
26
+ * @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onFound]
27
+ * @param {((data: {targetIndex: number}) => void | Promise<void>) | null} [options.onLost]
28
+ * @param {((data: {targetIndex: number, worldMatrix: number[]}) => void) | null} [options.onUpdate]
29
+ * @param {Object} [options.cameraConfig]
30
+ */
20
31
  constructor({
21
32
  container,
22
33
  targetSrc,
@@ -59,6 +70,7 @@ class SimpleAR {
59
70
  const targets = Array.isArray(this.targetSrc) ? this.targetSrc : [this.targetSrc];
60
71
  const result = await this.controller.addImageTargets(targets);
61
72
  this.markerDimensions = result.dimensions; // [ [w1, h1], [w2, h2], ... ]
73
+ console.log("Targets loaded. Dimensions:", this.markerDimensions);
62
74
 
63
75
  this.controller.processVideo(this.video);
64
76
 
@@ -163,46 +175,59 @@ class SimpleAR {
163
175
  const isVideoLandscape = videoW > videoH;
164
176
  const needsRotation = isPortrait && isVideoLandscape;
165
177
 
166
- // The tracker uses 1280x720. Focal length is based on 720 (sensor height).
167
- const f = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
178
+ // Current display dimensions of the video (accounting for rotation)
179
+ const vW = needsRotation ? videoH : videoW;
180
+ const vH = needsRotation ? videoW : videoH;
181
+
182
+ // Robust "object-fit: cover" scale calculation
183
+ const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
184
+
185
+ const displayW = vW * perspectiveScale;
186
+ const displayH = vH * perspectiveScale;
187
+ const offsetX = (containerRect.width - displayW) / 2;
188
+ const offsetY = (containerRect.height - displayH) / 2;
189
+
190
+ // The tracker uses focal length based on height dimension in tracker space
191
+ const f = (videoH / 2) / Math.tan((45.0 * Math.PI / 180) / 2);
168
192
 
169
- // Project Center of the marker into camera space
193
+ // Center of the marker in camera space (marker coordinates origin at top-left)
170
194
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
171
195
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
172
196
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
173
197
 
174
- let screenX, screenY, rotation, perspectiveScale;
198
+ let screenX, screenY, rotation;
175
199
 
176
200
  if (needsRotation) {
177
- // PORTRAIT MOBILE:
178
- // Browser rotates 1280 (W) -> vertical, 720 (H) -> horizontal
179
- const scale = containerRect.height / videoW;
180
- const displayW = videoH * scale;
181
- const offsetX = (containerRect.width - displayW) / 2;
182
-
183
- // Mapping: Buffer +X (Right) -> Screen +Y (Down), Buffer +Y (Down) -> Screen -X (Left)
184
- screenX = offsetX + (displayW / 2 - (ty * f / tz) * scale);
185
- screenY = (containerRect.height / 2 + (tx * f / tz) * scale);
201
+ const bufferOffsetX = (tx * f / tz);
202
+ const bufferOffsetY = (ty * f / tz);
186
203
 
204
+ screenX = offsetX + (displayW / 2) - (bufferOffsetY * perspectiveScale);
205
+ screenY = offsetY + (displayH / 2) + (bufferOffsetX * perspectiveScale);
187
206
  rotation = Math.atan2(mVT[1][0], mVT[0][0]) - Math.PI / 2;
188
- perspectiveScale = scale;
189
207
  } else {
190
- // LANDSCAPE / LAPTOP:
191
- const scale = containerRect.width / videoW;
192
- const displayH = videoH * scale;
193
- const offsetY = (containerRect.height - displayH) / 2;
194
-
195
- screenX = (containerRect.width / 2 + (tx * f / tz) * scale);
196
- screenY = offsetY + (displayH / 2 + (ty * f / tz) * scale);
208
+ const bufferOffsetX = (tx * f / tz);
209
+ const bufferOffsetY = (ty * f / tz);
197
210
 
211
+ screenX = offsetX + (displayW / 2) + (bufferOffsetX * perspectiveScale);
212
+ screenY = offsetY + (displayH / 2) + (bufferOffsetY * perspectiveScale);
198
213
  rotation = Math.atan2(mVT[1][0], mVT[0][0]);
199
- perspectiveScale = scale;
200
214
  }
201
215
 
202
- // Final Scale
203
216
  const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
204
217
  const finalScale = (f / tz) * perspectiveScale * matrixScale * this.scaleMultiplier;
205
218
 
219
+ // DEBUG LOGS
220
+ if (window.AR_DEBUG) {
221
+ console.log('--- AR POSITION DEBUG ---');
222
+ console.log('Container:', containerRect.width, 'x', containerRect.height);
223
+ console.log('Video:', videoW, 'x', videoH, 'needsRotation:', needsRotation);
224
+ console.log('PerspectiveScale (Cover):', perspectiveScale);
225
+ console.log('Display:', displayW, 'x', displayH, 'Offsets:', offsetX, offsetY);
226
+ console.log('Projection (tx, ty, tz):', tx.toFixed(2), ty.toFixed(2), tz.toFixed(2));
227
+ console.log('Screen Coords:', screenX.toFixed(2), screenY.toFixed(2));
228
+ console.log('Final Scale:', finalScale.toFixed(4), '(MatrixScale:', matrixScale.toFixed(4), ')');
229
+ }
230
+
206
231
  // Apply
207
232
  this.overlay.style.width = `${markerW}px`;
208
233
  this.overlay.style.height = 'auto';