@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.
- package/dist/compiler/simple-ar.js +36 -19
- package/package.json +1 -1
- package/src/compiler/simple-ar.js +39 -20
|
@@ -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
|
-
|
|
135
|
-
|
|
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
|
|
147
|
+
const bufferAspect = effectiveBufferW / effectiveBufferH;
|
|
139
148
|
let displayW, displayH, offsetX, offsetY;
|
|
140
|
-
if (containerAspect >
|
|
149
|
+
if (containerAspect > bufferAspect) {
|
|
141
150
|
displayW = containerRect.width;
|
|
142
|
-
displayH = containerRect.width /
|
|
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 *
|
|
157
|
+
displayW = containerRect.height * bufferAspect;
|
|
149
158
|
offsetX = (containerRect.width - displayW) / 2;
|
|
150
159
|
offsetY = 0;
|
|
151
160
|
}
|
|
152
|
-
const scaleX = displayW /
|
|
153
|
-
const scaleY = displayH /
|
|
154
|
-
// Project
|
|
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 =
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
//
|
|
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
|
@@ -155,48 +155,68 @@ class SimpleAR {
|
|
|
155
155
|
|
|
156
156
|
const [markerW, markerH] = this.markerDimensions[targetIndex];
|
|
157
157
|
const containerRect = this.container.getBoundingClientRect();
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
175
|
+
const bufferAspect = effectiveBufferW / effectiveBufferH;
|
|
164
176
|
|
|
165
177
|
let displayW, displayH, offsetX, offsetY;
|
|
166
178
|
|
|
167
|
-
if (containerAspect >
|
|
179
|
+
if (containerAspect > bufferAspect) {
|
|
168
180
|
displayW = containerRect.width;
|
|
169
|
-
displayH = containerRect.width /
|
|
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 *
|
|
186
|
+
displayW = containerRect.height * bufferAspect;
|
|
175
187
|
offsetX = (containerRect.width - displayW) / 2;
|
|
176
188
|
offsetY = 0;
|
|
177
189
|
}
|
|
178
190
|
|
|
179
|
-
const scaleX = displayW /
|
|
180
|
-
const scaleY = displayH /
|
|
191
|
+
const scaleX = displayW / effectiveBufferW;
|
|
192
|
+
const scaleY = displayH / effectiveBufferH;
|
|
181
193
|
|
|
182
|
-
// Project
|
|
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 =
|
|
200
|
+
const f = effectiveBufferH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
|
|
189
201
|
|
|
190
|
-
//
|
|
191
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
233
|
+
// Apply transform
|
|
215
234
|
this.overlay.style.width = 'auto';
|
|
216
235
|
this.overlay.style.height = 'auto';
|
|
217
236
|
this.overlay.style.position = 'absolute';
|