Search code examples
javaswingjframepolygongraphics2d

How do I connect the dots to get a polygon in JAVA?


I'm making an application for creating convex polygon.

I imagined it to be so that I first set the vertices of the polygon and then create it.

I was able to make the addition of points (vertices). Now I need help connecting the dots with a line.

This is what it looks like: It looks like this

And I would like when I click the Draw Polygon button that these points connect and it looks like a polygon, like this: and it should look like this when I click the button

Here's the code so you can run it yourself:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;

public class MyPaint{
    
    public static void main(String[] args){    
        
        final PadDraw drawPad = new PadDraw();     

        JFrame frame = new JFrame("Draw Polygon");  
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(100, 100, 450, 300);

        Container contentPane = frame.getContentPane();
        ((JComponent) contentPane).setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        frame.setContentPane(contentPane);

        JPanel buttonPanel = new JPanel();
        buttonPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
        contentPane.add(buttonPanel, BorderLayout.SOUTH);
        
        JButton buttonDrawPolygon = new JButton("Draw Polygon");
        buttonPanel.add(buttonDrawPolygon);
        
        JButton buttonReset = new JButton("Reset");
        buttonPanel.add(buttonReset);
        
        contentPane.add(drawPad, BorderLayout.CENTER);
    
    }
}


class PadDraw extends JComponent{

    private Image image;    
    private Graphics2D graphics2D;  
    private int currentX , currentY , oldX , oldY ;
    
    public PadDraw(){
    addMouseListener(new MouseAdapter(){
        public void mousePressed(MouseEvent e){
            oldX = e.getX();
            oldY = e.getY();
        }
    });
    
    addMouseListener(new MouseAdapter(){
        public void mousePressed(MouseEvent e){
            currentX = e.getX();
            currentY = e.getY();
            if(graphics2D != null) {
                graphics2D.drawLine(oldX, oldY, currentX, currentY);
                repaint();
                oldX = currentX;
                oldY = currentY;
            }
            System.out.println(oldX + " " + oldY);
        }
    });
    
}   
    
    public void paintComponent(Graphics g){
    if(image == null){
        image = createImage(getSize().width, getSize().height);
        graphics2D = (Graphics2D)image.getGraphics();
        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics2D.setStroke(new BasicStroke(5));
    }
    g.drawImage(image, 0, 0, null);
    }

}

Solution

  • Introduction

    Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.

    Here's your revised GUI

    Start

    Here's the GUI with four points

    Points

    Here's the GUI with a polygon

    Polygon

    I'm not showing it, but the Reset button clears the drawing area.

    Explanation

    When I create a Swing GUI, I use the model-view-controller pattern. This pattern allows me to separate my concerns and focus on one part of the Swing application at a time.

    A Swing application model consists of one or more plain Java getter/setter classes.

    A Swing view consists of a JFrame and one or more JPanels.

    Swing controllers are the listeners that are attached to JButtons and drawing JPanels.

    Model

    For this application, I created a PolygonModel class. The class holds a boolean that tells me whether or not to draw the polygon and a java.util.List of java.awt.Point instances. The Point class holds an X and Y int value.

    View

    All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.

    I broke up your main method into a couple of methods. I separate the creation of the JFrame from the creation of the JPanels. This allows me to separate my concerns and focus on one part of the GUI at a time.

    The JFrame methods must be called in a specific order. This is the order I use for most of my Swing applications.

    I changed your PadDraw class to extend a JPanel. I moved the MouseAdapter code to its own class. Your drawing panel should draw. Period. Nothing else.

    The paintComponent method always starts with a call to the super.paintComponent method. This maintains the Swing paint chain and helps to eliminate unwanted drawing artifacts.

    The drawing JPanel is cleared before every repaint. Therefore, you have to completely redraw your image each time. That's why we store the List of Point instances in the model.

    Controller

    I created three controller classes.

    The PointListener class extends MouseAdapter. Notice how simple the mousePressed method becomes with an application model.

    The two JButtons each have their own ActionListener. Since they are so simple, I made each of them lambdas.

    Code

    Here's the complete runnable code. I made all the additional classes inner classes so I could make them public and post the code as one block.

    import java.awt.BasicStroke;
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.RenderingHints;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.border.EtchedBorder;
    
    public class PolygonImage implements Runnable {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new PolygonImage());
        }
    
        private final PolygonModel model;
    
        private final PadDraw drawPad;
    
        public PolygonImage() {
            this.model = new PolygonModel();
            this.drawPad = new PadDraw(this, model);
        }
    
        @Override
        public void run() {
            JFrame frame = new JFrame("Draw Polygon");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            frame.add(drawPad, BorderLayout.CENTER);
            frame.add(createButtonPanel(), BorderLayout.SOUTH);
    
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        private JPanel createButtonPanel() {
            JPanel buttonPanel = new JPanel();
            buttonPanel
                    .setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
    
            JButton buttonDrawPolygon = new JButton("Draw Polygon");
            buttonDrawPolygon.addActionListener(event -> {
                model.setConnectPoints(true);
                repaint();
            });
            buttonPanel.add(buttonDrawPolygon);
    
            JButton buttonReset = new JButton("Reset");
            buttonReset.addActionListener(event -> {
                model.setConnectPoints(false);
                model.clearList();
                repaint();
            });
            buttonPanel.add(buttonReset);
    
            return buttonPanel;
        }
    
        public void repaint() {
            drawPad.repaint();
        }
    
        public class PadDraw extends JPanel {
    
            private static final long serialVersionUID = 1L;
    
            private final PolygonModel model;
    
            public PadDraw(PolygonImage view, PolygonModel model) {
                this.model = model;
                this.addMouseListener(new PointListener(view, model));
                this.setPreferredSize(new Dimension(450, 300));
            }
    
            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
    
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setColor(Color.black);
    
                // Draw points
                for (Point p : model.getPoints()) {
                    int radius = 6;
                    int diameter = radius + radius;
                    g2d.fillOval(p.x - radius, p.y - radius, diameter, diameter);
                }
    
                // Draw polygon
                if (model.isConnectPoints()) {
                    g2d.setStroke(new BasicStroke(5));
                    List<Point> points = model.getPoints();
                    if (points.size() >= 1) {
                        Point old = points.get(0);
                        for (int index = 1; index < points.size(); index++) {
                            Point p = points.get(index);
                            g2d.drawLine(old.x, old.y, p.x, p.y);
                            old = p;
                        }
                        Point p = points.get(0);
                        g2d.drawLine(p.x, p.y, old.x, old.y);
                    }
                }
            }
    
        }
    
        public class PointListener extends MouseAdapter {
    
            private final PolygonImage view;
    
            private final PolygonModel model;
    
            public PointListener(PolygonImage view, PolygonModel model) {
                this.view = view;
                this.model = model;
            }
    
            @Override
            public void mousePressed(MouseEvent event) {
                model.addPoint(event.getPoint());
                view.repaint();
            }
        }
    
        public class PolygonModel {
    
            private boolean connectPoints;
    
            private final List<Point> points;
    
            public PolygonModel() {
                this.points = new ArrayList<>();
                this.connectPoints = false;
            }
    
            public void setConnectPoints(boolean connectPoints) {
                this.connectPoints = connectPoints;
            }
    
            public boolean isConnectPoints() {
                return connectPoints;
            }
    
            public void clearList() {
                this.points.clear();
            }
    
            public void addPoint(Point point) {
                this.points.add(point);
            }
    
            public List<Point> getPoints() {
                return points;
            }
    
        }
    
    }