242 lines
6.7 KiB
C++
242 lines
6.7 KiB
C++
// ©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<UEdComboActionGraph>(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<UComboActionGraphEditorSettings>();
|
|
}
|
|
|
|
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<UEdComboActionGraphNode*> 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<UComboActionGraphNode*> ParentNodes = RRoot->ParentNodes;
|
|
TArray<UComboActionGraphNode*> 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<UEdComboActionGraphNode*> &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<UEdComboActionGraphNode*> &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;
|
|
}
|
|
}
|