143 lines
5.0 KiB
C#
143 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace AibisDream.FrameAnimation
|
|
{
|
|
internal sealed class FrameAnimationReachability
|
|
{
|
|
public IReadOnlyList<AnimationNode> Nodes { get; }
|
|
public IReadOnlyList<AnimationEdge> Edges { get; }
|
|
|
|
internal FrameAnimationReachability(
|
|
IReadOnlyList<AnimationNode> nodes,
|
|
IReadOnlyList<AnimationEdge> edges)
|
|
{
|
|
Nodes = nodes ?? Array.Empty<AnimationNode>();
|
|
Edges = edges ?? Array.Empty<AnimationEdge>();
|
|
}
|
|
}
|
|
|
|
internal sealed class FrameAnimationGraphTopology
|
|
{
|
|
private readonly FrameAnimationGraph graph;
|
|
private readonly Dictionary<string, List<AnimationNode>> nodesById;
|
|
private readonly Dictionary<string, List<AnimationEdge>> outgoingByNodeId;
|
|
|
|
internal FrameAnimationGraphTopology(FrameAnimationGraph graph)
|
|
{
|
|
this.graph = graph;
|
|
nodesById = (graph?.Nodes ?? Array.Empty<AnimationNode>())
|
|
.Where(node => node != null)
|
|
.GroupBy(node => node.InternalId ?? string.Empty)
|
|
.ToDictionary(group => group.Key, group => group.ToList());
|
|
outgoingByNodeId = (graph?.Edges ?? Array.Empty<AnimationEdge>())
|
|
.Where(edge => edge != null)
|
|
.GroupBy(edge => edge.FromNodeId ?? string.Empty)
|
|
.ToDictionary(group => group.Key, group => group.ToList());
|
|
}
|
|
|
|
internal IReadOnlyList<AnimationNode> FindNodes(string internalId)
|
|
{
|
|
return nodesById.TryGetValue(internalId ?? string.Empty, out var nodes)
|
|
? nodes
|
|
: Array.Empty<AnimationNode>();
|
|
}
|
|
|
|
internal IReadOnlyList<AnimationEdge> GetOutgoing(string nodeId)
|
|
{
|
|
return outgoingByNodeId.TryGetValue(nodeId ?? string.Empty, out var edges)
|
|
? edges
|
|
: Array.Empty<AnimationEdge>();
|
|
}
|
|
|
|
internal FrameAnimationReachability GetReachable(string entryNodeId)
|
|
{
|
|
var nodes = new List<AnimationNode>();
|
|
var edges = new List<AnimationEdge>();
|
|
var visitedNodes = new HashSet<string>();
|
|
var visitedEdges = new HashSet<string>();
|
|
var queue = new Queue<string>();
|
|
queue.Enqueue(entryNodeId ?? string.Empty);
|
|
|
|
while (queue.Count > 0)
|
|
{
|
|
var nodeId = queue.Dequeue();
|
|
if (!visitedNodes.Add(nodeId))
|
|
{
|
|
continue;
|
|
}
|
|
if (nodesById.TryGetValue(nodeId, out var matches))
|
|
{
|
|
nodes.AddRange(matches);
|
|
}
|
|
if (!outgoingByNodeId.TryGetValue(nodeId, out var outgoing))
|
|
{
|
|
continue;
|
|
}
|
|
foreach (var edge in outgoing)
|
|
{
|
|
if (visitedEdges.Add(edge.InternalId ?? string.Empty))
|
|
{
|
|
edges.Add(edge);
|
|
}
|
|
queue.Enqueue(edge.ToNodeId ?? string.Empty);
|
|
}
|
|
}
|
|
return new FrameAnimationReachability(nodes, edges);
|
|
}
|
|
|
|
internal bool WouldCreateCycle(string fromNodeId, string toNodeId)
|
|
{
|
|
if (string.IsNullOrEmpty(fromNodeId) || string.IsNullOrEmpty(toNodeId) || fromNodeId == toNodeId)
|
|
{
|
|
return true;
|
|
}
|
|
var visited = new HashSet<string>();
|
|
var stack = new Stack<string>();
|
|
stack.Push(toNodeId);
|
|
while (stack.Count > 0)
|
|
{
|
|
var current = stack.Pop();
|
|
if (!visited.Add(current))
|
|
{
|
|
continue;
|
|
}
|
|
if (current == fromNodeId)
|
|
{
|
|
return true;
|
|
}
|
|
foreach (var edge in GetOutgoing(current))
|
|
{
|
|
stack.Push(edge.ToNodeId ?? string.Empty);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal IReadOnlyList<AnimationFlow> FindFlowsUsingNode(string nodeId)
|
|
{
|
|
if (graph == null)
|
|
{
|
|
return Array.Empty<AnimationFlow>();
|
|
}
|
|
return graph.Flows.Where(flow => flow != null &&
|
|
GetReachable(flow.EntryNodeId).Nodes.Any(node => node != null && node.InternalId == nodeId))
|
|
.OrderBy(flow => flow.Id, StringComparer.Ordinal)
|
|
.ToArray();
|
|
}
|
|
|
|
internal IReadOnlyList<AnimationFlow> FindFlowsUsingEdge(string edgeId)
|
|
{
|
|
if (graph == null)
|
|
{
|
|
return Array.Empty<AnimationFlow>();
|
|
}
|
|
return graph.Flows.Where(flow => flow != null &&
|
|
GetReachable(flow.EntryNodeId).Edges.Any(edge => edge != null && edge.InternalId == edgeId))
|
|
.OrderBy(flow => flow.Id, StringComparer.Ordinal)
|
|
.ToArray();
|
|
}
|
|
}
|
|
}
|