Skip to content

Commit 18ed189

Browse files
committed
AVL smoother rotation
1 parent 6fbda1f commit 18ed189

File tree

2 files changed

+133
-49
lines changed

2 files changed

+133
-49
lines changed

src/algorithms/controllers/AVLTreeInsertion.js

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,22 @@ export default {
6363
/**
6464
* compute node positions during rotation
6565
* given x-y coordinates of the root and the child, compute the
66-
* intermediate position after step 1 or step 2
66+
* intermediate position after step 1, step 2, ...
6767
* coords represented as record with fields rX, rY, cX, cY (root X&Y,
6868
* child X&Y)
69+
* Steps 1&2:
6970
* We keep the edge the same length and rotate it around the point 40%
7071
* along the edge from the root. If initially the y coordinate of the
7172
* root is 1 and the child is zero, after step 1 the root is at 2/3
7273
* and the child is at 1/2 and after step 2 the root is at 1/3 and the
7374
* child is at 1. The x coordinates are computed so as to keep the length
7475
* of the edge same (magic numbers derived from maths).
7576
* returns record with new coordinates
77+
* Further steps:
78+
* Not coded here but in the next steps the (new) root is moved
79+
* more towards the center and the (new) child (and it's child?)
80+
* is moved down.
81+
*
7682
*/
7783
function rotateStep(pos0, step) {
7884

@@ -82,14 +88,16 @@ export default {
8288
// edge length = sqrt(deltaX**2 + deltaY**2)
8389
let deltaX1; // deltaX for new position
8490
let pos1 = {rX:0, rY:0, cX:0, cY:0};
85-
if (step == 1) {
91+
if (step === 1) {
8692
pos1.rY = (2*rY + cY) / 3;
8793
pos1.cY = (rY + cY) / 2;
8894
deltaX1 = Math.sqrt((35/36)*deltaY**2 + deltaX**2);
89-
} else { // assume step == 2
95+
} else if (step === 2) {
9096
pos1.rY = (rY + 2*cY) / 3;
9197
pos1.cY = rY;
9298
deltaX1 = Math.sqrt((5/9)*deltaY**2 + deltaX**2);
99+
} else {
100+
console.log('Invalid rotateStep step ', step);
93101
}
94102
if (rX > cX) // reverse direction of deltaX
95103
deltaX1 = -deltaX1;
@@ -100,13 +108,16 @@ export default {
100108

101109
// We apply the following char notations in our rotation code.
102110
// It helps code reusability and readability.
111+
// XXX might be good to change some of these (RR+RL added
112+
// later); note that P is not necessarilty the *global* root, R
113+
// can stand for root and right.
103114
// tNum / charNotation
104115
//
105116
// P/G (Global root /Parent) P/G
106117
// | |
107118
// 6/R 2/R
108119
// / \ Right Rotation / \
109-
// 2/A 7 - - - - - - - > 1 6/A
120+
// 2/A 7/RR - - - - - - - > 1/RL 6/A
110121
// / \ < - - - - - - - / \
111122
// 1 4/D Left Rotation 4/D 7
112123

@@ -176,8 +187,6 @@ export default {
176187
}
177188

178189
// -- following code is for visualising the rotation step by step --
179-
let rootNode = vis.graph.findNode(tt2);
180-
let leafNode = vis.graph.findNode(tt2);
181190
// freeze the layout to avoid the nodes moving automatically
182191
vis.graph.setPauseLayout(true);
183192
let pos0 = vis.graph.getRotPos(); // original position
@@ -192,7 +201,6 @@ export default {
192201
// let t2's right child point to t6
193202
chunker.add('t2.right = t6',
194203
(vis, t6, t2, t4, p, rotate) => {
195-
// highlight the edge between t2 and t6
196204
if (rotate) vis.graph.visit(t2, p);
197205

198206
vis.graph.removeEdge(t6, t2);
@@ -206,24 +214,42 @@ export default {
206214
vis.graph.addEdge(t2, t6);
207215

208216
if (t4 !== null) vis.graph.removeEdge(t6, t4);
217+
// set the new position of the node(s)
218+
// - just move (new) root node towards center more.
219+
// New child and grandchild moved in next chunk -
220+
// could do them here also but less confusing as is?
221+
let pos0 = vis.graph.getRotPos(); // original position
222+
let pos2 = rotateStep(pos0, 2); // previous position
223+
vis.graph.setNodePosition(t2, (pos0.rX + 3*pos0.cX)/4, pos2.cY);
209224
},
210225
[R.key, A.key, D ? D.key : null, parentNode ? parentNode.key : null, rotateVis],
211226
depth
212227
);
213228

229+
let RR = (R.right? R.right.key: null);
214230
// if t4 is not null, let t6's left child point to t4
215231
// if (D) { // we now animate this step even if t4 is null
216-
chunker.add('t6.left = t4',
217-
(vis, r, d) => {
218-
if (d !== null) vis.graph.addEdge(r, d);
219-
},
220-
[R.key, D ? D.key : null],
221-
depth
222-
)
223-
// }
232+
chunker.add('t6.left = t4',
233+
(vis, t6, t6r, t2, t4, p, rotate) => {
234+
// vis.graph.setMoveRatio(9/10);
235+
if (t4 !== null) vis.graph.addEdge(t6, t4);
236+
// set the new position of the nodes
237+
// - just move (new) child node down more
238+
// plus it's child also (if it has one)
239+
let pos0 = vis.graph.getRotPos(); // original position
240+
let pos2 = rotateStep(pos0, 2); // position after step 2
241+
let deltaY = (pos2.rY - pos0.rY) / 3;
242+
vis.graph.setNodePosition(t6, pos2.rX, pos2.rY + deltaY);
243+
if (t6r !== null) {
244+
vis.graph.moveNodePosition(t6r, 0, deltaY);
245+
}
246+
},
247+
[R.key, RR, A.key, D ? D.key : null, parentNode ? parentNode.key : null, rotateVis],
248+
depth
249+
)
224250

225251
// perform the rotation in our objects
226-
const temp = root.left;
252+
const temp = root.left; // XXX temp = A ???
227253
root.left = temp.right;
228254
temp.right = root;
229255
updateHeight(root);
@@ -252,15 +278,20 @@ export default {
252278

253279
// finalise the rotation
254280
chunker.add('return t2',
255-
(vis, p, t2, t6) => {
281+
(vis, g, p, t2, t6) => {
256282
// vis.graph.clearTID();
283+
console.log(g, p, t2, t6);
257284
vis.graph.updateTID(t2, 't2');
258285
if (p !== null) {
259286
vis.graph.removeEdge(p, t6);
260287
vis.graph.addEdge(p, t2);
261288
}
289+
vis.graph.setMoveRatio(3/6);
290+
vis.graph.setPauseLayout(false);
291+
vis.graph.layoutAVL(g, true, false);
292+
vis.graph.rectangle_size();
262293
},
263-
[parentNode ? parentNode.key : null, A.key, R.key],
294+
[parentNode ? globalRoot.key : temp.key, parentNode ? parentNode.key : null, A.key, R.key],
264295
depth
265296
);
266297

@@ -360,20 +391,38 @@ export default {
360391
vis.graph.addEdge(t6, t2);
361392
// remove edge after layout to perform the middle step
362393
if (t4 !== null) vis.graph.removeEdge(t2, t4);
394+
// set the new position of the nodes
395+
// - just move (new) root node towards center more.
396+
// New child and grandchild moved in next chunk -
397+
// could do them here also but less confusing as is?
398+
let pos0 = vis.graph.getRotPos(); // original position
399+
let pos2 = rotateStep(pos0, 2); // previous position
400+
vis.graph.setNodePosition(t6, (pos0.rX + 3*pos0.cX)/4, pos2.cY);
363401
},
364402
[R.key, A.key, D ? D.key : null, parentNode ? parentNode.key : null, rotateVis],
365403
depth
366404
);
367405

406+
let RL = (R.left? R.left.key: null);
368407
// if t4 is not null, let t2's right child point to t4
369408
// if (D) { // we now animate this step even if t4 is null
370-
chunker.add('t2.right = t4',
371-
// reconnect the edge between t2 and t4
372-
(vis, r, d) => {
373-
if (d !== null) vis.graph.addEdge(r, d);
374-
},
375-
[R.key, D ? D.key : null],
376-
depth
409+
chunker.add('t2.right = t4',
410+
// reconnect the edge between t2 and t4
411+
(vis, t2, t2l, t6, t4, p, rotate) => {
412+
if (t4 !== null) vis.graph.addEdge(t2, t4);
413+
// set the new position of the nodes
414+
// - just move (new) child node down more
415+
// plus it's child also (if it has one)
416+
let pos0 = vis.graph.getRotPos(); // original position
417+
let pos2 = rotateStep(pos0, 2); // position after step 2
418+
let deltaY = (pos2.rY - pos0.rY) / 3;
419+
vis.graph.setNodePosition(t2, pos2.rX, pos2.rY + deltaY);
420+
if (t2l !== null) {
421+
vis.graph.moveNodePosition(t2l, 0, deltaY);
422+
}
423+
},
424+
[R.key, RL, A.key, D ? D.key : null, parentNode ? parentNode.key : null, rotateVis],
425+
depth
377426
)
378427
// }
379428

@@ -407,15 +456,19 @@ export default {
407456

408457
// finalise the rotation
409458
chunker.add('return t6',
410-
(vis, p, t6, t2) => {
459+
(vis, g, p, t6, t2) => {
411460
// vis.graph.clearTID();
412461
vis.graph.updateTID(t6, 't6');
413462
if (p !== null) {
414463
vis.graph.removeEdge(p, t2);
415464
vis.graph.addEdge(p, t6);
416465
}
466+
vis.graph.setMoveRatio(3/6);
467+
vis.graph.setPauseLayout(false);
468+
vis.graph.layoutAVL(g, true, false);
469+
vis.graph.rectangle_size();
417470
},
418-
[parentNode ? parentNode.key : null, A.key, R.key],
471+
[parentNode ? globalRoot.key : temp.key, parentNode ? parentNode.key : null, A.key, R.key],
419472
depth
420473
);
421474
return temp; // new root node after the rotation
@@ -442,6 +495,7 @@ export default {
442495
vis.graph.updateHeight(r.key, r.height);
443496
vis.graph.setPauseLayout(false);
444497
vis.graph.layoutAVL(g, true, false);
498+
vis.graph.rectangle_size();
445499
}, [(parentNode !== null) ? globalRoot.key : root.key, root.left], depth);
446500

447501
// chunker.add('return right rotation on t', (vis) => { }, [], depth);
@@ -471,9 +525,9 @@ export default {
471525
vis.graph.updateHeight(r.key, r.height);
472526
vis.graph.setPauseLayout(false);
473527
vis.graph.layoutAVL(g, true, false);
528+
vis.graph.rectangle_size();
474529
}, [(parentNode !== null) ? globalRoot.key : root.key, root.right], depth);
475530

476-
// chunker.add('return left rotation on t', (vis) => { }, [], depth);
477531

478532
// perform left rotation on the root node
479533
return RRCR(root, parentNode, depth, true);
@@ -548,7 +602,7 @@ export default {
548602
);
549603
*/
550604

551-
// update the chilld of the parent node
605+
// update the child of the parent node
552606
if (parentNode !== null) {
553607
if (key < parentNode.key) {
554608
parentNode.left = root;
@@ -664,6 +718,7 @@ export default {
664718
// clear the function information after the rotation
665719
// and tidy up the nodes' position
666720
chunker.add('return rightRotate(t)', (vis, g, r) => {
721+
vis.graph.setMoveRatio(1);
667722
vis.graph.updateHeight(r.key, r.height);
668723
vis.graph.setFunctionNode(null);
669724
vis.graph.clearSelect_Circle_Count();
@@ -700,6 +755,7 @@ export default {
700755
// clear the function information after the rotation
701756
// and tidy up the nodes' position
702757
chunker.add('return leftRotate(t)', (vis, g, r) => {
758+
vis.graph.setMoveRatio(1);
703759
vis.graph.updateHeight(r.key, r.height);
704760
// vis.graph.clearTID();
705761
vis.graph.setFunctionNode(null);
@@ -736,6 +792,7 @@ export default {
736792
// clear the function information after the rotation
737793
chunker.add('return rightRotate(t) after leftRotate',
738794
(vis, g, r) => {
795+
vis.graph.setMoveRatio(1);
739796
vis.graph.updateHeight(r.key, r.height);
740797
// vis.graph.clearTID();
741798
vis.graph.setFunctionNode(null);
@@ -775,6 +832,7 @@ export default {
775832
// clear the function information after the rotation
776833
chunker.add('return leftRotate(t) after rightRotate',
777834
(vis, g, r) => {
835+
vis.graph.setMoveRatio(1);
778836
vis.graph.updateHeight(r.key, r.height);
779837
// vis.graph.clearTID();
780838
vis.graph.setFunctionNode(null);
@@ -804,6 +862,8 @@ export default {
804862
vis.graph.isWeighted = true;
805863
vis.graph.setFunctionName('Tree is Empty');
806864
vis.graph.setFunctionInsertText(``);
865+
vis.graph.setPauseLayout(false);
866+
vis.graph.setMoveRatio(1);
807867
vis.array.patch(0);
808868
// make a bit more room for tree
809869
vis.graph.setSize(2.5);

0 commit comments

Comments
 (0)