@srsergio/taptapp-ar 1.0.24 → 1.0.26

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,81 +131,66 @@ class SimpleAR {
131
131
  return;
132
132
  const [markerW, markerH] = this.markerDimensions[targetIndex];
133
133
  const containerRect = this.container.getBoundingClientRect();
134
- // 1. Raw Video Dimensions (Sensor Frame)
135
134
  const videoW = this.video.videoWidth;
136
135
  const videoH = this.video.videoHeight;
137
- // 2. Detect if screen orientation is different from video buffer
138
136
  const isPortrait = containerRect.height > containerRect.width;
139
137
  const isVideoLandscape = videoW > videoH;
140
138
  const needsRotation = isPortrait && isVideoLandscape;
141
- // Effective dimensions of the display buffer
142
- const effectiveBufferW = needsRotation ? videoH : videoW;
143
- const effectiveBufferH = needsRotation ? videoW : videoH;
144
- const containerAspect = containerRect.width / containerRect.height;
145
- const bufferAspect = effectiveBufferW / effectiveBufferH;
146
- let displayW, displayH, offsetX, offsetY;
147
- if (containerAspect > bufferAspect) {
148
- displayW = containerRect.width;
149
- displayH = containerRect.width / bufferAspect;
150
- offsetX = 0;
151
- offsetY = (containerRect.height - displayH) / 2;
152
- }
153
- else {
154
- displayH = containerRect.height;
155
- displayW = containerRect.height * bufferAspect;
156
- offsetX = (containerRect.width - displayW) / 2;
157
- offsetY = 0;
158
- }
159
- const scaleX = displayW / effectiveBufferW;
160
- const scaleY = displayH / effectiveBufferH;
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
139
+ // Current display dimensions of the video (accounting for rotation)
140
+ const vW = needsRotation ? videoH : videoW;
141
+ const vH = needsRotation ? videoW : videoH;
142
+ // Robust "object-fit: cover" scale calculation
143
+ const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
144
+ const displayW = vW * perspectiveScale;
145
+ const displayH = vH * perspectiveScale;
146
+ const offsetX = (containerRect.width - displayW) / 2;
147
+ const offsetY = (containerRect.height - displayH) / 2;
148
+ // The tracker uses focal length based on height dimension in tracker space
149
+ const f = (videoH / 2) / Math.tan((45.0 * Math.PI / 180) / 2);
150
+ // Center of the marker in camera space (marker coordinates origin at top-left)
165
151
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
166
152
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
167
153
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
168
- // 5. Map Camera coordinates to Screen coordinates
169
- let screenX, screenY;
154
+ let screenX, screenY, rotation;
170
155
  if (needsRotation) {
171
- // Mapping Sensor coordinates to Rotated Screen coordinates
172
- // Sensor +X -> Screen +Y
173
- // Sensor +Y -> Screen -X (relative to logical center)
174
- screenX = offsetX + (effectiveBufferW / 2 + (ty * f / tz)) * scaleX;
175
- screenY = offsetY + (effectiveBufferH / 2 - (tx * f / tz)) * scaleY;
156
+ // Mapping for 90deg clockwise rotation:
157
+ // Buffer X (0..videoW) -> Screen Y (0..displayH)
158
+ // Buffer Y (0..videoH) -> Screen X (displayW..0)
159
+ const bufferOffsetX = (tx * f / tz);
160
+ const bufferOffsetY = (ty * f / tz);
161
+ screenX = offsetX + (displayW / 2) - (bufferOffsetY * perspectiveScale);
162
+ screenY = offsetY + (displayH / 2) + (bufferOffsetX * perspectiveScale);
163
+ rotation = Math.atan2(mVT[1][0], mVT[0][0]) - Math.PI / 2;
176
164
  }
177
165
  else {
178
- screenX = offsetX + (effectiveBufferW / 2 + (tx * f / tz)) * scaleX;
179
- screenY = offsetY + (effectiveBufferH / 2 + (ty * f / tz)) * scaleY;
166
+ // Normal mapping:
167
+ // Buffer X -> Screen X
168
+ // Buffer Y -> Screen Y
169
+ const bufferOffsetX = (tx * f / tz);
170
+ const bufferOffsetY = (ty * f / tz);
171
+ screenX = offsetX + (displayW / 2) + (bufferOffsetX * perspectiveScale);
172
+ screenY = offsetY + (displayH / 2) + (bufferOffsetY * perspectiveScale);
173
+ rotation = Math.atan2(mVT[1][0], mVT[0][0]);
180
174
  }
181
- // 6. Rotation: sync with CSS transform
182
- //atan2 gives angle of world X-axis in camera space.
183
- let rotation = Math.atan2(mVT[1][0], mVT[0][0]);
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;
175
+ // Final Scale calculation:
176
+ // f/tz converts world units to buffer pixels
177
+ // perspectiveScale converts buffer pixels to screen pixels
178
+ // matrixScale handles the target's relative orientation in 2D
191
179
  const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
192
- const perspectiveScale = (f / tz) * scaleX;
193
- // Target pixel width on screen = markerW * matrixScale * perspectiveScale
194
- const targetScreenWidth = markerW * matrixScale * perspectiveScale;
195
- const finalScale = (targetScreenWidth / BASE_CSS_WIDTH) * this.scaleMultiplier;
180
+ const finalScale = (f / tz) * perspectiveScale * matrixScale * this.scaleMultiplier;
196
181
  // Apply
197
- this.overlay.style.width = `${BASE_CSS_WIDTH}px`;
182
+ this.overlay.style.width = `${markerW}px`;
198
183
  this.overlay.style.height = 'auto';
199
184
  this.overlay.style.position = 'absolute';
200
185
  this.overlay.style.transformOrigin = 'center center';
201
186
  this.overlay.style.left = '0';
202
187
  this.overlay.style.top = '0';
203
188
  this.overlay.style.transform = `
204
- translate(${screenX}px, ${screenY}px)
205
- translate(-50%, -50%)
206
- scale(${finalScale})
207
- rotate(${rotation}rad)
208
- `;
189
+ translate(${screenX}px, ${screenY}px)
190
+ translate(-50%, -50%)
191
+ scale(${finalScale})
192
+ rotate(${rotation}rad)
193
+ `;
209
194
  }
210
195
  }
211
196
  export { SimpleAR };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "description": "AR Compiler for Node.js and Browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -156,91 +156,77 @@ class SimpleAR {
156
156
  const [markerW, markerH] = this.markerDimensions[targetIndex];
157
157
  const containerRect = this.container.getBoundingClientRect();
158
158
 
159
- // 1. Raw Video Dimensions (Sensor Frame)
160
159
  const videoW = this.video.videoWidth;
161
160
  const videoH = this.video.videoHeight;
162
161
 
163
- // 2. Detect if screen orientation is different from video buffer
164
162
  const isPortrait = containerRect.height > containerRect.width;
165
163
  const isVideoLandscape = videoW > videoH;
166
164
  const needsRotation = isPortrait && isVideoLandscape;
167
165
 
168
- // Effective dimensions of the display buffer
169
- const effectiveBufferW = needsRotation ? videoH : videoW;
170
- const effectiveBufferH = needsRotation ? videoW : videoH;
166
+ // Current display dimensions of the video (accounting for rotation)
167
+ const vW = needsRotation ? videoH : videoW;
168
+ const vH = needsRotation ? videoW : videoH;
171
169
 
172
- const containerAspect = containerRect.width / containerRect.height;
173
- const bufferAspect = effectiveBufferW / effectiveBufferH;
170
+ // Robust "object-fit: cover" scale calculation
171
+ const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
174
172
 
175
- let displayW, displayH, offsetX, offsetY;
176
- if (containerAspect > bufferAspect) {
177
- displayW = containerRect.width;
178
- displayH = containerRect.width / bufferAspect;
179
- offsetX = 0;
180
- offsetY = (containerRect.height - displayH) / 2;
181
- } else {
182
- displayH = containerRect.height;
183
- displayW = containerRect.height * bufferAspect;
184
- offsetX = (containerRect.width - displayW) / 2;
185
- offsetY = 0;
186
- }
187
-
188
- const scaleX = displayW / effectiveBufferW;
189
- const scaleY = displayH / effectiveBufferH;
173
+ const displayW = vW * perspectiveScale;
174
+ const displayH = vH * perspectiveScale;
175
+ const offsetX = (containerRect.width - displayW) / 2;
176
+ const offsetY = (containerRect.height - displayH) / 2;
190
177
 
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);
178
+ // The tracker uses focal length based on height dimension in tracker space
179
+ const f = (videoH / 2) / Math.tan((45.0 * Math.PI / 180) / 2);
194
180
 
195
- // 4. Project marker center into camera space
181
+ // Center of the marker in camera space (marker coordinates origin at top-left)
196
182
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
197
183
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
198
184
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
199
185
 
200
- // 5. Map Camera coordinates to Screen coordinates
201
- let screenX, screenY;
202
- if (needsRotation) {
203
- // Mapping Sensor coordinates to Rotated Screen coordinates
204
- // Sensor +X -> Screen +Y
205
- // Sensor +Y -> Screen -X (relative to logical center)
206
- screenX = offsetX + (effectiveBufferW / 2 + (ty * f / tz)) * scaleX;
207
- screenY = offsetY + (effectiveBufferH / 2 - (tx * f / tz)) * scaleY;
208
- } else {
209
- screenX = offsetX + (effectiveBufferW / 2 + (tx * f / tz)) * scaleX;
210
- screenY = offsetY + (effectiveBufferH / 2 + (ty * f / tz)) * scaleY;
211
- }
186
+ let screenX, screenY, rotation;
212
187
 
213
- // 6. Rotation: sync with CSS transform
214
- //atan2 gives angle of world X-axis in camera space.
215
- let rotation = Math.atan2(mVT[1][0], mVT[0][0]);
216
188
  if (needsRotation) {
217
- rotation -= Math.PI / 2; // Mapping Sensor frame to Portrait Screen frame
189
+ // Mapping for 90deg clockwise rotation:
190
+ // Buffer X (0..videoW) -> Screen Y (0..displayH)
191
+ // Buffer Y (0..videoH) -> Screen X (displayW..0)
192
+ const bufferOffsetX = (tx * f / tz);
193
+ const bufferOffsetY = (ty * f / tz);
194
+
195
+ screenX = offsetX + (displayW / 2) - (bufferOffsetY * perspectiveScale);
196
+ screenY = offsetY + (displayH / 2) + (bufferOffsetX * perspectiveScale);
197
+ rotation = Math.atan2(mVT[1][0], mVT[0][0]) - Math.PI / 2;
198
+ } else {
199
+ // Normal mapping:
200
+ // Buffer X -> Screen X
201
+ // Buffer Y -> Screen Y
202
+ const bufferOffsetX = (tx * f / tz);
203
+ const bufferOffsetY = (ty * f / tz);
204
+
205
+ screenX = offsetX + (displayW / 2) + (bufferOffsetX * perspectiveScale);
206
+ screenY = offsetY + (displayH / 2) + (bufferOffsetY * perspectiveScale);
207
+ rotation = Math.atan2(mVT[1][0], mVT[0][0]);
218
208
  }
219
209
 
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;
210
+ // Final Scale calculation:
211
+ // f/tz converts world units to buffer pixels
212
+ // perspectiveScale converts buffer pixels to screen pixels
213
+ // matrixScale handles the target's relative orientation in 2D
224
214
  const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
225
- const perspectiveScale = (f / tz) * scaleX;
226
-
227
- // Target pixel width on screen = markerW * matrixScale * perspectiveScale
228
- const targetScreenWidth = markerW * matrixScale * perspectiveScale;
229
- const finalScale = (targetScreenWidth / BASE_CSS_WIDTH) * this.scaleMultiplier;
215
+ const finalScale = (f / tz) * perspectiveScale * matrixScale * this.scaleMultiplier;
230
216
 
231
217
  // Apply
232
- this.overlay.style.width = `${BASE_CSS_WIDTH}px`;
218
+ this.overlay.style.width = `${markerW}px`;
233
219
  this.overlay.style.height = 'auto';
234
220
  this.overlay.style.position = 'absolute';
235
221
  this.overlay.style.transformOrigin = 'center center';
236
222
  this.overlay.style.left = '0';
237
223
  this.overlay.style.top = '0';
238
224
  this.overlay.style.transform = `
239
- translate(${screenX}px, ${screenY}px)
240
- translate(-50%, -50%)
241
- scale(${finalScale})
242
- rotate(${rotation}rad)
243
- `;
225
+ translate(${screenX}px, ${screenY}px)
226
+ translate(-50%, -50%)
227
+ scale(${finalScale})
228
+ rotate(${rotation}rad)
229
+ `;
244
230
  }
245
231
  }
246
232