/** * This file is part of VisiCut. * Copyright (C) 2011 - 2013 Thomas Oster <thomas.oster@rwth-aachen.de> * RWTH Aachen University - 52062 Aachen, Germany * * VisiCut is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VisiCut is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with VisiCut. If not, see <http://www.gnu.org/licenses/>. **/ package com.t_oster.visicut.gui.beans; import com.t_oster.visicut.misc.Helper; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; /** * * @author Thomas Oster <thomas.oster@rwth-aachen.de> */ public class EditRectangle extends Rectangle2D.Double { private double rotationAngle = 0; /** * Get the value of rotationAngle * * @return the value of rotationAngle */ public double getRotationAngle() { return rotationAngle; } /** * Set the value of rotationAngle * * @param rotationAngle new value of rotationAngle */ public void setRotationAngle(double rotationAngle) { this.rotationAngle = rotationAngle; } protected boolean rotateMode = false; /** * Get the value of rotateMode * * @return the value of rotateMode */ public boolean isRotateMode() { return rotateMode; } /** * Set the value of rotateMode * * @param rotateMode new value of rotateMode */ public void setRotateMode(boolean rotateMode) { this.rotateMode = rotateMode; } private int buttonSize = 10; public EditRectangle(double x, double y, double width, double height) { super(x,y,width,height); } public EditRectangle(Rectangle2D r) { super(r.getX(),r.getY(),r.getWidth(),r.getHeight()); } public enum Button { TOP_LEFT, TOP_CENTER, TOP_RIGHT, CENTER_LEFT, CENTER_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT, ROTATE_BUTTON } /** * Returns the Rectangle of the given Button * of an EditRectangle with the given Dimensions * @param b * @return */ private Rectangle2D getButton(Button b, Rectangle2D r, double buttonSize) { switch (b) { case TOP_LEFT: return new Rectangle2D.Double(r.getX() - buttonSize / 2, r.getY() - buttonSize / 2, buttonSize, buttonSize); case TOP_CENTER: return new Rectangle2D.Double(r.getX() + r.getWidth() / 2 - buttonSize / 2, r.getY() - buttonSize / 2, buttonSize, buttonSize); case TOP_RIGHT: return new Rectangle2D.Double(r.getX() + r.getWidth() - buttonSize / 2, r.getY() - buttonSize / 2, buttonSize, buttonSize); case CENTER_LEFT: return new Rectangle2D.Double(r.getX() - buttonSize / 2, r.getY() + r.getHeight() / 2 - buttonSize / 2, buttonSize, buttonSize); case CENTER_RIGHT: return new Rectangle2D.Double(r.getX() + r.getWidth() - buttonSize / 2, r.getY() + r.getHeight() / 2 - buttonSize / 2, buttonSize, buttonSize); case BOTTOM_LEFT: return new Rectangle2D.Double(r.getX() - buttonSize / 2, r.getY() + r.getHeight() - buttonSize / 2, buttonSize, buttonSize); case BOTTOM_CENTER: return new Rectangle2D.Double(r.getX() + r.getWidth() / 2 - buttonSize / 2, r.getY() + r.getHeight() - buttonSize / 2, buttonSize, buttonSize); case BOTTOM_RIGHT: return new Rectangle2D.Double(r.getX() + r.getWidth() - buttonSize / 2, r.getY() + r.getHeight() - buttonSize / 2, buttonSize, buttonSize); } return null; } public Button getButtonByPoint(Point2D.Double p, AffineTransform mm2px) { Rectangle2D tr = this; if (this.rotateMode) { double diagonal = Math.sqrt(tr.getWidth()*tr.getWidth()+tr.getHeight()*tr.getHeight()); Point center = new Point((int) (tr.getX()+tr.getWidth()/2), (int) (tr.getY()+tr.getHeight()/2)); if (p.distance((center.x + Math.cos(rotationAngle) * diagonal/2), (center.y + Math.sin(rotationAngle) * diagonal/2)) <= buttonSize) { return Button.ROTATE_BUTTON; } } else { double bs = this.buttonSize / mm2px.getScaleX(); for (Button b : Button.values()) { if (b != Button.ROTATE_BUTTON && this.getButton(b, tr, bs).contains(p)) { return b; } } } return null; } private Color lineColor = new Color(104,146,255); private Color textColor = Color.BLACK; private Color buttonColor = lineColor; public enum ParameterField { X, Y, WIDTH, HEIGHT, ANGLE } private Rectangle[] parameterFieldBounds = new Rectangle[]{ new Rectangle(), new Rectangle(), new Rectangle(), new Rectangle(), new Rectangle() }; /** * Returns the Bounds of the textFields * displaying the given parameter * in Panel Coordinates * @param pmf * @return */ public Rectangle getParameterFieldBounds(ParameterField pmf) { switch (pmf) { case X: return parameterFieldBounds[0]; case Y: return parameterFieldBounds[1]; case WIDTH: return parameterFieldBounds[2]; case HEIGHT: return parameterFieldBounds[3]; case ANGLE: return parameterFieldBounds[4]; } return null; } /** * Renders the Edit Rectangle on the given Graphics2D. * The Rectangle coordinates are transformed according * the current Transform in the graphics. The Stroke width * and Button size, however are always in pixel. * @param gg * @param transform */ public void render(Graphics2D gg, AffineTransform mm2px, boolean full) { //draw the rectangle gg.setColor(lineColor); gg.setStroke(new BasicStroke(full ? 2 : 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] { 10, 10 }, 0)); Rectangle tr = Helper.toRect(Helper.transform(this, mm2px)); if (this.rotateMode) { double diagonal = Math.sqrt(tr.width*tr.width+tr.height*tr.height); Point center = new Point((int) (tr.x+tr.width/2d), (int) (tr.y+tr.height/2d)); //draw a circle around the object gg.drawOval((int) (center.x-diagonal/2), (int) (center.y-diagonal/2), (int) diagonal, (int) diagonal); gg.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 0, null, 0)); //draw the rotate button on the circle gg.drawOval((int) (center.x + Math.cos(rotationAngle) * diagonal/2 - buttonSize/2), (int) (center.y + Math.sin(rotationAngle) * diagonal/2 -buttonSize/2), buttonSize, buttonSize); //draw the angle gg.setColor(textColor); int w = (int) Math.round(10*Helper.angle2degree(this.rotationAngle)); String txt = (w/10)+","+(w%10)+"°"; w = gg.getFontMetrics().stringWidth(txt); int ascend = gg.getFontMetrics().getAscent(); int h = gg.getFontMetrics().getHeight(); if (full) { gg.drawString(txt, 10+(int) (center.x+diagonal/2), center.y+h/2); } this.parameterFieldBounds[4].setBounds(10+(int) (center.x+diagonal/2), center.y+h/2-ascend, w, h); } else { gg.drawRect(tr.x, tr.y, tr.width, tr.height); if (full) { //draw the corner buttons gg.setColor(buttonColor); for (Button b : Button.values()) { Rectangle r = Helper.toRect(this.getButton(b,tr,this.buttonSize)); gg.fillRect(r.x, r.y, r.width, r.height); } } //draw the width gg.setColor(textColor); int w = (int) Math.round(this.width); String txt = (w/10)+","+(w%10)+" cm"; w = gg.getFontMetrics().stringWidth(txt); int ascend = gg.getFontMetrics().getAscent(); int h = gg.getFontMetrics().getHeight(); if (full) { gg.drawString(txt, tr.x+tr.width/2-w/2, tr.y+tr.height+h); } this.parameterFieldBounds[2].setBounds(tr.x+tr.width/2-w/2, tr.y+tr.height+h-ascend, w, h); //draw the height w = (int) Math.round(this.height); txt = (w/10)+","+(w%10)+" cm"; w = gg.getFontMetrics().stringWidth(txt); if (full) { gg.drawString(txt, tr.x+tr.width+5, tr.y+tr.height/2); } this.parameterFieldBounds[3].setBounds(tr.x+tr.width+5, tr.y+tr.height/2-ascend, w, h); //draw lines from the left and upper center gg.setColor(lineColor); Point zero = new Point(0, 0); if (full) { gg.drawLine(zero.x, tr.y+tr.height/2, tr.x, tr.y+tr.height/2); gg.drawLine(tr.x+tr.width/2, zero.y, tr.x+tr.width/2, tr.y); } //draw the left gg.setColor(textColor); w = (int) Math.round(this.x); txt = (w/10)+","+(w%10)+" cm"; w = gg.getFontMetrics().stringWidth(txt); h = gg.getFontMetrics().getHeight(); if (full) { gg.drawString(txt, tr.x-w-10, tr.y+tr.height/2+h); } this.parameterFieldBounds[0].setBounds(tr.x-w-10, tr.y+tr.height/2+h-ascend, w, h); //draw the top offset w = (int) Math.round(this.y); txt = (w/10)+","+(w%10)+" cm"; w = gg.getFontMetrics().stringWidth(txt); if (full) { gg.drawString(txt, tr.x+tr.width/2+5, tr.y-h); } this.parameterFieldBounds[1].setBounds(tr.x+tr.width/2+5, tr.y-h-ascend, w, h); } } @Override public EditRectangle clone() { return new EditRectangle(x, y, width, height); } }