1

I'm writing a program which should allow the user to create a floor plan of a room - this means they can draw lines to represent walls etc. I am using a JPanel with Graphics 2D to do all the drawing stuff and currently I have a grid set as the background for the JPanel and I can draw lines based on the mouse movement(I have an enum for the mousestate with two states - DRAGGING and IDLE). However I want to add control points to the lines so that they can be moved around the JPanel once they are drawn and I can't figure out how to this. I know that I need rectangles to represent the points but I don't know how to link them to the lines so that if the points are moved, the lines are moved too. I would appreciate any help with this.

Here is the code for the JPanel class:

 package floorplan;

 /**
 *
 * @author xodkx
 */

 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import java.awt.image.BufferedImage;
 import java.awt.event.MouseListener;

 public class Floor extends JPanel implements MouseListener, MouseMotionListener
 {


 private static final int WIDTH = Integer.parseInt(JOptionPane.showInputDialog("Please 
                                  enter the width of your room"));
 private static final int LENGTH = Integer.parseInt(JOptionPane.showInputDialog("Please       
                                  enter the width of your room"));
 private static final Color BACKGROUND = Color.WHITE;
 private static final Color INITIAL_COLOUR = Color.BLACK;
 private static final Framework INITIAL_FRAMEWORK = Framework.WALL;  

 private MouseState state = MouseState.IDLE;
 private Framework frameworkType = INITIAL_FRAMEWORK; 
 private Color colour = INITIAL_COLOUR;


 private Point start = null;
 private Point end = null;
 private Rectangle startpt = new Rectangle(0, 0, 8, 8);// Start control point
 private Rectangle endpt = new Rectangle(0, 0, 8, 8);// End control point

 private BufferedImage bufImage = null;


 public Floor()
  {
      setPreferredSize(new Dimension(LENGTH,WIDTH));
      setBackground(Color.white);
      setBorder(BorderFactory.createLineBorder (Color.black, 5));

      this.addMouseListener(this);
      this.addMouseMotionListener(this);

  }

    public void setColor(Color color)
  {
      colour = color;

  }  

   public void setFramework(Framework framework)
  {
      frameworkType = framework;
  }  

@Override
 public void paintComponent(Graphics g)
  {
     super.paintComponent(g);
     Graphics2D g2 = (Graphics2D)g;
     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                          RenderingHints.VALUE_ANTIALIAS_ON);

      if(bufImage == null)
      {
          int h = this.getHeight();
          int w = this.getWidth();
          bufImage = (BufferedImage)this.createImage(h,w);
          Graphics2D gc = bufImage.createGraphics(); 
          gc.setColor(BACKGROUND);   
          gc.fillRect(0, 0, w, h);
      }


      g2.drawImage(bufImage,null,0,0);

      drawGrid(g2);

      if(state == MouseState.DRAGGING)
      {
          createComponent(g2);
      }
  }

public void drawGrid(Graphics g2)// sets a grid as the background of the JPanel
{
     int gridDivisions = 20;
     int divisionSize = WIDTH/gridDivisions;
     int grid = WIDTH*LENGTH;

     g2.setColor(Color.lightGray);

     for(int i=1; i<grid; i++)
    {
        int x = i * divisionSize;
        g2.drawLine(x,0,x,getSize().height);
    }

     for(int i=1; i<grid; i++)
    {  
       int y = i*divisionSize;
       g2.drawLine(0,y,getSize().width,y);
    }
}


  public void createComponent(Graphics2D g2)// method that draws the lines for various  
                                                components of the room
  {
      g2.setColor(colour);

      switch (frameworkType)
      { 
          case WALL:
            g2.setStroke(new BasicStroke(5));
            g2.drawLine(start.x, start.y, end.x,end.y);
            break;

          case DOOR:
            g2.setStroke(new BasicStroke(5));
            g2.drawLine(start.x, start.y, end.x,end.y);
            break;

          case WINDOW:
            g2.setStroke(new BasicStroke(5));
            g2.drawLine(start.x, start.y, end.x,end.y);
            break;


          default:
          g2.drawString("test", 10, 20);
          break;   
      }        
  }


@Override
public void mousePressed(MouseEvent e) 
{
     state = MouseState.DRAGGING;
     start = e.getPoint();
     end = start;
}

@Override

public void mouseDragged(MouseEvent e) 
{

    state = MouseState.DRAGGING;
    end = e.getPoint();
    this.repaint();

}

@Override
public void mouseReleased(MouseEvent e) 
{
    end = e.getPoint();
    if(state == MouseState.DRAGGING)
    {
       state = MouseState.IDLE;
       createComponent(bufImage.createGraphics());
       this.repaint();
    } 
}


 @Override
public void mouseClicked(MouseEvent e) 
{

}

@Override
public void mouseEntered(MouseEvent e) 
{

}

@Override
public void mouseExited(MouseEvent e) 
{

}



@Override
public void mouseMoved(MouseEvent e) 
{

}
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
odk
  • 39
  • 1
  • 7
  • Also be aware of the project "Sweet Home 3D": http://www.sweethome3d.com/index.jsp It's open source and uses Swing AFAIK. Maybe you want to join that project. – Puce Jan 29 '13 at 15:40
  • See also [`LinePanel`](http://stackoverflow.com/a/5797965/230513). – trashgod Jan 29 '13 at 17:27

1 Answers1

11

Why not create an ArrayList of your control points, or perhaps even better, Ellipse2D objects centered on the control points? Then you can test if the mouse is pressed in an Ellipse to determine what its behavior should be, and you could use the List of Ellipse2D objects to obtain the line end points when drawing in the paintComponent(...) method.

Note that your code does not compile nor run for us, and so makes it difficult for us to fully analyze, test, and modify it. For better more specific help, consider condensing your code to the smallest possible code that still compiles, runs and is able to reproduce your problem, an sscce.

For example:

import java.awt.BasicStroke;
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.Stroke;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

@SuppressWarnings("serial")
public class Floor2 extends JPanel {
   private static final Color BACKGROUND = new Color(0, 0, 0, 0);
   private static final Color INITIAL_COLOUR = Color.BLACK;
   private static final int BORDER_WIDTH = 5;
   private static final Color LINE_DRAWING_COLOR = new Color(200, 200, 255);
   private static final Color LINE_COLOR = Color.blue;
   private static final Stroke DRAWING_LINE_STROKE = new BasicStroke((float)BORDER_WIDTH);
   public static final int ELLIPSE_DIAMETER = 10;
   private MouseState mouseState = MouseState.IDLE;
   private BufferedImage bufImage = null;
   private int width;
   private int height;
   private int gridDivisions;
   private List<List<Ellipse2D>> ellipseList = new ArrayList<List<Ellipse2D>>();
   private Line2D drawingLine = null;

   public enum MouseState {
      IDLE, DRAGGING
   }

   public Floor2(int width, int height, int gridDivisions) {
      this.width = width;
      this.height = height;
      this.gridDivisions = gridDivisions;
      setBackground(Color.white);
      setBorder(BorderFactory.createLineBorder(Color.black, BORDER_WIDTH));

      MyMouseAdapter mouseAdapter = new MyMouseAdapter();
      addMouseListener(mouseAdapter);
      addMouseMotionListener(mouseAdapter);
   }

   private static void createAndShowGui() {
      int w = 600;
      int h = w;
      int gridDiv = 20;
      Floor2 mainPanel = new Floor2(w, h, gridDiv);

      JFrame frame = new JFrame("Floor2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(width, height);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (bufImage == null) {
         bufImage = createGridImage();
      }

      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

      Stroke initStroke = g2.getStroke();
      g2.setStroke(DRAWING_LINE_STROKE);
      if (mouseState == MouseState.DRAGGING && drawingLine != null) {
         g2.setColor(LINE_DRAWING_COLOR);
         g2.draw(drawingLine);
      }

      g2.setColor(LINE_COLOR);
      for (List<Ellipse2D> ellipses : ellipseList) {
         Point2D p2d1 = new Point2D.Double(ellipses.get(0).getCenterX(), ellipses.get(0).getCenterY());
         Point2D p2d2 = new Point2D.Double(ellipses.get(1).getCenterX(), ellipses.get(1).getCenterY());
         Line2D line = new Line2D.Double(p2d1, p2d2);
         g2.draw(line);
      }

      g.drawImage(bufImage, 0, 0, this);
      g2.setStroke(initStroke);
   }

   private BufferedImage createGridImage() {
      BufferedImage gridImage = new BufferedImage(width, height,
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = gridImage.createGraphics();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setBackground(BACKGROUND);
      g2.clearRect(0, 0, width, height);

      int divisionSize = width / gridDivisions;
      int grid = width * height;
      g2.setColor(Color.lightGray);
      for (int i = 1; i < grid; i++) {
         int x = i * divisionSize;
         g2.drawLine(x, 0, x, getSize().height);
      }
      for (int i = 1; i < grid; i++) {
         int y = i * divisionSize;
         g2.drawLine(0, y, getSize().width, y);
      }

      g2.dispose();
      return gridImage;
   }

   private class MyMouseAdapter extends MouseAdapter {
      private Point p1;

      @Override
      public void mousePressed(MouseEvent e) {
         if (e.getButton() != MouseEvent.BUTTON1) {
            return;
         }

         for (List<Ellipse2D> endPts : ellipseList) {
            // check if one of the ellipses has been selected
            // if so, remove it from elipseList
            // set drawingLine == to end points
            // setdragging = true
            // repaint
            // return
         }
         mouseState = MouseState.DRAGGING;
         p1 = e.getPoint();
      }

      @Override
      public void mouseDragged(MouseEvent e) {
         if (mouseState != MouseState.DRAGGING) {
            return;
         }
         drawingLine = new Line2D.Double(p1, e.getPoint());
         repaint();
      }

      @Override
      public void mouseReleased(MouseEvent e) {
         if (drawingLine != null) {
            List<Ellipse2D> newEndPoints = new ArrayList<Ellipse2D>();

            double x1 = drawingLine.getX1() - ELLIPSE_DIAMETER / 2;
            double y1 = drawingLine.getY1() - ELLIPSE_DIAMETER / 2;
            Ellipse2D ellipse1 = new Ellipse2D.Double(x1, y1, ELLIPSE_DIAMETER, ELLIPSE_DIAMETER);
            x1 = drawingLine.getX2() - ELLIPSE_DIAMETER / 2;
            y1 = drawingLine.getY2() - ELLIPSE_DIAMETER / 2;
            Ellipse2D ellipse2 = new Ellipse2D.Double(x1, y1, ELLIPSE_DIAMETER, ELLIPSE_DIAMETER);
            newEndPoints.add(ellipse1);
            newEndPoints.add(ellipse2);
            ellipseList.add(newEndPoints);
            repaint();
         }

         mouseState = MouseState.IDLE;
         drawingLine = null;
      }
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

edit: mousePressed method change:

private class MyMouseAdapter extends MouseAdapter {
  private Point2D p1;

  @Override
  public void mousePressed(MouseEvent e) {
     if (e.getButton() != MouseEvent.BUTTON1) {
        return;
     }

     for (List<Ellipse2D> endPts : ellipseList) {
        for (int i = 0; i < endPts.size(); i++) {
           Ellipse2D endPt = endPts.get(i);
           if (endPt.contains(e.getPoint())) {
              Ellipse2D endPt2 = endPts.get(Math.abs(i - 1));
              ellipseList.remove(endPts);

              Point2D p2 = new Point2D.Double(endPt.getCenterX(), endPt.getCenterY());
              p1 = new Point2D.Double(endPt2.getCenterX(), endPt2.getCenterY());
              drawingLine = new Line2D.Double(p1, p2);
              mouseState = MouseState.DRAGGING;
              repaint();
              return;
           }
        }
     }
     mouseState = MouseState.DRAGGING;
     p1 = e.getPoint();
  }

  @Override
  public void mouseDragged(MouseEvent e) {
     if (mouseState != MouseState.DRAGGING) {
        return;
     }
     drawingLine = new Line2D.Double(p1, e.getPoint());
     repaint();
  }

  @Override
  public void mouseReleased(MouseEvent e) {
     if (drawingLine != null) {
        List<Ellipse2D> newEndPoints = new ArrayList<Ellipse2D>();

        double x1 = drawingLine.getX1() - ELLIPSE_DIAMETER / 2;
        double y1 = drawingLine.getY1() - ELLIPSE_DIAMETER / 2;
        Ellipse2D ellipse1 = new Ellipse2D.Double(x1, y1, ELLIPSE_DIAMETER, ELLIPSE_DIAMETER);
        x1 = drawingLine.getX2() - ELLIPSE_DIAMETER / 2;
        y1 = drawingLine.getY2() - ELLIPSE_DIAMETER / 2;
        Ellipse2D ellipse2 = new Ellipse2D.Double(x1, y1, ELLIPSE_DIAMETER, ELLIPSE_DIAMETER);
        newEndPoints.add(ellipse1);
        newEndPoints.add(ellipse2);
        ellipseList.add(newEndPoints);
        repaint();
     }

     mouseState = MouseState.IDLE;
     drawingLine = null;
  }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you so much for this example! I'll try it out and hope it wrks! Thanks! :) – odk Jan 29 '13 at 21:15