Added HexGrid::GetCellsForLine().

WorldChunkRefactoring
Martin Felis 2023-08-13 21:18:46 +02:00
parent 4839bfcb00
commit cc54b66d55
6 changed files with 447 additions and 86 deletions

View File

@ -1,9 +1,7 @@
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using Godot; using Godot;
using Array = Godot.Collections.Array; using GodotComponentTest.utils;
public class HexCell : IEquatable<HexCell> public class HexCell : IEquatable<HexCell>
{ {
@ -20,16 +18,58 @@ public class HexCell : IEquatable<HexCell>
return _cubeCoords.GetHashCode(); return _cubeCoords.GetHashCode();
} }
public static readonly Vector2 size = new Vector2(1, Mathf.Sqrt(3) / 2); public static readonly Vector2 Size = new(1, Mathf.Sqrt(3) / 2);
public static readonly Vector3 DIR_N = new Vector3(0, 1, -1); private const float Width = 2;
public static readonly Vector3 DIR_NE = new Vector3(1, 0, -1); private static readonly float Height = Mathf.Sqrt(3);
public static readonly Vector3 DIR_SE = new Vector3(1, -1, 0);
public static readonly Vector3 DIR_S = new Vector3(0, -1, 1);
public static readonly Vector3 DIR_SW = new Vector3(-1, 0, 1);
public static readonly Vector3 DIR_NW = new Vector3(-1, 1, 0);
public static readonly Array DIR_ALL = new Array() public static readonly Vector3 DIR_N = new(0, 1, -1);
{ DIR_N, DIR_NE, DIR_SE, DIR_S, DIR_SW, DIR_NW }; public static readonly Vector3 DIR_NE = new(1, 0, -1);
public static readonly Vector3 DIR_SE = new(1, -1, 0);
public static readonly Vector3 DIR_S = new(0, -1, 1);
public static readonly Vector3 DIR_SW = new(-1, 0, 1);
public static readonly Vector3 DIR_NW = new(-1, 1, 0);
public static readonly Vector3[] NeighborDirections =
{
DIR_N,
DIR_NW,
DIR_SW,
DIR_S,
DIR_SE,
DIR_NE
};
private static readonly Vector2 CornerNW = new Vector2(-Width / 4, -Height / 2);
private static readonly Vector2 CornerNE = new Vector2(Width / 4, -Height / 2);
private static readonly Vector2 CornerE = new Vector2(Width / 2, 0);
private static readonly Vector2 CornerSE = new Vector2(Width / 4, Height / 2);
private static readonly Vector2 CornerSW = new Vector2(-Width / 4, Height / 2);
private static readonly Vector2 CornerW = new Vector2(-Width / 2, 0);
private static readonly Vector2 PlaneNormalN = new Vector2(0, 1);
private static readonly Vector2 PlaneNormalNE =
-new Vector2(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30)));
private static readonly Vector2 PlaneNormalSE =
-new Vector2(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30)));
private static readonly Vector2 PlaneNormalS = new Vector2(0, -1);
private static readonly Vector2 PlaneNormalSW =
new Vector2(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30)));
private static readonly Vector2 PlaneNormalNW =
new Vector2(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30)));
private static readonly Plane2D[] BoundaryPlanes =
{
new Plane2D(CornerNE, PlaneNormalN),
new Plane2D(CornerNW, PlaneNormalNW),
new Plane2D(CornerW, PlaneNormalSW),
new Plane2D(CornerSW, PlaneNormalS),
new Plane2D(CornerSE, PlaneNormalSE),
new Plane2D(CornerE, PlaneNormalNE),
};
public HexCell() public HexCell()
{ {
@ -46,7 +86,7 @@ public class HexCell : IEquatable<HexCell>
{ {
return false; return false;
} }
return CubeCoords == other.CubeCoords; return CubeCoords == other.CubeCoords;
} }
@ -54,12 +94,12 @@ public class HexCell : IEquatable<HexCell>
{ {
return Equals(cellA, cellB); return Equals(cellA, cellB);
} }
public static bool operator !=(HexCell cellA, HexCell cellB) public static bool operator !=(HexCell cellA, HexCell cellB)
{ {
return !(cellA == cellB); return !(cellA == cellB);
} }
public HexCell(Vector3 cubeCoords) public HexCell(Vector3 cubeCoords)
{ {
CubeCoords = cubeCoords; CubeCoords = cubeCoords;
@ -117,16 +157,16 @@ public class HexCell : IEquatable<HexCell>
{ {
int x = (int)CubeCoords.x; int x = (int)CubeCoords.x;
int y = (int)CubeCoords.y; int y = (int)CubeCoords.y;
int off_y = y + (x - (x & 1)) / 2; int offY = y + (x - (x & 1)) / 2;
return new Vector2(x, off_y); return new Vector2(x, offY);
} }
set set
{ {
int x = (int)value.x; int x = (int)value.x;
int y = (int)value.y; int y = (int)value.y;
int cube_y = y - (x - (x & 1)) / 2; int cubeY = y - (x - (x & 1)) / 2;
AxialCoords = new Vector2(x, cube_y); AxialCoords = new Vector2(x, cubeY);
} }
} }
@ -175,10 +215,10 @@ public class HexCell : IEquatable<HexCell>
{ {
return new[] return new[]
{ {
GetAdjacent(DIR_NE), GetAdjacent(DIR_NE),
GetAdjacent(DIR_SE), GetAdjacent(DIR_SE),
GetAdjacent(DIR_S), GetAdjacent(DIR_S),
GetAdjacent(DIR_SW), GetAdjacent(DIR_SW),
GetAdjacent(DIR_NW), GetAdjacent(DIR_NW),
GetAdjacent(DIR_N) GetAdjacent(DIR_N)
}; };
@ -211,4 +251,33 @@ public class HexCell : IEquatable<HexCell>
return path; return path;
} }
public void QueryClosestCellBoundary(Vector2 pointLocal, Vector2 dir, out int neighbourIndex, out float distance)
{
distance = Single.PositiveInfinity;
neighbourIndex = 0;
foreach (int i in Enumerable.Range(0, 6))
{
if (BoundaryPlanes[i].Normal.Dot(dir) >= Plane2D.DistancePrecision)
{
continue;
}
float planeDistance = BoundaryPlanes[i].DistanceToLineSegment(pointLocal, dir);
if (planeDistance > Single.NegativeInfinity && planeDistance < distance)
{
distance = planeDistance;
neighbourIndex = i;
}
}
}
public HexCell NextCellAlongLine(Vector2 pointLocal, Vector2 dir)
{
int planeIndex;
QueryClosestCellBoundary(pointLocal, dir, out planeIndex, out _);
return GetAdjacent(NeighborDirections[planeIndex]);
}
} }

