Java tutorial
/* Copyright (c) 2010, Carl Burch. License information is located in the * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */ package com.cburch.draw.shapes; import java.awt.Graphics; import java.util.Arrays; import java.util.List; import org.apache.commons.collections15.list.UnmodifiableList; import com.cburch.draw.model.CanvasObject; import com.cburch.draw.model.Handle; import com.cburch.draw.model.HandleGesture; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Location; abstract class Rectangular extends FillableCanvasObject { private Bounds bounds; // excluding the stroke's width public Rectangular(int x, int y, int w, int h) { bounds = Bounds.create(x, y, w, h); } @Override public boolean matches(CanvasObject other) { if (other instanceof Rectangular) { Rectangular that = (Rectangular) other; return this.bounds.equals(that.bounds) && super.matches(that); } else { return false; } } @Override public int matchesHashCode() { return bounds.hashCode() * 31 + super.matchesHashCode(); } public int getX() { return bounds.getX(); } public int getY() { return bounds.getY(); } public int getWidth() { return bounds.getWidth(); } public int getHeight() { return bounds.getHeight(); } @Override public Bounds getBounds() { int wid = getStrokeWidth(); Object type = getPaintType(); if (wid < 2 || type == DrawAttr.PAINT_FILL) { return bounds; } else { return bounds.expand(wid / 2); } } @Override public void translate(int dx, int dy) { bounds = bounds.translate(dx, dy); } @Override public List<Handle> getHandles(HandleGesture gesture) { return UnmodifiableList.decorate(Arrays.asList(getHandleArray(gesture))); } private Handle[] getHandleArray(HandleGesture gesture) { Bounds bds = bounds; int x0 = bds.getX(); int y0 = bds.getY(); int x1 = x0 + bds.getWidth(); int y1 = y0 + bds.getHeight(); if (gesture == null) { return new Handle[] { new Handle(this, x0, y0), new Handle(this, x1, y0), new Handle(this, x1, y1), new Handle(this, x0, y1) }; } else { int hx = gesture.getHandle().getX(); int hy = gesture.getHandle().getY(); int dx = gesture.getDeltaX(); int dy = gesture.getDeltaY(); int newX0 = x0 == hx ? x0 + dx : x0; int newY0 = y0 == hy ? y0 + dy : y0; int newX1 = x1 == hx ? x1 + dx : x1; int newY1 = y1 == hy ? y1 + dy : y1; if (gesture.isShiftDown()) { if (gesture.isAltDown()) { if (x0 == hx) newX1 -= dx; if (x1 == hx) newX0 -= dx; if (y0 == hy) newY1 -= dy; if (y1 == hy) newY0 -= dy; int w = Math.abs(newX1 - newX0); int h = Math.abs(newY1 - newY0); if (w > h) { // reduce width to h int dw = (w - h) / 2; newX0 -= (newX0 > newX1 ? 1 : -1) * dw; newX1 -= (newX1 > newX0 ? 1 : -1) * dw; } else { int dh = (h - w) / 2; newY0 -= (newY0 > newY1 ? 1 : -1) * dh; newY1 -= (newY1 > newY0 ? 1 : -1) * dh; } } else { int w = Math.abs(newX1 - newX0); int h = Math.abs(newY1 - newY0); if (w > h) { // reduce width to h if (x0 == hx) { newX0 = newX1 + (newX0 > newX1 ? 1 : -1) * h; } if (x1 == hx) { newX1 = newX0 + (newX1 > newX0 ? 1 : -1) * h; } } else { // reduce height to w if (y0 == hy) { newY0 = newY1 + (newY0 > newY1 ? 1 : -1) * w; } if (y1 == hy) { newY1 = newY0 + (newY1 > newY0 ? 1 : -1) * w; } } } } else { if (gesture.isAltDown()) { if (x0 == hx) newX1 -= dx; if (x1 == hx) newX0 -= dx; if (y0 == hy) newY1 -= dy; if (y1 == hy) newY0 -= dy; } else { ; // already handled } } return new Handle[] { new Handle(this, newX0, newY0), new Handle(this, newX1, newY0), new Handle(this, newX1, newY1), new Handle(this, newX0, newY1) }; } } @Override public boolean canMoveHandle(Handle handle) { return true; } @Override public Handle moveHandle(HandleGesture gesture) { Handle[] oldHandles = getHandleArray(null); Handle[] newHandles = getHandleArray(gesture); Handle moved = gesture == null ? null : gesture.getHandle(); Handle result = null; int x0 = Integer.MAX_VALUE; int x1 = Integer.MIN_VALUE; int y0 = Integer.MAX_VALUE; int y1 = Integer.MIN_VALUE; int i = -1; for (Handle h : newHandles) { i++; if (oldHandles[i].equals(moved)) { result = h; } int hx = h.getX(); int hy = h.getY(); if (hx < x0) x0 = hx; if (hx > x1) x1 = hx; if (hy < y0) y0 = hy; if (hy > y1) y1 = hy; } bounds = Bounds.create(x0, y0, x1 - x0, y1 - y0); return result; } @Override public void paint(Graphics g, HandleGesture gesture) { if (gesture == null) { Bounds bds = bounds; draw(g, bds.getX(), bds.getY(), bds.getWidth(), bds.getHeight()); } else { Handle[] handles = getHandleArray(gesture); Handle p0 = handles[0]; Handle p1 = handles[2]; int x0 = p0.getX(); int y0 = p0.getY(); int x1 = p1.getX(); int y1 = p1.getY(); if (x1 < x0) { int t = x0; x0 = x1; x1 = t; } if (y1 < y0) { int t = y0; y0 = y1; y1 = t; } draw(g, x0, y0, x1 - x0, y1 - y0); } } @Override public boolean contains(Location loc, boolean assumeFilled) { Object type = getPaintType(); if (assumeFilled && type == DrawAttr.PAINT_STROKE) { type = DrawAttr.PAINT_STROKE_FILL; } Bounds b = bounds; int x = b.getX(); int y = b.getY(); int w = b.getWidth(); int h = b.getHeight(); int qx = loc.getX(); int qy = loc.getY(); if (type == DrawAttr.PAINT_FILL) { return isInRect(qx, qy, x, y, w, h) && contains(x, y, w, h, loc); } else if (type == DrawAttr.PAINT_STROKE) { int stroke = getStrokeWidth(); int tol2 = Math.max(2 * Line.ON_LINE_THRESH, stroke); int tol = tol2 / 2; return isInRect(qx, qy, x - tol, y - tol, w + tol2, h + tol2) && contains(x - tol, y - tol, w + tol2, h + tol2, loc) && !contains(x + tol, y + tol, w - tol2, h - tol2, loc); } else if (type == DrawAttr.PAINT_STROKE_FILL) { int stroke = getStrokeWidth(); int tol2 = stroke; int tol = tol2 / 2; return isInRect(qx, qy, x - tol, y - tol, w + tol2, h + tol2) && contains(x - tol, y - tol, w + tol2, h + tol2, loc); } else { return false; } } boolean isInRect(int qx, int qy, int x0, int y0, int w, int h) { return qx >= x0 && qx < x0 + w && qy >= y0 && qy < y0 + h; } protected abstract boolean contains(int x, int y, int w, int h, Location q); protected abstract void draw(Graphics g, int x, int y, int w, int h); }