This example shows how to use a triangular grid. (In case you need to build a Tholian web or something.) Row numbers are defined in the obvious way with the first row having number 0.

Column numbers are a little stranger. The first *complete* triangle has index 0 and the indexes for the following triangles increase by 0.5. That way triangles that have whole number columns have the same orientation as the first complete triangle on the row ad the triangles with fractional indexes are upside down.

When you move the mouse over the grid, the program changes its title bar to indicate the row and column below the mouse. When you click on a triangle, the program displays that triangle in blue.

To use a triangular grid, the program needs to be able to do two things:

- Map a row and column to the points that make up the triangle
- Map a point (x, y) to a row and column

# Row/Column → Triangle Points

The following `TriangleToPoints` method maps a row and column to a triangle’s points. Given the triangle height and a row and column, it returns an array containing three points that you can use to draw the triangle.

// Return the points that define the indicated triangle. private PointF[] TriangleToPoints(float height, float row, float col) { float width = TriangleWidth(height); float y = row * height; float x = (col + 0.5f) * width; // See if this triangle should be drawn // right-side up or upside down. bool whole_col = (Math.Abs(col - (int)col) < 0.1); bool rightside_up; if ((int)row % 2 == 0) { // Even row. rightside_up = whole_col; } else { // Odd row. rightside_up = !whole_col; } // Draw the triangle. if (rightside_up) return new PointF[] { new PointF(x, y), new PointF(x + width / 2, y + height), new PointF(x - width / 2, y + height), }; else return new PointF[] { new PointF(x, y + height), new PointF(x + width / 2, y), new PointF(x - width / 2, y), }; }

The code first calls the following `TriangleWidth` method to get the triangle's width. This method simply uses trigonometry to calculate the width of an equilateral triangle.

// Return the width of a triangle. private float TriangleWidth(float height) { return (float)(2 * height / Math.Sqrt(3)); }

Next the `TriangleToPoints` method calculates the triangle's top Y coordinate. This is simply the height of the triangles times the row number.

The code then sets `x` equal to the X coordinate for the point at the top or bottom of the triangle.

Now things get a little weirder. The code needs to figure out if it should draw the triangle right-side up or upside down.

The code first sets variable `whole_col` to true if the column is a whole number (like 3) and false if it has a fractional part (like 1.5).

It then sets variable `rightside_up` to indicate whether it should draw the triangle right-side up. If the row number is even, then `rightside_up` is true if the column number is a whole number. If the row number is odd, then `rightside_up` is true if the column number is not a whole number.

Now knowing the X and Y coordinates of the triangle's top/bottom vertex and whether the triangle should be right-side up, the code can build and return the array holding the triangle's points.

The following `Paint` event handler uses the `TriangleToPoints` method to draw the grid.

// Selected triangles. private ListTriangles = new List (); // Redraw the grid. private void picGrid_Paint(object sender, PaintEventArgs e) { e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Draw the selected triangles. foreach (PointF point in Triangles) { e.Graphics.FillPolygon(Brushes.LightBlue, TriangleToPoints(TriangleHeight, point.X, point.Y)); } // Draw the grid. DrawTriangularGrid(e.Graphics, Pens.Black, 0, picGrid.ClientSize.Width, 0, picGrid.ClientSize.Height, TriangleHeight); }

The `Triangles` list holds `Point` objects that represent selected triangles. The points' X and Y coordinates give the selected triangles' rows and columns. I'll explain how triangles are selected later.

The `Paint` event handler loops through the selected triangles, calls `TriangleToPoints` to get their points, and fills them. It then calls the following `DrawTriangularGrid` method to draw the grid.

// Draw a triangular grid for the indicated area. private void DrawTriangularGrid(Graphics gr, Pen pen, float xmin, float xmax, float ymin, float ymax, float height) { float width = TriangleWidth(height); int row = 0; for (float y = 0; y <= ymax + width / 2; y += height) { float x = 0; if (row % 2 == 0) x = width / 2; PointF[] points = { new PointF(x, y), new PointF(x + width / 2, y + height), new PointF(x - width / 2, y + height), }; for (; x <= xmax; x += width) { gr.DrawPolygon(pen, points); points[0].X += width; points[1].X += width; points[2].X += width; } row++; } }

This method first gets the triangles' width. It then loops over Y coordinate values until the variable `y` drops off the bottom of the drawing area.

For each `y` value, the code sets `x` equal to the X coordinate for the first right-side up triangle in the row. It then creates an array holding points to represent the right-side up triangle.

The code then enters a loop that runs until `x` exceeds the width of the drawing area. For each trip through the loop, the code draws the triangle and then adds the triangle width to each point's X coordinate to move the triangle one position to the right.

# Point → Row/Column

The following `PointToTriangle` method maps a point to a triangle's row and column.

// Return the row and column of the triangle at this point. private void PointToTriangle(float x, float y, float height, out float row, out float col) { float width = TriangleWidth(height); row = (int)(y / height); col = (int)(x / width); float dy = (row + 1) * height - y; float dx = x - col * width; if (row % 2 == 1) dy = height - dy; if (dy > 1) { if (dx < width / 2) { // Left half of triangle. float ratio = dx / dy; if (ratio < 1f / Math.Sqrt(3)) col -= 0.5f; } else { // Right half of triangle. float ratio = (width - dx) / dy; if (ratio < 1f / Math.Sqrt(3)) col += 0.5f; } } }

First the method divides the Y and X coordinates by the triangle height and width respectively. That gives a row and column assuming a rectangular grid. The red dashed rectangle shown in Figure 1 indicates the rectangular grid position for the point.

Now the method needs to figure out if the point lies outside of the triangle that occupies most of the rectangular box. To do that it first calculates `dx` and `dy`, the distances from the point to the upper left corner of the rectangular box.

Whether the point could lie above or below the edges of the triangle depends on whether the triangle is right-side up or upside down. The code takes that into account by subtracting `dy` from the triangle height for odd numbered rows.

Depending on whether the point lies in the left or right half of the rectangular box, the code determines whether the point lies above the left or right edge of the triangle (adjusted for upside down triangles) and updates the column if necessary.

The program uses the `PointToTriangle` method in two places: the `MouseMove` and `MouseClick` event handlers.

// Display the row and column under the mouse. private void picGrid_MouseMove(object sender, MouseEventArgs e) { float row, col; PointToTriangle(e.X, e.Y, TriangleHeight, out row, out col); this.Text = "(" + row + ", " + col + ")"; } // Add the clicked triangle to the Triangles list. private void picGrid_MouseClick(object sender, MouseEventArgs e) { float row, col; PointToTriangle(e.X, e.Y, TriangleHeight, out row, out col); Triangles.Add(new PointF(row, col)); picGrid.Refresh(); }

The `MouseMove` event handler calls `PointToTriangle` and displays the point's row and column in the form's title bar.

The `MouseClick` event handler also calls `PointToTriangle`. It adds the triangle's row and column to the `Triangles` list and then refreshes the `PictureBox` to redraw the newly selected triangle in blue.

Pingback: Draw a hexagonal grid in C# - C# HelperC# Helper

if i have my own x and y value how should i use it in this code

Do you mean you have a point (x, y) and want to know the row and column numbers? Use the PointToTriangle method as used in the example’s picGrid_MouseClick event handler. Download the example to see the details.

thank you

I think I may try to use this in my drawing program and implement a snap to points option.

I got started on doing my grid! Although I’m just drawing lines based on a square grid with aspect ratio.