@@ -13,20 +13,24 @@ let offscreenCtx = null;
1313let canvasW = 0 ;
1414let canvasH = 0 ;
1515
16- // Init backoff state
17- let initInProgress = null ; // Promise | null
18- let initFailCount = 0 ; // increases on each failure
19- let initFailedUntil = 0 ; // timestamp when next retry is allowed
16+ // Marker and filtering state
17+ const loadedMarkers = new Map ( ) ; // patternUrl -> markerId
18+ const loadingMarkers = new Map ( ) ; // patternUrl -> Promise<markerId>
19+ const trackedPatternIds = new Set ( ) ; // Set<number>
20+ let PATTERN_MARKER_TYPE = 0 ; // will be read from ARToolkit if available
21+ let MIN_CONFIDENCE = 0.6 ; // configurable via init payload
2022
21- // Marker cache/dedupe
22- const loadedMarkers = new Map ( ) ; // patternUrl -> markerId
23- const loadingMarkers = new Map ( ) ; // patternUrl -> Promise<markerId>
23+ // Init backoff state
24+ let initInProgress = null ;
25+ let initFailCount = 0 ;
26+ let initFailedUntil = 0 ;
2427
25- // Init-time options (can be overridden via init payload if you already set this up )
28+ // Init-time options (overridable from main thread )
2629let INIT_OPTS = {
2730 moduleUrl : null ,
2831 cameraParametersUrl : null ,
29- wasmBaseUrl : null
32+ wasmBaseUrl : null ,
33+ minConfidence : null
3034} ;
3135
3236if ( typeof self === 'undefined' ) {
@@ -79,30 +83,52 @@ function serializeGetMarkerEvent(ev) {
7983 }
8084}
8185
86+ function shouldForwardGetMarker ( event ) {
87+ const data = event ?. data || { } ;
88+ const type = data . type ;
89+ const marker = data . marker || { } ;
90+ const id = marker . idPatt ?? marker . patternId ?? marker . pattern_id ?? null ;
91+ const conf = marker . cfPatt ?? marker . confidence ?? 0 ;
92+ const matrix = data . matrix ;
93+
94+ // Type must be PATTERN_MARKER (fallback numeric 0 if constants not available)
95+ if ( type !== PATTERN_MARKER_TYPE ) return false ;
96+
97+ // Confidence gate
98+ if ( ! ( Number . isFinite ( conf ) && conf >= MIN_CONFIDENCE ) ) return false ;
99+
100+ // Matrix must exist with at least 16 values
101+ const m = Array . isArray ( matrix ) ? matrix : ( matrix && Array . from ( matrix ) ) || null ;
102+ if ( ! m || m . length < 16 ) return false ;
103+
104+ // If we have tracked IDs, only forward those IDs
105+ if ( trackedPatternIds . size && id != null && ! trackedPatternIds . has ( id ) ) return false ;
106+
107+ return true ;
108+ }
109+
82110function attachGetMarkerForwarder ( ) {
83111 if ( ! arController || typeof arController . addEventListener !== 'function' || getMarkerForwarderAttached ) return ;
84112 arController . addEventListener ( 'getMarker' , ( event ) => {
113+ if ( ! shouldForwardGetMarker ( event ) ) return ;
85114 const payload = serializeGetMarkerEvent ( event ) ;
86- try { console . log ( '[Worker] getMarker' , payload ) ; } catch { }
115+ try { console . log ( '[Worker] getMarker (filtered) ' , payload ) ; } catch { }
87116 sendMessage ( { type : 'getMarker' , payload } ) ;
88117 } ) ;
89118 getMarkerForwarderAttached = true ;
90119}
91120
92- // IMPORTANT: this function should be the only place that initializes ARToolKit.
93- // It is guarded by initInProgress and a failure backoff.
121+ // Guarded init with backoff
94122async function initArtoolkit ( width = 640 , height = 480 ) {
95123 if ( arControllerInitialized ) return true ;
96124
97- // Respect backoff window
98125 const now = Date . now ( ) ;
99126 if ( now < initFailedUntil ) {
100127 const waitMs = initFailedUntil - now ;
101128 console . warn ( '[Worker] initArtoolkit skipped due to backoff (ms):' , waitMs ) ;
102129 return false ;
103130 }
104131
105- // If an init is already in-flight, await it
106132 if ( initInProgress ) {
107133 try {
108134 await initInProgress ;
@@ -112,30 +138,38 @@ async function initArtoolkit(width = 640, height = 480) {
112138 }
113139 }
114140
115- // Start a new init attempt
116141 initInProgress = ( async ( ) => {
117142 try {
118143 const jsartoolkit = await ( async ( ) => {
119144 if ( INIT_OPTS . moduleUrl ) {
120145 console . log ( '[Worker] Loading artoolkit from moduleUrl:' , INIT_OPTS . moduleUrl ) ;
121146 return await import ( INIT_OPTS . moduleUrl ) ;
122147 }
123- // Fallback to bare import if your environment supports it (import map/bundler)
148+ // If your environment supports bare import (import map/bundler), this will work:
124149 return await import ( '@ar-js-org/artoolkit5-js' ) ;
125150 } ) ( ) ;
126151
127- const { ARController } = jsartoolkit ;
152+ //const { ARController, ARToolkit } = jsartoolkit;
153+
154+ // Read the constant if available; else keep default 0
155+ if ( ARToolkit && typeof ARToolkit . PATTERN_MARKER === 'number' ) {
156+ PATTERN_MARKER_TYPE = ARToolkit . PATTERN_MARKER ;
157+ }
128158
129159 if ( INIT_OPTS . wasmBaseUrl && ARController ) {
130160 try {
131161 ARController . baseURL = INIT_OPTS . wasmBaseUrl . endsWith ( '/' ) ? INIT_OPTS . wasmBaseUrl : INIT_OPTS . wasmBaseUrl + '/' ;
132162 } catch { }
133163 }
134164
165+ if ( typeof INIT_OPTS . minConfidence === 'number' ) {
166+ MIN_CONFIDENCE = INIT_OPTS . minConfidence ;
167+ }
168+
135169 const camUrl = INIT_OPTS . cameraParametersUrl
136170 || 'https://raw.githack.com/AR-js-org/AR.js/master/data/data/camera_para.dat' ;
137171
138- console . log ( '[Worker] ARToolKit init' , { width, height, camUrl } ) ;
172+ console . log ( '[Worker] ARToolKit init' , { width, height, camUrl, minConfidence : MIN_CONFIDENCE , patternType : PATTERN_MARKER_TYPE } ) ;
139173 arController = await ARToolkit . ARController . initWithDimensions ( width , height , camUrl , { } ) ;
140174 arControllerInitialized = ! ! arController ;
141175 console . log ( '[Worker] ARToolKit initialized:' , arControllerInitialized ) ;
@@ -144,46 +178,40 @@ async function initArtoolkit(width = 640, height = 480) {
144178
145179 attachGetMarkerForwarder ( ) ;
146180
147- // Reset failure state
148181 initFailCount = 0 ;
149182 initFailedUntil = 0 ;
150183 } catch ( err ) {
151184 console . error ( '[Worker] ARToolKit init failed:' , err ) ;
152185 arController = null ;
153186 arControllerInitialized = false ;
154187
155- // Exponential backoff up to 30s
156- initFailCount = Math . min ( initFailCount + 1 , 6 ) ; // caps at ~64x
157- const delay = Math . min ( 30000 , 1000 * Math . pow ( 2 , initFailCount ) ) ; // 1s,2s,4s,8s,16s,30s
188+ initFailCount = Math . min ( initFailCount + 1 , 6 ) ;
189+ const delay = Math . min ( 30000 , 1000 * Math . pow ( 2 , initFailCount ) ) ;
158190 initFailedUntil = Date . now ( ) + delay ;
159191
160- // Surface a single error to main thread (optional)
161192 sendMessage ( { type : 'error' , payload : { message : `ARToolKit init failed (${ err ?. message || err } ). Retrying in ${ delay } ms.` } } ) ;
162193 throw err ;
163194 } finally {
164- // Mark as done (success or failure)
165- const ok = arControllerInitialized ;
166195 initInProgress = null ;
167- return ok ;
196+ return arControllerInitialized ;
168197 }
169198 } ) ( ) ;
170199
171200 try {
172201 await initInProgress ;
173- } catch {
174- // already handled
175- }
202+ } catch { }
176203 return arControllerInitialized ;
177204}
178205
179- // Dedupe marker loading by URL
206+ // Dedupe marker loading by URL and record tracked IDs
180207async function loadPatternOnce ( patternUrl ) {
181208 if ( loadedMarkers . has ( patternUrl ) ) return loadedMarkers . get ( patternUrl ) ;
182209 if ( loadingMarkers . has ( patternUrl ) ) return loadingMarkers . get ( patternUrl ) ;
183210
184211 const p = ( async ( ) => {
185212 const id = await arController . loadMarker ( patternUrl ) ;
186213 loadedMarkers . set ( patternUrl , id ) ;
214+ trackedPatternIds . add ( id ) ;
187215 loadingMarkers . delete ( patternUrl ) ;
188216 return id ;
189217 } ) ( ) . catch ( ( e ) => {
@@ -199,11 +227,14 @@ onMessage(async (ev) => {
199227 const { type, payload } = ev || { } ;
200228 try {
201229 if ( type === 'init' ) {
202- // Accept init overrides
203230 if ( payload && typeof payload === 'object' ) {
204- INIT_OPTS . moduleUrl = payload . moduleUrl || INIT_OPTS . moduleUrl ;
205- INIT_OPTS . cameraParametersUrl = payload . cameraParametersUrl || INIT_OPTS . cameraParametersUrl ;
206- INIT_OPTS . wasmBaseUrl = payload . wasmBaseUrl || INIT_OPTS . wasmBaseUrl ;
231+ INIT_OPTS . moduleUrl = payload . moduleUrl ?? INIT_OPTS . moduleUrl ;
232+ INIT_OPTS . cameraParametersUrl = payload . cameraParametersUrl ?? INIT_OPTS . cameraParametersUrl ;
233+ INIT_OPTS . wasmBaseUrl = payload . wasmBaseUrl ?? INIT_OPTS . wasmBaseUrl ;
234+ if ( typeof payload . minConfidence === 'number' ) {
235+ INIT_OPTS . minConfidence = payload . minConfidence ;
236+ MIN_CONFIDENCE = payload . minConfidence ;
237+ }
207238 }
208239 sendMessage ( { type : 'ready' } ) ;
209240 return ;
@@ -235,14 +266,11 @@ onMessage(async (ev) => {
235266
236267 if ( type === 'processFrame' ) {
237268 const { imageBitmap, width, height } = payload || { } ;
238-
239- // Browser path: only attempt init at controlled cadence (guard handles backoff)
240269 if ( ! isNodeWorker && imageBitmap ) {
241270 try {
242271 const w = width || imageBitmap . width || 640 ;
243272 const h = height || imageBitmap . height || 480 ;
244273
245- // Attempt init once; if it fails, guard prevents hammering it per-frame
246274 await initArtoolkit ( w , h ) ;
247275
248276 if ( ! offscreenCanvas || canvasW !== w || canvasH !== h ) {
@@ -269,12 +297,10 @@ onMessage(async (ev) => {
269297 }
270298 } catch ( err ) {
271299 console . error ( '[Worker] processFrame error:' , err ) ;
272- // No spam: let initArtoolkit handle error posting and backoff logging
273300 }
274301 return ;
275302 }
276303
277- // Node fallback: do nothing (no real AR in Node)
278304 await new Promise ( ( r ) => setTimeout ( r , 5 ) ) ;
279305 return ;
280306 }
0 commit comments