View File

@ -1,30 +1,24 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading;
using Godot; using Godot;
using Godot.Collections; using GodotComponentTest.utils;
using Priority_Queue; using Priority_Queue;
using Array = Godot.Collections.Array; using AxialCoordDirectionPair = System.Tuple<Godot.Vector2, Godot.Vector3>;
using AxialCoordDirectionPair = System.Tuple<Godot.Vector2, Godot.Vector3>;
public class HexGrid : Resource public class HexGrid : Resource
{ {
private Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2);
private Vector2 _baseHexSize = new Vector2(1, Mathf.Sqrt(3) / 2); private Vector2 _hexSize = new(1, Mathf.Sqrt(3) / 2);
private Vector2 _hexSize = new Vector2(1, Mathf.Sqrt(3) / 2); private Vector2 _hexScale = new(1, 1);
private Vector2 _hexScale = new Vector2(1, 1); private Transform2D _hexTransform;
private Godot.Transform2D _hexTransform; private Transform2D _hexTransformInv;
private Godot.Transform2D _hexTransformInv; private HexCell _minCoords = new();
private HexCell _minCoords = new HexCell(); private HexCell _maxCoords = new();
private HexCell _maxCoords = new HexCell(); private Rect2 _boundsAxialCoords;
private Rect2 _boundsAxialCoords = new Rect2();
public System.Collections.Generic.Dictionary<Vector2, float> Obstacles = new System.Collections.Generic.Dictionary<Vector2, float>();
public System.Collections.Generic.Dictionary<(Vector2, Vector3), float> Barriers = public Dictionary<Vector2, float> Obstacles = new();
new System.Collections.Generic.Dictionary<(Vector2, Vector3), float>();
public Dictionary<(Vector2, Vector3), float> Barriers = new();
public float PathCostDefault = 1; public float PathCostDefault = 1;
@ -84,9 +78,9 @@ public class HexGrid : Resource
public void SetBoundsOffset(Vector2 minOffset, Vector2 maxOffset) public void SetBoundsOffset(Vector2 minOffset, Vector2 maxOffset)
{ {
SetBounds (HexCell.FromOffsetCoords(minOffset), HexCell.FromOffsetCoords(maxOffset)); SetBounds(HexCell.FromOffsetCoords(minOffset), HexCell.FromOffsetCoords(maxOffset));
} }
public void SetBounds(Vector2 minAxial, Vector2 maxAxial) public void SetBounds(Vector2 minAxial, Vector2 maxAxial)
{ {
SetBounds(new HexCell(minAxial), new HexCell(maxAxial)); SetBounds(new HexCell(minAxial), new HexCell(maxAxial));
@ -96,32 +90,26 @@ public class HexGrid : Resource
{ {
_minCoords = minCell; _minCoords = minCell;
_maxCoords = maxCell; _maxCoords = maxCell;
_boundsAxialCoords = new Rect2(_minCoords.AxialCoords, (_maxCoords.AxialCoords - _minCoords.AxialCoords) + Vector2.One); _boundsAxialCoords = new Rect2(_minCoords.AxialCoords,
(_maxCoords.AxialCoords - _minCoords.AxialCoords) + Vector2.One);
} }
public void AddObstacle(Vector2 axialCoords, float cost = 0) public void AddObstacle(Vector2 axialCoords, float cost = 0)
{ {
AddObstacle(new HexCell(axialCoords), cost); AddObstacle(new HexCell(axialCoords), cost);
} }
public void AddObstacle(HexCell cell, float cost = 0) public void AddObstacle(HexCell cell, float cost = 0)
{ {
if (Obstacles.ContainsKey(cell.AxialCoords)) Obstacles[cell.AxialCoords] = cost;
{
Obstacles[cell.AxialCoords] = cost;
}
else
{
Obstacles.Add(cell.AxialCoords, cost);
}
} }
public void RemoveObstacle(HexCell cell) public void RemoveObstacle(HexCell cell)
{ {
Obstacles.Remove(cell.AxialCoords); Obstacles.Remove(cell.AxialCoords);
} }
public void AddBarrier(Vector2 axialCoords, Vector3 directionCube, float cost = 0) public void AddBarrier(Vector2 axialCoords, Vector3 directionCube, float cost = 0)
{ {
AddBarrier(new HexCell(axialCoords), directionCube, cost); AddBarrier(new HexCell(axialCoords), directionCube, cost);
@ -129,13 +117,11 @@ public class HexGrid : Resource
public void AddBarrier(HexCell cell, Vector3 directionCube, float cost = 0) public void AddBarrier(HexCell cell, Vector3 directionCube, float cost = 0)
{ {
AxialCoordDirectionPair barrierKey = new AxialCoordDirectionPair(cell.AxialCoords, directionCube);
Barriers.Add((cell.AxialCoords, directionCube), cost); Barriers.Add((cell.AxialCoords, directionCube), cost);
} }
public void RemoveBarrier(HexCell cell, Vector3 directionCube) public void RemoveBarrier(HexCell cell, Vector3 directionCube)
{ {
AxialCoordDirectionPair barrierKey = new AxialCoordDirectionPair(cell.AxialCoords, directionCube);
if (Barriers.ContainsKey((cell.AxialCoords, directionCube))) if (Barriers.ContainsKey((cell.AxialCoords, directionCube)))
{ {
Barriers.Remove((cell.AxialCoords, directionCube)); Barriers.Remove((cell.AxialCoords, directionCube));
@ -176,8 +162,7 @@ public class HexGrid : Resource
} }
float barrierCost; float barrierCost;
AxialCoordDirectionPair barrierKey = new AxialCoordDirectionPair(axialCoords, directionCube); if (Barriers.ContainsKey((axialCoords, directionCube)))
if (Barriers.ContainsKey((axialCoords, directionCube)))
{ {
barrierCost = Barriers[(axialCoords, directionCube)]; barrierCost = Barriers[(axialCoords, directionCube)];
if (barrierCost == 0) if (barrierCost == 0)
@ -188,7 +173,6 @@ public class HexGrid : Resource
cost += barrierCost; cost += barrierCost;
} }
AxialCoordDirectionPair reversedBarrierKey = new AxialCoordDirectionPair(targetCell.AxialCoords, -directionCube);
if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube))) if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube)))
{ {
barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)]; barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)];
@ -199,11 +183,11 @@ public class HexGrid : Resource
cost += barrierCost; cost += barrierCost;
} }
return cost; return cost;
} }
public HexCell GetClosestWalkableCell(HexCell fromCell, HexCell toCell) public HexCell GetClosestWalkableCell(HexCell fromCell, HexCell toCell)
{ {
if (GetHexCost(toCell) == 0) if (GetHexCost(toCell) == 0)
@ -222,28 +206,29 @@ public class HexGrid : Resource
return toCell; return toCell;
} }
public List<HexCell> FindPath(HexCell startHex, HexCell goalHex) public List<HexCell> FindPath(HexCell startHex, HexCell goalHex)
{ {
Vector2 startAxialCoords = startHex.AxialCoords;
Vector2 goalAxialCoords = goalHex.AxialCoords; Vector2 goalAxialCoords = goalHex.AxialCoords;
SimplePriorityQueue<Vector2, float> frontier = new SimplePriorityQueue<Vector2, float>(); SimplePriorityQueue<Vector2, float> frontier = new SimplePriorityQueue<Vector2, float>();
frontier.Enqueue(startHex.AxialCoords, 0); frontier.Enqueue(startHex.AxialCoords, 0);
System.Collections.Generic.Dictionary<Vector2, Vector2> cameFrom = new System.Collections.Generic.Dictionary<Vector2, Vector2>(); Dictionary<Vector2, Vector2> cameFrom =
System.Collections.Generic.Dictionary<Vector2, float> costSoFar = new System.Collections.Generic.Dictionary<Vector2, float>(); new Dictionary<Vector2, Vector2>();
Dictionary<Vector2, float> costSoFar =
new Dictionary<Vector2, float>();
cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords); cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords);
costSoFar.Add(startHex.AxialCoords, 0); costSoFar.Add(startHex.AxialCoords, 0);
FindPathCheckedCells = 0; FindPathCheckedCells = 0;
while (frontier.Any()) while (frontier.Any())
{ {
FindPathCheckedCells++; FindPathCheckedCells++;
HexCell currentHex = new HexCell(frontier.Dequeue()); HexCell currentHex = new HexCell(frontier.Dequeue());
Vector2 currentAxial = currentHex.AxialCoords; Vector2 currentAxial = currentHex.AxialCoords;
if (currentHex == goalHex) if (currentHex == goalHex)
{ {
break; break;
@ -280,14 +265,14 @@ public class HexGrid : Resource
} }
// GD.Print("Checked Cell Count: " + FindPathCheckedCells); // GD.Print("Checked Cell Count: " + FindPathCheckedCells);
List<HexCell> result = new List<HexCell>();
if (!cameFrom.ContainsKey(goalHex.AxialCoords)) if (!cameFrom.ContainsKey(goalHex.AxialCoords))
{ {
GD.Print("Failed to find path from " + startHex + " to " + goalHex); GD.Print("Failed to find path from " + startHex + " to " + goalHex);
return new List<HexCell>(); return result;
} }
List<HexCell> result = new List<HexCell>();
if (GetHexCost(goalAxialCoords) != 0) if (GetHexCost(goalAxialCoords) != 0)
{ {
result.Add(goalHex); result.Add(goalHex);
@ -302,4 +287,35 @@ public class HexGrid : Resource
return result; return result;
} }
public List<HexCell> GetCellsForLine(Vector2 fromPlane, Vector2 toPlane)
{
List<HexCell> result = new List<HexCell>();
HexCell toCell = GetHexAt(toPlane);
Vector2 direction = (toPlane - fromPlane).Normalized();
Vector2 currentPointPlane = fromPlane;
HexCell currentCell = GetHexAt(currentPointPlane);
do
{
result.Add(currentCell);
GetHexCenter(currentCell);
Vector2 currentPointLocal = currentPointPlane - GetHexCenter(currentCell);
int neighbourIndex;
float boundaryPlaneDistance;
currentCell.QueryClosestCellBoundary(currentPointLocal, direction, out neighbourIndex,
out boundaryPlaneDistance);
currentCell = currentCell.GetAdjacent(HexCell.NeighborDirections[neighbourIndex]);
currentPointPlane += direction * boundaryPlaneDistance * 1.001f;
} while (currentCell != toCell);
result.Add(currentCell);
return result;
}
} }

