/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Nomad 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 Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Created on Jan 7, 2006
*/
package net.sf.nmedit.jtheme.component.misc;
import java.awt.geom.Point2D;
/**
* @author Christian Schneider
*/
public class KnobMetrics {
public final static double PI2 = Math.PI*2.0d;
public final static double PIdiv2 = Math.PI/2.0d;
public final static double PIdiv4 = Math.PI/4.0d;
/**
* Returns the norm of p. If (p = z+k) and z \in Z and 0<=k<=1 then normP(p)==k.
* (normP(p):=p-Math.floor(p))
*
* @param p
* @return value k with 0<=k<=1
*/
public static double normP(double p) {
return p==1 ? 1 : p-Math.floor(p);
}
/**
* Returns true if a and b have nearly the same value.
* (abs(a-b)<1e-20)
*
* @return true if a and b have nearly the same value
*/
public static boolean areSimilar(double a, double b) {
return Math.abs(a-b)<1e-20;
}
/**
* Returns true if a and 0 are similar
* @param a number to check if it is similar to zero
* @return true if a and 0 are similar
* @see #areSimilar(double, double)
*/
public static boolean isCloseToZero(double a) {
return areSimilar(0, a);
}
/**
* <pre> Angles:
* 180
* |
* 270 --- 90
* |
* 0</pre>
*
* @param mx ordinate (x-coordinate) of the point m(mx, my)
* @param my abscissa (y-coordinate) of the point m(mx, my)
* @param px ordinate (x-coordinate) of the point p(px, py)
* @param py abscissa (y-coordinate) of the point p(px, py)
*
* @return the angle <code>a</code> of point <code>p</code> concering the coordinate systems center <code>m</code>.
* For the return value <code>a</code> the condition <code>0<=a<=1</code> is true if
* the equation (areSimilar(mx, px) and areSimilar(my,py)) is false. Otherwise
* an angle can not be returned. To indicate this, -1 will be returned. (Note -1 is equivalent to +1 or 0).
* @see #areSimilar(double, double)
*/
public static double getAngle(double mx, double my, double px, double py) {
if (KnobMetrics.areSimilar(mx, px) && KnobMetrics.areSimilar(my,py))
return -1; // no angle
double pangle = PIdiv2-Math.atan((py-my)/(px-mx));
if (px<mx) {
pangle+=Math.PI;
}
return normP(pangle/PI2);
}
public static double getAngle(double mx, double my, double px, double py, double arcStart, double arcStop) {
double angle = KnobMetrics.getAngle(mx, my, px, py);
double arcRange = arcStop-arcStart;
return angle<=arcStart ? 0: angle >= arcStop ? 1 : (angle-arcStart)/arcRange;
}
public static double getAngleCCW(double mx, double my, double px, double py, double arcStart, double arcStop) {
double angle = KnobMetrics.cw(KnobMetrics.getAngle(mx, my, px, py));
double arcRange = arcStop-arcStart;
return angle<=arcStart ? 0: angle >= arcStop ? 1 : (angle-arcStart)/arcRange;
}
/**
* Returns the inverse angle.
*
* For example the angles
* <pre>
* 180
* |
* 270 --- 90
* |
* 0</pre>
* will be transformed to
* <pre>
* 180
* |
* 90 --- 270
* |
* 0</pre>
*
* @param angle
* @return the value r:=normP(1-angle) (normP makes shure that the condition 0<=r<1 is true)
*/
public static double cw(double angle) {
return normP(1.0d-angle);
}
/**
* Returns the point with distance radius from origin (midx, midy) with pangle==getAngle(midx,midy, p.x,p.y).
*
* @param midx
* @param midy
* @param radius
* @param pangle
* @return
*/
public static Point2D getPoint(double midx, double midy, double radius, double pangle) {
if (isCloseToZero(radius))
return new Point2D.Double(midx, midy);
pangle = pangle*Math.PI*2;
return new Point2D.Double(midx+(Math.sin(pangle)*radius), midy+(Math.cos(pangle)*radius));
}
/**
* Returns the point that lays on the arc (arcStart, arcStop). The arcs origin is (midx, midy).
* The distance of the arc (point) to the origin is radius.
*
* @see #getPoint(double, double, double, double)
*/
public static Point2D getPoint(double midx, double midy, double radius, double arcPos, double arcStart, double arcStop) {
double arcRange = arcStop-arcStart;
return KnobMetrics.getPoint(midx, midy, radius, arcStart+arcPos*arcRange);
}
public static Point2D getPointCCW(double midx, double midy, double radius, double arcPos, double arcStart, double arcStop) {
double arcRange = arcStop-arcStart;
return KnobMetrics.getPoint(midx, midy, radius, KnobMetrics.cw(arcStart+arcPos*arcRange));
}
}