Refactor/genericise MutableIntRange, add immmutables, add more methods
call now and I'll throw in not one but two float equality extensions for free
This commit is contained in:
parent
9ef32cf8f5
commit
e7d0fb8299
|
@ -19,7 +19,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
get => _x;
|
||||
set
|
||||
{
|
||||
_x = _rangeX.Constrain(value);
|
||||
_x = value.ConstrainWithin(_rangeX);
|
||||
SetAnalog();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
get => _y;
|
||||
set
|
||||
{
|
||||
_y = _rangeY.Constrain(value);
|
||||
_y = value.ConstrainWithin(_rangeY);
|
||||
SetAnalog();
|
||||
}
|
||||
}
|
||||
|
@ -56,24 +56,22 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
public void SetRangeX(float[] range)
|
||||
{
|
||||
_actualRangeX.Min = (int) range[0];
|
||||
_actualRangeX.Max = (int) range[2];
|
||||
|
||||
_actualRangeX.Start = (int) range[0];
|
||||
_actualRangeX.EndInclusive = (int) range[2];
|
||||
Rerange();
|
||||
}
|
||||
|
||||
public void SetRangeY(float[] range)
|
||||
{
|
||||
_actualRangeY.Min = (int) range[0];
|
||||
_actualRangeY.Max = (int) range[2];
|
||||
|
||||
_actualRangeY.Start = (int) range[0];
|
||||
_actualRangeY.EndInclusive = (int) range[2];
|
||||
Rerange();
|
||||
}
|
||||
|
||||
private readonly MutableIntRange _rangeX = new MutableIntRange(-128, 127);
|
||||
private readonly MutableIntRange _rangeY = new MutableIntRange(-128, 127);
|
||||
private readonly MutableIntRange _actualRangeX = new MutableIntRange(-128, 127);
|
||||
private readonly MutableIntRange _actualRangeY = new MutableIntRange(-128, 127);
|
||||
private readonly MutableRange<int> _rangeX = new MutableRange<int>(-128, 127);
|
||||
private readonly MutableRange<int> _rangeY = new MutableRange<int>(-128, 127);
|
||||
private RangeStruct<int> _actualRangeX = new RangeStruct<int> { Start = -128, EndInclusive = 127 };
|
||||
private RangeStruct<int> _actualRangeY = new RangeStruct<int> { Start = -128, EndInclusive = 127 };
|
||||
|
||||
private bool _reverseX;
|
||||
private bool _reverseY;
|
||||
|
@ -83,15 +81,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
_reverseX = _userRangePercentageX < 0;
|
||||
_reverseY = _userRangePercentageY < 0;
|
||||
|
||||
var midX = (_actualRangeX.Min + _actualRangeX.Max) / 2.0;
|
||||
var halfRangeX = (_reverseX ? -1 : 1) * (_actualRangeX.Max - _actualRangeX.Min) * _userRangePercentageX / 200.0;
|
||||
_rangeX.Min = (int) (midX - halfRangeX);
|
||||
_rangeX.Max = (int) (midX + halfRangeX);
|
||||
var midX = (_actualRangeX.Start + _actualRangeX.EndInclusive) / 2.0;
|
||||
var halfRangeX = (_reverseX ? -1 : 1) * (_actualRangeX.EndInclusive - _actualRangeX.Start) * _userRangePercentageX / 200.0;
|
||||
_rangeX.Overwrite((int) (midX - halfRangeX), (int) (midX + halfRangeX));
|
||||
|
||||
var midY = (_actualRangeY.Min + _actualRangeY.Max) / 2.0;
|
||||
var halfRangeY = (_reverseY ? -1 : 1) * (_actualRangeY.Max - _actualRangeY.Min) * _userRangePercentageY / 200.0;
|
||||
_rangeY.Min = (int) (midY - halfRangeY);
|
||||
_rangeY.Max = (int) (midY + halfRangeY);
|
||||
var midY = (_actualRangeY.Start + _actualRangeY.EndInclusive) / 2.0;
|
||||
var halfRangeY = (_reverseY ? -1 : 1) * (_actualRangeY.EndInclusive - _actualRangeY.Start) * _userRangePercentageY / 200.0;
|
||||
_rangeY.Overwrite((int) (midY - halfRangeY), (int) (midY + halfRangeY));
|
||||
|
||||
// re-constrain after changing ranges
|
||||
X = X;
|
||||
|
@ -108,12 +104,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
/// <remarks>
|
||||
/// min + (max - i) == max - (i - min) == min + max - i
|
||||
/// </remarks>
|
||||
private int MaybeReversedInX(int i) => _reverseX ? _rangeX.Min + _rangeX.Max - i : i;
|
||||
private int MaybeReversedInX(int i) => _reverseX ? _rangeX.Start + _rangeX.EndInclusive - i : i;
|
||||
/// <inheritdoc cref="MaybeReversedInX"/>
|
||||
private int MaybeReversedInY(int i) => _reverseY ? _rangeY.Min + _rangeY.Max - i : i;
|
||||
private int MaybeReversedInY(int i) => _reverseY ? _rangeY.Start + _rangeY.EndInclusive - i : i;
|
||||
|
||||
private int PixelSizeX => (int)(_rangeX.GetCount() * ScaleX);
|
||||
private int PixelSizeY => (int)(_rangeY.GetCount() * ScaleY);
|
||||
private int PixelSizeX => (int)(_rangeX.Count() * ScaleX);
|
||||
private int PixelSizeY => (int)(_rangeY.Count() * ScaleY);
|
||||
private int PixelMinX => (Size.Width - PixelSizeX) / 2;
|
||||
private int PixelMinY => (Size.Height - PixelSizeY) / 2;
|
||||
private int PixelMidX => PixelMinX + PixelSizeX / 2;
|
||||
|
@ -122,16 +118,16 @@ namespace BizHawk.Client.EmuHawk
|
|||
private int PixelMaxY => PixelMinY + PixelSizeY - 1;
|
||||
|
||||
private int RealToGfxX(int val) =>
|
||||
PixelMinX + ((MaybeReversedInX(_rangeX.Constrain(val)) - _rangeX.Min) * ScaleX).RoundToInt();
|
||||
PixelMinX + ((MaybeReversedInX(val.ConstrainWithin(_rangeX)) - _rangeX.Start) * ScaleX).RoundToInt();
|
||||
|
||||
private int RealToGfxY(int val) =>
|
||||
PixelMinY + ((MaybeReversedInY(_rangeY.Constrain(val)) - _rangeY.Min) * ScaleY).RoundToInt();
|
||||
PixelMinY + ((MaybeReversedInY(val.ConstrainWithin(_rangeY)) - _rangeY.Start) * ScaleY).RoundToInt();
|
||||
|
||||
private int GfxToRealX(int val) =>
|
||||
MaybeReversedInX(_rangeX.Constrain(_rangeX.Min + ((val - PixelMinX) / ScaleX).RoundToInt()));
|
||||
MaybeReversedInX((_rangeX.Start + ((val - PixelMinX) / ScaleX).RoundToInt()).ConstrainWithin(_rangeX));
|
||||
|
||||
private int GfxToRealY(int val) =>
|
||||
MaybeReversedInY(_rangeY.Constrain(_rangeY.Min + ((val - PixelMinY) / ScaleY).RoundToInt()));
|
||||
MaybeReversedInY((_rangeY.Start + ((val - PixelMinY) / ScaleY).RoundToInt()).ConstrainWithin(_rangeY));
|
||||
|
||||
private readonly Pen _blackPen = new Pen(Brushes.Black);
|
||||
private readonly Pen _bluePen = new Pen(Brushes.Blue, 2);
|
||||
|
@ -200,7 +196,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
var pX = (int)_previous.GetFloat(XName);
|
||||
var pY = (int)_previous.GetFloat(YName);
|
||||
e.Graphics.DrawLine(_grayPen, PixelMidX, PixelMidY, RealToGfxX(pX), RealToGfxY(pY));
|
||||
e.Graphics.DrawImage(_grayDot, RealToGfxX(pX) - 3, RealToGfxY(_rangeY.Max) - RealToGfxY(pY) - 3);
|
||||
e.Graphics.DrawImage(_grayDot, RealToGfxX(pX) - 3, RealToGfxY(_rangeY.EndInclusive) - RealToGfxY(pY) - 3);
|
||||
}
|
||||
|
||||
// Line
|
||||
|
|
|
@ -84,10 +84,10 @@
|
|||
<Compile Include="IImportResolver.cs" />
|
||||
<Compile Include="IMonitor.cs" />
|
||||
<Compile Include="Log.cs" />
|
||||
<Compile Include="MutableIntRange.cs" />
|
||||
<Compile Include="OSTailoredCode.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="QuickCollections.cs" />
|
||||
<Compile Include="Ranges.cs" />
|
||||
<Compile Include="Serializer.cs" />
|
||||
<Compile Include="SettingsUtil.cs" />
|
||||
<Compile Include="SimpleTime.cs" />
|
||||
|
|
|
@ -122,5 +122,17 @@ namespace BizHawk.Common.NumberExtensions
|
|||
}
|
||||
|
||||
public static int RoundToInt(this float f) => (int) Math.Round(f);
|
||||
|
||||
/// <summary>2^-53</summary>
|
||||
private const double ExtremelySmallNumber = 1.1102230246251565E-16;
|
||||
|
||||
/// <inheritdoc cref="HawkFloatEquality(float,float,float)"/>
|
||||
public static bool HawkFloatEquality(this double d, double other, double ε = ExtremelySmallNumber) => Math.Abs(other - d) < ε;
|
||||
|
||||
/// <summary>2^-24</summary>
|
||||
private const float ReallySmallNumber = 5.96046448E-08f;
|
||||
|
||||
/// <remarks>don't use this in cores without picking a suitable ε</remarks>
|
||||
public static bool HawkFloatEquality(this float f, float other, float ε = ReallySmallNumber) => Math.Abs(other - f) < ε;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public class MutableIntRange
|
||||
{
|
||||
private int _min;
|
||||
private int _max;
|
||||
|
||||
/// <exception cref="ArgumentException">(from setter) <paramref name="value"/> > <see cref="Max"/></exception>
|
||||
public int Min
|
||||
{
|
||||
get => _min;
|
||||
set
|
||||
{
|
||||
if (_max < value) throw new ArgumentException();
|
||||
_min = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <exception cref="ArgumentException">(from setter) <paramref name="value"/> < <see cref="Min"/></exception>
|
||||
public int Max
|
||||
{
|
||||
get => _max;
|
||||
set
|
||||
{
|
||||
if (value < _min) throw new ArgumentException();
|
||||
_max = value;
|
||||
}
|
||||
}
|
||||
|
||||
public MutableIntRange(int min, int max)
|
||||
{
|
||||
_min = min;
|
||||
Max = max; // set property instead of field to validate and possibly throw an ArgumentException
|
||||
}
|
||||
|
||||
public int Constrain(int i) => i < _min ? _min : i > _max ? _max : i;
|
||||
|
||||
/// <returns>true if i is in the inclusive range <see cref="Min"/>..<see cref="Max"/>, false otherwise</returns>
|
||||
public bool Covers(int i) => _min <= i && i <= _max;
|
||||
|
||||
public uint GetCount() => (uint) ((long) _max - _min + 1);
|
||||
|
||||
/// <returns>true if i is in the exclusive range <see cref="Min"/>..<see cref="Max"/>, false otherwise</returns>
|
||||
/// <remarks>
|
||||
/// You probably want <see cref="Covers"/>
|
||||
/// </remarks>
|
||||
public bool StrictContains(int i) => _min < i && i < _max;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public struct RangeStruct<T> where T : unmanaged, IComparable<T>
|
||||
{
|
||||
public T Start;
|
||||
|
||||
public T EndInclusive;
|
||||
}
|
||||
|
||||
/// <summary>represents a closed, inclusive range of <typeparamref name="T"/> (<see cref="Start"/> ≤ <see cref="EndInclusive"/>)</summary>
|
||||
public interface Range<out T> where T : unmanaged, IComparable<T>
|
||||
{
|
||||
T Start { get; }
|
||||
|
||||
T EndInclusive { get; }
|
||||
}
|
||||
|
||||
public class RangeImpl<T> : Range<T> where T : unmanaged, IComparable<T>
|
||||
{
|
||||
protected RangeStruct<T> r;
|
||||
|
||||
/// <exception cref="ArgumentException"><paramref name="range"/>.<see cref="RangeStruct{T}.EndInclusive"/> < <paramref name="range"/>.<see cref="RangeStruct{T}.Start"/>, or <typeparamref name="T"/> is <see cref="float">float</see>/<see cref="double">double</see> and either bound is <c>NaN</c></exception>
|
||||
internal RangeImpl(RangeStruct<T> range)
|
||||
{
|
||||
r = ValidatedOrThrow(range);
|
||||
}
|
||||
|
||||
/// <exception cref="ArgumentException"><paramref name="endInclusive"/> < <paramref name="start"/>, or <typeparamref name="T"/> is <see langword="float"/>/<see langword="double"/> and either bound is <see cref="float.NaN"/></exception>
|
||||
public RangeImpl(T start, T endInclusive) : this(new RangeStruct<T> { Start = start, EndInclusive = endInclusive }) {}
|
||||
|
||||
public T Start => r.Start;
|
||||
|
||||
public T EndInclusive => r.EndInclusive;
|
||||
|
||||
internal RangeStruct<T> CopyStruct() => r;
|
||||
|
||||
protected static RangeStruct<T1> ValidatedOrThrow<T1>(RangeStruct<T1> range) where T1 : unmanaged, IComparable<T1>
|
||||
{
|
||||
if (range is RangeStruct<float> fr && (float.IsNaN(fr.Start) || float.IsNaN(fr.EndInclusive))
|
||||
|| range is RangeStruct<double> dr && (double.IsNaN(dr.Start) || double.IsNaN(dr.EndInclusive)))
|
||||
{
|
||||
throw new ArgumentException("range bound is NaN", nameof(range));
|
||||
}
|
||||
if (range.EndInclusive.CompareTo(range.Start) < 0) throw new ArgumentException("range end < start", nameof(range));
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>represents a closed, inclusive range of <typeparamref name="T"/> (<see cref="Start"/> ≤ <see cref="EndInclusive"/>) which can be grown or shrunk</summary>
|
||||
/// <remarks>inheriting <see cref="RangeImpl{T}"/> reduces code duplication in <see cref="RangeExtensions"/></remarks>
|
||||
public class MutableRange<T> : RangeImpl<T> where T : unmanaged, IComparable<T>
|
||||
{
|
||||
/// <inheritdoc cref="RangeImpl{T}(BizHawk.Common.RangeStruct{T})"/>
|
||||
internal MutableRange(RangeStruct<T> range) : base(range) {}
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public MutableRange(T start, T endInclusive) : base(new RangeStruct<T> { Start = start, EndInclusive = endInclusive }) {}
|
||||
|
||||
/// <exception cref="ArgumentException">(from setter) <paramref name="value"/> > <see cref="EndInclusive"/></exception>
|
||||
public new T Start
|
||||
{
|
||||
get => r.Start;
|
||||
set => r.Start = r.EndInclusive.CompareTo(value) < 0
|
||||
? throw new ArgumentException("attempted to set start > end", nameof(value))
|
||||
: value;
|
||||
}
|
||||
|
||||
/// <exception cref="ArgumentException">(from setter) <paramref name="value"/> < <see cref="Start"/></exception>
|
||||
public new T EndInclusive
|
||||
{
|
||||
get => r.EndInclusive;
|
||||
set => r.EndInclusive = value.CompareTo(r.Start) < 0
|
||||
? throw new ArgumentException("attempted to set end < start", nameof(value))
|
||||
: value;
|
||||
}
|
||||
|
||||
public void Overwrite(T start, T endInclusive) => r = ValidatedOrThrow(new RangeStruct<T> { Start = start, EndInclusive = endInclusive });
|
||||
}
|
||||
|
||||
/// <summary>contains most of the logic for ranges</summary>
|
||||
/// <remarks>non-generic overloads are used where the method requires an increment or decrement</remarks>
|
||||
public static class RangeExtensions
|
||||
{
|
||||
private const string EXCL_RANGE_ARITH_EXC_TEXT = "exclusive range end is min value of integral type";
|
||||
|
||||
/// <returns><paramref name="value"/> if it's contained in <paramref name="range"/>, or else whichever bound of <paramref name="range"/> is closest to <paramref name="value"/></returns>
|
||||
public static T ConstrainWithin<T>(this T value, Range<T> range) where T : unmanaged, IComparable<T> => value.CompareTo(range.Start) < 0
|
||||
? range.Start
|
||||
: range.EndInclusive.CompareTo(value) < 0
|
||||
? range.EndInclusive
|
||||
: value;
|
||||
|
||||
/// <returns>true iff <paramref name="value"/> is contained in <paramref name="range"/> (<paramref name="value"/> is considered to be in the range if it's exactly equal to either bound)</returns>
|
||||
/// <seealso cref="StrictlyInBounds"/>
|
||||
public static bool Contains<T>(this Range<T> range, T value) where T : unmanaged, IComparable<T> => !(value.CompareTo(range.Start) < 0 || range.EndInclusive.CompareTo(value) < 0);
|
||||
|
||||
public static byte Count(this Range<byte> range) => (byte) (range.EndInclusive - range.Start + (byte) 1U);
|
||||
|
||||
public static uint Count(this Range<int> range) => (uint) (1L + range.EndInclusive - range.Start);
|
||||
|
||||
public static ulong Count(this Range<long> range) => throw new NotImplementedException("TODO fancy math");
|
||||
|
||||
public static byte Count(this Range<sbyte> range) => (byte) (1 + range.EndInclusive - range.Start);
|
||||
|
||||
public static ushort Count(this Range<short> range) => (ushort) (1 + range.EndInclusive - range.Start);
|
||||
|
||||
public static uint Count(this Range<uint> range) => range.EndInclusive - range.Start + 1U;
|
||||
|
||||
public static ulong Count(this Range<ulong> range) => range.EndInclusive - range.Start + 1U;
|
||||
|
||||
public static ushort Count(this Range<ushort> range) => (ushort) (range.EndInclusive - range.Start + (ushort) 1U);
|
||||
|
||||
/// <remarks>TODO is this faster or slower than the <c>yield return</c> algorithm used in <see cref="Enumerate(Range{int})"/>?</remarks>
|
||||
public static IEnumerable<byte> Enumerate(this Range<byte> range) => Enumerable.Range(range.Start, range.Count()).Select(i => (byte) i);
|
||||
|
||||
/// <inheritdoc cref="Enumerate(Range{float},float)"/>
|
||||
public static IEnumerable<double> Enumerate(this Range<double> range, double step)
|
||||
{
|
||||
var d = range.Start;
|
||||
while (d < range.EndInclusive)
|
||||
{
|
||||
yield return d;
|
||||
d += step;
|
||||
}
|
||||
if (d.HawkFloatEquality(range.EndInclusive)) yield return d;
|
||||
}
|
||||
|
||||
/// <remarks>beware precision errors</remarks>
|
||||
public static IEnumerable<float> Enumerate(this Range<float> range, float step)
|
||||
{
|
||||
var f = range.Start;
|
||||
while (f < range.EndInclusive)
|
||||
{
|
||||
yield return f;
|
||||
f += step;
|
||||
}
|
||||
if (f.HawkFloatEquality(range.EndInclusive)) yield return f;
|
||||
}
|
||||
|
||||
public static IEnumerable<int> Enumerate(this Range<int> range)
|
||||
{
|
||||
var i = range.Start;
|
||||
while (i < range.EndInclusive) yield return i++;
|
||||
yield return i;
|
||||
}
|
||||
|
||||
public static IEnumerable<long> Enumerate(this Range<long> range)
|
||||
{
|
||||
var l = range.Start;
|
||||
while (l < range.EndInclusive) yield return l++;
|
||||
yield return l;
|
||||
}
|
||||
|
||||
public static IEnumerable<sbyte> Enumerate(this Range<sbyte> range) => Enumerable.Range(range.Start, range.Count()).Select(i => (sbyte) i);
|
||||
|
||||
public static IEnumerable<short> Enumerate(this Range<short> range) => Enumerable.Range(range.Start, range.Count()).Select(i => (short) i);
|
||||
|
||||
public static IEnumerable<uint> Enumerate(this Range<uint> range)
|
||||
{
|
||||
var i = range.Start;
|
||||
while (i < range.EndInclusive) yield return i++;
|
||||
yield return i;
|
||||
}
|
||||
|
||||
public static IEnumerable<ulong> Enumerate(this Range<ulong> range)
|
||||
{
|
||||
var l = range.Start;
|
||||
while (l < range.EndInclusive) yield return l++;
|
||||
yield return l;
|
||||
}
|
||||
|
||||
public static IEnumerable<ushort> Enumerate(this Range<ushort> range) => Enumerable.Range(range.Start, range.Count()).Select(i => (ushort) i);
|
||||
|
||||
public static Range<T> GetImmutableCopy<T>(this MutableRange<T> range) where T : unmanaged, IComparable<T> => new MutableRange<T>(range.CopyStruct());
|
||||
|
||||
public static MutableRange<T> GetMutableCopy<T>(this Range<T> range) where T : unmanaged, IComparable<T> => range is RangeImpl<T> impl
|
||||
? new MutableRange<T>(impl.CopyStruct()) // copied by value when using the implementations in this file
|
||||
: new MutableRange<T>(range.Start, range.EndInclusive);
|
||||
|
||||
/// <inheritdoc cref="MutableRange{T}(T,T)"/>
|
||||
public static MutableRange<T> MutableRangeTo<T>(this T start, T endInclusive) where T : unmanaged, IComparable<T> => new MutableRange<T>(start, endInclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<byte> MutableRangeToExcluding(this byte start, byte endExclusive) => endExclusive == byte.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<byte>(start, (byte) (endExclusive - 1U));
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<int> MutableRangeToExcluding(this int start, int endExclusive) => endExclusive == int.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<int>(start, endExclusive - 1);
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<long> MutableRangeToExcluding(this long start, long endExclusive) => endExclusive == long.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<long>(start, endExclusive - 1L);
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<sbyte> MutableRangeToExcluding(this sbyte start, sbyte endExclusive) => endExclusive == sbyte.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<sbyte>(start, (sbyte) (endExclusive - 1));
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<short> MutableRangeToExcluding(this short start, short endExclusive) => endExclusive == short.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<short>(start, (short) (endExclusive - 1));
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<uint> MutableRangeToExcluding(this uint start, uint endExclusive) => endExclusive == uint.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<uint>(start, endExclusive - 1U);
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<ulong> MutableRangeToExcluding(this ulong start, ulong endExclusive) => endExclusive == ulong.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<ulong>(start, endExclusive - 1UL);
|
||||
|
||||
/// <inheritdoc cref="RangeToExcluding(int,int)"/>
|
||||
public static MutableRange<ushort> MutableRangeToExcluding(this ushort start, ushort endExclusive) => endExclusive == ushort.MinValue
|
||||
? throw new ArgumentException(EXCL_RANGE_ARITH_EXC_TEXT, nameof(endExclusive))
|
||||
: new MutableRange<ushort>(start, (ushort) (endExclusive - 1U));
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<T> RangeTo<T>(this T start, T endInclusive) where T : unmanaged, IComparable<T> => new RangeImpl<T>(start, endInclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<byte> RangeToExcluding(this byte start, byte endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <exception cref="ArgumentException"><paramref name="endExclusive"/> ≤ <paramref name="start"/> (empty ranges where <paramref name="start"/> = <paramref name="endExclusive"/> are not permitted)</exception>
|
||||
/// <exception cref="ArithmeticException"><paramref name="endExclusive"/> is min value of integral type (therefore <paramref name="endExclusive"/> ≤ <paramref name="start"/>)</exception>
|
||||
public static Range<int> RangeToExcluding(this int start, int endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<long> RangeToExcluding(this long start, long endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<sbyte> RangeToExcluding(this sbyte start, sbyte endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<short> RangeToExcluding(this short start, short endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<uint> RangeToExcluding(this uint start, uint endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<ulong> RangeToExcluding(this ulong start, ulong endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <inheritdoc cref="RangeImpl{T}(T,T)"/>
|
||||
public static Range<ushort> RangeToExcluding(this ushort start, ushort endExclusive) => MutableRangeToExcluding(start, endExclusive);
|
||||
|
||||
/// <returns>true iff <paramref name="value"/> is strictly contained in <paramref name="range"/> (<paramref name="value"/> is considered to be OUTSIDE the range if it's exactly equal to either bound)</returns>
|
||||
/// <seealso cref="Contains"/>
|
||||
public static bool StrictlyBoundedBy<T>(this T value, Range<T> range) where T : unmanaged, IComparable<T> => range.Start.CompareTo(value) < 0 && value.CompareTo(range.EndInclusive) < 0;
|
||||
}
|
||||
}
|
|
@ -357,15 +357,15 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
// clear inter-sprite priority buffer
|
||||
Array.Clear(InterSpritePriorityBuffer, 0, FrameWidth);
|
||||
|
||||
var testRange = new MutableIntRange(0, vdc.ActiveLine + 1);
|
||||
var testRange = new MutableRange<int>(0, vdc.ActiveLine + 1);
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int y = (vdc.SpriteAttributeTable[(i * 4) + 0] & 1023) - 64;
|
||||
int x = (vdc.SpriteAttributeTable[(i * 4) + 1] & 1023) - 32;
|
||||
ushort flags = vdc.SpriteAttributeTable[(i * 4) + 3];
|
||||
byte height = heightTable[(flags >> 12) & 3];
|
||||
testRange.Min = vdc.ActiveLine - height;
|
||||
if (!testRange.StrictContains(y)) continue;
|
||||
testRange.Start = vdc.ActiveLine - height;
|
||||
if (!y.StrictlyBoundedBy(testRange)) continue;
|
||||
|
||||
int patternNo = (((vdc.SpriteAttributeTable[(i * 4) + 2]) >> 1) & 0x1FF);
|
||||
int paletteBase = 256 + ((flags & 15) * 16);
|
||||
|
|
Loading…
Reference in New Issue