View File

@ -1,4 +1,3 @@
using System;
using Godot; using Godot;
using GoDotTest; using GoDotTest;
using System.Diagnostics; using System.Diagnostics;
@ -30,11 +29,11 @@ public class HexCellTests : TestClass
bool result = cellA == cellB; bool result = cellA == cellB;
Assert.True(cellA == cellB); Assert.True(cellA == cellB);
Assert.False(cellA != cellB); Assert.False(cellA != cellB);
cellB.AxialCoords = new Vector2(3, 2); cellB.AxialCoords = new Vector2(3, 2);
Assert.NotEqual(cellA, cellB); Assert.NotEqual(cellA, cellB);
Assert.False(cellA == cellB); Assert.False(cellA == cellB);
Assert.True(cellA != cellB); Assert.True(cellA != cellB);
} }
[Test] [Test]
@ -133,13 +132,13 @@ public class HexCellTests : TestClass
{ {
HexCell cell = new HexCell(); HexCell cell = new HexCell();
cell.OffsetCoords = new Vector2(1, 2); cell.OffsetCoords = new Vector2(1, 2);
Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(0, 0))) == 3); Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(0, 0))) == 3);
Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(3, 4))) == 4); Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(3, 4))) == 4);
Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(-1, -1))) == 5); Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(-1, -1))) == 5);
} }
[Test] [Test]
public void TestLineTo() public void TestLineTo()
{ {
@ -156,7 +155,7 @@ public class HexCellTests : TestClass
new HexCell(4, 2), new HexCell(4, 2),
new HexCell(5, 2) new HexCell(5, 2)
}; };
Debug.Assert(path.Length == pathExpected.Length); Debug.Assert(path.Length == pathExpected.Length);
foreach (int index in Enumerable.Range(0, path.Length)) foreach (int index in Enumerable.Range(0, path.Length))
@ -181,10 +180,10 @@ public class HexCellTests : TestClass
new HexCell(2, 3), new HexCell(2, 3),
new HexCell(3, 3), new HexCell(3, 3),
new HexCell(4, 3), new HexCell(4, 3),
new HexCell(4, 4), new HexCell(4, 4),
new HexCell(5, 4) new HexCell(5, 4)
}; };
Debug.Assert(path.Length == pathExpected.Length); Debug.Assert(path.Length == pathExpected.Length);
foreach (int index in Enumerable.Range(0, path.Length)) foreach (int index in Enumerable.Range(0, path.Length))
@ -192,7 +191,7 @@ public class HexCellTests : TestClass
Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords); Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
} }
} }
[Test] [Test]
public void TestLineEdge() public void TestLineEdge()
@ -207,16 +206,109 @@ public class HexCellTests : TestClass
new HexCell(1, 2), new HexCell(1, 2),
new HexCell(2, 2), new HexCell(2, 2),
new HexCell(2, 3), new HexCell(2, 3),
new HexCell(2, 4), new HexCell(2, 4),
new HexCell(3, 4) new HexCell(3, 4)
}; };
Debug.Assert(path.Length == pathExpected.Length); Debug.Assert(path.Length == pathExpected.Length);
foreach (int index in Enumerable.Range(0, path.Length)) foreach (int index in Enumerable.Range(0, path.Length))
{ {
Debug.Print("index: " + index + " path: " + path[index].AxialCoords + " expected: " + pathExpected[index].AxialCoords); Debug.Print("index: " + index + " path: " + path[index].AxialCoords + " expected: " +
pathExpected[index].AxialCoords);
Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords); Debug.Assert(path[index].AxialCoords == pathExpected[index].AxialCoords);
} }
} }
[Test]
public void TestCellDirections()
{
HexCell cell = new HexCell();
HexCell cellN = HexCell.FromOffsetCoords(new Vector2(0, 1));
HexCell cellNW = HexCell.FromOffsetCoords(new Vector2(-1, 0));
HexCell cellSW = HexCell.FromOffsetCoords(new Vector2(-1, -1));
HexCell cellS = HexCell.FromOffsetCoords(new Vector2(0, -1));
HexCell cellSE = HexCell.FromOffsetCoords(new Vector2(1, -1));
HexCell cellNE = HexCell.FromOffsetCoords(new Vector2(1, 0));
HexCell neighbour = cell.GetAdjacent(HexCell.DIR_N);
Assert.Equal(cellN, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_NW);
Assert.Equal(cellNW, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_SW);
Assert.Equal(cellSW, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_S);
Assert.Equal(cellS, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_SE);
Assert.Equal(cellSE, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_NW);
Assert.Equal(cellNW, neighbour);
}
[Test]
public void TestCellDirectionsNonzeroReference()
{
HexCell cell = HexCell.FromOffsetCoords(new Vector2(-4, -3));
HexCell cellN = HexCell.FromOffsetCoords(new Vector2(-4, -2));
HexCell cellNW = HexCell.FromOffsetCoords(new Vector2(-5, -3));
HexCell cellSW = HexCell.FromOffsetCoords(new Vector2(-5, -4));
HexCell cellS = HexCell.FromOffsetCoords(new Vector2(-4, -4));
HexCell cellSE = HexCell.FromOffsetCoords(new Vector2(-3, -4));
HexCell cellNE = HexCell.FromOffsetCoords(new Vector2(-3, -3));
HexCell neighbour = cell.GetAdjacent(HexCell.DIR_N);
Assert.Equal(cellN, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_NW);
Assert.Equal(cellNW, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_SW);
Assert.Equal(cellSW, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_S);
Assert.Equal(cellS, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_SE);
Assert.Equal(cellSE, neighbour);
neighbour = cell.GetAdjacent(HexCell.DIR_NW);
Assert.Equal(cellNW, neighbour);
}
[Test]
public void TestNextCellAlongLine()
{
HexCell cell = new HexCell();
HexCell cellN = HexCell.FromOffsetCoords(new Vector2(0, 1));
HexCell cellNE = HexCell.FromOffsetCoords(new Vector2(1, 0));
HexCell cellSE = HexCell.FromOffsetCoords(new Vector2(1, -1));
HexCell cellS = HexCell.FromOffsetCoords(new Vector2(0, -1));
HexCell cellSW = HexCell.FromOffsetCoords(new Vector2(-1, -1));
HexCell cellNW = HexCell.FromOffsetCoords(new Vector2(-1, 0));
HexCell nextCell = cell.NextCellAlongLine(new Vector2(0, 0), new Vector2(0, 1));
Assert.Equal(cellS, nextCell);
nextCell = cell.NextCellAlongLine(new Vector2(0, 0), new Vector2(1, 1).Normalized());
Assert.Equal(cellSE, nextCell);
nextCell = cell.NextCellAlongLine(new Vector2(0, 0), new Vector2(1, -1).Normalized());
Assert.Equal(cellNE, nextCell);
nextCell = cell.NextCellAlongLine(new Vector2(0, 0), new Vector2(0, -1));
Assert.Equal(cellN, nextCell);
nextCell = cell.NextCellAlongLine(new Vector2(0, 0), new Vector2(-1, -1).Normalized());
Assert.Equal(cellNW, nextCell);
nextCell = cell.NextCellAlongLine(new Vector2(0, 0), new Vector2(-1, 1).Normalized());
Assert.Equal(cellSW, nextCell);
}
} }

