// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. #include "ComboActionTreeSolveLayoutStrategy.h" #include "ComboActionGraph.h" #include "Ed/EdComboActionGraph.h" #include "Ed/EdComboActionGraphNode.h" #include "Nodes/ComboActionGraphNode.h" #include "Settings/ComboActionGraphEditorSettings.h" void UComboActionTreeSolveLayoutStrategy::Layout(UEdGraph *InEdGraph) { this->EdGraph = Cast(InEdGraph); check(this->EdGraph != nullptr); this->EdGraph->RebuildComboActionGraph(); this->Graph = this->EdGraph->GetComboActionGraph(); check(this->Graph != nullptr); bool bFirstPassOnly = false; if (this->Settings == nullptr) { this->Settings = GetMutableDefault(); } if (this->Settings != nullptr) { this->OptimalDistance = this->Settings->GetOptimalDistance(); this->MaxIteration = this->Settings->GetMaxIteration(); bFirstPassOnly = this->Settings->IsFirstPassOnly(); } FVector2D Anchor(0.f, 0.f); for (UComboActionGraphNode *RootNode : this->Graph->RootNodes) { this->InitPass(RootNode, Anchor); if (!bFirstPassOnly) { for (int32 j = 0; j < this->MaxIteration; ++j) { bool HasConflict = this->ResolveConflictPass(RootNode); if (!HasConflict) { break; } } } } for (int32 i = 0; i < Graph->RootNodes.Num(); i++) { for (int32 j = 0; j < i; j++) { this->ResolveConflict(Graph->RootNodes[j], Graph->RootNodes[i]); } } } void UComboActionTreeSolveLayoutStrategy::InitPass(UComboActionGraphNode *RootNode, const FVector2D &Anchor) { UEdComboActionGraphNode *EdNode_RootNode = this->EdGraph->NodeMap[RootNode]; FVector2D ChildAnchor(FVector2D(0.0f, this->GetNodeHeight(EdNode_RootNode) + this->OptimalDistance + Anchor.Y)); for (int32 i = 0; i < RootNode->ChildNodes.Num(); i++) { UComboActionGraphNode *Child = RootNode->ChildNodes[i]; UEdComboActionGraphNode *EdNode_ChildNode = this->EdGraph->NodeMap[Child]; if (i > 0) { UComboActionGraphNode *PreChild = RootNode->ChildNodes[i - 1]; UEdComboActionGraphNode *EdNode_PreChildNode = this->EdGraph->NodeMap[PreChild]; ChildAnchor.X += this->OptimalDistance + this->GetNodeWidth(EdNode_PreChildNode) / 2; } ChildAnchor.X += this->GetNodeWidth(EdNode_ChildNode) / 2; this->InitPass(Child, ChildAnchor); } float NodeWidth = this->GetNodeWidth(EdNode_RootNode); EdNode_RootNode->NodePosY = Anchor.Y; if (RootNode->ChildNodes.Num() == 0) { EdNode_RootNode->NodePosX = Anchor.X - NodeWidth / 2; } else { this->UpdateParentNodePosition(RootNode); } } bool UComboActionTreeSolveLayoutStrategy::ResolveConflictPass(UComboActionGraphNode *Node) { bool HasConflict = false; for (int32 i = 0; i < Node->ChildNodes.Num(); ++i) { UComboActionGraphNode *Child = Node->ChildNodes[i]; if (this->ResolveConflictPass(Child)) { HasConflict = true; } } for (const UComboActionGraphNode *ParentNode : Node->ParentNodes) { for (UComboActionGraphNode *LeftSibling : ParentNode->ChildNodes) { if (LeftSibling == Node) { break; } if (this->ResolveConflict(LeftSibling, Node)) { HasConflict = true; } } } return HasConflict; } bool UComboActionTreeSolveLayoutStrategy::ResolveConflict(UComboActionGraphNode *LRoot, UComboActionGraphNode *RRoot) { TArray RightContour, LeftContour; this->GetRightContour(LRoot, 0, RightContour); this->GetLeftContour(RRoot, 0, LeftContour); int32 MaxOverlapDistance = 0; int32 Num = FMath::Min(LeftContour.Num(), RightContour.Num()); for (int32 i = 0; i < Num; ++i) { if (RightContour.Contains(LeftContour[i]) || LeftContour.Contains(RightContour[i])) break; int32 RightBound = RightContour[i]->NodePosX + this->GetNodeWidth(RightContour[i]); int32 LeftBound = LeftContour[i]->NodePosX; int32 Distance = RightBound + OptimalDistance - LeftBound; if (Distance > MaxOverlapDistance) { MaxOverlapDistance = Distance; } } if (MaxOverlapDistance > 0) { this->ShiftSubTree(RRoot, FVector2D(MaxOverlapDistance, 0.f)); TArray ParentNodes = RRoot->ParentNodes; TArray NextParentNodes; while (ParentNodes.Num() != 0) { for (int32 i = 0; i < ParentNodes.Num(); ++i) { UpdateParentNodePosition(ParentNodes[i]); NextParentNodes.Append(ParentNodes[i]->ParentNodes); } ParentNodes = NextParentNodes; NextParentNodes.Reset(); } return true; } return false; } void UComboActionTreeSolveLayoutStrategy::GetLeftContour(UComboActionGraphNode *RootNode, int32 Level, TArray &Contour) { UEdComboActionGraphNode *EdNode_Node = this->EdGraph->NodeMap[RootNode]; if (Level >= Contour.Num()) { Contour.Add(EdNode_Node); } else if (EdNode_Node->NodePosX < Contour[Level]->NodePosX) { Contour[Level] = EdNode_Node; } for (UComboActionGraphNode *Child : RootNode->ChildNodes) { this->GetLeftContour(Child, Level + 1, Contour); } } void UComboActionTreeSolveLayoutStrategy::GetRightContour(UComboActionGraphNode *RootNode, int32 Level, TArray &Contour) { UEdComboActionGraphNode *EdNode_Node = this->EdGraph->NodeMap[RootNode]; if (Level >= Contour.Num()) { Contour.Add(EdNode_Node); } else if (EdNode_Node->NodePosX + this->GetNodeWidth(EdNode_Node) > Contour[Level]->NodePosX + this->GetNodeWidth(Contour[Level])) { Contour[Level] = EdNode_Node; } for (UComboActionGraphNode *Child : RootNode->ChildNodes) { this->GetRightContour(Child, Level + 1, Contour); } } void UComboActionTreeSolveLayoutStrategy::ShiftSubTree(UComboActionGraphNode *RootNode, const FVector2D &Offset) { UEdComboActionGraphNode *EdNode_Node = this->EdGraph->NodeMap[RootNode]; EdNode_Node->NodePosX += Offset.X; EdNode_Node->NodePosY += Offset.Y; for (UComboActionGraphNode *Child : RootNode->ChildNodes) { if (Child->ParentNodes[0] == RootNode) { this->ShiftSubTree(Child, Offset); } } } void UComboActionTreeSolveLayoutStrategy::UpdateParentNodePosition(UComboActionGraphNode *RootNode) { UEdComboActionGraphNode *EdNode_ParentNode = this->EdGraph->NodeMap[RootNode]; if (RootNode->ChildNodes.Num() % 2 == 0) { UEdComboActionGraphNode *FirstChild = this->EdGraph->NodeMap[RootNode->ChildNodes[0]]; UEdComboActionGraphNode *LastChild = this->EdGraph->NodeMap[RootNode->ChildNodes.Last()]; float LeftBound = FirstChild->NodePosX; float RightBound = LastChild->NodePosX + this->GetNodeWidth(LastChild); EdNode_ParentNode->NodePosX = (LeftBound + RightBound) / 2 - this->GetNodeWidth(EdNode_ParentNode) / 2; } else { UEdComboActionGraphNode *MidChild = this->EdGraph->NodeMap[RootNode->ChildNodes[RootNode->ChildNodes.Num() / 2]]; EdNode_ParentNode->NodePosX = MidChild->NodePosX + this->GetNodeWidth(MidChild) / 2 - this->GetNodeWidth(EdNode_ParentNode) / 2; } }