Skip to content

Commit 9a8db78

Browse files
committed
MSD radix ok with poppers except for slider
1 parent 8395ffe commit 9a8db78

File tree

7 files changed

+112
-80
lines changed

7 files changed

+112
-80
lines changed

src/algorithms/controllers/HashingChainingInsertion.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ export default {
155155
},
156156
]
157157
});
158+
// vis.array.setPopper(idx, floatingBoxes[idx]);
158159
}
159160
popper.innerHTML = table[idx];
160161
}
@@ -218,7 +219,7 @@ export default {
218219
const slot = document.getElementById('chain_' + inserts[key]);
219220
floatingBoxes[inserts[key]] = createPopper(slot, popper, {
220221
placement: "right-start",
221-
strategy: "fixed",
222+
strategy: "absolute",
222223
modifiers: [
223224
{
224225
name: 'preventOverflow',
@@ -255,6 +256,7 @@ export default {
255256
let prevIdx;
256257

257258
let floatingBoxes = new Array(SIZE); // List of all popper instances
259+
floatingBoxes.fill(null);
258260

259261
// Init hash table with dynamic array in each slot
260262
let table = new Array(SIZE);
@@ -291,6 +293,16 @@ export default {
291293

292294
// vis.array.hideArrayAtIndex([VALUE, POINTER]); // Hide value and pointer row intially
293295
vis.array.hideArrayAtIndex([POINTER]); // Hide pointer row intially
296+
// destroy existing poppers, replace with null
297+
floatingBoxes.forEach((p) => {
298+
if (p !== null) {
299+
console.log('destroy', p.state);
300+
p.state.elements.popper.innerHTML = ""; // reset HTML
301+
p.destroy(); // remove popper
302+
return null; // array el. = null
303+
}
304+
});
305+
// vis.array.setPoppers(floatingBoxes); // init with no poppers
294306

295307
vis.graph.weighted(true);
296308

@@ -310,9 +322,10 @@ export default {
310322
break;
311323
}
312324
},
313-
[table.length, table.length <= PRIMES[POINTER_CUT_OFF] ?
314-
[indexArr, valueArr, nullArr] :
315-
[indexArr, valueArr]
325+
[table.length,
326+
table.length <= PRIMES[POINTER_CUT_OFF] ?
327+
[indexArr, valueArr, nullArr] :
328+
[indexArr, valueArr]
316329
]
317330
);
318331

src/algorithms/controllers/MSDRadixSort.js

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -299,21 +299,6 @@ export default {
299299
STACK_FRAME_COLOR.Current_stackFrameR,
300300
);
301301
}
302-
303-
if (cur_depth === undefined) {
304-
return stack_vis;
305-
}
306-
307-
if (!isPartitionExpanded()) { return stack_vis; }
308-
309-
// if (cur_i >= 0 && cur_i < n) {
310-
// stack_vis[cur_depth][cur_i].extra.push(STACK_FRAME_COLOR.I_color);
311-
// }
312-
313-
// if (cur_j >= 0 && cur_j < n) {
314-
// stack_vis[cur_depth][cur_j].extra.push(STACK_FRAME_COLOR.J_color);
315-
// }
316-
317302
return stack_vis;
318303
}
319304

@@ -333,13 +318,14 @@ export default {
333318
let j; // the recursive calls; XX best rename - "i" too generic
334319
let prev_i; // for unhighlighting
335320
let prev_j; // for unhighlighting
321+
let floatingBoxes = new Array(n); // XXX popper instances (rename)
322+
336323
const partition = (arr, left, right, mask, depth) => {
337324
i = left
338325
j = right
339326

340327
const partitionChunkerWrapper = (bookmark) => {
341-
partitionChunker(bookmark, i, j, prev_i, prev_j, left, right,
342-
depth, arr, mask)
328+
partitionChunker(bookmark, i, j, prev_i, prev_j, left, right, depth, arr, mask)
343329
}
344330

345331
function swapAction(bookmark, n1, n2) {
@@ -350,19 +336,28 @@ depth, arr, mask)
350336
[arr[n1], arr[n2]] = [arr[n2], arr[n1]]
351337

352338
chunker.add(bookmark,
353-
(vis, _n1, _n2, cur_real_stack, cur_finished_stack_frames, cur_i, cur_j, cur_depth, A) => {
339+
(vis, _n1, _n2, cur_real_stack, cur_finished_stack_frames,
340+
cur_i, cur_j, cur_depth, A) => {
354341

355342
vis.array.swapElements(_n1, _n2);
356343
refreshStack(vis, cur_real_stack, cur_finished_stack_frames, cur_i, cur_j, prev_i, prev_j, left, right, cur_depth, false, undefined, A, mask)
357-
// XXX redo poppers: swapping the elements keeps the
344+
// redo poppers: swapping the elements keeps the
358345
// contents of the poppers correct but the position needs
359-
// to change (the documentation suggests update()
346+
// to change. The documentation suggests update()
360347
// should work and it does partially but it also screws up
361-
// sometimes)
362-
// vis.array.poppers[_n1].update();
363-
// vis.array.poppers[_n2].update();
348+
// the position sometimes, particularly for _n1; it
349+
// returns a promise but we can't use await here. The
350+
// contents also gets screwed up sometimes stepping
351+
// backwards (stepping backwards again seems to help). The
352+
// wonders of asynchronous programming...
353+
// The solution we use here is to schedule a forceUpdate()
354+
// after a bit of a delay - seems to work ok on some
355+
// devices at least...
356+
setTimeout( () => floatingBoxes[_n1].forceUpdate(), 900);
357+
setTimeout( () => floatingBoxes[_n2].forceUpdate(), 900);
364358
},
365-
[n1, n2, real_stack, finished_stack_frames, i, j, depth, arr],
359+
[n1, n2, real_stack, finished_stack_frames, i, j, depth,
360+
arr],
366361
depth);
367362
}
368363

@@ -411,19 +406,6 @@ depth, arr, mask)
411406
// Base case: If the array has 1 or fewer elements or mask is less than 0, stop
412407
partitionChunker(MSD_BOOKMARKS.rec_function, undefined, undefined, undefined, undefined, left, right, depth, arr, mask)
413408
maxIndex = undefined; // defined only for top level call
414-
/*
415-
chunker.add(MSD_BOOKMARKS.rec_function, (vis, left, right) => {
416-
if (left < n)
417-
assignVariable(vis, VIS_VARIABLE_STRINGS.left, left);
418-
if (right >= 0)
419-
assignVariable(vis, VIS_VARIABLE_STRINGS.right, right);
420-
updateMask(vis, mask)
421-
422-
for (let i = 0; i < n; i++) {
423-
unhighlight(vis, i);
424-
}
425-
}, [left, right], depth)
426-
*/
427409
chunker.add(MSD_BOOKMARKS.base_case, (vis) => {}, [], depth)
428410

429411
if (left < right && mask >= 0) {
@@ -451,12 +433,33 @@ depth, arr, mask)
451433
finished_stack_frames.push(real_stack.pop());
452434
}
453435

436+
// XXX probably should rename to something like poppers
437+
// Handling is rather tricky. We have the global array of poppers
438+
// which is initially all null. When the second chunk is executed,
439+
// poppers are created and when later chunks are executed things
440+
// can move around. When we step backwards, we go back and execute
441+
// from the first chunk, so the first chunk cleans up and destroys
442+
// all the existing poppers and later chunks re-create them. To
443+
// make things more complicated, its all asynchronous, so we put
444+
// delays in to (hopefully) stop it screwing up.
445+
floatingBoxes.fill(null);
446+
454447
// Initialise the array on start
455448
chunker.add(MSD_BOOKMARKS.start,
456449
(vis, array) => {
457450
vis.array.set(array, 'MSDRadixSort')
458451
vis.array.setSize(5); // more space for array
459452
vis.array.setZoom(0.90);
453+
// destroy existing poppers, replace with null
454+
floatingBoxes.forEach((p) => {
455+
if (p !== null) {
456+
console.log('popper gone');
457+
p.state.elements.popper.innerHTML = ""; // reset HTML
458+
p.forceUpdate();
459+
p.destroy(); // remove popper
460+
return null; // array el. = null
461+
}
462+
});
460463
},
461464
[nodes],
462465
0
@@ -472,43 +475,54 @@ depth, arr, mask)
472475
vis.mask.setMaxBits(mask + 1)
473476
updateMask(vis, mask)
474477
updateBinary(vis, A[maxIndex])
475-
let floatingBoxes = new Array(n); // List of all popper instances
476-
for (let idx = 0; idx < A.length; idx++) {
478+
// set up poppers
479+
// A bit of a nightmare due to asynchronous programming. If
480+
// we have stepped backwards the poppers have been reset and
481+
// destroyed but if we immediately create new poppers some
482+
// of the old state persists. If we wait a while then create
483+
// them it seems to work on some devices at least...
484+
// XXX do popper.innerHTML = immediately; use setTimeout for createPopper
485+
// XXX have array for the popper.innerHTML stuff?,
486+
setTimeout( () => {
487+
for (let idx = 0; idx < A.length; idx++) {
477488
const popper = document.getElementById('float_box_' + idx);
478489
const slot = document.getElementById('chain_' + idx);
479-
// vis.array.setPopper(idx, createPopper(slot, popper, {
480-
floatingBoxes[idx] = createPopper(slot, popper, {
490+
floatingBoxes[idx] = createPopper(slot, popper, {
481491
placement: "right-start",
482492
strategy: "fixed",
483493
modifiers: [
484494
{
495+
removeOnDestroy: true, // doesn't work well?
485496
name: 'preventOverflow',
486497
options: {
487-
// popper_boundary not defined for 1D
498+
// XXX popper_boundary not defined for 1D
488499
// array - maybe it should be??
489-
// boundary: document.getElementById('popper_boundary'),
500+
boundary: document.getElementById('popper_boundary'),
490501
},
491502
},
492503
]
493504
});
494505
popper.innerHTML = A[idx].toString(2).padStart(mask + 1, "0");
495506
}
496-
vis.array.setPoppers(floatingBoxes);
507+
}, 1000);
508+
/*
509+
console.log(floatingBoxes);
510+
setTimeout( () => {
511+
console.log(floatingBoxes);
512+
floatingBoxes.forEach((p) => {
513+
if (p !== null) {
514+
p.setOptions({placement: "right-start"});
515+
p.forceUpdate()
516+
}
517+
})
518+
}, 2000);
519+
*/
497520
},
498521
[maxIndex, mask, A],
499522
0
500523
)
501-
/*
502-
chunker.add(MSD_BOOKMARKS.rec_function,
503-
(vis, maxIndex) => {
504-
unhighlight(vis, maxIndex)
505-
}, [maxIndex],
506-
0
507-
)
508-
*/
509524
msdRadixSortRecursive(A, 0, n-1, mask, 0);
510525

511-
512526
chunker.add(MSD_BOOKMARKS.done,
513527
vis => {
514528
vis.array.setStackDepth(0)

src/components/DataStructures/Array/Array1DRenderer/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import styles from './Array1DRenderer.module.scss';
2727
import { classes } from '../../common/util';
2828
import { mode } from '../../../top/Settings';
2929
// Add your algo to this if you want to use the float box/popper
30+
// XXX add straight radix sort some time?
3031
const ALGOS_USING_FLOAT_BOX = ["MSDRadixSort"];
3132

3233
let modename;

src/components/DataStructures/Array/Array1DTracer.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ class Array1DTracer extends Array2DTracer {
2020
init() {
2121
super.init();
2222
this.chartTracer = null;
23-
this.poppers = [];
2423
}
2524

2625
set(array1d = [], algo) {
@@ -142,22 +141,6 @@ class Array1DTracer extends Array2DTracer {
142141
this.stackDepth = depth;
143142
}
144143

145-
// XXX move to super
146-
// XXX should kill existing poppers
147-
setPoppers(poppers) {
148-
this.poppers = poppers;
149-
}
150-
151-
setPopper(i, popper) {
152-
this.poppers[i] = popper;
153-
}
154-
155-
// NQR and not currently used
156-
/*
157-
setPopperHTML(i, HTML) {
158-
this.poppers[i].innerHTML = HTML;
159-
}
160-
*/
161144

162145
// default is to compute largestColumnValue but we can set it
163146
// explicitly so we can make two arrays look the same when moving

src/components/DataStructures/Array/Array2DTracer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ class Array2DTracer extends Tracer {
5353
return Array2DRenderer;
5454
}
5555

56+
init() {
57+
super.init();
58+
}
59+
5660
/**
5761
* @param {array} array2d
5862
* @param {string} algo used to mark if it is a specific algorithm

src/components/mid-panel/ProgressBar.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class ProgressBar extends React.Component {
1717
this.current;
1818
this.viewable;
1919
this.next;
20-
this.prev;
20+
this.backTo;
2121

2222
this.inner;
2323
}
@@ -33,7 +33,7 @@ class ProgressBar extends React.Component {
3333
let chunkNum;
3434

3535
// how far around mouse on X to look for viewable chunk
36-
let searchRadius = 10;
36+
let searchRadius = 60;
3737
let rect = this.inner.getBoundingClientRect();
3838
let width = rect.right - rect.left;
3939

@@ -79,7 +79,7 @@ class ProgressBar extends React.Component {
7979
this.next({stopAt: chunkNum, playing: false});
8080
}
8181
if (chunkNum < this.current && chunkNum !== this.max - 1) {
82-
this.prev({stopAt: chunkNum, playing: false});
82+
this.backTo({stopAt: chunkNum, playing: false});
8383
}
8484
}
8585
}
@@ -109,8 +109,8 @@ class ProgressBar extends React.Component {
109109
state.chunker.viewable :
110110
null;
111111

112-
this.prev = (playing) => {
113-
dispatch(GlobalActions.PREV_LINE, playing);
112+
this.backTo = (playing) => {
113+
dispatch(GlobalActions.BACK_TO_LINE, playing);
114114
};
115115

116116
this.next = (playing) => {

src/context/actions.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ export const GlobalActions = {
412412
} while (!state.chunker.viewable[stopAt])
413413
}
414414
}
415-
let result1 = { bookmark: "", chunk: state.chunker.currentChunk };
415+
// let result1 = { bookmark: "", chunk: state.chunker.currentChunk };
416416
const result = state.chunker.goBackTo(stopAt); // changes state
417417

418418
// const lineExplan = findBookmark(state.pseudocode, result.bookmark).explanation;
@@ -425,6 +425,23 @@ export const GlobalActions = {
425425
};
426426
},
427427

428+
// added to avoid O(N^2) behaviour for progress bar
429+
// Goes back to a line in one step
430+
BACK_TO_LINE: (state, playing) => {
431+
let stopAt;
432+
if (typeof playing === 'object') {
433+
stopAt = playing.stopAt;
434+
playing = playing.playing;
435+
} // XXX else error
436+
console.log(stopAt, playing, state);
437+
const result = state.chunker.goBackTo(stopAt); // changes state
438+
return {
439+
...state,
440+
...result,
441+
playing,
442+
};
443+
},
444+
428445
TOGGLE_PLAY: (state, playing) => ({
429446
...state,
430447
playing,

0 commit comments

Comments
 (0)