All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xmlcml.euclid.Angle Maven / Gradle / Ivy

There is a newer version: 2.9
Show newest version
/**
 *    Copyright 2011 Peter Murray-Rust
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.xmlcml.euclid;

import org.apache.log4j.Logger;

/**
 * Angle object
 * 
 * Angle represents an angle The reason for the class is to help remember about
 * radian/degree problems, to keep the angle in the right range (0, 2*PI) or
 * (-PI, PI) as required, and to format output.
 * 

* To construct an angle the user must consciously use RADIANS *

* The angle returned is always in RADIANS (except if getDegrees() is used) *

*
* If SIGNED is used, the angle is in the range -180 to 180 (-pi to pi)
* If UNSIGNED is used, the angle is in the range 0 to 360 (0 to 2*pi)
* Default is SIGNED *

* Default value of Angle is 0.0; Constructions of invalid angles should throw * exceptions rather than try to make invalid angles. * * @author (C) P. Murray-Rust, 1996 */ public class Angle { private final static Logger LOG = Logger.getLogger(Angle.class); /** units */ public enum Units { /** */ DEGREES, /** */ RADIANS; } /** range */ public enum Range { /** * any value. */ UNLIMITED, /** * 0 to 2*PI. */ UNSIGNED, /** * -PI to PI. */ SIGNED; } /** * default is UNLIMITED */ Range range = Range.UNLIMITED; /** * default is RADIANS */ Units type = Units.RADIANS; /** */ public final static double DEGREES_IN_RADIAN = 180.0 / Math.PI; /** * ALWAYS held as radians internally */ double angle = 0.0; /** * create default Angle default is (0.0) */ public Angle() { } /** * create an angle IN RADIANS * * @param a radian value */ public Angle(double a) { angle = a; } /** * construct using degrees or radians * * @param a value of angle * @param units unit of angle. It can either be in DEGREES or RADIANS */ public Angle(double a, Units units) { angle = (units == Units.RADIANS) ? a : a / DEGREES_IN_RADIAN; } /** * from X and Y components (uses atan2) * * @param x The x coordinate of the point * @param y The y coordinate of the point */ public Angle(double y, double x) { angle = Math.atan2(y, x); } /** * copy constructor * * @param a Angle object that needs to be copied */ public Angle(Angle a) { angle = a.angle; range = a.range; type = a.type; } /** * shallowCopy * * @param a Angle object that needs to be copied */ public void shallowCopy(Angle a) { range = a.range; type = a.type; angle = a.angle; } /** * add two angles * * @param a2 the specified angle to be added to this angle * @return new angle */ public Angle plus(Angle a2) { Angle temp = new Angle(angle + a2.angle); return temp; } /** * subtract two angles * * @param a2 the specified angle to be subtracted from this angle * @return new angle */ public Angle subtract(Angle a2) { Angle temp = new Angle(angle - a2.angle); return temp; } /** * multiply an angle by a scalar * * @param f a floating point scalar value * @return new angle */ public Angle multiplyBy(double f) { Angle temp = new Angle(angle * f); return temp; } /** * trigonometric functions * * @return cosine of angle */ public double cos() { return Math.cos(angle); } /** * sin of angle * * @return sine */ public double sin() { return Math.sin(angle); } /** * tan. * * @return the tan */ public double tan() { return Math.tan(angle); } /** * normalise angle. to range 0 -{@literal >} 2*PI * * @param angle the angle to be normalised * @return normalised angle */ public static double normalise(double angle) { while (angle > 2 * Math.PI) { angle -= 2 * Math.PI; } while (angle < 0.0) { angle += 2 * Math.PI; } LOG.trace(angle); return angle; } /** normalizes angle to be in range - Math.PI -{@literal >} Math.PI. * */ public void normalizeToPlusMinusPI() { angle = Angle.normalise(angle); if (angle > Math.PI) { angle -= 2 * Math.PI; } } /** * Normalises angle to be in range 0 -> Math.PI * 2. */ public void normalizeTo2Pi() { angle = Angle.normalise(angle); } /** * Tests whether this is a right angle. * * @param eps tolerance * @return 1 for PI/2, -1 for -PI/2 else 0 */ public Integer getRightAngle(Angle eps) { if (eps == null) return null; double absEps = Math.abs(eps.getRadian()); normalizeToPlusMinusPI(); Integer rt = 0; if (Math.abs(Math.PI / 2. - this.getRadian()) < absEps) { rt = 1; } else if (Math.abs(-Math.PI / 2. - this.getRadian()) < absEps) { rt = -1; } return rt; } /** * relational operators normalise the angles internally before comparison */ /** * are two normalised angles equal. * * @param a the specified angle to which this angle is compared * @return boolean * @deprecated // use epsilon method * */ public boolean isEqualTo(double a) { return Real.isEqual(Angle.normalise(angle), Angle.normalise(a)); } /** compare angles allowing for epsilon * * @param a the specified angle to which this angle is compared to be equal to * @param epsilon tolerance limit for equality * @return boolean */ public boolean isEqualTo(double a, double epsilon) { return Real.isEqual(Angle.normalise(angle), Angle.normalise(a), epsilon); } /** * is one angle greater than another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater * @return greater than */ public boolean greaterThan(double a) { return Angle.normalise(angle) > Angle.normalise(a); } /** * is one angle greater than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater than or equal to * @return greater than or equals */ public boolean greaterThanOrEquals(double a) { return Angle.normalise(angle) >= Angle.normalise(a); } /** * is one angle less than another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser * @return {@literal <} */ public boolean lessThan(double a) { return Angle.normalise(angle) < Angle.normalise(a); } /** * is one angle less than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser than or equal to * @return {@literal <}= */ public boolean lessThanOrEquals(double a) { return Angle.normalise(angle) <= Angle.normalise(a); } /** * are two angles equal * * @param a the specified angle to which this angle is compared to be equal to * @return == */ public boolean isEqualTo(Angle a) { return isEqualTo(a.angle); } /** * are two angles equal * * @param a the specified angle to which this angle is compared to be equal to * @param eps epsilon tolerance limit for equality * @return == */ public boolean isEqualTo(Angle a, double eps) { return a != null && Real.isEqual(a.getRadian(), this.getRadian(), eps); } /** * is one angle greater than another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater (after normalisation) * @return {@literal >} */ public boolean greaterThan(Angle a) { return greaterThan(a.angle); } /** * is one angle greater than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater than or equal to (after normalisation) * @return {@literal >}= */ public boolean greaterThanOrEquals(Angle a) { return greaterThanOrEquals(a.angle); } /** * is one angle less than another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser (after normalisation) * @return {@literal <} */ public boolean lessThan(Angle a) { return lessThan(a.angle); } /** * is one angle less than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser than or equal to (after normalisation) * @return {@literal <}= */ public boolean lessThanOrEquals(Angle a) { return lessThanOrEquals(a.angle); } /** * get angle in radians * * @return angle */ public double getAngle() { return adjust(angle); } /** * get angle in radians * * @return angle */ public double getRadian() { return adjust(angle); } /** * get angle in degrees * * @return angle */ public double getDegrees() { return adjust(angle) * DEGREES_IN_RADIAN; } /** * @param a angle in degrees */ public void putDegrees(double a) { angle = a / DEGREES_IN_RADIAN; } /** * set type of range * * @param range type of range */ public void setRange(Range range) { this.range = range; } /** * set angle to correct range */ private double adjust(double a) { if (range == Range.UNLIMITED) return a; double temp = normalise(a); if (range == Range.UNSIGNED) { return temp; } if (temp > Math.PI) { temp -= 2 * Math.PI; } else if (temp < -Math.PI) { temp += 2 * Math.PI; } return temp; } /** * to string. * * @return string */ public String toString() { StringBuffer s = new StringBuffer(); double temp = adjust(angle); if (type == Units.DEGREES) { s.append(temp).append(" degrees"); } else { s.append(temp); } return s.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy