用坐标系生成六边形网格的算法

我试图将19行代码汇总到一个for循环中,但我感觉有点难过。 我问的原因是因为我希望网格是其他尺寸而不是5。

Main::drawHexGridAdvanced() ,我试图推断出每一行之间的相似性,而不是Main::drawHexGridBasic() ,其中我是硬编码值。

我不确定如何确定每行中每列的x的开始,因为n == 5的模式是0, -1 -2 -2 -2之后,每个连续的列只是递增,除非循环到达中途点……

信息和理解

 `n` must be odd n | columns-per row sequence --+------------------------- 3 | 2 3 2 5 | 3 4 5 4 3 7 | 4 5 6 7 6 5 4 9 | 5 6 7 8 9 8 7 6 5 
 int[] columns(int n) { int[] columns = new int[n]; int h = (int) java.lang.Math.floor(n / 2); for (int i = 0; i < n; i++) { columns[i] = n - java.lang.Math.abs(i - h); } return columns; } // Prints [5, 6, 7, 8, 9, 8, 7, 6, 5] System.out.println(java.util.Arrays.toString(columns(n))); 

Python看起来更优雅:

 def hex(n): for x in [(n-abs(x-int(n/2))) for x in range(n)]: for y in range(nx): print(' '), for y in range(x): print(' * '), print('') hex(5) # * * * # * * * * # * * * * * # * * * * # * * * 

这是我的预期输出:

在此处输入图像描述

