Skip to content
Advertisement

2D grid is not fitting in JFrame for pathing visualizer

I am trying to create a 2D grid with node sizes depending on the size of the height/width of the frame but whenever I run the program, the bottom row and right columns are somewhat cut off. Here is my execution code:

public class PathfindingVisualizer {
    public static void main(String[] args) {
        Grid grid = new Grid(10,10, new Dimension(1000, 1000));

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GridFrame(grid);
            }
        });
    }
}

Here is the Grid class:

package gui;

import javax.swing.*;
import java.awt.*;
import java.util.Arrays;

public class Grid extends JPanel {
    private final int rows, cols;
    private final Node[][] grid;
    private Node start, end;
    private final Dimension size;
    private int nodeSize;

    public Grid(int rows, int cols, Dimension size) {
        this.rows = rows;
        this.cols = cols;
        this.grid = new Node[cols][rows];
        this.size = size;
        this.nodeSize = getWidth() / cols;
        initializeGrid();
    }

    public void initializeGrid() {
        // populate grid as a 2D array of Nodes
        for (int col=0; col<cols; col++) {
            for (int row=0; row<rows; row++) {
                grid[col][row] = new Node(col, row);
            }
        }

        // create neighbors for all the Nodes
        for (int col=0; col<cols; col++) {
            for (int row=0; row<rows; row++) {
                setNeighbors(col, row);
            }
        }

        // create start and end points for pathfinding
        start = grid[0][0];
        end = grid[cols-1][rows-1];
    }

    private void setNeighbors(int col, int row) {
        int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

        for (int[] direction : directions) {
            int neighborCol = direction[0] + col;
            int neighborRow = direction[1] + row;

            if (neighborCol >= 0 && neighborCol < cols &&
                neighborRow >= 0 && neighborRow < rows) {
                if (Arrays.equals(direction, new int[]{1, 0})) {
                    grid[col][row].setDown(grid[neighborCol][neighborRow]);
                }
                if (Arrays.equals(direction, new int[]{-1, 0})) {
                    grid[col][row].setUp(grid[neighborCol][neighborRow]);
                }
                if (Arrays.equals(direction, new int[]{0, 1})) {
                    grid[col][row].setRight(grid[neighborCol][neighborRow]);
                }
                if (Arrays.equals(direction, new int[]{0, -1})) {
                    grid[col][row].setLeft(grid[neighborCol][neighborRow]);
                }
            }
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.BLACK);
        for (int col=0; col<cols; col++) {
            for (int row=0; row<rows; row++) {
                g.drawRect(col*nodeSize, row*nodeSize, nodeSize, nodeSize);
            }
        }
    }

    public int getRows() {
        return rows;
    }

    public int getCols() {
        return cols;
    }

    public Node[][] getGrid() {
        return grid;
    }

    @Override
    public Dimension getSize() {
        return size;
    }

    public int getHeight(){
        return (int) size.getHeight();
    }

    public int getWidth(){
        return (int) size.getWidth();
    }
}

And here is the GridFrame class:

package gui;

import javax.swing.*;
import java.awt.*;

