@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.
Files changed (63) hide show
  1. package/dist/compiler/node-worker.js +1 -197
  2. package/dist/compiler/offline-compiler.js +1 -207
  3. package/dist/core/constants.js +1 -38
  4. package/dist/core/detector/crop-detector.js +1 -88
  5. package/dist/core/detector/detector-lite.js +1 -455
  6. package/dist/core/detector/freak.js +1 -89
  7. package/dist/core/estimation/estimate.js +1 -16
  8. package/dist/core/estimation/estimator.js +1 -30
  9. package/dist/core/estimation/morph-refinement.js +1 -116
  10. package/dist/core/estimation/non-rigid-refine.js +1 -70
  11. package/dist/core/estimation/pnp-solver.js +1 -109
  12. package/dist/core/estimation/refine-estimate.js +1 -311
  13. package/dist/core/estimation/utils.js +1 -67
  14. package/dist/core/features/auto-rotation-feature.js +1 -30
  15. package/dist/core/features/crop-detection-feature.js +1 -26
  16. package/dist/core/features/feature-base.js +1 -1
  17. package/dist/core/features/feature-manager.js +1 -55
  18. package/dist/core/features/one-euro-filter-feature.js +1 -44
  19. package/dist/core/features/temporal-filter-feature.js +1 -57
  20. package/dist/core/image-list.js +1 -54
  21. package/dist/core/input-loader.js +1 -87
  22. package/dist/core/matching/hamming-distance.js +1 -66
  23. package/dist/core/matching/hdc.js +1 -102
  24. package/dist/core/matching/hierarchical-clustering.js +1 -130
  25. package/dist/core/matching/hough.js +1 -170
  26. package/dist/core/matching/matcher.js +1 -66
  27. package/dist/core/matching/matching.js +1 -401
  28. package/dist/core/matching/ransacHomography.js +1 -132
  29. package/dist/core/perception/bio-inspired-engine.js +1 -232
  30. package/dist/core/perception/foveal-attention.js +1 -280
  31. package/dist/core/perception/index.js +1 -17
  32. package/dist/core/perception/predictive-coding.js +1 -278
  33. package/dist/core/perception/saccadic-controller.js +1 -269
  34. package/dist/core/perception/saliency-map.js +1 -254
  35. package/dist/core/perception/scale-orchestrator.js +1 -68
  36. package/dist/core/protocol.js +1 -254
  37. package/dist/core/tracker/extract-utils.js +1 -29
  38. package/dist/core/tracker/extract.js +1 -306
  39. package/dist/core/tracker/tracker.js +1 -352
  40. package/dist/core/utils/cumsum.js +1 -37
  41. package/dist/core/utils/delaunay.js +1 -125
  42. package/dist/core/utils/geometry.js +1 -101
  43. package/dist/core/utils/gpu-compute.js +1 -231
  44. package/dist/core/utils/homography.js +1 -138
  45. package/dist/core/utils/images.js +1 -108
  46. package/dist/core/utils/lsh-binarizer.js +1 -37
  47. package/dist/core/utils/lsh-direct.js +1 -76
  48. package/dist/core/utils/projection.js +1 -51
  49. package/dist/core/utils/randomizer.js +1 -25
  50. package/dist/core/utils/worker-pool.js +1 -89
  51. package/dist/index.js +1 -7
  52. package/dist/libs/one-euro-filter.js +1 -70
  53. package/dist/react/TaptappAR.js +1 -151
  54. package/dist/react/types.js +1 -16
  55. package/dist/react/use-ar.js +1 -118
  56. package/dist/runtime/aframe.js +1 -272
  57. package/dist/runtime/bio-inspired-controller.js +1 -358
  58. package/dist/runtime/controller.js +1 -592
  59. package/dist/runtime/controller.worker.js +1 -93
  60. package/dist/runtime/index.js +1 -5
  61. package/dist/runtime/three.js +1 -304
  62. package/dist/runtime/track.js +1 -381
  63. package/package.json +9 -3
@@ -1,108 +1 @@
1
- // artoolkit version. slower. is it necessary?
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 { FREAKPOINTS } from "../detector/freak.js";
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 mRandSeed = 1234;
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 * from "./react/types.js";
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}}
@@ -1,151 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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 "})]})};