package org.geogebra.common.euclidian; import java.util.ArrayList; import org.geogebra.common.awt.GBasicStroke; import org.geogebra.common.awt.GColor; import org.geogebra.common.awt.GEllipse2DDouble; import org.geogebra.common.awt.GGraphics2D; import org.geogebra.common.awt.GRectangle2D; import org.geogebra.common.factories.AwtFactory; /** * bounding box construction for selected elements * * @author csilla * */ public class BoundingBox { private GRectangle2D rectangle; private ArrayList<GEllipse2DDouble> handlers; private int nrHandlers = 8; /** * Make new bounding box */ public BoundingBox() { handlers = new ArrayList<GEllipse2DDouble>(); } /** * @return box part of bounding box construction */ public GRectangle2D getRectangle() { return rectangle; } /** * @param rectangle * - box part of bounding box construction */ public void setRectangle(GRectangle2D rectangle) { this.rectangle = rectangle; if (rectangle != null) { createHandlers(); } } /** * @return handler points of bounding box construction */ public ArrayList<GEllipse2DDouble> getHandlers() { return handlers; } /** * @param handlers * - points of bounding box construction */ public void setHandlers(ArrayList<GEllipse2DDouble> handlers) { this.handlers = handlers; } /** * @return number of needed handlers */ public int getNrHandlers() { return nrHandlers; } /** * @param nrHandlers * - number of handlers */ public void setNrHandlers(int nrHandlers) { this.nrHandlers = nrHandlers; } private void createHandlers() { if (handlers == null) { handlers = new ArrayList<GEllipse2DDouble>(); } handlers.clear(); // init handler list for (int i = 0; i < /* = */nrHandlers; i++) { GEllipse2DDouble handler = AwtFactory.getPrototype() .newEllipse2DDouble(); handlers.add(handler); } if (nrHandlers == 8) { // corner handlers handlers.get(0).setFrameFromCenter(rectangle.getX(), rectangle.getY(), rectangle.getX() + 5, rectangle.getY() + 5); handlers.get(1).setFrameFromCenter(rectangle.getX(), rectangle.getMaxY(), rectangle.getX() + 5, rectangle.getMaxY() + 5); handlers.get(2).setFrameFromCenter( rectangle.getMaxX(), rectangle.getMaxY(), rectangle.getMaxX() + 5, rectangle.getMaxY() + 5); handlers.get(3).setFrameFromCenter( rectangle.getMaxX(), rectangle.getY(), rectangle.getMaxX() + 5, rectangle.getY() + 5); // side handlers // top handlers.get(4).setFrameFromCenter( (rectangle.getMinX() + rectangle.getMaxX()) / 2, rectangle.getMinY(), (rectangle.getMinX() + rectangle.getMaxX()) / 2 + 5, rectangle.getMinY() + 5); // left handlers.get(5).setFrameFromCenter(rectangle.getMinX(), (rectangle.getMinY() + rectangle.getMaxY()) / 2, rectangle.getMinX() + 5, (rectangle.getMinY() + rectangle.getMaxY()) / 2 + 5); // bottom handlers.get(6).setFrameFromCenter( (rectangle.getMinX() + rectangle.getMaxX()) / 2, rectangle.getMaxY(), (rectangle.getMinX() + rectangle.getMaxX()) / 2 + 5, rectangle.getMaxY() + 5); // right handlers.get(7).setFrameFromCenter(rectangle.getMaxX(), (rectangle.getMinY() + rectangle.getMaxY()) / 2, rectangle.getMaxX() + 5, (rectangle.getMinY() + rectangle.getMaxY()) / 2 + 5); // handler for rotation // handlers.get(8).setFrameFromCenter( // (rectangle.getMinX() + rectangle.getMaxX()) / 2, // rectangle.getMaxY() + 15, // (rectangle.getMinX() + rectangle.getMaxX()) / 2 + 3, // rectangle.getMaxY() + 15 + 3); } } /** * method to draw the bounding box construction for selected geo * * @param g2 * - graphics */ public void draw(GGraphics2D g2) { // draw bounding box if (rectangle != null) { g2.setColor(GColor.newColor(192, 192, 192, 0.0)); g2.setStroke(AwtFactory.getPrototype().newBasicStroke(2.0f, GBasicStroke.CAP_BUTT, GBasicStroke.JOIN_MITER)); g2.fill(rectangle); g2.setColor(GColor.MOW_PURPLE); g2.draw(rectangle); } if (handlers != null && !handlers.isEmpty()) { // join rotation handler and bounding box // GLine2D line = AwtFactory.getPrototype().newLine2D(); // line.setLine((rectangle.getMinX() + rectangle.getMaxX()) / 2, // rectangle.getMaxY(), // (rectangle.getMinX() + rectangle.getMaxX()) / 2, // rectangle.getMaxY() + 15); // g2.setColor(GColor.GEOGEBRA_GRAY); // g2.draw(line); for (int i = 0; i < /* = */nrHandlers; i++) { g2.setPaint(GColor.MOW_PURPLE); g2.fill(handlers.get(i)); g2.setStroke(AwtFactory.getPrototype().newBasicStroke(2.0f, GBasicStroke.CAP_BUTT, GBasicStroke.JOIN_MITER)); g2.setColor(GColor.GEOGEBRA_GRAY); g2.draw(handlers.get(i)); } } } /** * reset the parts of bounding box construction */ public void resetBoundingBox() { rectangle = null; handlers.clear(); } /** * * @param threshold * controller threshold * @return distance threshold to select a point */ public static final int getSelectionThreshold(int threshold) { return threshold + 12; } /** * @param x * - mouse event x * @param y * - mouse event y * @param hitThreshold * - threshold * @return number of handler which was hit */ public int hitHandlers(int x, int y, int hitThreshold) { int index = -1; if (!handlers.isEmpty()) { for (int i = 0; i < handlers.size(); i++) { GEllipse2DDouble point = handlers.get(i); int r = getSelectionThreshold(hitThreshold); double dx = point.getBounds().getX() + point.getBounds().getWidth() / 2 - x; double dy = point.getBounds().getY() + point.getBounds().getHeight() / 2 - y; if (dx < r && dx > -r && dx * dx + dy * dy <= r * r) { return i; } } } return index; } /** * @param x * - x coord of hit * @param y * - y coord og hit * @param hitThreshold * - threshold * @return true if hits any side of boundingBox */ public boolean hitSideOfBoundingBox(int x, int y, int hitThreshold) { if (rectangle == null) { return false; } return // left side onSegment(rectangle.getMinX(), rectangle.getMinY(), x, y, rectangle.getMinX(), rectangle.getMaxY(), hitThreshold) // top side || onSegment(rectangle.getMinX(), rectangle.getMinY(), x, y, rectangle.getMaxX(), rectangle.getMinY(), hitThreshold) // bottom side || onSegment(rectangle.getMinX(), rectangle.getMaxY(), x, y, rectangle.getMaxX(), rectangle.getMaxY(), hitThreshold) // right side || onSegment(rectangle.getMaxX(), rectangle.getMinY(), x, y, rectangle.getMaxX(), rectangle.getMaxY(), hitThreshold); } // check if intersection point is on segment private static boolean onSegment(double segStartX, double segStartY, int hitX, int hitY, double segEndX, double segEndY, int hitThreshold) { if (hitX <= Math.max(segStartX, segEndX) + 2 * hitThreshold && hitX >= Math.min(segStartX, segEndX) - 2 * hitThreshold && hitY <= Math.max(segStartY, segEndY) + 2 * hitThreshold && hitY >= Math.min(segStartY, segEndY) - 2 * hitThreshold) { return true; } return false; } }