1- <!DOCTYPE html>
1+ <!doctype html>
22< html lang ="en ">
33< head >
4- < meta charset ="UTF -8 ">
5- < meta name ="viewport " content ="width=device-width, initial-scale=1.0 " >
6- < title > ARToolKit Plugin - Simple Marker Example</ title >
4+ < meta charset ="utf -8 " / >
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1 " / >
6+ < title > Simple Marker Example - ARToolKit Plugin </ title >
77 < style >
8- body {
9- margin : 0 ;
10- padding : 20px ;
11- font-family : Arial, sans-serif;
12- background : # f0f0f0 ;
13- }
14-
15- .container {
16- max-width : 1200px ;
17- margin : 0 auto;
18- }
19-
20- h1 {
21- color : # 333 ;
22- }
23-
24- .info {
25- background : # fff ;
26- padding : 15px ;
27- border-radius : 5px ;
28- margin-bottom : 20px ;
29- border-left : 4px solid # 007bff ;
30- }
31-
32- .video-container {
33- position : relative;
34- background : # 000 ;
35- border-radius : 5px ;
36- overflow : hidden;
37- margin-bottom : 20px ;
38- }
39-
40- # video {
41- width : 100% ;
42- max-width : 640px ;
43- display : block;
44- }
45-
46- .controls {
47- background : # fff ;
48- padding : 15px ;
49- border-radius : 5px ;
50- margin-bottom : 20px ;
51- }
52-
53- button {
54- background : # 007bff ;
55- color : white;
56- border : none;
57- padding : 10px 20px ;
58- border-radius : 5px ;
59- cursor : pointer;
60- font-size : 16px ;
61- margin-right : 10px ;
62- }
63-
64- button : hover {
65- background : # 0056b3 ;
66- }
67-
68- button : disabled {
69- background : # ccc ;
70- cursor : not-allowed;
71- }
72-
73- .console {
74- background : # 1e1e1e ;
75- color : # d4d4d4 ;
76- padding : 15px ;
77- border-radius : 5px ;
78- font-family : 'Courier New' , monospace;
79- font-size : 12px ;
80- max-height : 300px ;
81- overflow-y : auto;
82- margin-bottom : 20px ;
83- }
84-
85- .console-line {
86- margin-bottom : 5px ;
87- padding : 2px 0 ;
88- }
89-
90- .console-line .info {
91- color : # 4ec9b0 ;
92- }
93-
94- .console-line .error {
95- color : # f48771 ;
96- }
97-
98- .console-line .warn {
99- color : # dcdcaa ;
100- }
101-
102- .status {
103- background : # fff ;
104- padding : 15px ;
105- border-radius : 5px ;
106- margin-bottom : 20px ;
107- }
108-
109- .status-item {
110- margin : 5px 0 ;
111- }
112-
113- .badge {
114- display : inline-block;
115- padding : 3px 8px ;
116- border-radius : 3px ;
117- font-size : 12px ;
118- font-weight : bold;
119- margin-left : 10px ;
120- }
121-
122- .badge .success {
123- background : # 28a745 ;
124- color : white;
125- }
126-
127- .badge .warning {
128- background : # ffc107 ;
129- color : # 333 ;
130- }
131-
132- .badge .danger {
133- background : # dc3545 ;
134- color : white;
135- }
8+ body { font-family : Arial, sans-serif; max-width : 900px ; margin : 40px auto; padding : 20px ; }
9+ h1 { color : # 333 ; }
10+ .status { padding : 10px ; margin : 10px 0 ; border-radius : 4px ; background-color : # f0f0f0 ; }
11+ .status .success { background-color : # d4edda ; color : # 155724 ; }
12+ .status .error { background-color : # f8d7da ; color : # 721c24 ; }
13+ .log { background : # f5f5f5 ; padding : 10px ; border-radius : 4px ; max-height : 300px ; overflow-y : auto; font-family : monospace; font-size : 12px ; }
14+ button { padding : 10px 16px ; margin : 6px ; cursor : pointer; border : none; border-radius : 4px ; background : # 007bff ; color : white; }
15+ button [disabled ] { background : # 999 ; cursor : default; }
16+ # video { display : none; }
13617 </ style >
13718</ head >
13819< body >
139- < div class ="container ">
140- < h1 > ARToolKit Plugin - Simple Marker Detection Example</ h1 >
141-
142- < div class ="info ">
143- < p > < strong > Instructions:</ strong > </ p >
144- < ol >
145- < li > Click "Start Camera" to begin webcam capture</ li >
146- < li > Show an ARToolKit marker to the camera</ li >
147- < li > Detection results will appear in the console below</ li >
148- < li > Check your browser's developer console for detailed logs</ li >
149- </ ol >
150- < p > < strong > Note:</ strong > This example requires HTTPS or localhost to access the webcam.</ p >
151- </ div >
152-
153- < div class ="controls ">
154- < button id ="startBtn "> Start Camera</ button >
155- < button id ="stopBtn " disabled > Stop Camera</ button >
156- </ div >
157-
158- < div class ="status ">
159- < h3 > Status</ h3 >
160- < div class ="status-item ">
161- Camera: < span id ="cameraStatus " class ="badge warning "> Not started</ span >
162- </ div >
163- < div class ="status-item ">
164- Plugin: < span id ="pluginStatus " class ="badge warning "> Not initialized</ span >
165- </ div >
166- < div class ="status-item ">
167- Worker: < span id ="workerStatus " class ="badge warning "> Not ready</ span >
168- </ div >
169- < div class ="status-item ">
170- Frames processed: < span id ="frameCount "> 0</ span >
171- </ div >
172- < div class ="status-item ">
173- Markers detected: < span id ="markerCount "> 0</ span >
174- </ div >
175- </ div >
176-
177- < div class ="video-container ">
178- < video id ="video " autoplay playsinline > </ video >
179- </ div >
180-
181- < div >
182- < h3 > Console Output</ h3 >
183- < div id ="console " class ="console ">
184- < div class ="console-line info "> Ready. Click "Start Camera" to begin.</ div >
185- </ div >
186- </ div >
187- </ div >
188-
189- < script type ="module " src ="./script.js "> </ script >
20+ < h1 > Simple Marker Example</ h1 >
21+ < p > This example demonstrates loading a pattern marker using the ARToolKit plugin.</ p >
22+
23+ < div id ="status " class ="status "> Initializing...</ div >
24+ < div >
25+ < button id ="startCamBtn "> Start Camera</ button >
26+ < button id ="loadMarkerBtn " disabled > Load Marker</ button >
27+ </ div >
28+
29+ < h3 > Event Log:</ h3 >
30+ < div id ="log " class ="log "> </ div >
31+
32+ < video id ="video " autoplay playsinline > </ video >
33+
34+ < script type ="module ">
35+ import { ArtoolkitPlugin } from '../../src/plugin.js' ;
36+
37+ const statusEl = document . getElementById ( 'status' ) ;
38+ const logEl = document . getElementById ( 'log' ) ;
39+ const loadMarkerBtn = document . getElementById ( 'loadMarkerBtn' ) ;
40+ const startCamBtn = document . getElementById ( 'startCamBtn' ) ;
41+ const video = document . getElementById ( 'video' ) ;
42+
43+ function log ( message ) {
44+ const ts = new Date ( ) . toISOString ( ) ;
45+ const el = document . createElement ( 'div' ) ;
46+ el . textContent = `[${ ts } ] ${ message } ` ;
47+ logEl . appendChild ( el ) ;
48+ logEl . scrollTop = logEl . scrollHeight ;
49+ console . log ( message ) ;
50+ }
51+
52+ function setStatus ( msg , type = 'normal' ) {
53+ statusEl . textContent = msg ;
54+ statusEl . className = 'status' ;
55+ if ( type === 'success' ) statusEl . classList . add ( 'success' ) ;
56+ if ( type === 'error' ) statusEl . classList . add ( 'error' ) ;
57+ }
58+
59+ // Minimal eventBus / core used by the plugin
60+ const eventBus = {
61+ _h : new Map ( ) ,
62+ on ( e , h ) { if ( ! this . _h . has ( e ) ) this . _h . set ( e , [ ] ) ; this . _h . get ( e ) . push ( h ) ; } ,
63+ off ( e , h ) { if ( ! this . _h . has ( e ) ) return ; this . _h . set ( e , this . _h . get ( e ) . filter ( x => x !== h ) ) ; } ,
64+ emit ( e , p ) { ( this . _h . get ( e ) || [ ] ) . forEach ( fn => fn ( p ) ) ; }
65+ } ;
66+ const core = { eventBus } ;
67+
68+ let plugin ;
69+
70+ async function start ( ) {
71+ try {
72+ log ( 'Creating ArtoolkitPlugin instance...' ) ;
73+ plugin = new ArtoolkitPlugin ( { worker : true } ) ;
74+ await plugin . init ( core ) ;
75+ await plugin . enable ( ) ;
76+
77+ // wire event handlers
78+ eventBus . on ( 'ar:workerReady' , ( ) => {
79+ log ( 'Worker ready' ) ;
80+ setStatus ( 'Worker ready. Start camera and then load marker.' , 'success' ) ;
81+ loadMarkerBtn . disabled = false ;
82+ } ) ;
83+
84+ eventBus . on ( 'ar:markerFound' , d => log ( `markerFound: ${ JSON . stringify ( d ) } ` ) ) ;
85+ eventBus . on ( 'ar:markerUpdated' , d => log ( `markerUpdated: ${ JSON . stringify ( d ) } ` ) ) ;
86+ eventBus . on ( 'ar:markerLost' , d => log ( `markerLost: ${ JSON . stringify ( d ) } ` ) ) ;
87+ eventBus . on ( 'ar:workerError' , e => {
88+ log ( `workerError: ${ JSON . stringify ( e ) } ` ) ;
89+ setStatus ( 'Worker error (see console)' , 'error' ) ;
90+ } ) ;
91+
92+ // Also log detectionResult from plugin console.log (plugin logs detectionResult)
93+ setStatus ( 'Plugin initialized. Waiting for worker...' , 'normal' ) ;
94+ } catch ( err ) {
95+ log ( 'Init error: ' + ( err && err . message ? err . message : err ) ) ;
96+ setStatus ( 'Initialization error' , 'error' ) ;
97+ }
98+ }
99+
100+ // Start camera when user clicks (to ensure user gesture)
101+ startCamBtn . addEventListener ( 'click' , async ( ) => {
102+ try {
103+ startCamBtn . disabled = true ;
104+ log ( 'Starting camera...' ) ;
105+ const stream = await navigator . mediaDevices . getUserMedia ( { video : { facingMode : 'environment' } } ) ;
106+ video . srcObject = stream ;
107+ // emit engine:update frames using createImageBitmap
108+ const canvas = document . createElement ( 'canvas' ) ;
109+ const ctx = canvas . getContext ( '2d' ) ;
110+ let id = 0 ;
111+
112+ async function tick ( ) {
113+ if ( video . videoWidth === 0 ) { requestAnimationFrame ( tick ) ; return ; }
114+ canvas . width = video . videoWidth ;
115+ canvas . height = video . videoHeight ;
116+ ctx . drawImage ( video , 0 , 0 ) ;
117+ const imageBitmap = await createImageBitmap ( canvas ) ;
118+ // emit engine:update; plugin will transfer the ImageBitmap to the worker
119+ eventBus . emit ( 'engine:update' , { id : ++ id , imageBitmap, width : canvas . width , height : canvas . height } ) ;
120+ requestAnimationFrame ( tick ) ;
121+ }
122+ requestAnimationFrame ( tick ) ;
123+ log ( 'Camera started.' ) ;
124+ setStatus ( 'Camera started. You can now load the marker.' , 'success' ) ;
125+ } catch ( err ) {
126+ log ( 'Camera error: ' + ( err && err . message ? err . message : err ) ) ;
127+ setStatus ( 'Camera error' , 'error' ) ;
128+ startCamBtn . disabled = false ;
129+ }
130+ } ) ;
131+
132+ // Load marker button uses plugin.loadMarker(...)
133+ loadMarkerBtn . addEventListener ( 'click' , async ( ) => {
134+ if ( ! plugin ) return log ( 'Plugin not ready' ) ;
135+ loadMarkerBtn . disabled = true ;
136+ setStatus ( 'Loading marker...' , 'normal' ) ;
137+ try {
138+ // Use absolute/site-root-relative URL so worker can fetch it.
139+ const patternUrl = '/examples/simple-marker/data/patt.hiro' ;
140+ log ( `Requesting loadMarker for ${ patternUrl } ` ) ;
141+ const res = await plugin . loadMarker ( patternUrl , 1 ) ;
142+ log ( `loadMarker result: ${ JSON . stringify ( res ) } ` ) ;
143+ setStatus ( `Marker loaded (id=${ res . markerId } )` , 'success' ) ;
144+ } catch ( err ) {
145+ log ( 'loadMarker failed: ' + ( err && err . message ? err . message : err ) ) ;
146+ setStatus ( 'Failed to load marker' , 'error' ) ;
147+ } finally {
148+ loadMarkerBtn . disabled = false ;
149+ }
150+ } ) ;
151+
152+ // initialize plugin but don't start camera automatically
153+ start ( ) ;
154+ </ script >
190155</ body >
191- </ html >
156+ </ html >
0 commit comments