@srsergio/taptapp-ar 1.0.101 → 1.1.2
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/node-worker.js +1 -197
- package/dist/compiler/offline-compiler.js +1 -207
- package/dist/core/constants.js +1 -38
- package/dist/core/detector/crop-detector.js +1 -88
- package/dist/core/detector/detector-lite.js +1 -455
- package/dist/core/detector/freak.js +1 -89
- package/dist/core/estimation/estimate.js +1 -16
- package/dist/core/estimation/estimator.js +1 -30
- package/dist/core/estimation/morph-refinement.js +1 -116
- package/dist/core/estimation/non-rigid-refine.js +1 -70
- package/dist/core/estimation/pnp-solver.js +1 -109
- package/dist/core/estimation/refine-estimate.js +1 -311
- package/dist/core/estimation/utils.js +1 -67
- package/dist/core/features/auto-rotation-feature.js +1 -30
- package/dist/core/features/crop-detection-feature.js +1 -26
- package/dist/core/features/feature-base.js +1 -1
- package/dist/core/features/feature-manager.js +1 -55
- package/dist/core/features/one-euro-filter-feature.js +1 -44
- package/dist/core/features/temporal-filter-feature.js +1 -57
- package/dist/core/image-list.js +1 -54
- package/dist/core/input-loader.js +1 -87
- package/dist/core/matching/hamming-distance.js +1 -66
- package/dist/core/matching/hdc.js +1 -102
- package/dist/core/matching/hierarchical-clustering.js +1 -130
- package/dist/core/matching/hough.js +1 -170
- package/dist/core/matching/matcher.js +1 -66
- package/dist/core/matching/matching.js +1 -401
- package/dist/core/matching/ransacHomography.js +1 -132
- package/dist/core/perception/bio-inspired-engine.js +1 -232
- package/dist/core/perception/foveal-attention.js +1 -280
- package/dist/core/perception/index.js +1 -17
- package/dist/core/perception/predictive-coding.js +1 -278
- package/dist/core/perception/saccadic-controller.js +1 -269
- package/dist/core/perception/saliency-map.js +1 -254
- package/dist/core/perception/scale-orchestrator.js +1 -68
- package/dist/core/protocol.js +1 -254
- package/dist/core/tracker/extract-utils.js +1 -29
- package/dist/core/tracker/extract.js +1 -306
- package/dist/core/tracker/tracker.js +1 -352
- package/dist/core/utils/cumsum.js +1 -37
- package/dist/core/utils/delaunay.js +1 -125
- package/dist/core/utils/geometry.js +1 -101
- package/dist/core/utils/gpu-compute.js +1 -231
- package/dist/core/utils/homography.js +1 -138
- package/dist/core/utils/images.js +1 -108
- package/dist/core/utils/lsh-binarizer.js +1 -37
- package/dist/core/utils/lsh-direct.js +1 -76
- package/dist/core/utils/projection.js +1 -51
- package/dist/core/utils/randomizer.js +1 -25
- package/dist/core/utils/worker-pool.js +1 -89
- package/dist/index.js +1 -7
- package/dist/libs/one-euro-filter.js +1 -70
- package/dist/react/TaptappAR.js +1 -151
- package/dist/react/types.js +1 -16
- package/dist/react/use-ar.js +1 -118
- package/dist/runtime/aframe.js +1 -272
- package/dist/runtime/bio-inspired-controller.js +1 -358
- package/dist/runtime/controller.js +1 -592
- package/dist/runtime/controller.worker.js +1 -93
- package/dist/runtime/index.js +1 -5
- package/dist/runtime/three.js +1 -304
- package/dist/runtime/track.js +1 -381
- package/package.json +9 -3
|
@@ -1,108 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const upsampleBilinear = ({ image, padOneWidth, padOneHeight }) => {
|
|
3
|
-
const { width, height, data } = image;
|
|
4
|
-
const dstWidth = image.width * 2 + (padOneWidth ? 1 : 0);
|
|
5
|
-
const dstHeight = image.height * 2 + (padOneHeight ? 1 : 0);
|
|
6
|
-
const temp = new Float32Array(dstWidth * dstHeight);
|
|
7
|
-
for (let i = 0; i < dstWidth; i++) {
|
|
8
|
-
const si = 0.5 * i - 0.25;
|
|
9
|
-
let si0 = Math.floor(si);
|
|
10
|
-
let si1 = Math.ceil(si);
|
|
11
|
-
if (si0 < 0)
|
|
12
|
-
si0 = 0; // border
|
|
13
|
-
if (si1 >= width)
|
|
14
|
-
si1 = width - 1; // border
|
|
15
|
-
for (let j = 0; j < dstHeight; j++) {
|
|
16
|
-
const sj = 0.5 * j - 0.25;
|
|
17
|
-
let sj0 = Math.floor(sj);
|
|
18
|
-
let sj1 = Math.ceil(sj);
|
|
19
|
-
if (sj0 < 0)
|
|
20
|
-
sj0 = 0; // border
|
|
21
|
-
if (sj1 >= height)
|
|
22
|
-
sj1 = height - 1; //border
|
|
23
|
-
const value = (si1 - si) * (sj1 - sj) * data[sj0 * width + si0] +
|
|
24
|
-
(si1 - si) * (sj - sj0) * data[sj1 * width + si0] +
|
|
25
|
-
(si - si0) * (sj1 - sj) * data[sj0 * width + si1] +
|
|
26
|
-
(si - si0) * (sj - sj0) * data[sj1 * width + si1];
|
|
27
|
-
temp[j * dstWidth + i] = value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return { data: temp, width: dstWidth, height: dstHeight };
|
|
31
|
-
};
|
|
32
|
-
const downsampleBilinear = ({ image }) => {
|
|
33
|
-
const { data, width, height } = image;
|
|
34
|
-
const dstWidth = width >>> 1;
|
|
35
|
-
const dstHeight = height >>> 1;
|
|
36
|
-
const temp = new Uint8Array(dstWidth * dstHeight);
|
|
37
|
-
// Speed optimization: using Int32 views and manual indexing
|
|
38
|
-
// Also using bitwise operations for color averaging
|
|
39
|
-
for (let j = 0; j < dstHeight; j++) {
|
|
40
|
-
const row0 = (j * 2) * width;
|
|
41
|
-
const row1 = row0 + width;
|
|
42
|
-
const dstRow = j * dstWidth;
|
|
43
|
-
for (let i = 0; i < dstWidth; i++) {
|
|
44
|
-
const i2 = i * 2;
|
|
45
|
-
// Efficient Int32 math for blurring
|
|
46
|
-
const val = (data[row0 + i2] + data[row0 + i2 + 1] +
|
|
47
|
-
data[row1 + i2] + data[row1 + i2 + 1]) >> 2;
|
|
48
|
-
temp[dstRow + i] = val & 0xFF;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return { data: temp, width: dstWidth, height: dstHeight };
|
|
52
|
-
};
|
|
53
|
-
const resize = ({ image, ratio }) => {
|
|
54
|
-
// Fast path for identity
|
|
55
|
-
if (ratio === 1) {
|
|
56
|
-
return {
|
|
57
|
-
data: new Uint8Array(image.data), // Copy to be safe/consistent
|
|
58
|
-
width: image.width,
|
|
59
|
-
height: image.height
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
// Recursive downsampling for better quality on large reductions
|
|
63
|
-
if (ratio <= 0.5) {
|
|
64
|
-
// 1024 -> 512 -> ...
|
|
65
|
-
return resize({
|
|
66
|
-
image: downsampleBilinear({ image }),
|
|
67
|
-
ratio: ratio * 2
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
const width = Math.round(image.width * ratio) | 0;
|
|
71
|
-
const height = Math.round(image.height * ratio) | 0;
|
|
72
|
-
const imageData = new Uint8Array(width * height);
|
|
73
|
-
const srcData = image.data;
|
|
74
|
-
const srcW = image.width | 0;
|
|
75
|
-
const srcH = image.height | 0;
|
|
76
|
-
// Pre-calculate limits to avoid Math.min inside loop
|
|
77
|
-
const srcW_1 = (srcW - 1) | 0;
|
|
78
|
-
const srcH_1 = (srcH - 1) | 0;
|
|
79
|
-
let dstIndex = 0;
|
|
80
|
-
for (let j = 0; j < height; j++) {
|
|
81
|
-
// Y coords
|
|
82
|
-
const srcY = j / ratio;
|
|
83
|
-
const y0 = srcY | 0; // Math.floor
|
|
84
|
-
const y1 = (y0 < srcH_1 ? y0 + 1 : srcH_1) | 0;
|
|
85
|
-
const fy = srcY - y0;
|
|
86
|
-
const ify = 1 - fy;
|
|
87
|
-
// Row offsets
|
|
88
|
-
const row0 = (y0 * srcW) | 0;
|
|
89
|
-
const row1 = (y1 * srcW) | 0;
|
|
90
|
-
for (let i = 0; i < width; i++) {
|
|
91
|
-
// X coords
|
|
92
|
-
const srcX = i / ratio;
|
|
93
|
-
const x0 = srcX | 0; // Math.floor
|
|
94
|
-
const x1 = (x0 < srcW_1 ? x0 + 1 : srcW_1) | 0;
|
|
95
|
-
const fx = srcX - x0;
|
|
96
|
-
const ifx = 1 - fx;
|
|
97
|
-
// Bilinear interpolation optimized
|
|
98
|
-
// v = (1-fx)(1-fy)v00 + fx(1-fy)v10 + (1-fx)fy*v01 + fx*fy*v11
|
|
99
|
-
// Factored: (1-fy) * ((1-fx)v00 + fx*v10) + fy * ((1-fx)v01 + fx*v11)
|
|
100
|
-
const val0 = srcData[row0 + x0] * ifx + srcData[row0 + x1] * fx;
|
|
101
|
-
const val1 = srcData[row1 + x0] * ifx + srcData[row1 + x1] * fx;
|
|
102
|
-
const value = val0 * ify + val1 * fy;
|
|
103
|
-
imageData[dstIndex++] = value | 0;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return { data: imageData, width: width, height: height };
|
|
107
|
-
};
|
|
108
|
-
export { downsampleBilinear, upsampleBilinear, resize };
|
|
1
|
+
const t=({image:t,padOneWidth:e,padOneHeight:h})=>{const{width:a,height:i,data:r}=t,n=2*t.width+(e?1:0),o=2*t.height+(h?1:0),d=new Float32Array(n*o);for(let t=0;t<n;t++){const e=.5*t-.25;let h=Math.floor(e),g=Math.ceil(e);h<0&&(h=0),g>=a&&(g=a-1);for(let l=0;l<o;l++){const o=.5*l-.25;let w=Math.floor(o),c=Math.ceil(o);w<0&&(w=0),c>=i&&(c=i-1);const s=(g-e)*(c-o)*r[w*a+h]+(g-e)*(o-w)*r[c*a+h]+(e-h)*(c-o)*r[w*a+g]+(e-h)*(o-w)*r[c*a+g];d[l*n+t]=s}}return{data:d,width:n,height:o}},e=({image:t})=>{const{data:e,width:h,height:a}=t,i=h>>>1,r=a>>>1,n=new Uint8Array(i*r);for(let t=0;t<r;t++){const a=2*t*h,r=a+h,o=t*i;for(let t=0;t<i;t++){const h=2*t,i=e[a+h]+e[a+h+1]+e[r+h]+e[r+h+1]>>2;n[o+t]=255&i}}return{data:n,width:i,height:r}},h=({image:t,ratio:a})=>{if(1===a)return{data:new Uint8Array(t.data),width:t.width,height:t.height};if(a<=.5)return h({image:e({image:t}),ratio:2*a});const i=0|Math.round(t.width*a),r=0|Math.round(t.height*a),n=new Uint8Array(i*r),o=t.data,d=0|t.width,g=d-1|0,l=(0|t.height)-1|0;let w=0;for(let t=0;t<r;t++){const e=t/a,h=0|e,r=e-h,c=1-r,s=h*d|0,f=(0|(h<l?h+1:l))*d|0;for(let t=0;t<i;t++){const e=t/a,h=0|e,i=0|(h<g?h+1:g),d=e-h,l=1-d,u=(o[s+h]*l+o[s+i]*d)*c+(o[f+h]*l+o[f+i]*d)*r;n[w++]=0|u}}return{data:n,width:i,height:r}};export{e as downsampleBilinear,t as upsampleBilinear,h as resize};
|
|
@@ -1,37 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* LSH Binarizer for FREAK descriptors.
|
|
3
|
-
*
|
|
4
|
-
* This utility implements Locality Sensitive Hashing (LSH) for binary descriptors.
|
|
5
|
-
* It uses simple Bit-Sampling to reduce the 672 bits (84 bytes) of a FREAK
|
|
6
|
-
* descriptor into a 64-bit (8-byte) fingerprint.
|
|
7
|
-
*
|
|
8
|
-
* Bit-sampling is chosen for maximum speed and zero memory overhead,
|
|
9
|
-
* which fits the Moonshot goal of an ultra-lightweight bundle.
|
|
10
|
-
*/
|
|
11
|
-
// For 64-bit LSH, we use a uniform sampling across the 672-bit descriptor.
|
|
12
|
-
const SAMPLING_INDICES = new Int32Array(64);
|
|
13
|
-
for (let i = 0; i < 64; i++) {
|
|
14
|
-
SAMPLING_INDICES[i] = Math.floor(i * (672 / 64));
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Converts an 84-byte FREAK descriptor into a Uint32Array of 2 elements (64 bits).
|
|
18
|
-
* @param {Uint8Array} descriptor - The 84-byte FREAK descriptor.
|
|
19
|
-
* @returns {Uint32Array} Array of two 32-bit integers.
|
|
20
|
-
*/
|
|
21
|
-
export function binarizeFREAK64(descriptor) {
|
|
22
|
-
const result = new Uint32Array(2);
|
|
23
|
-
for (let i = 0; i < 64; i++) {
|
|
24
|
-
const bitIndex = SAMPLING_INDICES[i];
|
|
25
|
-
const byteIdx = bitIndex >> 3;
|
|
26
|
-
const bitIdx = 7 - (bitIndex & 7);
|
|
27
|
-
if ((descriptor[byteIdx] >> bitIdx) & 1) {
|
|
28
|
-
const uintIdx = i >> 5; // i / 32
|
|
29
|
-
const uintBitIdx = i & 31; // i % 32
|
|
30
|
-
result[uintIdx] |= (1 << uintBitIdx);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
// Backward compatibility or for other uses
|
|
36
|
-
export const binarizeFREAK128 = binarizeFREAK64;
|
|
37
|
-
export const binarizeFREAK32 = binarizeFREAK64;
|
|
1
|
+
const n=new Int32Array(64);for(let r=0;r<64;r++)n[r]=Math.floor(10.5*r);export function binarizeFREAK64(r){const t=new Uint32Array(2);for(let o=0;o<64;o++){const e=n[o],i=7-(7&e);if(r[e>>3]>>i&1){const n=31&o;t[o>>5]|=1<<n}}return t}export const binarizeFREAK128=binarizeFREAK64;export const binarizeFREAK32=binarizeFREAK64;
|
|
@@ -1,76 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
/**
|
|
3
|
-
* 🚀 Moonshot: LSH-Direct Descriptor
|
|
4
|
-
*
|
|
5
|
-
* Instead of computing 672 bits of FREAK and then sampling 64 bits for LSH,
|
|
6
|
-
* we directly compute only the 64 bits we need.
|
|
7
|
-
*
|
|
8
|
-
* Speedup: >10x in descriptor generation.
|
|
9
|
-
*/
|
|
10
|
-
// 1. Pre-calculate the 64 pairs of indices (i, j) that correspond to our LSH sampling
|
|
11
|
-
const LSH_PAIRS = new Int32Array(64 * 2);
|
|
12
|
-
const SAMPLING_INDICES = new Int32Array(64);
|
|
13
|
-
for (let i = 0; i < 64; i++) {
|
|
14
|
-
SAMPLING_INDICES[i] = Math.floor(i * (672 / 64));
|
|
15
|
-
}
|
|
16
|
-
// Map bit indices to FREAK point pairs
|
|
17
|
-
let currentBit = 0;
|
|
18
|
-
let samplingIdx = 0;
|
|
19
|
-
for (let i = 0; i < FREAKPOINTS.length; i++) {
|
|
20
|
-
for (let j = i + 1; j < FREAKPOINTS.length; j++) {
|
|
21
|
-
if (samplingIdx < 64 && currentBit === SAMPLING_INDICES[samplingIdx]) {
|
|
22
|
-
LSH_PAIRS[samplingIdx * 2] = i;
|
|
23
|
-
LSH_PAIRS[samplingIdx * 2 + 1] = j;
|
|
24
|
-
samplingIdx++;
|
|
25
|
-
}
|
|
26
|
-
currentBit++;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Directly compute 64-bit LSH from FREAK samples
|
|
31
|
-
* @param {Float32Array} samples - Pre-sampled intensities at FREAK positions
|
|
32
|
-
* @returns {Uint32Array} 2-element array (64 bits)
|
|
33
|
-
*/
|
|
34
|
-
export function computeLSH64(samples) {
|
|
35
|
-
const result = new Uint32Array(2);
|
|
36
|
-
for (let i = 0; i < 64; i++) {
|
|
37
|
-
const p1 = LSH_PAIRS[i * 2];
|
|
38
|
-
const p2 = LSH_PAIRS[i * 2 + 1];
|
|
39
|
-
if (samples[p1] < samples[p2]) {
|
|
40
|
-
const uintIdx = i >> 5; // i / 32
|
|
41
|
-
const uintBitIdx = i & 31; // i % 32
|
|
42
|
-
result[uintIdx] |= (1 << uintBitIdx);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
// For backward compatibility if any 84-byte descriptor is still needed
|
|
48
|
-
export function computeFullFREAK(samples) {
|
|
49
|
-
const descriptor = new Uint8Array(84);
|
|
50
|
-
let bitCount = 0;
|
|
51
|
-
let byteIdx = 0;
|
|
52
|
-
for (let i = 0; i < FREAKPOINTS.length; i++) {
|
|
53
|
-
for (let j = i + 1; j < FREAKPOINTS.length; j++) {
|
|
54
|
-
if (samples[i] < samples[j]) {
|
|
55
|
-
descriptor[byteIdx] |= (1 << (7 - bitCount));
|
|
56
|
-
}
|
|
57
|
-
bitCount++;
|
|
58
|
-
if (bitCount === 8) {
|
|
59
|
-
byteIdx++;
|
|
60
|
-
bitCount = 0;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return descriptor;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Super-fast 8-byte (64-bit) dummy descriptor for Protocol V6 compatibility
|
|
68
|
-
* when full descriptors are not required but an object is expected.
|
|
69
|
-
*/
|
|
70
|
-
export function packLSHIntoDescriptor(lsh) {
|
|
71
|
-
const desc = new Uint8Array(8);
|
|
72
|
-
const view = new DataView(desc.buffer);
|
|
73
|
-
view.setUint32(0, lsh[0], true);
|
|
74
|
-
view.setUint32(4, lsh[1], true);
|
|
75
|
-
return desc;
|
|
76
|
-
}
|
|
1
|
+
import{FREAKPOINTS as t}from"../detector/freak.js";const e=new Int32Array(128),n=new Int32Array(64);for(let f=0;f<64;f++)n[f]=Math.floor(10.5*f);let r=0,o=0;for(let l=0;l<t.length;l++)for(let c=l+1;c<t.length;c++)o<64&&r===n[o]&&(e[2*o]=l,e[2*o+1]=c,o++),r++;export function computeLSH64(t){const n=new Uint32Array(2);for(let r=0;r<64;r++){const o=e[2*r],f=e[2*r+1];if(t[o]<t[f]){const t=31&r;n[r>>5]|=1<<t}}return n}export function computeFullFREAK(e){const n=new Uint8Array(84);let r=0,o=0;for(let f=0;f<t.length;f++)for(let l=f+1;l<t.length;l++)e[f]<e[l]&&(n[o]|=1<<7-r),r++,8===r&&(o++,r=0);return n}export function packLSHIntoDescriptor(t){const e=new Uint8Array(8),n=new DataView(e.buffer);return n.setUint32(0,t[0],!0),n.setUint32(4,t[1],!0),e}
|
|
@@ -1,51 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* 📐 AR Projection Utilities
|
|
3
|
-
* Common logic for projecting 3D marker-space points to 2D screen CSS pixels.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Projects a 3D marker-space point (x, y, z) into 2D screen coordinates.
|
|
7
|
-
*
|
|
8
|
-
* @param {number} x - Marker X coordinate
|
|
9
|
-
* @param {number} y - Marker Y coordinate
|
|
10
|
-
* @param {number} z - Marker Z coordinate (height from surface)
|
|
11
|
-
* @param {number[][]} mVT - ModelViewTransform matrix (3x4)
|
|
12
|
-
* @param {number[][]} proj - Projection matrix (3x3)
|
|
13
|
-
* @param {number} videoW - Internal video width
|
|
14
|
-
* @param {number} videoH - Internal video height
|
|
15
|
-
* @param {Object} containerRect - {width, height} of the display container
|
|
16
|
-
* @param {boolean} needsRotation - Whether the feed needs 90deg rotation (e.g. portrait mobile)
|
|
17
|
-
* @returns {{sx: number, sy: number}} Screen coordinates [X, Y]
|
|
18
|
-
*/
|
|
19
|
-
export function projectToScreen(x, y, z, mVT, proj, videoW, videoH, containerRect, needsRotation = false) {
|
|
20
|
-
// 1. Marker Space -> Camera Space (3D)
|
|
21
|
-
const tx = mVT[0][0] * x + mVT[0][1] * y + mVT[0][2] * z + mVT[0][3];
|
|
22
|
-
const ty = mVT[1][0] * x + mVT[1][1] * y + mVT[1][2] * z + mVT[1][3];
|
|
23
|
-
const tz = mVT[2][0] * x + mVT[2][1] * y + mVT[2][2] * z + mVT[2][3];
|
|
24
|
-
// 2. Camera Space -> Buffer Pixels (2D)
|
|
25
|
-
// Using intrinsic projection from controller
|
|
26
|
-
const bx = (proj[0][0] * tx / tz) + proj[0][2];
|
|
27
|
-
const by = (proj[1][1] * ty / tz) + proj[1][2];
|
|
28
|
-
// 3. Buffer Pixels -> Screen CSS Pixels
|
|
29
|
-
const vW = needsRotation ? videoH : videoW;
|
|
30
|
-
const vH = needsRotation ? videoW : videoH;
|
|
31
|
-
// Calculate how the video is scaled to cover (object-fit: cover) the container
|
|
32
|
-
const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
|
|
33
|
-
const displayW = vW * perspectiveScale;
|
|
34
|
-
const displayH = vH * perspectiveScale;
|
|
35
|
-
// Centering offsets
|
|
36
|
-
const offsetX = (containerRect.width - displayW) / 2;
|
|
37
|
-
const offsetY = (containerRect.height - displayH) / 2;
|
|
38
|
-
let sx, sy;
|
|
39
|
-
if (needsRotation) {
|
|
40
|
-
// Rotation Mapping:
|
|
41
|
-
// Camera +X (Right) -> Screen +Y (Down)
|
|
42
|
-
// Camera +Y (Down) -> Screen -X (Left)
|
|
43
|
-
sx = offsetX + (displayW / 2) - (by - proj[1][2]) * perspectiveScale;
|
|
44
|
-
sy = offsetY + (displayH / 2) + (bx - proj[0][2]) * perspectiveScale;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
sx = offsetX + (displayW / 2) + (bx - proj[0][2]) * perspectiveScale;
|
|
48
|
-
sy = offsetY + (displayH / 2) + (by - proj[1][2]) * perspectiveScale;
|
|
49
|
-
}
|
|
50
|
-
return { sx, sy };
|
|
51
|
-
}
|
|
1
|
+
export function projectToScreen(t,e,h,i,n,o,r,c,s=!1){const x=i[0][0]*t+i[0][1]*e+i[0][2]*h+i[0][3],a=i[1][0]*t+i[1][1]*e+i[1][2]*h+i[1][3],d=i[2][0]*t+i[2][1]*e+i[2][2]*h+i[2][3],g=n[0][0]*x/d+n[0][2],p=n[1][1]*a/d+n[1][2],u=s?r:o,w=s?o:r,f=Math.max(c.width/u,c.height/w),j=u*f,l=w*f,m=(c.width-j)/2,y=(c.height-l)/2;let M,S;return s?(M=m+j/2-(p-n[1][2])*f,S=y+l/2+(g-n[0][2])*f):(M=m+j/2+(g-n[0][2])*f,S=y+l/2+(p-n[1][2])*f),{sx:M,sy:S}}
|
|
@@ -1,25 +1 @@
|
|
|
1
|
-
const
|
|
2
|
-
const createRandomizer = () => {
|
|
3
|
-
const randomizer = {
|
|
4
|
-
seed: mRandSeed,
|
|
5
|
-
arrayShuffle(options) {
|
|
6
|
-
const { arr, sampleSize } = options;
|
|
7
|
-
for (let i = 0; i < sampleSize; i++) {
|
|
8
|
-
this.seed = (214013 * this.seed + 2531011) % (1 << 31);
|
|
9
|
-
let k = (this.seed >> 16) & 0x7fff;
|
|
10
|
-
k = k % arr.length;
|
|
11
|
-
let tmp = arr[i];
|
|
12
|
-
arr[i] = arr[k];
|
|
13
|
-
arr[k] = tmp;
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
nextInt(maxValue) {
|
|
17
|
-
this.seed = (214013 * this.seed + 2531011) % (1 << 31);
|
|
18
|
-
let k = (this.seed >> 16) & 0x7fff;
|
|
19
|
-
k = k % maxValue;
|
|
20
|
-
return k;
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
return randomizer;
|
|
24
|
-
};
|
|
25
|
-
export { createRandomizer };
|
|
1
|
+
const e=()=>({seed:1234,arrayShuffle(e){const{arr:t,sampleSize:s}=e;for(let e=0;e<s;e++){this.seed=(214013*this.seed+2531011)%(1<<31);let s=this.seed>>16&32767;s%=t.length;let h=t[e];t[e]=t[s],t[s]=h}},nextInt(e){this.seed=(214013*this.seed+2531011)%(1<<31);let t=this.seed>>16&32767;return t%=e,t}});export{e as createRandomizer};
|
|
@@ -1,89 +1 @@
|
|
|
1
|
-
export class WorkerPool {
|
|
2
|
-
constructor(workerPath, poolSize, WorkerClass) {
|
|
3
|
-
this.workerPath = workerPath;
|
|
4
|
-
this.poolSize = poolSize;
|
|
5
|
-
this.WorkerClass = WorkerClass;
|
|
6
|
-
this.workers = [];
|
|
7
|
-
this.queue = [];
|
|
8
|
-
this.activeWorkers = 0;
|
|
9
|
-
}
|
|
10
|
-
runTask(taskData) {
|
|
11
|
-
return new Promise((resolve, reject) => {
|
|
12
|
-
const task = { taskData, resolve, reject };
|
|
13
|
-
if (this.workers.length > 0) {
|
|
14
|
-
this._executeTask(this.workers.pop(), task);
|
|
15
|
-
}
|
|
16
|
-
else if (this.activeWorkers < this.poolSize) {
|
|
17
|
-
this._executeTask(this._createWorker(), task);
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
this.queue.push(task);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
_createWorker() {
|
|
25
|
-
this.activeWorkers++;
|
|
26
|
-
const worker = new this.WorkerClass(this.workerPath);
|
|
27
|
-
return worker;
|
|
28
|
-
}
|
|
29
|
-
_executeTask(worker, task) {
|
|
30
|
-
const onMessage = (msg) => {
|
|
31
|
-
if (msg.type === 'progress' && task.taskData.onProgress) {
|
|
32
|
-
task.taskData.onProgress(msg.percent);
|
|
33
|
-
}
|
|
34
|
-
else if (msg.type === 'compileDone') {
|
|
35
|
-
cleanup();
|
|
36
|
-
// If it's the new unified result, return both.
|
|
37
|
-
if (msg.matchingData && msg.trackingData) {
|
|
38
|
-
this._finishTask(worker, task.resolve, {
|
|
39
|
-
matchingData: msg.matchingData,
|
|
40
|
-
trackingData: msg.trackingData
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
this._finishTask(worker, task.resolve, msg.trackingData);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
else if (msg.type === 'matchDone') {
|
|
48
|
-
cleanup();
|
|
49
|
-
this._finishTask(worker, task.resolve, msg.matchingData);
|
|
50
|
-
}
|
|
51
|
-
else if (msg.type === 'error') {
|
|
52
|
-
cleanup();
|
|
53
|
-
this._finishTask(worker, task.reject, new Error(msg.error));
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
const onError = (err) => {
|
|
57
|
-
cleanup();
|
|
58
|
-
this._finishTask(worker, task.reject, err);
|
|
59
|
-
};
|
|
60
|
-
const cleanup = () => {
|
|
61
|
-
worker.removeListener('message', onMessage);
|
|
62
|
-
worker.removeListener('error', onError);
|
|
63
|
-
};
|
|
64
|
-
worker.on('message', onMessage);
|
|
65
|
-
worker.on('error', onError);
|
|
66
|
-
// Create a copy of taskData without functions for the worker
|
|
67
|
-
const serializableData = {};
|
|
68
|
-
for (const [key, value] of Object.entries(task.taskData)) {
|
|
69
|
-
if (typeof value !== 'function') {
|
|
70
|
-
serializableData[key] = value;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
worker.postMessage(serializableData);
|
|
74
|
-
}
|
|
75
|
-
_finishTask(worker, callback, result) {
|
|
76
|
-
if (this.queue.length > 0) {
|
|
77
|
-
this._executeTask(worker, this.queue.shift());
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
this.workers.push(worker);
|
|
81
|
-
}
|
|
82
|
-
callback(result);
|
|
83
|
-
}
|
|
84
|
-
async destroy() {
|
|
85
|
-
await Promise.all(this.workers.map(w => w.terminate()));
|
|
86
|
-
this.workers = [];
|
|
87
|
-
this.activeWorkers = 0;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
1
|
+
export class WorkerPool{constructor(e,s,t){this.workerPath=e,this.poolSize=s,this.WorkerClass=t,this.workers=[],this.queue=[],this.activeWorkers=0}runTask(e){return new Promise((s,t)=>{const r={taskData:e,resolve:s,reject:t};this.workers.length>0?this._executeTask(this.workers.pop(),r):this.activeWorkers<this.poolSize?this._executeTask(this._createWorker(),r):this.queue.push(r)})}_createWorker(){return this.activeWorkers++,new this.WorkerClass(this.workerPath)}_executeTask(e,s){const t=t=>{"progress"===t.type&&s.taskData.onProgress?s.taskData.onProgress(t.percent):"compileDone"===t.type?(a(),t.matchingData&&t.trackingData?this._finishTask(e,s.resolve,{matchingData:t.matchingData,trackingData:t.trackingData}):this._finishTask(e,s.resolve,t.trackingData)):"matchDone"===t.type?(a(),this._finishTask(e,s.resolve,t.matchingData)):"error"===t.type&&(a(),this._finishTask(e,s.reject,new Error(t.error)))},r=t=>{a(),this._finishTask(e,s.reject,t)},a=()=>{e.removeListener("message",t),e.removeListener("error",r)};e.on("message",t),e.on("error",r);const i={};for(const[e,t]of Object.entries(s.taskData))"function"!=typeof t&&(i[e]=t);e.postMessage(i)}_finishTask(e,s,t){this.queue.length>0?this._executeTask(e,this.queue.shift()):this.workers.push(e),s(t)}async destroy(){await Promise.all(this.workers.map(e=>e.terminate())),this.workers=[],this.activeWorkers=0}}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from "./react/TaptappAR.js";
|
|
3
|
-
export * from "./react/use-ar.js";
|
|
4
|
-
export * from "./compiler/offline-compiler.js";
|
|
5
|
-
export { Controller } from "./runtime/controller.js";
|
|
6
|
-
export { createTracker, startTracking } from "./runtime/track.js";
|
|
7
|
-
export * as protocol from "./core/protocol.js";
|
|
1
|
+
export*from"./react/types.js";export*from"./react/TaptappAR.js";export*from"./react/use-ar.js";export*from"./compiler/offline-compiler.js";export{Controller}from"./runtime/controller.js";export{createTracker,startTracking}from"./runtime/track.js";export*as protocol from"./core/protocol.js";
|
|
@@ -1,70 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Author: Gery Casiez
|
|
3
|
-
* Reference: http://www.lifl.fr/~casiez/1euro/
|
|
4
|
-
*/
|
|
5
|
-
class LowPassFilter {
|
|
6
|
-
constructor(alpha, initval = 0) {
|
|
7
|
-
this.y = initval;
|
|
8
|
-
this.s = initval;
|
|
9
|
-
this.alpha = alpha;
|
|
10
|
-
}
|
|
11
|
-
setAlpha(alpha) {
|
|
12
|
-
if (alpha <= 0 || alpha > 1) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
this.alpha = alpha;
|
|
16
|
-
}
|
|
17
|
-
filter(value) {
|
|
18
|
-
this.y = value;
|
|
19
|
-
this.s = this.alpha * value + (1.0 - this.alpha) * this.s;
|
|
20
|
-
return this.s;
|
|
21
|
-
}
|
|
22
|
-
filterWithAlpha(value, alpha) {
|
|
23
|
-
this.setAlpha(alpha);
|
|
24
|
-
return this.filter(value);
|
|
25
|
-
}
|
|
26
|
-
lastValue() {
|
|
27
|
-
return this.y;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
export class OneEuroFilter {
|
|
31
|
-
constructor({ minCutOff = 1.0, beta = 0.0, dCutOff = 1.0 }) {
|
|
32
|
-
this.minCutOff = minCutOff;
|
|
33
|
-
this.beta = beta;
|
|
34
|
-
this.dCutOff = dCutOff;
|
|
35
|
-
this.x = null;
|
|
36
|
-
this.dx = null;
|
|
37
|
-
this.lastTime = null;
|
|
38
|
-
}
|
|
39
|
-
_alpha(cutoff, te) {
|
|
40
|
-
const tau = 1.0 / (2 * Math.PI * cutoff);
|
|
41
|
-
return 1.0 / (1.0 + tau / te);
|
|
42
|
-
}
|
|
43
|
-
reset() {
|
|
44
|
-
this.lastTime = null;
|
|
45
|
-
this.x = null;
|
|
46
|
-
this.dx = null;
|
|
47
|
-
}
|
|
48
|
-
filter(time, value) {
|
|
49
|
-
if (this.lastTime === null || this.x === null) {
|
|
50
|
-
this.lastTime = time;
|
|
51
|
-
this.x = value.map((v) => new LowPassFilter(this._alpha(this.minCutOff, 1.0), v));
|
|
52
|
-
this.dx = value.map((v) => new LowPassFilter(this._alpha(this.dCutOff, 1.0), 0));
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
const te = (time - this.lastTime) / 1000.0;
|
|
56
|
-
if (te <= 0)
|
|
57
|
-
return value;
|
|
58
|
-
this.lastTime = time;
|
|
59
|
-
const filteredValue = [];
|
|
60
|
-
for (let i = 0; i < value.length; i++) {
|
|
61
|
-
const edvalue = (value[i] - this.x[i].lastValue()) / te;
|
|
62
|
-
const alpha_d = this._alpha(this.dCutOff, te);
|
|
63
|
-
const edvalue_filtered = this.dx[i].filterWithAlpha(edvalue, alpha_d);
|
|
64
|
-
const cutoff = this.minCutOff + this.beta * Math.abs(edvalue_filtered);
|
|
65
|
-
const alpha = this._alpha(cutoff, te);
|
|
66
|
-
filteredValue[i] = this.x[i].filterWithAlpha(value[i], alpha);
|
|
67
|
-
}
|
|
68
|
-
return filteredValue;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
1
|
+
class t{constructor(t,s=0){this.y=s,this.s=s,this.alpha=t}setAlpha(t){t<=0||t>1||(this.alpha=t)}filter(t){return this.y=t,this.s=this.alpha*t+(1-this.alpha)*this.s,this.s}filterWithAlpha(t,s){return this.setAlpha(s),this.filter(t)}lastValue(){return this.y}}export class OneEuroFilter{constructor({minCutOff:t=1,beta:s=0,dCutOff:i=1}){this.minCutOff=t,this.beta=s,this.dCutOff=i,this.x=null,this.dx=null,this.lastTime=null}_alpha(t,s){return 1/(1+1/(2*Math.PI*t)/s)}reset(){this.lastTime=null,this.x=null,this.dx=null}filter(s,i){if(null===this.lastTime||null===this.x)return this.lastTime=s,this.x=i.map(s=>new t(this._alpha(this.minCutOff,1),s)),this.dx=i.map(s=>new t(this._alpha(this.dCutOff,1),0)),i;const h=(s-this.lastTime)/1e3;if(h<=0)return i;this.lastTime=s;const l=[];for(let t=0;t<i.length;t++){const s=(i[t]-this.x[t].lastValue())/h,a=this._alpha(this.dCutOff,h),e=this.dx[t].filterWithAlpha(s,a),r=this.minCutOff+this.beta*Math.abs(e),n=this._alpha(r,h);l[t]=this.x[t].filterWithAlpha(i[t],n)}return l}}
|
package/dist/react/TaptappAR.js
CHANGED
|
@@ -1,151 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { useMemo } from "react";
|
|
3
|
-
import { useAR } from "./use-ar.js";
|
|
4
|
-
export const TaptappAR = ({ config, className = "", showScanningOverlay = true, showErrorOverlay = true }) => {
|
|
5
|
-
const { containerRef, overlayRef, status, toggleVideo, trackedPoints, error } = useAR(config);
|
|
6
|
-
// Simple heuristic to determine if it's a video or image
|
|
7
|
-
// based on the presence of videoSrc and common extensions
|
|
8
|
-
const isVideo = useMemo(() => {
|
|
9
|
-
if (!config.videoSrc)
|
|
10
|
-
return false;
|
|
11
|
-
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov'];
|
|
12
|
-
const url = config.videoSrc.toLowerCase().split('?')[0];
|
|
13
|
-
return videoExtensions.some(ext => url.endsWith(ext)) || config.videoSrc.includes('video');
|
|
14
|
-
}, [config.videoSrc]);
|
|
15
|
-
return (_jsxs("div", { className: `taptapp-ar-wrapper ${className} ${status}`, style: { position: 'relative', width: '100%', height: '100%', overflow: 'hidden' }, children: [showScanningOverlay && status === "scanning" && (_jsx("div", { className: "taptapp-ar-overlay taptapp-ar-scanning", children: _jsxs("div", { className: "scanning-content", children: [_jsxs("div", { className: "scanning-frame", children: [_jsx("img", { className: "target-preview", src: config.targetImageSrc, alt: "Target", crossOrigin: "anonymous" }), _jsx("div", { className: "scanning-line" })] }), _jsx("p", { className: "scanning-text", children: "Apunta a la imagen para comenzar" })] }) })), status === "compiling" && (_jsx("div", { className: "taptapp-ar-overlay taptapp-ar-compiling", style: { background: 'rgba(0,0,0,0.9)' }, children: _jsxs("div", { className: "scanning-content", children: [_jsx("div", { className: "loading-spinner" }), _jsx("p", { className: "scanning-text", style: { marginTop: '20px' }, children: "Preparando motor AR..." }), _jsx("p", { style: { fontSize: '0.8rem', opacity: 0.6 }, children: "Compilando imagen de referencia" })] }) })), showErrorOverlay && status === "error" && (_jsx("div", { className: "taptapp-ar-overlay taptapp-ar-error", children: _jsxs("div", { className: "error-content", children: [_jsx("span", { className: "error-icon", children: "\u26A0\uFE0F" }), _jsx("p", { className: "error-title", children: "No se pudo iniciar AR" }), _jsx("p", { className: "error-text", children: error || "Verifica los permisos de cámara" }), _jsx("button", { className: "retry-btn", onClick: () => window.location.reload(), children: "Reintentar" })] }) })), _jsx("div", { ref: containerRef, className: "taptapp-ar-container", onClick: toggleVideo, style: { width: '100%', height: '100%' }, children: isVideo ? (_jsx("video", { ref: overlayRef, className: "taptapp-ar-overlay-element", src: config.videoSrc, preload: "auto", loop: true, playsInline: true, muted: true, crossOrigin: "anonymous" })) : (_jsx("img", { ref: overlayRef, className: "taptapp-ar-overlay-element", src: config.videoSrc || config.targetImageSrc, crossOrigin: "anonymous", alt: "AR Overlay" })) }), trackedPoints.length > 0 && (_jsx("div", { className: "taptapp-ar-points-overlay", style: { opacity: status === "tracking" ? 1 : 0.6 }, children: trackedPoints
|
|
16
|
-
.map((point, i) => {
|
|
17
|
-
// 🚀 Reflex visualization of the engine's new sensitivity
|
|
18
|
-
const isStable = point.stability > 0.3 && point.reliability > 0.2;
|
|
19
|
-
const size = (3 + point.reliability * 8) * (0.7 + point.stability * 0.3);
|
|
20
|
-
return (_jsx("div", { className: `tracking-point ${!isStable ? 'flickering' : ''}`, style: {
|
|
21
|
-
left: `${point.x}px`,
|
|
22
|
-
top: `${point.y}px`,
|
|
23
|
-
width: `${size}px`,
|
|
24
|
-
height: `${size}px`,
|
|
25
|
-
opacity: (0.4 + (point.reliability * 0.5)) * (0.3 + point.stability * 0.7),
|
|
26
|
-
backgroundColor: isStable ? '#00e5ff' : '#ffffff',
|
|
27
|
-
boxShadow: isStable ? '0 0 10px #00e5ff' : '0 0 5px rgba(255,255,255,0.5)',
|
|
28
|
-
border: 'none'
|
|
29
|
-
} }, i));
|
|
30
|
-
}) })), _jsx("style", { children: `
|
|
31
|
-
.taptapp-ar-wrapper {
|
|
32
|
-
background: #000;
|
|
33
|
-
color: white;
|
|
34
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
35
|
-
}
|
|
36
|
-
.taptapp-ar-overlay {
|
|
37
|
-
position: absolute;
|
|
38
|
-
top: 0;
|
|
39
|
-
left: 0;
|
|
40
|
-
right: 0;
|
|
41
|
-
bottom: 0;
|
|
42
|
-
z-index: 20;
|
|
43
|
-
display: flex;
|
|
44
|
-
align-items: center;
|
|
45
|
-
justify-content: center;
|
|
46
|
-
background: rgba(0,0,0,0.7);
|
|
47
|
-
backdrop-filter: blur(4px);
|
|
48
|
-
transition: opacity 0.3s ease;
|
|
49
|
-
}
|
|
50
|
-
.scanning-content, .error-content {
|
|
51
|
-
text-align: center;
|
|
52
|
-
display: flex;
|
|
53
|
-
flex-direction: column;
|
|
54
|
-
align-items: center;
|
|
55
|
-
padding: 20px;
|
|
56
|
-
}
|
|
57
|
-
.scanning-frame {
|
|
58
|
-
position: relative;
|
|
59
|
-
width: 200px;
|
|
60
|
-
height: 200px;
|
|
61
|
-
border: 2px solid rgba(255,255,255,0.3);
|
|
62
|
-
border-radius: 20px;
|
|
63
|
-
overflow: hidden;
|
|
64
|
-
margin-bottom: 20px;
|
|
65
|
-
}
|
|
66
|
-
.target-preview {
|
|
67
|
-
width: 100%;
|
|
68
|
-
height: 100%;
|
|
69
|
-
object-fit: cover;
|
|
70
|
-
opacity: 0.6;
|
|
71
|
-
}
|
|
72
|
-
.scanning-line {
|
|
73
|
-
position: absolute;
|
|
74
|
-
top: 0;
|
|
75
|
-
left: 0;
|
|
76
|
-
width: 100%;
|
|
77
|
-
height: 2px;
|
|
78
|
-
background: #00e5ff;
|
|
79
|
-
box-shadow: 0 0 15px #00e5ff;
|
|
80
|
-
animation: scan 2s linear infinite;
|
|
81
|
-
}
|
|
82
|
-
@keyframes scan {
|
|
83
|
-
0% { top: 0; }
|
|
84
|
-
50% { top: 100%; }
|
|
85
|
-
100% { top: 0; }
|
|
86
|
-
}
|
|
87
|
-
.scanning-text {
|
|
88
|
-
font-size: 1.1rem;
|
|
89
|
-
font-weight: 500;
|
|
90
|
-
letter-spacing: 0.5px;
|
|
91
|
-
}
|
|
92
|
-
.loading-spinner {
|
|
93
|
-
width: 40px;
|
|
94
|
-
height: 40px;
|
|
95
|
-
border: 3px solid rgba(255,255,255,0.1);
|
|
96
|
-
border-radius: 50%;
|
|
97
|
-
border-top-color: #00e5ff;
|
|
98
|
-
animation: spin 1s ease-in-out infinite;
|
|
99
|
-
}
|
|
100
|
-
@keyframes spin {
|
|
101
|
-
to { transform: rotate(360deg); }
|
|
102
|
-
}
|
|
103
|
-
.error-icon { font-size: 3rem; margin-bottom: 10px; }
|
|
104
|
-
.error-title { font-size: 1.2rem; font-weight: bold; margin: 0; }
|
|
105
|
-
.error-text { opacity: 0.8; margin: 5px 0 20px; }
|
|
106
|
-
.retry-btn {
|
|
107
|
-
padding: 10px 25px;
|
|
108
|
-
border-radius: 30px;
|
|
109
|
-
border: none;
|
|
110
|
-
background: #fff;
|
|
111
|
-
color: #000;
|
|
112
|
-
font-weight: 600;
|
|
113
|
-
cursor: pointer;
|
|
114
|
-
transition: transform 0.2s;
|
|
115
|
-
}
|
|
116
|
-
.retry-btn:active { transform: scale(0.95); }
|
|
117
|
-
.taptapp-ar-overlay-element {
|
|
118
|
-
display: none; /* Controlled by tracker */
|
|
119
|
-
position: absolute;
|
|
120
|
-
top: 0;
|
|
121
|
-
left: 0;
|
|
122
|
-
width: auto;
|
|
123
|
-
height: auto;
|
|
124
|
-
pointer-events: none;
|
|
125
|
-
z-index: 10;
|
|
126
|
-
/* Will be positioned via matrix3d by track.ts */
|
|
127
|
-
}
|
|
128
|
-
.taptapp-ar-points-overlay {
|
|
129
|
-
position: absolute;
|
|
130
|
-
top: 0;
|
|
131
|
-
left: 0;
|
|
132
|
-
width: 100%;
|
|
133
|
-
height: 100%;
|
|
134
|
-
pointer-events: none;
|
|
135
|
-
z-index: 100; /* High z-index to be above overlay */
|
|
136
|
-
}
|
|
137
|
-
.tracking-point {
|
|
138
|
-
position: absolute;
|
|
139
|
-
background: black;
|
|
140
|
-
border: 1px solid rgba(255,255,255,0.5);
|
|
141
|
-
border-radius: 50%;
|
|
142
|
-
transform: translate(-50%, -50%);
|
|
143
|
-
box-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
|
|
144
|
-
pointer-events: none;
|
|
145
|
-
transition: background-color 0.3s ease, box-shadow 0.3s ease, width 0.2s ease, height 0.2s ease, opacity 0.2s ease;
|
|
146
|
-
}
|
|
147
|
-
.tracking-point.flickering {
|
|
148
|
-
z-index: 101;
|
|
149
|
-
}
|
|
150
|
-
` })] }));
|
|
151
|
-
};
|
|
1
|
+
import{jsx as n,jsxs as e}from"react/jsx-runtime";import{useMemo as a}from"react";import{useAR as r}from"./use-ar.js";export const TaptappAR=({config:t,className:i="",showScanningOverlay:o=!0,showErrorOverlay:s=!0})=>{const{containerRef:p,overlayRef:l,status:c,toggleVideo:d,trackedPoints:g,error:m}=r(t),f=a(()=>{if(!t.videoSrc)return!1;const n=t.videoSrc.toLowerCase().split("?")[0];return[".mp4",".webm",".ogg",".mov"].some(e=>n.endsWith(e))||t.videoSrc.includes("video")},[t.videoSrc]);return e("div",{className:`taptapp-ar-wrapper ${i} ${c}`,style:{position:"relative",width:"100%",height:"100%",overflow:"hidden"},children:[o&&"scanning"===c&&n("div",{className:"taptapp-ar-overlay taptapp-ar-scanning",children:e("div",{className:"scanning-content",children:[e("div",{className:"scanning-frame",children:[n("img",{className:"target-preview",src:t.targetImageSrc,alt:"Target",crossOrigin:"anonymous"}),n("div",{className:"scanning-line"})]}),n("p",{className:"scanning-text",children:"Apunta a la imagen para comenzar"})]})}),"compiling"===c&&n("div",{className:"taptapp-ar-overlay taptapp-ar-compiling",style:{background:"rgba(0,0,0,0.9)"},children:e("div",{className:"scanning-content",children:[n("div",{className:"loading-spinner"}),n("p",{className:"scanning-text",style:{marginTop:"20px"},children:"Preparando motor AR..."}),n("p",{style:{fontSize:"0.8rem",opacity:.6},children:"Compilando imagen de referencia"})]})}),s&&"error"===c&&n("div",{className:"taptapp-ar-overlay taptapp-ar-error",children:e("div",{className:"error-content",children:[n("span",{className:"error-icon",children:"⚠️"}),n("p",{className:"error-title",children:"No se pudo iniciar AR"}),n("p",{className:"error-text",children:m||"Verifica los permisos de cámara"}),n("button",{className:"retry-btn",onClick:()=>window.location.reload(),children:"Reintentar"})]})}),n("div",{ref:p,className:"taptapp-ar-container",onClick:d,style:{width:"100%",height:"100%"},children:f?n("video",{ref:l,className:"taptapp-ar-overlay-element",src:t.videoSrc,preload:"auto",loop:!0,playsInline:!0,muted:!0,crossOrigin:"anonymous"}):n("img",{ref:l,className:"taptapp-ar-overlay-element",src:t.videoSrc||t.targetImageSrc,crossOrigin:"anonymous",alt:"AR Overlay"})}),g.length>0&&n("div",{className:"taptapp-ar-points-overlay",style:{opacity:"tracking"===c?1:.6},children:g.map((e,a)=>{const r=e.stability>.3&&e.reliability>.2,t=(3+8*e.reliability)*(.7+.3*e.stability);return n("div",{className:"tracking-point "+(r?"":"flickering"),style:{left:`${e.x}px`,top:`${e.y}px`,width:`${t}px`,height:`${t}px`,opacity:(.4+.5*e.reliability)*(.3+.7*e.stability),backgroundColor:r?"#00e5ff":"#ffffff",boxShadow:r?"0 0 10px #00e5ff":"0 0 5px rgba(255,255,255,0.5)",border:"none"}},a)})}),n("style",{children:"\n .taptapp-ar-wrapper {\n background: #000;\n color: white;\n font-family: system-ui, -apple-system, sans-serif;\n }\n .taptapp-ar-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 20;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0,0,0,0.7);\n backdrop-filter: blur(4px);\n transition: opacity 0.3s ease;\n }\n .scanning-content, .error-content {\n text-align: center;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 20px;\n }\n .scanning-frame {\n position: relative;\n width: 200px;\n height: 200px;\n border: 2px solid rgba(255,255,255,0.3);\n border-radius: 20px;\n overflow: hidden;\n margin-bottom: 20px;\n }\n .target-preview {\n width: 100%;\n height: 100%;\n object-fit: cover;\n opacity: 0.6;\n }\n .scanning-line {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 2px;\n background: #00e5ff;\n box-shadow: 0 0 15px #00e5ff;\n animation: scan 2s linear infinite;\n }\n @keyframes scan {\n 0% { top: 0; }\n 50% { top: 100%; }\n 100% { top: 0; }\n }\n .scanning-text {\n font-size: 1.1rem;\n font-weight: 500;\n letter-spacing: 0.5px;\n }\n .loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid rgba(255,255,255,0.1);\n border-radius: 50%;\n border-top-color: #00e5ff;\n animation: spin 1s ease-in-out infinite;\n }\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n .error-icon { font-size: 3rem; margin-bottom: 10px; }\n .error-title { font-size: 1.2rem; font-weight: bold; margin: 0; }\n .error-text { opacity: 0.8; margin: 5px 0 20px; }\n .retry-btn {\n padding: 10px 25px;\n border-radius: 30px;\n border: none;\n background: #fff;\n color: #000;\n font-weight: 600;\n cursor: pointer;\n transition: transform 0.2s;\n }\n .retry-btn:active { transform: scale(0.95); }\n .taptapp-ar-overlay-element {\n display: none; /* Controlled by tracker */\n position: absolute;\n top: 0;\n left: 0;\n width: auto;\n height: auto;\n pointer-events: none;\n z-index: 10;\n /* Will be positioned via matrix3d by track.ts */\n }\n .taptapp-ar-points-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n z-index: 100; /* High z-index to be above overlay */\n }\n .tracking-point {\n position: absolute;\n background: black;\n border: 1px solid rgba(255,255,255,0.5);\n border-radius: 50%;\n transform: translate(-50%, -50%);\n box-shadow: 0 0 2px rgba(255, 255, 255, 0.8);\n pointer-events: none;\n transition: background-color 0.3s ease, box-shadow 0.3s ease, width 0.2s ease, height 0.2s ease, opacity 0.2s ease;\n }\n .tracking-point.flickering {\n z-index: 101;\n }\n "})]})};
|