@srsergio/taptapp-ar 1.0.13 → 1.0.15
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/controller.d.ts +11 -0
- package/dist/compiler/controller.js +57 -2
- package/dist/compiler/detector/crop-detector.js +11 -6
- package/dist/compiler/simple-ar.js +5 -2
- package/package.json +1 -1
- package/src/compiler/controller.js +71 -2
- package/src/compiler/detector/crop-detector.js +12 -6
- package/src/compiler/simple-ar.js +6 -2
|
@@ -51,6 +51,7 @@ export class Controller {
|
|
|
51
51
|
trackingDataList: any[];
|
|
52
52
|
};
|
|
53
53
|
tracker: Tracker | undefined;
|
|
54
|
+
matchingDataList: any[] | undefined;
|
|
54
55
|
/**
|
|
55
56
|
* Load image targets from a single ArrayBuffer (backward compatible)
|
|
56
57
|
* @param {ArrayBuffer} buffer - Single .mind file buffer
|
|
@@ -100,8 +101,18 @@ export class Controller {
|
|
|
100
101
|
trackUpdate(modelViewTransform: any, trackFeatures: any): Promise<any>;
|
|
101
102
|
_workerMatch(featurePoints: any, targetIndexes: any): Promise<any>;
|
|
102
103
|
workerMatchDone: ((data: any) => void) | undefined;
|
|
104
|
+
_matchOnMainThread(featurePoints: any, targetIndexes: any): Promise<{
|
|
105
|
+
targetIndex: number;
|
|
106
|
+
modelViewTransform: number[][] | null;
|
|
107
|
+
debugExtra: {
|
|
108
|
+
frames: never[];
|
|
109
|
+
} | null;
|
|
110
|
+
}>;
|
|
111
|
+
mainThreadMatcher: import("./matching/matcher.js").Matcher | undefined;
|
|
112
|
+
mainThreadEstimator: import("./estimation/estimator.js").Estimator | undefined;
|
|
103
113
|
_workerTrackUpdate(modelViewTransform: any, trackingFeatures: any): Promise<any>;
|
|
104
114
|
workerTrackDone: ((data: any) => void) | undefined;
|
|
115
|
+
_trackUpdateOnMainThread(modelViewTransform: any, trackingFeatures: any): Promise<never[][] | null>;
|
|
105
116
|
_glModelViewMatrix(modelViewTransform: any, targetIndex: any): any[];
|
|
106
117
|
_glProjectionMatrix({ projectionTransform, width, height, near, far }: {
|
|
107
118
|
projectionTransform: any;
|
|
@@ -120,6 +120,7 @@ class Controller {
|
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
this.markerDimensions = allDimensions;
|
|
123
|
+
this.matchingDataList = allMatchingData; // Store for main-thread fallback
|
|
123
124
|
return { dimensions: allDimensions, matchingDataList: allMatchingData, trackingDataList: allTrackingData };
|
|
124
125
|
}
|
|
125
126
|
/**
|
|
@@ -324,6 +325,11 @@ class Controller {
|
|
|
324
325
|
}
|
|
325
326
|
_workerMatch(featurePoints, targetIndexes) {
|
|
326
327
|
return new Promise((resolve) => {
|
|
328
|
+
// If no worker available, process on main thread
|
|
329
|
+
if (!this.worker) {
|
|
330
|
+
this._matchOnMainThread(featurePoints, targetIndexes).then(resolve);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
327
333
|
this.workerMatchDone = (data) => {
|
|
328
334
|
resolve({
|
|
329
335
|
targetIndex: data.targetIndex,
|
|
@@ -331,16 +337,51 @@ class Controller {
|
|
|
331
337
|
debugExtra: data.debugExtra,
|
|
332
338
|
});
|
|
333
339
|
};
|
|
334
|
-
this.worker
|
|
340
|
+
this.worker.postMessage({ type: "match", featurePoints: featurePoints, targetIndexes });
|
|
335
341
|
});
|
|
336
342
|
}
|
|
343
|
+
async _matchOnMainThread(featurePoints, targetIndexes) {
|
|
344
|
+
// Lazy initialize Matcher and Estimator for main thread
|
|
345
|
+
if (!this.mainThreadMatcher) {
|
|
346
|
+
const { Matcher } = await import("./matching/matcher.js");
|
|
347
|
+
const { Estimator } = await import("./estimation/estimator.js");
|
|
348
|
+
this.mainThreadMatcher = new Matcher(this.inputWidth, this.inputHeight, this.debugMode);
|
|
349
|
+
this.mainThreadEstimator = new Estimator(this.projectionTransform);
|
|
350
|
+
}
|
|
351
|
+
let matchedTargetIndex = -1;
|
|
352
|
+
let matchedModelViewTransform = null;
|
|
353
|
+
let matchedDebugExtra = null;
|
|
354
|
+
for (let i = 0; i < targetIndexes.length; i++) {
|
|
355
|
+
const matchingIndex = targetIndexes[i];
|
|
356
|
+
const { keyframeIndex, screenCoords, worldCoords, debugExtra } = this.mainThreadMatcher.matchDetection(this.matchingDataList[matchingIndex], featurePoints);
|
|
357
|
+
matchedDebugExtra = debugExtra;
|
|
358
|
+
if (keyframeIndex !== -1) {
|
|
359
|
+
const modelViewTransform = this.mainThreadEstimator.estimate({ screenCoords, worldCoords });
|
|
360
|
+
if (modelViewTransform) {
|
|
361
|
+
matchedTargetIndex = matchingIndex;
|
|
362
|
+
matchedModelViewTransform = modelViewTransform;
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
targetIndex: matchedTargetIndex,
|
|
369
|
+
modelViewTransform: matchedModelViewTransform,
|
|
370
|
+
debugExtra: matchedDebugExtra,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
337
373
|
_workerTrackUpdate(modelViewTransform, trackingFeatures) {
|
|
338
374
|
return new Promise((resolve) => {
|
|
375
|
+
// If no worker available, process on main thread
|
|
376
|
+
if (!this.worker) {
|
|
377
|
+
this._trackUpdateOnMainThread(modelViewTransform, trackingFeatures).then(resolve);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
339
380
|
this.workerTrackDone = (data) => {
|
|
340
381
|
resolve(data.modelViewTransform);
|
|
341
382
|
};
|
|
342
383
|
const { worldCoords, screenCoords } = trackingFeatures;
|
|
343
|
-
this.worker
|
|
384
|
+
this.worker.postMessage({
|
|
344
385
|
type: "trackUpdate",
|
|
345
386
|
modelViewTransform,
|
|
346
387
|
worldCoords,
|
|
@@ -348,6 +389,20 @@ class Controller {
|
|
|
348
389
|
});
|
|
349
390
|
});
|
|
350
391
|
}
|
|
392
|
+
async _trackUpdateOnMainThread(modelViewTransform, trackingFeatures) {
|
|
393
|
+
// Lazy initialize Estimator for main thread
|
|
394
|
+
if (!this.mainThreadEstimator) {
|
|
395
|
+
const { Estimator } = await import("./estimation/estimator.js");
|
|
396
|
+
this.mainThreadEstimator = new Estimator(this.projectionTransform);
|
|
397
|
+
}
|
|
398
|
+
const { worldCoords, screenCoords } = trackingFeatures;
|
|
399
|
+
const finalModelViewTransform = this.mainThreadEstimator.refineEstimate({
|
|
400
|
+
initialModelViewTransform: modelViewTransform,
|
|
401
|
+
worldCoords,
|
|
402
|
+
screenCoords,
|
|
403
|
+
});
|
|
404
|
+
return finalModelViewTransform;
|
|
405
|
+
}
|
|
351
406
|
_glModelViewMatrix(modelViewTransform, targetIndex) {
|
|
352
407
|
const height = this.markerDimensions[targetIndex][1];
|
|
353
408
|
const openGLWorldMatrix = [
|
|
@@ -24,11 +24,16 @@ class CropDetector {
|
|
|
24
24
|
}
|
|
25
25
|
detectMoving(input) {
|
|
26
26
|
const imageData = input;
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
// Expanded to 5x5 grid (25 positions) for better coverage
|
|
28
|
+
const gridSize = 5;
|
|
29
|
+
const dx = this.lastRandomIndex % gridSize;
|
|
30
|
+
const dy = Math.floor(this.lastRandomIndex / gridSize);
|
|
31
|
+
// Calculate offset from center, with overlap for better detection
|
|
32
|
+
const stepX = this.cropSize / 3;
|
|
33
|
+
const stepY = this.cropSize / 3;
|
|
34
|
+
let startY = Math.floor(this.height / 2 - this.cropSize / 2 + (dy - 2) * stepY);
|
|
35
|
+
let startX = Math.floor(this.width / 2 - this.cropSize / 2 + (dx - 2) * stepX);
|
|
36
|
+
// Clamp to valid bounds
|
|
32
37
|
if (startX < 0)
|
|
33
38
|
startX = 0;
|
|
34
39
|
if (startY < 0)
|
|
@@ -37,7 +42,7 @@ class CropDetector {
|
|
|
37
42
|
startX = this.width - this.cropSize - 1;
|
|
38
43
|
if (startY >= this.height - this.cropSize)
|
|
39
44
|
startY = this.height - this.cropSize - 1;
|
|
40
|
-
this.lastRandomIndex = (this.lastRandomIndex + 1) %
|
|
45
|
+
this.lastRandomIndex = (this.lastRandomIndex + 1) % (gridSize * gridSize);
|
|
41
46
|
const result = this._detect(imageData, startX, startY);
|
|
42
47
|
return result;
|
|
43
48
|
}
|
|
@@ -152,11 +152,14 @@ class SimpleAR {
|
|
|
152
152
|
// Matrix is column-major: [m0,m1,m2,m3, m4,m5,m6,m7, m8,m9,m10,m11, m12,m13,m14,m15]
|
|
153
153
|
const tx = worldMatrix[12];
|
|
154
154
|
const ty = worldMatrix[13];
|
|
155
|
-
const
|
|
155
|
+
const matrixScale = Math.sqrt(worldMatrix[0] ** 2 + worldMatrix[1] ** 2);
|
|
156
156
|
const rotation = Math.atan2(worldMatrix[1], worldMatrix[0]);
|
|
157
157
|
// Convert from normalized coords to screen coords
|
|
158
158
|
const screenX = offsetX + (videoW / 2 + tx) * scaleX;
|
|
159
159
|
const screenY = offsetY + (videoH / 2 - ty) * scaleY;
|
|
160
|
+
// Scale factor: use a reasonable multiplier (0.5 instead of 0.01)
|
|
161
|
+
// The matrixScale from the tracking is in image-space coordinates
|
|
162
|
+
const finalScale = matrixScale * scaleX * 0.5;
|
|
160
163
|
// Apply transform
|
|
161
164
|
this.overlay.style.position = 'absolute';
|
|
162
165
|
this.overlay.style.transformOrigin = 'center center';
|
|
@@ -165,7 +168,7 @@ class SimpleAR {
|
|
|
165
168
|
this.overlay.style.transform = `
|
|
166
169
|
translate(${screenX}px, ${screenY}px)
|
|
167
170
|
translate(-50%, -50%)
|
|
168
|
-
scale(${
|
|
171
|
+
scale(${finalScale})
|
|
169
172
|
rotate(${-rotation}rad)
|
|
170
173
|
`;
|
|
171
174
|
}
|
package/package.json
CHANGED
|
@@ -153,6 +153,7 @@ class Controller {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
this.markerDimensions = allDimensions;
|
|
156
|
+
this.matchingDataList = allMatchingData; // Store for main-thread fallback
|
|
156
157
|
return { dimensions: allDimensions, matchingDataList: allMatchingData, trackingDataList: allTrackingData };
|
|
157
158
|
}
|
|
158
159
|
|
|
@@ -395,6 +396,12 @@ class Controller {
|
|
|
395
396
|
|
|
396
397
|
_workerMatch(featurePoints, targetIndexes) {
|
|
397
398
|
return new Promise((resolve) => {
|
|
399
|
+
// If no worker available, process on main thread
|
|
400
|
+
if (!this.worker) {
|
|
401
|
+
this._matchOnMainThread(featurePoints, targetIndexes).then(resolve);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
398
405
|
this.workerMatchDone = (data) => {
|
|
399
406
|
resolve({
|
|
400
407
|
targetIndex: data.targetIndex,
|
|
@@ -402,17 +409,63 @@ class Controller {
|
|
|
402
409
|
debugExtra: data.debugExtra,
|
|
403
410
|
});
|
|
404
411
|
};
|
|
405
|
-
this.worker
|
|
412
|
+
this.worker.postMessage({ type: "match", featurePoints: featurePoints, targetIndexes });
|
|
406
413
|
});
|
|
407
414
|
}
|
|
408
415
|
|
|
416
|
+
async _matchOnMainThread(featurePoints, targetIndexes) {
|
|
417
|
+
// Lazy initialize Matcher and Estimator for main thread
|
|
418
|
+
if (!this.mainThreadMatcher) {
|
|
419
|
+
const { Matcher } = await import("./matching/matcher.js");
|
|
420
|
+
const { Estimator } = await import("./estimation/estimator.js");
|
|
421
|
+
this.mainThreadMatcher = new Matcher(this.inputWidth, this.inputHeight, this.debugMode);
|
|
422
|
+
this.mainThreadEstimator = new Estimator(this.projectionTransform);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
let matchedTargetIndex = -1;
|
|
426
|
+
let matchedModelViewTransform = null;
|
|
427
|
+
let matchedDebugExtra = null;
|
|
428
|
+
|
|
429
|
+
for (let i = 0; i < targetIndexes.length; i++) {
|
|
430
|
+
const matchingIndex = targetIndexes[i];
|
|
431
|
+
|
|
432
|
+
const { keyframeIndex, screenCoords, worldCoords, debugExtra } = this.mainThreadMatcher.matchDetection(
|
|
433
|
+
this.matchingDataList[matchingIndex],
|
|
434
|
+
featurePoints,
|
|
435
|
+
);
|
|
436
|
+
matchedDebugExtra = debugExtra;
|
|
437
|
+
|
|
438
|
+
if (keyframeIndex !== -1) {
|
|
439
|
+
const modelViewTransform = this.mainThreadEstimator.estimate({ screenCoords, worldCoords });
|
|
440
|
+
|
|
441
|
+
if (modelViewTransform) {
|
|
442
|
+
matchedTargetIndex = matchingIndex;
|
|
443
|
+
matchedModelViewTransform = modelViewTransform;
|
|
444
|
+
}
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
targetIndex: matchedTargetIndex,
|
|
451
|
+
modelViewTransform: matchedModelViewTransform,
|
|
452
|
+
debugExtra: matchedDebugExtra,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
409
456
|
_workerTrackUpdate(modelViewTransform, trackingFeatures) {
|
|
410
457
|
return new Promise((resolve) => {
|
|
458
|
+
// If no worker available, process on main thread
|
|
459
|
+
if (!this.worker) {
|
|
460
|
+
this._trackUpdateOnMainThread(modelViewTransform, trackingFeatures).then(resolve);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
411
464
|
this.workerTrackDone = (data) => {
|
|
412
465
|
resolve(data.modelViewTransform);
|
|
413
466
|
};
|
|
414
467
|
const { worldCoords, screenCoords } = trackingFeatures;
|
|
415
|
-
this.worker
|
|
468
|
+
this.worker.postMessage({
|
|
416
469
|
type: "trackUpdate",
|
|
417
470
|
modelViewTransform,
|
|
418
471
|
worldCoords,
|
|
@@ -421,6 +474,22 @@ class Controller {
|
|
|
421
474
|
});
|
|
422
475
|
}
|
|
423
476
|
|
|
477
|
+
async _trackUpdateOnMainThread(modelViewTransform, trackingFeatures) {
|
|
478
|
+
// Lazy initialize Estimator for main thread
|
|
479
|
+
if (!this.mainThreadEstimator) {
|
|
480
|
+
const { Estimator } = await import("./estimation/estimator.js");
|
|
481
|
+
this.mainThreadEstimator = new Estimator(this.projectionTransform);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const { worldCoords, screenCoords } = trackingFeatures;
|
|
485
|
+
const finalModelViewTransform = this.mainThreadEstimator.refineEstimate({
|
|
486
|
+
initialModelViewTransform: modelViewTransform,
|
|
487
|
+
worldCoords,
|
|
488
|
+
screenCoords,
|
|
489
|
+
});
|
|
490
|
+
return finalModelViewTransform;
|
|
491
|
+
}
|
|
492
|
+
|
|
424
493
|
_glModelViewMatrix(modelViewTransform, targetIndex) {
|
|
425
494
|
const height = this.markerDimensions[targetIndex][1];
|
|
426
495
|
|
|
@@ -33,19 +33,25 @@ class CropDetector {
|
|
|
33
33
|
detectMoving(input) {
|
|
34
34
|
const imageData = input;
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
const
|
|
38
|
-
const
|
|
36
|
+
// Expanded to 5x5 grid (25 positions) for better coverage
|
|
37
|
+
const gridSize = 5;
|
|
38
|
+
const dx = this.lastRandomIndex % gridSize;
|
|
39
|
+
const dy = Math.floor(this.lastRandomIndex / gridSize);
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// Calculate offset from center, with overlap for better detection
|
|
42
|
+
const stepX = this.cropSize / 3;
|
|
43
|
+
const stepY = this.cropSize / 3;
|
|
42
44
|
|
|
45
|
+
let startY = Math.floor(this.height / 2 - this.cropSize / 2 + (dy - 2) * stepY);
|
|
46
|
+
let startX = Math.floor(this.width / 2 - this.cropSize / 2 + (dx - 2) * stepX);
|
|
47
|
+
|
|
48
|
+
// Clamp to valid bounds
|
|
43
49
|
if (startX < 0) startX = 0;
|
|
44
50
|
if (startY < 0) startY = 0;
|
|
45
51
|
if (startX >= this.width - this.cropSize) startX = this.width - this.cropSize - 1;
|
|
46
52
|
if (startY >= this.height - this.cropSize) startY = this.height - this.cropSize - 1;
|
|
47
53
|
|
|
48
|
-
this.lastRandomIndex = (this.lastRandomIndex + 1) %
|
|
54
|
+
this.lastRandomIndex = (this.lastRandomIndex + 1) % (gridSize * gridSize);
|
|
49
55
|
|
|
50
56
|
const result = this._detect(imageData, startX, startY);
|
|
51
57
|
return result;
|
|
@@ -179,13 +179,17 @@ class SimpleAR {
|
|
|
179
179
|
// Matrix is column-major: [m0,m1,m2,m3, m4,m5,m6,m7, m8,m9,m10,m11, m12,m13,m14,m15]
|
|
180
180
|
const tx = worldMatrix[12];
|
|
181
181
|
const ty = worldMatrix[13];
|
|
182
|
-
const
|
|
182
|
+
const matrixScale = Math.sqrt(worldMatrix[0] ** 2 + worldMatrix[1] ** 2);
|
|
183
183
|
const rotation = Math.atan2(worldMatrix[1], worldMatrix[0]);
|
|
184
184
|
|
|
185
185
|
// Convert from normalized coords to screen coords
|
|
186
186
|
const screenX = offsetX + (videoW / 2 + tx) * scaleX;
|
|
187
187
|
const screenY = offsetY + (videoH / 2 - ty) * scaleY;
|
|
188
188
|
|
|
189
|
+
// Scale factor: use a reasonable multiplier (0.5 instead of 0.01)
|
|
190
|
+
// The matrixScale from the tracking is in image-space coordinates
|
|
191
|
+
const finalScale = matrixScale * scaleX * 0.5;
|
|
192
|
+
|
|
189
193
|
// Apply transform
|
|
190
194
|
this.overlay.style.position = 'absolute';
|
|
191
195
|
this.overlay.style.transformOrigin = 'center center';
|
|
@@ -194,7 +198,7 @@ class SimpleAR {
|
|
|
194
198
|
this.overlay.style.transform = `
|
|
195
199
|
translate(${screenX}px, ${screenY}px)
|
|
196
200
|
translate(-50%, -50%)
|
|
197
|
-
scale(${
|
|
201
|
+
scale(${finalScale})
|
|
198
202
|
rotate(${-rotation}rad)
|
|
199
203
|
`;
|
|
200
204
|
}
|