public void OperatorOverloadPlus() { Vec2D point = new Vec2D( 1f, 2f ) + new Vec2D( 3f, 4f ); Assert.That( point.x, Is.InRange<float>( 4f - EPSILON, 4f + EPSILON ) ); Assert.That( point.y, Is.InRange<float>( 6f - EPSILON, 6f + EPSILON ) ); }
public void ConstructorXZ() { Vec2D point = new Vec2D( 1f, 2f ); Assert.That( point.x, Is.EqualTo( 1f ) ); Assert.That( point.y, Is.EqualTo( 2f ) ); }
public void OperatorOverloadMinus() { Vec2D point = new Vec2D( 4f, 3f ) - new Vec2D( 1f, 2f ); Assert.That( point.x, Is.InRange<float>( 3f - EPSILON, 3f + EPSILON ) ); Assert.That( point.y, Is.InRange<float>( 1f - EPSILON, 1f + EPSILON ) ); }
public void ConstructorParameterless() { Vec2D point = new Vec2D(); Assert.That( point.x, Is.EqualTo( 0f ) ); Assert.That( point.y, Is.EqualTo( 0f ) ); }
public void Length() { Vec2D point = new Vec2D( 2, 2 ); float length = point.Length(); float expected = (float)( 2 * Math.Sqrt( 2 ) ); Assert.That( length, Is.InRange<float>( expected - EPSILON, expected + EPSILON ) ); }
public void Distance() { Vec2D point1 = new Vec2D( 1, 1 ); Vec2D point2 = new Vec2D( 3, 3 ); float length = point1.Distance( point2 ); float expected = (float)( 2 * Math.Sqrt( 2 ) ); Assert.That( length, Is.InRange<float>( expected - EPSILON, expected + EPSILON ) ); }
/// <summary> /// Returns a TriangleEnum representing the triangle (within the current slice, either /// top or bottom, that the point belongs to). /// </summary> /// <param name="withinSlice">A point on the x-z cartesian plane, relative to the top-left /// of the current slice.</param> /// <param name="parity">The "parity" of the slice, indicating wither the border seaparting /// the two triangles within the slice goes from top-left to bottom-right (ODD) or /// bottom-left to top-right (EVEN).</param> /// <returns>A TriangleEnum representing the triangle (within the current slice, either /// top or bottom, that the point belongs to).</returns> private TriangleEnum WhichTriangle(Vec2D withinSlice, ParityEnum parity) { float xFraction = withinSlice.x / Slice.x; float yBorder = 0f; if (parity == ParityEnum.Even) { // When parity is even, the border is bottom-left to top-right. yBorder = Slice.y * xFraction; } else { // When parity is odd, the border is top-left to bottom-right. yBorder = Slice.y * (1f - xFraction); } return((withinSlice.y < yBorder) ? TriangleEnum.Top : TriangleEnum.Bottom); }
PointToCubic(Vec2D point) { // This function exhibited one of the nastiest bugs I've ever come across in my career // thanks to a subtle order or bracketing error resulting in bad order of operations. // // The offending line of code: // float q = ( point.x * ( SQRT_3 - point.y ) * ONE_THIRD ) / HexRadius; // // evaluates to: // float q = ( point.x * ONE_THIRD * SQRT_3 - point.x * point.y * ONE_THIRD ) / HexRadius; // // Resulting is a weird distortion on the coordinate space because the x and y // coordinates of the input point were getting multiplied together in the 2nd term! // // The correct version of the code is now implemented below -- after 2 solid days of // meticulous and demoralizing debugging!!! float q = (point.x * ONE_THIRD * SQRT_3 - point.y * ONE_THIRD) / HexRadius; float r = (point.y * TWO_THIRDS) / HexRadius; return(new FloatAxial(q, r).ToFloatCubic().Round()); }
PointToDirectionInHex(Vec2D point) { AxialHexCoord axial = PointToCubic(point).ToAxial(); //AxialHexCoord axial = PointToAxial( point ); Vec2D center = AxialToPoint(axial); Vec2D topLeft = center - new Vec2D(0.5f * SQRT_3 * HexRadius, HexRadius); Vec2D fromTopLeft = point - topLeft; int hSlice = (int)Math.Floor(fromTopLeft.x / Slice.x); int vSlice = (int)Math.Floor(fromTopLeft.y / Slice.y); Vec2D withinSlice = new Vec2D( fromTopLeft.x % Slice.x, fromTopLeft.y % Slice.y ); ParityEnum parity = ParityEnum.Even; if (((hSlice & 1) + (vSlice & 1)) == 1) { // One of the two dimensions is odd, otherwise parity is even by default. parity = ParityEnum.Odd; } // // Debugging output associated with a horrible bug in PointToCubic() // //Console.WriteLine( "Point: x: " + point.x + ", y: " + point.y ); //Console.WriteLine( "Axial: q: " + axial.q + ", r: " + axial.r ); //Console.WriteLine( "Center: x: " + center.x + ", y: " + center.y ); //Console.WriteLine( "Top Left: x: " + topLeft.x + ", y: " + topLeft.y ); //Console.WriteLine( "From Top Left: x: " + fromTopLeft.x + ", y: " + fromTopLeft.y ); //Console.WriteLine( "Slice: x: " + Slice.x + ", y: " + Slice.y ); //Console.WriteLine( "Slice Index: x: " + hSlice + ", y: " + vSlice ); //Console.WriteLine( "Within Slice: x: " + withinSlice.x + ", y: " + withinSlice.y ); //Console.WriteLine( "Parity: " + parity ); // Compute and return the triangle by which the point is contained. bool isInvalid = false; DirectionEnum direction = DirectionEnum.E; TriangleEnum triangle = WhichTriangle(withinSlice, parity); if (hSlice == 0) { switch (vSlice) { case 0: if (triangle == TriangleEnum.Bottom) { direction = DirectionEnum.NW; } else { isInvalid = true; } break; case 1: direction = (triangle == TriangleEnum.Top) ? DirectionEnum.NW : DirectionEnum.W; break; case 2: direction = (triangle == TriangleEnum.Top) ? DirectionEnum.W : DirectionEnum.SW; break; case 3: if (triangle == TriangleEnum.Top) { direction = DirectionEnum.SW; } else { isInvalid = true; } break; default: isInvalid = true; break; } } else // hSlice == 1 { switch (vSlice) { case 0: if (triangle == TriangleEnum.Bottom) { direction = DirectionEnum.NE; } else { isInvalid = true; } break; case 1: direction = (triangle == TriangleEnum.Top) ? DirectionEnum.NE : DirectionEnum.E; break; case 2: direction = (triangle == TriangleEnum.Top) ? DirectionEnum.E : DirectionEnum.SE; break; case 3: if (triangle == TriangleEnum.Top) { direction = DirectionEnum.SE; } else { isInvalid = true; } break; default: isInvalid = true; break; } } // // Debugging output associated with a horrible bug in PointToCubic() // //Console.WriteLine( "Triangle: " + triangle ); //Console.WriteLine( "Direction: " + direction ); //Console.WriteLine( "" ); //Console.WriteLine( "" ); if (isInvalid) { throw new InvalidOperationException("This should never happen!"); } else { return(direction); } }
/// <summary> /// Returns the distance from this Vec2D to the given other. /// </summary> /// <param name="other">Any other Vec2D.</param> /// <returns>A float representing the distance from this Vec2D to the given other. /// </returns> public float Distance(Vec2D other) { return((other - this).Length()); }
/// <summary> /// Get the hex that contains the given point on the x-z cartesian plane. /// </summary> /// <param name="point">A point on the x-y cartesian plane.</param> /// <returns>A CubicHexCoord representation of the grid position of the hex that the point /// is contained by.</returns> public CubicHexCoord PointToCubic( Vec2D point ) { // This function exhibited one of the nastiest bugs I've ever come across in my career // thanks to a subtle order or bracketing error resulting in bad order of operations. // // The offending line of code: // float q = ( point.x * ( SQRT_3 - point.y ) * ONE_THIRD ) / HexRadius; // // evaluates to: // float q = ( point.x * ONE_THIRD * SQRT_3 - point.x * point.y * ONE_THIRD ) / HexRadius; // // Resulting is a weird distortion on the coordinate space because the x and y // coordinates of the input point were getting multiplied together in the 2nd term! // // The correct version of the code is now implemented below -- after 2 solid days of // meticulous and demoralizing debugging!!! float q = ( point.x * ONE_THIRD * SQRT_3 - point.y * ONE_THIRD ) / HexRadius; float r = ( point.y * TWO_THIRDS ) / HexRadius; return new FloatAxial( q, r ).ToFloatCubic().Round(); }
public void PointToDirectionInHex() { HexGrid grid = new HexGrid( 2f ); Vec2D hexPos = grid.AxialToPoint( new AxialHexCoord( 10, 10 ) ); float offset = 0.5f * grid.HexRadius; Vec2D[] points = new Vec2D[ 12 ] { new Vec2D( hexPos.x + offset, hexPos.y + 0f * offset - 0.01f ), // 0a new Vec2D( hexPos.x + offset, hexPos.y + 0f * offset + 0.01f ), // 0b new Vec2D( hexPos.x + offset, hexPos.y + 1f * offset - 0.01f ), // 1a new Vec2D( hexPos.x + offset, hexPos.y + 1f * offset + 0.01f ), // 1b new Vec2D( hexPos.x - offset, hexPos.y + 1f * offset + 0.01f ), // 2a new Vec2D( hexPos.x - offset, hexPos.y + 1f * offset - 0.01f ), // 2b new Vec2D( hexPos.x - offset, hexPos.y + 0f * offset + 0.01f ), // 3a new Vec2D( hexPos.x - offset, hexPos.y + 0f * offset - 0.01f ), // 3b new Vec2D( hexPos.x - offset, hexPos.y - 1f * offset + 0.01f ), // 4a new Vec2D( hexPos.x - offset, hexPos.y - 1f * offset - 0.01f ), // 4b new Vec2D( hexPos.x + offset, hexPos.y - 1f * offset - 0.01f ), // 5a new Vec2D( hexPos.x + offset, hexPos.y -1f * offset + 0.01f ) // 5b }; DirectionEnum[] results = new DirectionEnum[ 12 ]; for ( int i = 0; i < points.Length; i++ ) { results[ i ] = grid.PointToDirectionInHex( points[ i ] ); } Assert.That( results, Is.EquivalentTo( new DirectionEnum[ 12 ] { DirectionEnum.E, // 0a DirectionEnum.E, // 0b DirectionEnum.SE, // 1a DirectionEnum.SE, // 1b DirectionEnum.SW, // 2a DirectionEnum.SW, // 2b DirectionEnum.W, // 3a DirectionEnum.W, // 3b DirectionEnum.NW, // 4a DirectionEnum.NW, // 4b DirectionEnum.NE, // 5a DirectionEnum.NE // 5b } ) ); }
/// <summary> /// Create a new HexGrid given the radius of all hexes within the grid. /// </summary> /// <param name="hexRadius">The distance from the center of any given hex to one of its /// vertices.</param> public HexGrid( float hexRadius ) { __hexRadius = hexRadius; __slice = new Vec2D( 0.5f * SQRT_3 * HexRadius, 0.5f * HexRadius ); }
/// <summary> /// Returns a TriangleEnum representing the triangle (within the current slice, either /// top or bottom, that the point belongs to). /// </summary> /// <param name="withinSlice">A point on the x-z cartesian plane, relative to the top-left /// of the current slice.</param> /// <param name="parity">The "parity" of the slice, indicating wither the border seaparting /// the two triangles within the slice goes from top-left to bottom-right (ODD) or /// bottom-left to top-right (EVEN).</param> /// <returns>A TriangleEnum representing the triangle (within the current slice, either /// top or bottom, that the point belongs to).</returns> private TriangleEnum WhichTriangle( Vec2D withinSlice, ParityEnum parity ) { float xFraction = withinSlice.x / Slice.x; float yBorder = 0f; if ( parity == ParityEnum.Even ) { // When parity is even, the border is bottom-left to top-right. yBorder = Slice.y * xFraction; } else { // When parity is odd, the border is top-left to bottom-right. yBorder = Slice.y * ( 1f - xFraction ); } return ( withinSlice.y < yBorder ) ? TriangleEnum.Top : TriangleEnum.Bottom; }
/// <summary> /// Returns a DirectionEnum identifying the triangle by which the given point is contained. /// </summary> /// <param name="point">A point on the x-y cartesian plane.</param> /// <returns>A DirectionEnum representing the direction of the triangle that the point is /// contained by within the hex (relative to the center of the hex).</returns> public DirectionEnum PointToDirectionInHex( Vec2D point ) { AxialHexCoord axial = PointToCubic( point ).ToAxial(); //AxialHexCoord axial = PointToAxial( point ); Vec2D center = AxialToPoint( axial ); Vec2D topLeft = center - new Vec2D( 0.5f * SQRT_3 * HexRadius, HexRadius ); Vec2D fromTopLeft = point - topLeft; int hSlice = (int)Math.Floor( fromTopLeft.x / Slice.x ); int vSlice = (int)Math.Floor( fromTopLeft.y / Slice.y ); Vec2D withinSlice = new Vec2D( fromTopLeft.x % Slice.x, fromTopLeft.y % Slice.y ); ParityEnum parity = ParityEnum.Even; if ( ( ( hSlice & 1 ) + ( vSlice & 1 ) ) == 1 ) { // One of the two dimensions is odd, otherwise parity is even by default. parity = ParityEnum.Odd; } // // Debugging output associated with a horrible bug in PointToCubic() // //Console.WriteLine( "Point: x: " + point.x + ", y: " + point.y ); //Console.WriteLine( "Axial: q: " + axial.q + ", r: " + axial.r ); //Console.WriteLine( "Center: x: " + center.x + ", y: " + center.y ); //Console.WriteLine( "Top Left: x: " + topLeft.x + ", y: " + topLeft.y ); //Console.WriteLine( "From Top Left: x: " + fromTopLeft.x + ", y: " + fromTopLeft.y ); //Console.WriteLine( "Slice: x: " + Slice.x + ", y: " + Slice.y ); //Console.WriteLine( "Slice Index: x: " + hSlice + ", y: " + vSlice ); //Console.WriteLine( "Within Slice: x: " + withinSlice.x + ", y: " + withinSlice.y ); //Console.WriteLine( "Parity: " + parity ); // Compute and return the triangle by which the point is contained. bool isInvalid = false; DirectionEnum direction = DirectionEnum.E; TriangleEnum triangle = WhichTriangle( withinSlice, parity ); if ( hSlice == 0 ) { switch ( vSlice ) { case 0: if ( triangle == TriangleEnum.Bottom ) { direction = DirectionEnum.NW; } else { isInvalid = true; } break; case 1: direction = ( triangle == TriangleEnum.Top ) ? DirectionEnum.NW : DirectionEnum.W; break; case 2: direction = ( triangle == TriangleEnum.Top ) ? DirectionEnum.W : DirectionEnum.SW; break; case 3: if ( triangle == TriangleEnum.Top ) { direction = DirectionEnum.SW; } else { isInvalid = true; } break; default: isInvalid = true; break; } } else // hSlice == 1 { switch ( vSlice ) { case 0: if ( triangle == TriangleEnum.Bottom ) { direction = DirectionEnum.NE; } else { isInvalid = true; } break; case 1: direction = ( triangle == TriangleEnum.Top ) ? DirectionEnum.NE : DirectionEnum.E; break; case 2: direction = ( triangle == TriangleEnum.Top ) ? DirectionEnum.E : DirectionEnum.SE; break; case 3: if ( triangle == TriangleEnum.Top ) { direction = DirectionEnum.SE; } else { isInvalid = true; } break; default: isInvalid = true; break; } } // // Debugging output associated with a horrible bug in PointToCubic() // //Console.WriteLine( "Triangle: " + triangle ); //Console.WriteLine( "Direction: " + direction ); //Console.WriteLine( "" ); //Console.WriteLine( "" ); if ( isInvalid ) { throw new InvalidOperationException( "This should never happen!" ); } else { return direction; } }
HexGrid(float hexRadius) { __hexRadius = hexRadius; __slice = new Vec2D(0.5f * SQRT_3 * HexRadius, 0.5f * HexRadius); }
/// <summary> /// Returns the distance from this Vec2D to the given other. /// </summary> /// <param name="other">Any other Vec2D.</param> /// <returns>A float representing the distance from this Vec2D to the given other. /// </returns> public float Distance( Vec2D other ) { return ( other - this ).Length(); }