hexagonal grids java loops dynamic coordinates hexagonal-tiles

java - grids - hex grid generator



Algoritmo para generar una cuadrĂ­cula hexagonal con sistema de coordenadas (2)

Incluso es posible un código más compacto, pero esto da salida sin repetición y sin derivación.

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); } }

Espero que esto ayude.

Estoy tratando de enrollar 19 líneas de código en un solo ciclo, pero me siento un poco perplejo. La razón por la que pregunto es porque quiero que la cuadrícula tenga otros tamaños en lugar de 5.

En Main::drawHexGridAdvanced() , estoy tratando de deducir las similitudes entre cada línea a diferencia de Main::drawHexGridBasic() donde estoy codificando valores.

No estoy seguro de cómo determinar el inicio de la x para cada columna en cada fila, porque el patrón para n == 5 es 0, -1 -2 -2 -2 después de eso, cada columna consecutiva se incrementa, excepto cuando el ciclo alcanza el punto medio ...

Información y comprensión

`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 se ve mucho más elegante:

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

Aquí está mi resultado esperado:

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); } }


Lo he descubierto, gracias por los comentarios de Tanmay .

Me di cuenta de que el desplazamiento y era incorrecto para n - cols y debería ser row - half lugar.

A continuación se muestra el código más completo y compacto que pude obtener. Aunque se prefiere ingresar un número entero impar para el tamaño, puede ingresar un valor positivo. También agregué un relleno al desplazamiento.

Este if-conditional todavía me preocupa: 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); } }