@@ -8,162 +8,238 @@ let parent = null;
88let arController = null ;
99let arControllerInitialized = false ;
1010
11+ // forwarder guard
12+ let getMarkerForwarderAttached = false ;
13+
1114// Detect environment and setup worker communication
1215// Only try to import node:worker_threads if we're in a Node.js environment
1316if ( typeof self === 'undefined' ) {
14- // We're in Node.js (no 'self' global)
15- try {
16- const wt = await import ( 'node:worker_threads' ) . catch ( ( ) => null ) ;
17- if ( wt && wt . parentPort ) {
18- isNodeWorker = true ;
19- parent = wt . parentPort ;
20- }
21- } catch ( e ) {
22- // Fallback: not in worker_threads context
23- isNodeWorker = false ;
24- parent = null ;
17+ // We're in Node.js (no 'self' global)
18+ try {
19+ const wt = await import ( 'node:worker_threads' ) . catch ( ( ) => null ) ;
20+ if ( wt && wt . parentPort ) {
21+ isNodeWorker = true ;
22+ parent = wt . parentPort ;
2523 }
24+ } catch ( e ) {
25+ // Fallback: not in worker_threads context
26+ isNodeWorker = false ;
27+ parent = null ;
28+ }
2629}
2730
2831function onMessage ( fn ) {
29- if ( isNodeWorker ) {
30- parent . on ( 'message' , ( msg ) => fn ( msg ) ) ;
31- } else {
32- self . addEventListener ( 'message' , ( ev ) => fn ( ev . data ) ) ;
33- }
32+ if ( isNodeWorker ) {
33+ parent . on ( 'message' , ( msg ) => fn ( msg ) ) ;
34+ } else {
35+ self . addEventListener ( 'message' , ( ev ) => fn ( ev . data ) ) ;
36+ }
3437}
3538
3639function sendMessage ( msg ) {
37- if ( isNodeWorker ) {
38- parent . postMessage ( msg ) ;
39- } else {
40- self . postMessage ( msg ) ;
41- }
40+ if ( isNodeWorker ) {
41+ parent . postMessage ( msg ) ;
42+ } else {
43+ self . postMessage ( msg ) ;
44+ }
45+ }
46+
47+ // Serialize AR.js-style getMarker event into a transferable payload
48+ function serializeGetMarkerEvent ( ev ) {
49+ try {
50+ const data = ev ?. data || { } ;
51+ const marker = data . marker || { } ;
52+ const matrix = Array . isArray ( data . matrix ) ? data . matrix . slice ( 0 , 16 )
53+ : ( data . matrix && data . matrix . length ? Array . from ( data . matrix ) . slice ( 0 , 16 ) : null ) ;
54+ const vertex = marker . vertex
55+ ? ( Array . isArray ( marker . vertex ) ? marker . vertex . slice ( ) : null )
56+ : ( marker . corners ? marker . corners . flatMap ( c => [ c . x ?? c [ 0 ] , c . y ?? c [ 1 ] ] ) : null ) ;
57+
58+ return {
59+ type : data . type , // e.g., ARToolkit.PATTERN_MARKER
60+ matrix,
61+ marker : {
62+ idPatt : marker . idPatt ?? marker . patternId ?? marker . pattern_id ?? null ,
63+ idMatrix : marker . idMatrix ?? null ,
64+ cfPatt : marker . cfPatt ?? marker . confidence ?? null ,
65+ cfMatrix : marker . cfMatrix ?? null ,
66+ vertex : vertex || null
67+ }
68+ } ;
69+ } catch ( _e ) {
70+ return { type : null , matrix : null , marker : { } } ;
71+ }
72+ }
73+
74+ function attachGetMarkerForwarder ( ) {
75+ if ( ! arController || typeof arController . addEventListener !== 'function' || getMarkerForwarderAttached ) return ;
76+ try {
77+ arController . addEventListener ( 'getMarker' , ( event ) => {
78+ const payload = serializeGetMarkerEvent ( event ) ;
79+ try { console . log ( '[Worker] getMarker' , payload ) ; } catch ( _ ) { }
80+ sendMessage ( { type : 'getMarker' , payload } ) ;
81+ } ) ;
82+ getMarkerForwarderAttached = true ;
83+ } catch ( _e ) {
84+ // ignore
85+ }
4286}
4387
4488// Initialize ARController with default dimensions if not already initialized
4589async function initArtoolkit ( width = 640 , height = 480 ) {
46- if ( arControllerInitialized ) {
47- return true ;
48- }
49-
50- try {
51- // Stub implementation - in real usage, this would initialize the actual ARToolKit WASM module
52- console . log ( `[Worker] Initializing ARToolKit with dimensions ${ width } x${ height } ` ) ;
53-
54- // Simulate ARController initialization
55- arController = {
56- loadMarker : async ( patternUrl ) => {
57- console . log ( `[Worker] Loading marker pattern from: ${ patternUrl } ` ) ;
58- // Simulate async marker loading
59- await new Promise ( ( r ) => setTimeout ( r , 50 ) ) ;
60- // Return a simulated marker ID
61- return Math . floor ( Math . random ( ) * 1000 ) ;
62- } ,
63- trackPatternMarkerId : ( markerId , size ) => {
64- console . log ( `[Worker] Tracking pattern marker ID ${ markerId } with size ${ size } ` ) ;
65- return true ;
66- }
67- } ;
68-
69- arControllerInitialized = true ;
70- console . log ( '[Worker] ARToolKit initialized successfully' ) ;
90+ if ( arControllerInitialized ) return true ;
91+
92+ try {
93+ // Stub implementation - in real usage, this would initialize the actual ARToolKit WASM module
94+ console . log ( `[Worker] Initializing ARToolKit with dimensions ${ width } x${ height } ` ) ;
95+
96+ // Minimal event-capable stub; real ARController will provide these
97+ const _listeners = new Map ( ) ;
98+ arController = {
99+ addEventListener : ( name , fn ) => {
100+ if ( ! _listeners . has ( name ) ) _listeners . set ( name , [ ] ) ;
101+ _listeners . get ( name ) . push ( fn ) ;
102+ } ,
103+ removeEventListener : ( name , fn ) => {
104+ if ( ! _listeners . has ( name ) ) return ;
105+ _listeners . set ( name , _listeners . get ( name ) . filter ( x => x !== fn ) ) ;
106+ } ,
107+ dispatchEvent : ( ev ) => {
108+ const name = ev ?. type || ev ?. name ;
109+ const list = name ? ( _listeners . get ( name ) || [ ] ) : [ ] ;
110+ list . forEach ( h => h ( ev ) ) ;
111+ } ,
112+ loadMarker : async ( patternUrl ) => {
113+ console . log ( `[Worker] Loading marker pattern from: ${ patternUrl } ` ) ;
114+ await new Promise ( ( r ) => setTimeout ( r , 50 ) ) ; // simulate async
115+ return Math . floor ( Math . random ( ) * 1000 ) ; // simulated marker id
116+ } ,
117+ trackPatternMarkerId : ( markerId , size ) => {
118+ console . log ( `[Worker] Tracking pattern marker ID ${ markerId } with size ${ size } ` ) ;
71119 return true ;
72- } catch ( err ) {
73- console . error ( '[Worker] Failed to initialize ARToolKit:' , err ) ;
74- arControllerInitialized = false ;
75- return false ;
76- }
120+ } ,
121+ // no-op; real ARController will process canvas/imageData and emit getMarker events
122+ process : ( _source ) => { }
123+ } ;
124+
125+ arControllerInitialized = true ;
126+ console . log ( '[Worker] ARToolKit initialized successfully' ) ;
127+
128+ // If the real ARController is used, this will forward its getMarker events
129+ attachGetMarkerForwarder ( ) ;
130+
131+ return true ;
132+ } catch ( err ) {
133+ console . error ( '[Worker] Failed to initialize ARToolKit:' , err ) ;
134+ arControllerInitialized = false ;
135+ return false ;
136+ }
77137}
78138
79139onMessage ( async ( ev ) => {
80- const { type, payload } = ev || { } ;
81- try {
82- if ( type === 'init' ) {
83- sendMessage ( { type : 'ready' } ) ;
84- } else if ( type === 'loadMarker' ) {
85- // Handle marker loading request
86- console . log ( '[Worker] Received loadMarker message:' , payload ) ;
87- const { patternUrl, size, requestId } = payload || { } ;
88-
89- if ( ! patternUrl ) {
90- console . error ( '[Worker] loadMarker: missing patternUrl' ) ;
91- sendMessage ( {
92- type : 'loadMarkerResult' ,
93- payload : { ok : false , error : 'Missing patternUrl parameter' , requestId }
94- } ) ;
95- return ;
96- }
97-
98- try {
99- // Ensure ARController is initialized before loading marker
100- if ( ! arControllerInitialized ) {
101- console . log ( '[Worker] Initializing ARToolKit with default dimensions before loading marker' ) ;
102- const initSuccess = await initArtoolkit ( ) ;
103- if ( ! initSuccess ) {
104- throw new Error ( 'Failed to initialize ARToolKit' ) ;
105- }
106- }
107-
108- // Load the marker pattern
109- console . log ( `[Worker] Loading marker from ${ patternUrl } ` ) ;
110- const markerId = await arController . loadMarker ( patternUrl ) ;
111- console . log ( `[Worker] Marker loaded with ID: ${ markerId } ` ) ;
112-
113- // Track the pattern marker with specified size (default to 1)
114- const markerSize = size !== undefined ? size : 1 ;
115- console . log ( `[Worker] Tracking pattern marker ${ markerId } with size ${ markerSize } ` ) ;
116- arController . trackPatternMarkerId ( markerId , markerSize ) ;
117-
118- sendMessage ( {
119- type : 'loadMarkerResult' ,
120- payload : { ok : true , markerId, size : markerSize , requestId }
121- } ) ;
122- console . log ( '[Worker] Marker loading completed successfully' ) ;
123- } catch ( err ) {
124- console . error ( '[Worker] Error loading marker:' , err ) ;
125- sendMessage ( {
126- type : 'loadMarkerResult' ,
127- payload : { ok : false , error : err ?. message || String ( err ) , requestId }
128- } ) ;
129- }
130- } else if ( type === 'processFrame' ) {
131- // Browser: payload.imageBitmap may exist
132- const { imageBitmap, frameId } = payload || { } ;
133-
134- // If ImageBitmap present, we could run detection on it.
135- // In this stub, we just simulate a small delay, then close the ImageBitmap.
136- if ( imageBitmap && typeof imageBitmap . close === 'function' ) {
137- // simulate async processing
138- await new Promise ( ( r ) => setTimeout ( r , 10 ) ) ;
139- // optional: read pixels via OffscreenCanvas if needed
140- try {
141- // Always close the ImageBitmap to free resources
142- imageBitmap . close ( ) ;
143- } catch ( e ) {
144- // ignore
145- }
146- } else {
147- // No ImageBitmap (Node mode or fallback). Simulate async detection.
148- await new Promise ( ( r ) => setTimeout ( r , 10 ) ) ;
149- }
150-
151- const fakeMatrix = new Float32Array ( [ 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 ] ) ;
152- const result = {
153- detections : [
154- {
155- id : 'demo' ,
156- confidence : 0.9 ,
157- poseMatrix : Array . from ( fakeMatrix ) ,
158- corners : [ { x :0 , y :0 } , { x :1 , y :0 } , { x :1 , y :1 } , { x :0 , y :1 } ] ,
159- frameId
160- }
161- ]
162- } ;
163-
164- sendMessage ( { type : 'detectionResult' , payload : result } ) ;
140+ const { type, payload } = ev || { } ;
141+ try {
142+ if ( type === 'init' ) {
143+ sendMessage ( { type : 'ready' } ) ;
144+ } else if ( type === 'loadMarker' ) {
145+ // Handle marker loading request
146+ console . log ( '[Worker] Received loadMarker message:' , payload ) ;
147+ const { patternUrl, size, requestId } = payload || { } ;
148+
149+ if ( ! patternUrl ) {
150+ console . error ( '[Worker] loadMarker: missing patternUrl' ) ;
151+ sendMessage ( {
152+ type : 'loadMarkerResult' ,
153+ payload : { ok : false , error : 'Missing patternUrl parameter' , requestId }
154+ } ) ;
155+ return ;
156+ }
157+
158+ try {
159+ // Ensure ARController is initialized before loading marker
160+ if ( ! arControllerInitialized ) {
161+ console . log ( '[Worker] Initializing ARToolKit with default dimensions before loading marker' ) ;
162+ const initSuccess = await initArtoolkit ( ) ;
163+ if ( ! initSuccess ) throw new Error ( 'Failed to initialize ARToolKit' ) ;
164+ }
165+
166+ // Load the marker pattern
167+ console . log ( `[Worker] Loading marker from ${ patternUrl } ` ) ;
168+ const markerId = await arController . loadMarker ( patternUrl ) ;
169+ console . log ( `[Worker] Marker loaded with ID: ${ markerId } ` ) ;
170+
171+ // Track the pattern marker with specified size (default to 1)
172+ const markerSize = size !== undefined ? size : 1 ;
173+ console . log ( `[Worker] Tracking pattern marker ${ markerId } with size ${ markerSize } ` ) ;
174+ arController . trackPatternMarkerId ( markerId , markerSize ) ;
175+
176+ sendMessage ( {
177+ type : 'loadMarkerResult' ,
178+ payload : { ok : true , markerId, size : markerSize , requestId }
179+ } ) ;
180+ console . log ( '[Worker] Marker loading completed successfully' ) ;
181+ } catch ( err ) {
182+ console . error ( '[Worker] Error loading marker:' , err ) ;
183+ sendMessage ( {
184+ type : 'loadMarkerResult' ,
185+ payload : { ok : false , error : err ?. message || String ( err ) , requestId }
186+ } ) ;
187+ }
188+ } else if ( type === 'processFrame' ) {
189+ // Browser: payload.imageBitmap may exist
190+ const { imageBitmap, frameId } = payload || { } ;
191+
192+ // If ImageBitmap present, we could run detection on it.
193+ // In this stub, we just simulate a small delay, then close the ImageBitmap.
194+ if ( imageBitmap && typeof imageBitmap . close === 'function' ) {
195+ // simulate async processing
196+ await new Promise ( ( r ) => setTimeout ( r , 10 ) ) ;
197+ try {
198+ // Always close the ImageBitmap to free resources
199+ imageBitmap . close ( ) ;
200+ } catch ( _e ) {
201+ // ignore
165202 }
166- } catch ( err ) {
167- sendMessage ( { type : 'error' , payload : { message : err ?. message || String ( err ) } } ) ;
203+ } else {
204+ // No ImageBitmap (Node mode or fallback). Simulate async detection.
205+ await new Promise ( ( r ) => setTimeout ( r , 10 ) ) ;
206+ }
207+
208+ // Simulated detection result (keeps pipeline functional)
209+ const fakeMatrix = new Float32Array ( [
210+ 1 , 0 , 0 , 0 ,
211+ 0 , 1 , 0 , 0 ,
212+ 0 , 0 , 1 , 0 ,
213+ 0 , 0 , 0 , 1
214+ ] ) ;
215+ const demoDetection = {
216+ id : 'demo' ,
217+ confidence : 0.9 ,
218+ poseMatrix : Array . from ( fakeMatrix ) ,
219+ corners : [ { x :0 , y :0 } , { x :1 , y :0 } , { x :1 , y :1 } , { x :0 , y :1 } ] ,
220+ frameId
221+ } ;
222+
223+ // Post existing structured detectionResult
224+ sendMessage ( { type : 'detectionResult' , payload : { detections : [ demoDetection ] } } ) ;
225+
226+ // Also forward an AR.js-style getMarker event to the main thread
227+ const vertex = demoDetection . corners . flatMap ( c => [ c . x , c . y ] ) ;
228+ const getMarkerPayload = {
229+ type : 0 , // ARToolkit.PATTERN_MARKER (placeholder numeric id)
230+ matrix : demoDetection . poseMatrix ,
231+ marker : {
232+ idPatt : demoDetection . id ,
233+ cfPatt : demoDetection . confidence ,
234+ vertex
235+ }
236+ } ;
237+ try { console . log ( '[Worker] getMarker (derived from detectionResult)' , getMarkerPayload ) ; } catch ( _e ) { }
238+ sendMessage ( { type : 'getMarker' , payload : getMarkerPayload } ) ;
239+
240+ // If a real ARController is used and emits 'getMarker', the forwarder will send actual events as well.
168241 }
242+ } catch ( err ) {
243+ sendMessage ( { type : 'error' , payload : { message : err ?. message || String ( err ) } } ) ;
244+ }
169245} ) ;
0 commit comments