@srsergio/taptapp-ar 1.0.21 → 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.
@@ -131,51 +131,68 @@ class SimpleAR {
131
131
  return;
132
132
  const [markerW, markerH] = this.markerDimensions[targetIndex];
133
133
  const containerRect = this.container.getBoundingClientRect();
134
- const videoW = this.video.videoWidth;
135
- 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;
136
145
  // Calculate display area considering object-fit: cover
137
146
  const containerAspect = containerRect.width / containerRect.height;
138
- const videoAspect = videoW / videoH;
147
+ const bufferAspect = effectiveBufferW / effectiveBufferH;
139
148
  let displayW, displayH, offsetX, offsetY;
140
- if (containerAspect > videoAspect) {
149
+ if (containerAspect > bufferAspect) {
141
150
  displayW = containerRect.width;
142
- displayH = containerRect.width / videoAspect;
151
+ displayH = containerRect.width / bufferAspect;
143
152
  offsetX = 0;
144
153
  offsetY = (containerRect.height - displayH) / 2;
145
154
  }
146
155
  else {
147
156
  displayH = containerRect.height;
148
- displayW = containerRect.height * videoAspect;
157
+ displayW = containerRect.height * bufferAspect;
149
158
  offsetX = (containerRect.width - displayW) / 2;
150
159
  offsetY = 0;
151
160
  }
152
- const scaleX = displayW / videoW;
153
- const scaleY = displayH / videoH;
154
- // Project the center of the marker (markerW/2, markerH/2, 0) into camera space
161
+ const scaleX = displayW / effectiveBufferW;
162
+ const scaleY = displayH / effectiveBufferH;
163
+ // Project marker center into camera space
155
164
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
156
165
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
157
166
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
158
167
  // focal length (roughly 45 degrees FOV match Controller.js)
159
- const f = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
160
- // Perspective projection to screen space
161
- // Using + for both since t is relative to principal point and Y is down in screen coords.
162
- const screenX = offsetX + (videoW / 2 + (tx * f / tz)) * scaleX;
163
- const screenY = offsetY + (videoH / 2 + (ty * f / tz)) * scaleY;
164
- // Rotation calculation: atan2(y, x) of world X-axis in camera space
165
- 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;
166
185
  const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
167
- // Perspective scale: 1 world pixel = (f/tz) screen pixels
168
186
  const perspectiveScale = (f / tz) * scaleX;
169
187
  // Detect overlay intrinsic size
170
188
  const intrinsicWidth = (this.overlay instanceof HTMLVideoElement)
171
189
  ? this.overlay.videoWidth
172
190
  : (this.overlay instanceof HTMLImageElement ? this.overlay.naturalWidth : 0);
173
- // Final scale = (Target Width in Pixels on screen) / (Overlay Intrinsic Width) * scaleMultiplier
174
191
  const baseScale = intrinsicWidth > 0
175
192
  ? (matrixScale * markerW * perspectiveScale) / intrinsicWidth
176
193
  : 1.0;
177
194
  const finalScale = baseScale * this.scaleMultiplier;
178
- // Ensure element doesn't have CSS width that interferes with scaling
195
+ // Apply transform
179
196
  this.overlay.style.width = 'auto';
180
197
  this.overlay.style.height = 'auto';
181
198
  this.overlay.style.position = 'absolute';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "AR Compiler for Node.js and Browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -155,48 +155,68 @@ class SimpleAR {
155
155
 
156
156
  const [markerW, markerH] = this.markerDimensions[targetIndex];
157
157
  const containerRect = this.container.getBoundingClientRect();
158
- const videoW = this.video.videoWidth;
159
- 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;
160
172
 
161
173
  // Calculate display area considering object-fit: cover
162
174
  const containerAspect = containerRect.width / containerRect.height;
163
- const videoAspect = videoW / videoH;
175
+ const bufferAspect = effectiveBufferW / effectiveBufferH;
164
176
 
165
177
  let displayW, displayH, offsetX, offsetY;
166
178
 
167
- if (containerAspect > videoAspect) {
179
+ if (containerAspect > bufferAspect) {
168
180
  displayW = containerRect.width;
169
- displayH = containerRect.width / videoAspect;
181
+ displayH = containerRect.width / bufferAspect;
170
182
  offsetX = 0;
171
183
  offsetY = (containerRect.height - displayH) / 2;
172
184
  } else {
173
185
  displayH = containerRect.height;
174
- displayW = containerRect.height * videoAspect;
186
+ displayW = containerRect.height * bufferAspect;
175
187
  offsetX = (containerRect.width - displayW) / 2;
176
188
  offsetY = 0;
177
189
  }
178
190
 
179
- const scaleX = displayW / videoW;
180
- const scaleY = displayH / videoH;
191
+ const scaleX = displayW / effectiveBufferW;
192
+ const scaleY = displayH / effectiveBufferH;
181
193
 
182
- // Project the center of the marker (markerW/2, markerH/2, 0) into camera space
194
+ // Project marker center into camera space
183
195
  const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
184
196
  const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
185
197
  const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
186
198
 
187
199
  // focal length (roughly 45 degrees FOV match Controller.js)
188
- 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);
189
201
 
190
- // Perspective projection to screen space
191
- // Using + for both since t is relative to principal point and Y is down in screen coords.
192
- const screenX = offsetX + (videoW / 2 + (tx * f / tz)) * scaleX;
193
- const screenY = offsetY + (videoH / 2 + (ty * f / tz)) * scaleY;
202
+ // Normalized Device Coordinates (NDC) to Screen space
203
+ let screenX, screenY;
194
204
 
195
- // Rotation calculation: atan2(y, x) of world X-axis in camera space
196
- const rotation = Math.atan2(mVT[1][0], mVT[0][0]);
197
- 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
+ }
198
214
 
199
- // Perspective scale: 1 world pixel = (f/tz) screen pixels
215
+ // Rotation: compensate for buffer rotation
216
+ let rotation = Math.atan2(mVT[1][0], mVT[0][0]);
217
+ if (needsRotation) rotation -= Math.PI / 2;
218
+
219
+ const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
200
220
  const perspectiveScale = (f / tz) * scaleX;
201
221
 
202
222
  // Detect overlay intrinsic size
@@ -204,14 +224,13 @@ class SimpleAR {
204
224
  ? this.overlay.videoWidth
205
225
  : (this.overlay instanceof HTMLImageElement ? this.overlay.naturalWidth : 0);
206
226
 
207
- // Final scale = (Target Width in Pixels on screen) / (Overlay Intrinsic Width) * scaleMultiplier
208
227
  const baseScale = intrinsicWidth > 0
209
228
  ? (matrixScale * markerW * perspectiveScale) / intrinsicWidth
210
229
  : 1.0;
211
230
 
212
231
  const finalScale = baseScale * this.scaleMultiplier;
213
232
 
214
- // Ensure element doesn't have CSS width that interferes with scaling
233
+ // Apply transform
215
234
  this.overlay.style.width = 'auto';
216
235
  this.overlay.style.height = 'auto';
217
236
  this.overlay.style.position = 'absolute';