This elegant algorithm is from James McNeill. I cannot say much to improve on his explanation, so I will only note that a few details specific to this implementation have been filled in.

From the Grid Coordinates section of HexgridPanel.cs:

    /// Mathemagically (left as exercise for the reader) our 'picking' matrices are 
    /// these, assuming: 
    ///  - origin at upper-left corner of hex (0,0);
    ///  - 'straight' hex-axis vertically down; and
    ///  - 'oblique'  hex-axis up-and-to-right (at 120 degrees from 'straight').
    private Matrix matrixX { 
      get { return new Matrix((3.0F/2.0F)/GridSize.Width,  (3.0F/2.0F)/GridSize.Width,
                                    1.0F/GridSize.Height,       -1.0F/GridSize.Height,  -0.5F,-0.5F); } 
    private Matrix matrixY { 
      get { return new Matrix(       0.0F,                 (3.0F/2.0F)/GridSize.Width,
                                   2.0F/GridSize.Height,         1.0F/GridSize.Height,  -0.5F,-0.5F); } 

    /// <summary>Canonical coordinates for a selected hex for a given AutoScroll position.</summary>
    /// <param name="point">Screen point specifying hex to be identified.</param>
    /// <param name="autoScroll">AutoScrollPosition for game-display Panel.</param>
    /// <returns>Canonical coordinates for a hex specified by a screen point.</returns>
    /// <see cref="HexGridAlgorithm.mht"/>
    protected ICoordsCanon GetHexCoords(Point point, Size autoScroll) {
      if( Host == null ) return Coords.EmptyCanon;

      autoScroll = TransposeSize(autoScroll);

      /// Adjust for origin not as assumed by GetCoordinate().
      var grid    = new Size((int)(GridSize.Width*2F/3F), (int)GridSize.Height);
      var margin  = new Size((int)(MapMargin.Width *MapScale), 

      point      -= autoScroll + margin + grid;
      return Coords.NewCanonCoords( GetCoordinate(matrixX, point), 
                                    GetCoordinate(matrixY, point) );

    Size TransposeSize(Size size) { return IsTransposed ? new Size (size.Height, size.Width)
                                                        : size; }

    /// <summary>Calculates a (canonical X or Y) grid-coordinate for a point, from the supplied 'picking' matrix.</summary>
    /// <param name="matrix">The 'picking' matrix</param>
    /// <param name="point">The screen point identifying the hex to be 'picked'.</param>
    /// <returns>A (canonical X or Y) grid coordinate of the 'picked' hex.</returns>
	  private static int GetCoordinate (Matrix matrix, Point point){
      var pts = new Point[] {point};
		  return (int) Math.Floor( (pts[0].X + pts[0].Y + 2F) / 3F );

Last edited Feb 23, 2013 at 9:30 PM by pgeerkens, version 5