Last active
December 16, 2019 04:36
-
-
Save Kittoes0124/69e7215f3f082cbb74b91ab55f6bcbb9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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