forked from laicasaane/unity-supplements
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGridRange.cs
385 lines (310 loc) · 16.1 KB
/
GridRange.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace System.Grid
{
[Serializable]
public readonly partial struct GridRange : IRange<GridIndex, GridRange.Enumerator>,
IEquatableReadOnlyStruct<GridRange>, ISerializable
{
public GridSize Size { get; }
/// <summary>
/// Should iterate only within the boundary of <see cref="Start"/> and <see cref="End"/>.
/// </summary>
public bool Clamped { get; }
public GridIndex Start { get; }
public GridIndex End { get; }
public bool IsFromEnd { get; }
/// <summary>
/// <para>Iterate by <see cref="GridDirection.Column"/> or <see cref="GridDirection.Row"/>.</para>
/// <para>If <see cref="Clamped"/> == false, the direction will always be <see cref="GridDirection.Column"/>.</para>
/// </summary>
public GridDirection Direction { get; }
public GridRange(in GridSize size, in GridIndex start, in GridIndex end, bool fromEnd = false, GridDirection direction = default)
{
this.Size = size;
this.Clamped = true;
this.Start = start;
this.End = end;
this.IsFromEnd = fromEnd;
this.Direction = direction;
}
/// <summary>
///
/// </summary>
/// <param name="size"></param>
/// <param name="clamped">Should iterate only within the boundary of <see cref="Start"/> and <see cref="End"/>.</param>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="fromEnd"></param>
/// <param name="direction">Iterate by <see cref="GridDirection.Column"/> or <see cref="GridDirection.Row"/>.</param>
public GridRange(in GridSize size, bool clamped, in GridIndex start, in GridIndex end, bool fromEnd = false, GridDirection direction = default)
{
this.Size = size;
this.Clamped = clamped;
this.Start = start;
this.End = end;
this.IsFromEnd = fromEnd;
this.Direction = direction;
}
private GridRange(SerializationInfo info, StreamingContext context)
{
this.Size = info.GetValueOrDefault<GridSize>(nameof(this.Size));
this.Clamped = info.GetBooleanOrDefault(nameof(this.Clamped));
this.Start = info.GetValueOrDefault<GridIndex>(nameof(this.Start));
this.End = info.GetValueOrDefault<GridIndex>(nameof(this.End));
this.IsFromEnd = info.GetBooleanOrDefault(nameof(this.IsFromEnd));
this.Direction = info.GetValueOrDefault<GridDirection>(nameof(this.Direction));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(nameof(this.Size), this.Size);
info.AddValue(nameof(this.Clamped), this.Clamped);
info.AddValue(nameof(this.Start), this.Start);
info.AddValue(nameof(this.End), this.End);
info.AddValue(nameof(this.IsFromEnd), this.IsFromEnd);
info.AddValue(nameof(this.Direction), this.Direction);
}
public void Deconstruct(out GridSize size, out bool clamped, out GridIndex start, out GridIndex end, out bool fromEnd, out GridDirection direction)
{
size = this.Size;
clamped = this.Clamped;
start = this.Start;
end = this.End;
fromEnd = this.IsFromEnd;
direction = this.Direction;
}
public GridRange With(in GridSize? Size = null, bool? Clamped = null, in GridIndex? Start = null,
in GridIndex? End = null, bool? IsFromEnd = null, GridDirection? Direction = null)
=> new GridRange(
Size ?? this.Size,
Clamped ?? this.Clamped,
Start ?? this.Start,
End ?? this.End,
IsFromEnd ?? this.IsFromEnd,
Direction ?? this.Direction
);
public GridRange ByRow()
=> new GridRange(this.Size, this.Clamped, this.Start, this.End, this.IsFromEnd, GridDirection.Row);
public GridRange ByColumn()
=> new GridRange(this.Size, this.Clamped, this.Start, this.End, this.IsFromEnd, GridDirection.Column);
public GridRange FromStart()
=> new GridRange(this.Size, this.Clamped, this.Start, this.End, false, this.Direction);
public GridRange FromEnd()
=> new GridRange(this.Size, this.Clamped, this.Start, this.End, true, this.Direction);
IRange<GridIndex> IRange<GridIndex>.FromStart()
=> FromStart();
IRange<GridIndex> IRange<GridIndex>.FromEnd()
=> FromEnd();
public GridRange Clamp()
=> new GridRange(this.Size, true, this.Start, this.End, this.IsFromEnd, this.Direction);
public GridRange Unclamp()
=> new GridRange(this.Size, false, this.Start, this.End, this.IsFromEnd, this.Direction);
public bool Contains(in GridIndex value)
{
if (this.Clamped)
{
var containsRow = this.Start.Row <= this.End.Row
? value.Row >= this.Start.Row && value.Row <= this.End.Row
: value.Row >= this.End.Row && value.Row <= this.Start.Row;
var containsCol = this.Start.Column <= this.End.Column
? value.Column >= this.Start.Column && value.Column <= this.End.Column
: value.Column >= this.End.Column && value.Column <= this.Start.Column;
return containsRow && containsCol;
}
var index = value.ToIndex1(this.Size);
var startIndex = value.ToIndex1(this.Size);
var endIndex = value.ToIndex1(this.Size);
return startIndex <= endIndex
? index >= startIndex && index <= endIndex
: index >= endIndex && index <= startIndex;
}
public int Count()
{
if (this.Clamped)
{
var row = Math.Max(this.End.Row - this.Start.Row + 1, 0);
var col = Math.Max(this.End.Column - this.Start.Column + 1, 0);
return row * col;
}
var startIndex = this.Start.ToIndex1(this.Size);
var endIndex = this.End.ToIndex1(this.Size);
return Math.Max(endIndex - startIndex + 1, 0);
}
public override bool Equals(object obj)
=> obj is GridRange other &&
this.Size == other.Size &&
this.Clamped == other.Clamped &&
this.Start == other.Start &&
this.End == other.End &&
this.IsFromEnd == other.IsFromEnd &&
this.Direction == other.Direction;
public bool Equals(in GridRange other)
=> this.Size == other.Size &&
this.Clamped == other.Clamped &&
this.Start == other.Start &&
this.End == other.End &&
this.IsFromEnd == other.IsFromEnd &&
this.Direction == other.Direction;
public bool Equals(GridRange other)
=> this.Size == other.Size &&
this.Clamped == other.Clamped &&
this.Start == other.Start &&
this.End == other.End &&
this.IsFromEnd == other.IsFromEnd &&
this.Direction == other.Direction;
public override int GetHashCode()
{
#if USE_SYSTEM_HASHCODE
return HashCode.Combine(this.Size, this.Clamped, this.Start, this.End, this.IsFromEnd, this.Direction);
#endif
#pragma warning disable CS0162 // Unreachable code detected
var hashCode = -535992267;
hashCode = hashCode * -1521134295 + this.Size.GetHashCode();
hashCode = hashCode * -1521134295 + this.Clamped.GetHashCode();
hashCode = hashCode * -1521134295 + this.Start.GetHashCode();
hashCode = hashCode * -1521134295 + this.End.GetHashCode();
hashCode = hashCode * -1521134295 + this.IsFromEnd.GetHashCode();
hashCode = hashCode * -1521134295 + this.Direction.GetHashCode();
return hashCode;
#pragma warning restore CS0162 // Unreachable code detected
}
public override string ToString()
=> $"{{ {nameof(this.Size)}={this.Size}, {nameof(this.Clamped)}={this.Clamped}, {nameof(this.Start)}={this.Start}, {nameof(this.End)}={this.End}, {nameof(this.IsFromEnd)}={this.IsFromEnd}, {nameof(this.Direction)}={this.Direction} }}";
public Enumerator GetEnumerator()
=> new Enumerator(this);
public Enumerator Range()
=> GetEnumerator();
IEnumerator<GridIndex> IRange<GridIndex>.Range()
=> GetEnumerator();
public GridRange Normalize()
=> Normal(this.Size, this.Clamped, this.Start, this.End, this.IsFromEnd, this.Direction);
public bool Intersects(in GridRange other)
{
var nThis = Normalize();
var nOther = other.Normalize();
if (nThis.Clamped && nOther.Clamped)
return (nOther.Start.Column <= nThis.End.Column && nOther.Start.Row <= nThis.End.Row &&
nOther.End.Column >= nThis.Start.Column && nOther.End.Row >= nThis.Start.Row);
if (nThis.Clamped)
return Intersects(nThis, nOther);
if (nOther.Clamped)
return Intersects(nOther, nThis);
if (nThis.Direction == nOther.Direction)
return Intersects(nThis, nOther);
if (nThis.Start.Row == nThis.End.Row ||
nThis.Start.Column == nThis.End.Column)
return Intersects(nThis, nOther);
if (nOther.Start.Row == nOther.End.Row ||
nOther.Start.Column == nOther.End.Column)
return Intersects(nOther, nThis);
return true;
}
private bool Intersects(in GridRange clamped, in GridRange unclamped)
{
switch (unclamped.Direction)
{
case GridDirection.Column:
{
if (unclamped.Start.Row > clamped.End.Row ||
unclamped.End.Row < clamped.Start.Row)
return false;
if (unclamped.Start.Row == clamped.End.Row)
return unclamped.Start.Column <= clamped.End.Column;
if (unclamped.End.Row == clamped.Start.Row)
return unclamped.End.Column >= clamped.Start.Column;
return true;
}
case GridDirection.Row:
{
if (unclamped.Start.Column > clamped.End.Column ||
unclamped.End.Column < clamped.Start.Column)
return false;
if (unclamped.Start.Column == clamped.End.Column)
return unclamped.Start.Row <= clamped.End.Row;
if (unclamped.End.Column == clamped.Start.Column)
return unclamped.End.Row >= clamped.Start.Row;
return true;
}
default:
return false;
}
}
/// <summary>
/// Create a normal range from (a, b) where <see cref="Start"/> is lesser than <see cref="End"/>.
/// </summary>
public static GridRange Normal(in GridSize size, bool clamped, in GridIndex a, in GridIndex b, bool fromEnd = false, GridDirection direction = default)
{
GridIndex start, end;
if (clamped)
{
var rowIncreasing = a.Row.CompareTo(b.Row) <= 0;
var colIncreasing = a.Column.CompareTo(b.Column) <= 0;
start = new GridIndex(
rowIncreasing ? a.Row : b.Row,
colIncreasing ? a.Column : b.Column
);
end = new GridIndex(
rowIncreasing ? b.Row : a.Row,
colIncreasing ? b.Column : a.Column
);
}
else
{
var a1 = a.ToIndex1(size);
var b1 = b.ToIndex1(size);
var increasing = a1 < b1;
start = increasing ? a : b;
end = increasing ? b : a;
}
return new GridRange(size, clamped, start, end, fromEnd, direction);
}
/// <summary>
/// Create a range from a size whose row and column are greater than 0
/// </summary>
/// <exception cref="InvalidOperationException">Row and column must be greater than 0</exception>
public static GridRange FromSize(in GridIndex value, bool fromEnd = false)
{
if (value.Row <= 0 || value.Column <= 0)
throw new InvalidOperationException("Row and column must be greater than 0");
return new GridRange(value, GridIndex.Zero, value - GridIndex.One, fromEnd);
}
public static GridRange FromStart(in GridSize size, in GridIndex start, in GridIndex end)
=> new GridRange(size, start, end, false);
public static GridRange FromEnd(in GridSize size, in GridIndex start, in GridIndex end)
=> new GridRange(size, start, end, true);
public static GridRange FromSize(in GridIndex value, bool clamped, bool fromEnd = false)
=> new GridRange(value, clamped, GridIndex.Zero, value - GridIndex.One, fromEnd);
public static GridRange FromStart(in GridSize size, bool clamped, in GridIndex start, in GridIndex end)
=> new GridRange(size, clamped, start, end, false);
public static GridRange FromEnd(in GridSize size, bool clamped, in GridIndex start, in GridIndex end)
=> new GridRange(size, clamped, start, end, true);
public static implicit operator GridRange(in (GridSize size, GridIndex start, GridIndex end) value)
=> new GridRange(value.size, value.start, value.end);
public static implicit operator GridRange(in (GridSize size, GridIndex start, GridIndex end, bool fromEnd) value)
=> new GridRange(value.size, value.start, value.end, value.fromEnd);
public static implicit operator GridRange(in (GridSize size, GridIndex start, GridIndex end, bool fromEnd, GridDirection direction) value)
=> new GridRange(value.size, value.start, value.end, value.fromEnd, value.direction);
public static implicit operator GridRange(in (GridSize size, bool clamped, GridIndex start, GridIndex end) value)
=> new GridRange(value.size, value.clamped, value.start, value.end);
public static implicit operator GridRange(in (GridSize size, bool clamped, GridIndex start, GridIndex end, bool fromEnd) value)
=> new GridRange(value.size, value.clamped, value.start, value.end, value.fromEnd);
public static implicit operator GridRange(in (GridSize size, bool clamped, GridIndex start, GridIndex end, bool fromEnd, GridDirection direction) value)
=> new GridRange(value.size, value.clamped, value.start, value.end, value.fromEnd, value.direction);
public static implicit operator GridIndexRange(in GridRange value)
=> new GridIndexRange(value.Start, value.End, value.IsFromEnd, value.Direction);
public static bool operator ==(in GridRange lhs, in GridRange rhs)
=> lhs.Size == rhs.Size &&
lhs.Clamped == rhs.Clamped &&
lhs.Start == rhs.Start &&
lhs.End == rhs.End &&
lhs.IsFromEnd == rhs.IsFromEnd &&
lhs.Direction == rhs.Direction;
public static bool operator !=(in GridRange lhs, in GridRange rhs)
=> lhs.Size != rhs.Size ||
lhs.Clamped != rhs.Clamped ||
lhs.Start != rhs.Start ||
lhs.End != rhs.End ||
lhs.IsFromEnd != rhs.IsFromEnd ||
lhs.Direction != rhs.Direction;
}
}