-
Notifications
You must be signed in to change notification settings - Fork 0
/
Layout.cs
136 lines (114 loc) · 4.64 KB
/
Layout.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
// Ishan Pranav's REBUS: Layout.cs
// Copyright (c) 2021-2022 Ishan Pranav. All rights reserved.
// Licensed under the MIT License.
using System;
using SkiaSharp;
namespace Rebus.Client
{
/// <summary>
/// Represents a grid of hexagons.
/// </summary>
/// <remarks>
/// The implementation of this class was inspired by and based on <see href="https://www.redblobgames.com/grids/hexagons/">this</see> article by <see href="http://www-cs-students.stanford.edu/~amitp/">Amit Patel</see>.
/// </remarks>
/// <seealso href="https://www.redblobgames.com/grids/hexagons/">Red Blob Games - Hexagonal Grids</seealso>
/// <seealso href="http://www-cs-students.stanford.edu/~amitp/">Amit Patel's Home Page</seealso>
public class Layout
{
private const int HexagonEdges = 6;
private const float Theta0 = MathF.PI / HexagonEdges;
private const float ThetaI = MathF.Tau / HexagonEdges;
private static readonly float s_sqrt3 = MathF.Sqrt(3);
/// <summary>
/// Gets the <em>x</em> coordinate of the origin.
/// </summary>
/// <value>The <em>x</em> coordinate of the origin.</value>
public float X { get; }
/// <summary>
/// Gets the <em>y</em> coordinate of the origin.
/// </summary>
/// <value>The <em>y</em> coordinate of the origin.</value>
public float Y { get; }
/// <summary>
/// Gets or sets the width factor of a hexagon.
/// </summary>
/// <value>The width factor of a hexagon.</value>
public float HexagonWidth { get; set; }
/// <summary>
/// Gets or sets the height factor of a hexagon.
/// </summary>
/// <value>The height factor of a hexagon.</value>
public float HexagonHeight { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Layout"/> class.
/// </summary>
/// <param name="x">The <em>x</em> coordinate of the origin.</param>
/// <param name="y">The <em>y</em> coordinate of the origin.</param>
public Layout(float x, float y)
{
X = x;
Y = y;
}
/// <summary>
/// Gets the point representing the center of the hexagon at the given coordinate.
/// </summary>
/// <param name="value">The coordinate.</param>
/// <returns>The center of the hexagon.</returns>
public SKPoint GetCenter(HexPoint value)
{
return new SKPoint(HexagonWidth * (s_sqrt3 * value.Q + s_sqrt3 * 0.5f * value.R) + X, HexagonHeight * (1.5f * value.R) + Y);
}
/// <summary>
/// Gets the path representing the hexagon at the given coordinate.
/// </summary>
/// <param name="value">The coordinate.</param>
/// <param name="scale">The scale factor of the hexagon.</param>
/// <returns>The hexagon.</returns>
public SKPath GetHexagon(HexPoint value, float scale)
{
SKPath result = new SKPath();
SKPoint center = GetCenter(value);
SKPoint first = getVertex(n: 0);
result.MoveTo(first);
for (int i = 1; i < HexagonEdges; i++)
{
result.LineTo(getVertex(i));
}
result.Close();
return result;
SKPoint getVertex(int n)
{
(float sin, float cos) = MathF.SinCos(n * ThetaI + Theta0);
return new SKPoint(center.X + HexagonWidth * scale * cos, center.Y + HexagonHeight * scale * sin);
}
}
/// <summary>
/// Gets the hexagonal coordinate corresponding to the Cartesian coordinate.
/// </summary>
/// <param name="x">The Cartesian <em>x</em> coordinate.</param>
/// <param name="y">The Cartesian <em>y</em> coordinate.</param>
/// <returns>The hexagonal coordinate.</returns>
public HexPoint GetHexPoint(float x, float y)
{
y = (y - Y) / HexagonHeight;
float qF = s_sqrt3 / 3 * ((x - X) / HexagonWidth) - y / 3;
float rF = 2 * y / 3;
float sF = -qF - rF;
int q = (int)MathF.Round(qF);
int r = (int)MathF.Round(rF);
int s = (int)MathF.Round(sF);
float qError = Math.Abs(q - qF);
float rError = Math.Abs(r - rF);
float sError = Math.Abs(s - sF);
if (qError > rError && qError > sError)
{
q = -r - s;
}
else if (rError > sError)
{
r = -q - s;
}
return new HexPoint(q, r);
}
}
}