Changed Namespaces, Move files
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
using System.Diagnostics;
|
||||
using ChickenGameTest;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using FoodFactory.Items;
|
||||
using FoodFactory.Math;
|
||||
using FoodFactory.Voxel;
|
||||
using Godot;
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
|
||||
|
||||
132
src/Conveyors/ConveyorItemRender.cs
Normal file
132
src/Conveyors/ConveyorItemRender.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using FoodFactory.Items;
|
||||
using Godot;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class ConveyorItemRender : Node
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
[Export] protected Path3D Path3D { get; set; } = default!;
|
||||
[Export] protected TestItemConveyor ItemConveyor { get; set; } = default!;
|
||||
[Chickensoft.AutoInject.Dependency] protected IItemRenderer Items => this.DependOn<IItemRenderer>();
|
||||
// private Chickensoft.Sync.Primitives.AutoList<ConveyorSlice>.Binding _binding = default!;
|
||||
public override async void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
if (!ItemConveyor.IsNodeReady())
|
||||
{
|
||||
await ToSignal(ItemConveyor, Node.SignalName.Ready);
|
||||
}
|
||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
|
||||
ItemConveyor.GetChildren().OfType<Timer>().First().Timeout += () => offset = 0;
|
||||
return;
|
||||
// _binding = ItemConveyor.Items.Items.Bind();
|
||||
// binding.OnRemove(callback =>
|
||||
// {
|
||||
// Items.Remove(callback.Item);
|
||||
// // GD.PrintS(callback.Item, callback.BeltT);
|
||||
// // if (itemsRenders.TryGetValue(callback.Item, out var node))
|
||||
// // {
|
||||
// // GD.PrintS(callback.Item, callback.BeltT,node);
|
||||
// // itemsRenders.Remove(callback.Item);
|
||||
// // node.QueueFree();
|
||||
// // }
|
||||
// });
|
||||
// _binding.OnAdd((i, v) => Items.UpdateTransform(i.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(i.BeltT)));
|
||||
// _binding.OnUpdate((a, b) => Items.UpdateTransform(a.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(b.BeltT).Translated(-Path3D.Curve.SampleBakedWithRotation(b.BeltT).Basis.X * b.LaneSpan.Start)));
|
||||
|
||||
}
|
||||
float offset = 0;
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
offset += (float)delta;
|
||||
base._Process(delta);
|
||||
var e = ItemConveyor.EnumerateTowardStart();
|
||||
while (e.MoveNext())
|
||||
{
|
||||
Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position + (ItemConveyor.SignedSpeed * offset)));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
// _binding.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IItemRenderer
|
||||
{
|
||||
// Node3D GetVisualNode(IBeltItem beltItem);
|
||||
void UpdateTransform(IBeltItem beltItem, Transform3D newTransform);
|
||||
void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time);
|
||||
void Remove(IBeltItem beltItem);
|
||||
}
|
||||
public partial class ItemRenderSimple : Node3D, IItemRenderer
|
||||
{
|
||||
private Dictionary<IBeltItem, Node3D> _items = [];
|
||||
public void Remove(IBeltItem beltItem)
|
||||
{
|
||||
if (_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
node.QueueFree();
|
||||
}
|
||||
_items.Remove(beltItem);
|
||||
}
|
||||
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
|
||||
{
|
||||
if (!_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
_items.Add(beltItem, node = beltItem.CreateItemVisual());
|
||||
AddChild(node);
|
||||
beltItem.Disposed += _ => { node.QueueFree(); _items.Remove(beltItem); };
|
||||
node.Transform = newTransform;
|
||||
return;
|
||||
}
|
||||
node.Transform = newTransform;
|
||||
}
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => UpdateTransform(beltItem, newTransform, .25f);
|
||||
}
|
||||
public partial class TestItemRendered : Node3D, IItemRenderer
|
||||
{
|
||||
public Dictionary<IBeltItem, Node3D> _items = [];
|
||||
private ConditionalWeakTable<IBeltItem, Tween> _tweens = [];
|
||||
public void Remove(IBeltItem beltItem)
|
||||
{
|
||||
if (_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
node.QueueFree();
|
||||
}
|
||||
_items.Remove(beltItem);
|
||||
}
|
||||
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
|
||||
{
|
||||
if (!_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
_items.Add(beltItem, node = beltItem.CreateItemVisual());
|
||||
AddChild(node);
|
||||
beltItem.Disposed += _ => { node.QueueFree(); _items.Remove(beltItem); };
|
||||
node.Transform = newTransform;
|
||||
return;
|
||||
}
|
||||
if (_tweens.TryGetValue(beltItem, out var tween))
|
||||
{
|
||||
tween.Kill();
|
||||
_tweens.Remove(beltItem);
|
||||
}
|
||||
// GD.Print(newTransform);
|
||||
tween = GetTree().CreateTween().BindNode(node);
|
||||
tween.TweenProperty(node, "transform", newTransform, time);
|
||||
_tweens.Add(beltItem, tween);
|
||||
}
|
||||
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => UpdateTransform(beltItem, newTransform, .25f);
|
||||
}
|
||||
1
src/Conveyors/ConveyorItemRender.cs.uid
Normal file
1
src/Conveyors/ConveyorItemRender.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bjuntmf2sjynp
|
||||
281
src/Conveyors/ItemConveyor.cs
Normal file
281
src/Conveyors/ItemConveyor.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using SJK.Functional;
|
||||
using SJK.Math;
|
||||
using FoodFactory.Items;
|
||||
using FoodFactory.Voxel;
|
||||
|
||||
public interface IMovementConveyor
|
||||
{
|
||||
IBeltPort StartPort { get; }
|
||||
// IBeltSlotProfile StartPort { get; }
|
||||
IBeltPort EndPort { get; }
|
||||
// IBeltSlotProfile EndPort { get; }
|
||||
// IOption<IBeltSlotProfile> InputPort { get; }
|
||||
// IOption<IBeltSlotProfile> OutputPort { get; }
|
||||
// IEnumerable<IBeltSlotProfile> GetPorts();
|
||||
IEnumerable<IBeltPort> GetPorts();
|
||||
IAutoValue<float> SpeedValue { get; }
|
||||
float SpeedMagnitude { get; set; }
|
||||
float SignedSpeed { get; set; }
|
||||
bool IsReversed { get; set; }
|
||||
float Length { get; set; }
|
||||
// float GetAvailableTravel(ItemConveyor.BeltDirection beltDirection, LaneSpanT itemSpan, float maxDistance);
|
||||
// IBeltSlotProfile GetPortFacingStart();
|
||||
// IBeltSlotProfile GetPortFacingEnd();
|
||||
BeltDirection GetBeltDirection();
|
||||
// Option<ConveyorSlice> GetItemTowardStart();
|
||||
// Option<ConveyorSlice> GetItemTowardEnd();
|
||||
// Option<ConveyorSlice> GetItemTowardInput();
|
||||
// Option<ConveyorSlice> GetItemTowardOutput();
|
||||
Ordered1DList<IBeltItem>.Enumerator EnumerateTowardEnd();
|
||||
Ordered1DList<IBeltItem>.Enumerator EnumerateTowardStart();
|
||||
IBeltMovement GetMovementPolicy();
|
||||
// IList<ConveyorSlice> Items { get; }
|
||||
IOption<IBeltPort> GetPortFacing(IBeltPort slot);
|
||||
BeltObstacle GetDistanceToNextItem(BeltDirection beltDirection, float itemBeltT, float maxDistToCheck, LaneSpan laneSpan, HashSet<IMovementConveyor>? visted = null);
|
||||
ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT, LaneSpan laneSpan);
|
||||
}
|
||||
public readonly struct ConveyorItemHandle
|
||||
{
|
||||
private readonly Action _remove;
|
||||
private readonly Action<ConveyorSlice> _replace;
|
||||
public int Index { get; }
|
||||
public ConveyorSlice Slice { get; }
|
||||
public IBeltItem Item => Slice.Item;
|
||||
public LaneSpan Span => Slice.LaneSpan;
|
||||
public float BeltT => Slice.BeltT;
|
||||
public ConveyorItemHandle(int index, ConveyorSlice slice, Action remove, Action<ConveyorSlice> replace)
|
||||
{
|
||||
Index = index;
|
||||
Slice = slice;
|
||||
_remove = remove;
|
||||
_replace = replace;
|
||||
}
|
||||
public void Remove() => _remove();
|
||||
public void Replace(ConveyorSlice slice) => _replace(slice);
|
||||
}
|
||||
public interface IBeltMovement
|
||||
{
|
||||
void AdvanceBelt(IMovementConveyor conveyor, float delta);
|
||||
|
||||
}
|
||||
public sealed class IndividualMovement : IBeltMovement
|
||||
{
|
||||
public void AdvanceBelt(IMovementConveyor conveyor, float delta)
|
||||
{
|
||||
// GD.Print(conveyor.Items);
|
||||
if (conveyor.GetBeltDirection() == BeltDirection.NotMoving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// GD.Print("hello");
|
||||
var towardStart = conveyor.GetBeltDirection() == BeltDirection.TowardStart;
|
||||
var enumerator = towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var itemRef = enumerator.Current;
|
||||
float maxMove = conveyor.SpeedMagnitude * delta;
|
||||
var space = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(), itemRef.Position, maxMove, LaneSpan.One);
|
||||
|
||||
|
||||
// space -= ITEMSIZE;//AcountForSPacing
|
||||
// if ()
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (space is ItemBeltObstacle itemOb && itemOb.Distance <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float amountToMove = Mathf.Min(space.Distance, maxMove);
|
||||
|
||||
enumerator.Set(itemRef.Position + amountToMove * Mathf.Sign(conveyor.SignedSpeed));
|
||||
// itemRef.Replace(itemRef.Value with { BeltT = itemRef.Value.BeltT + amountToMove * Mathf.Sign(conveyor.SignedSpeed) });
|
||||
// GD.PrintS(conveyor.Items.Count,space,amountToMove," "+ itemRef.BeltT,itemRef.Item,conveyor.GetBeltDirection(),towardStart);
|
||||
if (towardStart ? itemRef.Position <= 0 : itemRef.Position >= conveyor.Length)
|
||||
{
|
||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||
if (facing.HasValue(out var slot) && slot.TryInsert(itemRef.Value, LaneSpan.One, 0))
|
||||
{
|
||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||
// GD.Print(itemRef.Item);
|
||||
enumerator.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
enumerator.SortAll();
|
||||
}
|
||||
}
|
||||
public sealed class StrictMovement : IBeltMovement
|
||||
{
|
||||
// // public event IBeltMovement.ItemMoved? OnItemMoved;
|
||||
|
||||
// public void AdvanceBelt(ItemConveyor conveyor, float delta)
|
||||
// {
|
||||
// if (!conveyor.AnyItems())
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// var beltDirection = conveyor.GetBeltDirection();
|
||||
// if (beltDirection == BeltDirection.NotMoving)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// var towardStart = beltDirection == BeltDirection.TowardStart;
|
||||
// // GD.Print("Thing: " + Speed + " "+ conveyor.GetConveyorPortFacing(conveyor.GetOutputPort()));
|
||||
// // var moveItemsIntoNextBelt = false;
|
||||
// var next = conveyor.GetConveyorPortFacing(conveyor.GetOutputPort());
|
||||
// var possibleItemTravel = conveyor.GetAvailableTravelForFrontItem(next.HasValue);
|
||||
|
||||
// // return;
|
||||
// if (!possibleItemTravel.HasValue)
|
||||
// {
|
||||
// //There is no items, this should not be called
|
||||
// throw new NotSupportedException("This should not e possible if there is no items or the belt is not moving, it should have returend before");
|
||||
// }
|
||||
// // if (next.HasValue && next.Value.Port.BeltT is BeltTEnd beltTEnd && (next.Value.Port.Direction == PortAccess.In || next.Value.Port.Direction == PortAccess.BiDirectional))
|
||||
// // {
|
||||
// // possibleItemTravel += next.Value.Conveyor.DistanceFromEnd(beltTEnd.End);
|
||||
// // moveItemsIntoNextBelt = true;
|
||||
// // }
|
||||
|
||||
|
||||
// // var itemPair = conveyor.GetFirstItem();
|
||||
// // var distToEnd = conveyor.GetAvailableTravel(conveyor.ItemsCount - 1, beltDirection);//(Speed < 0 ? ConveyorEnd.Start : ConveyorEnd.End);
|
||||
// var maxSpeed = Mathf.Min(conveyor.SpeedMagnitude * delta, possibleItemTravel.Value) * Mathf.Sign(conveyor.SignedSpeed);//Or possibly (int)BeltDirection, givn that they stgore the sign as part of the enum
|
||||
// if (maxSpeed == 0)
|
||||
// {
|
||||
// // var fI = conveyor.GetAvailableTravelForFrontItem();
|
||||
// // if (fI.HasValue && fI.Value <= 0 && next.Value.Port.TryInsertItem(conveyor.GetFirstItem().Value.Item, LaneSpan.One))
|
||||
// // {
|
||||
// // GD.Print("Item Moved");
|
||||
// // conveyor._items.Remove(conveyor.GetFirstItem().Value);
|
||||
// // }
|
||||
|
||||
// return;
|
||||
// }
|
||||
// List<ConveyorSlice> toMove = [];
|
||||
// // Dictionary<IBeltItem, List<ItemMovementSegment>> moves = [];
|
||||
// for (int i = 0; i < conveyor.ItemsCount; i++)
|
||||
// {
|
||||
// // List<ItemMovementSegment> segments = [];
|
||||
// // var startT = conveyor._items[i].BeltT;
|
||||
// var endT = Mathf.Clamp(conveyor._items[i].BeltT + maxSpeed, 0, conveyor.Length);
|
||||
|
||||
// conveyor._items[i] = conveyor._items[i] with { BeltT = endT };
|
||||
// // segments.Add(new(conveyor, startT, towardStart? Mathf.Max(endT, 0) : Mathf.Min(endT, conveyor.Length),Mathf.Abs(maxSpeed)));
|
||||
// if (towardStart ? (endT <= 0) : endT >= conveyor.Length)
|
||||
// {
|
||||
// toMove.Add(conveyor._items[i]);
|
||||
// // segments.Add(new(next.Value.Conveyor, ));
|
||||
// }
|
||||
// // moves.Add(conveyor._items[i].Item, segments);
|
||||
|
||||
// GD.Print($"Item: {conveyor._items[i].Item}, Position:{endT}");
|
||||
// }
|
||||
// // conveyor._items.ForEach((item) => item = item = Mathf.Clamp(item.Position + maxSpeed, 0, Length));
|
||||
// // var list = _items.Where(item=> Speed>=0?item.BeltT >= Length:item.BeltT <= 0).ToList();
|
||||
// // GD.Print(list.Count);
|
||||
// if (next.HasValue)
|
||||
// {
|
||||
// foreach (var item in toMove)
|
||||
// {
|
||||
// GD.Print($"Item: {item.Item}, Position:{item.BeltT} -----------------");
|
||||
// //TEST
|
||||
// // var v = GetConveyorPortFacing(GetOutputPort());
|
||||
// // if (v.HasValue)//Should be true if items are > Length
|
||||
// // {
|
||||
// // GD.PrintS(v.Value.Port.LocalOffset,v.Value.Port.BeltT,v.Value.Port.Direction,v.Value.Port.PullPush,v.Value.Port.Face);
|
||||
// // var end = (next.Value.Port.BeltT as BeltTEnd).End;
|
||||
// // next.Value.Conveyor.InsertAtEndWithoutCheck(end, item.Item);
|
||||
// if (next.Value.Port.TryInsertItem(item.Item, item.LaneSpan))
|
||||
// {
|
||||
// GD.Print("Item Moved");
|
||||
// conveyor._items.Remove(item);
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public void AdvanceBelt(IMovementConveyor conveyor, float delta) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public struct ConveyorSlice(IBeltItem item, float beltT = 0)
|
||||
{
|
||||
public IBeltItem Item = item;
|
||||
public float BeltT = beltT;
|
||||
public LaneSpan LaneSpan = LaneSpan.One;
|
||||
public override string ToString() => $"{Item}, {BeltT}, {LaneSpan}";
|
||||
|
||||
}
|
||||
public readonly struct LaneSpan : IEquatable<LaneSpan>
|
||||
{
|
||||
public readonly ushort Start;
|
||||
public readonly ushort End;
|
||||
public readonly int Width => End - Start;
|
||||
public static readonly LaneSpan One = new(0, 1);
|
||||
public static readonly LaneSpan Zero = new(0, 0);
|
||||
public LaneSpan(ushort start, ushort end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
// [MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public static bool OverLaps(LaneSpan a, LaneSpan b) => a.Start < b.End && b.Start < a.End;
|
||||
// [MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public static LaneSpan Shifted(LaneSpan a, int amount) => checked(new LaneSpan((ushort)(a.Start + amount), (ushort)(a.End + amount)));
|
||||
public static bool Encapsulates(LaneSpan a, LaneSpan b) => b.Start >= a.Start && b.End <= b.End;
|
||||
public override string ToString() => $"(Start:{Start}, End:{End})";
|
||||
public bool Equals(LaneSpan other) => Start == other.Start && End == other.End;
|
||||
public static bool operator ==(LaneSpan a, LaneSpan b) => a.Equals(b);
|
||||
public static bool operator !=(LaneSpan a, LaneSpan b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
public enum BeltDirection : sbyte
|
||||
{
|
||||
TowardStart = -1,
|
||||
NotMoving = 0,
|
||||
TowardEnd = 1
|
||||
}
|
||||
public enum ConveyorEnd { Start, End }
|
||||
|
||||
public abstract record BeltT();
|
||||
public record BeltTEnd(ConveyorEnd End) : BeltT();
|
||||
public record BeltTOffset(float T) : BeltT();
|
||||
|
||||
public static class ConveyorExtensions
|
||||
{
|
||||
|
||||
public static IOption<IBeltPort> GetPortFacing(this IBeltPort slot, IVoxelGridRegistry gridRegistry)
|
||||
{
|
||||
var toCheck = new List<Vector3I>();
|
||||
for (int i = 0; i < slot.Profile.Width; i++)
|
||||
{
|
||||
toCheck.Add(slot.Profile.LocalOffset.LocalToWorld(Vector3I.Forward));
|
||||
}
|
||||
return gridRegistry
|
||||
.Get<IBeltPort>([.. toCheck])
|
||||
.Where(port => port.Profile.LocalOffset.TransformDirection(Vector3I.Forward) == slot.Profile.LocalOffset.TransformDirection(Vector3I.Back))
|
||||
.FirstOrNone();
|
||||
}
|
||||
public static BeltDirection DirectionTo(this ConveyorEnd end) => end switch
|
||||
{
|
||||
ConveyorEnd.Start => BeltDirection.TowardStart,
|
||||
ConveyorEnd.End => BeltDirection.TowardEnd,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
public static ConveyorEnd Swap(this ConveyorEnd end) => end switch { ConveyorEnd.Start => ConveyorEnd.End, ConveyorEnd.End => ConveyorEnd.Start, _ => throw new NotSupportedException() };
|
||||
}
|
||||
1
src/Conveyors/ItemConveyor.cs.uid
Normal file
1
src/Conveyors/ItemConveyor.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cx35jfqkjnou8
|
||||
421
src/Conveyors/TestItemConveyor.cs
Normal file
421
src/Conveyors/TestItemConveyor.cs
Normal file
@@ -0,0 +1,421 @@
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.AutoInject;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using SJK.Functional;
|
||||
using SJK.Math;
|
||||
using FoodFactory.Items;
|
||||
using FoodFactory.Voxel;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
[Tool]
|
||||
public partial class TestItemConveyor : Node, IMovementConveyor
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
[Dependency] public IBeltMovement MovementSystem => this.DependOn<IBeltMovement>(() => new IndividualMovement());
|
||||
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
|
||||
// private readonly AutoList<ConveyorSlice> _items = [];
|
||||
// public IAutoList<ConveyorSlice> Items => _items;
|
||||
// public readonly Sorted1DList<ConveyorSlice> Items = new(pos => pos.BeltT);
|
||||
private readonly Ordered1DList<IBeltItem> _items = new();
|
||||
// private AutoList<ConveyorSlice>.Binding _itemsBinding;
|
||||
public IBeltPort StartPort { get; set; }
|
||||
// public IBeltSlotProfile StartPort => new ItemConveyor.ConveyorPort() {
|
||||
// Face = Direction.Back,
|
||||
// MovementConveyor = this,
|
||||
// LocalOffset = Position,
|
||||
// BeltT = new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.Start),
|
||||
// PullPush = TransferMode.PushPull ,
|
||||
// Direction = PortAccess.BiDirectional,
|
||||
// AcceptItemFunc = (item,offset)=>{ _items.Insert(0,new ConveyorSlice(item,offset)); return true;},
|
||||
// CanAcceptItemFunc = (item, offset) =>
|
||||
// {
|
||||
// return .25f <= GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardEnd, 0, 5f, LaneSpan.One);
|
||||
// }
|
||||
// };
|
||||
public IBeltPort EndPort { get; set; }
|
||||
// public IBeltSlotProfile EndPort => new ItemConveyor.ConveyorPort() {
|
||||
// Face = Direction.Front,
|
||||
// MovementConveyor = this,
|
||||
// Direction = PortAccess.BiDirectional,
|
||||
// LocalOffset = Position,
|
||||
// BeltT = new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.End),
|
||||
// PullPush = TransferMode.PushPull,
|
||||
// AcceptItemFunc = (item,offset)=>{ _items.Add(new ConveyorSlice(item,Length-offset)); return true;},
|
||||
// CanAcceptItemFunc = (item,offset)=> .25f >= GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardStart,Length,.5f,LaneSpan.One)
|
||||
// };
|
||||
public IList<IBeltPort> OtherPorts = [];
|
||||
// [Export] public Vector3I Position { get; set; } = default!;
|
||||
//Speed per unit time
|
||||
private readonly AutoValue<float> _speed = new(1);
|
||||
public IAutoValue<float> SpeedValue => _speed;
|
||||
|
||||
public float SignedSpeed
|
||||
{
|
||||
get => _speed.Value;
|
||||
set => _speed.Value = value;
|
||||
}
|
||||
|
||||
public float SpeedMagnitude
|
||||
{
|
||||
get => Mathf.Abs(_speed.Value);
|
||||
set => _speed.Value = Mathf.Abs(value) * Mathf.Sign(_speed.Value);
|
||||
}
|
||||
public bool IsReversed { get => _speed.Value < 0; set => _speed.Value = SpeedMagnitude * (value ? -1 : 1); }
|
||||
// public IList<ConveyorSlice> Items => _items;
|
||||
public float Length { get; set; } = 1;
|
||||
|
||||
// public IEnumerable<ConveyorSlice> EnumerateTowardEnd() => _items;
|
||||
|
||||
// public IEnumerable<ConveyorSlice> EnumerateTowardStart() => _items.Reverse();
|
||||
public BeltDirection GetBeltDirection() => IsReversed ? BeltDirection.TowardStart : BeltDirection.TowardEnd;
|
||||
public IBeltMovement GetMovementPolicy() => MovementSystem;
|
||||
public IEnumerable<IBeltPort> GetPorts() => [StartPort, EndPort, .. OtherPorts];
|
||||
public void OnResolved()
|
||||
{
|
||||
if (Engine.IsEditorHint())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var timer = new Timer() { WaitTime = .05f, Autostart = true };
|
||||
AddChild(timer);
|
||||
timer.Timeout += () => MovementSystem.AdvanceBelt(this, (float)timer.WaitTime);
|
||||
if (StartPort is null || EndPort is null)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
foreach (var item in GetPorts())
|
||||
{
|
||||
var points = item.Points();
|
||||
var guid = GridRegistry.Register(item, [.. points]);
|
||||
TreeExiting += () => GridRegistry.UnRegister(guid);
|
||||
}
|
||||
}
|
||||
//ChatGPT Assisted
|
||||
public BeltObstacle GetDistanceToNextItem(
|
||||
BeltDirection beltDirection,
|
||||
float itemBeltT,
|
||||
float maxDistToCheck,
|
||||
LaneSpan laneSpan,
|
||||
HashSet<IMovementConveyor>? visited = null)
|
||||
{
|
||||
if (beltDirection == BeltDirection.NotMoving)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
visited ??= [];
|
||||
if (!visited.Add(this))
|
||||
{
|
||||
return new BeltObstacle(0);
|
||||
}
|
||||
|
||||
bool towardStart = beltDirection == BeltDirection.TowardStart;
|
||||
|
||||
// 1️⃣ Try to find next item on this conveyor
|
||||
var nextItem = FindNextLocalItem(towardStart, itemBeltT, laneSpan);
|
||||
// GD.Print(nextItem);
|
||||
if (nextItem.HasValue(out var item))
|
||||
{
|
||||
return new ItemBeltObstacle(MathF.Abs(itemBeltT - item.BeltT) - ITEMSIZE, item, laneSpan);
|
||||
// return MathF.Abs(itemBeltT - item.BeltT);//TODO UseLaneSpan inadditon
|
||||
}
|
||||
|
||||
// 2️⃣ No local item → try crossing into adjacent conveyor
|
||||
return GetDistanceAcrossPort(
|
||||
towardStart,
|
||||
itemBeltT,
|
||||
maxDistToCheck,
|
||||
laneSpan,
|
||||
visited
|
||||
);
|
||||
}
|
||||
//ChatGPT Assisted
|
||||
private IOption<ConveyorSlice> FindNextLocalItem(
|
||||
bool towardStart,
|
||||
float itemBeltT,
|
||||
LaneSpan laneSpan)
|
||||
{
|
||||
var sequence = towardStart
|
||||
? _items.EnumerateTowardStart(itemBeltT)
|
||||
: _items.EnumerateTowardEnd(itemBeltT);
|
||||
while (sequence.MoveNext())
|
||||
{
|
||||
var item = sequence.Current;
|
||||
if (towardStart ? item.Position < itemBeltT : item.Position > itemBeltT)
|
||||
{
|
||||
return Some<ConveyorSlice>.Of(new ConveyorSlice(item.Value, item.Position));
|
||||
}
|
||||
}
|
||||
return None<ConveyorSlice>.Of();
|
||||
// return sequence
|
||||
// .Where(i => towardStart ? i.Value.BeltT < itemBeltT : i.Value.BeltT > itemBeltT)
|
||||
// .Where(i => LaneSpan.OverLaps(i.Value.LaneSpan, laneSpan))
|
||||
// .Select(item => item.Value)
|
||||
// .FirstOrNone();
|
||||
}
|
||||
//ChatGPT Assisted
|
||||
private BeltObstacle GetDistanceAcrossPort(
|
||||
bool towardStart,
|
||||
float itemBeltT,
|
||||
float maxDistToCheck,
|
||||
LaneSpan laneSpan,
|
||||
HashSet<IMovementConveyor> visited)
|
||||
{
|
||||
var port = towardStart ? StartPort : EndPort;
|
||||
var boundaryT = towardStart ? 0f : Length;
|
||||
var distanceToBoundary = MathF.Abs(itemBeltT - boundaryT);
|
||||
|
||||
// 🚫 Boundary already exceeds budget
|
||||
// if (distanceToBoundary >= maxDistToCheck)
|
||||
// {
|
||||
// return maxDistToCheck;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
var neighborPort = FindFacingConveyorPort(port);
|
||||
if (!neighborPort.HasValue(out var conveyorPort))
|
||||
{
|
||||
var o = GetPortFacing(port);
|
||||
if (o.HasValue(out var otherport))
|
||||
{
|
||||
var mappedLane2 = MapLaneOrFail(port, otherport, laneSpan);
|
||||
if (mappedLane2.HasValue(out var lane2))
|
||||
{
|
||||
return new PortBeltObstacle(distanceToBoundary, lane2, otherport);
|
||||
// return new BeltObstacle(ObstacleKind.Boundary,distanceToBoundary+1,lane1,this,null, null);//TODO THIS SHOULD GET SPACE IN PORT SO ITEMS KNOW IF THEY CAN FIT
|
||||
|
||||
}
|
||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||
// return new BeltObstacle(ObstacleKind.Boundary,distanceToBoundary,laneSpan,this,null, null);//TODO THIS SHOULD GET SPACE IN PORT SO ITEMS KNOW IF THEY CAN FIT
|
||||
}
|
||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||
}
|
||||
|
||||
if (conveyorPort.BeltT is not BeltTEnd end)
|
||||
{
|
||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||
}
|
||||
|
||||
var mappedLane = MapLaneOrFail(port, conveyorPort, laneSpan);
|
||||
if (!mappedLane.HasValue(out var lane))
|
||||
{
|
||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||
}
|
||||
|
||||
var nextDirection = DirectionAwayFromEnd(end.End);
|
||||
var startT = end.End == ConveyorEnd.Start
|
||||
? 0f
|
||||
: conveyorPort.Conveyor.Length;
|
||||
|
||||
// 🔻 Remaining budget after reaching boundary
|
||||
// var remainingDist = maxDistToCheck - distanceToBoundary;
|
||||
// if (remainingDist <= 0f)
|
||||
// {
|
||||
// return maxDistToCheck;
|
||||
// }
|
||||
|
||||
var recursiveDistance =
|
||||
conveyorPort.Conveyor.GetDistanceToNextItem(
|
||||
nextDirection,
|
||||
startT,
|
||||
maxDistToCheck,
|
||||
lane,
|
||||
visited
|
||||
);
|
||||
return recursiveDistance with { Distance = recursiveDistance.Distance + distanceToBoundary };
|
||||
}
|
||||
private IOption<ConveyorPort> FindFacingConveyorPort(
|
||||
IBeltPort slot)
|
||||
{
|
||||
var targetpos = slot.Profile.LocalOffset.TransformDirection(Vector3I.Forward);
|
||||
// var targetPos = port.Profile.Position + port.Profile.Face.ToVector();
|
||||
return GridRegistry
|
||||
.Get<IBeltPort>(slot.Profile.LocalOffset.LocalToWorld(Vector3I.Forward))
|
||||
|
||||
.Where(port => port is ConveyorPort)
|
||||
.Where(port => port.Profile.LocalOffset.TransformDirection(Vector3I.Forward) == slot.Profile.LocalOffset.TransformDirection(Vector3I.Back))
|
||||
.FirstOrNone()
|
||||
.Bind(p => p is ConveyorPort cp
|
||||
? cp.ToOption()
|
||||
: None<ConveyorPort>.Of());
|
||||
}
|
||||
public IOption<IBeltPort> GetPortFacing(IBeltPort slot) => slot.GetPortFacing(GridRegistry);
|
||||
private IOption<LaneSpan> MapLaneOrFail(
|
||||
IBeltPort from,
|
||||
IBeltPort to,
|
||||
LaneSpan incoming)
|
||||
{
|
||||
// return None<LaneSpan>.Of();
|
||||
bool mirror = false;
|
||||
// full mapping of the source port
|
||||
var mapped = from.MapLaneSpanToFacingPort(to);
|
||||
|
||||
if (mapped == LaneSpan.Zero)
|
||||
return LaneSpan.Zero.ToOption();
|
||||
|
||||
int sourceWidth = from.Profile.Width;
|
||||
int targetWidth = mapped.Width;
|
||||
|
||||
int startOffset = incoming.Start; // start relative to source port
|
||||
int spanLength = incoming.Width;
|
||||
|
||||
int newStart;
|
||||
if (mirror)
|
||||
{
|
||||
// mirrored: right side of incoming aligns with right side of mapped
|
||||
newStart = mapped.End - (startOffset + spanLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal: left side of incoming aligns with left side of mapped
|
||||
newStart = mapped.Start + startOffset;
|
||||
}
|
||||
|
||||
int newEnd = newStart + spanLength;
|
||||
|
||||
// clamp to mapped range
|
||||
newStart = Math.Max(mapped.Start, newStart);
|
||||
newEnd = Math.Min(mapped.End, newEnd);
|
||||
|
||||
// GD.Print(incoming, new LaneSpan((ushort)newStart, (ushort)newEnd));
|
||||
if (newStart >= newEnd)
|
||||
{
|
||||
// GD.Print("none");
|
||||
return None<LaneSpan>.Of();
|
||||
}
|
||||
|
||||
return new LaneSpan((ushort)newStart, (ushort)newEnd).ToOption();
|
||||
}
|
||||
|
||||
private BeltDirection DirectionAwayFromEnd(ConveyorEnd conveyorEnd) => conveyorEnd switch { ConveyorEnd.Start => BeltDirection.TowardEnd, ConveyorEnd.End => BeltDirection.TowardStart, _ => throw new NotSupportedException() };
|
||||
// public IEnumerable<Sorted1DList<ConveyorSlice>.ItemHandle> EnumerateTowardEnd() => Items.EnumerateTowardEnd();
|
||||
// public IEnumerable<Sorted1DList<ConveyorSlice>.ItemHandle> EnumerateTowardStart() => Items.EnumerateTowardStart();
|
||||
private static readonly Vector3[] _square = [new(-.5f, .5f, -.5f), new(.5f, .5f, -.5f), new(.5f, -.5f, -.5f), new(-.5f, -.5f, -.5f), new(-.5f, .5f, -.5f)];
|
||||
public const float ITEMSIZE = .2f;
|
||||
private const float ITEMHALFSIZE = ITEMSIZE / 2f;
|
||||
|
||||
// public override void _Process(double delta)
|
||||
// {
|
||||
// if (StartPort is null || EndPort is null)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// foreach (var item in GetPorts())
|
||||
// {
|
||||
// for (int ii = 0; ii < item.Profile.Width; ii++)
|
||||
// {
|
||||
// DebugDraw3D.DrawLinePath(_square.Select(i => item.Profile.LocalOffset.ToGodot().TranslatedLocal(i).TranslatedLocal(new Vector3(ii, 0, 0)).Origin).ToArray(), (!Engine.IsEditorHint()) && GetPortFacing(item).HasValue() ? Colors.Green : Colors.Red);
|
||||
|
||||
// }
|
||||
// // DebugDraw3D.DrawArrow(item.Profile.LocalOffset.Origin, item.Profile.LocalOffset.LocalToWorld(item.Profile.Face.ToVector()),(!Engine.IsEditorHint())&&GetPortFacing(item).HasValue()?Colors.Green:Colors.Red);
|
||||
// }
|
||||
// }
|
||||
|
||||
public ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT, LaneSpan laneSpan)
|
||||
{
|
||||
// var towardStartEnd = beltT is ItemConveyor.BeltTEnd end && end.End == ItemConveyor.ConveyorEnd.End;
|
||||
bool accept(IBeltItem item, float offset, LaneSpan lane)
|
||||
{
|
||||
if (beltT is BeltTEnd end)
|
||||
{
|
||||
var startBeltT = end.End == ConveyorEnd.Start ? 0 : Length;
|
||||
var obstacle = GetDistanceToNextItem(end.End.Swap().DirectionTo(), startBeltT, offset, lane);
|
||||
var distanceAllowed = obstacle.Distance;
|
||||
return ITEMSIZE < distanceAllowed;
|
||||
}
|
||||
else if (beltT is BeltTOffset endOffset)
|
||||
{
|
||||
return HasClearance(endOffset.T, lane);
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
bool tryInsert(IBeltItem item, float offset, LaneSpan laneSpan)
|
||||
{
|
||||
if (beltT is BeltTEnd end)
|
||||
{
|
||||
if (end.End == ConveyorEnd.End)
|
||||
{
|
||||
_items.Insert(item, Length - offset);
|
||||
// Items.Insert(new(item, Length - offset) { LaneSpan = laneSpan });
|
||||
// _items.Add(new(item, Length-offset));
|
||||
}
|
||||
else
|
||||
{
|
||||
_items.Insert(item, offset);
|
||||
// Items.Insert(new(item, offset) { LaneSpan = laneSpan });
|
||||
// _items.Insert(0, new(item, offset));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (beltT is BeltTOffset endOffset)
|
||||
{
|
||||
|
||||
if (!HasClearance(endOffset.T, LaneSpan.One))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_items.Insert(item, endOffset.T);
|
||||
// Items.Insert(new(item, endOffset.T) { LaneSpan = laneSpan });
|
||||
// int insertIndex = FindInsertIndex(endOffset.T);
|
||||
|
||||
// _items.Insert(insertIndex, new(item, endOffset.T));
|
||||
|
||||
return true;
|
||||
}
|
||||
// _items.Insert(0,new ConveyorSlice(item,0));
|
||||
// return true;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var port = new ConveyorPort(this,
|
||||
profile,
|
||||
beltT,
|
||||
accept,
|
||||
tryInsert);
|
||||
return port;
|
||||
}
|
||||
private bool HasClearance(float centerT, LaneSpan span)
|
||||
{
|
||||
var lower = GetDistanceToNextItem(
|
||||
BeltDirection.TowardStart,
|
||||
centerT,
|
||||
ITEMSIZE,
|
||||
span
|
||||
);
|
||||
|
||||
var upper = GetDistanceToNextItem(
|
||||
BeltDirection.TowardEnd,
|
||||
centerT,
|
||||
ITEMSIZE,
|
||||
span
|
||||
);
|
||||
|
||||
float lowerAllowed =
|
||||
lower.Distance;// -
|
||||
// (lower.IsItem ? ItemConveyor.ITEMSIZE : 0);
|
||||
|
||||
float upperAllowed =
|
||||
upper.Distance;// -
|
||||
// (upper.IsItem ? ItemConveyor.ITEMSIZE : 0);
|
||||
|
||||
return ITEMSIZE < lowerAllowed &&
|
||||
ITEMSIZE < upperAllowed;
|
||||
}
|
||||
|
||||
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardEnd() => _items.EnumerateTowardEnd(-1);
|
||||
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardStart() => _items.EnumerateTowardStart(_items.Count);
|
||||
}
|
||||
//TODO Should liklely account for max search distance where the conveyorm may be needed to know
|
||||
public record class BeltObstacle(float Distance);
|
||||
public record ItemBeltObstacle(float Distance, ConveyorSlice Item, LaneSpan LaneSpan) : BeltObstacle(Distance);
|
||||
public record BoundaryBeltObstacle(float DistanceToBoundary) : BeltObstacle(DistanceToBoundary);
|
||||
public record PortBeltObstacle(float DistanceToBoundary, LaneSpan LaneSpan, IBeltPort BeltPort) : BoundaryBeltObstacle(DistanceToBoundary);
|
||||
|
||||
1
src/Conveyors/TestItemConveyor.cs.uid
Normal file
1
src/Conveyors/TestItemConveyor.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://j24fuotdwwx4
|
||||
Reference in New Issue
Block a user