@srsergio/taptapp-ar 1.0.16 → 1.0.18
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.
|
@@ -283,7 +283,7 @@ class Controller {
|
|
|
283
283
|
clone = this.getRotatedZ90Matrix(clone);
|
|
284
284
|
}
|
|
285
285
|
this.onUpdate &&
|
|
286
|
-
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone });
|
|
286
|
+
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone, modelViewTransform: trackingState.currentModelViewTransform });
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
this.onUpdate && this.onUpdate({ type: "processDone" });
|
|
@@ -47,6 +47,7 @@ export class SimpleAR {
|
|
|
47
47
|
* Initialize and start AR tracking
|
|
48
48
|
*/
|
|
49
49
|
start(): Promise<this>;
|
|
50
|
+
markerDimensions: any;
|
|
50
51
|
/**
|
|
51
52
|
* Stop AR tracking and release resources
|
|
52
53
|
*/
|
|
@@ -55,6 +56,6 @@ export class SimpleAR {
|
|
|
55
56
|
_startCamera(): Promise<void>;
|
|
56
57
|
_initController(): void;
|
|
57
58
|
_handleUpdate(data: any): void;
|
|
58
|
-
_positionOverlay(
|
|
59
|
+
_positionOverlay(mVT: any, targetIndex: any): void;
|
|
59
60
|
}
|
|
60
61
|
import { Controller } from "./controller.js";
|
|
@@ -41,7 +41,8 @@ class SimpleAR {
|
|
|
41
41
|
this._initController();
|
|
42
42
|
// 4. Load targets (supports single URL or array of URLs)
|
|
43
43
|
const targets = Array.isArray(this.targetSrc) ? this.targetSrc : [this.targetSrc];
|
|
44
|
-
await this.controller.addImageTargets(targets);
|
|
44
|
+
const result = await this.controller.addImageTargets(targets);
|
|
45
|
+
this.markerDimensions = result.dimensions; // [ [w1, h1], [w2, h2], ... ]
|
|
45
46
|
this.controller.processVideo(this.video);
|
|
46
47
|
return this;
|
|
47
48
|
}
|
|
@@ -59,6 +60,7 @@ class SimpleAR {
|
|
|
59
60
|
this.video = null;
|
|
60
61
|
}
|
|
61
62
|
this.isTracking = false;
|
|
63
|
+
this.markerDimensions = [];
|
|
62
64
|
}
|
|
63
65
|
_createVideo() {
|
|
64
66
|
this.video = document.createElement('video');
|
|
@@ -101,7 +103,7 @@ class SimpleAR {
|
|
|
101
103
|
_handleUpdate(data) {
|
|
102
104
|
if (data.type !== 'updateMatrix')
|
|
103
105
|
return;
|
|
104
|
-
const { targetIndex, worldMatrix } = data;
|
|
106
|
+
const { targetIndex, worldMatrix, modelViewTransform } = data;
|
|
105
107
|
if (worldMatrix) {
|
|
106
108
|
// Target found
|
|
107
109
|
if (!this.isTracking) {
|
|
@@ -110,7 +112,7 @@ class SimpleAR {
|
|
|
110
112
|
this.onFound && this.onFound({ targetIndex });
|
|
111
113
|
}
|
|
112
114
|
this.lastMatrix = worldMatrix;
|
|
113
|
-
this._positionOverlay(
|
|
115
|
+
this._positionOverlay(modelViewTransform, targetIndex);
|
|
114
116
|
this.onUpdateCallback && this.onUpdateCallback({ targetIndex, worldMatrix });
|
|
115
117
|
}
|
|
116
118
|
else {
|
|
@@ -122,9 +124,10 @@ class SimpleAR {
|
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
|
-
_positionOverlay(
|
|
126
|
-
if (!this.overlay)
|
|
127
|
+
_positionOverlay(mVT, targetIndex) {
|
|
128
|
+
if (!this.overlay || !this.markerDimensions[targetIndex])
|
|
127
129
|
return;
|
|
130
|
+
const [markerW, markerH] = this.markerDimensions[targetIndex];
|
|
128
131
|
const containerRect = this.container.getBoundingClientRect();
|
|
129
132
|
const videoW = this.video.videoWidth;
|
|
130
133
|
const videoH = this.video.videoHeight;
|
|
@@ -133,14 +136,12 @@ class SimpleAR {
|
|
|
133
136
|
const videoAspect = videoW / videoH;
|
|
134
137
|
let displayW, displayH, offsetX, offsetY;
|
|
135
138
|
if (containerAspect > videoAspect) {
|
|
136
|
-
// Container is wider - video fills width, crops height
|
|
137
139
|
displayW = containerRect.width;
|
|
138
140
|
displayH = containerRect.width / videoAspect;
|
|
139
141
|
offsetX = 0;
|
|
140
142
|
offsetY = (containerRect.height - displayH) / 2;
|
|
141
143
|
}
|
|
142
144
|
else {
|
|
143
|
-
// Container is taller - video fills height, crops width
|
|
144
145
|
displayH = containerRect.height;
|
|
145
146
|
displayW = containerRect.height * videoAspect;
|
|
146
147
|
offsetX = (containerRect.width - displayW) / 2;
|
|
@@ -148,23 +149,28 @@ class SimpleAR {
|
|
|
148
149
|
}
|
|
149
150
|
const scaleX = displayW / videoW;
|
|
150
151
|
const scaleY = displayH / videoH;
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
const tx =
|
|
154
|
-
const ty =
|
|
155
|
-
const tz =
|
|
156
|
-
// focal length (roughly 45 degrees FOV)
|
|
152
|
+
// Project the center of the marker (markerW/2, markerH/2, 0) into camera space
|
|
153
|
+
// Marker coordinates are pixels from top-left.
|
|
154
|
+
const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
|
|
155
|
+
const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
|
|
156
|
+
const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
|
|
157
|
+
// focal length (roughly 45 degrees FOV match Controller.js)
|
|
157
158
|
const f = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
|
|
158
|
-
//
|
|
159
|
-
// tx, ty, tz are in marker units relative to camera
|
|
159
|
+
// Perspective projection to screen space
|
|
160
160
|
const screenX = offsetX + (videoW / 2 + (tx * f / -tz)) * scaleX;
|
|
161
161
|
const screenY = offsetY + (videoH / 2 - (ty * f / -tz)) * scaleY;
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
const
|
|
162
|
+
// Use the first row of mVT to determine rotation and base scale component
|
|
163
|
+
// Since marker coordinates are in pixels, mVT[0][0] and mVT[0][1] are unitless scale factors
|
|
164
|
+
const rotation = Math.atan2(mVT[1][0], mVT[0][0]);
|
|
165
|
+
const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
|
|
165
166
|
// Perspective scale: how much larger/smaller the object is based on distance (tz)
|
|
167
|
+
// Correct scale should make a marker-width sized element cover the marker.
|
|
166
168
|
const perspectiveScale = (f / -tz) * scaleX;
|
|
167
|
-
|
|
169
|
+
// The overlay element has its own width in CSS pixels (e.g. 200px)
|
|
170
|
+
const overlayCSSWidth = parseFloat(this.overlay.style.width) || 200;
|
|
171
|
+
// Final scale = (Target Width in Pixels on screen) / (Overlay CSS Width)
|
|
172
|
+
// matrixScale is usually ~1.0 if tracking is rigid
|
|
173
|
+
const finalScale = (matrixScale * markerW * perspectiveScale) / overlayCSSWidth;
|
|
168
174
|
// Apply transform
|
|
169
175
|
this.overlay.style.position = 'absolute';
|
|
170
176
|
this.overlay.style.transformOrigin = 'center center';
|
package/package.json
CHANGED
|
@@ -348,7 +348,7 @@ class Controller {
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
this.onUpdate &&
|
|
351
|
-
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone });
|
|
351
|
+
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone, modelViewTransform: trackingState.currentModelViewTransform });
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
|
|
@@ -55,7 +55,9 @@ class SimpleAR {
|
|
|
55
55
|
|
|
56
56
|
// 4. Load targets (supports single URL or array of URLs)
|
|
57
57
|
const targets = Array.isArray(this.targetSrc) ? this.targetSrc : [this.targetSrc];
|
|
58
|
-
await this.controller.addImageTargets(targets);
|
|
58
|
+
const result = await this.controller.addImageTargets(targets);
|
|
59
|
+
this.markerDimensions = result.dimensions; // [ [w1, h1], [w2, h2], ... ]
|
|
60
|
+
|
|
59
61
|
this.controller.processVideo(this.video);
|
|
60
62
|
|
|
61
63
|
return this;
|
|
@@ -75,6 +77,7 @@ class SimpleAR {
|
|
|
75
77
|
this.video = null;
|
|
76
78
|
}
|
|
77
79
|
this.isTracking = false;
|
|
80
|
+
this.markerDimensions = [];
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
_createVideo() {
|
|
@@ -121,7 +124,7 @@ class SimpleAR {
|
|
|
121
124
|
_handleUpdate(data) {
|
|
122
125
|
if (data.type !== 'updateMatrix') return;
|
|
123
126
|
|
|
124
|
-
const { targetIndex, worldMatrix } = data;
|
|
127
|
+
const { targetIndex, worldMatrix, modelViewTransform } = data;
|
|
125
128
|
|
|
126
129
|
if (worldMatrix) {
|
|
127
130
|
// Target found
|
|
@@ -132,7 +135,7 @@ class SimpleAR {
|
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
this.lastMatrix = worldMatrix;
|
|
135
|
-
this._positionOverlay(
|
|
138
|
+
this._positionOverlay(modelViewTransform, targetIndex);
|
|
136
139
|
this.onUpdateCallback && this.onUpdateCallback({ targetIndex, worldMatrix });
|
|
137
140
|
|
|
138
141
|
} else {
|
|
@@ -145,9 +148,10 @@ class SimpleAR {
|
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
150
|
|
|
148
|
-
_positionOverlay(
|
|
149
|
-
if (!this.overlay) return;
|
|
151
|
+
_positionOverlay(mVT, targetIndex) {
|
|
152
|
+
if (!this.overlay || !this.markerDimensions[targetIndex]) return;
|
|
150
153
|
|
|
154
|
+
const [markerW, markerH] = this.markerDimensions[targetIndex];
|
|
151
155
|
const containerRect = this.container.getBoundingClientRect();
|
|
152
156
|
const videoW = this.video.videoWidth;
|
|
153
157
|
const videoH = this.video.videoHeight;
|
|
@@ -159,13 +163,11 @@ class SimpleAR {
|
|
|
159
163
|
let displayW, displayH, offsetX, offsetY;
|
|
160
164
|
|
|
161
165
|
if (containerAspect > videoAspect) {
|
|
162
|
-
// Container is wider - video fills width, crops height
|
|
163
166
|
displayW = containerRect.width;
|
|
164
167
|
displayH = containerRect.width / videoAspect;
|
|
165
168
|
offsetX = 0;
|
|
166
169
|
offsetY = (containerRect.height - displayH) / 2;
|
|
167
170
|
} else {
|
|
168
|
-
// Container is taller - video fills height, crops width
|
|
169
171
|
displayH = containerRect.height;
|
|
170
172
|
displayW = containerRect.height * videoAspect;
|
|
171
173
|
offsetX = (containerRect.width - displayW) / 2;
|
|
@@ -175,27 +177,34 @@ class SimpleAR {
|
|
|
175
177
|
const scaleX = displayW / videoW;
|
|
176
178
|
const scaleY = displayH / videoH;
|
|
177
179
|
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
const tx =
|
|
181
|
-
const ty =
|
|
182
|
-
const tz =
|
|
180
|
+
// Project the center of the marker (markerW/2, markerH/2, 0) into camera space
|
|
181
|
+
// Marker coordinates are pixels from top-left.
|
|
182
|
+
const tx = mVT[0][0] * (markerW / 2) + mVT[0][1] * (markerH / 2) + mVT[0][3];
|
|
183
|
+
const ty = mVT[1][0] * (markerW / 2) + mVT[1][1] * (markerH / 2) + mVT[1][3];
|
|
184
|
+
const tz = mVT[2][0] * (markerW / 2) + mVT[2][1] * (markerH / 2) + mVT[2][3];
|
|
183
185
|
|
|
184
|
-
// focal length (roughly 45 degrees FOV)
|
|
186
|
+
// focal length (roughly 45 degrees FOV match Controller.js)
|
|
185
187
|
const f = videoH / 2 / Math.tan((45.0 * Math.PI / 180) / 2);
|
|
186
188
|
|
|
187
|
-
//
|
|
188
|
-
// tx, ty, tz are in marker units relative to camera
|
|
189
|
+
// Perspective projection to screen space
|
|
189
190
|
const screenX = offsetX + (videoW / 2 + (tx * f / -tz)) * scaleX;
|
|
190
191
|
const screenY = offsetY + (videoH / 2 - (ty * f / -tz)) * scaleY;
|
|
191
192
|
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
const
|
|
193
|
+
// Use the first row of mVT to determine rotation and base scale component
|
|
194
|
+
// Since marker coordinates are in pixels, mVT[0][0] and mVT[0][1] are unitless scale factors
|
|
195
|
+
const rotation = Math.atan2(mVT[1][0], mVT[0][0]);
|
|
196
|
+
const matrixScale = Math.sqrt(mVT[0][0] ** 2 + mVT[1][0] ** 2);
|
|
195
197
|
|
|
196
198
|
// Perspective scale: how much larger/smaller the object is based on distance (tz)
|
|
199
|
+
// Correct scale should make a marker-width sized element cover the marker.
|
|
197
200
|
const perspectiveScale = (f / -tz) * scaleX;
|
|
198
|
-
|
|
201
|
+
|
|
202
|
+
// The overlay element has its own width in CSS pixels (e.g. 200px)
|
|
203
|
+
const overlayCSSWidth = parseFloat(this.overlay.style.width) || 200;
|
|
204
|
+
|
|
205
|
+
// Final scale = (Target Width in Pixels on screen) / (Overlay CSS Width)
|
|
206
|
+
// matrixScale is usually ~1.0 if tracking is rigid
|
|
207
|
+
const finalScale = (matrixScale * markerW * perspectiveScale) / overlayCSSWidth;
|
|
199
208
|
|
|
200
209
|
// Apply transform
|
|
201
210
|
this.overlay.style.position = 'absolute';
|