@srsergio/taptapp-ar 1.0.95 → 1.0.96

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/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export * from "./react/TaptappAR.js";
3
3
  export * from "./react/use-ar.js";
4
4
  export * from "./compiler/offline-compiler.js";
5
5
  export { Controller } from "./runtime/controller.js";
6
- export { SimpleAR } from "./runtime/simple-ar.js";
6
+ export { createTracker, startTracking } from "./runtime/track.js";
7
7
  export * as protocol from "./core/protocol.js";
package/dist/index.js CHANGED
@@ -3,5 +3,5 @@ export * from "./react/TaptappAR.js";
3
3
  export * from "./react/use-ar.js";
4
4
  export * from "./compiler/offline-compiler.js";
5
5
  export { Controller } from "./runtime/controller.js";
6
- export { SimpleAR } from "./runtime/simple-ar.js";
6
+ export { createTracker, startTracking } from "./runtime/track.js";
7
7
  export * as protocol from "./core/protocol.js";
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
3
  import { useAR } from "./use-ar.js";
4
4
  export const TaptappAR = ({ config, className = "", showScanningOverlay = true, showErrorOverlay = true }) => {
5
- const { containerRef, overlayRef, status, toggleVideo, trackedPoints } = useAR(config);
5
+ const { containerRef, overlayRef, status, toggleVideo, trackedPoints, error } = useAR(config);
6
6
  // Simple heuristic to determine if it's a video or image
7
7
  // based on the presence of videoSrc and common extensions
8
8
  const isVideo = useMemo(() => {
@@ -12,7 +12,7 @@ export const TaptappAR = ({ config, className = "", showScanningOverlay = true,
12
12
  const url = config.videoSrc.toLowerCase().split('?')[0];
13
13
  return videoExtensions.some(ext => url.endsWith(ext)) || config.videoSrc.includes('video');
14
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" })] }) })), 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: "Verifica los permisos de c\u00E1mara" }), _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
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
16
  .map((point, i) => {
17
17
  // 🚀 Reflex visualization of the engine's new sensitivity
18
18
  const isStable = point.stability > 0.3 && point.reliability > 0.2;
@@ -89,6 +89,17 @@ export const TaptappAR = ({ config, className = "", showScanningOverlay = true,
89
89
  font-weight: 500;
90
90
  letter-spacing: 0.5px;
91
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
+ }
92
103
  .error-icon { font-size: 3rem; margin-bottom: 10px; }
93
104
  .error-title { font-size: 1.2rem; font-weight: bold; margin: 0; }
94
105
  .error-text { opacity: 0.8; margin: 5px 0 20px; }
@@ -104,12 +115,15 @@ export const TaptappAR = ({ config, className = "", showScanningOverlay = true,
104
115
  }
105
116
  .retry-btn:active { transform: scale(0.95); }
106
117
  .taptapp-ar-overlay-element {
107
- display: block;
108
- width: 100%;
118
+ display: none; /* Controlled by tracker */
119
+ position: absolute;
120
+ top: 0;
121
+ left: 0;
122
+ width: auto;
109
123
  height: auto;
110
- opacity: 0;
111
124
  pointer-events: none;
112
- transition: opacity 0.3s ease;
125
+ z-index: 10;
126
+ /* Will be positioned via matrix3d by track.ts */
113
127
  }
114
128
  .taptapp-ar-points-overlay {
115
129
  position: absolute;
@@ -1,7 +1,7 @@
1
1
  export interface ARConfig {
2
2
  cardId: string;
3
3
  targetImageSrc: string;
4
- targetTaarSrc: string;
4
+ targetTaarSrc?: string;
5
5
  videoSrc: string;
6
6
  videoWidth: number;
7
7
  videoHeight: number;
@@ -1,5 +1,5 @@
1
1
  import type { ARConfig } from "./types.js";
2
- export type ARStatus = "scanning" | "tracking" | "error";
2
+ export type ARStatus = "compiling" | "scanning" | "tracking" | "error";
3
3
  export interface TrackedPoint {
4
4
  x: number;
5
5
  y: number;
@@ -13,5 +13,6 @@ export interface UseARReturn {
13
13
  isPlaying: boolean;
14
14
  toggleVideo: () => Promise<void>;
15
15
  trackedPoints: TrackedPoint[];
16
+ error: string | null;
16
17
  }
17
18
  export declare const useAR: (config: ARConfig) => UseARReturn;
@@ -5,6 +5,7 @@ export const useAR = (config) => {
5
5
  const [status, setStatus] = useState("scanning");
6
6
  const [isPlaying, setIsPlaying] = useState(false);
7
7
  const [trackedPoints, setTrackedPoints] = useState([]);
8
+ const [error, setError] = useState(null);
8
9
  const arInstanceRef = useRef(null);
9
10
  const toggleVideo = useCallback(async () => {
10
11
  const overlay = overlayRef.current;
@@ -31,65 +32,70 @@ export const useAR = (config) => {
31
32
  const initAR = async () => {
32
33
  try {
33
34
  // Safe hybrid import for SSR + Speed
34
- const { SimpleAR } = await import("../runtime/simple-ar.js");
35
+ const { createTracker } = await import("../runtime/track.js");
35
36
  if (!isMounted)
36
37
  return;
37
- const instance = new SimpleAR({
38
+ setStatus("compiling");
39
+ const instance = await createTracker({
38
40
  container: containerRef.current,
39
- targetSrc: config.targetTaarSrc,
41
+ targetSrc: config.targetTaarSrc || config.targetImageSrc,
40
42
  overlay: overlayRef.current,
41
43
  scale: config.scale,
42
- debug: false,
43
- onUpdate: (data) => {
44
- const { screenCoords, reliabilities, stabilities } = data;
45
- if (screenCoords && reliabilities && stabilities) {
46
- const points = screenCoords.map((p, i) => ({
47
- x: p.x,
48
- y: p.y,
49
- reliability: reliabilities[i],
50
- stability: stabilities[i]
51
- }));
52
- setTrackedPoints(points);
53
- }
54
- },
55
- onFound: async ({ targetIndex }) => {
56
- console.log(`🎯 Target ${targetIndex} detected!`);
57
- if (!isMounted)
58
- return;
59
- setStatus("tracking");
60
- const overlay = overlayRef.current;
61
- if (overlay instanceof HTMLVideoElement) {
62
- try {
63
- await overlay.play();
64
- setIsPlaying(true);
44
+ debugMode: false,
45
+ callbacks: {
46
+ onUpdate: (data) => {
47
+ const { screenCoords, reliabilities, stabilities } = data;
48
+ if (screenCoords && reliabilities && stabilities) {
49
+ const points = screenCoords.map((p, i) => ({
50
+ x: p.x,
51
+ y: p.y,
52
+ reliability: reliabilities[i],
53
+ stability: stabilities[i]
54
+ }));
55
+ setTrackedPoints(points);
65
56
  }
66
- catch (err) {
67
- console.warn("Auto-play blocked:", err);
57
+ },
58
+ onFound: async ({ targetIndex }) => {
59
+ console.log(`🎯 Target ${targetIndex} detected!`);
60
+ if (!isMounted)
61
+ return;
62
+ setStatus("tracking");
63
+ const overlay = overlayRef.current;
64
+ if (overlay instanceof HTMLVideoElement) {
65
+ try {
66
+ await overlay.play();
67
+ setIsPlaying(true);
68
+ }
69
+ catch (err) {
70
+ console.warn("Auto-play blocked:", err);
71
+ }
72
+ }
73
+ },
74
+ onLost: ({ targetIndex }) => {
75
+ console.log(`👋 Target ${targetIndex} lost`);
76
+ if (!isMounted)
77
+ return;
78
+ setStatus("scanning");
79
+ setTrackedPoints([]);
80
+ const overlay = overlayRef.current;
81
+ if (overlay instanceof HTMLVideoElement) {
82
+ overlay.pause();
83
+ setIsPlaying(false);
68
84
  }
69
- }
70
- },
71
- onLost: ({ targetIndex }) => {
72
- console.log(`👋 Target ${targetIndex} lost`);
73
- if (!isMounted)
74
- return;
75
- setStatus("scanning");
76
- setTrackedPoints([]);
77
- const overlay = overlayRef.current;
78
- if (overlay instanceof HTMLVideoElement) {
79
- overlay.pause();
80
- setIsPlaying(false);
81
85
  }
82
86
  }
83
87
  });
84
88
  arInstanceRef.current = instance;
85
- await instance.start();
89
+ await instance.startCamera();
86
90
  if (isMounted)
87
91
  setStatus("scanning");
88
92
  }
89
93
  catch (err) {
90
- console.error("Failed to initialize AR:", err);
91
- if (isMounted)
94
+ console.error(" [TapTapp AR] Error durante la inicialización:", err);
95
+ if (isMounted) {
96
+ setError(err.message || String(err));
92
97
  setStatus("error");
98
+ }
93
99
  }
94
100
  };
95
101
  initAR();
@@ -98,13 +104,14 @@ export const useAR = (config) => {
98
104
  arInstanceRef.current?.stop();
99
105
  arInstanceRef.current = null;
100
106
  };
101
- }, [config.targetTaarSrc, config.scale]);
107
+ }, [config.targetTaarSrc, config.targetImageSrc, config.scale]);
102
108
  return {
103
109
  containerRef,
104
110
  overlayRef,
105
111
  status,
106
112
  isPlaying,
107
113
  toggleVideo,
108
- trackedPoints
114
+ trackedPoints,
115
+ error
109
116
  };
110
117
  };
@@ -1,4 +1,5 @@
1
1
  export * from "./controller.js";
2
- export * from "./simple-ar.js";
2
+ export * from "./bio-inspired-controller.js";
3
+ export * from "./track.js";
3
4
  export * from "./three.js";
4
5
  export * from "./aframe.js";
@@ -1,4 +1,5 @@
1
1
  export * from "./controller.js";
2
- export * from "./simple-ar.js";
2
+ export * from "./bio-inspired-controller.js";
3
+ export * from "./track.js";
3
4
  export * from "./three.js";
4
5
  export * from "./aframe.js";
@@ -0,0 +1,170 @@
1
+ /**
2
+ * TapTapp AR - Easy Tracking Configuration
3
+ *
4
+ * Simple API for configuring image target tracking with minimal setup.
5
+ * Based on the reliable configuration from reliability-test.html.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { createTracker } from 'taptapp-ar';
10
+ *
11
+ * const tracker = await createTracker({
12
+ * targetSrc: './my-target.png',
13
+ * container: document.getElementById('ar-container')!,
14
+ * overlay: document.getElementById('overlay')!,
15
+ * callbacks: {
16
+ * onFound: () => console.log('Target found!'),
17
+ * onLost: () => console.log('Target lost'),
18
+ * onUpdate: (data) => console.log('Update:', data)
19
+ * }
20
+ * });
21
+ *
22
+ * // Start tracking from camera
23
+ * tracker.startCamera();
24
+ *
25
+ * // Or track from a video/canvas element
26
+ * tracker.startVideo(videoElement);
27
+ *
28
+ * // Stop tracking
29
+ * tracker.stop();
30
+ * ```
31
+ */
32
+ import { BioInspiredController } from './bio-inspired-controller.js';
33
+ /**
34
+ * Tracking update data passed to onUpdate callback
35
+ */
36
+ export interface TrackingUpdate {
37
+ /** Whether the target is currently being tracked */
38
+ isTracking: boolean;
39
+ /** 4x4 world transformation matrix (column-major, for WebGL/Three.js) */
40
+ worldMatrix: number[] | null;
41
+ /** 3x4 model-view transform matrix */
42
+ modelViewTransform: number[][] | null;
43
+ /** Screen coordinates of tracked feature points */
44
+ screenCoords: Array<{
45
+ x: number;
46
+ y: number;
47
+ id: number;
48
+ }>;
49
+ /** Reliability scores (0-1) for each tracked point */
50
+ reliabilities: number[];
51
+ /** Stability scores (0-1) for each tracked point */
52
+ stabilities: number[];
53
+ /** Average reliability across all points */
54
+ avgReliability: number;
55
+ /** Average stability across all points */
56
+ avgStability: number;
57
+ /** Reference to the controller for advanced usage */
58
+ controller: BioInspiredController;
59
+ /** Index of the tracked target (for multi-target tracking) */
60
+ targetIndex: number;
61
+ /** Target dimensions [width, height] */
62
+ targetDimensions: [number, number];
63
+ }
64
+ /**
65
+ * Tracking event callbacks
66
+ */
67
+ export interface TrackingCallbacks {
68
+ /**
69
+ * Called when the target is first detected
70
+ * @param data Initial tracking data
71
+ */
72
+ onFound?: (data: TrackingUpdate) => void;
73
+ /**
74
+ * Called when tracking is lost
75
+ * @param data Last known tracking data
76
+ */
77
+ onLost?: (data: TrackingUpdate) => void;
78
+ /**
79
+ * Called on every frame update while tracking
80
+ * @param data Current tracking data
81
+ */
82
+ onUpdate?: (data: TrackingUpdate) => void;
83
+ /**
84
+ * Called during target compilation
85
+ * @param progress Progress percentage (0-100)
86
+ */
87
+ onCompileProgress?: (progress: number) => void;
88
+ }
89
+ /**
90
+ * Configuration options for the tracker
91
+ */
92
+ export interface TrackerConfig {
93
+ /**
94
+ * Source of the target image to track.
95
+ * Can be a URL string, HTMLImageElement, ImageData, or ArrayBuffer (pre-compiled .taar)
96
+ */
97
+ targetSrc: string | HTMLImageElement | ImageData | ArrayBuffer;
98
+ /**
99
+ * Container element for the video/canvas display
100
+ */
101
+ container: HTMLElement;
102
+ /**
103
+ * Optional overlay element to position over the tracked target
104
+ */
105
+ overlay?: HTMLElement;
106
+ /**
107
+ * Tracking event callbacks
108
+ */
109
+ callbacks?: TrackingCallbacks;
110
+ /**
111
+ * Camera configuration (MediaStreamConstraints['video'])
112
+ * @default { facingMode: 'environment', width: { ideal: 1280 }, height: { ideal: 960 } }
113
+ */
114
+ cameraConfig?: MediaStreamConstraints['video'];
115
+ /**
116
+ * Viewport width for processing
117
+ * @default 1280
118
+ */
119
+ viewportWidth?: number;
120
+ /**
121
+ * Viewport height for processing
122
+ * @default 960
123
+ */
124
+ viewportHeight?: number;
125
+ /**
126
+ * Enable debug mode for additional logging
127
+ * @default false
128
+ */
129
+ debugMode?: boolean;
130
+ /**
131
+ * Enable bio-inspired perception optimizations
132
+ * @default true
133
+ */
134
+ bioInspiredEnabled?: boolean;
135
+ /**
136
+ * Scale multiplier for the overlay
137
+ * @default 1.0
138
+ */
139
+ scale?: number;
140
+ }
141
+ /**
142
+ * Tracker instance returned by createTracker
143
+ */
144
+ export interface Tracker {
145
+ /** Start tracking from device camera */
146
+ startCamera(): Promise<void>;
147
+ /** Start tracking from a video or canvas element */
148
+ startVideo(source: HTMLVideoElement | HTMLCanvasElement): void;
149
+ /** Stop tracking and release resources */
150
+ stop(): void;
151
+ /** Whether the tracker is currently active */
152
+ readonly isActive: boolean;
153
+ /** Whether a target is currently being tracked */
154
+ readonly isTracking: boolean;
155
+ /** The underlying BioInspiredController instance */
156
+ readonly controller: BioInspiredController;
157
+ /** Target dimensions [width, height] */
158
+ readonly targetDimensions: [number, number];
159
+ /** Get the projection matrix for 3D rendering */
160
+ getProjectionMatrix(): number[];
161
+ }
162
+ /**
163
+ * Create and configure an AR tracker with minimal setup
164
+ */
165
+ export declare function createTracker(config: TrackerConfig): Promise<Tracker>;
166
+ /**
167
+ * Convenience function to create a tracker with camera autostart
168
+ */
169
+ export declare function startTracking(config: TrackerConfig): Promise<Tracker>;
170
+ export default createTracker;