View File

@ -1,6 +1,8 @@
using System.Collections.Generic;
using Godot; using Godot;
using GoDotTest; using GoDotTest;
using System.Diagnostics; using System.Diagnostics;
using Xunit;
public class HexGridTests : TestClass public class HexGridTests : TestClass
{ {
@ -20,4 +22,70 @@ public class HexGridTests : TestClass
Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, -h / 2)).AxialCoords == new Vector2(1, 0)); Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, -h / 2)).AxialCoords == new Vector2(1, 0));
Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, h / 2)).AxialCoords == new Vector2(1, -1)); Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, h / 2)).AxialCoords == new Vector2(1, -1));
} }
[Test]
public void TestGetCellsForLineSimple()
{
HexGrid grid = new HexGrid();
List<HexCell> lineCells =
grid.GetCellsForLine(new Vector2(0, 0), grid.GetHexCenterFromOffset(new Vector2(0, 2)));
Assert.Equal(3, lineCells.Count);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 0)), lineCells[0]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 1)), lineCells[1]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 2)), lineCells[2]);
lineCells =
grid.GetCellsForLine(grid.GetHexCenterFromOffset(new Vector2(0, 2)), new Vector2(0, 0));
Assert.Equal(3, lineCells.Count);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 2)), lineCells[0]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 1)), lineCells[1]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 0)), lineCells[2]);
}
[Test]
public void TestGetCellsDiagonal()
{
HexGrid grid = new HexGrid();
List<HexCell> lineCells =
grid.GetCellsForLine(new Vector2(0, 0), grid.GetHexCenterFromOffset(new Vector2(2, 1)));
Assert.Equal(3, lineCells.Count);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 0)), lineCells[0]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(1, 0)), lineCells[1]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(2, 1)), lineCells[2]);
lineCells =
grid.GetCellsForLine(grid.GetHexCenterFromOffset(new Vector2(2, 1)), new Vector2(0, 0));
Assert.Equal(3, lineCells.Count);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(2, 1)), lineCells[0]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(1, 0)), lineCells[1]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 0)), lineCells[2]);
}
[Test]
public void TestGetCellsForLineAlongEdge()
{
HexGrid grid = new HexGrid();
List<HexCell> lineCells =
grid.GetCellsForLine(new Vector2(0, -0.0001f), grid.GetHexCenterFromOffset(new Vector2(2, 0)));
Assert.Equal(3, lineCells.Count);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 0)), lineCells[0]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(1, 0)), lineCells[1]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(2, 0)), lineCells[2]);
lineCells =
grid.GetCellsForLine(new Vector2(0, 0.0001f), grid.GetHexCenterFromOffset(new Vector2(2, 0)));
Assert.Equal(3, lineCells.Count);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(0, 0)), lineCells[0]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(1, -1)), lineCells[1]);
Assert.Equal(HexCell.FromOffsetCoords(new Vector2(2, 0)), lineCells[2]);
}
} }

