1- // Gift wrapping convex hull algorithm (based on DFSrec)
2- // but code is simpler and has different structure of course.
3- // XXX iterative version ends up with fewer "fontier" edges because if
4- // you add the node to the frontier again, the previous edge reverts - that
5- // should probably be changed?? Not sure about BFS etc
6- // Copied and modified from dijkstra.js (nicer code than DFS)
7- // Might be some leftover bit from dijkstra.js that could be cleaned up
8- // further.
9- // XXX add support for multiple end nodes
1+ // Gift wrapping convex hull algorithm
2+ //
3+ // XXX Based on DFSrec - may still be some leftover rubbish to delete.
4+ //
5+ // XXX Uses EuclideanMatrixParams; need to make some changes to this:
6+ // 1) The graphs here (initially at least) have no edges - need to change
7+ // graph generation and adjust display accordingly. Probably best to
8+ // have a noEdges option like we have weighted and directed as options.
9+ // 2) The initial display is too big (and too far left) - other
10+ // algorithms have an array display also and this affects zoom etc. Need
11+ // to fix this display without breaking other algorithms (code seems a
12+ // bit messy).
13+ //
14+ // XXX Not properly linked to pseudocode (need more bookmarks and change
15+ // the bookmarks in this controller code); also will need to animate a
16+ // few more things.
17+ //
18+ // XXX Currently the "wrapper" uses an edge to a node that is off the
19+ // screen, usually. It should be off the screen always - need to work
20+ // more on node position. If the user zooms out it is currently visible
21+ // - that should be OK. It would be nice if moving the wrapper was done
22+ // more smoothly. Currently tweening only really works for nodes, not
23+ // edges - if this couls be fixed it would be useful for AVL trees and
24+ // other algorithms also. Otherwise, perhaps the wrapper could be moved
25+ // in two steps at least - the first being close to the next node and
26+ // the second being at the next node. Or maybe the wrapper edge could
27+ // look a bit different from the hull edges (not too different) so the
28+ // wrapper could go through the next node at the first step then the new
29+ // hull edge could change a bit.
30+ //
31+ // XXX Maybe p,q,i should be shown explicitly - currently they are all
32+ // implicit using edges, colors etc (should at least color node i)
33+ //
34+ // XXX Nice to have an option of removing all points in a quadralateral
35+ // made up of lef, top, right and bottom points. See union-find for an
36+ // example of such an option. Need to fix up pseudocode for this (some
37+ // work has been done). Probably best not give too many details in code
38+ // or animation - mode details in explanation and/or MORE tab.
39+ //
40+ // XXX a bunch more other stuff mentioned in code below
41+ //
42+ // XXX Color stuff with graphs is a bit of a mess - this is a Global
43+ // Issue to be worked in separately - communication about this and how
44+ // merge conflicts are handed may be needed
45+
1046import GraphTracer from '../../components/DataStructures/Graph/GraphTracer' ;
11- import Array2DTracer from '../../components/DataStructures/Array/Array2DTracer' ;
1247import { colorsCH } from './convexHullColours' ;
13- import { colors } from './graphSearchColours' ; // XX
1448
1549export default {
1650 initVisualisers ( ) {
@@ -22,21 +56,23 @@ export default {
2256 } ;
2357 } ,
2458
59+ // XXX don't need startNode, endNodes, edgeValueMatrix
60+ // Nest parameter code(?)
2561 run ( chunker , { edgeValueMatrix, coordsMatrix, startNode, endNodes, moveNode} ) {
2662 // String Variables used in displaying algo
27- const algNameStr = 'DFSrec' ;
28- const nStr = 'n' ;
29- const mStr = 'm' ;
3063
31- const E = [ ...edgeValueMatrix ] ;
64+ // id for wrapper node - could be anything other than a small
65+ // integer; '@' looks a bit like a roll of paper/string...
66+ // It's not normally visible but may be found if someone searches
67+ const wrapperStr = '@' ;
68+
3269 const coords = [ ...coordsMatrix ] ;
3370 const numVertices = edgeValueMatrix . length ;
34- const unassigned = ' ' ; // unassigned parent
35- const parents = [ ] ; // parent of each node; initially unassigned
36- const seen = [ ] ; // neighbours of finalised node
37- const nodes = [ ] ;
38- const start = startNode - 1 ;
39- const end = endNodes [ 0 ] - 1 ;
71+ // const E = [...edgeValueMatrix];
72+ // edge matrix is NxN matrix of edge weights, zero meaning no edge
73+ // Here we ignore edgeValueMatrix and create our own zero matrix
74+ const Zeros = new Array ( numVertices ) . fill ( 0 ) ;
75+ const E = new Array ( numVertices ) . fill ( Zeros ) ;
4076
4177 // Javascript to find convex hull of a set of coords. Refer
4278 // https://www.geeksforgeeks.org/dsa/orientation-3-ordered-coords/
@@ -117,8 +153,8 @@ export default {
117153 // XXX Graph too big and too far left...
118154 // vis.graph.setZoom(0.5);
119155 vis . graph . colorNode ( c_p , colorsCH . HULLP_N ) ;
120- vis . graph . addEdge ( 'W' , c_p ) ;
121- vis . graph . colorEdge ( c_p , 'W' , colorsCH . HULL_E ) ;
156+ vis . graph . addEdge ( wrapperStr , c_p ) ;
157+ vis . graph . colorEdge ( c_p , wrapperStr , colorsCH . HULL_E ) ;
122158 } ,
123159 [ E , coords , p , q ] , 0
124160 ) ;
@@ -140,18 +176,19 @@ export default {
140176 // want q s.t. for no i, p->i->q is clockwise
141177 q = ( p + 1 ) % n ;
142178
143- chunker . add ( 'start ' ,
179+ chunker . add ( 'initQ ' ,
144180 ( vis , edgeArray , coordsArray , c_p , c_q ) => {
145181 vis . graph . colorNode ( c_q , colorsCH . NEXTQ_N ) ;
146182 } ,
147183 [ E , coords , p , q ] , 0
148184 ) ;
149- let hullHasQ , hullHasI , hullHasPQ , hullHasQI ;
185+ let hullHasQ , hullHasI , hullHasPI , hullHasQI ;
150186 hullHasQ = hull . includes ( q ) ;
151- hullHasPQ = includesConsecutive ( hull , p , q ) ;
187+ chunker . add ( 'assignI' ) ;
152188 for ( let i = 0 ; i < n ; i ++ )
153189 {
154190 hullHasI = hull . includes ( i ) ;
191+ hullHasPI = includesConsecutive ( hull , p , i ) ;
155192 hullHasQI = includesConsecutive ( hull , i , q ) ;
156193 let old_q = q ;
157194 // If i is more clockwise than
@@ -162,16 +199,16 @@ export default {
162199 // Here, if there are multiple coords with minimal
163200 // clockwiseness (if thats a word), we pick the first one
164201 // (min point number)
165- chunker . add ( 'start ' ,
202+ chunker . add ( 'piqTest ' ,
166203 ( vis , edgeArray , coordsArray , c_p , c_q , c_i ) => {
167- vis . graph . addEdge ( c_p , c_q ) ;
204+ vis . graph . addEdge ( c_p , c_i ) ;
168205 vis . graph . addEdge ( c_q , c_i ) ;
169- vis . graph . colorEdge ( c_p , c_q , colorsCH . CLOCKWISE_E ) ;
206+ vis . graph . colorEdge ( c_p , c_i , colorsCH . CLOCKWISE_E ) ;
170207 vis . graph . colorEdge ( c_q , c_i , colorsCH . CLOCKWISE_E ) ;
171208 } ,
172209 [ E , coords , p , q , i ] , 0
173210 ) ;
174- chunker . add ( 'start ' ,
211+ chunker . add ( 'q<-i ' ,
175212 ( vis , edgeArray , coordsArray , c_p , c_q , c_i , h_q ) => {
176213 vis . graph . colorNode ( c_i , colorsCH . NEXTQ_N ) ;
177214 if ( h_q )
@@ -182,38 +219,41 @@ export default {
182219 [ E , coords , p , q , i , hullHasQ ] , 0
183220 ) ;
184221 q = i ;
185- // defer recalculation of hullHasQ, hullHasPQ - we want
186- // them to refere to the old version of q for now
222+ // defer recalculation of hullHasQ - we want
223+ // to refer to the old version of q for now
187224 } else {
188- chunker . add ( 'start ' ,
225+ chunker . add ( 'piqTest ' ,
189226 ( vis , edgeArray , coordsArray , c_p , c_q , c_i ) => {
190- vis . graph . addEdge ( c_p , c_q ) ;
227+ vis . graph . addEdge ( c_p , c_i ) ;
191228 vis . graph . addEdge ( c_q , c_i ) ;
192- vis . graph . colorEdge ( c_p , c_q , colorsCH . ANTICLOCK_E ) ;
229+ vis . graph . colorEdge ( c_p , c_i , colorsCH . ANTICLOCK_E ) ;
193230 vis . graph . colorEdge ( c_q , c_i , colorsCH . ANTICLOCK_E ) ;
194231 } ,
195232 [ E , coords , p , q , i ] , 0
196233 ) ;
197234 }
198- chunker . add ( 'start ' ,
199- ( vis , edgeArray , coordsArray , c_p , c_q , c_i , h_pq , h_qi ) => {
200- if ( h_pq )
201- vis . graph . colorEdge ( c_p , c_q , colorsCH . HULL_E ) ;
235+ chunker . add ( 'assignI ' ,
236+ ( vis , edgeArray , coordsArray , c_p , c_q , c_i , h_pi , h_qi ) => {
237+ if ( h_pi )
238+ vis . graph . colorEdge ( c_p , c_i , colorsCH . HULL_E ) ;
202239 else
203- vis . graph . removeEdge ( c_p , c_q ) ;
240+ vis . graph . removeEdge ( c_p , c_i ) ;
204241 if ( h_qi )
205242 vis . graph . colorEdge ( c_q , c_i , colorsCH . HULL_E ) ;
206243 else
207244 vis . graph . removeEdge ( c_q , c_i ) ;
208245 } ,
209- [ E , coords , p , old_q , i , hullHasPQ , hullHasQI ] , 0
246+ [ E , coords , p , old_q , i , hullHasPI , hullHasQI ] , 0
210247 ) ;
211- // now update hullHasQ, hullHasPQ in case q changed above
248+ // now update hullHasQ in case q changed above
212249 hullHasQ = hull . includes ( q ) ;
213- hullHasPQ = includesConsecutive ( hull , p , q ) ;
214250 }
215251
216- chunker . add ( 'start' ,
252+ // XXX this is more animation of add p to hull...
253+ // Should move most of this earlier but have to deal with
254+ // initial case when q is undefined and we don't have to
255+ // add edge etc
256+ chunker . add ( 'p<-q' ,
217257 ( vis , edgeArray , coordsArray , c_p , c_q ) => {
218258 // Nice to do this in a couple of steps if possile
219259 // Move wrapper close then final position with node
@@ -222,29 +262,33 @@ export default {
222262 vis . graph . colorEdge ( c_p , c_q , colorsCH . HULL_E ) ;
223263 vis . graph . colorNode ( c_q , colorsCH . HULLP_N ) ;
224264 // redo "wrapping" edge
225- vis . graph . removeEdge ( c_p , 'W' ) ;
226- vis . graph . addEdge ( 'W' , c_q ) ;
227- vis . graph . colorEdge ( c_q , 'W' , colorsCH . HULL_E ) ;
265+ vis . graph . removeEdge ( c_p , wrapperStr ) ;
266+ vis . graph . addEdge ( wrapperStr , c_q ) ;
267+ vis . graph . colorEdge ( c_q , wrapperStr , colorsCH . HULL_E ) ;
228268 let [ pX , pY ] = vis . graph . getNodePosition ( c_p ) ;
229269 let [ qX , qY ] = vis . graph . getNodePosition ( c_q ) ;
230- let wX = qX + 5 * ( qX - pX ) ; // XXX
231- let wY = qY + 5 * ( qY - pY ) ;
232- vis . graph . setNodePosition ( 'W' , wX , wY ) ;
270+ // XXX wrapper position should be colinear but far away
271+ // Currently if p and q are close, wrapper may be too
272+ // close; also need to consider case where p=q...?
273+ let wX = qX + 50 * ( qX - pX ) ;
274+ let wY = qY + 50 * ( qY - pY ) ;
275+ vis . graph . setNodePosition ( wrapperStr , wX , wY ) ;
233276 } ,
234277 [ E , coords , p , q ] , 0
235278 ) ;
236279 // Now q is the most clockwise with
237280 // respect to p. Set p as q for next iteration,
238281 // so that q is added to result 'hull'
239282 p = q ;
283+ chunker . add ( 'whileP' ) ;
240284
241285 } while ( p != l ) ; // While we don't come to first
242286 // point
243287 // console.log("Reached (" + coords[p][X] + ", " + coords[p][Y] + ") again");
244- chunker . add ( 'start ' ,
288+ chunker . add ( 'returnHull ' ,
245289 ( vis , edgeArray , coordsArray , c_p , c_q ) => {
246- vis . graph . removeEdge ( c_p , 'W' ) ;
247- vis . graph . removeNode ( 'W' ) ;
290+ vis . graph . removeEdge ( c_p , wrapperStr ) ;
291+ vis . graph . removeNode ( wrapperStr ) ;
248292 } ,
249293 [ E , coords , p , q ] , 0
250294 ) ;
@@ -284,8 +328,8 @@ export default {
284328 vis . graph . set ( edgeArray , Array . from ( { length : numVertices } , ( v , k ) => ( k + 1 ) ) , coordsArray ) ;
285329 // vis.graph.edges = []
286330 // Add special node a long way away for "wrapper"
287- vis . graph . addNode ( 'W' , 'W' ) ;
288- vis . graph . setNodePosition ( 'W' , 10 , - 900 ) ;
331+ vis . graph . addNode ( wrapperStr , wrapperStr ) ;
332+ vis . graph . setNodePosition ( wrapperStr , 10 , - 2000 ) ;
289333 } ,
290334 [ E , coords ] , 0
291335 ) ;
0 commit comments