001 package physics3d;
002
003 /****************************************************************************
004 * Copyright (C) 1999 by the Massachusetts Institute of Technology,
005 * Cambridge, Massachusetts.
006 *
007 * All Rights Reserved
008 *
009 * Permission to use, copy, modify, and distribute this software and
010 * its documentation for any purpose and without fee is hereby
011 * granted, provided that the above copyright notice appear in all
012 * copies and that both that copyright notice and this permission
013 * notice appear in supporting documentation, and that MIT's name not
014 * be used in advertising or publicity pertaining to distribution of
015 * the software without specific, written prior permission.
016 *
017 * THE MASSACHUSETTS INSTITUTE OF TECHNOLOGY DISCLAIMS ALL WARRANTIES
018 * WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
019 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE MASSACHUSETTS
020 * INSTITUTE OF TECHNOLOGY BE LIABLE FOR ANY SPECIAL, INDIRECT OR
021 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
022 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
023 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
024 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
025 *
026 *
027 * Author: Matt Frank, MIT Laboratory for Computer Science,
028 * mfrank@lcs.mit.edu
029 * 1999-Apr-03
030 *
031 * Version: $Id: Angle.java,v 1.3 2007/05/14 20:04:35 varenc Exp $
032 *
033 ***************************************************************************/
034
035 import java.io.Serializable;
036
037 /**
038 * Angle is an immutable abstract data type which represents the
039 * mathematical notion of an angle. <code>Angle</code> represents a
040 * non-negative angle less than 360 degrees or 2*pi radians.
041 **/
042 public strictfp final class Angle
043 implements Serializable, Comparable
044 {
045
046 // Rep. Invariant:
047 // cosine^2 + sine^2 = 1
048
049 // Abstraction Function:
050 // The angle <a> such that cos(a) = cosine and sin(a) = sine
051
052 // Rep. Rationale:
053 // In most of the math we use here we start in cartesian coordinates, so
054 // calculating sine and cosine is relatively efficient (just a sqrt and
055 // a division). Finding the angle in terms of arcsin and arccos would be
056 // very slow. On the other hand, adding and subtracting angles that are
057 // represented this way can be done relatively efficiently using standard
058 // trig. identities.
059
060 private final double cosine;
061 private final double sine;
062
063 private static final double SQRT = Math.sqrt(0.5);
064 // useful constants
065 /** A zero-degree or zero-radian angle */
066 public static final Angle ZERO = new Angle(1.0, 0.0);
067 /** A 45-degree angle */
068 public static final Angle DEG_45 = new Angle(SQRT, SQRT);
069 /** A 90-degree angle */
070 public static final Angle DEG_90 = new Angle(0.0, 1.0);
071 /** A 135-degree angle */
072 public static final Angle DEG_135 = new Angle(-SQRT, SQRT);
073 /** A 180-degree angle */
074 public static final Angle DEG_180 = new Angle(-1.0, 0.0);
075 /** A 225-degree angle */
076 public static final Angle DEG_225 = new Angle(-SQRT, -SQRT);
077 /** A 270-degree angle */
078 public static final Angle DEG_270 = new Angle(0.0, -1.0);
079 /** A 315-degree angle */
080 public static final Angle DEG_315 = new Angle(SQRT, -SQRT);
081
082 /** An angle of pi/4 radians */
083 public static final Angle RAD_PI_OVER_FOUR = DEG_45;
084 /** An angle of pi/2 radians */
085 public static final Angle RAD_PI_OVER_TWO = DEG_90;
086 /** An angle of pi radians */
087 public static final Angle RAD_PI = DEG_180;
088
089 // CONSTRUCTORS:
090
091 /**
092 * @effects constructs an <code>Angle</code> with <code>radians</code> radians.
093 */
094 public Angle(double radians) {
095 cosine = Math.cos(radians);
096 sine = Math.sin(radians);
097 }
098
099 /**
100 * @requires (x,y) != (0,0)
101 *
102 * @effects constructs the <code>Angle</code> that is formed between the
103 * positive x-axis and the line from the origin to (<code>x</code>,
104 * <code>y</code>).
105 **/
106 public Angle(double x, double y) {
107 double r = Math.sqrt((x * x) + (y * y));
108 if (r == 0.0) {
109 if ((x == 0.0) && (y == 0.0)) {
110 throw new IllegalArgumentException("Requires violated: Triangle is singular");
111 } else {
112 throw new ArithmeticException("Triangle is singular; imprecision on <" + x + "," + y + ">");
113 }
114 }
115 cosine = x / r;
116 sine = y / r;
117 }
118
119 // OBSERVERS:
120
121 /**
122 * @return the cosine of this.
123 */
124 public double cos() {
125 return cosine;
126 }
127
128 /**
129 * @return the sine of this.
130 */
131 public double sin() {
132 return sine;
133 }
134
135 /**
136 * @return the tangent of this.
137 */
138 public double tan() {
139 return sine/cosine;
140 }
141
142 /**
143 * @return the number of radians represented by this in the range of
144 * -pi to pi.
145 **/
146 public double radians() {
147 double d = Math.atan2(sine, cosine);
148 if (d > Math.PI || d < -Math.PI) {
149 //System.out.println("d = " + d);
150 throw new IllegalArgumentException();
151 }
152 return d;
153 }
154
155 /**
156 * @return the number of degrees represented by this in the range of -180 to 180
157 */
158 public double degrees() {
159 return radians() * (180/Math.PI);
160 }
161
162 /**
163 * Compares this object with the specified object for order.
164 * @return a negative integer, zero, or a positive integer as this
165 * object is less than, equal to, or greater than the specified object.
166 * @exception ClassCastException if <code>o</code> is not an Angle
167 * @exception NullPointerException if <code>o</code> is null
168 */
169 public int compareTo(Object o)
170 {
171 // Comparable.compareTo allows us to throw a ClassCastException
172 return compareTo((Angle) o);
173 }
174
175 /**
176 * Compares this object with the specified object for order.
177 * @return a negative integer, zero, or a positive integer as this
178 * object is less than, equal to, or greater than the specified object.
179 * @exception NullPointerException if <code>c</code> is null
180 */
181 public int compareTo(Angle c)
182 {
183 if (this.equals(c))
184 return 0;
185
186 // first discriminate on the basis of top vs. bottom half (sin)
187 // then discriminate on the basis of left vs. right half (cos)
188
189 if (sine >= 0.0) {
190 if (c.sine < 0.0) {
191 return -1;
192 } else {
193 if (cosine < c.cosine) {
194 return 1;
195 } else {
196 return -1;
197 }
198 }
199 } else {
200 if (c.sine >= 0.0) {
201 return 1;
202 } else {
203 if (cosine < c.cosine) {
204 return -1;
205 } else {
206 return 1;
207 }
208 }
209 }
210 }
211
212 // PRODUCERS:
213
214 /**
215 * @requires <code>a</code> is not null
216 * @return the angle <code>this</code> + <code>a</code>.
217 */
218 public Angle plus(Angle a) {
219 // These are standard trig identities. See the appendix of your
220 // favorite calculus text book.
221 double cosine = (this.cosine * a.cosine) - (this.sine * a.sine);
222 double sine = (this.sine * a.cosine) + (this.cosine * a.sine);
223
224 return new Angle(cosine, sine);
225 }
226
227 /**
228 * @requires <code>a</code> is not null
229 * @return the angle <code>this</code> - <code>a</code>.
230 */
231 public Angle minus(Angle a) {
232 // These are standard trig identities. See the appendix of your
233 // favorite calculus text book.
234 double cosine = (this.cosine * a.cosine) + (this.sine * a.sine);
235 double sine = (this.sine * a.cosine) - (this.cosine * a.sine);
236
237 return new Angle(cosine, sine);
238 }
239
240 public String toString() {
241 return "Angle(" + cosine + "," + sine + ")";
242 }
243
244 public boolean equals(Angle a) {
245 if (a == null) return false;
246 return ((this.cosine == a.cosine) && (this.sine == a.sine));
247 }
248
249 public boolean equals(Object o) {
250 return (o instanceof Angle) && equals((Angle) o);
251 }
252
253 public int hashCode() {
254 return (new Double(sine)).hashCode() + (new Double(cosine)).hashCode();
255 }
256 static final long serialVersionUID = 6950015567604951763L;
257 }