Search code examples
javajcomponenthexagonal-tiles

Java - Hexagon location in grid


Im searching for an algorythem to get the location of a hexagon in a grid. I found this one but it doesnt seam to work:

for(int i = 0; i < width; i++) {
        for(int j = 0; j < height; j++) {
            grid[i][j] = new Hexagon(x+(j*((3*Hexagon.S)/2)), y+((j%2)*Hexagon.A)+(2*i*Hexagon.A));
        }
    }

The output is kind of strange: output

This is the window-creating class(just a test class):

import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Grid extends JPanel {

    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        int width = 2;
        int height = 4;
        int x = 100;
        int y = 100;
        Hexagon[][] grid = new Hexagon[width][height];

        JFrame f = new JFrame();
        Container cp = f.getContentPane();


        for(int i = 0; i < width; i++) {
            for(int j = 0; j < height; j++) {
                grid[i][j] = new Hexagon(x+(j*((3*Hexagon.S)/2)), y+((j%2)*Hexagon.A)+(2*i*Hexagon.A));
                cp.add(grid[i][j]);
            }
        }

        f.setLayout(null);
        f.setBounds(100, 100, 300, 300);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
    }
}

The Hexagon.java class:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;

import javax.swing.JButton;

 public class Hexagon extends JButton {

    public static final int S = 50;
    public static final int A = (int) (Math.sqrt(3)*(S/2));

    private static final long serialVersionUID = 1L;
    private final int x, y;
    private final Polygon shape;

    public Hexagon(int x, int y) {
        this.x = x;
        this.y = y;
        this.shape = initHexagon();
        setSize(2*S, 2*A);
        setLocation(x-S, y-A);
        setContentAreaFilled(false);
    }

    private Polygon initHexagon() {
        Polygon p = new Polygon();
        p.addPoint(x+(S/2), y-A);
        p.addPoint(x+S, y);
        p.addPoint(x+(S/2), y+A);
        p.addPoint(x-(S/2), y+A);
        p.addPoint(x-S, y);
        p.addPoint(x-(S/2), y-A);
        return p;
    }

    protected void paintComponent(Graphics g) {
        g.setColor(Color.BLACK);
        g.drawPolygon(this.shape);
    } 

    protected void paintBorder(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.BLACK);
        g2.setStroke(new BasicStroke(4));
        g2.drawPolygon(this.shape);
    }

    public boolean contains(int x, int y) {
        return this.shape.contains(x, y);
    }
}

As i said, this class worked just fine using non-rectangular shapes. There was no clipping or such.


Solution

  • You've posted your definition of Hexagon too late, so I copy-pasted a modified version of a similar class from my collection of code snippets.


    Here is one way to generate a hexagonal grid:

    import java.awt.Container;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JComponent;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Color;
    import java.awt.GridLayout;
    import java.util.function.*;
    
    public class Hexagons extends JPanel {
    
        private static final long serialVersionUID = 1L;
    
        /** Height of an equilateral triangle with side length = 1 */
        private static final double H = Math.sqrt(3) / 2;
    
        static class Hexagon {
          final int row;
          final int col;
          final double sideLength;
          public Hexagon(int r, int c, double a) {
            this.row = r;
            this.col = c;
            this.sideLength = a;
          }
    
          double getCenterX() {
            return 2 * H * sideLength * (col + (row % 2) * 0.5);
          }
    
          double getCenterY() {
            return 3 * sideLength / 2  * row;
          }
    
          void foreachVertex(BiConsumer<Double, Double> f) {
            double cx = getCenterX();
            double cy = getCenterY();
            f.accept(cx + 0, cy + sideLength);
            f.accept(cx - H * sideLength, cy + 0.5 * sideLength);
            f.accept(cx - H * sideLength, cy - 0.5 * sideLength);
            f.accept(cx + 0, cy - sideLength);
            f.accept(cx + H * sideLength, cy - 0.5 * sideLength);
            f.accept(cx + H * sideLength, cy + 0.5 * sideLength);
          }
        }
    
        public static void main(String[] args) {
            final int width = 50;
            final int height = 50;
            final Hexagon[][] grid = new Hexagon[height][width];
            for(int row = 0; row < height; row++) {
                for(int col = 0; col < width; col++) {
                    grid[row][col] = new Hexagon(row, col, 50);
                }
            }
    
            JFrame f = new JFrame("Hexagons");
            f.getContentPane().setLayout(new GridLayout());
            f.getContentPane().add(new JComponent() {
              @Override public void paint(Graphics g) {
                g.setColor(new Color(0xFF, 0xFF, 0xFF));
                g.fillRect(0,0,1000,1000);
                g.setColor(new Color(0,0,0));
                final int[] xs = new int[6];
                final int[] ys = new int[6];
                for (Hexagon[] row : grid) {
                  for (Hexagon h: row) {
                    final int[] i = {0};
                    h.foreachVertex((x, y) -> {
                      xs[i[0]] = (int)((double)x);
                      ys[i[0]] = (int)((double)y);
                      i[0]++;
                    });
                    g.drawPolygon(xs, ys, 6);
    
                    g.drawString(
                      "(" + h.row + "," + h.col + ")", 
                      (int)(h.getCenterX() - 15), 
                      (int)(h.getCenterY() + 12)
                    );
                  }
                }
              }
            });
            f.setBounds(0, 0, 500, 500);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
            try {
                Thread.sleep(100);
            } catch (Throwable e) {
    
            } finally {
                f.repaint();
            }
        }
    }
    

    It produces the following output:

    enter image description here

    Sorry for the lack of anti-aliasing. A few hints:

    • Height H of an equilateral triangle with unit side length is sqrt(3) / 2
    • The six offsets from the center are (0, +1), (H, +1/2), (H, -1/2), (0, -1), (-H, -1/2), (-H, +1/2), everything times side length.
    • Distance between rows is 1.5, distance between columns is 2 * H (times scaling constant = side length).
    • Every odd row is shifted by (0, H) (times scaling constant).
    • The position of (row,col)-th hexagon is (1.5 * row, 2 * H * (col + 0.5 * (row % 2))) (times constant).

    If you want to rotate the hexagons such that two of their sides are horizontal, you have to flip rows and columns.