@srsergio/taptapp-ar 1.0.22 → 1.0.24

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.
@@ -131,18 +131,16 @@ class SimpleAR {
131
131
  return;
132
132
  const [markerW, markerH] = this.markerDimensions[targetIndex];
133
133
  const containerRect = this.container.getBoundingClientRect();
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
134
+ // 1. Raw Video Dimensions (Sensor Frame)
135
+ const videoW = this.video.videoWidth;
136
+ const videoH = this.video.videoHeight;
137
+ // 2. Detect if screen orientation is different from video buffer
139
138
  const isPortrait = containerRect.height > containerRect.width;
140
139
  const isVideoLandscape = videoW > videoH;
141
140
  const needsRotation = isPortrait && isVideoLandscape;
142
- // If rotated, the effective buffer dimensions are swapped
141
+ // Effective dimensions of the display buffer
143
142
  const effectiveBufferW = needsRotation ? videoH : videoW;
144
143
  const effectiveBufferH = needsRotation ? videoW : videoH;
145
- // Calculate display area considering object-fit: cover
146
144
  const containerAspect = containerRect.width / containerRect.height;
147
145
  const bufferAspect = effectiveBufferW / effectiveBufferH;
148
146
  let displayW, displayH, offsetX, offsetY;
@@ -160,17 +158,19 @@ class SimpleAR {
160
158
  }
161
159
  const scaleX = displayW / effectiveBufferW;
162
160
  const scaleY = displayH / effectiveBufferH;
163
- // Project marker center into camera space
161
+ // 3. Focal Length (MUST match Controller.js)
162
+ // Controller uses inputHeight / 2 (sensor's vertical dimension) as the baseline.
163
+ const f = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
164
+ // 4. Project marker center into camera space
164
165
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
165
166
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
166
167
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
167
- // focal length (roughly 45 degrees FOV match Controller.js)
168
- const f = effectiveBufferH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
169
- // Normalized Device Coordinates (NDC) to Screen space
168
+ // 5. Map Camera coordinates to Screen coordinates
170
169
  let screenX, screenY;
171
170
  if (needsRotation) {
172
- // Map camera X/Y to rotated screen Y/X
173
- // Camera X -> Screen Y, Camera Y -> Screen -X
171
+ // Mapping Sensor coordinates to Rotated Screen coordinates
172
+ // Sensor +X -> Screen +Y
173
+ // Sensor +Y -> Screen -X (relative to logical center)
174
174
  screenX = offsetX + (effectiveBufferW / 2 + (ty * f / tz)) * scaleX;
175
175
  screenY = offsetY + (effectiveBufferH / 2 - (tx * f / tz)) * scaleY;
176
176
  }
@@ -178,22 +178,23 @@ class SimpleAR {
178
178
  screenX = offsetX + (effectiveBufferW / 2 + (tx * f / tz)) * scaleX;
179
179
  screenY = offsetY + (effectiveBufferH / 2 + (ty * f / tz)) * scaleY;
180
180
  }
181
- // Rotation: compensate for buffer rotation
181
+ // 6. Rotation: sync with CSS transform
182
+ //atan2 gives angle of world X-axis in camera space.
182
183
  let rotation = Math.atan2(mVT[1][0], mVT[0][0]);
183
- if (needsRotation)
184
- rotation -= Math.PI / 2;
184
+ if (needsRotation) {
185
+ rotation -= Math.PI / 2; // Mapping Sensor frame to Portrait Screen frame
186
+ }
187
+ // 7. Scale calculation (Robust Method)
188
+ // Instead of detecting intrinsic width (unstable), we force a base CSS width
189
+ // and calculate the scale to match the marker's projected screen width.
190
+ const BASE_CSS_WIDTH = 1000;
185
191
  const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
186
192
  const perspectiveScale = (f / tz) * scaleX;
187
- // Detect overlay intrinsic size
188
- const intrinsicWidth = (this.overlay instanceof HTMLVideoElement)
189
- ? this.overlay.videoWidth
190
- : (this.overlay instanceof HTMLImageElement ? this.overlay.naturalWidth : 0);
191
- const baseScale = intrinsicWidth > 0
192
- ? (matrixScale * markerW * perspectiveScale) / intrinsicWidth
193
- : 1.0;
194
- const finalScale = baseScale * this.scaleMultiplier;
195
- // Apply transform
196
- this.overlay.style.width = 'auto';
193
+ // Target pixel width on screen = markerW * matrixScale * perspectiveScale
194
+ const targetScreenWidth = markerW * matrixScale * perspectiveScale;
195
+ const finalScale = (targetScreenWidth / BASE_CSS_WIDTH) * this.scaleMultiplier;
196
+ // Apply
197
+ this.overlay.style.width = `${BASE_CSS_WIDTH}px`;
197
198
  this.overlay.style.height = 'auto';
198
199
  this.overlay.style.position = 'absolute';
199
200
  this.overlay.style.transformOrigin = 'center center';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "AR Compiler for Node.js and Browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -156,26 +156,23 @@ class SimpleAR {
156
156
  const [markerW, markerH] = this.markerDimensions[targetIndex];
157
157
  const containerRect = this.container.getBoundingClientRect();
158
158
 
159
- // Video source dimensions (e.g., 1280x720)
160
- let videoW = this.video.videoWidth;
161
- let videoH = this.video.videoHeight;
159
+ // 1. Raw Video Dimensions (Sensor Frame)
160
+ const videoW = this.video.videoWidth;
161
+ const videoH = this.video.videoHeight;
162
162
 
163
- // Detect if the video is effectively rotated (Portrait on Mobile)
164
- // Browser displays video vertically but videoWidth > videoHeight
163
+ // 2. Detect if screen orientation is different from video buffer
165
164
  const isPortrait = containerRect.height > containerRect.width;
166
165
  const isVideoLandscape = videoW > videoH;
167
166
  const needsRotation = isPortrait && isVideoLandscape;
168
167
 
169
- // If rotated, the effective buffer dimensions are swapped
168
+ // Effective dimensions of the display buffer
170
169
  const effectiveBufferW = needsRotation ? videoH : videoW;
171
170
  const effectiveBufferH = needsRotation ? videoW : videoH;
172
171
 
173
- // Calculate display area considering object-fit: cover
174
172
  const containerAspect = containerRect.width / containerRect.height;
175
173
  const bufferAspect = effectiveBufferW / effectiveBufferH;
176
174
 
177
175
  let displayW, displayH, offsetX, offsetY;
178
-
179
176
  if (containerAspect > bufferAspect) {
180
177
  displayW = containerRect.width;
181
178
  displayH = containerRect.width / bufferAspect;
@@ -191,20 +188,21 @@ class SimpleAR {
191
188
  const scaleX = displayW / effectiveBufferW;
192
189
  const scaleY = displayH / effectiveBufferH;
193
190
 
194
- // Project marker center into camera space
191
+ // 3. Focal Length (MUST match Controller.js)
192
+ // Controller uses inputHeight / 2 (sensor's vertical dimension) as the baseline.
193
+ const f = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
194
+
195
+ // 4. Project marker center into camera space
195
196
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
196
197
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
197
198
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
198
199
 
199
- // focal length (roughly 45 degrees FOV match Controller.js)
200
- const f = effectiveBufferH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
201
-
202
- // Normalized Device Coordinates (NDC) to Screen space
200
+ // 5. Map Camera coordinates to Screen coordinates
203
201
  let screenX, screenY;
204
-
205
202
  if (needsRotation) {
206
- // Map camera X/Y to rotated screen Y/X
207
- // Camera X -> Screen Y, Camera Y -> Screen -X
203
+ // Mapping Sensor coordinates to Rotated Screen coordinates
204
+ // Sensor +X -> Screen +Y
205
+ // Sensor +Y -> Screen -X (relative to logical center)
208
206
  screenX = offsetX + (effectiveBufferW / 2 + (ty * f / tz)) * scaleX;
209
207
  screenY = offsetY + (effectiveBufferH / 2 - (tx * f / tz)) * scaleY;
210
208
  } else {
@@ -212,26 +210,26 @@ class SimpleAR {
212
210
  screenY = offsetY + (effectiveBufferH / 2 + (ty * f / tz)) * scaleY;
213
211
  }
214
212
 
215
- // Rotation: compensate for buffer rotation
213
+ // 6. Rotation: sync with CSS transform
214
+ //atan2 gives angle of world X-axis in camera space.
216
215
  let rotation = Math.atan2(mVT[1][0], mVT[0][0]);
217
- if (needsRotation) rotation -= Math.PI / 2;
216
+ if (needsRotation) {
217
+ rotation -= Math.PI / 2; // Mapping Sensor frame to Portrait Screen frame
218
+ }
218
219
 
220
+ // 7. Scale calculation (Robust Method)
221
+ // Instead of detecting intrinsic width (unstable), we force a base CSS width
222
+ // and calculate the scale to match the marker's projected screen width.
223
+ const BASE_CSS_WIDTH = 1000;
219
224
  const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
220
225
  const perspectiveScale = (f / tz) * scaleX;
221
226
 
222
- // Detect overlay intrinsic size
223
- const intrinsicWidth = (this.overlay instanceof HTMLVideoElement)
224
- ? this.overlay.videoWidth
225
- : (this.overlay instanceof HTMLImageElement ? this.overlay.naturalWidth : 0);
226
-
227
- const baseScale = intrinsicWidth > 0
228
- ? (matrixScale * markerW * perspectiveScale) / intrinsicWidth
229
- : 1.0;
230
-
231
- const finalScale = baseScale * this.scaleMultiplier;
227
+ // Target pixel width on screen = markerW * matrixScale * perspectiveScale
228
+ const targetScreenWidth = markerW * matrixScale * perspectiveScale;
229
+ const finalScale = (targetScreenWidth / BASE_CSS_WIDTH) * this.scaleMultiplier;
232
230
 
233
- // Apply transform
234
- this.overlay.style.width = 'auto';
231
+ // Apply
232
+ this.overlay.style.width = `${BASE_CSS_WIDTH}px`;
235
233
  this.overlay.style.height = 'auto';
236
234
  this.overlay.style.position = 'absolute';
237
235
  this.overlay.style.transformOrigin = 'center center';