@@ -106,6 +106,7 @@ extension BaseNodeUpdateExtension on BaseNode {
106106 bool recursivelyCalculateChildrenGlobalBoxes = true ,
107107 Vec ? globalParentBoundingBoxPos,
108108 bool forceUpdateEdgePins = false ,
109+ bool updatingSortedNodeList = false ,
109110 }) =>
110111 NodeProcessor .updateNode (
111112 node: this ,
@@ -122,6 +123,7 @@ extension BaseNodeUpdateExtension on BaseNode {
122123 recursivelyCalculateChildrenGlobalBoxes,
123124 globalParentBoundingBoxPos: globalParentBoundingBoxPos,
124125 forceUpdateEdgePins: forceUpdateEdgePins,
126+ updatingSortedNodeList: updatingSortedNodeList,
125127 );
126128
127129 /// Sets rotation for this node.
@@ -132,24 +134,17 @@ extension BaseNodeUpdateExtension on BaseNode {
132134
133135 /// Updates rotation for this node recursively for its children as well if
134136 /// [updateChildren] is true.
135- void updateNodeRotation (int newRotationDegrees,
136- {bool updateChildren = true }) {
137+ void updateNodeRotation (
138+ int newRotationDegrees, {
139+ bool updateChildren = true ,
140+ }) {
137141 setNodeRotation (newRotationDegrees);
138142
139- if (updateChildren) {
140- NodeProcessor .updateGlobalRotation (
141- node: this , newRotationDegrees: newRotationDegrees);
142- } else {
143- if (id == kRootNode) {
144- globalRotationDegrees = newRotationDegrees;
145- globalRotationRadians = newRotationDegrees * pi / 180 ;
146- } else {
147- final BaseNode parent = NodeProcessor .getNode (parentID);
148- globalRotationDegrees =
149- parent.globalRotationDegrees + newRotationDegrees;
150- globalRotationRadians = globalRotationDegrees * pi / 180 ;
151- }
152- }
143+ NodeProcessor .updateGlobalRotation (
144+ node: this ,
145+ newRotationDegrees: newRotationDegrees,
146+ updateChildren: updateChildren,
147+ );
153148 }
154149}
155150
@@ -204,33 +199,36 @@ class NodeProcessor {
204199 static void updateGlobalRotation ({
205200 required BaseNode node,
206201 required int newRotationDegrees,
202+ bool updateChildren = true ,
207203 }) {
208204 if (node.id == kRootNode) {
209205 node.globalRotationDegrees = newRotationDegrees;
210206 node.globalRotationRadians = newRotationDegrees * pi / 180 ;
207+ node._rotatedTopLeftCorner = node.middleBoxLocal.topLeft;
211208 } else {
212209 final BaseNode parent = getNode (node.parentID);
210+
213211 node.globalRotationDegrees =
214212 parent.globalRotationDegrees + newRotationDegrees;
215213 node.globalRotationRadians = node.globalRotationDegrees * pi / 180 ;
216- }
217214
218- for (var childId in node.childrenOrEmpty) {
219- final child = getNode (childId);
220- updateGlobalRotation (
221- node: child, newRotationDegrees: child.rotationDegrees);
215+ node._rotatedTopLeftCorner = calculateGlobalRotatedBoxTopLeft (
216+ node.id,
217+ boundaryType: NodeBoundaryType .middleBox,
218+ unrotate: false ,
219+ updatingSortedNodeList: true ,
220+ );
222221 }
223- }
224222
225- /// Switches the parent node of this node from [oldParent] to [newParent] .
226- static void switchParent ( {
227- required BaseNode node,
228- required BaseNode newParent,
229- required BaseNode oldParent ,
230- }) {
231- oldParent.childrenOrEmpty. remove (node.id );
232- oldParent.childrenOrEmpty. add (node.id);
233- node.parentID = newParent.id;
223+ if (updateChildren) {
224+ for ( final String childId in node.childrenOrEmpty) {
225+ final child = getNode (childId);
226+ updateGlobalRotation (
227+ node : child ,
228+ newRotationDegrees : child.rotationDegrees,
229+ );
230+ }
231+ }
234232 }
235233
236234 /// [updateNode] is responsible for updating the node and its children when
@@ -248,6 +246,11 @@ class NodeProcessor {
248246 /// children are updated recursively. If it is set to false, only the node is
249247 /// updated. This parameter is used during nodes initialization. Since all the
250248 /// nodes are being laid out, there's no need for recursive updates.
249+ ///
250+ /// if [updatingSortedNodeList] is true, then you are currently updating a
251+ /// list of nodes one-by-one order from parent to child order. If this is
252+ /// the case, then we can make important assumptions that can help optimize
253+ /// and avoid recursive computation.
251254 static void updateNode ({
252255 required BaseNode node,
253256 EdgeInsetsModel ? margin,
@@ -262,6 +265,7 @@ class NodeProcessor {
262265 bool recursivelyCalculateChildrenGlobalBoxes = true ,
263266 Vec ? globalParentBoundingBoxPos,
264267 bool forceUpdateEdgePins = false ,
268+ bool updatingSortedNodeList = false ,
265269 }) {
266270 final bool marginChanged = margin != null && margin != node.margin;
267271 final bool paddingChanged = padding != null && padding != node.padding;
@@ -349,46 +353,58 @@ class NodeProcessor {
349353 }
350354 }
351355
352- if (globalParentBoundingBoxPos == null ) {
353- _computeGlobalAndRotatedBoxes (
354- node,
355- recursivelyCalculateChildren: recursivelyCalculateChildrenGlobalBoxes,
356- );
357- } else {
358- _computeGlobalAndRotatedBoxes (
359- node,
360- recursivelyCalculateChildren: false ,
361- );
362- }
356+ // If a performLayout was run, the children do not need calculation
357+ // because they will be updated next.
358+ _computeGlobalAndRotatedBoxes (
359+ node,
360+ recursivelyCalculateChildren:
361+ ! performLayoutRan && recursivelyCalculateChildrenGlobalBoxes,
362+ updatingSortedNodeList: updatingSortedNodeList,
363+ );
363364
364365 _computeInnerBoxLocal (node);
365366 }
366367
367368 static void _computeGlobalAndRotatedBoxes (
368369 BaseNode node, {
369370 bool recursivelyCalculateChildren = true ,
371+ bool updatingSortedNodeList = false ,
370372 }) {
371373 // Order matters.
372374 // Middle and inner boxes depend on outer.
373375 // Rotated boxes depend on all of them.
374- _computeOuterBoxGlobal (node);
376+ _computeOuterBoxGlobal (
377+ node,
378+ updatingSortedNodeList: updatingSortedNodeList,
379+ );
375380 _computeMiddleBoxGlobal (node);
376381 _computeInnerBoxGlobal (node);
377- _computeRotatedBoxes (node);
382+
383+ _computeRotatedBoxes (
384+ node,
385+ updatingSortedNodeList: updatingSortedNodeList,
386+ );
378387
379388 if (recursivelyCalculateChildren) {
380389 for (final String childID in node.childrenOrEmpty) {
381390 final BaseNode childNode = getNode (childID);
382- _computeGlobalAndRotatedBoxes (childNode);
391+ _computeGlobalAndRotatedBoxes (
392+ childNode,
393+ updatingSortedNodeList: updatingSortedNodeList,
394+ );
383395 }
384396 }
385397 }
386398
387- static void _computeOuterBoxGlobal (BaseNode node) {
388- final Vec globalBoundingTopLeft = getGlobalRotatedBoxTopLeft (
399+ static void _computeOuterBoxGlobal (
400+ BaseNode node, {
401+ bool updatingSortedNodeList = false ,
402+ }) {
403+ final Vec globalBoundingTopLeft = calculateGlobalRotatedBoxTopLeft (
389404 node.id,
390405 unrotate: true ,
391406 boundaryType: NodeBoundaryType .outerBox,
407+ updatingSortedNodeList: updatingSortedNodeList,
392408 );
393409 node._outerBoxGlobal = OuterNodeBox (
394410 globalBoundingTopLeft.x,
@@ -422,7 +438,10 @@ class NodeProcessor {
422438 );
423439 }
424440
425- static void _computeRotatedBoxes (BaseNode node) {
441+ static void _computeRotatedBoxes (
442+ BaseNode node, {
443+ bool updatingSortedNodeList = false ,
444+ }) {
426445 final RectC middleRotatedBox = getRotatedBoundingBoxAroundSelf (
427446 node.middleBoxLocal.x,
428447 node.middleBoxLocal.y,
@@ -449,8 +468,11 @@ class NodeProcessor {
449468 );
450469
451470 // GLOBAL
452- final middleUnrotatedGlobalTopLeft = getGlobalRotatedBoxTopLeft (node.id,
453- boundaryType: NodeBoundaryType .middleBox);
471+ final middleUnrotatedGlobalTopLeft = calculateGlobalRotatedBoxTopLeft (
472+ node.id,
473+ boundaryType: NodeBoundaryType .middleBox,
474+ updatingSortedNodeList: updatingSortedNodeList,
475+ );
454476
455477 final RectC middleGlobalRotatedBox = getRotatedBoundingBoxAroundSelf (
456478 middleUnrotatedGlobalTopLeft.x,
@@ -480,10 +502,16 @@ class NodeProcessor {
480502
481503 /// [unrotate] will unrotate the top-left corner of the rotated box to the
482504 /// top-left corner of the bounding box.
483- static Vec getGlobalRotatedBoxTopLeft (
505+ ///
506+ /// if [updatingSortedNodeList] is true, then you are currently updating a
507+ /// list of nodes one-by-one order from parent to child order. If this is
508+ /// the case, then we can make important assumptions that can help optimize
509+ /// and avoid recursive computation.
510+ static Vec calculateGlobalRotatedBoxTopLeft (
484511 String id, {
485512 bool unrotate = true ,
486513 required NodeBoundaryType boundaryType,
514+ bool updatingSortedNodeList = false ,
487515 }) {
488516 assert (
489517 ! boundaryType.isRotatedBox,
@@ -495,6 +523,14 @@ class NodeProcessor {
495523
496524 if (id == kRootNode) return currentVec;
497525
526+ if (updatingSortedNodeList) {
527+ return calculateSortedGlobalRotatedBoxTopLeft (
528+ id,
529+ unrotate: unrotate,
530+ boundaryType: boundaryType,
531+ );
532+ }
533+
498534 // First collect the node's and all of it's ancestor's id.
499535 final List <BaseNode > parents = [];
500536 String currentId = id;
@@ -590,4 +626,100 @@ class NodeProcessor {
590626
591627 return unrotated;
592628 }
629+
630+ /// [unrotate] will unrotate the top-left corner of the rotated box to the
631+ /// top-left corner of the bounding box.
632+ ///
633+ /// This function is used when you are updating a list of nodes one-by-one
634+ /// order from parent to child order. If this is the case, then we can make
635+ /// important assumptions that can help optimize and avoid recursive
636+ /// computation.
637+ static Vec calculateSortedGlobalRotatedBoxTopLeft (
638+ String id, {
639+ bool unrotate = true ,
640+ required NodeBoundaryType boundaryType,
641+ }) {
642+ assert (
643+ ! boundaryType.isRotatedBox,
644+ "This function is computing the rotated part for you. You can't give it a rotated node boundary type" ,
645+ );
646+
647+ // Our main offset were guiding to the target id.
648+ Vec currentVec = Vec .zero;
649+
650+ if (id == kRootNode) return currentVec;
651+
652+ final node = getNode (id);
653+ final parent = getNode (node.parentID);
654+
655+ final NodeBox parentBox = parent.id == kRootNode
656+ ? parent.basicBoxLocal
657+ : boundaryType.getGlobalBoxForNode (parent);
658+ final NodeBox nodeBox = boundaryType.getLocalBoxForNode (node);
659+
660+ currentVec = Vec .zero;
661+
662+ if (node.globalRotationDegrees == 0 ) {
663+ currentVec = parentBox is OuterNodeBox
664+ ? parentBox.innerTopLeft
665+ : parentBox.topLeft;
666+
667+ currentVec += nodeBox.topLeft;
668+ } else {
669+ currentVec = parent.rotatedTopLeftCorner;
670+
671+ final RectC rect =
672+ (nodeBox is OuterNodeBox ? nodeBox.innerRect : nodeBox.rect);
673+
674+ // Returns local box top left after rotation.
675+ // This will be the point of the top-left drag handle of this box.
676+ // IE: The literal visually rotated top left corner.
677+ //
678+ // The x/y is NOT yet translated inside the parent's space.
679+ // That's the next step.
680+ Vec rotated = calculateRotatedBoxTopLeftAroundSelf (
681+ radians: node.rotationRadians,
682+ x1: rect.left,
683+ y1: rect.top,
684+ x2: rect.right,
685+ y2: rect.bottom,
686+ );
687+
688+ if (boundaryType == NodeBoundaryType .outerBox) {
689+ if (parent.rotationDegrees == 0 ) {
690+ currentVec += parent.outerBoxLocal.edgeTopLeft;
691+ }
692+ }
693+
694+ // Converting it to global.
695+ rotated = localRotatedVecToGlobalVec (
696+ localX: rotated.x,
697+ localY: rotated.y,
698+ parentX: currentVec.x,
699+ parentY: currentVec.y,
700+ radians: parent.globalRotationRadians,
701+ );
702+
703+ currentVec = rotated;
704+ }
705+
706+ // If our target happens to be orthogonal w global.
707+ if (node.globalRotationDegrees == 0 || ! unrotate) return currentVec;
708+
709+ // Otherwise were pointing to an already rotated node which is not too
710+ // useful for most callers so we make it orthogonal w global.
711+ final SizeC size =
712+ (nodeBox is OuterNodeBox ? nodeBox.innerSize : nodeBox.size);
713+
714+ final Vec unrotated = calculateUnrotatedVecFromRotatedVec (
715+ x: currentVec.x,
716+ y: currentVec.y,
717+ width: size.width,
718+ height: size.height,
719+ radians: node.globalRotationRadians,
720+ ) -
721+ (nodeBox is OuterNodeBox ? nodeBox.edgeTopLeft : Vec .zero);
722+
723+ return unrotated;
724+ }
593725}
0 commit comments