Search code examples
javaswingdrawinggraphics2dpaintcomponent

Free line drawing java2d app connects lines of each new drawing


I'm building an app that recognises lines drawn with mouse and creates music. I'm new to java2d so heres the problem i'm having:

  • When you draw, and then make another drawing, the last point of the previous drawing connects to the first of the new one. I havent been able to figure out how to fix this. Below is the code.

  • Another question is: I would like to store each stroke of drawing (from mousePressed to mouseReleased) into a ArrayList of type Shape, how can I go into that?

I'd like to be pointed in the right direction as I havent been able to find helpful info online. Thanks!

public class DrawBoard extends JPanel implements MouseListener,
        MouseMotionListener {

    public JLabel status;
    public Point pstart, pfinish;
    private Shape currentShape = null;
    private ArrayList<Point> points = new ArrayList<Point>();
    private ArrayList<Shape> lines = new ArrayList<Shape>();

    public DrawBoard() {

        Dimension size = getPreferredSize();
        size.setSize(1024, 800); // w, h
        setPreferredSize(size);
        setOpaque(false);
        status = new JLabel("default");
        add(status, BorderLayout.SOUTH);

        addMouseListener(this);
        addMouseMotionListener(this);

    }

    @Override
    public void mouseClicked(MouseEvent e) {
        status.setText(String.format("Clicked at %d,%d", e.getX(), e.getY()));
    }

    // Where the drawing happens
    @Override
    public void mousePressed(MouseEvent e) {
        status.setText("you pressed down the mouse");
        this.pstart = e.getPoint();

    }

    @Override
    public void mouseDragged(MouseEvent e) {
        status.setText("you draged the mouse");
        points.add(e.getPoint());
        repaint();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        status.setText("you release the mouse click");
        pfinish = e.getPoint();
    }

    // End of where the drawing happens

    @Override
    public void mouseEntered(MouseEvent e) {
        status.setText("you entered the area");
    }

    @Override
    public void mouseExited(MouseEvent e) {
        status.setText("mouse exited the area");
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        // throw new UnsupportedOperationException("Not supported yet.");
    }

    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.BLACK);
        g2.setStroke(new BasicStroke(5));

        for (int i = 0; i < points.size() - 2; i++) {

            Point p1 = points.get(i);
            Point p2 = points.get(i + 1);

            g2.drawLine(p1.x, p1.y, p2.x, p2.y);
        }

    }
}

I have modified it using BufferedImage and it works for drawing normally, now the only thing does nto work is the clear method, i have tried different methods but none have worked. My new code is Below:

  public class DrawBoard extends JPanel implements MouseListener, MouseMotionListener{


public JLabel status;
private JLabel imgLabel;
public Point pstart, pfinish;
private Shape currentShape = null;
private List<Point> points = new ArrayList<Point>();
private List<BufferedImage> lines = new ArrayList<BufferedImage>();

private static final int BI_WIDTH = 1024;
private static final int BI_HEIGHT = 800;

private BufferedImage bImage = new BufferedImage(BI_WIDTH, BI_HEIGHT,
        BufferedImage.TYPE_INT_ARGB);

public DrawBoard(){  

   Graphics2D g2d = bImage.createGraphics();
   g2d.dispose();

    Dimension size = getPreferredSize();
    size.setSize(1024,800); //w, h
    setPreferredSize(size);
    status =  new JLabel("default");
    add(status, BorderLayout.SOUTH);
    addMouseListener(this);
    addMouseMotionListener(this);  


     imgLabel = new JLabel(new ImageIcon(bImage)) {
     @Override
     protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        paintInLabel(g);
     }
  };
     imgLabel.setOpaque(false);
     setOpaque(false);
      add(imgLabel, BorderLayout.CENTER);

}

private void paintInLabel(Graphics g) {
  Graphics2D g2d = (Graphics2D) g;
  g2d.setColor(Color.BLUE); // this colour is when mouse is pressed
  g2d.setStroke(new BasicStroke(5));
  if (points.size() < 2) {
     return;
  }
  for (int i = 1; i < points.size(); i++) {
     int x1 = points.get(i - 1).x;
     int y1 = points.get(i - 1).y;
     int x2 = points.get(i).x;
     int y2 = points.get(i).y;
     g2d.drawLine(x1, y1, x2, y2);
  }
 }



@Override
public void mouseClicked(MouseEvent e) {
    status.setText(String.format("Clicked at %d,%d", e.getX(), e.getY()));
}


// Where the drawing happens
@Override
public void mousePressed(MouseEvent e) {
    status.setText("you pressed down the mouse");
    this.pstart = e.getPoint();
    points.add(e.getPoint());

}

@Override
public void mouseDragged(MouseEvent e) {
  status.setText("you draged the mouse");
  points.add(e.getPoint());
  imgLabel.repaint();
}

@Override
public void mouseReleased(MouseEvent e) {
    status.setText("you release the mouse click");
    Graphics2D g2d = bImage.createGraphics();
    g2d.setColor(Color.blue); // this is the final colour
    g2d.setStroke(new BasicStroke(5));

     if (points.size() >= 2) {
        for (int i = 1; i < points.size(); i++) {
           int x1 = points.get(i - 1).x;
           int y1 = points.get(i - 1).y;
           int x2 = points.get(i).x;
           int y2 = points.get(i).y;
           g2d.drawLine(x1, y1, x2, y2);
        }
     }
     g2d.dispose();

     points.clear();
     imgLabel.repaint();

}
// End of where the drawing happens

@Override
public void mouseEntered(MouseEvent e) {
   status.setText("you entered the area");
}


@Override
public void mouseExited(MouseEvent e) {
   status.setText("mouse exited the area");
}

@Override
public void mouseMoved(MouseEvent e) {
    //throw new UnsupportedOperationException("Not supported yet.");
}

public void clearDrawBoard() {

    imgLabel.setIcon(null);
}

 }

Solution

  • A few solutions and suggestions:

    • Don't draw from a single List<Point> as there's no way of knowing where one line ends and another begins.
    • Consider drawing each line to a BufferedImage once it is complete and display that BufferedImage in your JComponent in its paintComponent method. You would place it in the BufferedImage in the mouseReleased(...) method.
    • Or consider creating an List<List<Point>> so you can iterate through each line as needed. You would add the new List<Point> to the original List in the mouseReleased(...) method.
    • Consider using your List<Shape> and fill it with Line2D objects that can be drawn. The Line2D would be added to the List<Shape> in the mouseReleased(...) method.
    • Also: You should not override paint(...) but rather `paintComponent(...).
    • Also: Don't forget the @Override annotation for safer coding.

    For example, please see my code and answer here.