69
tests/Plane2DTests.cs Normal file
View File

@ -0,0 +1,69 @@
using System;
using Godot;
using GodotComponentTest.utils;
using GoDotTest;
using Xunit;
namespace GodotComponentTest.tests;
public class Plane2DTests : TestClass
{
public Plane2DTests(Node testScene) : base(testScene)
{
}
[Test]
public void Plane2DDistSimple()
{
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(0, -1));
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 0)) - 1) < Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 1))) < Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 2)) + 1) < Single.Epsilon);
}
[Test]
public void Plane2DDistAngled()
{
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 1))) < Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(0, 0)) - MathF.Sqrt(2) / 2) < Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToPoint(new Vector2(-1, 0))) < Single.Epsilon);
}
[Test]
public void Plane2DDistLineSegment()
{
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.True(
Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(-1, 0)) - 1) < Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(0, 1)) - 1) <
Single.Epsilon);
Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, -1).Normalized()) +
MathF.Sqrt(2) / 2) < Plane2D.DistancePrecision);
Assert.True(Mathf.Abs(plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(-1, 1).Normalized()) -
MathF.Sqrt(2) / 2) < Plane2D.DistancePrecision);
}
[Test]
public void Plane2DTestIsParallel()
{
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.True(plane2D.IsParallelToDir(new Vector2(1, 1.00001f).Normalized()));
Assert.True(plane2D.IsParallelToDir(new Vector2(1, 0.99999f).Normalized()));
}
[Test]
public void Plane2DDistLineSegmentParallel()
{
Plane2D plane2D = new Plane2D(new Vector2(0, 1), new Vector2(1, -1).Normalized());
Assert.Equal(Single.PositiveInfinity,
plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, 1.00001f).Normalized()));
Assert.Equal(Single.NegativeInfinity,
plane2D.DistanceToLineSegment(new Vector2(0, 0), new Vector2(1, 0.99999f).Normalized()));
}
}

47
utils/Plane2D.cs Normal file
View File

@ -0,0 +1,47 @@
using System;
using Godot;
namespace GodotComponentTest.utils;
public class Plane2D
{
public static readonly float DistancePrecision = 1.0e-5f;
private Vector2 _planePoint;
public Vector2 Normal;
public Plane2D(Vector2 planePoint, Vector2 normal)
{
_planePoint = planePoint;
Normal = normal;
}
public float DistanceToPoint(Vector2 point)
{
return (point - _planePoint).Dot(Normal);
}
public bool IsParallelToDir(Vector2 dir)
{
float normalDotDir = Normal.Dot(dir);
return (Mathf.Abs(normalDotDir) <= Plane2D.DistancePrecision);
}
public float DistanceToLineSegment(Vector2 point, Vector2 dir)
{
float normalDotDir = Normal.Dot(dir);
if (Mathf.Abs(normalDotDir) > Plane2D.DistancePrecision)
{
return (_planePoint.Dot(Normal) - point.Dot(Normal)) / normalDotDir;
}
if (normalDotDir < 0)
{
return Single.PositiveInfinity;
}
return Single.NegativeInfinity;
}
}