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 }