canto/Assets/Scripts/System/Range.cs
2025-08-14 19:11:32 -04:00

155 lines
4.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using KitsuneCafe.Sys.Numerics;
namespace KitsuneCafe.Sys
{
public record Index(int Value, bool IsFromEnd = false)
{
public static Index Start = new(0);
public static Index End = new(0, true);
public static Index NegativeInfinity = new(int.MinValue);
public static Index Infinity = new(int.MaxValue);
public static Index FromStart(int value) => new(value);
public static Index FromEnd(int value) => new(value, true);
public int GetOffset(int length) => IsFromEnd ? length - Value : Value;
public static implicit operator int(Index value) => value.IsFromEnd ? -value.Value : value.Value;
public static implicit operator Index(int value) => new(value, value < 0);
public static implicit operator global::System.Index(Index index) => new(index.Value, index.IsFromEnd);
public static implicit operator Index(global::System.Index index) => new(index.Value, index.IsFromEnd);
}
public record Range(Index Start, Index End)
{
public static Range All = new(Index.Start, Index.End);
public static Range StartAt(Index start) => new(start, Index.End);
public static Range EndAt(Index end) => new(Index.Start, end);
public static Range OfLength(Index offset, int length) => new(offset, offset + length);
public ClampingRange Clamp() => new(Start, End);
public ShiftingRange Shift() => new(Start, End);
public WrappingRange Wrap() => new(Start, End);
public static implicit operator System.Range(Range range) => new(range.Start, range.End);
public static implicit operator Range(System.Range range) => new(range.Start, range.End);
public virtual (int Offset, int Length) GetOffsetAndLength(int length)
{
var start = Start.GetOffset(length);
var end = End.GetOffset(length);
return (start, end - start);
}
public virtual bool Contains(int length, Index index)
{
var start = Start.GetOffset(length);
var end = End.GetOffset(length);
return index >= start && index < end;
}
public virtual IEnumerable<int> Indices(int length)
{
var (start, len) = GetOffsetAndLength(length);
UnityEngine.Debug.Log($"making indices: start {start}, length {len}");
for (int i = start; i < start + len; i++)
{
yield return i;
}
}
public virtual int[] ToArray(int length)
{
UnityEngine.Debug.Log($"making array of indices of length {length}");
return Indices(length).ToArray();
}
public void Deconstruct(out Index start, out Index end)
{
start = Start;
end = End;
}
}
public record ClampingRange(Index Start, Index End) : Range(Start, End)
{
public override (int Offset, int Length) GetOffsetAndLength(int length)
{
var (start, len) = base.GetOffsetAndLength(length);
return (start, Math.Clamp(len, 0, length - 1));
}
}
public record ShiftingRange(Index Start, Index End) : Range(Start, End)
{
public override (int Offset, int Length) GetOffsetAndLength(int length)
{
var (start, len) = base.GetOffsetAndLength(length);
int newStart = start;
int newEnd = start + len;
if (newEnd > length)
{
int offset = newEnd - length;
newStart -= offset;
newEnd -= offset;
}
if (newStart < 0)
{
int shiftAmount = 0 - newStart;
newStart += shiftAmount;
newEnd += shiftAmount;
}
int newLength = Math.Min(newEnd - newStart, length);
int newOffset = newStart;
return (newOffset, newLength);
}
};
public record WrappingRange(Index Start, Index End) : Range(Start, End)
{
public static WrappingRange WrapFrom(Index start) => new(start, Index.Infinity);
public static WrappingRange WrapUntil(Index end) => new(Index.NegativeInfinity, end);
public override (int Offset, int Length) GetOffsetAndLength(int length)
{
var start = Start.GetOffset(length);
var end = End.GetOffset(length);
var len = start + Math.Abs(end - start);
return (start, len);
}
public override bool Contains(int length, Index index)
{
if (Start <= End)
{
return base.Contains(length, index);
}
else
{
return (Start <= index && index < length) || (0 <= index && index < End);
}
}
public override IEnumerable<int> Indices(int length)
{
var (start, len) = GetOffsetAndLength(length);
for (int i = start; i < len; i++)
{
yield return i % length;
}
}
}
}