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    }