import java.awt.*;
import java.util.*;
/**
*
*/
/**
* @author Matt Chun-Lum
*
*/
public abstract class DShape implements ModelListener {
public static final int KNOB_SIZE = 9;
public static final Color KNOB_COLOR = Color.black;
public static Rectangle getBigBoundsForModel(DShapeModel model) {
Rectangle bounds = model.getBounds();
return new Rectangle(bounds.x - KNOB_SIZE/2, bounds.y - KNOB_SIZE/2, bounds.width + KNOB_SIZE, bounds.height + KNOB_SIZE);
}
protected DShapeModel model;
protected Canvas canvas;
protected Rectangle lastBounds;
protected ArrayList<Point> knobs;
protected boolean needsRecomputeKnobs;
public DShape(DShapeModel model, Canvas canvas) {
this.model = model;
this.canvas = canvas;
lastBounds = new Rectangle(getBounds());
knobs = null;
needsRecomputeKnobs = false;
model.addListener(this);
}
public void move(int dx, int dy) {
needsRecomputeKnobs = true;
model.move(dx, dy);
}
/**
* Give the ID of the model
* @return
*/
public int getModelID() {
return model.getID();
}
/**
* @return the bounds of the shape
*/
public Rectangle getBounds() {
return model.getBounds();
}
/**
* @return the bounds including knobs
*/
public Rectangle getBigBounds() {
return getBigBoundsForModel(model);
}
/**
* @return the bounds of the last position including knobs
*/
public Rectangle getBigBoundsOfLastPosition() {
return new Rectangle(lastBounds.x - KNOB_SIZE/2, lastBounds.y - KNOB_SIZE/2, lastBounds.width + KNOB_SIZE, lastBounds.height + KNOB_SIZE);
}
public void modifyShapeWithPoints(Point anchorPoint, Point movingPoint) {
needsRecomputeKnobs = true;
model.modifyWithPoints(anchorPoint, movingPoint);
}
/**
* Tests if the shape contains the given point
* @param pt
* @return true if the shape contains the given point
*/
public boolean containsPoint(Point pt) {
Rectangle bounds = getBounds();
if(bounds.contains(pt))
return true;
// Handle conditions where the shape has width or height 0
if(bounds.width == 0
&& Math.abs(pt.x - bounds.x) <= 3
&& pt.y <= bounds.y + bounds.height
&& pt.y >= bounds.y)
return true;
if(bounds.height == 0
&& Math.abs(pt.y - bounds.y) <= 3
&& pt.x >= bounds.x
&& pt.x <= bounds.x + bounds.width)
return true;
return false;
}
/**
* gets the color of the underlying model
* @return
*/
public Color getColor() {
return model.getColor();
}
/**
* Sets the color of the shape's model
* @param color
*/
public void setColor(Color color) {
model.setColor(color);
}
/**
* Should override if the shape is only described by two knobs
* @return an ArrayList of points representing the "knobs"
*/
public ArrayList<Point> getKnobs() {
if(knobs == null || needsRecomputeKnobs) {
knobs = new ArrayList<Point>();
Rectangle bounds = model.getBounds();
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
knobs.add(new Point(bounds.x + bounds.width * i, bounds.y + bounds.height * j));
// Very 4-point specific, orders the knobs to make it easier to compute an anchor
Point temp = knobs.remove(2);
knobs.add(temp);
needsRecomputeKnobs = false;
}
return knobs;
}
/**
* Determines if a click falls on a knob
* @param click
* @param knobCenter
* @return
*/
public boolean selectedKnob(Point click, Point knobCenter) {
Rectangle knob = new Rectangle(knobCenter.x - KNOB_SIZE/2, knobCenter.y - KNOB_SIZE/2, KNOB_SIZE, KNOB_SIZE);
return knob.contains(click);
}
/**
* Computes the anchor point for a given knob
* @param pt
* @return a copy of the point for the anchor knob
*/
public Point getAnchorForSelectedKnob(Point pt) {
int index = getKnobs().indexOf(pt);
return new Point(knobs.get((index + knobs.size()/2) % knobs.size()));
}
/**
* If the model has changed, update the canvas
*/
public void modelChanged(DShapeModel model) {
if(this.model == model) {
if(model.markedForRemoval()) {
canvas.removeShape(this);
//this.model = null;
return;
}
canvas.repaintShape(this);
if(!lastBounds.equals(getBounds())) {
canvas.repaintArea(getBigBoundsOfLastPosition());
lastBounds = new Rectangle(getBounds());
}
}
}
/**
* Instruct the shape to tell its model that its marked for removal
*/
public void markForRemoval() {
model.markForRemoval();
}
// ------------- Protected Methods -------------- //
protected void drawKnobs(Graphics g) {
g.setColor(KNOB_COLOR);
for(Point point : getKnobs())
g.fillRect(point.x - KNOB_SIZE/2, point.y - KNOB_SIZE/2, KNOB_SIZE, KNOB_SIZE);
}
// ------------ Abstract Methods -------------- //
abstract public DShapeModel getModel();
abstract public void draw(Graphics g, boolean selected);
}