mirror of
https://github.com/DCFApixels/DragonECS-Graphs.git
synced 2025-09-18 20:04:35 +08:00
Update
This commit is contained in:
parent
0e5e1ac1a4
commit
22d873ed38
409
src/Collections/EcsJoin.cs
Normal file
409
src/Collections/EcsJoin.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
209
src/EcsArc.cs
209
src/EcsArc.cs
@ -1,12 +1,14 @@
|
|||||||
using DCFApixels.DragonECS.Relations.Utils;
|
using DCFApixels.DragonECS.Relations.Utils;
|
||||||
|
using Leopotam.EcsLite;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS
|
namespace DCFApixels.DragonECS
|
||||||
{
|
{
|
||||||
//Edge world
|
//Arc
|
||||||
//Relation entity
|
//Arc world
|
||||||
//Relation component
|
//Rel entity
|
||||||
|
//Component
|
||||||
public class EcsArc
|
public class EcsArc
|
||||||
{
|
{
|
||||||
private readonly EcsWorld _startWorld;
|
private readonly EcsWorld _startWorld;
|
||||||
@ -19,15 +21,48 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
private readonly SparseArray64<int> _relationsMatrix = new SparseArray64<int>();
|
private readonly SparseArray64<int> _relationsMatrix = new SparseArray64<int>();
|
||||||
|
|
||||||
private EcsGroup _arcEntities;
|
private EcsJoin _joinEntities;
|
||||||
private ArcEntityInfo[] _arkEntityInfos; //N * (N - 1) / 2
|
private EcsGroup _relEntities;
|
||||||
|
private RelEntityInfo[] _relEntityInfos; //N * (N - 1) / 2
|
||||||
|
|
||||||
|
private bool _isLoop;
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
public EcsWorld StartWorld => _startWorld;
|
public EcsWorld StartWorld
|
||||||
public EcsWorld EndWorld => _endWorld;
|
{
|
||||||
public EcsArcWorld ArcWorld => _arcWorld;
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public EcsReadonlyGroup ArcEntities => _arcEntities.Readonly;
|
get { return _startWorld; }
|
||||||
public bool IsLoop => _startWorld == _endWorld;
|
}
|
||||||
|
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
|
#endregion
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
@ -37,44 +72,51 @@ namespace DCFApixels.DragonECS
|
|||||||
_endWorld = endWorld;
|
_endWorld = endWorld;
|
||||||
_arcWorld = arcWorld;
|
_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);
|
_arcWorldHandler = new ArcWorldHandler(this);
|
||||||
_endWorldHandler = new EndWorldHandler(this, _endWorld);
|
if (!_isLoop)
|
||||||
|
{
|
||||||
|
_endWorldHandler = new EndWorldHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
_startWorld.AddListener(worldEventListener: _startWorldHandler);
|
_relEntities = EcsGroup.New(_arcWorld);
|
||||||
_startWorld.AddListener(entityEventListener: _startWorldHandler);
|
_joinEntities = new EcsJoin(this);
|
||||||
_arcWorld.AddListener(worldEventListener: _arcWorldHandler);
|
|
||||||
_arcWorld.AddListener(entityEventListener: _arcWorldHandler);
|
|
||||||
_endWorld.AddListener(worldEventListener: _endWorldHandler);
|
|
||||||
_endWorld.AddListener(entityEventListener: _endWorldHandler);
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region New/Del
|
#region New/Del
|
||||||
public int New(int startEntityID, int endEntityID)
|
public int NewRelation(int startEntityID, int endEntityID)
|
||||||
{
|
{
|
||||||
if (Has(startEntityID, endEntityID))
|
if (Has(startEntityID, endEntityID))
|
||||||
|
{
|
||||||
throw new EcsRelationException();
|
throw new EcsRelationException();
|
||||||
|
}
|
||||||
|
|
||||||
int arcEntity = _arcWorld.NewEntity();
|
int relEntity = _arcWorld.NewEntity();
|
||||||
_relationsMatrix.Add(startEntityID, endEntityID, arcEntity);
|
_relationsMatrix.Add(startEntityID, endEntityID, relEntity);
|
||||||
|
|
||||||
_arkEntityInfos[arcEntity] = new ArcEntityInfo(startEntityID, endEntityID);
|
_relEntityInfos[relEntity] = new RelEntityInfo(startEntityID, endEntityID);
|
||||||
_arcEntities.Add(arcEntity);
|
_relEntities.Add(relEntity);
|
||||||
return arcEntity;
|
_joinEntities.Add(relEntity);
|
||||||
|
return relEntity;
|
||||||
}
|
}
|
||||||
public void Del(int startEntityID, int endEntityID)
|
public void DelRelation(int startEntityID, int endEntityID)
|
||||||
{
|
{
|
||||||
if (!_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int arcEntity))
|
if (!_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int relEntity))
|
||||||
|
{
|
||||||
throw new EcsRelationException();
|
throw new EcsRelationException();
|
||||||
|
}
|
||||||
|
_joinEntities.Del(relEntity);
|
||||||
|
|
||||||
_relationsMatrix.Remove(startEntityID, endEntityID);
|
_relationsMatrix.Remove(startEntityID, endEntityID);
|
||||||
_arcWorld.DelEntity(arcEntity);
|
_arcWorld.DelEntity(relEntity);
|
||||||
|
|
||||||
_arkEntityInfos[arcEntity] = ArcEntityInfo.Empty;
|
_relEntityInfos[relEntity] = RelEntityInfo.Empty;
|
||||||
_arcEntities.Remove(arcEntity);
|
_relEntities.Remove(relEntity);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -97,28 +139,28 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
#region ArcEntityInfo
|
#region ArcEntityInfo
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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 false;
|
||||||
return !_arkEntityInfos[arcEntityID].IsEmpty;
|
return !_relEntityInfos[arcEntityID].IsEmpty;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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();
|
throw new Exception();
|
||||||
return _arkEntityInfos[arcEntityID];
|
return _relEntityInfos[arcEntityID];
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int GetArcStart(int arcEntityID)
|
public int GetRelStart(int arcEntityID)
|
||||||
{
|
{
|
||||||
return GetArcInfo(arcEntityID).start;
|
return GetRelInfo(arcEntityID).start;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int GetArcEnd(int arcEntityID)
|
public int GetRelEnd(int arcEntityID)
|
||||||
{
|
{
|
||||||
return GetArcInfo(arcEntityID).end;
|
return GetRelInfo(arcEntityID).end;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -138,93 +180,84 @@ namespace DCFApixels.DragonECS
|
|||||||
private class ArcWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
|
private class ArcWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
|
||||||
{
|
{
|
||||||
private readonly EcsArc _arc;
|
private readonly EcsArc _arc;
|
||||||
|
|
||||||
public ArcWorldHandler(EcsArc arc)
|
public ArcWorldHandler(EcsArc arc)
|
||||||
{
|
{
|
||||||
_arc = arc;
|
_arc = arc;
|
||||||
|
EcsArcWorld arcWorld = arc.ArcWorld;
|
||||||
|
arcWorld.AddListener(worldEventListener: this);
|
||||||
|
arcWorld.AddListener(entityEventListener: this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Callbacks
|
#region Callbacks
|
||||||
public void OnDelEntity(int entityID)
|
public void OnDelEntity(int entityID) { }
|
||||||
{
|
public void OnNewEntity(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 OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
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);
|
_arc._arcWorld.ReleaseDelEntityBuffer(buffer.Length);
|
||||||
}
|
}
|
||||||
public void OnWorldDestroy()
|
public void OnWorldDestroy() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
public void OnWorldResize(int newSize)
|
public void OnWorldResize(int newSize)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _arc._arkEntityInfos, newSize);
|
Array.Resize(ref _arc._relEntityInfos, newSize);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
private class StartWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
|
private class StartWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
|
||||||
{
|
{
|
||||||
private readonly EcsArc _arc;
|
private readonly EcsArc _arc;
|
||||||
private readonly EcsWorld _start;
|
public StartWorldHandler(EcsArc arc)
|
||||||
|
|
||||||
public StartWorldHandler(EcsArc arc, EcsWorld world)
|
|
||||||
{
|
{
|
||||||
_arc = arc;
|
_arc = arc;
|
||||||
_start = world;
|
EcsWorld startWorld = arc.StartWorld;
|
||||||
|
startWorld.AddListener(worldEventListener: this);
|
||||||
|
startWorld.AddListener(entityEventListener: this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Callbacks
|
#region Callbacks
|
||||||
public void OnDelEntity(int entityID)
|
public void OnDelEntity(int startEntityID) { }
|
||||||
{
|
public void OnNewEntity(int startEntityID) { }
|
||||||
}
|
|
||||||
public void OnNewEntity(int entityID)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||||
{
|
{
|
||||||
|
foreach (var startEntityID in buffer)
|
||||||
|
{
|
||||||
|
_arc._joinEntities.DelStart(startEntityID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public void OnWorldDestroy()
|
public void OnWorldDestroy() { }
|
||||||
{
|
public void OnWorldResize(int newSize) { }
|
||||||
}
|
|
||||||
public void OnWorldResize(int newSize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
private class EndWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
|
private class EndWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener
|
||||||
{
|
{
|
||||||
private readonly EcsArc _arc;
|
private readonly EcsArc _arc;
|
||||||
private readonly EcsWorld _end;
|
public EndWorldHandler(EcsArc arc)
|
||||||
|
|
||||||
public EndWorldHandler(EcsArc arc, EcsWorld world)
|
|
||||||
{
|
{
|
||||||
_arc = arc;
|
_arc = arc;
|
||||||
_end = world;
|
EcsWorld endWorld = arc.EndWorld;
|
||||||
|
endWorld.AddListener(worldEventListener: this);
|
||||||
|
endWorld.AddListener(entityEventListener: this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Callbacks
|
#region Callbacks
|
||||||
public void OnDelEntity(int entityID)
|
public void OnDelEntity(int endEntityID) { }
|
||||||
{
|
public void OnNewEntity(int endEntityID) { }
|
||||||
}
|
|
||||||
public void OnNewEntity(int entityID)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||||
{
|
{
|
||||||
|
foreach (var endEntityID in buffer)
|
||||||
|
{
|
||||||
|
_arc._joinEntities.DelEnd(endEntityID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public void OnWorldDestroy()
|
public void OnWorldDestroy() { }
|
||||||
{
|
public void OnWorldResize(int newSize) { }
|
||||||
}
|
|
||||||
public void OnWorldResize(int newSize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,11 +4,12 @@ using System;
|
|||||||
|
|
||||||
namespace DCFApixels.DragonECS
|
namespace DCFApixels.DragonECS
|
||||||
{
|
{
|
||||||
public static class WorldGraph
|
public static class EcsWorldGraph
|
||||||
{
|
{
|
||||||
private static readonly SparseArray64<EcsArc> _matrix = new SparseArray64<EcsArc>(4);
|
private static readonly SparseArray64<EcsArc> _matrix = new SparseArray64<EcsArc>(4);
|
||||||
private static EcsArc[] _arcsMapping = new EcsArc[4];
|
private static EcsArc[] _arcsMapping = new EcsArc[4];
|
||||||
|
|
||||||
|
#region Register/Unregister
|
||||||
private static EcsArc Register(EcsWorld startWorld, EcsWorld endWorld, EcsArcWorld arcWorld)
|
private static EcsArc Register(EcsWorld startWorld, EcsWorld endWorld, EcsArcWorld arcWorld)
|
||||||
{
|
{
|
||||||
int startWorldID = startWorld.id;
|
int startWorldID = startWorld.id;
|
||||||
@ -39,7 +40,19 @@ namespace DCFApixels.DragonECS
|
|||||||
_arcsMapping[arc.ArcWorld.id] = null;
|
_arcsMapping[arc.ArcWorld.id] = null;
|
||||||
_matrix.Remove(startWorldID, endWorldID);
|
_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)
|
private static EcsArc Get(EcsWorld startWorld, EcsWorld otherWorld)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#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(EcsWorld startWorld, EcsWorld endWorld) => Has(startWorld.id, endWorld.id);
|
||||||
private static bool Has(int startWorldID, int endWorldID) => _matrix.Contains(startWorldID, endWorldID);
|
private static bool Has(int startWorldID, int endWorldID) => _matrix.Contains(startWorldID, endWorldID);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region Extension
|
#region Extension
|
||||||
public static bool IsRegistered(this EcsArcWorld self)
|
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 SetLoopArcAuto(this EcsWorld self) => SetArcAuto(self, self);
|
||||||
public static EcsArc SetArc(this EcsWorld start, EcsWorld end)
|
public static EcsArc SetArcAuto(this EcsWorld start, EcsWorld end)
|
||||||
{
|
{
|
||||||
if (start == null || end == null)
|
if (start == null || end == null)
|
||||||
{
|
{
|
@ -1,13 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS
|
namespace DCFApixels.DragonECS
|
||||||
{
|
{
|
||||||
public class BasketList
|
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||||
|
internal class BasketList
|
||||||
{
|
{
|
||||||
public const int RECYCLE = -1;
|
public const int RECYCLE = -1;
|
||||||
public const int HEAD = 0;
|
public const int HEAD = 0;
|
||||||
@ -16,10 +18,6 @@ namespace DCFApixels.DragonECS
|
|||||||
private Node[] _nodes;
|
private Node[] _nodes;
|
||||||
private int _recycledListLast = -1;
|
private int _recycledListLast = -1;
|
||||||
|
|
||||||
private int _basketsCount = 0;
|
|
||||||
private int[] _recycledBuskets = new int[64];
|
|
||||||
private int _recycledBusketsCount = 0;
|
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
public BasketList() : this(16) { }
|
public BasketList() : this(16) { }
|
||||||
public BasketList(int capacity)
|
public BasketList(int capacity)
|
||||||
@ -86,6 +84,11 @@ namespace DCFApixels.DragonECS
|
|||||||
LinkToRecycled(newSize - 1, 1);
|
LinkToRecycled(newSize - 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int GetBasketNodesCount(int basketIndex)
|
||||||
|
{
|
||||||
|
return _baskets[basketIndex].count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -108,7 +111,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool RemoveFromBasketAt(int basketIndex, int nodeIndex)
|
public void RemoveFromBasket(int basketIndex, int nodeIndex)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (nodeIndex <= 0)
|
if (nodeIndex <= 0)
|
||||||
@ -126,11 +129,11 @@ namespace DCFApixels.DragonECS
|
|||||||
{
|
{
|
||||||
basketInfo.nodeIndex = nextNode;
|
basketInfo.nodeIndex = nextNode;
|
||||||
}
|
}
|
||||||
return --basketInfo.count > 0;
|
basketInfo.count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private int InsertToBasket(int basketIndex, int value)
|
public int AddToBasket(int basketIndex, int value)
|
||||||
{
|
{
|
||||||
ref BasketInfo basketInfo = ref _baskets[basketIndex];
|
ref BasketInfo basketInfo = ref _baskets[basketIndex];
|
||||||
int newNodeIndex = TakeRecycledNode();
|
int newNodeIndex = TakeRecycledNode();
|
||||||
@ -273,6 +276,11 @@ namespace DCFApixels.DragonECS
|
|||||||
// return newBasketIndex;
|
// return newBasketIndex;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
public static void CreateCrossRef(int leftBasketIndex, int rightBasketIndex)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#region Node
|
#region Node
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
|
||||||
@ -306,86 +314,54 @@ namespace DCFApixels.DragonECS
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Basket
|
#region Basket
|
||||||
public Basket this[int basketIndex]
|
public BasketIterator this[int basketIndex]
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => GetBasket(basketIndex);
|
get => GetBasketIterator(basketIndex);
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Basket GetBasket(int basketIndex)
|
public BasketIterator GetBasketIterator(int basketIndex)
|
||||||
{
|
{
|
||||||
if (_baskets.Length <= basketIndex)
|
if (_baskets.Length <= basketIndex)
|
||||||
{
|
{
|
||||||
int newSize = GetHighBitNumber((uint)basketIndex) << 1;
|
int newSize = GetHighBitNumber((uint)basketIndex) << 1;
|
||||||
Array.Resize(ref _baskets, newSize);
|
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;
|
private readonly BasketList _basketList;
|
||||||
public readonly int _basketIndex;
|
private readonly int _basketIndex;
|
||||||
private int _count;
|
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
get { return _basketList._baskets[_basketIndex].count; }
|
||||||
get => _count;
|
|
||||||
}
|
|
||||||
public int BasketIndex
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => _basketIndex;
|
|
||||||
}
|
|
||||||
public int StartNodeIndex
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => _basketList._baskets[_basketIndex].nodeIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Basket(BasketList basketList, int basketIndex)
|
public BasketIterator(BasketList basketList, int basketIndex)
|
||||||
{
|
{
|
||||||
_basketList = basketList;
|
_basketList = basketList;
|
||||||
_basketIndex = basketIndex;
|
_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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||||
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<int>
|
public struct Enumerator : IEnumerator<int>
|
||||||
{
|
{
|
||||||
private readonly Node[] _nodes;
|
private readonly Node[] _nodes;
|
||||||
private int _nodeIndex;
|
private int _nodeIndex;
|
||||||
private int _nextNode;
|
private int _nextNodeIndex;
|
||||||
private int _count;
|
private int _count;
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Enumerator(Basket iterator)
|
public Enumerator(BasketIterator iterator)
|
||||||
{
|
{
|
||||||
ref BasketInfo basketInfo = ref iterator._basketList._baskets[iterator._basketIndex];
|
ref BasketInfo basketInfo = ref iterator._basketList._baskets[iterator._basketIndex];
|
||||||
_nodes = iterator._basketList._nodes;
|
_nodes = iterator._basketList._nodes;
|
||||||
_nodeIndex = -1;
|
_nodeIndex = -1;
|
||||||
_nextNode = basketInfo.nodeIndex;
|
_nextNodeIndex = basketInfo.nodeIndex;
|
||||||
_count = basketInfo.count;
|
_count = basketInfo.count;
|
||||||
}
|
}
|
||||||
public int Current
|
public int Current
|
||||||
@ -398,8 +374,8 @@ namespace DCFApixels.DragonECS
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
_nodeIndex = _nextNode;
|
_nodeIndex = _nextNodeIndex;
|
||||||
_nextNode = _nodes[_nextNode].next;
|
_nextNodeIndex = _nodes[_nextNodeIndex].next;
|
||||||
return _nodeIndex > 0 && _count-- > 0;
|
return _nodeIndex > 0 && _count-- > 0;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -409,5 +385,56 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 9d852affe1595eb4b9d8eb8dc6f28720
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@ -1,46 +1,47 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS
|
namespace DCFApixels.DragonECS
|
||||||
{
|
{
|
||||||
public readonly struct StartArcEnd
|
public readonly ref struct StartRelEnd
|
||||||
{
|
{
|
||||||
/// <summary>Start vertex entity ID.</summary>
|
/// <summary>Start vertex entity ID.</summary>
|
||||||
public readonly int start;
|
public readonly int start;
|
||||||
/// <summary>Arc entity ID.</summary>
|
/// <summary>Relation entity ID.</summary>
|
||||||
public readonly int arc;
|
public readonly int rel;
|
||||||
/// <summary>End vertex entity ID.</summary>
|
/// <summary>End vertex entity ID.</summary>
|
||||||
public readonly int end;
|
public readonly int end;
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public StartArcEnd(int start, int arc, int end)
|
public StartRelEnd(int start, int rel, int end)
|
||||||
{
|
{
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.arc = arc;
|
this.rel = rel;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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;
|
start = this.start;
|
||||||
arc = this.arc;
|
rel = this.rel;
|
||||||
end = this.end;
|
end = this.end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public readonly struct ArcEnd
|
public readonly ref struct RelEnd
|
||||||
{
|
{
|
||||||
/// <summary>Arc entity ID.</summary>
|
/// <summary>Relation entity ID.</summary>
|
||||||
public readonly int arc;
|
public readonly int rel;
|
||||||
/// <summary>End vertex entity ID.</summary>
|
/// <summary>End vertex entity ID.</summary>
|
||||||
public readonly int end;
|
public readonly int end;
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ArcEnd(int arc, int end)
|
public RelEnd(int rel, int end)
|
||||||
{
|
{
|
||||||
this.arc = arc;
|
this.rel = rel;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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;
|
end = this.end;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,9 +6,9 @@ namespace DCFApixels.DragonECS
|
|||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
|
||||||
[Serializable]
|
[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>
|
/// <summary>Start vertex entity ID.</summary>
|
||||||
public readonly int start;
|
public readonly int start;
|
||||||
@ -24,7 +24,7 @@ namespace DCFApixels.DragonECS
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal ArcEntityInfo(int startEntity, int endEntity)
|
internal RelEntityInfo(int startEntity, int endEntity)
|
||||||
{
|
{
|
||||||
start = startEntity;
|
start = startEntity;
|
||||||
end = endEntity;
|
end = endEntity;
|
||||||
@ -38,15 +38,15 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
#region operators
|
#region operators
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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)]
|
[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
|
#endregion
|
||||||
|
|
||||||
#region Other
|
#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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool Equals(ArcEntityInfo other) => this == other;
|
public bool Equals(RelEntityInfo other) => this == other;
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override int GetHashCode() => ~start ^ end;
|
public override int GetHashCode() => ~start ^ end;
|
||||||
public override string ToString() => $"arc({start} -> {end})";
|
public override string ToString() => $"arc({start} -> {end})";
|
@ -2,6 +2,7 @@
|
|||||||
//Benchmark result of indexer.get speed test with 300 elements:
|
//Benchmark result of indexer.get speed test with 300 elements:
|
||||||
//[Dictinary: 6.705us] [SparseArray64: 2.512us].
|
//[Dictinary: 6.705us] [SparseArray64: 2.512us].
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -225,6 +226,7 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
|
|
||||||
#region Utils
|
#region Utils
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||||
|
[DebuggerDisplay("next{next} hashKey{hashKey} value{value}")]
|
||||||
private struct Entry
|
private struct Entry
|
||||||
{
|
{
|
||||||
public int next; // Index of next entry, -1 if last
|
public int next; // Index of next entry, -1 if last
|
||||||
|
Loading…
Reference in New Issue
Block a user