# 1
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
public class MSFRx extends JPanel {
Rectangle crib;
List<Rectangle> rects;
int[] limits;
final int S = 25;
final int PAD = 3;
final int MARGIN = 40;
public MSFRx() {
Dimension d = getPreferredSize();
crib = new Rectangle(MARGIN, MARGIN, d.width-2*MARGIN, d.height-2*MARGIN);
rects = new ArrayList<Rectangle>();
initLimits();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(Color.green.darker());
g2.draw(crib);
g2.setPaint(Color.blue);
for(Rectangle r : rects)
g2.draw(r);
}
public void setRect(Rectangle r, int x, int y) {
r.setLocation(x, y);
repaint();
}
public void addRect() {
int x = (int)(crib.getCenterX() - S/2.0);
int y = crib.y + 1;
Rectangle r = new Rectangle(x, y, S, S);
Point nextPos = getNextPosition(r);
if(nextPos != null) {
r.setLocation(nextPos);
rects.add(r);
}
repaint();
}
private Point getNextPosition(Rectangle r) {
int w = crib.width;
int h = crib.height;
int xPos = crib.x + w/2, yPos = crib.y + 1;
for(int j = 0; j < rects.size()+1; j++) {
if(j < limits[0]) {
xPos = crib.x + w/2 + j*(S + PAD);
yPos = crib.y + 1;
} else if(j < limits[1]) {
xPos = crib.x + w - S -1;
yPos = crib.y + 1 + (j + 1 - limits[0])*(S + PAD);
} else if(j < limits[2]) {
xPos = crib.x + w - (j + 2 - limits[1])*(S + PAD);
yPos = crib.y + h - S - 1;
} else if(j < limits[3]) {
xPos = crib.x + 1;
yPos = crib.y + h - (j + 2 - limits[2])*(S + PAD);
} else if(j < limits[4]) {
xPos = crib.x + 1 + (j + 1 - limits[3])*(S + PAD);
yPos = crib.y + 1;
} else {
System.out.println("crib is full");
return null;
}
}
return new Point(xPos, yPos);
}
public Dimension getPreferredSize() {
return new Dimension(400,400);
}
private void initLimits() {
int w = crib.width;
int h = crib.height;
int[] numRects = new int[5];
// top middle to top right corner
numRects[0] = (w/2)/(S + PAD);
// right side: top to bottom
numRects[1] = (h - S - PAD)/(S + PAD);
// bottom: right to left
numRects[2] = (w - S - PAD)/(S + PAD);
// left: bottom to top
numRects[3] = (h - S + PAD)/(S + PAD);
// top: left corner to middle
numRects[4] = (w/2 - S - PAD - 1)/(S + PAD);
limits = new int[numRects.length];
for(int j = 0, total = 0; j < limits.length; j++) {
total += numRects[j];
limits[j] = total;
}
//System.out.printf("numRects = %s%nlimits = %s%n",
//Arrays.toString(numRects), Arrays.toString(limits));
}
public static void main(String[] args) {
MSFRx test = new MSFRx();
RectangleWrangler rw = new RectangleWrangler(test);
test.addMouseListener(rw);
test.addMouseMotionListener(rw);
JFrame f = new JFrame("Click in green to add rectangles");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(test);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class RectangleWrangler extends MouseInputAdapter {
MSFRx component;
Rectangle selectedRect = null;
Point offset = new Point();
boolean dragging = false;
public RectangleWrangler(MSFRx msfrx) {
component = msfrx;
}
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
List<Rectangle> list = component.rects;
// Are we selecting an existing rectangle?
for(int j = 0; j < list.size(); j++) {
Rectangle r = list.get(j);
if(r.contains(p)) {
selectedRect = r;
offset.x = p.x - r.x;
offset.y = p.y - r.y;
dragging = true;
break;
}
}
// Or are we adding a new rectangle to the crib?
if(selectedRect == null && component.crib.contains(p))
component.addRect();
}
public void mouseReleased(MouseEvent e) {
dragging = false;
selectedRect = null;
}
public void mouseDragged(MouseEvent e) {
if(dragging) {
Point p = e.getPoint();
double cx = selectedRect.getCenterX();
double cy = selectedRect.getCenterY();
Rectangle crib = component.crib;
int S = component.S;
boolean top= cy - crib.y < S;
boolean left= cx - crib.x < S;
boolean bottom = crib.y + crib.height - cy < S;
boolean right = crib.x + crib.width - cx < S;
boolean horizontal = top || bottom;
boolean vertical= left || right;
if(horizontal && vertical) {
if(top && left) {
// Check boundries.
if(selectedRect.x - crib.x < 0) selectedRect.x = crib.x -1;
if(selectedRect.y - crib.y < 0) selectedRect.y = crib.y +1;
// Choose direction according to larger displacment.
if(Math.abs(p.x - offset.x) <= Math.abs(p.y - offset.y))
horizontal = false;
else
vertical = false;
} else if(left && bottom) {
if(selectedRect.x - crib.x < 0) selectedRect.x = crib.x +1;
if(crib.y + crib.height - selectedRect.y < S)
selectedRect.y = crib.y + crib.height - S -1;
if(Math.abs(p.x - crib.x) < Math.abs(p.y - crib.y - crib.height))
horizontal = false;
else
vertical = false;
} else if(bottom && right) {
if(crib.x + crib.width - selectedRect.x + S < 0)
selectedRect.x = crib.x + crib.width - S -1;
if(crib.y + crib.height - selectedRect.y + S < 0)
selectedRect.y = crib.y + crib.height - S -1;
if(Math.abs(p.x - crib.x - crib.width) <=
Math.abs(p.y - crib.y - crib.height))
horizontal = false;
else
vertical = false;
} else if(right && top) {
if(crib.x + crib.width - selectedRect.x + S < 0)
selectedRect.x = crib.x + crib.width - S -1;
if(selectedRect.y - crib.y < 0) selectedRect.y = crib.y +1;
if(Math.abs(p.x - crib.x - crib.width) <= Math.abs(p.y - crib.y))
horizontal = false;
else
vertical = false;
}
}
int x = p.x - offset.x;
int y = p.y - offset.y;
// Make the rectangle follow the line.
if(horizontal) {
if(top) {
y = crib.x + 1;
} else if(bottom) {
y = crib.y + crib.height - S -1;
}
} else if(vertical) {
if(left) {
x = crib.x + 1;
} else if(right) {
x = crib.x + crib.width - S -1;
}
}
component.setRect(selectedRect, x, y);
}
}
}