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.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Godot;
using Array = Godot.Collections.Array;
using GodotComponentTest.utils;
public class HexCell : IEquatable<HexCell>
{
@ -20,16 +18,58 @@ public class HexCell : IEquatable<HexCell>
return _cubeCoords.GetHashCode();
}
public static readonly Vector2 size = new Vector2(1, Mathf.Sqrt(3) / 2);
public static readonly Vector3 DIR_N = new Vector3(0, 1, -1);
public static readonly Vector3 DIR_NE = new Vector3(1, 0, -1);
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 Vector2 Size = new(1, Mathf.Sqrt(3) / 2);
private const float Width = 2;
private static readonly float Height = Mathf.Sqrt(3);
public static readonly Array DIR_ALL = new Array()
{ DIR_N, DIR_NE, DIR_SE, DIR_S, DIR_SW, DIR_NW };
public static readonly Vector3 DIR_N = new(0, 1, -1);
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()
{
@ -46,7 +86,7 @@ public class HexCell : IEquatable<HexCell>
{
return false;
}
return CubeCoords == other.CubeCoords;
}
@ -54,12 +94,12 @@ public class HexCell : IEquatable<HexCell>
{
return Equals(cellA, cellB);
}
public static bool operator !=(HexCell cellA, HexCell cellB)
{
return !(cellA == cellB);
}
public HexCell(Vector3 cubeCoords)
{
CubeCoords = cubeCoords;
@ -117,16 +157,16 @@ public class HexCell : IEquatable<HexCell>
{
int x = (int)CubeCoords.x;
int y = (int)CubeCoords.y;
int off_y = y + (x - (x & 1)) / 2;
return new Vector2(x, off_y);
int offY = y + (x - (x & 1)) / 2;
return new Vector2(x, offY);
}
set
{
int x = (int)value.x;
int y = (int)value.y;
int cube_y = y - (x - (x & 1)) / 2;
AxialCoords = new Vector2(x, cube_y);
int cubeY = y - (x - (x & 1)) / 2;
AxialCoords = new Vector2(x, cubeY);
}
}
@ -175,10 +215,10 @@ public class HexCell : IEquatable<HexCell>
{
return new[]
{
GetAdjacent(DIR_NE),
GetAdjacent(DIR_SE),
GetAdjacent(DIR_S),
GetAdjacent(DIR_SW),
GetAdjacent(DIR_NE),
GetAdjacent(DIR_SE),
GetAdjacent(DIR_S),
GetAdjacent(DIR_SW),
GetAdjacent(DIR_NW),
GetAdjacent(DIR_N)
};
@ -211,4 +251,33 @@ public class HexCell : IEquatable<HexCell>
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.ObjectModel;
using System.Linq;
using System.Threading;
using Godot;
using Godot.Collections;
using GodotComponentTest.utils;
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
{
private Vector2 _baseHexSize = new Vector2(1, Mathf.Sqrt(3) / 2);
private Vector2 _hexSize = new Vector2(1, Mathf.Sqrt(3) / 2);
private Vector2 _hexScale = new Vector2(1, 1);
private Godot.Transform2D _hexTransform;
private Godot.Transform2D _hexTransformInv;
private HexCell _minCoords = new HexCell();
private HexCell _maxCoords = new HexCell();
private Rect2 _boundsAxialCoords = new Rect2();
public System.Collections.Generic.Dictionary<Vector2, float> Obstacles = new System.Collections.Generic.Dictionary<Vector2, float>();
private Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2);
private Vector2 _hexSize = new(1, Mathf.Sqrt(3) / 2);
private Vector2 _hexScale = new(1, 1);
private Transform2D _hexTransform;
private Transform2D _hexTransformInv;
private HexCell _minCoords = new();
private HexCell _maxCoords = new();
private Rect2 _boundsAxialCoords;
public System.Collections.Generic.Dictionary<(Vector2, Vector3), float> Barriers =
new System.Collections.Generic.Dictionary<(Vector2, Vector3), float>();
public Dictionary<Vector2, float> Obstacles = new();
public Dictionary<(Vector2, Vector3), float> Barriers = new();
public float PathCostDefault = 1;
@ -84,9 +78,9 @@ public class HexGrid : Resource
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)
{
SetBounds(new HexCell(minAxial), new HexCell(maxAxial));
@ -96,32 +90,26 @@ public class HexGrid : Resource
{
_minCoords = minCell;
_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)
{
AddObstacle(new HexCell(axialCoords), cost);
}
public void AddObstacle(HexCell cell, float cost = 0)
{
if (Obstacles.ContainsKey(cell.AxialCoords))
{
Obstacles[cell.AxialCoords] = cost;
}
else
{
Obstacles.Add(cell.AxialCoords, cost);
}
Obstacles[cell.AxialCoords] = cost;
}
public void RemoveObstacle(HexCell cell)
{
Obstacles.Remove(cell.AxialCoords);
}
public void AddBarrier(Vector2 axialCoords, Vector3 directionCube, float cost = 0)
{
AddBarrier(new HexCell(axialCoords), directionCube, cost);
@ -129,13 +117,11 @@ public class HexGrid : Resource
public void AddBarrier(HexCell cell, Vector3 directionCube, float cost = 0)
{
AxialCoordDirectionPair barrierKey = new AxialCoordDirectionPair(cell.AxialCoords, directionCube);
Barriers.Add((cell.AxialCoords, directionCube), cost);
}
public void RemoveBarrier(HexCell cell, Vector3 directionCube)
{
AxialCoordDirectionPair barrierKey = new AxialCoordDirectionPair(cell.AxialCoords, directionCube);
if (Barriers.ContainsKey((cell.AxialCoords, directionCube)))
{
Barriers.Remove((cell.AxialCoords, directionCube));
@ -176,8 +162,7 @@ public class HexGrid : Resource
}
float barrierCost;
AxialCoordDirectionPair barrierKey = new AxialCoordDirectionPair(axialCoords, directionCube);
if (Barriers.ContainsKey((axialCoords, directionCube)))
if (Barriers.ContainsKey((axialCoords, directionCube)))
{
barrierCost = Barriers[(axialCoords, directionCube)];
if (barrierCost == 0)
@ -188,7 +173,6 @@ public class HexGrid : Resource
cost += barrierCost;
}
AxialCoordDirectionPair reversedBarrierKey = new AxialCoordDirectionPair(targetCell.AxialCoords, -directionCube);
if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube)))
{
barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)];
@ -199,11 +183,11 @@ public class HexGrid : Resource
cost += barrierCost;
}
return cost;
}
public HexCell GetClosestWalkableCell(HexCell fromCell, HexCell toCell)
{
if (GetHexCost(toCell) == 0)
@ -222,28 +206,29 @@ public class HexGrid : Resource
return toCell;
}
public List<HexCell> FindPath(HexCell startHex, HexCell goalHex)
{
Vector2 startAxialCoords = startHex.AxialCoords;
Vector2 goalAxialCoords = goalHex.AxialCoords;
SimplePriorityQueue<Vector2, float> frontier = new SimplePriorityQueue<Vector2, float>();
frontier.Enqueue(startHex.AxialCoords, 0);
System.Collections.Generic.Dictionary<Vector2, Vector2> cameFrom = new System.Collections.Generic.Dictionary<Vector2, Vector2>();
System.Collections.Generic.Dictionary<Vector2, float> costSoFar = new System.Collections.Generic.Dictionary<Vector2, float>();
Dictionary<Vector2, Vector2> cameFrom =
new Dictionary<Vector2, Vector2>();
Dictionary<Vector2, float> costSoFar =
new Dictionary<Vector2, float>();
cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords);
costSoFar.Add(startHex.AxialCoords, 0);
FindPathCheckedCells = 0;
while (frontier.Any())
{
FindPathCheckedCells++;
HexCell currentHex = new HexCell(frontier.Dequeue());
Vector2 currentAxial = currentHex.AxialCoords;
if (currentHex == goalHex)
{
break;
@ -280,14 +265,14 @@ public class HexGrid : Resource
}
// GD.Print("Checked Cell Count: " + FindPathCheckedCells);
List<HexCell> result = new List<HexCell>();
if (!cameFrom.ContainsKey(goalHex.AxialCoords))
{
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)
{
result.Add(goalHex);
@ -302,4 +287,35 @@ public class HexGrid : Resource
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 GoDotTest;
using System.Diagnostics;
@ -30,11 +29,11 @@ public class HexCellTests : TestClass
bool result = cellA == cellB;
Assert.True(cellA == cellB);
Assert.False(cellA != cellB);
cellB.AxialCoords = new Vector2(3, 2);
Assert.NotEqual(cellA, cellB);
Assert.False(cellA == cellB);
Assert.True(cellA != cellB);
Assert.True(cellA != cellB);
}
[Test]
@ -133,13 +132,13 @@ public class HexCellTests : TestClass
{
HexCell cell = new HexCell();
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(3, 4))) == 4);
Debug.Assert(cell.DistanceTo(new HexCell(new Vector2(-1, -1))) == 5);
}
[Test]
public void TestLineTo()
{
@ -156,7 +155,7 @@ public class HexCellTests : TestClass
new HexCell(4, 2),
new HexCell(5, 2)
};
Debug.Assert(path.Length == pathExpected.Length);
foreach (int index in Enumerable.Range(0, path.Length))
@ -181,10 +180,10 @@ public class HexCellTests : TestClass
new HexCell(2, 3),
new HexCell(3, 3),
new HexCell(4, 3),
new HexCell(4, 4),
new HexCell(4, 4),
new HexCell(5, 4)
};
Debug.Assert(path.Length == pathExpected.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);
}
}
[Test]
public void TestLineEdge()
@ -207,16 +206,109 @@ public class HexCellTests : TestClass
new HexCell(1, 2),
new HexCell(2, 2),
new HexCell(2, 3),
new HexCell(2, 4),
new HexCell(2, 4),
new HexCell(3, 4)
};
Debug.Assert(path.Length == pathExpected.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);
}
}
[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 GoDotTest;
using System.Diagnostics;
using Xunit;
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, -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;
}
}