Changed Namespaces, Move files

This commit is contained in:
2026-05-12 09:53:30 -04:00
parent 4db51be374
commit bf4d856608
33 changed files with 164 additions and 252 deletions

View File

@@ -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>

View 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);
}

View File

@@ -0,0 +1 @@
uid://bjuntmf2sjynp

View 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() };
}

View File

@@ -0,0 +1 @@
uid://cx35jfqkjnou8

View 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);

View File

@@ -0,0 +1 @@
uid://j24fuotdwwx4