This commit is contained in:
Mikhail 2024-01-29 17:40:38 +08:00
parent 0e5e1ac1a4
commit 22d873ed38
11 changed files with 653 additions and 887 deletions

409
src/Collections/EcsJoin.cs Normal file
View File

@ -0,0 +1,409 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using static DCFApixels.DragonECS.Relations.Utils.EcsJoin;
namespace DCFApixels.DragonECS.Relations.Utils
{
public readonly ref struct EcsReadonlyJoin
{
private readonly EcsJoin _source;
#region Properties
public bool IsNull => _source == null;
public EcsArc Arc
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.Arc; }
}
public EcsWorld StartWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.StartWorld; }
}
public EcsWorld EndWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.EndWorld; }
}
public EcsArcWorld ArcWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.ArcWorld; }
}
public int ArcWorldID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.ArcWorldID; }
}
public bool IsLoopArc
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.IsLoopArc; }
}
public EnumerableArcEnd this[int startEntityID]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source[startEntityID]; }
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsReadonlyJoin(EcsJoin source) { _source = source; }
#endregion
#region Methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int relEntityID) { return _source.Has(relEntityID); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasStart(int startEntityID) { return _source.HasStart(startEntityID); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasEnd(int endEntityID) { return _source.HasEnd(endEntityID); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EnumerableArcEnd GetRelEnds(int startEntityID) { return _source.GetRelEnds(startEntityID); }
#endregion
#region Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EcsJoin GetSource_Internal() => _source;
#endregion
#region Other
public override string ToString()
{
return _source != null ? _source.ToString() : "NULL";
}
#pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член
[Obsolete("Equals() on EcsGroup will always throw an exception. Use the equality operator instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj) => throw new NotSupportedException();
[Obsolete("GetHashCode() on EcsGroup will always throw an exception.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => throw new NotSupportedException();
#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член
#endregion
}
//[DebuggerTypeProxy(typeof(DebuggerProxy))]
public class EcsJoin
{
private readonly EcsArc _source;
private readonly bool _isLoop;
private readonly BasketList _startBaskets;
private readonly BasketList _endBaskets;
private readonly RelInfo[] _relMapping;
#region Properties
public EcsReadonlyJoin Readonly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return new EcsReadonlyJoin(this); }
}
public EcsArc Arc
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source; }
}
public EcsWorld StartWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.StartWorld; }
}
public EcsWorld EndWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.EndWorld; }
}
public EcsArcWorld ArcWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.ArcWorld; }
}
public int ArcWorldID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.ArcWorldID; }
}
public bool IsLoopArc
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _isLoop; }
}
public EnumerableArcEnd this[int startEntityID]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return GetRelEnds(startEntityID); }
}
#endregion
#region Constructors
public EcsJoin(EcsArc arc)
{
_source = arc;
_isLoop = arc.IsLoop;
_startBaskets = new BasketList();
if (_isLoop)
{
_endBaskets = _startBaskets;
}
else
{
_endBaskets = new BasketList();
}
_relMapping = new RelInfo[arc.ArcWorld.Capacity];
}
#endregion
#region Add/Del
public void Add(int relEntityID)
{
var (startEntityID, endEntityID) = _source.GetRelInfo(relEntityID);
ref RelInfo arcInfo = ref _relMapping[relEntityID];
arcInfo.startNodeIndex = _startBaskets.AddToBasket(startEntityID, relEntityID);
if (_isLoop)
{
arcInfo.endNodeIndex = arcInfo.startNodeIndex;
}
else
{
arcInfo.endNodeIndex = _endBaskets.AddToBasket(endEntityID, relEntityID);
}
//arcInfo.endNodeIndex = _endBaskets.AddToBasket(endEntityID, relEntityID);
//if (!_isLoop)
//{
// arcInfo.startNodeIndex = _startBaskets.AddToBasket(startEntityID, relEntityID);
//}
//else
//{
// arcInfo.startNodeIndex = arcInfo.endNodeIndex;
//}
}
public void Del(int relEntityID)
{
var (startEntityID, endEntityID) = _source.GetRelInfo(relEntityID);
ref RelInfo relInfo = ref _relMapping[relEntityID];
_startBaskets.RemoveFromBasket(startEntityID, relInfo.startNodeIndex);
if (!_isLoop)
{
_startBaskets.RemoveFromBasket(endEntityID, relInfo.endNodeIndex);
}
}
public void DelStart(int startEntityID)
{
foreach (var relEntityID in _startBaskets.GetBasketIterator(startEntityID))
{
var endEntityID = _source.GetRelEnd(relEntityID);
ref RelInfo relInfo = ref _relMapping[relEntityID];
_endBaskets.RemoveFromBasket(endEntityID, relInfo.startNodeIndex);
}
_startBaskets.RemoveBasket(startEntityID);
}
public void DelEnd(int endEntityID)
{
foreach (var relEntityID in _endBaskets.GetBasketIterator(endEntityID))
{
var startEntityID = _source.GetRelStart(relEntityID);
ref RelInfo relInfo = ref _relMapping[relEntityID];
_startBaskets.RemoveFromBasket(startEntityID, relInfo.endNodeIndex);
}
_endBaskets.RemoveBasket(endEntityID);
}
#endregion
#region Has
public bool Has(int relEntityID)
{
return _relMapping[relEntityID] != RelInfo.Empty;
}
public bool HasStart(int startEntityID)
{
return _startBaskets.GetBasketNodesCount(startEntityID) > 0;
}
public bool HasEnd(int endEntityID)
{
return _endBaskets.GetBasketNodesCount(endEntityID) > 0;
}
#endregion
#region Clear
public void Clear()
{
_startBaskets.Clear();
if (!_isLoop)
{
_endBaskets.Clear();
}
}
#endregion
#region GetRelEnds
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EnumerableArcEnd GetRelEnds(int startEntityID)
{
return new EnumerableArcEnd(_source, _startBaskets.GetBasketIterator(startEntityID).GetEnumerator());
}
#endregion
#region EnumerableArcEnd
public readonly ref struct EnumerableArcEnd //: IEnumerable<RelEnd>
{
private readonly EcsArc _arc;
private readonly BasketList.BasketIterator.Enumerator _iterator;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EnumerableArcEnd(EcsArc arc, BasketList.BasketIterator.Enumerator iterator)
{
_arc = arc;
_iterator = iterator;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() { return new Enumerator(_arc, _iterator); }
//IEnumerator<RelEnd> IEnumerable<RelEnd>.GetEnumerator() { return GetEnumerator(); }
//IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public ref struct Enumerator //: IEnumerator<RelEnd>
{
private readonly EcsArc _arc;
private BasketList.BasketIterator.Enumerator _iterator;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(EcsArc arc, BasketList.BasketIterator.Enumerator iterator)
{
_arc = arc;
_iterator = iterator;
}
public RelEnd Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
int currentArc = _iterator.Current;
return new RelEnd(currentArc, _arc.GetRelEnd(currentArc));
}
}
//object IEnumerator.Current => Current;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() { return _iterator.MoveNext(); }
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public void Reset() { }
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public void Dispose() { }
}
}
#endregion
#region ArcInfo
private struct RelInfo : IEquatable<RelInfo>
{
public readonly static RelInfo Empty = default;
public int startNodeIndex;
public int endNodeIndex;
#region Object
public override bool Equals(object obj)
{
return obj is RelInfo && Equals((RelInfo)obj);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(RelInfo other)
{
return startNodeIndex == other.startNodeIndex &&
endNodeIndex == other.endNodeIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return ~startNodeIndex ^ endNodeIndex;
}
#endregion
#region operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(RelInfo a, RelInfo b) => a.startNodeIndex == b.startNodeIndex && a.endNodeIndex == b.endNodeIndex;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(RelInfo a, RelInfo b) => a.startNodeIndex != b.startNodeIndex || a.endNodeIndex != b.endNodeIndex;
#endregion
}
#endregion
#region Operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator EcsReadonlyJoin(EcsJoin a) => a.Readonly;
#endregion
#region DebuggerProxy
//internal class DebuggerProxy
//{
// private EcsJoinGroup _basket;
//
// public SpanDebugInfo[] HeadSpans => GetSpans(_basket._startList, _basket._startMapping);
// public SpanDebugInfo[] ValueSpans => GetSpans(_basket._endList, _basket._endMapping);
//
// private SpanDebugInfo[] GetSpans(IdsLinkedList list, SpanInfo[] mapping)
// {
// SpanDebugInfo[] result = new SpanDebugInfo[mapping.Length];
// for (int i = 0; i < mapping.Length; i++)
// result[i] = new SpanDebugInfo(list, mapping[i].nodeIndex, mapping[i].count);
// return result;
// }
// public DebuggerProxy(EcsJoinGroup basket)
// {
// _basket = basket;
// }
// public struct SpanDebugInfo
// {
// private IdsLinkedList _list;
// public int index;
// public int count;
// public NodeDebugInfo[] Nodes
// {
// get
// {
// var result = new NodeDebugInfo[this.count];
// var nodes = _list.Nodes;
// int index;
// int count = this.count;
// int next = this.index;
// int i = 0;
// while (true)
// {
// index = next;
// next = nodes[next].next;
// if (!(index > 0 && count-- > 0))
// break;
// var node = nodes[index];
// result[i] = new NodeDebugInfo(index, node.prev, node.next, node.value);
// i++;
// }
// return result;
// }
// }
// public SpanDebugInfo(IdsLinkedList list, int index, int count)
// {
// _list = list;
// this.index = index;
// this.count = count;
// }
// public override string ToString() => $"[{index}] {count}";
// }
// public struct NodeDebugInfo
// {
// public int index;
// public int prev;
// public int next;
// public int value;
// public NodeDebugInfo(int index, int prev, int next, int value)
// {
// this.index = index;
// this.prev = prev;
// this.next = next;
// this.value = value;
// }
// public override string ToString() => $"[{index}] {prev}_{next} - {value}";
// }
//}
#endregion
}
}

