From 970f22510b624613f6b17cf85837727271f653b5 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 25 Dec 2023 08:59:00 +0800 Subject: [PATCH] tmp update --- src/Builtin/EcsArcWorld.cs | 4 + src/Builtin/EcsEdgeWorld.cs | 4 - src/EcsArc.cs | 206 +++++++++++++++++ src/EcsEdge.cs | 213 ------------------ src/EcsJoinGroup.cs | 34 +-- src/Utils/{ArcTargets.cs => ArcEntityInfo.cs} | 14 +- src/WorldGraph.cs | 94 ++++---- 7 files changed, 285 insertions(+), 284 deletions(-) create mode 100644 src/Builtin/EcsArcWorld.cs delete mode 100644 src/Builtin/EcsEdgeWorld.cs create mode 100644 src/EcsArc.cs delete mode 100644 src/EcsEdge.cs rename src/Utils/{ArcTargets.cs => ArcEntityInfo.cs} (66%) diff --git a/src/Builtin/EcsArcWorld.cs b/src/Builtin/EcsArcWorld.cs new file mode 100644 index 0000000..c4ce31b --- /dev/null +++ b/src/Builtin/EcsArcWorld.cs @@ -0,0 +1,4 @@ +namespace DCFApixels.DragonECS +{ + public sealed class EcsArcWorld : EcsWorld { } +} diff --git a/src/Builtin/EcsEdgeWorld.cs b/src/Builtin/EcsEdgeWorld.cs deleted file mode 100644 index 8eaa9ff..0000000 --- a/src/Builtin/EcsEdgeWorld.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace DCFApixels.DragonECS -{ - public sealed class EcsEdgeWorld : EcsWorld { } -} diff --git a/src/EcsArc.cs b/src/EcsArc.cs new file mode 100644 index 0000000..6b815df --- /dev/null +++ b/src/EcsArc.cs @@ -0,0 +1,206 @@ +using DCFApixels.DragonECS.Relations.Utils; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS +{ + //Edge world + //Relation entity + //Relation component + + public class EcsEdge + { + + } + public class EcsArc : IEcsWorldEventListener, IEcsEntityEventListener + { + private readonly EcsWorld _startWorld; + private readonly EcsWorld _endWorld; + private readonly EcsArcWorld _arcWorld; + + private readonly VertexWorldHandler _worldHandler; + + private readonly IdsBasket _basket = new IdsBasket(256); + + private readonly SparseArray64 _relationsMatrix = new SparseArray64(); + + private ArcEntityInfo[] _arkEntityInfos; //N * (N - 1) / 2 + + #region Properties + public EcsWorld StartWorld => _startWorld; + public EcsWorld EndWorld => _endWorld; + public EcsArcWorld ArcWorld => _arcWorld; + + public bool IsLoop => _startWorld == _endWorld; + #endregion + + #region Constructors + internal EcsArc(EcsWorld world, EcsWorld otherWorld, EcsArcWorld arcWorld) + { + _arcWorld = arcWorld; + _startWorld = world; + _endWorld = otherWorld; + + _worldHandler = new VertexWorldHandler(this, _startWorld, _basket); + _startWorld.AddListener(_worldHandler); + + _arkEntityInfos = new ArcEntityInfo[arcWorld.Capacity]; + + _arcWorld.AddListener(worldEventListener: this); + _arcWorld.AddListener(entityEventListener: this); + } + #endregion + + #region New/Del + public int New(int startEntityID, int endEntityID) + { + if (Has(startEntityID, endEntityID)) + throw new EcsRelationException(); + int arcEntity = _arcWorld.NewEntity(); + _basket.AddToHead(startEntityID, endEntityID); + _relationsMatrix.Add(startEntityID, endEntityID, arcEntity); + _arkEntityInfos[arcEntity] = new ArcEntityInfo(startEntityID, endEntityID); + return arcEntity; + } + public void Del(int startEntityID, int endEntityID) + { + if (!_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int e)) + throw new EcsRelationException(); + _relationsMatrix.Remove(startEntityID, endEntityID); + _basket.DelHead(startEntityID); + _arcWorld.DelEntity(e); + _arkEntityInfos[e] = ArcEntityInfo.Empty; + } + #endregion + + #region Get/Has + public bool Has(int startEntityID, int endEntityID) + { + return _relationsMatrix.Contains(startEntityID, endEntityID); + } + //public bool HasRelationWith(EcsSubject subject, int entityID, int otherEntityID) + //{ + // if (subject.World != _relationWorld) + // throw new ArgumentException(); + // return _source._relationsMatrix.TryGetValue(entityID, otherEntityID, out int entity) && subject.IsMatches(entity); + //} + public int Get(int startEntityID, int endEntityID) + { + if (!_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int e)) + throw new EcsRelationException(); + return e; + } + public bool TryGet(int startEntityID, int endEntityID, out int arcEntityID) + { + return _relationsMatrix.TryGetValue(startEntityID, endEntityID, out arcEntityID); + } + //public bool TryGetRelation(EcsSubject subject, int entityID, int otherEntityID, out int entity) + //{ + // return _source._relationsMatrix.TryGetValue(entityID, otherEntityID, out entity) && subject.IsMatches(entity); + //} + + //#region GetRelations + //private IdsLinkedList.Span GetRelations(int entityID) + //{ + // return _basket.GetSpanFor(entityID); + //} + ////ReadOnlySpan временная заглушка, потому тут будет спан из линкедлиста + ////public ReadOnlySpan GetRelationsWith(EcsSubject subject, int entityID) + ////{ + //// if (subject.World != _relationWorld) + //// throw new ArgumentException(); + //// throw new NotImplementedException(); + ////} + //#endregion + #endregion + + #region ArcEntityInfo + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsArc(int arcEntityID) + { + if (arcEntityID <= 0 || arcEntityID >= _arkEntityInfos.Length) + return false; + return !_arkEntityInfos[arcEntityID].IsEmpty; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArcEntityInfo GetArcInfo(int arcEntityID) + { + if (arcEntityID <= 0 || arcEntityID >= _arkEntityInfos.Length) + throw new Exception(); + return _arkEntityInfos[arcEntityID]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetArcStart(int arcEntityID) + { + return GetArcInfo(arcEntityID).start; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetArcEnd(int arcEntityID) + { + return GetArcInfo(arcEntityID).end; + } + #endregion + + #region Other + public EcsArc GetInversetArc() + { + return _endWorld.GetArcWith(_startWorld); + } + public bool TryGetInversetArc(out EcsArc arc) + { + return _endWorld.TryGetArcWith(_startWorld, out arc); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IdsLinkedList.Span Get(int entityID) => _basket.GetSpanFor(entityID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IdsLinkedList.LongSpan GetLongs(int entityID) => _basket.GetLongSpanFor(_startWorld, entityID); + #endregion + + #region Callbacks + void IEcsWorldEventListener.OnWorldResize(int newSize) + { + Array.Resize(ref _arkEntityInfos, newSize); + } + void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { } + void IEcsWorldEventListener.OnWorldDestroy() { } + + void IEcsEntityEventListener.OnNewEntity(int entityID) { } + void IEcsEntityEventListener.OnDelEntity(int entityID) + { + ref ArcEntityInfo rel = ref _arkEntityInfos[entityID]; + if (_relationsMatrix.Contains(rel.start, rel.end)) + Del(rel.start, rel.end); + } + #endregion + + #region VertexWorldHandler + private class VertexWorldHandler : IEcsEntityEventListener + { + private readonly EcsArc _source; + private readonly EcsWorld _world; + private readonly IdsBasket _basket; + + public VertexWorldHandler(EcsArc source, EcsWorld world, IdsBasket basket) + { + _source = source; + _world = world; + _basket = basket; + } + + public void OnDelEntity(int entityID) + { + var span = _basket.GetSpanFor(entityID); + foreach (var arcEntityID in span) + { + } + } + public void OnNewEntity(int entityID) + { + + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/EcsEdge.cs b/src/EcsEdge.cs deleted file mode 100644 index 01605b8..0000000 --- a/src/EcsEdge.cs +++ /dev/null @@ -1,213 +0,0 @@ -using DCFApixels.DragonECS.Relations.Utils; -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace DCFApixels.DragonECS -{ - //Edge world - //Relation entity - //Relation component - public class EcsEdge : IEcsWorldEventListener, IEcsEntityEventListener - { - private readonly EcsWorld _world; - private readonly EcsWorld _otherWorld; - private readonly EcsEdgeWorld _edgeWorld; - - private readonly VertexWorldHandler _worldHandler; - private readonly VertexWorldHandler _otherWorldHandler; - - private readonly IdsBasket _basket = new IdsBasket(256); - private readonly IdsBasket _otherBasket = new IdsBasket(256); - private readonly SparseArray64 _relationsMatrix = new SparseArray64(); - - private ArcTargets[] _arkTargets; //N * (N - 1) / 2 - - private List> _groups = new List>(); - private Stack _groupsPool = new Stack(64); - - #region Properties - public EcsWorld World => _world; - public EcsWorld OtherWorld => _otherWorld; - public EcsEdgeWorld EdgeWorld => _edgeWorld; - - public bool IsLoop => _world == _otherWorld; - #endregion - - #region Constructors - internal EcsEdge(EcsWorld world, EcsWorld otherWorld, EcsEdgeWorld edgeWorld) - { - _edgeWorld = edgeWorld; - _world = world; - _otherWorld = otherWorld; - - _worldHandler = new VertexWorldHandler(this, _world, _basket); - _world.AddListener(_worldHandler); - if (IsLoop) - { - _otherWorldHandler = _worldHandler; - } - else - { - _otherWorldHandler = new VertexWorldHandler(this, _otherWorld, _otherBasket); - _world.AddListener(_otherWorldHandler); - } - - _arkTargets = new ArcTargets[edgeWorld.Capacity]; - - _edgeWorld.AddListener(worldEventListener: this); - _edgeWorld.AddListener(entityEventListener: this); - } - #endregion - - #region Join Groups Pool - internal void RegisterGroup(EcsJoinGroup group) - { - _groups.Add(new WeakReference(group)); - } - internal EcsJoinGroup GetFreeGroup() - { - EcsJoinGroup result = _groupsPool.Count <= 0 ? new EcsJoinGroup(this) : _groupsPool.Pop(); - result._isReleased = false; - return result; - } - internal void ReleaseGroup(EcsJoinGroup group) - { -#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS - if (group.Edge != this) throw new Exception(); -#endif - group._isReleased = true; - group.Clear(); - _groupsPool.Push(group); - } - #endregion - - #region New/Del - public int New(int entityID, int otherEntityID) - { - if (Has(entityID, otherEntityID)) - throw new EcsRelationException(); - int arcEntity = _edgeWorld.NewEntity(); - _basket.AddToHead(entityID, otherEntityID); - _otherBasket.AddToHead(otherEntityID, entityID); - _relationsMatrix.Add(entityID, otherEntityID, arcEntity); - _arkTargets[arcEntity] = new ArcTargets(entityID, otherEntityID); - return arcEntity; - } - public void Del(int entityID, int otherEntityID) - { - if (!_relationsMatrix.TryGetValue(entityID, otherEntityID, out int e)) - throw new EcsRelationException(); - _relationsMatrix.Remove(entityID, otherEntityID); - _basket.DelHead(entityID); - _otherBasket.Del(entityID); - _edgeWorld.DelEntity(e); - _arkTargets[e] = ArcTargets.Empty; - } - #endregion - - #region Get/Has - public bool Has(int entityID, int otherEntityID) => _relationsMatrix.Contains(entityID, otherEntityID); - //public bool HasRelationWith(EcsSubject subject, int entityID, int otherEntityID) - //{ - // if (subject.World != _relationWorld) - // throw new ArgumentException(); - // return _source._relationsMatrix.TryGetValue(entityID, otherEntityID, out int entity) && subject.IsMatches(entity); - //} - public int Get(int entityID, int otherEntityID) - { - if (!_relationsMatrix.TryGetValue(entityID, otherEntityID, out int e)) - throw new EcsRelationException(); - return e; - } - private bool TryGet(int entityID, int otherEntityID, out int entity) - { - return _relationsMatrix.TryGetValue(entityID, otherEntityID, out entity); - } - //public bool TryGetRelation(EcsSubject subject, int entityID, int otherEntityID, out int entity) - //{ - // return _source._relationsMatrix.TryGetValue(entityID, otherEntityID, out entity) && subject.IsMatches(entity); - //} - - //#region GetRelations - //private IdsLinkedList.Span GetRelations(int entityID) - //{ - // return _basket.GetSpanFor(entityID); - //} - ////ReadOnlySpan временная заглушка, потому тут будет спан из линкедлиста - ////public ReadOnlySpan GetRelationsWith(EcsSubject subject, int entityID) - ////{ - //// if (subject.World != _relationWorld) - //// throw new ArgumentException(); - //// throw new NotImplementedException(); - ////} - //#endregion - #endregion - - #region Other - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsArc(int arcEntityID) - { - if (arcEntityID <= 0 || arcEntityID >= _arkTargets.Length) - return false; - return !_arkTargets[arcEntityID].IsEmpty; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArcTargets GetArcTargets(int arcEntityID) - { - if (arcEntityID <= 0 || arcEntityID >= _arkTargets.Length) - throw new Exception(); - return _arkTargets[arcEntityID]; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IdsLinkedList.Span Get(int entityID) => _basket.GetSpanFor(entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IdsLinkedList.LongSpan GetLongs(int entityID) => _basket.GetLongSpanFor(_world, entityID); - #endregion - - #region Callbacks - void IEcsWorldEventListener.OnWorldResize(int newSize) - { - Array.Resize(ref _arkTargets, newSize); - } - void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { } - void IEcsWorldEventListener.OnWorldDestroy() { } - - void IEcsEntityEventListener.OnNewEntity(int entityID) { } - void IEcsEntityEventListener.OnDelEntity(int entityID) - { - ref ArcTargets rel = ref _arkTargets[entityID]; - if (_relationsMatrix.Contains(rel.start, rel.end)) - Del(rel.start, rel.end); - } - #endregion - - #region VertexWorldHandler - private class VertexWorldHandler : IEcsEntityEventListener - { - private readonly EcsEdge _source; - private readonly EcsWorld _world; - private readonly IdsBasket _basket; - - public VertexWorldHandler(EcsEdge source, EcsWorld world, IdsBasket basket) - { - _source = source; - _world = world; - _basket = basket; - } - - public void OnDelEntity(int entityID) - { - var span = _basket.GetSpanFor(entityID); - foreach (var arcEntityID in span) - { - } - } - public void OnNewEntity(int entityID) - { - - } - } - #endregion - } -} \ No newline at end of file diff --git a/src/EcsJoinGroup.cs b/src/EcsJoinGroup.cs index 3922626..554adf9 100644 --- a/src/EcsJoinGroup.cs +++ b/src/EcsJoinGroup.cs @@ -5,7 +5,7 @@ namespace DCFApixels.DragonECS { public class EcsJoinGroup { - private EcsEdge _source; + private EcsArc _source; private int[] _mapping; private int[] _counts; @@ -13,26 +13,26 @@ namespace DCFApixels.DragonECS internal bool _isReleased = true; #region Properites - public EcsEdge Edge => _source; + public EcsArc Edge => _source; #endregion #region Constrcutors/Dispose - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsJoinGroup New(EcsEdge edge) - { - return edge.GetFreeGroup(); - } - internal EcsJoinGroup(EcsEdge edge, int denseCapacity = 64) - { - _source = edge; - _source.RegisterGroup(this); - int capacity = edge.World.Capacity; - - _mapping = new int[capacity]; - _counts = new int[capacity]; - } - public void Dispose() => _source.ReleaseGroup(this); + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //public static EcsJoinGroup New(EcsArcController edge) + //{ + // return edge.GetFreeGroup(); + //} + //internal EcsJoinGroup(EcsArcController edge, int denseCapacity = 64) + //{ + // _source = edge; + // _source.RegisterGroup(this); + // int capacity = edge.World.Capacity; + // + // _mapping = new int[capacity]; + // _counts = new int[capacity]; + //} + //public void Dispose() => _source.ReleaseGroup(this); #endregion public void Add(int entityFrom, int entityTo) diff --git a/src/Utils/ArcTargets.cs b/src/Utils/ArcEntityInfo.cs similarity index 66% rename from src/Utils/ArcTargets.cs rename to src/Utils/ArcEntityInfo.cs index c90b65e..56ff965 100644 --- a/src/Utils/ArcTargets.cs +++ b/src/Utils/ArcEntityInfo.cs @@ -6,9 +6,9 @@ namespace DCFApixels.DragonECS { [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] [Serializable] - public readonly struct ArcTargets : IEquatable + public readonly struct ArcEntityInfo : IEquatable { - public static readonly ArcTargets Empty = new ArcTargets(); + public static readonly ArcEntityInfo Empty = new ArcEntityInfo(); /// Start vertex entity ID. public readonly int start; @@ -24,7 +24,7 @@ namespace DCFApixels.DragonECS #endregion [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ArcTargets(int startEntity, int endEntity) + internal ArcEntityInfo(int startEntity, int endEntity) { start = startEntity; end = endEntity; @@ -32,15 +32,15 @@ namespace DCFApixels.DragonECS #region operators [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(ArcTargets a, ArcTargets b) => a.start == b.start && a.end == b.end; + public static bool operator ==(ArcEntityInfo a, ArcEntityInfo b) => a.start == b.start && a.end == b.end; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(ArcTargets a, ArcTargets b) => a.start != b.start || a.end != b.end; + public static bool operator !=(ArcEntityInfo a, ArcEntityInfo b) => a.start != b.start || a.end != b.end; #endregion #region Other - public override bool Equals(object obj) => obj is ArcTargets targets && targets == this; + public override bool Equals(object obj) => obj is ArcEntityInfo targets && targets == this; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ArcTargets other) => this == other; + public bool Equals(ArcEntityInfo other) => this == other; [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => ~start ^ end; public override string ToString() => $"arc({start} -> {end})"; diff --git a/src/WorldGraph.cs b/src/WorldGraph.cs index fbdc572..0172f1b 100644 --- a/src/WorldGraph.cs +++ b/src/WorldGraph.cs @@ -6,40 +6,37 @@ namespace DCFApixels.DragonECS.Relations.Internal { internal static class WorldGraph { - private static readonly SparseArray64 _matrix = new SparseArray64(4); + private static readonly SparseArray64 _matrix = new SparseArray64(4); - internal static EcsEdge Register(EcsWorld world, EcsWorld otherWorld, EcsEdgeWorld edgeWorld) + internal static EcsArc Register(EcsWorld startWorld, EcsWorld endWorld, EcsArcWorld edgeWorld) { - int worldID = world.id; - int otherWorldID = otherWorld.id; + int startWorldID = startWorld.id; + int endWorldID = endWorld.id; #if DEBUG - if (_matrix.Contains(worldID, otherWorldID)) + if (_matrix.Contains(startWorldID, endWorldID)) throw new EcsFrameworkException(); #endif - EcsEdge edge = new EcsEdge(world, otherWorld, edgeWorld); - _matrix[worldID, otherWorldID] = edge; - _matrix[otherWorldID, worldID] = edge; + EcsArc edge = new EcsArc(startWorld, endWorld, edgeWorld); + _matrix[startWorldID, endWorldID] = edge; return edge; } - internal static void Unregister(EcsWorld world, EcsWorld otherWorld) + internal static void Unregister(EcsWorld startWorld, EcsWorld endWorld) { - int worldID = world.id; - int otherWorldID = otherWorld.id; - //var manager = _matrix[worldID, otherWorldID]; - _matrix.Remove(worldID, otherWorldID); - _matrix.Remove(otherWorldID, worldID); + int startWorldID = startWorld.id; + int endWorldID = endWorld.id; + _matrix.Remove(startWorldID, endWorldID); } - internal static EcsEdge Get(EcsWorld world, EcsWorld otherWorld) + internal static EcsArc Get(EcsWorld startWorld, EcsWorld otherWorld) { #if DEBUG - if (!_matrix.Contains(world.id, otherWorld.id)) + if (!_matrix.Contains(startWorld.id, otherWorld.id)) throw new EcsFrameworkException(); #endif - return _matrix[world.id, otherWorld.id]; + return _matrix[startWorld.id, otherWorld.id]; } - internal static bool HasEdge(EcsWorld world, EcsWorld otherWorld) => HasEdge(world.id, otherWorld.id); - internal static bool HasEdge(int worldID, int otherWorldID) => _matrix.Contains(worldID, otherWorldID); + internal static bool HasArc(EcsWorld startWorld, EcsWorld endWorld) => HasArc(startWorld.id, endWorld.id); + internal static bool HasArc(int startWorldID, int endWorldID) => _matrix.Contains(startWorldID, endWorldID); } } @@ -47,44 +44,55 @@ namespace DCFApixels.DragonECS { public static class WorldGraphExtensions { - public static EcsEdge SetEdgeWithSelf(this EcsWorld self) => SetEdgeWith(self, self); - public static EcsEdge SetEdgeWith(this EcsWorld self, EcsWorld otherWorld) + public static EcsArc SetLoopArc(this EcsWorld self) => SetArcWith(self, self); + public static EcsArc SetArcWith(this EcsWorld self, EcsWorld endWorld) { - if (self == null || otherWorld == null) + if (self == null || endWorld == null) throw new ArgumentNullException(); - return WorldGraph.Register(self, otherWorld, new EcsEdgeWorld()); - } - public static EcsEdge SetEdgeWithSelf(this EcsWorld self, EcsEdgeWorld edgeWorld) => SetEdgeWith(self, self, edgeWorld); - public static EcsEdge SetEdgeWith(this EcsWorld self, EcsWorld otherWorld, EcsEdgeWorld edgeWorld) - { - if (self == null || otherWorld == null || edgeWorld == null) - throw new ArgumentNullException(); - return WorldGraph.Register(self, otherWorld, edgeWorld); + return WorldGraph.Register(self, endWorld, new EcsArcWorld()); } - public static bool HasEdgeWithSelf(this EcsWorld self) => HasEdgeWith(self, self); - public static bool HasEdgeWith(this EcsWorld self, EcsWorld otherWorld) + public static EcsArc SetLoopArc(this EcsWorld self, EcsArcWorld edgeWorld) => SetEdgeWith(self, self, edgeWorld); + public static EcsArc SetEdgeWith(this EcsWorld startWorld, EcsWorld endWorld, EcsArcWorld edgeWorld) { - if (self == null || otherWorld == null) + if (startWorld == null || endWorld == null || edgeWorld == null) throw new ArgumentNullException(); - return WorldGraph.HasEdge(self, otherWorld); + return WorldGraph.Register(startWorld, endWorld, edgeWorld); } - public static EcsEdge GetEdgeWithSelf(this EcsWorld self) => GetEdgeWith(self, self); - public static EcsEdge GetEdgeWith(this EcsWorld self, EcsWorld otherWorld) + public static bool HasLoopArc(this EcsWorld self) => HasArcWith(self, self); + public static bool HasArcWith(this EcsWorld startWorld, EcsWorld endWorld) { - if (self == null || otherWorld == null) + if (startWorld == null || endWorld == null) throw new ArgumentNullException(); - return WorldGraph.Get(self, otherWorld); + return WorldGraph.HasArc(startWorld, endWorld); } - public static void DestroyEdgeWithSelf(this EcsWorld self) => DestroyEdgeWith(self, self); - public static void DestroyEdgeWith(this EcsWorld self, EcsWorld otherWorld) + public static EcsArc GetLoopArc(this EcsWorld self) => GetArcWith(self, self); + public static EcsArc GetArcWith(this EcsWorld startWorld, EcsWorld endWorld) { - if (self == null || otherWorld == null) + if (startWorld == null || endWorld == null) throw new ArgumentNullException(); - WorldGraph.Get(self, otherWorld).EdgeWorld.Destroy(); - WorldGraph.Unregister(self, otherWorld); + return WorldGraph.Get(startWorld, endWorld); + } + + public static bool TryGetLoopArc(this EcsWorld self, out EcsArc arc) => TryGetArcWith(self, self, out arc); + public static bool TryGetArcWith(this EcsWorld startWorld, EcsWorld endWorld, out EcsArc arc) + { + if (startWorld == null || endWorld == null) + throw new ArgumentNullException(); + bool result = WorldGraph.HasArc(startWorld, endWorld); + arc = result ? WorldGraph.Get(startWorld, endWorld) : null; + return result; + } + + public static void DestroyLoopArc(this EcsWorld self) => DestroyArcWith(self, self); + public static void DestroyArcWith(this EcsWorld startWorld, EcsWorld endWorld) + { + if (startWorld == null || endWorld == null) + throw new ArgumentNullException(); + WorldGraph.Get(startWorld, endWorld).ArcWorld.Destroy(); + WorldGraph.Unregister(startWorld, endWorld); } } }