Main.java

 package Foo.Bar.Hexagon; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Stroke; import javax.swing.JFrame; import javax.swing.JPanel; public class Main extends JPanel { private static final long serialVersionUID = 1L; private final int WIDTH = 1200; private final int HEIGHT = 800; private final int W2 = WIDTH / 2; private final int H2 = HEIGHT / 2; private Font font = new Font("Arial", Font.BOLD, 24); FontMetrics metrics; public Main() { setPreferredSize(new Dimension(WIDTH, HEIGHT)); } @Override public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER)); g2d.setFont(font); metrics = g.getFontMetrics(); drawCircle(g2d, W2, H2, 660, true, true, 0x4488FF, 0); drawHexGridAdvanced(g2d, 5, 60); } private void drawHexGridAdvanced(Graphics g, int n, int r) { double ang30 = Math.toRadians(30); double xOff = Math.cos(ang30) * r; double yOff = Math.sin(ang30) * r; int h = n / 2; int cols = 0; int row = 0; int col = 0; cols = 3; row = 0; col = 0; drawHex(g, +0, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); row = 0; col = 1; drawHex(g, +1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); row = 0; col = 2; drawHex(g, +2, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); cols = 4; row = 1; col = 0; drawHex(g, -1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); row = 1; col = 1; drawHex(g, +0, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); row = 1; col = 2; drawHex(g, +1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); row = 1; col = 3; drawHex(g, +2, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); cols = 5; row = 2; col = 0; drawHex(g, -2, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 2; col = 1; drawHex(g, -1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 2; col = 2; drawHex(g, +0, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 2; col = 3; drawHex(g, +1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 2; col = 4; drawHex(g, +2, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); cols = 4; row = 3; col = 0; drawHex(g, -2, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 3; col = 1; drawHex(g, -1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 3; col = 2; drawHex(g, +0, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 3; col = 3; drawHex(g, +1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); cols = 3; row = 4; col = 0; drawHex(g, -2, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 4; col = 1; drawHex(g, -1, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); row = 4; col = 2; drawHex(g, +0, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); } private void drawHexGridBasic(Graphics g, int n, int r) { double ang30 = Math.toRadians(30); double xOff = Math.cos(ang30) * r; double yOff = Math.sin(ang30) * r; int h = n / 2; drawHex(g, +0, -2, W2 - (int) (xOff * 2), H2 - (int) (yOff * 6), r); drawHex(g, +1, -2, W2 - (int) (xOff * 0), H2 - (int) (yOff * 6), r); drawHex(g, +2, -2, W2 + (int) (xOff * 2), H2 - (int) (yOff * 6), r); drawHex(g, -1, -1, W2 - (int) (xOff * 3), H2 - (int) (yOff * 3), r); drawHex(g, +0, -1, W2 - (int) (xOff * 1), H2 - (int) (yOff * 3), r); drawHex(g, +1, -1, W2 + (int) (xOff * 1), H2 - (int) (yOff * 3), r); drawHex(g, +2, -1, W2 + (int) (xOff * 3), H2 - (int) (yOff * 3), r); drawHex(g, -2, +0, W2 - (int) (xOff * 4), H2 - (int) (yOff * 0), r); drawHex(g, -1, +0, W2 - (int) (xOff * 2), H2 - (int) (yOff * 0), r); drawHex(g, +0, +0, W2 - (int) (xOff * 0), H2 - (int) (yOff * 0), r); drawHex(g, +1, +0, W2 + (int) (xOff * 2), H2 - (int) (yOff * 0), r); drawHex(g, +2, +0, W2 + (int) (xOff * 4), H2 - (int) (yOff * 0), r); drawHex(g, -2, +1, W2 - (int) (xOff * 3), H2 + (int) (yOff * 3), r); drawHex(g, -1, +1, W2 - (int) (xOff * 1), H2 + (int) (yOff * 3), r); drawHex(g, +0, +1, W2 + (int) (xOff * 1), H2 + (int) (yOff * 3), r); drawHex(g, +1, +1, W2 + (int) (xOff * 3), H2 + (int) (yOff * 3), r); drawHex(g, -2, +2, W2 - (int) (xOff * 2), H2 + (int) (yOff * 6), r); drawHex(g, -1, +2, W2 - (int) (xOff * 0), H2 + (int) (yOff * 6), r); drawHex(g, +0, +2, W2 + (int) (xOff * 2), H2 + (int) (yOff * 6), r); } private void drawHex(Graphics g, int posX, int posY, int x, int y, int r) { Hexagon hex = new Hexagon(x, y, r); String text = String.format("%s : %s", coord(posX), coord(posY)); int w = metrics.stringWidth(text); int h = metrics.getHeight(); g.setColor(new Color(0x008844)); g.fillPolygon(hex); g.setColor(new Color(0xFFDD88)); g.drawPolygon(hex); g.setColor(new Color(0xFFFFFF)); g.drawString(text, x - w/2, y + h/2); } private String coord(int value) { return (value > 0 ? "+" : "") + Integer.toString(value); } public void drawCircle(Graphics2D g, int x, int y, int diameter, boolean centered, boolean filled, int colorValue, int lineThickness) { drawOval(g, x, y, diameter, diameter, centered, filled, colorValue, lineThickness); } public void drawOval(Graphics2D g, int x, int y, int width, int height, boolean centered, boolean filled, int colorValue, int lineThickness) { // Store before changing. Stroke tmpS = g.getStroke(); Color tmpC = g.getColor(); g.setColor(new Color(colorValue)); g.setStroke(new BasicStroke(lineThickness, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); int x2 = centered ? x - (width / 2) : x; int y2 = centered ? y - (height / 2) : y; if (filled) g.fillOval(x2, y2, width, height); else g.drawOval(x2, y2, width, height); // Set values to previous when done. g.setColor(tmpC); g.setStroke(tmpS); } public static void main(String[] args) { JFrame f = new JFrame(); Main p = new Main(); f.setContentPane(p); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } } 

Haxagon.java

 package Foo.Bar.Hexagon; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Stroke; public class Hexagon extends Polygon { private static final long serialVersionUID = 1L; public static final int SIDES = 6; private Point[] points = new Point[SIDES]; private Point center = new Point(0, 0); private int radius; private int rotation = 90; public Hexagon(Point center, int radius) { npoints = SIDES; xpoints = new int[SIDES]; ypoints = new int[SIDES]; this.center = center; this.radius = radius; updatePoints(); } public Hexagon(int x, int y, int radius) { this(new Point(x, y), radius); } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; updatePoints(); } public int getRotation() { return rotation; } public void setRotation(int rotation) { this.rotation = rotation; updatePoints(); } public void setCenter(Point center) { this.center = center; updatePoints(); } public void setCenter(int x, int y) { setCenter(new Point(x, y)); } private double findAngle(double fraction) { return fraction * Math.PI * 2 + Math.toRadians((rotation + 180) % 360); } private Point findPoint(double angle) { int x = (int) (center.x + Math.cos(angle) * radius); int y = (int) (center.y + Math.sin(angle) * radius); return new Point(x, y); } protected void updatePoints() { for (int p = 0; p < SIDES; p++) { double angle = findAngle((double) p / SIDES); Point point = findPoint(angle); xpoints[p] = point.x; ypoints[p] = point.y; points[p] = point; System.out.printf("%d. (%d, %d)\n", p, point.x, point.y); } } public void drawPolygon(Graphics2D g, int x, int y, int lineThickness, int colorValue, boolean filled) { // Store before changing. Stroke tmpS = g.getStroke(); Color tmpC = g.getColor(); g.setColor(new Color(colorValue)); g.setStroke(new BasicStroke(lineThickness, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER)); if (filled) g.fillPolygon(xpoints, ypoints, npoints); else g.drawPolygon(xpoints, ypoints, npoints); // Set values to previous when done. g.setColor(tmpC); g.setStroke(tmpS); } } 

我已经弄明白了,感谢Tanmay的反馈。

我注意到n - cols的y偏移是不正确的,而应该是row - half而不是。

下面是我可以获得的最全面和最紧凑的代码。 虽然最好为大小输入一个奇整数,但您可以输入一个正值。 我还在偏移处添加了填充。

这条if条件仍然让我烦恼: int xLbl = row < half ? col - row : col - half; int xLbl = row < half ? col - row : col - half;

 private void drawHexGridLoop(Graphics g, Point origin, int size, int radius, int padding) { double ang30 = Math.toRadians(30); double xOff = Math.cos(ang30) * (radius + padding); double yOff = Math.sin(ang30) * (radius + padding); int half = size / 2; for (int row = 0; row < size; row++) { int cols = size - java.lang.Math.abs(row - half); for (int col = 0; col < cols; col++) { int xLbl = row < half ? col - row : col - half; int yLbl = row - half; int x = (int) (origin.x + xOff * (col * 2 + 1 - cols)); int y = (int) (origin.y + yOff * (row - half) * 3); drawHex(g, xLbl, yLbl, x, y, radius); } } } 

在此处输入图像描述

Main.java

 import java.awt.*; import javax.swing.*; public class Main extends JPanel { private static final long serialVersionUID = 1L; private final int WIDTH = 1200; private final int HEIGHT = 800; private Font font = new Font("Arial", Font.BOLD, 18); FontMetrics metrics; public Main() { setPreferredSize(new Dimension(WIDTH, HEIGHT)); } @Override public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; Point origin = new Point(WIDTH / 2, HEIGHT / 2); g2d.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER)); g2d.setFont(font); metrics = g.getFontMetrics(); drawCircle(g2d, origin, 380, true, true, 0x4488FF, 0); drawHexGridLoop(g2d, origin, 7, 50, 8); } private void drawHexGridLoop(Graphics g, Point origin, int size, int radius, int padding) { double ang30 = Math.toRadians(30); double xOff = Math.cos(ang30) * (radius + padding); double yOff = Math.sin(ang30) * (radius + padding); int half = size / 2; for (int row = 0; row < size; row++) { int cols = size - java.lang.Math.abs(row - half); for (int col = 0; col < cols; col++) { int xLbl = row < half ? col - row : col - half; int yLbl = row - half; int x = (int) (origin.x + xOff * (col * 2 + 1 - cols)); int y = (int) (origin.y + yOff * (row - half) * 3); drawHex(g, xLbl, yLbl, x, y, radius); } } } private void drawHex(Graphics g, int posX, int posY, int x, int y, int r) { Graphics2D g2d = (Graphics2D) g; Hexagon hex = new Hexagon(x, y, r); String text = String.format("%s : %s", coord(posX), coord(posY)); int w = metrics.stringWidth(text); int h = metrics.getHeight(); hex.draw(g2d, x, y, 0, 0x008844, true); hex.draw(g2d, x, y, 4, 0xFFDD88, false); g.setColor(new Color(0xFFFFFF)); g.drawString(text, x - w/2, y + h/2); } private String coord(int value) { return (value > 0 ? "+" : "") + Integer.toString(value); } public void drawCircle(Graphics2D g, Point origin, int radius, boolean centered, boolean filled, int colorValue, int lineThickness) { // Store before changing. Stroke tmpS = g.getStroke(); Color tmpC = g.getColor(); g.setColor(new Color(colorValue)); g.setStroke(new BasicStroke(lineThickness, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); int diameter = radius * 2; int x2 = centered ? origin.x - radius : origin.x; int y2 = centered ? origin.y - radius : origin.y; if (filled) g.fillOval(x2, y2, diameter, diameter); else g.drawOval(x2, y2, diameter, diameter); // Set values to previous when done. g.setColor(tmpC); g.setStroke(tmpS); } public static void main(String[] args) { JFrame f = new JFrame(); Main p = new Main(); f.setContentPane(p); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } } 

Hexagon.java

 import java.awt.*; public class Hexagon extends Polygon { private static final long serialVersionUID = 1L; public static final int SIDES = 6; private Point[] points = new Point[SIDES]; private Point center = new Point(0, 0); private int radius; private int rotation = 90; public Hexagon(Point center, int radius) { npoints = SIDES; xpoints = new int[SIDES]; ypoints = new int[SIDES]; this.center = center; this.radius = radius; updatePoints(); } public Hexagon(int x, int y, int radius) { this(new Point(x, y), radius); } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; updatePoints(); } public int getRotation() { return rotation; } public void setRotation(int rotation) { this.rotation = rotation; updatePoints(); } public void setCenter(Point center) { this.center = center; updatePoints(); } public void setCenter(int x, int y) { setCenter(new Point(x, y)); } private double findAngle(double fraction) { return fraction * Math.PI * 2 + Math.toRadians((rotation + 180) % 360); } private Point findPoint(double angle) { int x = (int) (center.x + Math.cos(angle) * radius); int y = (int) (center.y + Math.sin(angle) * radius); return new Point(x, y); } protected void updatePoints() { for (int p = 0; p < SIDES; p++) { double angle = findAngle((double) p / SIDES); Point point = findPoint(angle); xpoints[p] = point.x; ypoints[p] = point.y; points[p] = point; } } public void draw(Graphics2D g, int x, int y, int lineThickness, int colorValue, boolean filled) { // Store before changing. Stroke tmpS = g.getStroke(); Color tmpC = g.getColor(); g.setColor(new Color(colorValue)); g.setStroke(new BasicStroke(lineThickness, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER)); if (filled) g.fillPolygon(xpoints, ypoints, npoints); else g.drawPolygon(xpoints, ypoints, npoints); // Set values to previous when done. g.setColor(tmpC); g.setStroke(tmpS); } } 

可以使用更紧凑的代码,但是这样可以在不重复的情况下提供输出,也可以不进行分支。

 for (row = 0; row < h; row ++) { cols = h + row + 1; for (col = 0; col < cols; col++) { drawHex(g, col - row, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 - yOff * (n - cols) * 3), r); } } for (row = h; row < n; row++) { cols = n - row + h; for (col = 0; col < cols; col++) { drawHex(g, -h + col, -h + row, (int) (W2 + xOff * (-cols + (col * 2 + 1))), (int) (H2 + yOff * (n - cols) * 3), r); } } 

希望这可以帮助。