kandiman
kandiman

Reputation: 35

Paint application: Drag free-form lines (paths) with AffineTransform

How to drag free-form lines (paths) with AffineTransform?

I'm making a Paint-like application in Java and one of the requirements is being able to drag drawn free-form shapes to a new location on a JPanel used for drawing. I've been trying to get AffineTransform to do this for a day now and what it does at the moment is while the required line(which is stored as a Path2D) is selected and dragged it does move. However, once there is no more line selected, the line goes back to its original location. Also, when I select it again, it is immediately shown in that new location (if that makes any sense); this is probably to the coordinate system being translated, but I'm not sure... Any help will be highly appreciated! Perhaps there is an simpler way of doing this. PS I also noticed that when moving any of the drawn lines, except for the one drawn last, all of the lines drawn prior to the line, which is being moved, are moved together with it. Here's the code:

public class DrawPanel extends JPanel {
public double translateX=0;
public double translateY=0;
public int lastOffsetX;
public int lastOffsetY;

class Line {
    public Point start;
    public Point end;
    public Color color;
    public Path2D path;
}

ArrayList<Line> lines = new ArrayList<Line>();
ArrayList<Path2D> paths = new ArrayList<Path2D>();
    boolean moveMode = false;

    Path2D selectedLine;
    int xDistance;
    int yDistance;


    public DrawPanel() {
            setBackground(Color.WHITE);
            setFocusable(true);
            requestFocusInWindow();

            this.addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    //System.out.println(resizeMode);
                        if (moveMode) {
                            if (selectedLine!=null) {
                                int newX = e.getX() - lastOffsetX;
                            int newY = e.getY() - lastOffsetY;
                            lastOffsetX += newX;
                            lastOffsetY += newY;
                            translateX += newX;
                        translateY += newY;
                                    repaint();
                            }
                        } else {

                                Path2D p = paths.get(paths.size() - 1);
                                p.lineTo(e.getX(), e.getY());
                                    repaint();
                        }
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                        super.mouseMoved(e);
                        if (resizeMode) {
                                selectedLine = null;
                                for (Path2D l : paths) {
                                    if (l.contains(e.getPoint())) {
                                        selectedLine = l;
                                    } 
                                    }

                                repaint();
                        }
                }
        });

        this.addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                        super.mousePressed(e);
                        if (!moveMode) {
                                Line l = new Line(e.getPoint());
                                l.path = new Path2D.Double();
                                l.path.moveTo(e.getX(), e.getY());
                                lines.add(l);
                                paths.add(l.path);
                        } else {
                                lastOffsetX = e.getX();
                            lastOffsetY = e.getY();
                        }
                        }

                }

                @Override
                public void mouseReleased(MouseEvent e) {
                        super.mouseReleased(e);
                        if (!resizeMode) {
                            if (selectedLine == null) {
                                Line l = lines.get(lines.size() - 1);
                                l.end = e.getPoint();
                                l.path.lineTo(e.getX(), e.getY());
                                Path2D p = paths.get(paths.size() - 1);
                                p.lineTo(e.getX(), e.getY());
                                repaint();
                            } 
                        } else {

                            for (int j=0; j<paths.size();j++) {
                             if (selectedLine!=null && selectedLine.equals(paths.get(j))) {
                                 paths.set(j, selectedLine);
                             }
                            }

                            repaint();
                        }
                }

                });
    }

        private void setKeyBindings() {
            ActionMap actionMap = getActionMap();
            int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
            InputMap inputMap = getInputMap(condition );
            String ctrl = "VK_CONTROL";
            String ctrl_rel = "control_released";
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, KeyEvent.CTRL_DOWN_MASK, false), ctrl);
            inputMap.put(KeyStroke.getKeyStroke(("released CONTROL")), ctrl_rel);

            actionMap.put(ctrl, new KeyAction(ctrl));
            actionMap.put(ctrl_rel, new KeyAction(ctrl_rel));
    }
        private class KeyAction extends AbstractAction {
            public KeyAction(String actionCommand) {
               putValue(ACTION_COMMAND_KEY, actionCommand);
            }

            @Override
            public void actionPerformed(ActionEvent actionEvt) {
               if(actionEvt.getActionCommand() == "VK_CONTROL") {
                   moveMode = true;
               }
               else if(actionEvt.getActionCommand() == "control_released") {
                   moveMode = false;
                   repaint();
               }

            }
         }

    @Override
    protected void paintComponent(Graphics g) {
         super.paintComponent(g);
         Graphics2D g2d = (Graphics2D)g;
         g2d.setColor(Color.BLACK);
         g2d.setStroke(new BasicStroke(10.0f));
         g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );

         if (moveMode) {
                    for (int j=0; j<paths.size();j++) {
                         Path2D path = paths.get(j);
                         if (selectedLine!=null && selectedLine.equals(path)) {
                             AffineTransform at = new AffineTransform();
                             at.translate(translateX, translateY);
                             g2d.transform(at);
                             g2d.setColor(Color.RED);
                             g2d.draw(path);
                             g2d.setColor(Color.BLACK);
                             continue;

                         }  
                             g2d.draw(path);
                             g2d.setColor(Color.BLACK);

                     }
         } else {
                for (int i =0; i < paths.size();i++) {
                Path2D path = paths.get(i);
                 g2d.draw(path); // outline
                 }
         }
    }

Edit to include resolution: So in the end what I did is I saved all of the path's coordinates (which I got from PathIterator) to and ArrayList, created a new empty path, added the previous path's coordinates from the ArrayList to the new path (via moveTo, lineTo) and appended the new path to the ArrayList of all drawn paths.

Upvotes: 1

Views: 808

Answers (1)

trashgod
trashgod

Reputation: 205855

As you suspect, your AffineTransform alters the graphics context's coordinate system for all subsequent drawing.

In the example cited here, each shape is an instance of the class Node. Each Node includes a selected attribute, which allows the shape to be selected independently. The value determines the effect of updatePosition() when called from mouseDragged(). The implementation of updatePosition() simply updates each selected node's coordinates, but you can also use the createTransformedShape() of AffineTransform.

Upvotes: 4

Related Questions