Did a shitload more work. To what end? Not much.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
struct FAssetEditorTabs_ComboActionGraph
|
||||
{
|
||||
// Tab identifiers
|
||||
static const FName ComboActionGraphPropertyID;
|
||||
static const FName ViewportID;
|
||||
static const FName SearchToolbarID;
|
||||
};
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
// All rights reserved Dominik Pavlicek 2023
|
||||
|
||||
|
||||
#include "Layout/ComboActionForceDirectedSolveLayoutStrategy.h"
|
||||
|
||||
#include "ComboActionGraph.h"
|
||||
#include "Ed/EdComboActionGraph.h"
|
||||
#include "Ed/EdComboActionGraphNode.h"
|
||||
#include "Nodes/ComboActionGraphNode.h"
|
||||
#include "Settings/ComboActionGraphEditorSettings.h"
|
||||
|
||||
UComboActionForceDirectedSolveLayoutStrategy::UComboActionForceDirectedSolveLayoutStrategy()
|
||||
{
|
||||
this->bRandomInit = false;
|
||||
this->CoolDownRate = 10;
|
||||
this->InitTemperature = 10.f;
|
||||
}
|
||||
|
||||
static inline float CoolDown(float Temp, float CoolDownRate)
|
||||
{
|
||||
if (Temp < .01) return .01;
|
||||
return Temp - (Temp / CoolDownRate);
|
||||
}
|
||||
|
||||
static inline float GetAttractForce(float X, float K)
|
||||
{
|
||||
return (X * X) / K;
|
||||
}
|
||||
|
||||
static inline float GetRepulseForce(float X, float k)
|
||||
{
|
||||
return X != 0 ? k * k / X : TNumericLimits<float>::Max();
|
||||
}
|
||||
|
||||
void UComboActionForceDirectedSolveLayoutStrategy::Layout(UEdGraph *InEdGraph)
|
||||
{
|
||||
this->EdGraph = Cast<UEdComboActionGraph>(InEdGraph);
|
||||
check(this->EdGraph != nullptr);
|
||||
|
||||
this->EdGraph->RebuildComboActionGraph();
|
||||
this->Graph = this->EdGraph->GetComboActionGraph();
|
||||
check(this->Graph != nullptr);
|
||||
|
||||
if (this->Settings != nullptr)
|
||||
{
|
||||
this->OptimalDistance = Settings->GetOptimalDistance();
|
||||
this->MaxIteration = Settings->GetMaxIteration();
|
||||
this->bRandomInit = Settings->IsRandomInit();
|
||||
}
|
||||
|
||||
FBox2D PreTreeBound(ForceInitToZero);
|
||||
for (int32 i = 0; i < Graph->RootNodes.Num(); ++i)
|
||||
{
|
||||
PreTreeBound = this->LayoutOneTree(Graph->RootNodes[i], PreTreeBound);
|
||||
}
|
||||
}
|
||||
|
||||
FBox2D UComboActionForceDirectedSolveLayoutStrategy::LayoutOneTree(UComboActionGraphNode *RootNode, const FBox2D &PreTreeBound)
|
||||
{
|
||||
float Temp = this->InitTemperature;
|
||||
FBox2D TreeBound = GetActualBounds(RootNode);
|
||||
TreeBound.Min.X += PreTreeBound.Max.X + this->OptimalDistance;
|
||||
TreeBound.Max.X += PreTreeBound.Max.X + this->OptimalDistance;
|
||||
|
||||
if (this->bRandomInit)
|
||||
{
|
||||
this->RandomLayoutOneTree(RootNode, TreeBound);
|
||||
}
|
||||
|
||||
float RepulseForce, AttractForce, Distance;
|
||||
FVector2D Diff;
|
||||
|
||||
TMap<UEdGraphNode*, FVector2D> NodeToDisplacement;
|
||||
|
||||
for (const TObjectPtr<UEdGraphNode> &EdNode : this->EdGraph->Nodes)
|
||||
{
|
||||
NodeToDisplacement.Add(EdNode, FVector2D(0.0f, 0.0f));
|
||||
}
|
||||
|
||||
for (int32 IterationNum = 0; IterationNum < this->MaxIteration; IterationNum++)
|
||||
{
|
||||
// Calculate the repulsive forces.
|
||||
for (int32 i = 0; i < this->EdGraph->Nodes.Num(); i++)
|
||||
{
|
||||
for (int32 j = 0; j < this->EdGraph->Nodes.Num(); j++)
|
||||
{
|
||||
if (i == j)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Diff.X = this->EdGraph->Nodes[i]->NodePosX - this->EdGraph->Nodes[j]->NodePosX;
|
||||
Diff.Y = this->EdGraph->Nodes[i]->NodePosY - this->EdGraph->Nodes[j]->NodePosY;
|
||||
Distance = Diff.Size();
|
||||
Diff.Normalize();
|
||||
|
||||
RepulseForce = Distance > 2 * this->OptimalDistance ? 0 : GetRepulseForce(Distance, this->OptimalDistance);
|
||||
|
||||
NodeToDisplacement[this->EdGraph->Nodes[i]] += Diff * RepulseForce;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the attractive forces.
|
||||
int Level = 0;
|
||||
TArray<UComboActionGraphNode*> CurrLevelNodes = { RootNode };
|
||||
TArray<UComboActionGraphNode*> NextLevelNodes;
|
||||
|
||||
while (CurrLevelNodes.Num() != 0)
|
||||
{
|
||||
for (int32 i = 0; i < CurrLevelNodes.Num(); i++)
|
||||
{
|
||||
UComboActionGraphNode *Node = CurrLevelNodes[i];
|
||||
check(Node != nullptr);
|
||||
|
||||
UEdComboActionGraphNode *EdNode_ParentNode = this->EdGraph->NodeMap[Node];
|
||||
|
||||
for (int32 j = 0; j < Node->ChildrenNodes.Num(); j++)
|
||||
{
|
||||
NextLevelNodes.Add(Node->ChildrenNodes[j]);
|
||||
|
||||
UEdComboActionGraphNode *EdNode_ChildNode = this->EdGraph->NodeMap[Node->ChildrenNodes[j]];
|
||||
|
||||
Diff.X = EdNode_ChildNode->NodePosX - EdNode_ParentNode->NodePosY;
|
||||
Diff.Y = EdNode_ChildNode->NodePosY - EdNode_ParentNode->NodePosY;
|
||||
Distance = Diff.Size();
|
||||
Diff.Normalize();
|
||||
|
||||
AttractForce = GetAttractForce(Distance, this->OptimalDistance);
|
||||
|
||||
NodeToDisplacement[EdNode_ParentNode] += Distance * Diff;
|
||||
NodeToDisplacement[EdNode_ChildNode] -= Distance * Diff;
|
||||
}
|
||||
}
|
||||
|
||||
CurrLevelNodes = NextLevelNodes;
|
||||
NextLevelNodes.Reset();
|
||||
Level++;
|
||||
}
|
||||
|
||||
for (UEdGraphNode *EdNode : this->EdGraph->Nodes)
|
||||
{
|
||||
Distance = NodeToDisplacement[EdNode].Size();
|
||||
NodeToDisplacement[EdNode].Normalize();
|
||||
|
||||
float Minimum = Distance < Temp ? Distance : Temp;
|
||||
EdNode->NodePosX += NodeToDisplacement[EdNode].X * Minimum;
|
||||
EdNode->NodePosY += NodeToDisplacement[EdNode].Y * Minimum;
|
||||
}
|
||||
|
||||
Temp = CoolDown(Temp, this->CoolDownRate);
|
||||
}
|
||||
|
||||
FBox2D ActualBound = GetActualBounds(RootNode);
|
||||
|
||||
FVector2D Center = ActualBound.GetCenter();
|
||||
FVector2D TreeCenter = TreeBound.GetCenter();
|
||||
|
||||
FVector2D Scale = (TreeBound.Max - TreeBound.Min) / (ActualBound.Max - ActualBound.Min);
|
||||
|
||||
for (UEdGraphNode *EdNode : EdGraph->Nodes)
|
||||
{
|
||||
EdNode->NodePosX = TreeCenter.X + Scale.X * (EdNode->NodePosX - Center.X);
|
||||
EdNode->NodePosY = TreeCenter.Y + Scale.Y * (EdNode->NodePosY - Center.Y);
|
||||
}
|
||||
|
||||
return TreeBound;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// All rights reserved Dominik Pavlicek 2023
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Layout/ComboActionGraphLayoutStrategy.h"
|
||||
#include "UObject/Object.h"
|
||||
|
||||
#include "ComboActionForceDirectedSolveLayoutStrategy.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class COMBOINPUTEDITOR_API UComboActionForceDirectedSolveLayoutStrategy : public UComboActionGraphLayoutStrategy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UComboActionForceDirectedSolveLayoutStrategy();
|
||||
|
||||
virtual void Layout(UEdGraph *EdGraph) override;
|
||||
|
||||
protected:
|
||||
virtual FBox2D LayoutOneTree(UComboActionGraphNode* RootNode, const FBox2D &PreTreeBound);
|
||||
|
||||
protected:
|
||||
bool bRandomInit;
|
||||
float InitTemperature;
|
||||
float CoolDownRate;
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#include "ComboActionGraphLayoutStrategy.h"
|
||||
|
||||
#include "Ed/EdComboActionGraph.h"
|
||||
#include "Ed/EdComboActionGraphNode.h"
|
||||
#include "Ed/SEdComboActionGraphNode.h"
|
||||
#include "Nodes/ComboActionGraphNode.h"
|
||||
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
|
||||
|
||||
UComboActionGraphLayoutStrategy::UComboActionGraphLayoutStrategy()
|
||||
{
|
||||
Settings = nullptr;
|
||||
MaxIteration = 50;
|
||||
OptimalDistance = 150;
|
||||
}
|
||||
|
||||
int32 UComboActionGraphLayoutStrategy::GetNodeWidth(UEdComboActionGraphNode *EdNode)
|
||||
{
|
||||
return EdNode->SEdNode->GetCachedGeometry().GetLocalSize().X;
|
||||
}
|
||||
|
||||
int32 UComboActionGraphLayoutStrategy::GetNodeHeight(UEdComboActionGraphNode *EdNode)
|
||||
{
|
||||
return EdNode->SEdNode->GetCachedGeometry().GetLocalSize().Y;
|
||||
}
|
||||
|
||||
FBox2D UComboActionGraphLayoutStrategy::GetNodeBound(UEdGraphNode* EdNode)
|
||||
{
|
||||
int32 NodeWidth = GetNodeWidth(Cast<UEdComboActionGraphNode>(EdNode));
|
||||
int32 NodeHeight = GetNodeHeight(Cast<UEdComboActionGraphNode>(EdNode));
|
||||
FVector2D Min(EdNode->NodePosX, EdNode->NodePosY);
|
||||
FVector2D Max(EdNode->NodePosX + NodeWidth, EdNode->NodePosY + NodeHeight);
|
||||
return FBox2D(Min, Max);
|
||||
}
|
||||
|
||||
FBox2D UComboActionGraphLayoutStrategy::GetActualBounds(UComboActionGraphNode *RootNode)
|
||||
{
|
||||
int Level = 0;
|
||||
TArray<UComboActionGraphNode*> CurrLevelNodes = { RootNode };
|
||||
TArray<UComboActionGraphNode*> NextLevelNodes;
|
||||
|
||||
FBox2D Rtn = GetNodeBound(this->EdGraph->NodeMap[RootNode]);
|
||||
|
||||
while (CurrLevelNodes.Num() != 0)
|
||||
{
|
||||
for (const UComboActionGraphNode *Node : CurrLevelNodes)
|
||||
{
|
||||
check(Node != nullptr);
|
||||
|
||||
Rtn += GetNodeBound(this->EdGraph->NodeMap[Node]);
|
||||
|
||||
for (UComboActionGraphNode *ChildNode : Node->ChildrenNodes)
|
||||
{
|
||||
NextLevelNodes.Add(ChildNode);
|
||||
}
|
||||
}
|
||||
|
||||
CurrLevelNodes = NextLevelNodes;
|
||||
NextLevelNodes.Reset();
|
||||
++Level;
|
||||
}
|
||||
return Rtn;
|
||||
}
|
||||
|
||||
void UComboActionGraphLayoutStrategy::RandomLayoutOneTree(UComboActionGraphNode *RootNode, const FBox2D &Bound)
|
||||
{
|
||||
int Level = 0;
|
||||
TArray<UComboActionGraphNode*> CurrLevelNodes = { RootNode };
|
||||
TArray<UComboActionGraphNode*> NextLevelNodes;
|
||||
|
||||
while (CurrLevelNodes.Num() != 0)
|
||||
{
|
||||
for (const UComboActionGraphNode *Node : CurrLevelNodes)
|
||||
{
|
||||
check(Node != nullptr);
|
||||
|
||||
UEdComboActionGraphNode *EdNode_Node = this->EdGraph->NodeMap[Node];
|
||||
|
||||
EdNode_Node->NodePosX = UKismetMathLibrary::RandomFloatInRange(Bound.Min.X, Bound.Max.X);
|
||||
EdNode_Node->NodePosY = UKismetMathLibrary::RandomFloatInRange(Bound.Min.Y, Bound.Max.Y);
|
||||
|
||||
for (UComboActionGraphNode *ChildNode : Node->ChildrenNodes)
|
||||
{
|
||||
NextLevelNodes.Add(ChildNode);
|
||||
}
|
||||
}
|
||||
|
||||
CurrLevelNodes = NextLevelNodes;
|
||||
NextLevelNodes.Reset();
|
||||
++Level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
|
||||
#include "ComboActionGraphLayoutStrategy.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class COMBOINPUTEDITOR_API UComboActionGraphLayoutStrategy : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UComboActionGraphLayoutStrategy();
|
||||
|
||||
virtual void Layout(UEdGraph *G) {};
|
||||
|
||||
UComboActionGraphEditorSettings *Settings;
|
||||
|
||||
protected:
|
||||
int32 GetNodeWidth(UEdComboActionGraphNode *EdNode);
|
||||
|
||||
int32 GetNodeHeight(UEdComboActionGraphNode *EdNode);
|
||||
|
||||
FBox2D GetNodeBound(UEdGraphNode *EdNode);
|
||||
|
||||
FBox2D GetActualBounds(UComboActionGraphNode *RootNode);
|
||||
|
||||
virtual void RandomLayoutOneTree(UComboActionGraphNode *RootNode, const FBox2D &Bound);
|
||||
|
||||
protected:
|
||||
UComboActionGraph *Graph;
|
||||
UEdComboActionGraph *EdGraph;
|
||||
int32 MaxIteration;
|
||||
int32 OptimalDistance;
|
||||
};
|
||||
@@ -0,0 +1,241 @@
|
||||
// ©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->ChildrenNodes.Num(); i++)
|
||||
{
|
||||
UComboActionGraphNode *Child = RootNode->ChildrenNodes[i];
|
||||
UEdComboActionGraphNode *EdNode_ChildNode = this->EdGraph->NodeMap[Child];
|
||||
if (i > 0)
|
||||
{
|
||||
UComboActionGraphNode *PreChild = RootNode->ChildrenNodes[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->ChildrenNodes.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->ChildrenNodes.Num(); ++i)
|
||||
{
|
||||
UComboActionGraphNode *Child = Node->ChildrenNodes[i];
|
||||
if (this->ResolveConflictPass(Child))
|
||||
{
|
||||
HasConflict = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const UComboActionGraphNode *ParentNode : Node->ParentNodes)
|
||||
{
|
||||
for (UComboActionGraphNode *LeftSibling : ParentNode->ChildrenNodes)
|
||||
{
|
||||
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->ChildrenNodes)
|
||||
{
|
||||
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->ChildrenNodes)
|
||||
{
|
||||
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->ChildrenNodes)
|
||||
{
|
||||
if (Child->ParentNodes[0] == RootNode)
|
||||
{
|
||||
this->ShiftSubTree(Child, Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UComboActionTreeSolveLayoutStrategy::UpdateParentNodePosition(UComboActionGraphNode *RootNode)
|
||||
{
|
||||
UEdComboActionGraphNode *EdNode_ParentNode = this->EdGraph->NodeMap[RootNode];
|
||||
if (RootNode->ChildrenNodes.Num() % 2 == 0)
|
||||
{
|
||||
UEdComboActionGraphNode *FirstChild = this->EdGraph->NodeMap[RootNode->ChildrenNodes[0]];
|
||||
UEdComboActionGraphNode *LastChild = this->EdGraph->NodeMap[RootNode->ChildrenNodes.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->ChildrenNodes[RootNode->ChildrenNodes.Num() / 2]];
|
||||
EdNode_ParentNode->NodePosX = MidChild->NodePosX + this->GetNodeWidth(MidChild) / 2 - this->GetNodeWidth(EdNode_ParentNode) / 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Layout/ComboActionGraphLayoutStrategy.h"
|
||||
|
||||
#include "ComboActionTreeSolveLayoutStrategy.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class COMBOINPUTEDITOR_API UComboActionTreeSolveLayoutStrategy : public UComboActionGraphLayoutStrategy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
virtual void Layout(UEdGraph *InEdGraph) override;
|
||||
|
||||
protected:
|
||||
void InitPass(UComboActionGraphNode *RootNode, const FVector2D &Anchor);
|
||||
bool ResolveConflictPass(UComboActionGraphNode *Node);
|
||||
|
||||
bool ResolveConflict(UComboActionGraphNode *LRoot, UComboActionGraphNode *RRoot);
|
||||
|
||||
void GetLeftContour(UComboActionGraphNode *RootNode, int32 Level, TArray<UEdComboActionGraphNode*>& Contour);
|
||||
void GetRightContour(UComboActionGraphNode *RootNode, int32 Level, TArray<UEdComboActionGraphNode*>& Contour);
|
||||
|
||||
void ShiftSubTree(UComboActionGraphNode* RootNode, const FVector2D& Offset);
|
||||
|
||||
void UpdateParentNodePosition(UComboActionGraphNode* RootNode);
|
||||
};
|
||||
Reference in New Issue
Block a user