structure du projet, ajout de la lib hexagonal
This commit is contained in:
14
Src/Giants.Application/Giants.Application.csproj
Normal file
14
Src/Giants.Application/Giants.Application.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Giants.Infrastructure\Giants.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\Giants.Core\Giants.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
18
Src/Giants.Application/Src/GiantApplication.cs
Normal file
18
Src/Giants.Application/Src/GiantApplication.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Giants.Application;
|
||||
|
||||
using Giants.Core.Interfaces;
|
||||
using Giants.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Une application Giants permettant l'instanciations des services, utilisé pour une console
|
||||
/// ou autre archi sans injections de dependances
|
||||
/// /// </summary>
|
||||
public class GiantApplication
|
||||
{
|
||||
|
||||
/// <summary> Constructeur de base </summary>
|
||||
public GiantApplication()
|
||||
{
|
||||
IHexagonalGrid grid = new HexagonalGridImpl();
|
||||
}
|
||||
}
|
||||
9
Src/Giants.Core/Giants.Core.csproj
Normal file
9
Src/Giants.Core/Giants.Core.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
23
Src/Giants.Core/Src/Entities/Match.cs
Normal file
23
Src/Giants.Core/Src/Entities/Match.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Giants.Core.Interfaces;
|
||||
|
||||
namespace Giants.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Il s'agit d'une instance d'une partie de Giants, c'est la racine de toutes les entitées de l'instance
|
||||
/// /// </summary>
|
||||
public class Match
|
||||
{
|
||||
#region données statiques
|
||||
IHexagonalGrid _grid;
|
||||
#endregion
|
||||
|
||||
#region données dynamiques
|
||||
#endregion
|
||||
|
||||
/// <summary> Construction de base </summary>
|
||||
/// <param name="grid"> La grid du jeu</param>
|
||||
public Match(IHexagonalGrid grid)
|
||||
{
|
||||
_grid = grid;
|
||||
}
|
||||
}
|
||||
9
Src/Giants.Core/Src/Interfaces/IHexagonalGrid.cs
Normal file
9
Src/Giants.Core/Src/Interfaces/IHexagonalGrid.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Giants.Core.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// represente un outils qui permet de gérer une grille hexagonal
|
||||
/// </summary>
|
||||
public interface IHexagonalGrid
|
||||
{
|
||||
|
||||
}
|
||||
9
Src/Giants.Core/Src/ValuesObjects/BoardLayout.cs
Normal file
9
Src/Giants.Core/Src/ValuesObjects/BoardLayout.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Giants.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represente l'organisation d'un plateau, il ne contient aucune informations liées a une partie mais il permet
|
||||
/// d'effectuer des recherches de chemins ou de type de tuiles
|
||||
/// </summary>
|
||||
public class BoardLayout
|
||||
{
|
||||
}
|
||||
8
Src/Giants.Core/Src/ValuesObjects/TileInfos.cs
Normal file
8
Src/Giants.Core/Src/ValuesObjects/TileInfos.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Giants.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represente les information statiques d'une case. Ses voisins, son type, aucune informations qui peut changer pendant une partie
|
||||
/// </summary>
|
||||
public class TileInfos
|
||||
{
|
||||
}
|
||||
5
Src/Giants.Core/Src/ValuesObjects/TilePosition.cs
Normal file
5
Src/Giants.Core/Src/ValuesObjects/TilePosition.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Giants.Core;
|
||||
|
||||
public class TilePosition
|
||||
{
|
||||
}
|
||||
14
Src/Giants.Infrastructure/Giants.Infrastructure.csproj
Normal file
14
Src/Giants.Infrastructure/Giants.Infrastructure.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Giants.Core\Giants.Core.csproj" />
|
||||
<ProjectReference Include="..\HexagonalLib\src\HexagonalLib.Net\HexagonalLib.Net\HexagonalLib.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
16
Src/Giants.Infrastructure/Src/HexagonalGridImpl.cs
Normal file
16
Src/Giants.Infrastructure/Src/HexagonalGridImpl.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
namespace Giants.Infrastructure;
|
||||
|
||||
using Giants.Core.Interfaces;
|
||||
|
||||
using HexagonalLib;
|
||||
|
||||
public class HexagonalGridImpl : IHexagonalGrid
|
||||
{
|
||||
private readonly HexagonalGrid _grid;
|
||||
|
||||
public HexagonalGridImpl()
|
||||
{
|
||||
_grid = new HexagonalGrid(HexagonalGridType.PointyEven, 1.0f);
|
||||
}
|
||||
}
|
||||
127
Src/HexagonalLib/README.md
Normal file
127
Src/HexagonalLib/README.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# HexagonalLib implementation for .NET
|
||||
|
||||
_I was highly inspired by this article: [https://www.redblobgames.com/grids/hexagons/](https://www.redblobgames.com/grids/hexagons/) and this repo is mostly just C# implementation of it. Recommended for reading. Here I will describe some technical details regarding implementation._
|
||||
|
||||
## Hexagonal grid
|
||||
|
||||
To use hexagonal grids you need to create [HexagonalGrid](src/HexagonalLib/HexagonalGrid.cs) and initialize it with grid type and inscribed radius of hex.
|
||||
|
||||
```C#
|
||||
var grid = new HexagonalGrid(HexagonalGridType.PointyEven, 1.0f);
|
||||
```
|
||||
|
||||
Grids layouts and orientations are merged to one enum [HexagonalGridType](src/HexagonalLib/HexagonalGridType.cs).
|
||||
|
||||
* PointyOdd - Horizontal layout shoves odd rows right [odd-r]
|
||||
* PointyEven - Horizontal layout shoves even rows right [even-r]
|
||||
* FlatOdd - Vertical layout shoves odd columns down [odd-q]
|
||||
* FlatEven - Vertical layout shoves even columns down [even-q]
|
||||
|
||||
|
||||
## Coordinates systems
|
||||
|
||||
There is three coordinates systems represented in lib:
|
||||
|
||||
* [Offset](src/HexagonalLib/Coordinates/Offset.cs) - offset coordinates ([link](https://www.redblobgames.com/grids/hexagons/#coordinates-offset))
|
||||
* [Cubic](src/HexagonalLib/Coordinates/Cubic.cs) - cube coordinates ([link](https://www.redblobgames.com/grids/hexagons/#coordinates-cube))
|
||||
* [Axial](src/HexagonalLib/Coordinates/Axial.cs) - axial coordinates ([link](https://www.redblobgames.com/grids/hexagons/#coordinates-axial))
|
||||
|
||||
_Note: there is no [Doubled](https://www.redblobgames.com/grids/hexagons/#coordinates-doubled) coordinates yet. I will add them in later versions._
|
||||
|
||||
|
||||
Struct [HexagonalGrid](src/HexagonalLib/HexagonalGrid.cs) contains a bunch of methods for coordinates conversion (all details are [described](https://www.redblobgames.com/grids/hexagons/#conversions) in original article):
|
||||
|
||||
```C#
|
||||
var offsetFromCubic = grid.ToOffset(cubic);
|
||||
var offsetFromAxial = grid.ToOffset(axial);
|
||||
|
||||
var cubicFromOffset = grid.ToCubic(offset);
|
||||
var cubicFromAxial = grid.ToCubic(axial);
|
||||
|
||||
var axialFromOffset = grid.ToAxial(offset);
|
||||
var axialFromCubic = grid.ToAxial(cubic);
|
||||
```
|
||||
|
||||
## Neighbors
|
||||
|
||||
There is the order of neighbors of hex:
|
||||
|
||||

|
||||
|
||||
You can take a neighbor of any hex by providing a coordinate of hex and required neighbor index.
|
||||
|
||||
```C#
|
||||
var oNeighbor = grid.GetNeighbor(offset, neighborIndex);
|
||||
var cNeighbor = grid.GetNeighbor(cubic, neighborIndex);
|
||||
var aNeighbor = grid.GetNeighbor(axial, neighborIndex);
|
||||
```
|
||||
_The neighbor index can be negative or greater than 5 (it will be normalized)_
|
||||
|
||||
Or you can take all neighbors of particular hex
|
||||
|
||||
```C#
|
||||
var oNeighbors = grid.GetNeighbors(offset);
|
||||
var cNeighbors = grid.GetNeighbors(cubic);
|
||||
var aNeighbors = grid.GetNeighbors(axial);
|
||||
```
|
||||
|
||||
There is also option to check are two hexes neighbors or not:
|
||||
|
||||
```C#
|
||||
var isNeighbors1 = grid.IsNeighbors(offset, oNeighbor);
|
||||
var isNeighbors2 = grid.IsNeighbors(cubic, cNeighbor);
|
||||
var isNeighbors3 = grid.IsNeighbors(axial, aNeighbor);
|
||||
```
|
||||
|
||||
### Ring of neighbors
|
||||
You also can get ring of neighbor for particular hex
|
||||
|
||||

|
||||
|
||||
```C#
|
||||
var oNeighbors = grid.GetNeighborsRing(offset, radius);
|
||||
var cNeighbors = grid.GetNeighborsRing(cubic, radius);
|
||||
var aNeighbors = grid.GetNeighborsRing(axial, radius);
|
||||
```
|
||||
|
||||
### Circle of neighbors
|
||||
Same as ring but you will receive hexes from all rings
|
||||
|
||||

|
||||
|
||||
```C#
|
||||
var oNeighbors = grid.GetNeighborsAround(offset, radius);
|
||||
var cNeighbors = grid.GetNeighborsAround(cubic, radius);
|
||||
var aNeighbors = grid.GetNeighborsAround(axial, radius);
|
||||
```
|
||||
|
||||
## 2D space
|
||||
|
||||
There is also list of methods to convert hex coordinate to its center (point represented as tuple):
|
||||
|
||||
```C#
|
||||
var pointFromOffset = grid.ToPoint2(offset);
|
||||
var pointFromCubic = grid.ToPoint2(cubic);
|
||||
var pointFromAxial = grid.ToPoint2(axial);
|
||||
```
|
||||
|
||||
To convert point into a hex coordinate which contains this point you can use:
|
||||
|
||||
```C#
|
||||
var offsetFromPoint = grid.ToOffset(x, y);
|
||||
var cubicFromPoint = grid.ToCubic(x, y);
|
||||
var axialFromPoint = grid.ToAxial(x, y);
|
||||
```
|
||||
|
||||
Hexagons have 6 sides and 6 corners. There is corners order of the hexes:
|
||||
|
||||

|
||||
|
||||
To take them in code simly use:
|
||||
|
||||
```C#
|
||||
var cornerFromOffset = grid.GetCorner(offset, cornerIndex);
|
||||
var cornerFromCubic = grid.GetCorner(cubic, cornerIndex);
|
||||
var cornerFromAxial = grid.GetCorner(axial, cornerIndex);
|
||||
```
|
||||
_The corner index can be negative or greater than 5 (it will be normalized)_
|
||||
BIN
Src/HexagonalLib/img/hex-around.png
Normal file
BIN
Src/HexagonalLib/img/hex-around.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
Src/HexagonalLib/img/hex-corners-order.png
Normal file
BIN
Src/HexagonalLib/img/hex-corners-order.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
Src/HexagonalLib/img/hex-neighbors-order.png
Normal file
BIN
Src/HexagonalLib/img/hex-neighbors-order.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
Src/HexagonalLib/img/hex-rings.png
Normal file
BIN
Src/HexagonalLib/img/hex-rings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
70
Src/HexagonalLib/src/.editorconfig
Normal file
70
Src/HexagonalLib/src/.editorconfig
Normal file
@@ -0,0 +1,70 @@
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=crlf
|
||||
trim_trailing_whitespace=false
|
||||
insert_final_newline=false
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_new_line_before_members_in_object_initializers=false
|
||||
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||
csharp_style_var_elsewhere=true:suggestion
|
||||
csharp_style_var_for_built_in_types=true:suggestion
|
||||
csharp_style_var_when_type_is_apparent=true:suggestion
|
||||
dotnet_naming_rule.private_constants_rule.severity=warning
|
||||
dotnet_naming_rule.private_constants_rule.style=upper_camel_case_style
|
||||
dotnet_naming_rule.private_constants_rule.symbols=private_constants_symbols
|
||||
dotnet_naming_rule.private_static_readonly_rule.severity=warning
|
||||
dotnet_naming_rule.private_static_readonly_rule.style=lower_camel_case_style
|
||||
dotnet_naming_rule.private_static_readonly_rule.symbols=private_static_readonly_symbols
|
||||
dotnet_naming_style.lower_camel_case_style.capitalization=camel_case
|
||||
dotnet_naming_style.lower_camel_case_style.required_prefix=_
|
||||
dotnet_naming_style.upper_camel_case_style.capitalization=pascal_case
|
||||
dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities=private
|
||||
dotnet_naming_symbols.private_constants_symbols.applicable_kinds=field
|
||||
dotnet_naming_symbols.private_constants_symbols.required_modifiers=const
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities=private
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds=field
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers=static,readonly
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators=never_if_unnecessary:none
|
||||
dotnet_style_parentheses_in_other_binary_operators=never_if_unnecessary:none
|
||||
dotnet_style_parentheses_in_relational_binary_operators=never_if_unnecessary:none
|
||||
dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access=true:suggestion
|
||||
dotnet_style_qualification_for_event=false:suggestion
|
||||
dotnet_style_qualification_for_field=false:suggestion
|
||||
dotnet_style_qualification_for_method=false:suggestion
|
||||
dotnet_style_qualification_for_property=false:suggestion
|
||||
dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion
|
||||
|
||||
# ReSharper properties
|
||||
resharper_autodetect_indent_settings=true
|
||||
resharper_csharp_max_attribute_length_for_same_line=80
|
||||
resharper_csharp_max_line_length=170
|
||||
resharper_keep_existing_attribute_arrangement=true
|
||||
resharper_max_attribute_length_for_same_line=20
|
||||
resharper_place_field_attribute_on_same_line=false
|
||||
resharper_use_indent_from_vs=false
|
||||
|
||||
# ReSharper inspection severities
|
||||
resharper_arrange_redundant_parentheses_highlighting=hint
|
||||
resharper_arrange_this_qualifier_highlighting=hint
|
||||
resharper_arrange_type_member_modifiers_highlighting=hint
|
||||
resharper_arrange_type_modifiers_highlighting=hint
|
||||
resharper_built_in_type_reference_style_for_member_access_highlighting=hint
|
||||
resharper_built_in_type_reference_style_highlighting=hint
|
||||
resharper_member_can_be_private_global_highlighting=none
|
||||
resharper_partial_type_with_single_part_highlighting=none
|
||||
resharper_redundant_base_qualifier_highlighting=warning
|
||||
resharper_suggest_var_or_type_built_in_types_highlighting=hint
|
||||
resharper_suggest_var_or_type_elsewhere_highlighting=hint
|
||||
resharper_suggest_var_or_type_simple_types_highlighting=hint
|
||||
resharper_web_config_module_not_resolved_highlighting=warning
|
||||
resharper_web_config_type_not_resolved_highlighting=warning
|
||||
resharper_web_config_wrong_module_highlighting=warning
|
||||
|
||||
[*.{appxmanifest,asax,ascx,aspx,build,cs,cshtml,dtd,fs,fsi,fsscript,fsx,master,ml,mli,nuspec,razor,resw,resx,skin,vb,xaml,xamlx,xoml,xsd}]
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
tab_width=4
|
||||
4
Src/HexagonalLib/src/.gitignore
vendored
Normal file
4
Src/HexagonalLib/src/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.bin/*
|
||||
.obj/*
|
||||
.idea/*
|
||||
.packages/*
|
||||
@@ -0,0 +1,135 @@
|
||||
using HexagonalLib.Coordinates;
|
||||
using NUnit.Framework;
|
||||
using static System.Math;
|
||||
|
||||
namespace HexagonalLib.Tests
|
||||
{
|
||||
[TestFixture(TestOf = typeof(HexagonalGrid))]
|
||||
public class HexagonalGridTests
|
||||
{
|
||||
private float InscribedRadius => 0.5f;
|
||||
private float DescribedRadius => (float) (InscribedRadius / Cos(PI / HexagonalGrid.EdgesCount));
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check initial properties values for flat grids after creation")]
|
||||
[TestCase(HexagonalGridType.FlatEven)]
|
||||
[TestCase(HexagonalGridType.FlatOdd)]
|
||||
public void FlatPropertiesTest(HexagonalGridType type)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
Assert.AreEqual(grid.InscribedRadius, InscribedRadius);
|
||||
Assert.AreEqual(grid.DescribedRadius, DescribedRadius);
|
||||
Assert.AreEqual(grid.InscribedDiameter, InscribedRadius * 2);
|
||||
Assert.AreEqual(grid.DescribedDiameter, DescribedRadius * 2);
|
||||
Assert.AreEqual(grid.HorizontalOffset, DescribedRadius * 1.5f);
|
||||
Assert.AreEqual(grid.VerticalOffset, InscribedRadius * 2.0f);
|
||||
Assert.AreEqual(grid.Side, DescribedRadius);
|
||||
Assert.AreEqual(grid.AngleToFirstNeighbor, 30.0f);
|
||||
}
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check initial properties values for pointy grids after creation")]
|
||||
[TestCase(HexagonalGridType.PointyEven)]
|
||||
[TestCase(HexagonalGridType.PointyOdd)]
|
||||
public void PointyPropertiesTest(HexagonalGridType type)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
Assert.AreEqual(grid.InscribedRadius, InscribedRadius);
|
||||
Assert.AreEqual(grid.DescribedRadius, DescribedRadius);
|
||||
Assert.AreEqual(grid.InscribedDiameter, InscribedRadius * 2);
|
||||
Assert.AreEqual(grid.DescribedDiameter, DescribedRadius * 2);
|
||||
Assert.AreEqual(grid.HorizontalOffset, InscribedRadius * 2.0f);
|
||||
Assert.AreEqual(grid.VerticalOffset, DescribedRadius * 1.5f);
|
||||
Assert.AreEqual(grid.Side, DescribedRadius);
|
||||
Assert.AreEqual(grid.AngleToFirstNeighbor, 0.0f);
|
||||
}
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check coordinate conversion from Offset")]
|
||||
public void CoordinateConversionTest(
|
||||
[Values] HexagonalGridType type,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetX,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetY)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
var offset = new Offset(offsetX, offsetY);
|
||||
var axial = grid.ToAxial(offset);
|
||||
var cubic = grid.ToCubic(offset);
|
||||
Assert.IsTrue(cubic.IsValid(), $"Invalid cubic coordinate: {cubic.X}-{cubic.Y}-{cubic.Z}");
|
||||
Assert.AreEqual(offset, grid.ToOffset(axial));
|
||||
Assert.AreEqual(offset, grid.ToOffset(cubic));
|
||||
Assert.AreEqual(axial, grid.ToAxial(offset));
|
||||
Assert.AreEqual(axial, grid.ToAxial(cubic));
|
||||
Assert.AreEqual(cubic, grid.ToCubic(offset));
|
||||
Assert.AreEqual(cubic, grid.ToCubic(axial));
|
||||
}
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check conversion to Point2 from Offset")]
|
||||
public void PointConversionTest(
|
||||
[Values] HexagonalGridType type,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetX,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetY)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
var offset = new Offset(offsetX, offsetY);
|
||||
var axial = grid.ToAxial(offset);
|
||||
var cubic = grid.ToCubic(offset);
|
||||
|
||||
var fromOffset = grid.ToPoint2(offset);
|
||||
var fromAxial = grid.ToPoint2(axial);
|
||||
var fromCubic = grid.ToPoint2(cubic);
|
||||
|
||||
Assert.IsTrue(fromOffset.SimilarTo(fromAxial), $"Expected: {fromAxial.X}:{fromAxial.Y}; Actual: {fromOffset.X}:{fromOffset.Y}");
|
||||
Assert.IsTrue(fromOffset.SimilarTo(fromCubic), $"Expected: {fromCubic.X}:{fromCubic.Y}; Actual: {fromOffset.X}:{fromOffset.Y}");
|
||||
|
||||
Assert.AreEqual(offset, grid.ToOffset(fromOffset));
|
||||
Assert.AreEqual(axial, grid.ToAxial(fromAxial));
|
||||
Assert.AreEqual(cubic, grid.ToCubic(fromCubic));
|
||||
}
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check IsNeighbor methods for all coordinates types")]
|
||||
public void IsNeighborTest(
|
||||
[Values] HexagonalGridType type,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetX,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetY,
|
||||
[Values(-1, 0, 1, 2, 3, 4, 5, 6)] int neighborIndex)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
var offset = new Offset(offsetX, offsetY);
|
||||
var axial = grid.ToAxial(offset);
|
||||
var cubic = grid.ToCubic(offset);
|
||||
Assert.IsTrue(cubic.IsValid(), $"Invalid cubic coordinate: {cubic.X}-{cubic.Y}-{cubic.Z}");
|
||||
|
||||
var oNeighbor = grid.GetNeighbor(offset, neighborIndex);
|
||||
var aNeighbor = grid.GetNeighbor(axial, neighborIndex);
|
||||
var cNeighbor = grid.GetNeighbor(cubic, neighborIndex);
|
||||
|
||||
Assert.IsTrue(cNeighbor.IsValid(), $"Invalid cubic coordinate: {cNeighbor.X}-{cNeighbor.Y}-{cNeighbor.Z}");
|
||||
Assert.IsTrue(grid.IsNeighbors(offset, oNeighbor), $"Neighbor1={offset}; Neighbor2={oNeighbor}; Index={neighborIndex};");
|
||||
Assert.IsTrue(grid.IsNeighbors(axial, aNeighbor), $"Neighbor1={axial}; Neighbor2={aNeighbor}; Index={neighborIndex};");
|
||||
Assert.IsTrue(grid.IsNeighbors(cubic, cNeighbor), $"Neighbor1={cubic}; Neighbor2={cNeighbor}; Index={neighborIndex};");
|
||||
}
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check neighbors order for all coordinates types")]
|
||||
public void NeighborsOrderTest(
|
||||
[Values] HexagonalGridType type,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetX,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetY,
|
||||
[Values(-1, 0, 1, 2, 3, 4, 5, 6)] int neighborIndex)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
var offset = new Offset(offsetX, offsetY);
|
||||
var axial = grid.ToAxial(offset);
|
||||
var cubic = grid.ToCubic(offset);
|
||||
|
||||
var oNeighbor = grid.GetNeighbor(offset, neighborIndex);
|
||||
var aNeighbor = grid.GetNeighbor(axial, neighborIndex);
|
||||
var cNeighbor = grid.GetNeighbor(cubic, neighborIndex);
|
||||
|
||||
Assert.IsTrue(cNeighbor.IsValid(), $"Invalid cubic coordinate: {cNeighbor.X}-{cNeighbor.Y}-{cNeighbor.Z}");
|
||||
|
||||
var fromAxial = grid.ToOffset(aNeighbor);
|
||||
var fromCubic = grid.ToOffset(cNeighbor);
|
||||
|
||||
Assert.AreEqual(oNeighbor, fromAxial, $"Center=({offset} - {axial}); Current=({oNeighbor} - {aNeighbor}); Index={neighborIndex};");
|
||||
Assert.AreEqual(oNeighbor, fromCubic, $"Center=({offset} - {cubic}); Current=({oNeighbor} - {cNeighbor}); Index={neighborIndex};");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>HexagonalLib.Tests</RootNamespace>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TargetFrameworks>net472;net48;netcoreapp3.1</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Net.Tests\Debug\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Net.Tests\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="nunit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HexagonalLib.Net\HexagonalLib.Net.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,25 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace HexagonalLib.Tests
|
||||
{
|
||||
[TestFixture(TestOf = typeof(HexagonalMath))]
|
||||
public class HexagonalMathTests
|
||||
{
|
||||
[Test(Author = "Ivan Murashka", Description = "Check HexagonalMath.Rotate method")]
|
||||
[TestCase(0, 1, 0, 0, 1)]
|
||||
[TestCase(0, 1, 45, 0.5f, 0.5f)]
|
||||
[TestCase(0, 1, 90, 1, 0)]
|
||||
[TestCase(0, 1, 135, 0.5f, -0.5f)]
|
||||
[TestCase(0, 1, 180, 0, -1)]
|
||||
[TestCase(0, 1, 225, -0.5f, -0.5f)]
|
||||
[TestCase(0, 1, 270, -1, 0)]
|
||||
[TestCase(0, 1, 315, -0.5f, 0.5f)]
|
||||
[TestCase(0, 1, 360, 0, 1)]
|
||||
public void RotateTest(float x, float y, float degrees, float resultX, float resultY)
|
||||
{
|
||||
var vector = (x, y).Rotate(degrees);
|
||||
var result = (resultX, resultY).Normalize();
|
||||
Assert.IsTrue(vector.SimilarTo(result), $"Expected: {result.X}:{result.Y}; Actual: {vector.X}:{vector.Y}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>HexagonalLib</RootNamespace>
|
||||
<TargetFrameworks>net472;net48;netcoreapp3.1</TargetFrameworks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Title>HexagonalLib</Title>
|
||||
<Authors>Ivan Murashka</Authors>
|
||||
<AssemblyVersion>1.0.0</AssemblyVersion>
|
||||
<PackageProjectUrl>https://github.com/imurashka/HexagonalLib</PackageProjectUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Net\Debug\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Net\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\HexagonalLib\HexagonalLib.projitems" Label="Shared" />
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,57 @@
|
||||
using HexagonalLib.Coordinates;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace HexagonalLib.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class HexagonalGridTests
|
||||
{
|
||||
private float InscribedRadius => 0.5f;
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check coordinate conversion from Offset to Unity vectors")]
|
||||
public void Vector2ConversionTest(
|
||||
[Values] HexagonalGridType type,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetX,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetY)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
var offset = new Offset(offsetX, offsetY);
|
||||
var axial = grid.ToAxial(offset);
|
||||
var cubic = grid.ToCubic(offset);
|
||||
|
||||
var fromOffset = grid.ToVector2(offset);
|
||||
var fromAxial = grid.ToVector2(axial);
|
||||
var fromCubic = grid.ToVector2(cubic);
|
||||
|
||||
Assert.IsTrue(fromOffset.SimilarTo(fromAxial), $"Expected: {fromAxial}; Actual: {fromOffset}");
|
||||
Assert.IsTrue(fromOffset.SimilarTo(fromCubic), $"Expected: {fromCubic}; Actual: {fromOffset}");
|
||||
|
||||
Assert.AreEqual(offset, grid.ToOffset(fromOffset));
|
||||
Assert.AreEqual(axial, grid.ToAxial(fromAxial));
|
||||
Assert.AreEqual(cubic, grid.ToCubic(fromCubic));
|
||||
}
|
||||
|
||||
[Test(Author = "Ivan Murashka", Description = "Check coordinate conversion from Offset to Unity vectors")]
|
||||
public void Vector3ConversionTest(
|
||||
[Values] HexagonalGridType type,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetX,
|
||||
[Values(-13, -8, 0, 15, 22)] int offsetY)
|
||||
{
|
||||
var grid = new HexagonalGrid(type, InscribedRadius);
|
||||
var offset = new Offset(offsetX, offsetY);
|
||||
var axial = grid.ToAxial(offset);
|
||||
var cubic = grid.ToCubic(offset);
|
||||
|
||||
var fromOffset = grid.ToVector3(offset);
|
||||
var fromAxial = grid.ToVector3(axial);
|
||||
var fromCubic = grid.ToVector3(cubic);
|
||||
|
||||
Assert.IsTrue(fromOffset.SimilarTo(fromAxial), $"Expected: {fromAxial}; Actual: {fromOffset}");
|
||||
Assert.IsTrue(fromOffset.SimilarTo(fromCubic), $"Expected: {fromCubic}; Actual: {fromOffset}");
|
||||
|
||||
Assert.AreEqual(offset, grid.ToOffset(fromOffset));
|
||||
Assert.AreEqual(axial, grid.ToAxial(fromAxial));
|
||||
Assert.AreEqual(cubic, grid.ToCubic(fromCubic));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\.packages\NUnit.3.12.0\build\NUnit.props" Condition="Exists('..\..\.packages\NUnit.3.12.0\build\NUnit.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{1E807875-014C-4745-86AB-E1E35916F8B3}</ProjectGuid>
|
||||
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>HexagonalLib.Tests</RootNamespace>
|
||||
<AssemblyName>HexagonalLib.Unity.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Unity.Tests\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Unity.Tests\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb">
|
||||
<HintPath>..\..\.packages\NUnit.3.12.0\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>..\..\.packages\Unity3D.UnityEngine.2018.3.5.1\lib\UnityEngine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="HexagonalGridTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HexagonalLib.Unity\HexagonalLib.Unity.csproj">
|
||||
<Project>{ee497232-ac7a-48d0-9059-53b152c6bf01}</Project>
|
||||
<Name>HexagonalLib.Unity</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\.packages\NUnit.3.12.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\.packages\NUnit.3.12.0\build\NUnit.props'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("HexagonalLib.Unity.Tests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("HexagonalLib.Unity.Tests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1E807875-014C-4745-86AB-E1E35916F8B3")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit" version="3.12.0" targetFramework="net472" />
|
||||
<package id="Unity3D.UnityEngine" version="2018.3.5.1" targetFramework="net472" />
|
||||
</packages>
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace HexagonalLib.Coordinates
|
||||
{
|
||||
public partial struct Axial
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace HexagonalLib.Coordinates
|
||||
{
|
||||
public readonly partial struct Cubic
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace HexagonalLib.Coordinates
|
||||
{
|
||||
public readonly partial struct Offset
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HexagonalLib.Coordinates;
|
||||
using HexagonalLib.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HexagonalLib
|
||||
{
|
||||
public partial struct HexagonalGrid
|
||||
{
|
||||
#region ToOffset
|
||||
|
||||
/// <summary>
|
||||
/// Convert point to offset coordinate
|
||||
/// </summary>
|
||||
public Offset ToOffset(Vector2 vector)
|
||||
{
|
||||
return ToOffset(vector.AsTuple());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert point to offset coordinate
|
||||
/// </summary>
|
||||
public Offset ToOffset(Vector3 vector)
|
||||
{
|
||||
return ToOffset(vector.AsTuple());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToAxial
|
||||
|
||||
/// <summary>
|
||||
/// Convert point to axial coordinate
|
||||
/// </summary>
|
||||
public Axial ToAxial(Vector2 vector)
|
||||
{
|
||||
return ToAxial(vector.AsTuple());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert point to axial coordinate
|
||||
/// </summary>
|
||||
public Axial ToAxial(Vector3 vector)
|
||||
{
|
||||
return ToAxial(vector.AsTuple());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToCubic
|
||||
|
||||
/// <summary>
|
||||
/// Convert point to cubic coordinate
|
||||
/// </summary>
|
||||
public Cubic ToCubic(Vector2 vector)
|
||||
{
|
||||
return ToCubic(vector.AsTuple());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert point to cubic coordinate
|
||||
/// </summary>
|
||||
public Cubic ToCubic(Vector3 vector)
|
||||
{
|
||||
return ToCubic(vector.AsTuple());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToVector
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its offset coordinate to it center position in 2d space
|
||||
/// </summary>
|
||||
public Vector2 ToVector2(Offset coord)
|
||||
{
|
||||
return ToPoint2(coord).AsVector2();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its axial coordinate to it center position in 2d space
|
||||
/// </summary>
|
||||
public Vector2 ToVector2(Axial coord)
|
||||
{
|
||||
return ToPoint2(coord).AsVector2();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its cubic coordinate to it center position in 2d space
|
||||
/// </summary>
|
||||
public Vector2 ToVector2(Cubic coord)
|
||||
{
|
||||
return ToPoint2(coord).AsVector2();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its offset coordinate to it center position in 3d space OZ
|
||||
/// </summary>
|
||||
public Vector3 ToVector3(Offset coord, float y = 0)
|
||||
{
|
||||
return ToPoint2(coord).AsVector3(y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its axial coordinate to it center position in 3d space OZ
|
||||
/// </summary>
|
||||
public Vector3 ToVector3(Axial coord, float y = 0)
|
||||
{
|
||||
return ToPoint2(coord).AsVector3(y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its cubic coordinate to it center position in 3d space OZ
|
||||
/// </summary>
|
||||
public Vector3 ToVector3(Cubic coord, float y = 0)
|
||||
{
|
||||
return ToPoint2(coord).AsVector3(y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CreateMesh
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for list of hex. The generation algorithm is taken from the site:
|
||||
/// </summary>
|
||||
public Mesh CreateMesh(IReadOnlyList<Offset> hexes, int subdivide)
|
||||
{
|
||||
return CreateMesh(hexes, subdivide, CreateMesh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for list of hex. The generation algorithm is taken from the site:
|
||||
/// </summary>
|
||||
public Mesh CreateMesh(IReadOnlyList<Axial> hexes, int subdivide)
|
||||
{
|
||||
return CreateMesh(hexes, subdivide, CreateMesh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for list of hex. The generation algorithm is taken from the site:
|
||||
/// </summary>
|
||||
public Mesh CreateMesh(IReadOnlyList<Cubic> hexes, int subdivide)
|
||||
{
|
||||
return CreateMesh(hexes, subdivide, CreateMesh);
|
||||
}
|
||||
|
||||
private Mesh CreateMesh<T>(IReadOnlyList<T> hexes, int subdivide, Action<IEnumerable<T>, int, Action<int, (float X, float Y)>, Action<int, int>> createMesh)
|
||||
{
|
||||
var meshData = GetMeshData(hexes.Count, subdivide);
|
||||
var vertices = new Vector3[meshData.VerticesCount];
|
||||
var triangles = new int[meshData.IndicesCount];
|
||||
|
||||
createMesh(hexes, subdivide,
|
||||
(i, point) => vertices[i] = new Vector3(point.X, 0, point.Y),
|
||||
(i, index) => triangles[i] = index);
|
||||
|
||||
var mesh = new Mesh
|
||||
{
|
||||
vertices = vertices,
|
||||
triangles = triangles
|
||||
};
|
||||
|
||||
mesh.RecalculateNormals();
|
||||
return mesh;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{EE497232-AC7A-48D0-9059-53B152C6BF01}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>HexagonalLib</RootNamespace>
|
||||
<AssemblyName>HexagonalLib.Unity</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Unity\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\.bin\HexagonalLib.Unity\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>..\..\.packages\Unity3D.UnityEngine.2018.3.5.1\lib\UnityEngine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Coordinates\Axial.cs" />
|
||||
<Compile Include="Coordinates\Cubic.cs" />
|
||||
<Compile Include="Coordinates\Offset.cs" />
|
||||
<Compile Include="HexagonalGrid.cs" />
|
||||
<Compile Include="HexagonalMath.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utility\VectorUtility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\..\HexagonalLib\HexagonalLib.projitems" Label="Shared" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HexagonalLib
|
||||
{
|
||||
public static partial class HexagonalMath
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares two vectors and returns true if they are similar.
|
||||
/// </summary>
|
||||
public static bool SimilarTo(this in Vector2 a, in Vector2 b)
|
||||
{
|
||||
return a.x.SimilarTo(b.x) && a.y.SimilarTo(b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two vectors and returns true if they are similar.
|
||||
/// </summary>
|
||||
public static bool SimilarTo(this in Vector3 a, in Vector3 b)
|
||||
{
|
||||
return a.x.SimilarTo(b.x) && a.y.SimilarTo(b.y) && a.z.SimilarTo(b.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("HexagonalLib.Unity")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("HexagonalLib.Unity")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("EE497232-AC7A-48D0-9059-53B152C6BF01")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,27 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HexagonalLib.Utility
|
||||
{
|
||||
internal static class VectorUtility
|
||||
{
|
||||
internal static (float x, float y) AsTuple(this Vector2 vector)
|
||||
{
|
||||
return (vector.x, vector.y);
|
||||
}
|
||||
|
||||
internal static (float x, float y) AsTuple(this Vector3 vector)
|
||||
{
|
||||
return (vector.x, vector.z);
|
||||
}
|
||||
|
||||
internal static Vector2 AsVector2(this (float x, float y) tuple)
|
||||
{
|
||||
return new Vector2(tuple.x, tuple.y);
|
||||
}
|
||||
|
||||
internal static Vector3 AsVector3(this (float x, float y) tuple, float y = 0.0f)
|
||||
{
|
||||
return new Vector3(tuple.x, y, tuple.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Unity3D.UnityEngine" version="2018.3.5.1" targetFramework="net472" />
|
||||
</packages>
|
||||
50
Src/HexagonalLib/src/HexagonalLib.sln
Normal file
50
Src/HexagonalLib/src/HexagonalLib.sln
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "HexagonalLib", "HexagonalLib\HexagonalLib.shproj", "{30905D5D-8D2E-4A25-ABEB-B4B4B0C440E6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HexagonalLib.Unity", "HexagonalLib.Unity", "{C02A807F-4B02-4900-895F-67E25F8CF774}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexagonalLib.Unity.Tests", "HexagonalLib.Unity\HexagonalLib.Unity.Tests\HexagonalLib.Unity.Tests.csproj", "{1E807875-014C-4745-86AB-E1E35916F8B3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexagonalLib.Unity", "HexagonalLib.Unity\HexagonalLib.Unity\HexagonalLib.Unity.csproj", "{EE497232-AC7A-48D0-9059-53B152C6BF01}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HexagonalLib.Net", "HexagonalLib.Net", "{1F507917-8D29-4061-BC98-7B64DABADED3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexagonalLib.Net.Tests", "HexagonalLib.Net\HexagonalLib.Net.Tests\HexagonalLib.Net.Tests.csproj", "{2608E591-E33C-446A-9E95-AFED738B2512}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexagonalLib.Net", "HexagonalLib.Net\HexagonalLib.Net\HexagonalLib.Net.csproj", "{CF391253-3550-40CF-810B-A6F8FCA2EBF2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{30905D5D-8D2E-4A25-ABEB-B4B4B0C440E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{30905D5D-8D2E-4A25-ABEB-B4B4B0C440E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EE497232-AC7A-48D0-9059-53B152C6BF01}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE497232-AC7A-48D0-9059-53B152C6BF01}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE497232-AC7A-48D0-9059-53B152C6BF01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EE497232-AC7A-48D0-9059-53B152C6BF01}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1E807875-014C-4745-86AB-E1E35916F8B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1E807875-014C-4745-86AB-E1E35916F8B3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1E807875-014C-4745-86AB-E1E35916F8B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1E807875-014C-4745-86AB-E1E35916F8B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CF391253-3550-40CF-810B-A6F8FCA2EBF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CF391253-3550-40CF-810B-A6F8FCA2EBF2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CF391253-3550-40CF-810B-A6F8FCA2EBF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CF391253-3550-40CF-810B-A6F8FCA2EBF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2608E591-E33C-446A-9E95-AFED738B2512}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2608E591-E33C-446A-9E95-AFED738B2512}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2608E591-E33C-446A-9E95-AFED738B2512}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2608E591-E33C-446A-9E95-AFED738B2512}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{1E807875-014C-4745-86AB-E1E35916F8B3} = {C02A807F-4B02-4900-895F-67E25F8CF774}
|
||||
{EE497232-AC7A-48D0-9059-53B152C6BF01} = {C02A807F-4B02-4900-895F-67E25F8CF774}
|
||||
{CF391253-3550-40CF-810B-A6F8FCA2EBF2} = {1F507917-8D29-4061-BC98-7B64DABADED3}
|
||||
{2608E591-E33C-446A-9E95-AFED738B2512} = {1F507917-8D29-4061-BC98-7B64DABADED3}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
86
Src/HexagonalLib/src/HexagonalLib/Coordinates/Axial.cs
Normal file
86
Src/HexagonalLib/src/HexagonalLib/Coordinates/Axial.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HexagonalLib.Coordinates
|
||||
{
|
||||
[Serializable]
|
||||
public readonly partial struct Axial : IEquatable<Axial>, IEqualityComparer<Axial>
|
||||
{
|
||||
public static Axial Zero => new Axial(0, 0);
|
||||
|
||||
public readonly int Q;
|
||||
public readonly int R;
|
||||
|
||||
public Axial(int q, int r)
|
||||
: this()
|
||||
{
|
||||
Q = q;
|
||||
R = r;
|
||||
}
|
||||
|
||||
public static bool operator ==(Axial coord1, Axial coord2)
|
||||
{
|
||||
return (coord1.Q, coord1.R) == (coord2.Q, coord2.R);
|
||||
}
|
||||
|
||||
public static bool operator !=(Axial coord1, Axial coord2)
|
||||
{
|
||||
return (coord1.Q, coord1.R) != (coord2.Q, coord2.R);
|
||||
}
|
||||
|
||||
public static Axial operator +(Axial coord1, Axial coord2)
|
||||
{
|
||||
return new Axial(coord1.Q + coord2.Q, coord1.R + coord2.R);
|
||||
}
|
||||
|
||||
public static Axial operator +(Axial coord, int offset)
|
||||
{
|
||||
return new Axial(coord.Q + offset, coord.R + offset);
|
||||
}
|
||||
|
||||
public static Axial operator -(Axial coord1, Axial coord2)
|
||||
{
|
||||
return new Axial(coord1.Q - coord2.Q, coord1.R - coord2.R);
|
||||
}
|
||||
|
||||
public static Axial operator -(Axial coord, int offset)
|
||||
{
|
||||
return new Axial(coord.Q - offset, coord.R - offset);
|
||||
}
|
||||
|
||||
public static Axial operator *(Axial coord, int offset)
|
||||
{
|
||||
return new Axial(coord.Q * offset, coord.R * offset);
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
return other is Axial axial && Equals(axial);
|
||||
}
|
||||
|
||||
public bool Equals(Axial other)
|
||||
{
|
||||
return (Q, R) == (other.Q, other.R);
|
||||
}
|
||||
|
||||
public bool Equals(Axial coord1, Axial coord2)
|
||||
{
|
||||
return coord1.Equals(coord2);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (Q, R).GetHashCode();
|
||||
}
|
||||
|
||||
public int GetHashCode(Axial axial)
|
||||
{
|
||||
return axial.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"A-[{Q}:{R}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
148
Src/HexagonalLib/src/HexagonalLib/Coordinates/Cubic.cs
Normal file
148
Src/HexagonalLib/src/HexagonalLib/Coordinates/Cubic.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HexagonalLib.Coordinates
|
||||
{
|
||||
[Serializable]
|
||||
public readonly partial struct Cubic : IEquatable<Cubic>, IEqualityComparer<Cubic>
|
||||
{
|
||||
public static Cubic Zero => new Cubic(0, 0, 0);
|
||||
|
||||
public readonly int X;
|
||||
public readonly int Y;
|
||||
public readonly int Z;
|
||||
|
||||
public Cubic(int x, int y, int z)
|
||||
: this()
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round float coordinates to nearest valid coordinate
|
||||
/// </summary>
|
||||
public Cubic(float x, float y, float z)
|
||||
{
|
||||
var rx = (int) Math.Round(x);
|
||||
var ry = (int) Math.Round(y);
|
||||
var rz = (int) Math.Round(z);
|
||||
|
||||
var xDiff = Math.Abs(rx - x);
|
||||
var yDiff = Math.Abs(ry - y);
|
||||
var zDiff = Math.Abs(rz - z);
|
||||
|
||||
if (xDiff > yDiff && xDiff > zDiff)
|
||||
{
|
||||
rx = -ry - rz;
|
||||
}
|
||||
else if (yDiff > zDiff)
|
||||
{
|
||||
ry = -rx - rz;
|
||||
}
|
||||
else
|
||||
{
|
||||
rz = -rx - ry;
|
||||
}
|
||||
|
||||
X = rx;
|
||||
Y = ry;
|
||||
Z = rz;
|
||||
}
|
||||
|
||||
public static bool operator ==(Cubic coord1, Cubic coord2)
|
||||
{
|
||||
return (coord1.X, coord1.Y, coord1.Z) == (coord2.X, coord2.Y, coord2.Z);
|
||||
}
|
||||
|
||||
public static bool operator !=(Cubic coord1, Cubic coord2)
|
||||
{
|
||||
return (coord1.X, coord1.Y, coord1.Z) != (coord2.X, coord2.Y, coord2.Z);
|
||||
}
|
||||
|
||||
public static Cubic operator +(Cubic coord1, Cubic coord2)
|
||||
{
|
||||
return new Cubic(coord1.X + coord2.X, coord1.Y + coord2.Y, coord1.Z + coord2.Z);
|
||||
}
|
||||
|
||||
public static Cubic operator +(Cubic coord, int offset)
|
||||
{
|
||||
return new Cubic(coord.X + offset, coord.Y + offset, coord.Z + offset);
|
||||
}
|
||||
|
||||
public static Cubic operator -(Cubic coord1, Cubic coord2)
|
||||
{
|
||||
return new Cubic(coord1.X - coord2.X, coord1.Y - coord2.Y, coord1.Z - coord2.Z);
|
||||
}
|
||||
|
||||
public static Cubic operator -(Cubic coord, int offset)
|
||||
{
|
||||
return new Cubic(coord.X - offset, coord.Y - offset, coord.Z - offset);
|
||||
}
|
||||
|
||||
public static Cubic operator *(Cubic coord, int offset)
|
||||
{
|
||||
return new Cubic(coord.X * offset, coord.Y * offset, coord.Z * offset);
|
||||
}
|
||||
|
||||
public static Cubic operator *(Cubic coord, float delta)
|
||||
{
|
||||
return new Cubic(coord.X * delta, coord.Y * delta, coord.Z * delta);
|
||||
}
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return X + Y + Z == 0;
|
||||
}
|
||||
|
||||
public Cubic RotateToRight()
|
||||
{
|
||||
var x = -Y;
|
||||
var y = -Z;
|
||||
var z = -X;
|
||||
return new Cubic(x, y, z);
|
||||
}
|
||||
|
||||
public Cubic RotateToRight(int times)
|
||||
{
|
||||
var cur = this;
|
||||
for (var i = 0; i < times; i++)
|
||||
{
|
||||
cur = cur.RotateToRight();
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Cubic other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(Cubic other)
|
||||
{
|
||||
return (X, Y, Z) == (other.X, other.Y, other.Z);
|
||||
}
|
||||
|
||||
public bool Equals(Cubic coord1, Cubic coord2)
|
||||
{
|
||||
return coord1.Equals(coord2);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (X, Y, Z).GetHashCode();
|
||||
}
|
||||
|
||||
public int GetHashCode(Cubic coord)
|
||||
{
|
||||
return coord.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return !IsValid() ? "C-[Invalid]" : $"C-[{X}:{Y}:{Z}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Src/HexagonalLib/src/HexagonalLib/Coordinates/Offset.cs
Normal file
113
Src/HexagonalLib/src/HexagonalLib/Coordinates/Offset.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HexagonalLib.Coordinates
|
||||
{
|
||||
[Serializable]
|
||||
public readonly partial struct Offset : IEquatable<Offset>, IEqualityComparer<Offset>
|
||||
{
|
||||
public static Offset Zero => new Offset(0, 0);
|
||||
|
||||
public readonly int X;
|
||||
public readonly int Y;
|
||||
|
||||
public Offset(int x, int y)
|
||||
: this()
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public Offset Add(int xOffset, int yOffset)
|
||||
{
|
||||
return new Offset(X + xOffset, Y + yOffset);
|
||||
}
|
||||
|
||||
public static Offset Clamp(Offset coord, Offset min, Offset max)
|
||||
{
|
||||
var x = Clamp(coord.X, min.X, max.X);
|
||||
var y = Clamp(coord.Y, min.Y, max.Y);
|
||||
|
||||
return new Offset(x, y);
|
||||
}
|
||||
|
||||
private static int Clamp(int value, int min, int max)
|
||||
{
|
||||
if (value < min)
|
||||
{
|
||||
value = min;
|
||||
}
|
||||
else if (value > max)
|
||||
{
|
||||
value = max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static bool operator ==(Offset coord1, Offset coord2)
|
||||
{
|
||||
return (coord1.X, coord1.Y) == (coord2.X, coord2.Y);
|
||||
}
|
||||
|
||||
public static bool operator !=(Offset coord1, Offset coord2)
|
||||
{
|
||||
return (coord1.X, coord1.Y) != (coord2.X, coord2.Y);
|
||||
}
|
||||
|
||||
public static Offset operator +(Offset coord1, Offset coord2)
|
||||
{
|
||||
return new Offset(coord1.X + coord2.X, coord1.Y + coord2.Y);
|
||||
}
|
||||
|
||||
public static Offset operator +(Offset coord, int offset)
|
||||
{
|
||||
return new Offset(coord.X + offset, coord.Y + offset);
|
||||
}
|
||||
|
||||
public static Offset operator -(Offset coord, Offset index2)
|
||||
{
|
||||
return new Offset(coord.X - index2.X, coord.Y - index2.Y);
|
||||
}
|
||||
|
||||
public static Offset operator /(Offset coord, int value)
|
||||
{
|
||||
return new Offset(coord.X / value, coord.Y / value);
|
||||
}
|
||||
|
||||
public static Offset operator *(Offset coord, int offset)
|
||||
{
|
||||
return new Offset(coord.X * offset, coord.Y * offset);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Offset other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(Offset other)
|
||||
{
|
||||
return (X, Y) == (other.X, other.Y);
|
||||
}
|
||||
|
||||
public bool Equals(Offset coord1, Offset coord2)
|
||||
{
|
||||
return coord1.Equals(coord2);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (X, Y).GetHashCode();
|
||||
}
|
||||
|
||||
public int GetHashCode(Offset coord)
|
||||
{
|
||||
return coord.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"O-[{X}:{Y}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Src/HexagonalLib/src/HexagonalLib/HexagonalException.cs
Normal file
72
Src/HexagonalLib/src/HexagonalLib/HexagonalException.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace HexagonalLib
|
||||
{
|
||||
public class HexagonalException : Exception
|
||||
{
|
||||
private class MessageBuilder
|
||||
{
|
||||
private readonly StringBuilder _message = new StringBuilder();
|
||||
|
||||
public MessageBuilder Append(string message)
|
||||
{
|
||||
_message.AppendLine(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageBuilder Append(HexagonalGrid grid)
|
||||
{
|
||||
Append(nameof(grid.Type), grid.Type);
|
||||
Append(nameof(grid.InscribedRadius), grid.InscribedRadius);
|
||||
Append(nameof(grid.DescribedRadius), grid.DescribedRadius);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageBuilder Append(params (string, object)[] fields)
|
||||
{
|
||||
foreach (var (paramName, paramValue) in fields)
|
||||
{
|
||||
Append(paramName, paramValue);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void Append(string paramName, object paramValue)
|
||||
{
|
||||
_message.Append($"{paramName}={paramValue}; ");
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _message.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public HexagonalException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public HexagonalException(string message, params (string, object)[] fields)
|
||||
: base(CreateBuilder(message).Append(fields).ToString())
|
||||
{
|
||||
}
|
||||
|
||||
public HexagonalException(string message, HexagonalGrid grid)
|
||||
: base(CreateBuilder(message).Append(grid).ToString())
|
||||
{
|
||||
}
|
||||
|
||||
public HexagonalException(string message, HexagonalGrid grid, params (string, object)[] fields)
|
||||
: base(CreateBuilder(message).Append(grid).Append(fields).ToString())
|
||||
{
|
||||
}
|
||||
|
||||
private static MessageBuilder CreateBuilder(string message)
|
||||
{
|
||||
return new MessageBuilder().Append(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
812
Src/HexagonalLib/src/HexagonalLib/HexagonalGrid.cs
Normal file
812
Src/HexagonalLib/src/HexagonalLib/HexagonalGrid.cs
Normal file
@@ -0,0 +1,812 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HexagonalLib.Coordinates;
|
||||
using static System.Math;
|
||||
|
||||
namespace HexagonalLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent geometry logic for infinity hexagonal grid
|
||||
/// </summary>
|
||||
public readonly partial struct HexagonalGrid
|
||||
{
|
||||
/// <summary>
|
||||
/// Total count of edges in one Hex
|
||||
/// </summary>
|
||||
public const int EdgesCount = 6;
|
||||
|
||||
public static readonly float Sqrt3 = (float) Sqrt(3);
|
||||
|
||||
/// <summary>
|
||||
/// Inscribed radius of the hex
|
||||
/// </summary>
|
||||
public readonly float InscribedRadius;
|
||||
|
||||
/// <summary>
|
||||
/// Described radius of hex
|
||||
/// </summary>
|
||||
public readonly float DescribedRadius;
|
||||
|
||||
/// <summary>
|
||||
/// Hexagon side length
|
||||
/// </summary>
|
||||
public float Side => DescribedRadius;
|
||||
|
||||
/// <summary>
|
||||
/// Inscribed diameter of hex
|
||||
/// </summary>
|
||||
public float InscribedDiameter => InscribedRadius * 2;
|
||||
|
||||
/// <summary>
|
||||
/// Described diameter of hex
|
||||
/// </summary>
|
||||
public float DescribedDiameter => DescribedRadius * 2;
|
||||
|
||||
/// <summary>
|
||||
/// Orientation and layout of this grid
|
||||
/// </summary>
|
||||
public readonly HexagonalGridType Type;
|
||||
|
||||
/// <summary>
|
||||
/// Offset between hex and its right-side neighbour on X axis
|
||||
/// </summary>
|
||||
public float HorizontalOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
case HexagonalGridType.PointyEven:
|
||||
return InscribedRadius * 2.0f;
|
||||
case HexagonalGridType.FlatOdd:
|
||||
case HexagonalGridType.FlatEven:
|
||||
return DescribedRadius * 1.5f;
|
||||
default:
|
||||
throw new HexagonalException($"Can't get {nameof(HorizontalOffset)} with unexpected {nameof(Type)}", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Offset between hex and its up-side neighbour on Y axis
|
||||
/// </summary>
|
||||
public float VerticalOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
case HexagonalGridType.PointyEven:
|
||||
return DescribedRadius * 1.5f;
|
||||
case HexagonalGridType.FlatOdd:
|
||||
case HexagonalGridType.FlatEven:
|
||||
return InscribedRadius * 2.0f;
|
||||
default:
|
||||
throw new HexagonalException($"Can't get {nameof(VerticalOffset)} with unexpected {nameof(Type)}", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The angle between the centers of any hex and its first neighbor relative to the vector (0, 1) clockwise
|
||||
/// </summary>
|
||||
/// <exception cref="HexagonalException"></exception>
|
||||
public float AngleToFirstNeighbor
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
case HexagonalGridType.PointyEven:
|
||||
return 0.0f;
|
||||
case HexagonalGridType.FlatOdd:
|
||||
case HexagonalGridType.FlatEven:
|
||||
return 30.0f;
|
||||
default:
|
||||
throw new HexagonalException($"Can't get {nameof(AngleToFirstNeighbor)} with unexpected {nameof(Type)}", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base constructor for hexagonal grid
|
||||
/// </summary>
|
||||
/// <param name="type">Orientation and layout of the grid</param>
|
||||
/// <param name="radius">Inscribed radius</param>
|
||||
public HexagonalGrid(HexagonalGridType type, float radius)
|
||||
{
|
||||
Type = type;
|
||||
InscribedRadius = radius;
|
||||
DescribedRadius = (float) (radius / Cos(PI / EdgesCount));
|
||||
}
|
||||
|
||||
#region ToOffset
|
||||
|
||||
/// <summary>
|
||||
/// Convert cubic coordinate to offset
|
||||
/// </summary>
|
||||
public Offset ToOffset(Cubic coord)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
{
|
||||
var col = coord.X + (coord.Z - (coord.Z & 1)) / 2;
|
||||
var row = coord.Z;
|
||||
return new Offset(col, row);
|
||||
}
|
||||
case HexagonalGridType.PointyEven:
|
||||
{
|
||||
var col = coord.X + (coord.Z + (coord.Z & 1)) / 2;
|
||||
var row = coord.Z;
|
||||
return new Offset(col, row);
|
||||
}
|
||||
case HexagonalGridType.FlatOdd:
|
||||
{
|
||||
var col = coord.X;
|
||||
var row = coord.Z + (coord.X - (coord.X & 1)) / 2;
|
||||
return new Offset(col, row);
|
||||
}
|
||||
case HexagonalGridType.FlatEven:
|
||||
{
|
||||
var col = coord.X;
|
||||
var row = coord.Z + (coord.X + (coord.X & 1)) / 2;
|
||||
return new Offset(col, row);
|
||||
}
|
||||
default:
|
||||
throw new HexagonalException($"{nameof(ToOffset)} failed with unexpected {nameof(Type)}", this, (nameof(coord), coord));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert axial coordinate to offset
|
||||
/// </summary>
|
||||
public Offset ToOffset(Axial axial)
|
||||
{
|
||||
return ToOffset(ToCubic(axial));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the offset coordinate of the hex which contains a point
|
||||
/// </summary>
|
||||
public Offset ToOffset(float x, float y)
|
||||
{
|
||||
return ToOffset(ToCubic(x, y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the offset coordinate of the hex which contains a point
|
||||
/// </summary>
|
||||
public Offset ToOffset((float X, float Y) point)
|
||||
{
|
||||
return ToOffset(ToCubic(point.X, point.Y));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToAxial
|
||||
|
||||
/// <summary>
|
||||
/// Convert cubic coordinate to axial
|
||||
/// </summary>
|
||||
public Axial ToAxial(Cubic cubic)
|
||||
{
|
||||
return new Axial(cubic.X, cubic.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert offset coordinate to axial
|
||||
/// </summary>
|
||||
public Axial ToAxial(Offset offset)
|
||||
{
|
||||
return ToAxial(ToCubic(offset));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axial coordinate of the hex which contains a point
|
||||
/// </summary>
|
||||
public Axial ToAxial(float x, float y)
|
||||
{
|
||||
return ToAxial(ToCubic(x, y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axial coordinate of the hex which contains a point
|
||||
/// </summary>
|
||||
public Axial ToAxial((float X, float Y) point)
|
||||
{
|
||||
return ToAxial(ToCubic(point.X, point.Y));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToCubic
|
||||
|
||||
/// <summary>
|
||||
/// Convert offset coordinate to cubic
|
||||
/// </summary>
|
||||
public Cubic ToCubic(Offset coord)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
{
|
||||
var x = coord.X - (coord.Y - (coord.Y & 1)) / 2;
|
||||
var z = coord.Y;
|
||||
var y = -x - z;
|
||||
return new Cubic(x, y, z);
|
||||
}
|
||||
case HexagonalGridType.PointyEven:
|
||||
{
|
||||
var x = coord.X - (coord.Y + (coord.Y & 1)) / 2;
|
||||
var z = coord.Y;
|
||||
var y = -x - z;
|
||||
return new Cubic(x, y, z);
|
||||
}
|
||||
case HexagonalGridType.FlatOdd:
|
||||
{
|
||||
var x = coord.X;
|
||||
var z = coord.Y - (coord.X - (coord.X & 1)) / 2;
|
||||
var y = -x - z;
|
||||
return new Cubic(x, y, z);
|
||||
}
|
||||
case HexagonalGridType.FlatEven:
|
||||
{
|
||||
var x = coord.X;
|
||||
var z = coord.Y - (coord.X + (coord.X & 1)) / 2;
|
||||
var y = -x - z;
|
||||
return new Cubic(x, y, z);
|
||||
}
|
||||
default:
|
||||
throw new HexagonalException($"{nameof(ToCubic)} failed with unexpected {nameof(Type)}", this, (nameof(coord), coord));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert axial coordinate to cubic
|
||||
/// </summary>
|
||||
public Cubic ToCubic(Axial axial)
|
||||
{
|
||||
return new Cubic(axial.Q, -axial.Q - axial.R, axial.R);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cubic coordinate of the hex which contains a point
|
||||
/// </summary>
|
||||
public Cubic ToCubic(float x, float y)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
case HexagonalGridType.PointyEven:
|
||||
{
|
||||
var q = (x * Sqrt3 / 3.0f - y / 3.0f) / Side;
|
||||
var r = y * 2.0f / 3.0f / Side;
|
||||
return new Cubic(q, -q - r, r);
|
||||
}
|
||||
case HexagonalGridType.FlatOdd:
|
||||
case HexagonalGridType.FlatEven:
|
||||
{
|
||||
var q = x * 2.0f / 3.0f / Side;
|
||||
var r = (-x / 3.0f + Sqrt3 / 3.0f * y) / Side;
|
||||
return new Cubic(q, -q - r, r);
|
||||
}
|
||||
default:
|
||||
throw new HexagonalException($"{nameof(ToCubic)} failed with unexpected {nameof(Type)}", this, (nameof(x), x), (nameof(y), y));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cubic coordinate of the hex which contains a point
|
||||
/// </summary>
|
||||
public Cubic ToCubic((float X, float Y) point)
|
||||
{
|
||||
return ToCubic(point.X, point.Y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToPoint2
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its offset coordinate to it center position in 2d space
|
||||
/// </summary>
|
||||
public (float X, float Y) ToPoint2(Offset coord)
|
||||
{
|
||||
return ToPoint2(ToAxial(coord));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its axial coordinate to it center position in 2d space
|
||||
/// </summary>
|
||||
public (float X, float Y) ToPoint2(Axial coord)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
case HexagonalGridType.PointyEven:
|
||||
{
|
||||
var x = Side * (Sqrt3 * coord.Q + Sqrt3 / 2 * coord.R);
|
||||
var y = Side * (3.0f / 2.0f * coord.R);
|
||||
return (x, y);
|
||||
}
|
||||
case HexagonalGridType.FlatOdd:
|
||||
case HexagonalGridType.FlatEven:
|
||||
{
|
||||
var x = Side * (3.0f / 2.0f * coord.Q);
|
||||
var y = Side * (Sqrt3 / 2 * coord.Q + Sqrt3 * coord.R);
|
||||
return (x, y);
|
||||
}
|
||||
default:
|
||||
throw new HexagonalException($"{nameof(ToPoint2)} failed with unexpected {nameof(Type)}", this, (nameof(coord), coord));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex based on its cubic coordinate to it center position in 2d space
|
||||
/// </summary>
|
||||
public (float X, float Y) ToPoint2(Cubic coord)
|
||||
{
|
||||
return ToPoint2(ToAxial(coord));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetCornerPoint
|
||||
|
||||
/// <summary>
|
||||
/// Returns corner point in 2d space of given coordinate
|
||||
/// </summary>
|
||||
public (float X, float Y) GetCornerPoint(Offset coord, int edge)
|
||||
{
|
||||
return GetCornerPoint(coord, edge, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns corner point in 2d space of given coordinate
|
||||
/// </summary>
|
||||
public (float X, float Y) GetCornerPoint(Axial coord, int edge)
|
||||
{
|
||||
return GetCornerPoint(coord, edge, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns corner point in 2d space of given coordinate
|
||||
/// </summary>
|
||||
public (float X, float Y) GetCornerPoint(Cubic coord, int edge)
|
||||
{
|
||||
return GetCornerPoint(coord, edge, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns corner point in 2d space of given coordinate
|
||||
/// </summary>
|
||||
private (float X, float Y) GetCornerPoint<T>(T coord, int edge, Func<T, (float X, float Y)> toPoint)
|
||||
where T : struct
|
||||
{
|
||||
edge = NormalizeIndex(edge);
|
||||
var angleDeg = 60 * edge;
|
||||
if (Type == HexagonalGridType.PointyEven || Type == HexagonalGridType.PointyOdd)
|
||||
{
|
||||
angleDeg -= 30;
|
||||
}
|
||||
|
||||
var center = toPoint(coord);
|
||||
var angleRad = PI / 180 * angleDeg;
|
||||
var x = (float) (center.X + DescribedRadius * Cos(angleRad));
|
||||
var y = (float) (center.Y + DescribedRadius * Sin(angleRad));
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetNeighbor
|
||||
|
||||
/// <summary>
|
||||
/// Returns the neighbor at the specified index.
|
||||
/// </summary>
|
||||
public Offset GetNeighbor(Offset coord, int neighborIndex)
|
||||
{
|
||||
return coord + GetNeighborsOffsets(coord)[NormalizeIndex(neighborIndex)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the neighbor at the specified index.
|
||||
/// </summary>
|
||||
public Axial GetNeighbor(Axial coord, int neighborIndex)
|
||||
{
|
||||
return coord + _axialNeighbors[NormalizeIndex(neighborIndex)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the neighbor at the specified index.
|
||||
/// </summary>
|
||||
public Cubic GetNeighbor(Cubic coord, int neighborIndex)
|
||||
{
|
||||
return coord + _cubicNeighbors[NormalizeIndex(neighborIndex)];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetNeighbors
|
||||
|
||||
/// <summary>
|
||||
/// Return all neighbors of the hex
|
||||
/// </summary>
|
||||
public IEnumerable<Offset> GetNeighbors(Offset hex)
|
||||
{
|
||||
foreach (var offset in GetNeighborsOffsets(hex))
|
||||
{
|
||||
yield return offset + hex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return all neighbors of the hex
|
||||
/// </summary>
|
||||
public IEnumerable<Axial> GetNeighbors(Axial hex)
|
||||
{
|
||||
foreach (var offset in _axialNeighbors)
|
||||
{
|
||||
yield return offset + hex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return all neighbors of the hex
|
||||
/// </summary>
|
||||
public IEnumerable<Cubic> GetNeighbors(Cubic hex)
|
||||
{
|
||||
foreach (var offset in _cubicNeighbors)
|
||||
{
|
||||
yield return offset + hex;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsNeighbors
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two hexes are neighbors or no
|
||||
/// </summary>
|
||||
public bool IsNeighbors(Offset coord1, Offset coord2)
|
||||
{
|
||||
return IsNeighbors(coord1, coord2, GetNeighbor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two hexes are neighbors or no
|
||||
/// </summary>
|
||||
public bool IsNeighbors(Axial coord1, Axial coord2)
|
||||
{
|
||||
Func<Axial, int, Axial> getNeighbor = GetNeighbor;
|
||||
return IsNeighbors(coord1, coord2, getNeighbor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two hexes are neighbors or no
|
||||
/// </summary>
|
||||
public bool IsNeighbors(Cubic coord1, Cubic coord2)
|
||||
{
|
||||
return IsNeighbors(coord1, coord2, GetNeighbor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two hexes are neighbors or no
|
||||
/// </summary>
|
||||
public bool IsNeighbors<T>(T coord1, T coord2, in Func<T, int, T> getNeighbor)
|
||||
where T : struct, IEqualityComparer<T>
|
||||
{
|
||||
for (var neighborIndex = 0; neighborIndex < EdgesCount; neighborIndex++)
|
||||
{
|
||||
var neighbor = getNeighbor(coord1, neighborIndex);
|
||||
if (neighbor.Equals(coord2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetNeighborsRing
|
||||
|
||||
/// <summary>
|
||||
/// Returns a ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<Offset> GetNeighborsRing(Offset center, int radius)
|
||||
{
|
||||
return GetNeighborsRing(center, radius, GetNeighbor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<Axial> GetNeighborsRing(Axial center, int radius)
|
||||
{
|
||||
return GetNeighborsRing(center, radius, GetNeighbor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<Cubic> GetNeighborsRing(Cubic center, int radius)
|
||||
{
|
||||
return GetNeighborsRing(center, radius, GetNeighbor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
private static IEnumerable<T> GetNeighborsRing<T>(T center, int radius, Func<T, int, T> getNeighbor)
|
||||
where T : struct
|
||||
{
|
||||
if (radius == 0)
|
||||
{
|
||||
yield return center;
|
||||
yield break;
|
||||
}
|
||||
|
||||
for (var i = 0; i < radius; i++)
|
||||
{
|
||||
center = getNeighbor(center, 4);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
for (var j = 0; j < radius; j++)
|
||||
{
|
||||
yield return center;
|
||||
center = getNeighbor(center, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetNeighborsAround
|
||||
|
||||
/// <summary>
|
||||
/// Returns a all hexes in the ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<Offset> GetNeighborsAround(Offset center, int radius)
|
||||
{
|
||||
return GetNeighborsAround(center, radius, GetNeighborsRing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a all hexes in the ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<Axial> GetNeighborsAround(Axial center, int radius)
|
||||
{
|
||||
return GetNeighborsAround(center, radius, GetNeighborsRing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a all hexes in the ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<Cubic> GetNeighborsAround(Cubic center, int radius)
|
||||
{
|
||||
return GetNeighborsAround(center, radius, GetNeighborsRing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a all hexes in the ring with a radius of <see cref="radius"/> hexes around the given <see cref="center"/>.
|
||||
/// </summary>
|
||||
private static IEnumerable<T> GetNeighborsAround<T>(T center, int radius, Func<T, int, IEnumerable<T>> getNeighborRing)
|
||||
where T : struct
|
||||
{
|
||||
for (var i = 0; i < radius; i++)
|
||||
{
|
||||
foreach (var hex in getNeighborRing(center, i))
|
||||
{
|
||||
yield return hex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetNeighborIndex
|
||||
|
||||
/// <summary>
|
||||
/// Returns the bypass index to the specified neighbor
|
||||
/// </summary>
|
||||
public byte GetNeighborIndex(Offset center, Offset neighbor)
|
||||
{
|
||||
return GetNeighborIndex(center, neighbor, GetNeighbors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the bypass index to the specified neighbor
|
||||
/// </summary>
|
||||
public byte GetNeighborIndex(Axial center, Axial neighbor)
|
||||
{
|
||||
return GetNeighborIndex(center, neighbor, GetNeighbors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the bypass index to the specified neighbor
|
||||
/// </summary>
|
||||
public byte GetNeighborIndex(Cubic center, Cubic neighbor)
|
||||
{
|
||||
return GetNeighborIndex(center, neighbor, GetNeighbors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the bypass index to the specified neighbor
|
||||
/// </summary>
|
||||
private byte GetNeighborIndex<T>(T center, T neighbor, Func<T, IEnumerable<T>> getNeighbors)
|
||||
where T : struct, IEqualityComparer<T>
|
||||
{
|
||||
byte neighborIndex = 0;
|
||||
foreach (var current in getNeighbors(center))
|
||||
{
|
||||
if (current.Equals(neighbor))
|
||||
{
|
||||
return neighborIndex;
|
||||
}
|
||||
|
||||
neighborIndex++;
|
||||
}
|
||||
|
||||
throw new HexagonalException($"Can't find bypass index", this, (nameof(center), center), (nameof(neighbor), neighbor));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetPointBetweenTwoNeighbours
|
||||
|
||||
/// <summary>
|
||||
/// Returns the midpoint of the boundary segment of two neighbors
|
||||
/// </summary>
|
||||
public (float x, float y) GetPointBetweenTwoNeighbours(Offset coord1, Offset coord2)
|
||||
{
|
||||
return GetPointBetweenTwoNeighbours(coord1, coord2, IsNeighbors, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the midpoint of the boundary segment of two neighbors
|
||||
/// </summary>
|
||||
public (float x, float y) GetPointBetweenTwoNeighbours(Axial coord1, Axial coord2)
|
||||
{
|
||||
return GetPointBetweenTwoNeighbours(coord1, coord2, IsNeighbors, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the midpoint of the boundary segment of two neighbors
|
||||
/// </summary>
|
||||
public (float x, float y) GetPointBetweenTwoNeighbours(Cubic coord1, Cubic coord2)
|
||||
{
|
||||
return GetPointBetweenTwoNeighbours(coord1, coord2, IsNeighbors, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the midpoint of the boundary segment of two neighbors
|
||||
/// </summary>
|
||||
private (float x, float y) GetPointBetweenTwoNeighbours<T>(T coord1, T coord2, Func<T, T, bool> isNeighbor, Func<T, (float X, float Y)> toPoint)
|
||||
where T : struct
|
||||
{
|
||||
if (!isNeighbor(coord1, coord2))
|
||||
{
|
||||
throw new HexagonalException($"Can't calculate point between not neighbors", this, (nameof(coord1), coord1), (nameof(coord2), coord2));
|
||||
}
|
||||
|
||||
var c1 = toPoint(coord1);
|
||||
var c2 = toPoint(coord2);
|
||||
|
||||
return ((c1.X + c2.X) / 2, (c1.Y + c2.Y) / 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CubeDistance
|
||||
|
||||
/// <summary>
|
||||
/// Manhattan distance between two hexes
|
||||
/// </summary>
|
||||
public int CubeDistance(Offset h1, Offset h2)
|
||||
{
|
||||
var cubicFrom = ToCubic(h1);
|
||||
var cubicTo = ToCubic(h2);
|
||||
return CubeDistance(cubicFrom, cubicTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manhattan distance between two hexes
|
||||
/// </summary>
|
||||
public int CubeDistance(Axial h1, Axial h2)
|
||||
{
|
||||
var cubicFrom = ToCubic(h1);
|
||||
var cubicTo = ToCubic(h2);
|
||||
return CubeDistance(cubicFrom, cubicTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manhattan distance between two hexes
|
||||
/// </summary>
|
||||
public static int CubeDistance(Cubic h1, Cubic h2)
|
||||
{
|
||||
return (Abs(h1.X - h2.X) + Abs(h1.Y - h2.Y) + Abs(h1.Z - h2.Z)) / 2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Neighbors
|
||||
|
||||
/// <summary>
|
||||
/// Return all neighbors offsets of the hex
|
||||
/// </summary>
|
||||
private IReadOnlyList<Offset> GetNeighborsOffsets(Offset coord)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case HexagonalGridType.PointyOdd:
|
||||
return Abs(coord.Y % 2) == 0 ? _pointyEvenNeighbors : _pointyOddNeighbors;
|
||||
case HexagonalGridType.PointyEven:
|
||||
return Abs(coord.Y % 2) == 1 ? _pointyEvenNeighbors : _pointyOddNeighbors;
|
||||
case HexagonalGridType.FlatOdd:
|
||||
return Abs(coord.X % 2) == 0 ? _flatEvenNeighbors : _flatOddNeighbors;
|
||||
case HexagonalGridType.FlatEven:
|
||||
return Abs(coord.X % 2) == 1 ? _flatEvenNeighbors : _flatOddNeighbors;
|
||||
default:
|
||||
throw new HexagonalException($"{nameof(GetNeighborsOffsets)} failed with unexpected {nameof(Type)}", this, (nameof(coord), coord));
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<Offset> _pointyOddNeighbors = new List<Offset>
|
||||
{
|
||||
new Offset(+1, 0), new Offset(+1, -1), new Offset(0, -1),
|
||||
new Offset(-1, 0), new Offset(0, +1), new Offset(+1, +1),
|
||||
};
|
||||
|
||||
private static readonly List<Offset> _pointyEvenNeighbors = new List<Offset>
|
||||
{
|
||||
new Offset(+1, 0), new Offset(0, -1), new Offset(-1, -1),
|
||||
new Offset(-1, 0), new Offset(-1, +1), new Offset(0, +1),
|
||||
};
|
||||
|
||||
private static readonly List<Offset> _flatOddNeighbors = new List<Offset>
|
||||
{
|
||||
new Offset(+1, +1), new Offset(+1, 0), new Offset(0, -1),
|
||||
new Offset(-1, 0), new Offset(-1, +1), new Offset(0, +1),
|
||||
};
|
||||
|
||||
private static readonly List<Offset> _flatEvenNeighbors = new List<Offset>
|
||||
{
|
||||
new Offset(+1, 0), new Offset(+1, -1), new Offset(0, -1),
|
||||
new Offset(-1, -1), new Offset(-1, 0), new Offset(0, +1),
|
||||
};
|
||||
|
||||
private static readonly List<Axial> _axialNeighbors = new List<Axial>
|
||||
{
|
||||
new Axial(+1, 0), new Axial(+1, -1), new Axial(0, -1),
|
||||
new Axial(-1, 0), new Axial(-1, +1), new Axial(0, +1),
|
||||
};
|
||||
|
||||
private static readonly List<Cubic> _cubicNeighbors = new List<Cubic>
|
||||
{
|
||||
new Cubic(+1, -1, 0), new Cubic(+1, 0, -1), new Cubic(0, +1, -1),
|
||||
new Cubic(-1, +1, 0), new Cubic(-1, 0, +1), new Cubic(0, -1, +1),
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
private static int NormalizeIndex(int index)
|
||||
{
|
||||
index = index % EdgesCount;
|
||||
if (index < 0)
|
||||
{
|
||||
index += EdgesCount;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
189
Src/HexagonalLib/src/HexagonalLib/HexagonalGridMesh.cs
Normal file
189
Src/HexagonalLib/src/HexagonalLib/HexagonalGridMesh.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HexagonalLib.Coordinates;
|
||||
|
||||
namespace HexagonalLib
|
||||
{
|
||||
public readonly partial struct HexagonalGrid
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculate count of vertices and indices needed for build mesh.
|
||||
/// </summary>
|
||||
/// <param name="hexesCount">Count on hexes in mesh</param>
|
||||
/// <param name="subdivide">Count of triangles splits for each hex</param>
|
||||
public (int VerticesCount, int IndicesCount) GetMeshData(int hexesCount, int subdivide)
|
||||
{
|
||||
// Calculate number of vertices we need to generate
|
||||
// numVertices = 1 + Σ {S, i = 1} (i * 6)
|
||||
var numVertices = 1;
|
||||
|
||||
// Calculate number of indices we need to generate
|
||||
// numIndices = Σ {S, i = 1} (36 * i - 18)
|
||||
var numIndices = 0;
|
||||
|
||||
for (int i = 1; i <= subdivide; i++)
|
||||
{
|
||||
numVertices += i * 6;
|
||||
numIndices += 36 * i - 18;
|
||||
}
|
||||
|
||||
return (numVertices * hexesCount, numIndices * hexesCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for list of hex. The generation algorithm is taken from the site:
|
||||
/// </summary>
|
||||
public void CreateMesh(IEnumerable<Offset> hexes, int subdivide, Action<int, (float X, float Y)> setVertex, Action<int, int> setIndex)
|
||||
{
|
||||
CreateMesh(hexes, subdivide, setVertex, setIndex, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for list of hex. The generation algorithm is taken from the site:
|
||||
/// </summary>
|
||||
public void CreateMesh(IEnumerable<Axial> hexes, int subdivide, Action<int, (float X, float Y)> setVertex, Action<int, int> setIndex)
|
||||
{
|
||||
CreateMesh(hexes, subdivide, setVertex, setIndex, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for list of hex. The generation algorithm is taken from the site:
|
||||
/// </summary>
|
||||
public void CreateMesh(IEnumerable<Cubic> hexes, int subdivide, Action<int, (float X, float Y)> setVertex, Action<int, int> setIndex)
|
||||
{
|
||||
CreateMesh(hexes, subdivide, setVertex, setIndex, ToPoint2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for list of hex. The generation algorithm is taken from the site:
|
||||
/// http://www.voidinspace.com/2014/07/project-twa-part-1-generating-a-hexagonal-tile-and-its-triangular-grid/
|
||||
/// </summary>
|
||||
private void CreateMesh<T>(IEnumerable<T> hexes, int subdivide, Action<int, (float X, float Y)> setVertex, Action<int, int> setIndex, Func<T, (float X, float Y)> toPoint)
|
||||
{
|
||||
var vertex = 0;
|
||||
var index = 0;
|
||||
|
||||
var data = GetMeshData(1, subdivide);
|
||||
|
||||
foreach (var hex in hexes)
|
||||
{
|
||||
var localVertex = vertex;
|
||||
var localIndex = index;
|
||||
var center = toPoint(hex);
|
||||
|
||||
void setVertexLocal(int i, (float X, float Y) currentVertex)
|
||||
{
|
||||
var (x, y) = currentVertex;
|
||||
var shifted = (x + center.X, y + center.Y);
|
||||
setVertex(localVertex + i, shifted);
|
||||
}
|
||||
|
||||
void setIndexLocal(int i, int currentIndex) => setIndex(localIndex + i, localVertex + currentIndex);
|
||||
|
||||
CreateMesh(subdivide, setVertexLocal, setIndexLocal);
|
||||
|
||||
vertex += data.VerticesCount;
|
||||
index += data.IndicesCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mesh for one hex. The generation algorithm is taken from the site:
|
||||
/// http://www.voidinspace.com/2014/07/project-twa-part-1-generating-a-hexagonal-tile-and-its-triangular-grid/
|
||||
/// </summary>
|
||||
public void CreateMesh(int subdivide, Action<int, (float X, float Y)> setVertex, Action<int, int> setIndex)
|
||||
{
|
||||
// We'll need those later to generate our x, z coordinates
|
||||
var radius = InscribedRadius / (float) Math.Cos(Math.PI / 6);
|
||||
var sin60 = (float) Math.Sin(Math.PI / 3);
|
||||
var invTan60 = 1.0f / (float) Math.Tan(Math.PI / 3.0f);
|
||||
var rdq = radius / subdivide;
|
||||
|
||||
var verticesIndex = 0;
|
||||
var indicesIndex = 0;
|
||||
|
||||
// We'll need those to calculate our indices
|
||||
var currentNumPoints = 0;
|
||||
var prevRowNumPoints = 0;
|
||||
|
||||
// [colMin, colMax]
|
||||
var npCol0 = 2 * subdivide + 1;
|
||||
var colMin = -subdivide;
|
||||
var colMax = subdivide;
|
||||
|
||||
//
|
||||
// Now let's generate the grid
|
||||
// First we'll iterate through the Columns, starting from the bottom (fA)
|
||||
for (var itC = colMin; itC <= colMax; itC++)
|
||||
{
|
||||
// Calculate z for this row
|
||||
// That's the same for each point in this column
|
||||
var x = sin60 * rdq * itC;
|
||||
|
||||
// Calculate how many points (z values) we need to generate on for this column
|
||||
var npColI = npCol0 - Math.Abs(itC);
|
||||
|
||||
// [rowMin, rowMax]
|
||||
var rowMin = -subdivide;
|
||||
if (itC < 0)
|
||||
{
|
||||
rowMin += Math.Abs(itC);
|
||||
}
|
||||
|
||||
var rowMax = rowMin + npColI - 1;
|
||||
|
||||
// We need this for the indices
|
||||
currentNumPoints += npColI;
|
||||
|
||||
// Iterate through the Rows (fB)
|
||||
for (var itR = rowMin; itR <= rowMax; itR++)
|
||||
{
|
||||
// Calculate z
|
||||
var z = invTan60 * x + rdq * itR;
|
||||
setVertex(verticesIndex, (x, z).Rotate(AngleToFirstNeighbor));
|
||||
|
||||
// Indices
|
||||
// From each point we'll try to create triangles left and right
|
||||
if (verticesIndex < (currentNumPoints - 1))
|
||||
{
|
||||
// Triangles left from this column
|
||||
if (itC >= colMin && itC < colMax)
|
||||
{
|
||||
// To get the point above
|
||||
var padLeft = 0;
|
||||
if (itC < 0)
|
||||
{
|
||||
padLeft = 1;
|
||||
}
|
||||
|
||||
setIndex(indicesIndex++, verticesIndex + npColI + padLeft);
|
||||
setIndex(indicesIndex++, verticesIndex + 1);
|
||||
setIndex(indicesIndex++, verticesIndex);
|
||||
}
|
||||
|
||||
// Triangles right from this column
|
||||
if (itC > colMin && itC <= colMax)
|
||||
{
|
||||
// To get point bellow
|
||||
var padRight = 0;
|
||||
if (itC > 0)
|
||||
{
|
||||
padRight = 1;
|
||||
}
|
||||
|
||||
setIndex(indicesIndex++, verticesIndex - prevRowNumPoints + padRight);
|
||||
setIndex(indicesIndex++, verticesIndex);
|
||||
setIndex(indicesIndex++, verticesIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Next vertex...
|
||||
verticesIndex++;
|
||||
}
|
||||
|
||||
// Store the previous row number of points, used to calculate the index buffer
|
||||
prevRowNumPoints = npColI;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Src/HexagonalLib/src/HexagonalLib/HexagonalGridType.cs
Normal file
28
Src/HexagonalLib/src/HexagonalLib/HexagonalGridType.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace HexagonalLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The typical layouts and orientations for hex grids
|
||||
/// </summary>
|
||||
public enum HexagonalGridType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Horizontal layout shoves odd rows right [odd-r]
|
||||
/// </summary>
|
||||
PointyOdd,
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal layout shoves even rows right [even-r]
|
||||
/// </summary>
|
||||
PointyEven,
|
||||
|
||||
/// <summary>
|
||||
/// Vertical layout shoves odd columns down [odd-q]
|
||||
/// </summary>
|
||||
FlatOdd,
|
||||
|
||||
/// <summary>
|
||||
/// Vertical layout shoves even columns down [even-q]
|
||||
/// </summary>
|
||||
FlatEven,
|
||||
}
|
||||
}
|
||||
21
Src/HexagonalLib/src/HexagonalLib/HexagonalLib.projitems
Normal file
21
Src/HexagonalLib/src/HexagonalLib/HexagonalLib.projitems
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
<HasSharedItems>true</HasSharedItems>
|
||||
<SharedGUID>1FDA753A-7694-41B7-8C00-AA39FD0780CA</SharedGUID>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Import_RootNamespace>HexagonalLib</Import_RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Coordinates\Axial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Coordinates\Cubic.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Coordinates\Offset.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HexagonalException.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HexagonalGrid.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HexagonalGridMesh.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HexagonalGridType.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HexagonalMath.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
18
Src/HexagonalLib/src/HexagonalLib/HexagonalLib.shproj
Normal file
18
Src/HexagonalLib/src/HexagonalLib/HexagonalLib.shproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{30905D5D-8D2E-4A25-ABEB-B4B4B0C440E6}</ProjectGuid>
|
||||
<AssemblyName>HexagonalLib</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
|
||||
<Import Project="HexagonalLib.projitems" Label="Shared" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
|
||||
</Project>
|
||||
47
Src/HexagonalLib/src/HexagonalLib/HexagonalMath.cs
Normal file
47
Src/HexagonalLib/src/HexagonalLib/HexagonalMath.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace HexagonalLib
|
||||
{
|
||||
public static partial class HexagonalMath
|
||||
{
|
||||
/// <summary>
|
||||
/// Rotate 2d vector around z Axis clockwise
|
||||
/// </summary>
|
||||
/// <param name="vector">Vector to rotate</param>
|
||||
/// <param name="degrees">Angle of rotation</param>
|
||||
public static (float X, float Y) Rotate(this in (float X, float Y) vector, float degrees)
|
||||
{
|
||||
var radians = Math.PI / 180.0 * degrees;
|
||||
var sin = Math.Sin(radians);
|
||||
var cos = Math.Cos(radians);
|
||||
var x = (float) Math.Round(cos * vector.X - sin * vector.Y, 6);
|
||||
var y = (float) Math.Round(sin * vector.X + cos * vector.Y, 6);
|
||||
return (-x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this vector with a magnitude of 1
|
||||
/// </summary>
|
||||
public static (float X, float Y) Normalize(this in (float X, float Y) vector)
|
||||
{
|
||||
var distance = Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y);
|
||||
return ((float) (vector.X / distance), (float) (vector.Y / distance));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two floating point values and returns true if they are similar.
|
||||
/// </summary>
|
||||
public static bool SimilarTo(this in float a, in float b)
|
||||
{
|
||||
return Math.Abs(b - a) < (double) Math.Max(1E-06f * Math.Max(Math.Abs(a), Math.Abs(b)), float.Epsilon * 8f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two vectors and returns true if they are similar.
|
||||
/// </summary>
|
||||
public static bool SimilarTo(this in (float X, float Y) a, in (float X, float Y) b)
|
||||
{
|
||||
return a.X.SimilarTo(b.X) && a.Y.SimilarTo(b.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
Src/HexagonalLib/src/nuget.config
Normal file
5
Src/HexagonalLib/src/nuget.config
Normal file
@@ -0,0 +1,5 @@
|
||||
<configuration>
|
||||
<config>
|
||||
<add key="repositoryPath" value=".packages" />
|
||||
</config>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user