I have been learning about graphics. A language of choice would probably be OpenGL/WebGL but this post would be in Java. Specifically, I would be using the java.awt package. Most board games (chess, checkers ) use some sort of grid based on some shape( squares, rectangles, hexagons are the most popular) for a playing surface.
Today, I will be doing a hexagon grid. Hexagons can be either in a flat or pointy configuration.
Rembering geometry (SOHCAHTOH)
, the number of angles in a polygon (n -2) * 180
and some properties of equilateral
triangles, we come up with this annotation:
There are five vertices(I start from 0, because I am too used to array notation
):
vert0=(x−r2,y−h2)
vert1=(x+r2,y−h2)
vert2=(x+r,y)
vert3=(x+r2,y+h2)
vert4=(x−r2,y+h2)
vert5=(x−r,y)
We also need to get measurements for drawing rows and columns:
Other vertices:
vert6=(x+2r,y)
vert7=(x+5r2,y−h2)
vert8=(x+7r2,y−h2)
vert9=(x+4r,y)
vert10=(x−r,y+h)
The java code is shown below:
/** | |
* @license: | |
* The MIT License (MIT) | |
* | |
* Copyright (c) 2016 David Okusanya | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
* | |
* | |
* @brief: Drawing a sequence of hexagons | |
* | |
*/ | |
import java.awt.*; | |
import java.awt.event.*; | |
import java.util.ArrayList; | |
class HexagonGrid extends Frame{ | |
private int margin; | |
HexagonGrid(int margin){ | |
super("HexagonGrid - Flat Orientation"); | |
this.margin = margin; | |
addWindowListener(new WindowAdapter(){ | |
public void windowClosing(WindowEvent e){ | |
System.exit(0); | |
} | |
}); | |
setSize(400, 400); | |
HexagonRect hexa = new HexagonRect(margin); | |
add("Center", hexa); | |
show(); | |
} | |
class HexagonRect extends Canvas{ | |
private int margin, mousePointX, mousePointY, height; | |
private int vertex0X, vertex0Y, temp_vertex0Y, | |
vertex1X, vertex1Y, temp_vertex1Y, | |
vertex2X, vertex2Y, temp_vertex2Y, | |
vertex3X, vertex3Y, temp_vertex3Y, | |
vertex4X, vertex4Y, temp_vertex4Y, | |
vertex5X, vertex5Y, temp_vertex5Y; | |
/** | |
* This is the rectangle that would contain the grid | |
*/ | |
HexagonRect(int margin){ | |
this.margin = margin; | |
addMouseListener(new MouseAdapter(){ | |
public void mouseClicked(MouseEvent mt){ | |
mousePointX = mt.getX(); | |
mousePointY = mt.getY(); | |
repaint(); // To facilitate a redrawing of the points | |
} | |
}); | |
} | |
/** | |
* Return the radius of the hexagon from the point of the mouse click | |
* @return radius An integer value | |
*/ | |
public int getRadius(){ | |
if (mousePointX <= margin){ | |
mousePointX += margin; | |
} | |
if (mousePointY <= margin){ | |
mousePointY += margin; | |
} | |
int radiusX = mousePointX - margin; | |
int radiusY = mousePointY - margin; | |
int radius = (int) Math.sqrt((double)(radiusX * radiusX + radiusY * radiusY)); | |
return radius; | |
} | |
/** | |
* Calculate all the vertices of the first pentagon in the grid | |
* @param radius An integer value | |
* @param height An integer value | |
*/ | |
public void computeVertices(int radius, int height){ | |
// Going clockwise | |
// Left Topmost vertex | |
vertex0X = (mousePointX - radius / 2); | |
vertex0Y = (mousePointY - height / 2); | |
// Right Topmost vertex | |
vertex1X = (mousePointX + radius / 2); | |
vertex1Y = (mousePointY - height / 2); | |
// Rightmost vertex | |
vertex2X = (mousePointX + radius); | |
vertex2Y = mousePointY; | |
// Right Bottom vertex | |
vertex3X = (mousePointX + radius / 2); | |
vertex3Y = (mousePointY + height / 2); | |
// Left Bottom vertex | |
vertex4X = (mousePointX - radius / 2); | |
vertex4Y = (mousePointY + height / 2); | |
// Leftmost vertex | |
vertex5X = (mousePointX - radius); | |
vertex5Y = mousePointY; | |
} | |
/** | |
* This resets the y-coordinates of the hexagon column | |
*/ | |
public void resetTempVertices(){ | |
temp_vertex5Y = vertex5Y; | |
temp_vertex0Y = vertex0Y; | |
temp_vertex4Y = vertex4Y; | |
temp_vertex1Y = vertex1Y; | |
temp_vertex3Y = vertex3Y; | |
temp_vertex2Y = vertex2Y; | |
} | |
/** | |
* This increments all the x-coordinates, to enable another columns to drawn alongside the already | |
* drawn column | |
* @param radius An integer value | |
*/ | |
public void incrementX(int radius){ | |
vertex5X += 3 * radius; | |
vertex0X += 3 * radius; | |
vertex4X += 3 * radius; | |
vertex1X += 3 * radius; | |
vertex3X += 3 * radius; | |
vertex2X += 3 * radius; | |
} | |
/** | |
* Drawing method | |
* @param g Graphics object | |
*/ | |
public void paint(Graphics g){ | |
int radius = getRadius(); | |
System.out.println("Radius = " + radius); | |
if (radius > 0){ | |
height = Math.round(((float)(Math.sqrt(3) *radius))); | |
System.out.println("Height = " + height); | |
int nVert = numOfVerticalHexagons(radius); | |
int nHort = numOfHorizontalHexagons(radius); | |
computeVertices(radius, height); | |
for (int j = 0; j < nHort; j++){ | |
resetTempVertices(); | |
for (int i = 0; i < nVert; i++){ | |
// Draw northwest line | |
g.drawLine(vertex0X, temp_vertex0Y, vertex5X, temp_vertex5Y); | |
// Draw southwest line | |
g.drawLine(vertex5X, temp_vertex5Y, vertex4X, temp_vertex4Y); | |
// Draw top line | |
g.drawLine(vertex0X, temp_vertex0Y, vertex1X, temp_vertex1Y); | |
// Draw northeast line | |
g.drawLine(vertex1X, temp_vertex1Y, vertex2X, temp_vertex2Y); | |
if (j != nHort - 1){ // remove the last drawn line | |
// Draw top of the next column | |
g.drawLine(vertex2X, temp_vertex2Y, vertex2X + radius, temp_vertex2Y); | |
} | |
// Draw southeast line | |
g.drawLine(vertex2X, temp_vertex2Y, vertex3X, temp_vertex3Y); | |
temp_vertex5Y += height; | |
temp_vertex0Y += height; | |
temp_vertex4Y += height; | |
temp_vertex1Y += height; | |
temp_vertex3Y += height; | |
temp_vertex2Y += height; | |
} | |
// Draw the last line(botton line) joining the bottom hexagons | |
g.drawLine(vertex0X, temp_vertex0Y, vertex1X, temp_vertex1Y); | |
incrementX(radius); | |
} | |
} | |
} | |
/** | |
* Returns the number of hexagons that can be fit in one column along the rectangle's height | |
* @param radius An integer value | |
* @return count An integer value | |
*/ | |
public int numOfVerticalHexagons(int radius){ | |
Dimension d = getSize(); | |
int maxHeight = d.height - 1; | |
return maxHeight / (2 * radius); | |
} | |
/** | |
* Returns the number of hexagons that can be fit in one row along the rectangle's width | |
* @param radius An integer value | |
* @return count An integer value | |
*/ | |
public int numOfHorizontalHexagons(int radius){ | |
// Two columns of hexagons make one unit | |
// Width of one hexagon = 2 x radius | |
// There is an overlap of (1/2) * radius between the two columns | |
Dimension d = getSize(); | |
int maxWidth = d.width - 1; | |
if (maxWidth % 7 == 0){ | |
return maxWidth / ((7 / 2) * radius); | |
} | |
else { | |
return (maxWidth - 1) / ((7 / 2) * radius); | |
} | |
} | |
} | |
public static void main(String[] args){ | |
// Usage : java HexagonGrid | |
new HexagonGrid(40); // Static value(change if desired) | |
} | |
} |
Result on execution:
Something I might do in the future:
- Rewrite in javafx
- Make it coloured
- Add a pointy example.
Thanks!!