/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
package org.opensourcephysics.display;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.text.DecimalFormat;
/**
* A Protractor with an arrow that can be used to measure angles.
*
* @author W. Christian
* @version 1.0
*/
public class Protractor extends InteractiveCircle implements Drawable {
static final double PI2 = Math.PI*2;
static final String thetaStr = "$\\theta$="; //$NON-NLS-1$
int protractorRadius, protractorRadius2, arrowLengthPix;
protected Tip tip = new Tip();
protected double arrowTheta = 0, orientation = 0;
protected DecimalFormat f = new DecimalFormat("000"); //$NON-NLS-1$
protected boolean showTheta = false;
protected InteractiveLabel tauBox = new InteractiveLabel(thetaStr+f.format(getTheta()));
/**
* Constructs a protractor with the given pixel size.
*
* @param protractorRadius int
*/
public Protractor(int protractorRadius) {
this.protractorRadius = protractorRadius;
protractorRadius2 = protractorRadius*2;
arrowLengthPix = protractorRadius;
tip.color = Color.BLUE;
tauBox.setOffsetX(-20);
tauBox.setOffsetY(5);
}
/**
* Constructs a protractor with a default radius of 40 pixels.
*/
public Protractor() {
this(40);
}
/**
* Sets the angle of the arrow on the protractor.
* @param theta double
*/
public void setTheta(double angle) {
this.arrowTheta = angle+orientation;
}
/**
* Gets the angle of the arrow on the protractor.
* @return double
*/
public double getTheta() {
// return PBC.separation(arrowTheta-orientation,Math.PI*2);
// inline the method for efficiency and to decouple from the numerics package
double dr = arrowTheta-orientation;
return dr-PI2*Math.floor(dr/PI2+0.5);
}
/**
* Sets the orientation of the protractor.
* @param angle double
*/
public void setOrientation(double angle) {
this.orientation = angle;
}
/**
* Gets the orientation of the protractor.
* @return double
*/
public double getOrientation() {
return orientation;
}
/**
* Shows theta when the protractor is drawn when true.
* @param show boolean
*/
public void setShowTheta(boolean show) {
showTheta = show;
}
/**
* Gets the show theta property.
* @return boolean
*/
public boolean isShowTheta() {
return showTheta;
}
public Interactive findInteractive(DrawingPanel panel, int xpix, int ypix) {
Interactive interactive = super.findInteractive(panel, xpix, ypix);
if(interactive!=null) {
return interactive;
}
interactive = tip.findInteractive(panel, xpix, ypix);
if(interactive!=null) {
return interactive;
}
return tauBox.findInteractive(panel, xpix, ypix);
}
/**
* Draws the protractor on the given drawing panel.
*
* @param panel DrawingPanel
* @param g Graphics
*/
public void draw(DrawingPanel panel, Graphics g) {
Graphics2D g2 = (Graphics2D) g;
double i1 = panel.xToPix(x);
double j1 = panel.yToPix(y);
//draw the vector with angle ticks
g2.setColor(new Color(240, 40, 40, 40));
// replace the circle with two half circles to indicate angle
//g2.fill(new Double(i1-pixRadius,j1-pixRadius,pixRadius2,pixRadius2)); // draws circle
double start = Math.toDegrees(orientation);
g2.fill(new Arc2D.Double(i1-protractorRadius, j1-protractorRadius, protractorRadius2, protractorRadius2, start, 180, Arc2D.PIE));
g2.setColor(new Color(40, 40, 240, 40));
g2.fill(new Arc2D.Double(i1-protractorRadius, j1-protractorRadius, protractorRadius2, protractorRadius2, start+180, 180, Arc2D.PIE));
g2.setColor(Color.gray);
g2.setStroke(new BasicStroke(0.5f));
for(int i = 0; i<36; i++) {
AffineTransform at = g2.getTransform();
at.rotate(-i*Math.PI/18, i1, j1);
g2.setTransform(at);
g2.draw(new Line2D.Double(i1+protractorRadius-5, j1, i1+protractorRadius, j1));
at.rotate(+i*Math.PI/18, i1, j1);
g2.setTransform(at);
}
tauBox.setText(thetaStr+f.format(Math.toDegrees(getTheta())), x, y);
if(showTheta) {
tauBox.draw(panel, g);
}
//tip.draw(panel,g); // draws a small circle at the tip
//drawing the arrow with the red head
AffineTransform at = g2.getTransform();
at.rotate(-arrowTheta, i1, j1);
g2.setTransform(at);
g2.setColor(Color.RED);
Stroke currentStroke = g2.getStroke();
g2.setStroke(new BasicStroke(1.5f));
g2.draw(new Line2D.Double(i1, j1, i1+arrowLengthPix, j1));
GeneralPath arrowHead = new GeneralPath();
g2.draw(new Line2D.Double());
arrowHead.moveTo((float) (i1+arrowLengthPix), (float) j1);
arrowHead.lineTo((float) (i1+arrowLengthPix-15), (float) (j1-5));
arrowHead.lineTo((float) (i1+arrowLengthPix-15), (float) (j1+5));
arrowHead.closePath();
g2.fill(arrowHead);
g2.draw(arrowHead);
at.rotate(+arrowTheta, i1, j1);
g2.setTransform(at);
g2.setStroke(currentStroke);
double length = arrowLengthPix/panel.getXPixPerUnit();
tip.setXY(x+length*Math.cos(arrowTheta), y+length*Math.sin(arrowTheta));
}
public class Tip extends InteractiveCircle {
public void setXY(double x, double y) {
this.x = x;
this.y = y;
arrowTheta = Math.atan2(y-Protractor.this.y, x-Protractor.this.x);
}
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/