1+ class DOMDriver {
2+ constructor ( grid , algorithms , stepHistory , targetDOMElement ) {
3+ this . Grid = grid
4+ this . Algorithms = algorithms
5+ this . StepHistory = stepHistory
6+ this . DOMElementRoot = targetDOMElement
7+ this . CellReferences = { }
8+ this . preLoadImages ( )
9+ return this . constructVisuals ( )
10+ }
11+ // Visualise result
12+ constructVisuals ( ) {
13+ // Find largest steps
14+ for ( const algorithm of this . Algorithms ) {
15+ const algorithmStep = this . StepHistory [ algorithm ] . history . length
16+ MaxStep = algorithmStep > MaxStep ? algorithmStep : MaxStep
17+ }
18+ let visualResult = `<a style="color: orange">WARNING: WebGPU or the full functionality of WebGPU is not supported in this browser!</a><br>Performance will be drastically reduced!<br>Please use Chrome/Edge 113+, Safari Technology Preview, Opera 99+, Chrome for Android 138+, Samsung Internet 24+, Opera Mobile 80+ <br>Or enable WebGPU flag on Safari or Firefox.<br><br>`
19+
20+ // Add visual for each algorithm
21+ for ( const algorithm of Algorithms ) {
22+ // Statistics
23+ visualResult += `${ algorithm } : ${ this . StepHistory [ algorithm ] . history . length } steps found path with length ${ this . StepHistory [ algorithm ] . steps . length - 1 } in ${ Math . round ( this . StepHistory [ algorithm ] . time ) } ms.`
24+
25+ // Draw initial grid
26+ visualResult += `<table class="board ${ algorithm } ">\n<tbody>\n`
27+ for ( let y = 0 ; y < Grid . height ; y ++ ) {
28+ visualResult += `<tr id="row ${ y } ">\n`
29+ for ( let x = 0 ; x < Grid . width ; x ++ ) {
30+ let style = ""
31+ if ( Grid . getCell ( [ x , y ] ) === "start" )
32+ style += " style='filter: invert(33%) sepia(71%) saturate(4592%) hue-rotate(6deg) brightness(106%) contrast(106%);'"
33+
34+ visualResult += `<td id="${ x } -${ y } " role="cell" class="${ Grid . getCell ( [ x , y ] ) } "${ style } ></td>\n`
35+ }
36+ visualResult += `</tr>\n`
37+ }
38+ visualResult += `</tbody>\n</table>\n`
39+ }
40+
41+ document . getElementById ( this . DOMElementRoot ) . innerHTML = visualResult
42+
43+ this . saveReferences ( )
44+ }
45+
46+ preLoadImages ( ) {
47+ var image = new Image ( ) ;
48+ image . src = "./icons/arrow-down-square-fill.svg"
49+ image . src = "./icons/arrow-up-square-fill.svg"
50+ image . src = "./icons/arrow-left-square-fill.svg"
51+ image . src = "./icons/door-open-fill.svg"
52+ }
53+
54+ // Save cell references
55+ saveReferences ( ) {
56+ for ( const algorithm of this . Algorithms ) {
57+ this . CellReferences [ algorithm ] = [ ]
58+ const table = document . querySelector ( `table.board.${ algorithm } ` )
59+ for ( let y = 0 ; y < this . Grid . height ; y ++ ) {
60+ const row = table . rows [ y ]
61+ for ( let x = 0 ; x < this . Grid . width ; x ++ ) {
62+ this . CellReferences [ algorithm ] [ y * this . Grid . width + x ] = row . cells [ x ]
63+ }
64+ }
65+ }
66+ }
67+
68+ // Update algorithm results
69+ updateVisuals ( ) {
70+ for ( const algorithm of this . Algorithms ) {
71+ const { history, steps, directions } = this . StepHistory [ algorithm ]
72+ const algorithmSteps = history . length
73+
74+ const queue = QueueHistory [ algorithm ]
75+
76+ // Calculate render range
77+ const startStep = LastRenderedStep
78+ const endStep = CurrentStep
79+ const isForward = endStep > startStep
80+
81+ CellReferences [ algorithm ] . forEach ( cell => cell . removeAttribute ( "style" ) )
82+ CellReferences [ algorithm ] . forEach ( cell => cell . innerHTML = "" )
83+ const starts = document . getElementsByClassName ( "start" )
84+ Array . prototype . forEach . call ( starts , function ( start ) {
85+ start . innerHTML = ""
86+ } )
87+
88+ // Show which cell is being searched and which cells are in the queue/stack
89+ const searchingStep = CurrentStep <= algorithmSteps ? CurrentStep : - 1
90+ if ( searchingStep != - 1 && CurrentStep < algorithmSteps ) {
91+ const [ searchingX , searchingY ] = Object . keys ( history [ searchingStep ] ) [ 0 ] . split ( "-" ) . map ( function ( item ) {
92+ return parseInt ( item ) ;
93+ } )
94+
95+ // Queue might be array or objects, convert array into objects and rename different markings to score
96+ // Did not convert to maintain accuracy of benchmark
97+ // e.g. [{pos: [2, 5], score: 1}, {pos: [2, 7], score: 1}]
98+ const currentStepQueue = queue [ searchingStep ]
99+ const formattedCurrentStepQueue = [ ]
100+ if ( Array . isArray ( currentStepQueue [ 0 ] ) ) {
101+ currentStepQueue . forEach ( queueItem => {
102+ formattedCurrentStepQueue . push ( { pos : queueItem , score : - 1 } )
103+ } )
104+ } else {
105+ // Get score name/key
106+ const scoreKey = Object . keys ( currentStepQueue [ 0 ] ) [ 1 ]
107+ currentStepQueue . forEach ( queueItem => {
108+ formattedCurrentStepQueue . push ( { pos : queueItem . pos , score : queueItem [ scoreKey ] } )
109+ } )
110+ }
111+
112+ // Add score to visited
113+ formattedCurrentStepQueue . forEach ( queueCell => {
114+ const pos = queueCell . pos
115+ const score = queueCell . score
116+ if ( score != - 1 )
117+ CellReferences [ algorithm ] [ pos [ 1 ] * Grid . width + pos [ 0 ] ] . innerHTML = score
118+ else
119+ CellReferences [ algorithm ] [ pos [ 1 ] * Grid . width + pos [ 0 ] ] . innerHTML = "0"
120+ } )
121+
122+ // Set searching cell to #FFFF00, filter generated by: https://codepen.io/sosuke/pen/Pjoqqp
123+ CellReferences [ algorithm ] [ searchingY * Grid . width + searchingX ] . setAttribute ( "style" , "filter: invert(33%) sepia(71%) saturate(4592%) hue-rotate(6deg) brightness(106%) contrast(106%);" )
124+ }
125+
126+ // Reverse
127+ if ( ! isForward ) {
128+ for ( let step = startStep ; step >= endStep ; step -- ) {
129+ if ( step >= algorithmSteps ) continue
130+ const cells = Object . values ( history [ step ] ) . flat ( )
131+ cells . forEach ( ( [ x , y ] ) => {
132+ const index = y * Grid . width + x
133+ if ( CellReferences [ algorithm ] [ index ] . className !== "start" &&
134+ CellReferences [ algorithm ] [ index ] . className !== "target" ) {
135+ CellReferences [ algorithm ] [ index ] . className = "unvisited"
136+ }
137+ } )
138+ }
139+ // Forward
140+ } else {
141+ for ( let step = startStep ; step < endStep ; step ++ ) {
142+ if ( step >= algorithmSteps ) continue
143+ const cells = Object . values ( history [ step ] ) . flat ( )
144+ cells . forEach ( ( [ x , y ] ) => {
145+ const index = y * Grid . width + x
146+ if ( CellReferences [ algorithm ] [ index ] . className !== "start" &&
147+ CellReferences [ algorithm ] [ index ] . className !== "target" ) {
148+ CellReferences [ algorithm ] [ index ] . className = "visited"
149+ }
150+ } )
151+ }
152+ }
153+
154+ // Draw and un-draw path
155+ const isFinalStep = CurrentStep >= algorithmSteps
156+ const wasFinalStep = LastRenderedStep >= algorithmSteps
157+
158+ if ( wasFinalStep && ! isFinalStep ) {
159+ drawPath ( algorithm , false )
160+ }
161+
162+ if ( isFinalStep && steps . length > 0 ) {
163+ drawPath ( algorithm , true )
164+ }
165+ }
166+
167+ LastRenderedStep = CurrentStep
168+ }
169+
170+ drawPath ( algorithm , draw ) {
171+ const { steps, directions } = StepHistory [ algorithm ]
172+ for ( let pathStep = 1 ; pathStep < StepHistory [ algorithm ] . steps . length ; pathStep ++ ) {
173+ const [ x , y ] = steps [ pathStep ]
174+ if ( draw ) {
175+ CellReferences [ algorithm ] [ y * Grid . width + x ] . className = directions [ pathStep ]
176+ } else {
177+ CellReferences [ algorithm ] [ y * Grid . width + x ] . className = CurrentStep == 0 ? "unvisited" : "visited"
178+ }
179+ }
180+ // Update target cell
181+ const [ targetX , targetY ] = Grid . getTargetPos ( )
182+ CellReferences [ algorithm ] [ targetY * Grid . width + targetX ] . className = draw ? "targetReached" : "target"
183+ }
184+ }
0 commit comments