-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTri.cs
227 lines (208 loc) · 12.9 KB
/
Tri.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
using System;
using System.Collections.Generic;
using System.Linq;
namespace RT.Coordinates
{
/// <summary>
/// <para>
/// Represents a tile in a 2D triangular grid.</para></summary>
/// <remarks>
/// Represents a triangular tile in a two-dimensional grid in which the tiles alternative between being up-pointing
/// and down-pointing triangles. Each tri is represented as a pair of coordinates (X, Y), where X counts the tris in a
/// row and Y identifies the row. The (0, 0) tri is an up-pointing one.</remarks>
/// <image type="raw">
/// <svg xmlns='http://www.w3.org/2000/svg' viewBox='0.25 0.39711432 7 7'><path d='M0 0L0.75
/// 1.29903810567666L-0.75 1.29903810567666M2.25 1.29903810567666L1.5 0L0.75 1.29903810567666zM3.75 1.29903810567666L3
/// 0L2.25 1.29903810567666zM5.25 1.29903810567666L4.5 0L3.75 1.29903810567666zM6.75 1.29903810567666L6 0L5.25
/// 1.29903810567666zM7.5 0L6.75 1.29903810567666L8.25 1.29903810567666M1.5 2.59807621135332L0.75 1.29903810567666L0
/// 2.59807621135332zM3 2.59807621135332L2.25 1.29903810567666L1.5 2.59807621135332zM4.5 2.59807621135332L3.75
/// 1.29903810567666L3 2.59807621135332zM6 2.59807621135332L5.25 1.29903810567666L4.5 2.59807621135332zM7.5
/// 2.59807621135332L6.75 1.29903810567666L6 2.59807621135332zM0 2.59807621135332L0.75 3.89711431702997L-0.75
/// 3.89711431702997M2.25 3.89711431702997L1.5 2.59807621135332L0.75 3.89711431702997zM3.75 3.89711431702997L3
/// 2.59807621135332L2.25 3.89711431702997zM5.25 3.89711431702997L4.5 2.59807621135332L3.75 3.89711431702997zM6.75
/// 3.89711431702997L6 2.59807621135332L5.25 3.89711431702997zM7.5 2.59807621135332L6.75 3.89711431702997L8.25
/// 3.89711431702997M1.5 5.19615242270663L0.75 3.89711431702997L0 5.19615242270663zM3 5.19615242270663L2.25
/// 3.89711431702997L1.5 5.19615242270663zM4.5 5.19615242270663L3.75 3.89711431702997L3 5.19615242270663zM6
/// 5.19615242270663L5.25 3.89711431702997L4.5 5.19615242270663zM7.5 5.19615242270663L6.75 3.89711431702997L6
/// 5.19615242270663zM0 5.19615242270663L0.75 6.49519052838329L-0.75 6.49519052838329M2.25 6.49519052838329L1.5
/// 5.19615242270663L0.75 6.49519052838329zM3.75 6.49519052838329L3 5.19615242270663L2.25 6.49519052838329zM5.25
/// 6.49519052838329L4.5 5.19615242270663L3.75 6.49519052838329zM6.75 6.49519052838329L6 5.19615242270663L5.25
/// 6.49519052838329zM7.5 5.19615242270663L6.75 6.49519052838329L8.25 6.49519052838329M7.5 7.79422863405995L6.75
/// 6.49519052838329L6 7.79422863405995L5.25 6.49519052838329L4.5 7.79422863405995L3.75 6.49519052838329L3
/// 7.79422863405995L2.25 6.49519052838329L1.5 7.79422863405995L0.75 6.49519052838329L0 7.79422863405995' fill='none'
/// stroke-width='.05' stroke='black' /></svg></image>
public struct Tri : IEquatable<Tri>, INeighbor<Tri>, INeighbor<object>, IHasSvgGeometry
{
/// <summary>
/// Constructor.</summary>
/// <param name="x">
/// Position of the tri within its row.</param>
/// <param name="y">
/// Row.</param>
public Tri(int x, int y)
{
X = x;
Y = y;
}
/// <summary>
/// Returns a set of tris that make up a large up-pointing triangle structure.</summary>
/// <param name="sideLength">
/// Number of tri cells per side.</param>
/// <param name="dx">
/// Offset to shift the x-coordinates by. Default is <c>0</c>.</param>
/// <param name="dy">
/// Offset to shift the y-coordinates by. Default is <c>0</c>.</param>
/// <remarks>
/// With <paramref name="dx"/> and <paramref name="dy"/> equal to <c>0</c>, the bottom-left tri has an
/// x-coordinate of <c>0</c> for odd values of <paramref name="sideLength"/> and <c>1</c> for even values, while
/// the top tri has a y-coordinate of <c>0</c>.</remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="sideLength"/> was zero or negative.</exception>
/// <exception cref="ArgumentException">
/// The values of <paramref name="dx"/> and <paramref name="dy"/> are not both even or both odd.</exception>
public static IEnumerable<Tri> LargeUpPointingTriangle(int sideLength, int dx = 0, int dy = 0)
{
if (sideLength <= 0)
throw new ArgumentOutOfRangeException(nameof(sideLength), $"‘{nameof(sideLength)}’ must be positive.");
if (((dx ^ dy) & 1) != 0)
throw new ArgumentException($"The values of ‘{nameof(dx)}’ and ‘{nameof(dy)}’ must be both even or both odd.", nameof(dy));
for (var row = 0; row < sideLength; row++)
for (var x = 0; x < 2 * row + 1; x++)
yield return new Tri((1 - sideLength % 2) + sideLength - row + x - 1 + dx, row + dy);
}
/// <summary>
/// Returns a set of tris that make up a large down-pointing triangle structure.</summary>
/// <param name="sideLength">
/// Number of tri cells per side.</param>
/// <param name="dx">
/// Offset to shift the x-coordinates by. Default is <c>0</c>.</param>
/// <param name="dy">
/// Offset to shift the y-coordinates by. Default is <c>0</c>.</param>
/// <remarks>
/// With <paramref name="dx"/> and <paramref name="dy"/> equal to <c>0</c>, the top-left tri has coordinates
/// <c>(1, 0)</c>.</remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="sideLength"/> was zero or negative.</exception>
/// <exception cref="ArgumentException">
/// The values of <paramref name="dx"/> and <paramref name="dy"/> are not both even or both odd.</exception>
public static IEnumerable<Tri> LargeDownPointingTriangle(int sideLength, int dx = 0, int dy = 0)
{
if (sideLength <= 0)
throw new ArgumentOutOfRangeException(nameof(sideLength), $"‘{nameof(sideLength)}’ must be positive.");
if (((dx ^ dy) & 1) != 0)
throw new ArgumentException($"The values of ‘{nameof(dx)}’ and ‘{nameof(dy)}’ must be both even or both odd.", nameof(dy));
for (var row = 0; row < sideLength; row++)
for (var x = 0; x < 2 * (sideLength - row) - 1; x++)
yield return new Tri(row + 1 + x + dx, row + dy);
}
/// <summary>
/// Returns a set of tris that make up a large hexagon structure.</summary>
/// <param name="sideLength">
/// Number of triangles along each side of the hexagon.</param>
/// <param name="dx">
/// Offset to shift the x-coordinates by. Default is <c>0</c>.</param>
/// <param name="dy">
/// Offset to shift the y-coordinates by. Default is <c>0</c>.</param>
/// <remarks>
/// With <paramref name="dx"/> and <paramref name="dy"/> equal to <c>0</c>, the tris at the leftmost corner have
/// an x-coordinate of <c>0</c> for odd values of <paramref name="sideLength"/> and <c>1</c> for even values,
/// while the top row of tris has y-coordinates of <c>0</c>.</remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="sideLength"/> was zero or negative.</exception>
/// <exception cref="ArgumentException">
/// The values of <paramref name="dx"/> and <paramref name="dy"/> are not both even or both odd.</exception>
public static IEnumerable<Tri> LargeHexagon(int sideLength, int dx = 0, int dy = 0)
{
if (sideLength <= 0)
throw new ArgumentOutOfRangeException(nameof(sideLength), $"‘{nameof(sideLength)}’ must be positive.");
if (((dx ^ dy) & 1) != 0)
throw new ArgumentException($"The values of ‘{nameof(dx)}’ and ‘{nameof(dy)}’ must be both even or both odd.", nameof(dy));
for (var row = 0; row < sideLength; row++)
for (var x = 0; x < 2 * sideLength + 1 + 2 * row; x++)
{
yield return new Tri(x - row + (sideLength & ~1) + dx, row + dy);
yield return new Tri(x - row + (sideLength & ~1) + dx, 2 * sideLength - 1 - row + dy);
}
}
/// <summary>Returns the position of the tri within its row.</summary>
public int X { get; private set; }
/// <summary>Returns the row containing this tri.</summary>
public int Y { get; private set; }
/// <summary>Determines whether this tri is up-pointing (<c>true</c>) or down-pointing (<c>false</c>).</summary>
public readonly bool IsUpPointing => ((X ^ Y) & 1) == 0;
/// <inheritdoc/>
public readonly bool Equals(Tri other) => other.X == X && other.Y == Y;
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Tri tri && Equals(tri);
/// <inheritdoc/>
public override readonly int GetHashCode() => X * 1073741827 + Y;
/// <inheritdoc/>
public readonly IEnumerable<Tri> Neighbors
{
get
{
yield return new Tri(X - 1, Y);
yield return new Tri(X + 1, Y);
yield return IsUpPointing ? new Tri(X, Y + 1) : new Tri(X, Y - 1);
}
}
readonly IEnumerable<object> INeighbor<object>.Neighbors => Neighbors.Cast<object>();
/// <inheritdoc/>
public readonly IEnumerable<Link<Coordinates.Vertex>> Edges => Vertices.MakeEdges();
/// <summary>
/// Returns the vertices along the perimeter of this <see cref="Tri"/>, going clockwise from the top (up-pointing)
/// or top-left (down-pointing).</summary>
public readonly Coordinates.Vertex[] Vertices => IsUpPointing
? new Coordinates.Vertex[] { new Vertex(this), new Vertex(new Tri(X + 1, Y + 1)), new Vertex(new Tri(X - 1, Y + 1)) }
: new Coordinates.Vertex[] { new Vertex(new Tri(X - 1, Y)), new Vertex(new Tri(X + 1, Y)), new Vertex(new Tri(X, Y + 1)) };
private const double cos60 = .5;
private const double sin60 = .86602540378443864676372317075294;
/// <inheritdoc/>
public readonly PointD Center => new PointD(X * (1.5d * cos60), (IsUpPointing ? Y + 2d / 3d : Y + 1d / 3d) * (1.5d * sin60));
/// <inheritdoc/>
public override readonly string ToString() => $"T({X},{Y})";
/// <summary>Describes a 2D grid of triangular cells.</summary>
public class Grid : Structure<Tri>
{
/// <summary>
/// Constructs a grid of the specified width and height in which the top-left triangle is an up-pointing one.</summary>
/// <param name="width">
/// Number of triangles per row.</param>
/// <param name="height">
/// Number of rows.</param>
public Grid(int width, int height) : this(Enumerable.Range(0, width * height).Select(i => new Tri(i % width, i / width)))
{
}
/// <summary>
/// See <see cref="Structure{TCell}.Structure(IEnumerable{TCell}, IEnumerable{Link{TCell}}, Func{TCell,
/// IEnumerable{TCell}})"/>.</summary>
public Grid(IEnumerable<Tri> cells, IEnumerable<Link<Tri>> links = null, Func<Tri, IEnumerable<Tri>> getNeighbors = null) : base(cells, links, getNeighbors)
{
}
}
/// <summary>Describes a vertex (gridline intersection) in a triangular grid (<see cref="Grid"/>).</summary>
public class Vertex : Coordinates.Vertex
{
/// <summary>
/// Constructs a <see cref="Vertex"/> representing the top vertex of an up-pointing triangle.</summary>
/// <param name="tri">
/// The triangle representing this vertex.</param>
public Vertex(Tri tri)
{
if (!tri.IsUpPointing)
throw new ArgumentException("The tri must be an up-pointing tri.", nameof(tri));
Tri = tri;
}
/// <summary>Returns the tri whose top vertex is this vertex.</summary>
public Tri Tri { get; private set; }
/// <inheritdoc/>
public override bool Equals(Coordinates.Vertex other) => other is Vertex tv && tv.Tri.Equals(Tri);
/// <inheritdoc/>
public override bool Equals(object obj) => obj is Vertex tv && tv.Tri.Equals(Tri);
/// <inheritdoc/>
public override int GetHashCode() => unchecked(Tri.GetHashCode() + 47);
/// <inheritdoc/>
public override PointD Point => new PointD(Tri.X * cos60 * 1.5, Tri.Y * sin60 * 1.5);
}
}
}