Skip to content

Commit 1fed1ae

Browse files
committed
MSDRadixSort popper bugs ironed out
What a nightmare that was...
1 parent 9a8db78 commit 1fed1ae

File tree

1 file changed

+76
-68
lines changed

1 file changed

+76
-68
lines changed

src/algorithms/controllers/MSDRadixSort.js

Lines changed: 76 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,11 @@ export default {
318318
let j; // the recursive calls; XX best rename - "i" too generic
319319
let prev_i; // for unhighlighting
320320
let prev_j; // for unhighlighting
321+
// search for POPPERS: below for more detailed comments
321322
let floatingBoxes = new Array(n); // XXX popper instances (rename)
323+
let DELAY_POPPER_CREATE = 600;
324+
let DELAY_POPPER_RESET = 700;
325+
let DELAY_POPPER_SWAP = 700;
322326

323327
const partition = (arr, left, right, mask, depth) => {
324328
i = left
@@ -353,8 +357,8 @@ cur_i, cur_j, cur_depth, A) => {
353357
// The solution we use here is to schedule a forceUpdate()
354358
// after a bit of a delay - seems to work ok on some
355359
// devices at least...
356-
setTimeout( () => floatingBoxes[_n1].forceUpdate(), 900);
357-
setTimeout( () => floatingBoxes[_n2].forceUpdate(), 900);
360+
setTimeout( () => floatingBoxes[_n1].forceUpdate(), DELAY_POPPER_SWAP);
361+
setTimeout( () => floatingBoxes[_n2].forceUpdate(), DELAY_POPPER_SWAP);
358362
},
359363
[n1, n2, real_stack, finished_stack_frames, i, j, depth,
360364
arr],
@@ -433,90 +437,94 @@ arr],
433437
finished_stack_frames.push(real_stack.pop());
434438
}
435439

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.
440+
// XXX probably should rename floatingBoxes to something like poppers.
441+
// POPPERS:
442+
// Handling is rather tricky. We have the global array of poppers,
443+
// which display the binary version of the data when the mouse is
444+
// over the data. They are derived from the HTML displayed (the
445+
// array renderer puts wrappers around array elements). However,
446+
// the display of HTML is asynchronous, making things a bit of a
447+
// nightmare, to put it mildly. When the algorithm is initially
448+
// loaded/run, the array is filled with nulls. When the first
449+
// chunk is executed for the first time, a popper is created for
450+
// each data item and put in the array. A carefully crafted delay
451+
// (using setTimeout) is inserted before popper creation to allow
452+
// the HTML array contents to be rendered first. When array
453+
// elements are swapped, the wrappers that the poppers rely on are
454+
// swapped with them so the poppers automatically follow the data
455+
// around... almost. They need to update their location (so the
456+
// popper appears next to the current location of the data, not
457+
// where the data used to be or some other random point); we use
458+
// forceUpdate(), which also needs to be inside a setTimeout to
459+
// allow rendering to take place first. Just to add to the
460+
// complexity, if the execution steps backwards, all the chunks
461+
// are re-executed, starting from the first one. The first chunk
462+
// therefore checks if poppers have already been created and, if
463+
// so, calls forceUpdate() (inside setTimeout of course) for each
464+
// popper so it gets the right screen location.
465+
// XXX It would probably be nicer to allow users to click on a
466+
// data element and have that displayed with the mask etc. This
467+
// was implemented in the BROKEN-radix-click4binary branch but it
468+
// somehow breaks react - no further stepping through the
469+
// animation is possible after a click:(
445470
floatingBoxes.fill(null);
446471

472+
// We don't display maxIndex yet but use it for creating poppers
473+
// with the desired number of bits. We want to create the poppers
474+
// in the first chunk so there is a bit of a delay before anything
475+
// else happens.
476+
maxIndex = A.indexOf(Math.max(...A))
477+
const mask = getMaximumBit(A);
478+
447479
// Initialise the array on start
448480
chunker.add(MSD_BOOKMARKS.start,
449-
(vis, array) => {
481+
(vis, array, mask) => {
450482
vis.array.set(array, 'MSDRadixSort')
451483
vis.array.setSize(5); // more space for array
452484
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
485+
// set up poppers
486+
// A bit of a nightmare due to asynchronous programming. The
487+
// first time this is called we create the poppers (after a
488+
// delay); subsequently we just update them (after a delay).
489+
// XXX could just use one setTimeout for all the poppers
490+
for (let idx = 0; idx < array.length; idx++) {
491+
if (floatingBoxes[idx] === null) {
492+
setTimeout( () => {
493+
const popper = document.getElementById('float_box_' + idx);
494+
const slot = document.getElementById('chain_' + idx);
495+
floatingBoxes[idx] = createPopper(slot, popper, {
496+
placement: "right-start",
497+
strategy: "fixed",
498+
modifiers: [
499+
{
500+
removeOnDestroy: true, // doesn't work well?
501+
name: 'preventOverflow',
502+
options: {
503+
// XXX popper_boundary not defined for 1D
504+
// array - maybe it should be??
505+
boundary: document.getElementById('popper_boundary'),
506+
},
507+
},
508+
]
509+
});
510+
popper.innerHTML = array[idx].toString(2).padStart(mask + 1, "0");
511+
}, DELAY_POPPER_CREATE);
512+
} else {
513+
setTimeout( () => floatingBoxes[idx].forceUpdate(), DELAY_POPPER_RESET);
461514
}
462-
});
515+
}
463516
},
464-
[nodes],
517+
[A, mask],
465518
0
466519
)
467520

468-
maxIndex = A.indexOf(Math.max(...A))
469-
const mask = getMaximumBit(A);
470-
471-
// Highlight the index
521+
// Highlight the index of the max element + init mask
472522
chunker.add(MSD_BOOKMARKS.get_mask,
473523
(vis, maxIndex, mask, A) => {
474524
highlight(vis, maxIndex)
475525
vis.mask.setMaxBits(mask + 1)
476526
updateMask(vis, mask)
477527
updateBinary(vis, A[maxIndex])
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++) {
488-
const popper = document.getElementById('float_box_' + idx);
489-
const slot = document.getElementById('chain_' + idx);
490-
floatingBoxes[idx] = createPopper(slot, popper, {
491-
placement: "right-start",
492-
strategy: "fixed",
493-
modifiers: [
494-
{
495-
removeOnDestroy: true, // doesn't work well?
496-
name: 'preventOverflow',
497-
options: {
498-
// XXX popper_boundary not defined for 1D
499-
// array - maybe it should be??
500-
boundary: document.getElementById('popper_boundary'),
501-
},
502-
},
503-
]
504-
});
505-
popper.innerHTML = A[idx].toString(2).padStart(mask + 1, "0");
506-
}
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-
*/
520528
},
521529
[maxIndex, mask, A],
522530
0

0 commit comments

Comments
 (0)