public class GridFrame {
    public GridFrame(Grid grid) {
        JFrame frame = new JFrame("Pathfinding Visualizer");
        frame.setPreferredSize(grid.getSize());
        frame.setContentPane(grid);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

I have also been messing around with implementations that have a draw function w/in the Node class but haven’t had any luck with that as of yet.

The goal here is to make a pathing visualizer. This is my first Java project, so any help is helpful!

EDIT: Here is the Node class as well:

package gui;

public class Node {
    private final int row, col;
    private Node up, down, left, right;
    private boolean start, end, wall, visited;

    public Node(int col, int row) {
        this.col = col;
        this.row = row;
    }

    public int getRow() {
        return row;
    }

    public int getCol() {
        return col;
    }

    public Node getUp() {
        return up;
    }

    public void setUp(Node up) {
        this.up = up;
    }

    public Node getDown() {
        return down;
    }

    public void setDown(Node down) {
        this.down = down;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    public boolean isStart() {
        return start;
    }

    public void setStart(boolean start) {
        this.start = start;
    }

    public boolean isEnd() {
        return end;
    }

    public void setEnd(boolean end) {
        this.end = end;
    }

    public boolean isWall() {
        return wall;
    }

    public void setWall(boolean wall) {
        this.wall = wall;
    }

    public boolean isVisited() {
        return visited;
    }

    public void setVisited(boolean visited) {
        this.visited = visited;
    }
}

Advertisement

Answer

Let’s start with…

Grid grid = new Grid(10,10, new Dimension(1000, 1000));

public Grid(int rows, int cols, Dimension size) {
        this.rows = rows;
        this.cols = cols;
        this.grid = new Node[cols][rows];
        this.size = size;
        this.nodeSize = getWidth() / cols;
        initializeGrid();
}

Passing the Dimension into the constructor like this is a bad idea, apart from the fact that you are completely ignoring the height, you could end up with a integer truncation (getWidth() / cols will simple cut of the decimal values and won’t apply any kind of rounding, which may give you erroneous results)

Next you do…

this.nodeSize = getWidth() / cols;

In the normal state of things, getWidth at this point would return 0, except you do…

@Override
public Dimension getSize() {
    return size;
}

public int getHeight() {
    return (int) size.getHeight();
}

public int getWidth() {
    return (int) size.getWidth();
}

which is very questionable (and not at all recommended). Remember when I talked about “integer truncation”? This could mean that the size of the component is bigger/small then the actual area of the over all grid. Overriding getSize is also not recommended as you are messing with the layout management system which could produce no end of long term issues.

And finally, which is the nail in your problem, you do…

frame.setPreferredSize(grid.getSize());

What you don’t seem to recognise is, the viewable content size is the frame sizewindow decorations, this makes the viewable area of your program < 1000x1000

So, what’s the answer? Pass the size of the individual cell instead…

private int cellSize;

public Grid(int rows, int cols, int cellSize) {
    this.rows = rows;
    this.cols = cols;
    this.grid = new String[cols][rows];
    this.cellSize = cellSize;
    initializeGrid();
}

and override getPreferredSize (instead of getSize)…

@Override
public Dimension getPreferredSize() {
    return new Dimension(cellSize * cols, cellSize * rows);
}

and obviously paintComponent

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(Color.BLACK);
    for (int col = 0; col < cols; col++) {
        for (int row = 0; row < rows; row++) {
            g.drawRect(col * cellSize, row * cellSize, cellSize, cellSize);
        }
    }
}

and get rid of

 frame.setPreferredSize(grid.getSize());

Runnable example…

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new Grid(10, 10, 100));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Grid extends JPanel {
        private final int rows, cols;
        private final String[][] grid;
        private int cellSize;

        public Grid(int rows, int cols, int cellSize) {
            this.rows = rows;
            this.cols = cols;
            this.grid = new String[cols][rows];
            this.cellSize = cellSize;
            initializeGrid();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(cellSize * cols, cellSize * rows);
        }

        public void initializeGrid() {
            // populate grid as a 2D array of Nodes
            for (int col = 0; col < cols; col++) {
                for (int row = 0; row < rows; row++) {
                    grid[col][row] = col + "x" + row;
                }
            }

            // create neighbors for all the Nodes
            for (int col = 0; col < cols; col++) {
                for (int row = 0; row < rows; row++) {
                    setNeighbors(col, row);
                }
            }

            // create start and end points for pathfinding
            //start = grid[0][0];
            //end = grid[cols - 1][rows - 1];
        }

        private void setNeighbors(int col, int row) {
            //int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
            //
            //for (int[] direction : directions) {
            //    int neighborCol = direction[0] + col;
            //    int neighborRow = direction[1] + row;
            //
            //    if (neighborCol >= 0 && neighborCol < cols
            //            && neighborRow >= 0 && neighborRow < rows) {
            //        if (Arrays.equals(direction, new int[]{1, 0})) {
            //            grid[col][row].setDown(grid[neighborCol][neighborRow]);
            //        }
            //        if (Arrays.equals(direction, new int[]{-1, 0})) {
            //            grid[col][row].setUp(grid[neighborCol][neighborRow]);
            //        }
            //        if (Arrays.equals(direction, new int[]{0, 1})) {
            //            grid[col][row].setRight(grid[neighborCol][neighborRow]);
            //        }
            //        if (Arrays.equals(direction, new int[]{0, -1})) {
            //            grid[col][row].setLeft(grid[neighborCol][neighborRow]);
            //        }
            //    }
            //}
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.BLACK);
            for (int col = 0; col < cols; col++) {
                for (int row = 0; row < rows; row++) {
                    g.drawRect(col * cellSize, row * cellSize, cellSize, cellSize);
                }
            }
        }

        public int getRows() {
            return rows;
        }

        public int getCols() {
            return cols;
        }

        //public Node[][] getGrid() {
        //    return grid;
        //}
    }
}
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement