Skip to content

Commit 8a71b90

Browse files
committed
gwrap pseudocode partly linked...
1 parent b304fb2 commit 8a71b90

File tree

2 files changed

+109
-64
lines changed

2 files changed

+109
-64
lines changed

src/algorithms/controllers/gwrap.js

Lines changed: 99 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,50 @@
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+
1046
import GraphTracer from '../../components/DataStructures/Graph/GraphTracer';
11-
import Array2DTracer from '../../components/DataStructures/Array/Array2DTracer';
1247
import {colorsCH} from './convexHullColours';
13-
import {colors} from './graphSearchColours'; // XX
1448

1549
export 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
);

src/algorithms/pseudocode/gwrap.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ export default parse(`
44
55
\\Note{
66
Gift wrapping/Jarvis algm for convex hull
7+
XXX probably needs more bookmarks
78
8-
Includes optional optimisation - ignore initially, maybe fill in later
9-
and have option selected like path compression in union find algm.
9+
Includes sketch of optional optimisation - ignore initially, maybe fill in
10+
later and have option selected like path compression in union find algm.
1011
1112
Should be able to use graph package (disable edge input; maybe make
1213
nodes smaller); add/delete edges during algorithm execution.
@@ -49,10 +50,10 @@ giftWrap(P, n) // return convex hull of points P[0]...P[n-1] in a plane \\B star
4950
Note: going from p to q to any other point x requires a clockwise
5051
turn.
5152
\\Expl}
52-
p <- q
53+
p <- q \\B p<-q
5354
\\In}
54-
while p != minX // stop when we get back to the first node
55-
return hull
55+
while p != minX // stop when we get back to the first node \\B whileP
56+
return hull \\B returnHull
5657
\\In}
5758
\\Code}
5859
@@ -82,11 +83,11 @@ NextPoint
8283
clockwise than i. If no such i exists then q is the least clockwise
8384
point after p.
8485
\\Expl}
85-
q <- (p + 1) mod n // initialise q to a point other than p
86+
q <- (p + 1) mod n // initialise q to a point other than p \\B initQ
8687
\\Expl{ Any point other than p will work. Here we pick the next point
8788
in the array (or 0 if p is the last point).
8889
\\Expl}
89-
for i <- point in P
90+
for i <- point in P \\B assignI
9091
\\Expl{ We loop over all points. We could ignore point p but it
9192
does no harm. The path p->p->q is considered straight. Geometric
9293
algorithms often have tricky cases such as this. Another subtlety is
@@ -99,7 +100,7 @@ for i <- 0 to n-1
99100
\\In{
100101
if p->i->q is a clockwise turn \\Ref piqClockwise
101102
\\In{
102-
q <- i
103+
q <- i \\B q<-i
103104
\\Expl{ i is less clockwise than q, so we update q.
104105
\\Expl}
105106
\\In}
@@ -109,7 +110,7 @@ for i <- 0 to n-1
109110
\\Code{
110111
piqClockwise
111112
// check if the cross product of vectors pi and pq is positive
112-
if (i.y-p.y)*(q.x-i.x) - (i.x-p.x)*(q.y-i.y) > 0
113+
if (i.y-p.y)*(q.x-i.x) - (i.x-p.x)*(q.y-i.y) > 0 \\B piqTest
113114
\\Expl{ See the "BACKGROUND" and "MORE" tabs for more details. Code in
114115
geometric algorithms often has cryptic bits like this based on
115116
mathematical results from geometry. For many operations, the most

0 commit comments

Comments
 (0)