View File

@ -1,12 +1,14 @@
using DCFApixels.DragonECS.Relations.Utils;
using Leopotam.EcsLite;
using System;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
//Edge world
//Relation entity
//Relation component
//Arc
//Arc world
//Rel entity
//Component
public class EcsArc
{
private readonly EcsWorld _startWorld;
@ -19,15 +21,48 @@ namespace DCFApixels.DragonECS
private readonly SparseArray64<int> _relationsMatrix = new SparseArray64<int>();
private EcsGroup _arcEntities;
private ArcEntityInfo[] _arkEntityInfos; //N * (N - 1) / 2
private EcsJoin _joinEntities;
private EcsGroup _relEntities;
private RelEntityInfo[] _relEntityInfos; //N * (N - 1) / 2
private bool _isLoop;
#region Properties
public EcsWorld StartWorld => _startWorld;
public EcsWorld EndWorld => _endWorld;
public EcsArcWorld ArcWorld => _arcWorld;
public EcsReadonlyGroup ArcEntities => _arcEntities.Readonly;
public bool IsLoop => _startWorld == _endWorld;
public EcsWorld StartWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _startWorld; }
}
public EcsWorld EndWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _endWorld; }
}
public EcsArcWorld ArcWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _arcWorld; }
}
public int ArcWorldID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _arcWorld.id; }
}
public EcsReadonlyGroup RelEntities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _relEntities.Readonly; }
}
public EcsReadonlyJoin JoinEntities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _joinEntities.Readonly; }
}
public bool IsLoop
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _isLoop; }
}
#endregion
#region Constructors
@ -37,44 +72,51 @@ namespace DCFApixels.DragonECS
_endWorld = endWorld;
_arcWorld = arcWorld;
_arkEntityInfos = new ArcEntityInfo[arcWorld.Capacity];
_isLoop = startWorld == endWorld;
_startWorldHandler = new StartWorldHandler(this, _startWorld);
_relEntityInfos = new RelEntityInfo[arcWorld.Capacity];
_startWorldHandler = new StartWorldHandler(this);
_arcWorldHandler = new ArcWorldHandler(this);
_endWorldHandler = new EndWorldHandler(this, _endWorld);
if (!_isLoop)
{
_endWorldHandler = new EndWorldHandler(this);
}
_startWorld.AddListener(worldEventListener: _startWorldHandler);
_startWorld.AddListener(entityEventListener: _startWorldHandler);
_arcWorld.AddListener(worldEventListener: _arcWorldHandler);
_arcWorld.AddListener(entityEventListener: _arcWorldHandler);
_endWorld.AddListener(worldEventListener: _endWorldHandler);
_endWorld.AddListener(entityEventListener: _endWorldHandler);
_relEntities = EcsGroup.New(_arcWorld);
_joinEntities = new EcsJoin(this);
}
#endregion
#region New/Del
public int New(int startEntityID, int endEntityID)
public int NewRelation(int startEntityID, int endEntityID)
{
if (Has(startEntityID, endEntityID))
throw new EcsRelationException();
int arcEntity = _arcWorld.NewEntity();
_relationsMatrix.Add(startEntityID, endEntityID, arcEntity);
_arkEntityInfos[arcEntity] = new ArcEntityInfo(startEntityID, endEntityID);
_arcEntities.Add(arcEntity);
return arcEntity;
}
public void Del(int startEntityID, int endEntityID)
{
if (!_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int arcEntity))
throw new EcsRelationException();
}
int relEntity = _arcWorld.NewEntity();
_relationsMatrix.Add(startEntityID, endEntityID, relEntity);
_relEntityInfos[relEntity] = new RelEntityInfo(startEntityID, endEntityID);
_relEntities.Add(relEntity);
_joinEntities.Add(relEntity);
return relEntity;
}
public void DelRelation(int startEntityID, int endEntityID)
{
if (!_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int relEntity))
{
throw new EcsRelationException();
}
_joinEntities.Del(relEntity);
_relationsMatrix.Remove(startEntityID, endEntityID);
_arcWorld.DelEntity(arcEntity);
_arcWorld.DelEntity(relEntity);
_arkEntityInfos[arcEntity] = ArcEntityInfo.Empty;
_arcEntities.Remove(arcEntity);
_relEntityInfos[relEntity] = RelEntityInfo.Empty;
_relEntities.Remove(relEntity);
}
#endregion
@ -97,28 +139,28 @@ namespace DCFApixels.DragonECS
#region ArcEntityInfo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsArc(int arcEntityID)
public bool IsRel(int arcEntityID)
{
if (arcEntityID <= 0 || arcEntityID >= _arkEntityInfos.Length)
if (arcEntityID <= 0 || arcEntityID >= _relEntityInfos.Length)
return false;
return !_arkEntityInfos[arcEntityID].IsEmpty;
return !_relEntityInfos[arcEntityID].IsEmpty;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArcEntityInfo GetArcInfo(int arcEntityID)
public RelEntityInfo GetRelInfo(int arcEntityID)
{
if (arcEntityID <= 0 || arcEntityID >= _arkEntityInfos.Length)
if (arcEntityID <= 0 || arcEntityID >= _relEntityInfos.Length)
throw new Exception();
return _arkEntityInfos[arcEntityID];
return _relEntityInfos[arcEntityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetArcStart(int arcEntityID)
public int GetRelStart(int arcEntityID)
{
return GetArcInfo(arcEntityID).start;
return GetRelInfo(arcEntityID).start;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetArcEnd(int arcEntityID)
public int GetRelEnd(int arcEntityID)
{
return GetArcInfo(arcEntityID).end;
return GetRelInfo(arcEntityID).end;
}
#endregion
@ -138,93 +180,84 @@ namespace DCFApixels.DragonECS
private class ArcWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
{
private readonly EcsArc _arc;
public ArcWorldHandler(EcsArc arc)
{
_arc = arc;
EcsArcWorld arcWorld = arc.ArcWorld;
arcWorld.AddListener(worldEventListener: this);
arcWorld.AddListener(entityEventListener: this);
}
#region Callbacks
public void OnDelEntity(int entityID)
{
ref ArcEntityInfo rel = ref _arc._arkEntityInfos[entityID];
if (_arc._relationsMatrix.Contains(rel.start, rel.end))
{
_arc.Del(rel.start, rel.end);
}
}
public void OnNewEntity(int entityID)
{
}
public void OnDelEntity(int entityID) { }
public void OnNewEntity(int entityID) { }
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
foreach (var relEntityID in buffer)
{
ref RelEntityInfo rel = ref _arc._relEntityInfos[relEntityID];
if (_arc._relationsMatrix.Contains(rel.start, rel.end))
{
_arc.DelRelation(rel.start, rel.end);
}
}
_arc._arcWorld.ReleaseDelEntityBuffer(buffer.Length);
}
public void OnWorldDestroy()
{
}
public void OnWorldDestroy() { }
public void OnWorldResize(int newSize)
{
Array.Resize(ref _arc._arkEntityInfos, newSize);
Array.Resize(ref _arc._relEntityInfos, newSize);
}
#endregion
}
private class StartWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
{
private readonly EcsArc _arc;
private readonly EcsWorld _start;
public StartWorldHandler(EcsArc arc, EcsWorld world)
public StartWorldHandler(EcsArc arc)
{
_arc = arc;
_start = world;
EcsWorld startWorld = arc.StartWorld;
startWorld.AddListener(worldEventListener: this);
startWorld.AddListener(entityEventListener: this);
}
#region Callbacks
public void OnDelEntity(int entityID)
{
}
public void OnNewEntity(int entityID)
{
}
public void OnDelEntity(int startEntityID) { }
public void OnNewEntity(int startEntityID) { }
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
}
public void OnWorldDestroy()
foreach (var startEntityID in buffer)
{
_arc._joinEntities.DelStart(startEntityID);
}
public void OnWorldResize(int newSize)
{
}
public void OnWorldDestroy() { }
public void OnWorldResize(int newSize) { }
#endregion
}
private class EndWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
{
private readonly EcsArc _arc;
private readonly EcsWorld _end;
public EndWorldHandler(EcsArc arc, EcsWorld world)
public EndWorldHandler(EcsArc arc)
{
_arc = arc;
_end = world;
EcsWorld endWorld = arc.EndWorld;
endWorld.AddListener(worldEventListener: this);
endWorld.AddListener(entityEventListener: this);
}
#region Callbacks
public void OnDelEntity(int entityID)
{
}
public void OnNewEntity(int entityID)
{
}
public void OnDelEntity(int endEntityID) { }
public void OnNewEntity(int endEntityID) { }
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
}
public void OnWorldDestroy()
foreach (var endEntityID in buffer)
{
_arc._joinEntities.DelEnd(endEntityID);
}
public void OnWorldResize(int newSize)
{
}
public void OnWorldDestroy() { }
public void OnWorldResize(int newSize) { }
#endregion
}
#endregion

View File

@ -1,68 +0,0 @@
using DCFApixels.DragonECS.Relations.Utils;
namespace DCFApixels.DragonECS
{
public class EcsJoinGroup_OLD
{
private EcsArc _source;
private int[] _mapping;
private int[] _counts;
private IdsLinkedList _linkedList;
internal bool _isReleased = true;
#region Properites
public EcsArc Edge => _source;
#endregion
#region Constrcutors/Dispose
//[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)
{
ref int nodeIndex = ref _mapping[entityFrom];
if (nodeIndex <= 0)
{
nodeIndex = _linkedList.Add(entityTo);
_counts[entityFrom] = 1;
}
else
{
_linkedList.InsertAfter(nodeIndex, entityTo);
_counts[entityFrom]++;
}
}
public IdsLinkedList.Span GetEntitiesFor(int entity)
{
ref var nodeIndex = ref _mapping[entity];
if (nodeIndex <= 0)
return _linkedList.EmptySpan();
else
return _linkedList.GetSpan(nodeIndex, _counts[entity]);
}
public void Clear()
{
_linkedList.Clear();
for (int i = 0; i < _mapping.Length; i++)
_mapping[i] = 0;
}
}
}

View File

@ -4,11 +4,12 @@ using System;
namespace DCFApixels.DragonECS
{
public static class WorldGraph
public static class EcsWorldGraph
{
private static readonly SparseArray64<EcsArc> _matrix = new SparseArray64<EcsArc>(4);
private static EcsArc[] _arcsMapping = new EcsArc[4];
#region Register/Unregister
private static EcsArc Register(EcsWorld startWorld, EcsWorld endWorld, EcsArcWorld arcWorld)
{
int startWorldID = startWorld.id;
@ -39,7 +40,19 @@ namespace DCFApixels.DragonECS
_arcsMapping[arc.ArcWorld.id] = null;
_matrix.Remove(startWorldID, endWorldID);
}
#endregion
#region Get/Has
// private static EcsArc GetOrRigister(EcsWorld startWorld, EcsWorld otherWorld)
// {
//#if DEBUG
// if (!_matrix.Contains(startWorld.id, otherWorld.id))
// {
// return Register();
// }
//#endif
// return _matrix[startWorld.id, otherWorld.id];
// }
private static EcsArc Get(EcsWorld startWorld, EcsWorld otherWorld)
{
#if DEBUG
@ -52,9 +65,7 @@ namespace DCFApixels.DragonECS
}
private static bool Has(EcsWorld startWorld, EcsWorld endWorld) => Has(startWorld.id, endWorld.id);
private static bool Has(int startWorldID, int endWorldID) => _matrix.Contains(startWorldID, endWorldID);
#endregion
#region Extension
public static bool IsRegistered(this EcsArcWorld self)
@ -82,8 +93,8 @@ namespace DCFApixels.DragonECS
public static EcsArc SetLoopArc(this EcsWorld self) => SetArc(self, self);
public static EcsArc SetArc(this EcsWorld start, EcsWorld end)
public static EcsArc SetLoopArcAuto(this EcsWorld self) => SetArcAuto(self, self);
public static EcsArc SetArcAuto(this EcsWorld start, EcsWorld end)
{
if (start == null || end == null)
{

View File

@ -1,13 +1,15 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
public class BasketList
[DebuggerTypeProxy(typeof(DebuggerProxy))]
internal class BasketList
{
public const int RECYCLE = -1;
public const int HEAD = 0;
@ -16,10 +18,6 @@ namespace DCFApixels.DragonECS
private Node[] _nodes;
private int _recycledListLast = -1;
private int _basketsCount = 0;
private int[] _recycledBuskets = new int[64];
private int _recycledBusketsCount = 0;
#region Constructors
public BasketList() : this(16) { }
public BasketList(int capacity)
@ -86,6 +84,11 @@ namespace DCFApixels.DragonECS
LinkToRecycled(newSize - 1, 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetBasketNodesCount(int basketIndex)
{
return _baskets[basketIndex].count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -108,7 +111,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool RemoveFromBasketAt(int basketIndex, int nodeIndex)
public void RemoveFromBasket(int basketIndex, int nodeIndex)
{
#if DEBUG
if (nodeIndex <= 0)
@ -126,11 +129,11 @@ namespace DCFApixels.DragonECS
{
basketInfo.nodeIndex = nextNode;
}
return --basketInfo.count > 0;
basketInfo.count--;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int InsertToBasket(int basketIndex, int value)
public int AddToBasket(int basketIndex, int value)
{
ref BasketInfo basketInfo = ref _baskets[basketIndex];
int newNodeIndex = TakeRecycledNode();
@ -273,6 +276,11 @@ namespace DCFApixels.DragonECS
// return newBasketIndex;
//}
public static void CreateCrossRef(int leftBasketIndex, int rightBasketIndex)
{
}
#region Node
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
@ -306,86 +314,54 @@ namespace DCFApixels.DragonECS
#endregion
#region Basket
public Basket this[int basketIndex]
public BasketIterator this[int basketIndex]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetBasket(basketIndex);
get => GetBasketIterator(basketIndex);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Basket GetBasket(int basketIndex)
public BasketIterator GetBasketIterator(int basketIndex)
{
if (_baskets.Length <= basketIndex)
{
int newSize = GetHighBitNumber((uint)basketIndex) << 1;
Array.Resize(ref _baskets, newSize);
}
return new Basket(this, basketIndex);
return new BasketIterator(this, basketIndex);
}
public struct Basket : IEnumerable<int>
public readonly struct BasketIterator : IEnumerable<int>
{
private readonly BasketList _basketList;
public readonly int _basketIndex;
private int _count;
private readonly int _basketIndex;
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
}
public int BasketIndex
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _basketIndex;
}
public int StartNodeIndex
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _basketList._baskets[_basketIndex].nodeIndex;
get { return _basketList._baskets[_basketIndex].count; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Basket(BasketList basketList, int basketIndex)
public BasketIterator(BasketList basketList, int basketIndex)
{
_basketList = basketList;
_basketIndex = basketIndex;
_count = _basketList._baskets[basketIndex].count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Add(int value)
{
_count++;
return _basketList.InsertToBasket(_basketIndex, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int nodeIndex)
{
#if DEBUG
if (_count <= 0)
throw new Exception();
#endif
_basketList.RemoveFromBasketAt(_basketIndex, nodeIndex);
_count--;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<int>
{
private readonly Node[] _nodes;
private int _nodeIndex;
private int _nextNode;
private int _nextNodeIndex;
private int _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator(Basket iterator)
public Enumerator(BasketIterator iterator)
{
ref BasketInfo basketInfo = ref iterator._basketList._baskets[iterator._basketIndex];
_nodes = iterator._basketList._nodes;
_nodeIndex = -1;
_nextNode = basketInfo.nodeIndex;
_nextNodeIndex = basketInfo.nodeIndex;
_count = basketInfo.count;
}
public int Current
@ -398,8 +374,8 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_nodeIndex = _nextNode;
_nextNode = _nodes[_nextNode].next;
_nodeIndex = _nextNodeIndex;
_nextNodeIndex = _nodes[_nextNodeIndex].next;
return _nodeIndex > 0 && _count-- > 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -409,5 +385,56 @@ namespace DCFApixels.DragonECS
}
}
#endregion
#region DebuggerProxy
private class DebuggerProxy
{
private BasketList _basketList;
public IEnumerable<BasketIteratorDebbugerProxy> Baskets
{
get
{
List<BasketIteratorDebbugerProxy> result = new List<BasketIteratorDebbugerProxy>();
for (int i = 0; i < _basketList._baskets.Length; i++)
{
if (_basketList._baskets[i].count > 0)
{
result.Add(new BasketIteratorDebbugerProxy(_basketList[i]));
}
}
return result;
}
}
public DebuggerProxy(BasketList basketList)
{
_basketList = basketList;
}
public struct BasketIteratorDebbugerProxy
{
private BasketIterator _iterrator;
public int Count => _iterrator.Count;
public IEnumerable<int> RelEntities
{
get
{
List<int> result = new List<int>();
foreach (var e in _iterrator)
{
result.Add(e);
}
return result;
}
}
public BasketIteratorDebbugerProxy(BasketIterator iterrator)
{
_iterrator = iterrator;
}
public override string ToString()
{
return $"count: {_iterrator.Count}";
}
}
}
#endregion
}
}

View File

@ -1,242 +0,0 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS.Relations.Utils
{
[DebuggerTypeProxy(typeof(DebuggerProxy))]
internal class EcsJoinGroup
{
private EcsArc _arc;
private readonly bool _isLoop;
private BasketList _startBaskets;
private BasketList _endBaskets;
private ArcInfo[] _arcMapping;
public EcsJoinGroup(EcsArc arc)
{
_arc = arc;
_isLoop = arc.IsLoop;
_startBaskets = new BasketList();
if (_isLoop)
{
_endBaskets = _startBaskets;
}
else
{
_endBaskets = new BasketList();
}
_arcMapping = new ArcInfo[arc.ArcWorld.Capacity];
}
public void Clear()
{
_startBaskets.Clear();
if (!_isLoop)
{
_endBaskets.Clear();
}
}
public void Add(int arcEntityID)
{
var (startEntityID, endEntityID) = _arc.GetArcInfo(arcEntityID);
_startBaskets[startEntityID].Add(endEntityID);
_startBaskets[endEntityID].Add(startEntityID);
var (startEntityID, endEntityID) = _arc.GetArcInfo(arcEntityID);
ArcInfo arcInfo = _arcMapping[arcEntityID];
ref var startSpan = ref _startMapping[startEntityID];
if (startSpan.nodeIndex <= 0)
{
startSpan.nodeIndex = _startList.Add(endEntityID);
arcInfo.startNodeIndex = startSpan.nodeIndex;
}
else
{
arcInfo.startNodeIndex = _startList.InsertAfter(startSpan.nodeIndex, endEntityID);
}
startSpan.count++;
ref var endSpan = ref _endMapping[endEntityID];
if (endSpan.nodeIndex <= 0)
{
endSpan.nodeIndex = _endList.Add(startEntityID);
arcInfo.endNodeIndex = endSpan.nodeIndex;
}
else
{
arcInfo.endNodeIndex = _endList.InsertAfter(endSpan.nodeIndex, startEntityID);
}
endSpan.count++;
}
public void Del(int arcEntityID)
{
var (startEntityID, endEntityID) = _arc.GetArcInfo(arcEntityID);
ref ArcInfo arcInfo = ref _arcMapping[arcEntityID];
int nextIndex;
ref var startSpan = ref _startMapping[startEntityID];
nextIndex = _startList.RemoveIndexAndReturnNextIndex(arcInfo.startNodeIndex);
if (startSpan.nodeIndex == arcInfo.startNodeIndex)
{
startSpan.nodeIndex = nextIndex;
}
if (!_isLoop)
{
ref var endSpan = ref _endMapping[endEntityID];
nextIndex = _endList.RemoveIndexAndReturnNextIndex(arcInfo.endNodeIndex); ;
if (endSpan.nodeIndex == arcInfo.endNodeIndex)
{
endSpan.nodeIndex = nextIndex;
}
}
arcInfo = default;
}
public void DelEnd(int endEntityID)
{
DelPosInternal(_endMapping, _startList, _endList, endEntityID);
}
public void DelStart(int startEntityID)
{
DelPosInternal(_startMapping, _endList, _startList, startEntityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DelPosInternal(SpanInfo[] endMapping, IdsLinkedList startList, IdsLinkedList endList, int posEntityID)
{
ref var endSpan = ref endMapping[posEntityID];
if (endSpan.nodeIndex <= 0)
{
return;
}
foreach (var startNodeIndex in endList.GetSpan(endSpan.nodeIndex, endSpan.count))
{
int indx = startList.RemoveIndexAndReturnNextIndex(startNodeIndex);
if (endSpan.nodeIndex == startNodeIndex)
{
endSpan.nodeIndex = indx;
}
}
endList.RemoveSpan(endSpan.nodeIndex, endSpan.count);
endSpan = SpanInfo.Empty;
}
public IdsLinkedList.Span GetSpanFor(int startEntityID)
{
ref var head = ref _startMapping[startEntityID];
if (head.nodeIndex <= 0)
return _startList.EmptySpan();
else
return _startList.GetSpan(head.nodeIndex, head.count);
}
public IdsLinkedList.LongSpan GetLongSpanFor(EcsWorld world, int startEntityID)
{
ref var head = ref _startMapping[startEntityID];
if (head.nodeIndex <= 0)
return _startList.EmptyLongSpan(world);
else
return _startList.GetLongSpan(world, head.nodeIndex, head.count);
}
private struct SpanInfo
{
public static readonly SpanInfo Empty = default;
public int nodeIndex;
public int count;
}
private struct ArcInfo
{
public int startNodeIndex;
public int endNodeIndex;
}
#region DebuggerProxy
internal class DebuggerProxy
{
private EcsJoinGroup _basket;
public SpanDebugInfo[] HeadSpans => GetSpans(_basket._startList, _basket._startMapping);
public SpanDebugInfo[] ValueSpans => GetSpans(_basket._endList, _basket._endMapping);
private SpanDebugInfo[] GetSpans(IdsLinkedList list, SpanInfo[] mapping)
{
SpanDebugInfo[] result = new SpanDebugInfo[mapping.Length];
for (int i = 0; i < mapping.Length; i++)
result[i] = new SpanDebugInfo(list, mapping[i].nodeIndex, mapping[i].count);
return result;
}
public DebuggerProxy(EcsJoinGroup basket)
{
_basket = basket;
}
public struct SpanDebugInfo
{
private IdsLinkedList _list;
public int index;
public int count;
public NodeDebugInfo[] Nodes
{
get
{
var result = new NodeDebugInfo[this.count];
var nodes = _list.Nodes;
int index;
int count = this.count;
int next = this.index;
int i = 0;
while (true)
{
index = next;
next = nodes[next].next;
if (!(index > 0 && count-- > 0))
break;
var node = nodes[index];
result[i] = new NodeDebugInfo(index, node.prev, node.next, node.value);
i++;
}
return result;
}
}
public SpanDebugInfo(IdsLinkedList list, int index, int count)
{
_list = list;
this.index = index;
this.count = count;
}
public override string ToString() => $"[{index}] {count}";
}
public struct NodeDebugInfo
{
public int index;
public int prev;
public int next;
public int value;
public NodeDebugInfo(int index, int prev, int next, int value)
{
this.index = index;
this.prev = prev;
this.next = next;
this.value = value;
}
public override string ToString() => $"[{index}] {prev}_{next} - {value}";
}
}
#endregion
}
}

View File

@ -1,396 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Relations.Utils
{
[DebuggerTypeProxy(typeof(DebuggerProxy))]
public class IdsLinkedList : IEnumerable<int>
{
public const int Head = 0;
private Node[] _nodes;
private int _count;
private int _lastNodeIndex;
private int[] _recycledNodes = new int[4];
private int _recycledNodesCount;
#region Properties
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
}
public int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _nodes.Length;
}
public int Last
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _lastNodeIndex;
}
public ReadOnlySpan<Node> Nodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new ReadOnlySpan<Node>(_nodes);
}
#endregion
#region Constructors
public IdsLinkedList(int capacity)
{
_nodes = new Node[capacity + 10];
Clear();
}
#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
for (int i = 0; i < _nodes.Length; i++)
_nodes[i].next = 0;
_lastNodeIndex = Head;
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int nodeIndex, int value)
{
_nodes[nodeIndex].value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Get(int nodeIndex)
{
return _nodes[nodeIndex].value;
}
/// <summary> Insert after</summary>
/// <returns> new node index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int InsertAfter(int nodeIndex, int value)
{
if (++_count >= _nodes.Length)
{
Array.Resize(ref _nodes, _nodes.Length << 1);
}
int newNodeIndex = _recycledNodesCount > 0 ? _recycledNodes[--_recycledNodesCount] : _count;
ref Node prevNode = ref _nodes[nodeIndex];
ref Node nextNode = ref _nodes[prevNode.next];
if (prevNode.next == 0)
{
_lastNodeIndex = newNodeIndex;
}
_nodes[newNodeIndex].Set(value, nextNode.prev, prevNode.next);
prevNode.next = newNodeIndex;
nextNode.prev = newNodeIndex;
return newNodeIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int InsertBefore(int nodeIndex, int value)
{
if (++_count >= _nodes.Length)
Array.Resize(ref _nodes, _nodes.Length << 1);
int newNodeIndex = _recycledNodesCount > 0 ? _recycledNodes[--_recycledNodesCount] : _count;
ref Node nextNode = ref _nodes[nodeIndex];
ref Node prevNode = ref _nodes[nextNode.prev];
_nodes[newNodeIndex].Set(value, nextNode.prev, prevNode.next);
prevNode.next = newNodeIndex;
nextNode.prev = newNodeIndex;
return newNodeIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveIndex(int nodeIndex)
{
if (nodeIndex <= 0)
throw new ArgumentOutOfRangeException();
ref var node = ref _nodes[nodeIndex];
_nodes[node.next].prev = node.prev;
_nodes[node.prev].next = node.next;
if (_recycledNodesCount >= _recycledNodes.Length)
Array.Resize(ref _recycledNodes, _recycledNodes.Length << 1);
_recycledNodes[_recycledNodesCount++] = nodeIndex;
_count--;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int RemoveIndexAndReturnNextIndex(int nodeIndex)
{
if (nodeIndex <= 0)
throw new ArgumentOutOfRangeException();
ref var node = ref _nodes[nodeIndex];
int nextNode = node.next;
_nodes[node.next].prev = node.prev;
_nodes[node.prev].next = nextNode;
if (_recycledNodesCount >= _recycledNodes.Length)
Array.Resize(ref _recycledNodes, _recycledNodes.Length << 1);
_recycledNodes[_recycledNodesCount++] = nodeIndex;
_count--;
return nextNode;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveSpan(int startNodeIndex, int count)
{
if (count <= 0)
return;
int endNodeIndex = startNodeIndex;
if (_recycledNodesCount >= _recycledNodes.Length)
Array.Resize(ref _recycledNodes, _recycledNodes.Length << 1);
_recycledNodes[_recycledNodesCount++] = startNodeIndex;
for (int i = 1; i < count; i++)
{
endNodeIndex = _nodes[endNodeIndex].next;
if (endNodeIndex == 0)
throw new ArgumentOutOfRangeException();
if (_recycledNodesCount >= _recycledNodes.Length)
Array.Resize(ref _recycledNodes, _recycledNodes.Length << 1);
_recycledNodes[_recycledNodesCount++] = endNodeIndex;
}
ref var startNode = ref _nodes[startNodeIndex];
ref var endNode = ref _nodes[endNodeIndex];
_nodes[endNode.next].prev = startNode.prev;
_nodes[startNode.prev].next = endNode.next;
_count -= count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Add(int id)
{
return InsertAfter(_lastNodeIndex, id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly Node GetNode(int nodeIndex)
{
return ref _nodes[nodeIndex];
}
#region Span/Enumerator
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanEnumerator GetEnumerator() => new SpanEnumerator(_nodes, _nodes[Head].next, _count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span GetSpan(int startNodeIndex, int count) => new Span(this, startNodeIndex, count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span EmptySpan() => new Span(this, 0, 0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongSpan GetLongs(EcsWorld world) => new LongSpan(world, this, _nodes[Head].next, _count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongSpan GetLongSpan(EcsWorld world, int startNodeIndex, int count) => new LongSpan(world, this, startNodeIndex, count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongSpan EmptyLongSpan(EcsWorld world) => new LongSpan(world, this, 0, 0);
public readonly ref struct Span
{
private readonly IdsLinkedList _source;
private readonly int _startNodeIndex;
private readonly int _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span(IdsLinkedList source, int startNodeIndex, int count)
{
_source = source;
_startNodeIndex = startNodeIndex;
_count = count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanEnumerator GetEnumerator() => new SpanEnumerator(_source._nodes, _startNodeIndex, _count);
}
public struct SpanEnumerator : IEnumerator<int>
{
private readonly Node[] _nodes;
private int _count;
private int _index;
private int _next;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanEnumerator(Node[] nodes, int startIndex, int count)
{
_nodes = nodes;
_index = -1;
_count = count;
_next = startIndex;
}
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _nodes[_index].value;
}
}
object IEnumerator.Current => Current;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index = _next;
_next = _nodes[_next].next;
return _index > 0 && _count-- > 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void IDisposable.Dispose() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void IEnumerator.Reset()
{
_index = -1;
_next = Head;
}
}
public readonly ref struct LongSpan
{
private readonly EcsWorld _world;
private readonly IdsLinkedList _source;
private readonly int _startNodeIndex;
private readonly int _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongSpan(EcsWorld world, IdsLinkedList source, int startNodeIndex, int count)
{
_world = world;
_source = source;
_startNodeIndex = startNodeIndex;
_count = count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongSpanEnumerator GetEnumerator() => new LongSpanEnumerator(_world, _source._nodes, _startNodeIndex, _count);
}
public struct LongSpanEnumerator : IEnumerator<entlong>
{
private EcsWorld _world;
private readonly Node[] _nodes;
private int _count;
private int _index;
private int _next;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongSpanEnumerator(EcsWorld world, Node[] nodes, int startIndex, int count)
{
_world = world;
_nodes = nodes;
_index = -1;
_count = count;
_next = startIndex;
}
public entlong Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _world.GetEntityLong(_nodes[_index].value);
}
}
object IEnumerator.Current => Current;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index = _next;
_next = _nodes[_next].next;
return _index > 0 && _count-- > 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void IDisposable.Dispose() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void IEnumerator.Reset()
{
_index = -1;
_next = Head;
}
}
#endregion
#region Node
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
public struct Node
{
public static readonly Node Empty = new Node() { value = 0, next = -1 };
public int value;
/// <summary>next node index</summary>
public int next;
/// <summary>prev node index</summary>
public int prev;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int value, int prev, int next)
{
this.value = value;
this.next = next;
this.prev = prev;
}
public override string ToString() => $"node({prev}<>{next} v:{value})";
}
#endregion
#region Debug
internal class DebuggerProxy
{
private IdsLinkedList list;
public NodeInfo[] Nodes
{
get
{
var result = new NodeInfo[list.Count];
Node[] nodes = list._nodes;
int index = -1;
int count = list._count;
int next = list._nodes[Head].next;
int i = 0;
while (true)
{
index = next;
next = nodes[next].next;
if (!(index > 0 && count-- > 0))
break;
ref var node = ref nodes[index];
result[i] = new NodeInfo(index, node.prev, node.next, node.value);
i++;
}
return result;
}
}
public DebuggerProxy(IdsLinkedList list)
{
this.list = list;
}
public struct NodeInfo
{
public int index;
public int prev;
public int next;
public int value;
public NodeInfo(int index, int prev, int next, int value)
{
this.index = index;
this.prev = prev;
this.next = next;
this.value = value;
}
public override string ToString() => $"[{index}] {prev}_{next} - {value}";
}
}
#endregion
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9d852affe1595eb4b9d8eb8dc6f28720
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,46 +1,47 @@
using System.Runtime.CompilerServices;
using System;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
public readonly struct StartArcEnd
public readonly ref struct StartRelEnd
{
/// <summary>Start vertex entity ID.</summary>
public readonly int start;
/// <summary>Arc entity ID.</summary>
public readonly int arc;
/// <summary>Relation entity ID.</summary>
public readonly int rel;
/// <summary>End vertex entity ID.</summary>
public readonly int end;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public StartArcEnd(int start, int arc, int end)
public StartRelEnd(int start, int rel, int end)
{
this.start = start;
this.arc = arc;
this.rel = rel;
this.end = end;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out int start, out int arc, out int end)
public void Deconstruct(out int start, out int rel, out int end)
{
start = this.start;
arc = this.arc;
rel = this.rel;
end = this.end;
}
}
public readonly struct ArcEnd
public readonly ref struct RelEnd
{
/// <summary>Arc entity ID.</summary>
public readonly int arc;
/// <summary>Relation entity ID.</summary>
public readonly int rel;
/// <summary>End vertex entity ID.</summary>
public readonly int end;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArcEnd(int arc, int end)
public RelEnd(int rel, int end)
{
this.arc = arc;
this.rel = rel;
this.end = end;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out int arc, out int end)
public void Deconstruct(out int rel, out int end)
{
arc = this.arc;
rel = this.rel;
end = this.end;
}
}

View File

@ -6,9 +6,9 @@ namespace DCFApixels.DragonECS
{
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
[Serializable]
public readonly struct ArcEntityInfo : IEquatable<ArcEntityInfo>
public readonly struct RelEntityInfo : IEquatable<RelEntityInfo>
{
public static readonly ArcEntityInfo Empty = new ArcEntityInfo();
public static readonly RelEntityInfo Empty = new RelEntityInfo();
/// <summary>Start vertex entity ID.</summary>
public readonly int start;
@ -24,7 +24,7 @@ namespace DCFApixels.DragonECS
#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ArcEntityInfo(int startEntity, int endEntity)
internal RelEntityInfo(int startEntity, int endEntity)
{
start = startEntity;
end = endEntity;
@ -38,15 +38,15 @@ namespace DCFApixels.DragonECS
#region operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(ArcEntityInfo a, ArcEntityInfo b) => a.start == b.start && a.end == b.end;
public static bool operator ==(RelEntityInfo a, RelEntityInfo b) => a.start == b.start && a.end == b.end;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(ArcEntityInfo a, ArcEntityInfo b) => a.start != b.start || a.end != b.end;
public static bool operator !=(RelEntityInfo a, RelEntityInfo b) => a.start != b.start || a.end != b.end;
#endregion
#region Other
public override bool Equals(object obj) => obj is ArcEntityInfo targets && targets == this;
public override bool Equals(object obj) => obj is RelEntityInfo targets && targets == this;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ArcEntityInfo other) => this == other;
public bool Equals(RelEntityInfo other) => this == other;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => ~start ^ end;
public override string ToString() => $"arc({start} -> {end})";

View File

@ -2,6 +2,7 @@
//Benchmark result of indexer.get speed test with 300 elements:
//[Dictinary: 6.705us] [SparseArray64: 2.512us].
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -225,6 +226,7 @@ namespace DCFApixels.DragonECS.Relations.Utils
#region Utils
[StructLayout(LayoutKind.Sequential, Pack = 4)]
[DebuggerDisplay("next{next} hashKey{hashKey} value{value}")]
private struct Entry
{
public int next; // Index of next entry, -1 if last