

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
 
public class Resizing extends JPanel {
    Rectangle[] rects;
 
    public void setRect(Rectangle r, int x, int y) {
        r.setLocation(x, y);
        repaint();
    }
 
    public void resizeRect(Rectangle r, int[] changes) {
        int x = r.x + changes[0];
        int y = r.y + changes[1];
        int w = r.width  + changes[2] - changes[0];
        int h = r.height + changes[3] - changes[1];
        r.setFrame(x, y, w, h);
        repaint();
    }
 
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        if(rects == null)
            initRects();
        g2.setPaint(Color.blue);
        for(int j = 0; j < rects.length; j++)
            g2.draw(rects[j]);
    }
 
    private void initRects() {
        int w = getWidth();
        int h = getHeight();
        rects = new Rectangle[2];
        rects[0] = new Rectangle(w/3, h/8, w/3, h*3/8);
        rects[1] = new Rectangle(w/2, h*3/4, w/3, h/16);
    }
 
    public static void main(String[] args) {
        Resizing resizing = new Resizing();
        RectangleController controller =
                new RectangleController(resizing, new Resizer());
        resizing.addMouseListener(controller);
        resizing.addMouseMotionListener(controller);
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(resizing);
        f.setSize(500,400);
        f.setLocation(200,200);
        f.setVisible(true);
    }
}
 
class RectangleController extends MouseInputAdapter {
    Resizing resizeComponent;
    Resizer resizer;
    Rectangle selectedRect;
    Point offset = new Point();
    boolean resizing = false;
    boolean dragging = false;
 
    public RectangleController(Resizing rc, Resizer r) {
        resizeComponent = rc;
        resizer = r;
    }
 
    public void mousePressed(MouseEvent e) {
        Point p = e.getPoint();
        Rectangle[] rects = resizeComponent.rects;
        // Are we selecting a rectangle side to resize?
        for(int j = 0; j < rects.length; j++) {
            if(resizer.isSideSelected(p, rects[j])) {
                resizing = true;
                selectedRect = resizer.rect;
                break;
            }
        }
        // or, are we selecting a rectangle to drag?
        if(!resizing) {
            for(int j = 0; j < rects.length; j++) {
                if(rects[j].contains(p)) {
                    offset.x = p.x - rects[j].x;
                    offset.y = p.y - rects[j].y;
                    selectedRect = rects[j];
                    dragging = true;
                    break;
                }
            }
        }
    }
 
    public void mouseReleased(MouseEvent e) {
        resizing = false;
        dragging = false;
        resizer.reset();
    }
 
    public void mouseDragged(MouseEvent e) {
        Point p = e.getPoint();
        if(resizing) {
            int[] deltas = resizer.getResizeData(p);
            resizeComponent.resizeRect(selectedRect, deltas);
        } else if(dragging) {
            int x = p.x - offset.x;
            int y = p.y - offset.y;
            resizeComponent.setRect(selectedRect, x, y);
        }
    }
}
 
class Resizer {
    Rectangle rect;
    int selectedIndex = -1;
    final int PROXIMITY = 3;
    // PathIterator travels clockwise.
    final int TOP    = 0;
    final int RIGHT  = 1;
    final int BOTTOM = 2;
    final int LEFT   = 3;
 
    public boolean isSideSelected(Point p, Rectangle r) {
        rect = r;
        PathIterator pit = r.getPathIterator(null);
        double[] coords = new double[6];
        int side = 0;
        Point2D.Double start = new Point2D.Double();
        boolean haveLine = false;
        while(!pit.isDone()) {
            int type = pit.currentSegment(coords);
            switch(type) {
                case PathIterator.SEG_MOVETO:
                    start.x = coords[0];
                    start.y = coords[1];
                    break;
                case PathIterator.SEG_LINETO:
                    haveLine = true;
                    break;
                case PathIterator.SEG_CLOSE:
                    haveLine = false;
                    break;
                default:
                    System.out.println("Unexpected type: " + type);
            }
            if(haveLine) {
                if(isCloseToLine(p, start, coords[0], coords[1])) {
                    selectedIndex = side;
                    break;
                }
                start.x = coords[0];
                start.y = coords[1];
                side++;
            }
            pit.next();
        }
        return selectedIndex != -1;
    }
 
    private boolean isCloseToLine(Point p, Point2D.Double start,
                                           double endX, double endY) {
        boolean isClose = false;
        boolean isAdjacent = false;
        if(start.x == endX) {
            isClose = Math.abs(p.x-start.x) <= PROXIMITY;
            isAdjacent = (start.y < p.y && p.y < endY) ||
                         (endY < p.y && p.y < start.y);
        }
        else if(start.y == endY) {
            isClose = Math.abs(p.y-start.y) <= PROXIMITY;
            isAdjacent = (start.x < p.x && p.x < endX) ||
                         (endX < p.x && p.x < start.x);
        }
        return isClose & isAdjacent;
    }
 
    public int[] getResizeData(Point p) {
        // [x, y, width, height]
        int[] changes = new int[4];
        switch(selectedIndex) {
            case TOP:
                changes[1] = p.y - rect.y;
                break;
            case LEFT:
                changes[0] = p.x - rect.x;
                break;
            case BOTTOM:
                changes[3] = p.y - (rect.y + rect.height);
                break;
            case RIGHT:
                changes[2] = p.x - (rect.x + rect.width);
                break;
        }
        return changes;
    }
 
    public void reset() {
        rect = null;
        selectedIndex = -1;
    }
}

