Skip to content

Instantly share code, notes, and snippets.

@Kittoes0124
Last active December 16, 2019 04:36
Show Gist options
  • Save Kittoes0124/69e7215f3f082cbb74b91ab55f6bcbb9 to your computer and use it in GitHub Desktop.
Save Kittoes0124/69e7215f3f082cbb74b91ab55f6bcbb9 to your computer and use it in GitHub Desktop.
@page "/HexCoordinates"
<BECanvas Width="1200" Height="1200" @ref="m_canvasReference"></BECanvas>
@code {
/// <summary>
///
/// </summary>
/// <remarks>
/// https://www.iquilezles.org/www/index.htm
/// https://www.redblobgames.com/grids/hexagons
/// https://www.shadertoy.com/view/Xljczw
/// </remarks>
public readonly struct HexCoordinate {
public static IEnumerable<HexCoordinate> GenerateTileset(int offset, int count, Func<HexCoordinate, bool> predicate) {
var limit = (offset + count);
for (var hashCode = offset; (hashCode < limit); ++hashCode) {
var hexCoordinate = HexCoordinate.New(hashCode);
if (predicate(hexCoordinate)) {
yield return hexCoordinate;
}
}
}
public static HexCoordinate New(int hashCode) {
var (x, y) = BitwiseHelpers.ElegantUnpair(hashCode);
return HexCoordinate.New(x, y);
}
public static HexCoordinate New(short x, short y) => new HexCoordinate(x, y);
public static bool operator ==(HexCoordinate left, HexCoordinate right) => ((left.X == right.X) && (left.Y == right.Y));
public static bool operator !=(HexCoordinate left, HexCoordinate right) => ((left.X != right.X) && (left.Y != right.Y));
public static HexCoordinate operator +(HexCoordinate left, HexCoordinate right) => New(((short)(left.X + right.X)), ((short)(left.Y + right.Y)));
public static HexCoordinate operator -(HexCoordinate left, HexCoordinate right) => New(((short)(left.X - right.X)), ((short)(left.Y - right.Y)));
public static HexCoordinate operator *(HexCoordinate left, HexCoordinate right) => New(((short)(left.X * right.X)), ((short)(left.Y * right.Y)));
public short X { get; }
public short Y { get; }
public short Z { get; }
private HexCoordinate(short x, short y) {
X = x;
Y = y;
Z = ((short)((-x) - y));
}
private long GetLength() => ((BitwiseHelpers.Absolute(X) + BitwiseHelpers.Absolute(Y) + BitwiseHelpers.Absolute(Z)) >> 1);
public bool Equals(HexCoordinate other) => (this == other);
public override bool Equals(object other) => ((other is null) || ((other is HexCoordinate hexCoordinate) && Equals(hexCoordinate)));
public HexCoordinate GetDiagonalNeighborNorth() => New(((short)(X + 1)), ((short)(Y + 1)));
public HexCoordinate GetDiagonalNeighborNorthEast() => New(((short)(X + 2)), ((short)(Y - 1)));
public HexCoordinate GetDiagonalNeighborNorthWest() => New(((short)(X - 1)), ((short)(Y + 2)));
public HexCoordinate GetDiagonalNeighborSouthEast() => New(((short)(X + 1)), ((short)(Y - 2)));
public HexCoordinate GetDiagonalNeighborSouthWest() => New(((short)(X - 2)), ((short)(Y + 1)));
public HexCoordinate GetDiagonalNeighborSouth() => New(((short)(X - 1)), ((short)(Y - 1)));
public int GetDistance(HexCoordinate other) => ((int)(this - other).GetLength());
public override int GetHashCode() => BitwiseHelpers.ElegantPair(X, Y);
public HexCoordinate GetImmediateNeighborEast() => New(((short)(X + 1)), ((short)(Y - 1)));
public HexCoordinate GetImmediateNeighborNorthEast() => New(((short)(X + 1)), Y);
public HexCoordinate GetImmediateNeighborNorthWest() => New(X, ((short)(Y + 1)));
public HexCoordinate GetImmediateNeighborSouthEast() => New(X, ((short)(Y - 1)));
public HexCoordinate GetImmediateNeighborSouthWest() => New(((short)(X - 1)), Y);
public HexCoordinate GetImmediateNeighborWest() => New(((short)(X - 1)), ((short)(Y + 1)));
public (double x, double y) GetPixel(double size) {
const double SquareRootOfThree = 1.7320508075688772d;
const double SquareRootOfThreeHalved = (SquareRootOfThree * 0.5d);
var x = X;
var y = Z;
return (
x: (size * (1.5d * x)),
y: (size * ((SquareRootOfThreeHalved * x) + (SquareRootOfThree * y)))
);
}
public HexCoordinate RotateLeft() => New(((short)(-Y)), ((short)(-Z)));
public HexCoordinate RotateRight() => New(((short)(-Z)), ((short)(-X)));
public override string ToString() => $"({X},{Y},{Z})";
}
private static (double x, double y) GetVertex(double x, double y, double size, uint offset) {
const double OneDegree = (Math.PI / 180d);
var reducedOffset = (offset % 6);
var angleDegrees = (60U * reducedOffset);
var angleRadians = (OneDegree * angleDegrees);
return (
x: (x + (size * Math.Cos(angleRadians))),
y: (y + (size * Math.Sin(angleRadians)))
);
}
private static async Task<Canvas2DContext> RenderTileset2D(BECanvasComponent canvasComponent, int numberOfTiles, double sizeOfHexagons, Func<HexCoordinate, bool> predicate) {
var context = await canvasComponent.CreateCanvas2DAsync();
await context.SetFillStyleAsync("green");
await context.SetFontAsync("12px Lucida Console");
await context.FillRectAsync(10d, 100d, 800d, 800d);
foreach (var tile in HexCoordinate.GenerateTileset(0, numberOfTiles, predicate)) {
var pixel = tile.GetPixel(sizeOfHexagons);
var textValue = tile.ToString();
var textMeasure = await context.MeasureTextAsync(textValue);
var textWidth = textMeasure.Width;
var xPosition = (400d + pixel.x);
var yPosition = (500d + pixel.y);
var vertex = GetVertex(xPosition, yPosition, sizeOfHexagons, 0U);
await context.SetStrokeStyleAsync("white");
await context.StrokeTextAsync(textValue, (xPosition - (textWidth * 0.5d)), yPosition);
await context.SetStrokeStyleAsync("black");
await context.BeginPathAsync();
for (var j = 0U; (j < 6U); ++j) {
vertex = GetVertex(xPosition, yPosition, sizeOfHexagons, j);
await context.LineToAsync(vertex.x, vertex.y);
}
await context.ClosePathAsync();
await context.StrokeAsync();
}
return context;
}
private static async Task<WebGLContext> RenderTileset3D(BECanvasComponent canvasComponent, int numberOfTiles, double sizeOfHexagons, Func<HexCoordinate, bool> predicate) {
var context = await canvasComponent.CreateWebGLAsync();
await context.ClearColorAsync(0, 0, 0, 1);
await context.ClearAsync(BufferBits.COLOR_BUFFER_BIT);
var iResolution = new Vector2(1200f, 1200f);
var fragCoord = new Vector2(600f, 600f);
var uv = (2f * fragCoord - iResolution) / iResolution.Y;
using (var fragmentShaderReader = new StreamReader(new MemoryStream(Properties.Resources.hexFragmentShader), System.Text.Encoding.UTF8))
using (var vertexShaderReader = new StreamReader(new MemoryStream(Properties.Resources.hexVertexShader), System.Text.Encoding.UTF8)) {
var fragmentShader = await context.CreateShaderAsync(ShaderType.FRAGMENT_SHADER);
var vertexShader = await context.CreateShaderAsync(ShaderType.VERTEX_SHADER);
await context.ShaderSourceAsync(fragmentShader, fragmentShaderReader.ReadToEnd());
await context.CompileShaderAsync(fragmentShader);
await context.ShaderSourceAsync(vertexShader, vertexShaderReader.ReadToEnd());
await context.CompileShaderAsync(vertexShader);
var isFragmentShaderCompiled = await context.GetShaderParameterAsync<bool>(fragmentShader, ShaderParameter.COMPILE_STATUS);
var isVertexShaderCompiled = await context.GetShaderParameterAsync<bool>(vertexShader, ShaderParameter.COMPILE_STATUS);
if (isFragmentShaderCompiled && isVertexShaderCompiled) {
var program = await context.CreateProgramAsync();
await context.AttachShaderAsync(program, fragmentShader);
await context.AttachShaderAsync(program, vertexShader);
await context.LinkProgramAsync(program);
await context.UseProgramAsync(program);
var buffer = await context.CreateBufferAsync();
var position = ((uint)(await context.GetAttribLocationAsync(program, "position")));
var vertices = new[] { -1f, -1f, -1f, 1f, 1f, -1f, 1f, 1f, -1f, 1f, 1f, -1f };
await context.BindBufferAsync(BufferType.ARRAY_BUFFER, buffer);
await context.BufferDataAsync(BufferType.ARRAY_BUFFER, vertices, BufferUsageHint.STATIC_DRAW);
await context.EnableVertexAttribArrayAsync(position);
await context.VertexAttribPointerAsync(
index: position,
size: 2,
type: DataType.FLOAT,
normalized: false,
stride: 0,
offset: 0L
);
await context.DrawArraysAsync(Primitive.TRIANGLES, first: 0, count: 6);
}
else {
//throw new Exception(message: "error compiling the fragment or vertex shaders; call GetShaderInfoLog for more info");
Console.WriteLine(await context.GetShaderInfoLogAsync(fragmentShader));
}
}
return context;
}
private RenderingContext m_canvasContext;
private BECanvasComponent m_canvasReference;
protected override async Task OnAfterRenderAsync(bool firstRender) {
m_canvasContext = await RenderTileset3D(this.m_canvasReference, 49, 42d, (value) => BitwiseHelpers.IsPerfectSquare((ulong)BitwiseHelpers.Absolute(value.RotateRight().GetHashCode())));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment