@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.
- package/dist/compiler/simple-ar.js +40 -55
- package/package.json +1 -1
- package/src/compiler/simple-ar.js +43 -57
|
@@ -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
|
-
//
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
169
|
-
let screenX, screenY;
|
|
154
|
+
let screenX, screenY, rotation;
|
|
170
155
|
if (needsRotation) {
|
|
171
|
-
// Mapping
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
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 = `${
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
@@ -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
|
-
//
|
|
169
|
-
const
|
|
170
|
-
const
|
|
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
|
-
|
|
173
|
-
const
|
|
170
|
+
// Robust "object-fit: cover" scale calculation
|
|
171
|
+
const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
|
|
174
172
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
//
|
|
192
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
|
|
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
|
|
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 = `${
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
|