001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.geom;
034:
035: import com.vividsolutions.jts.geom.Coordinate;
036:
037: /**
038: * Utility functions for working with angles.
039: */
040: public class Angle {
041: public static final double PI_TIMES_2 = 2.0 * Math.PI;
042: public static final double PI_OVER_2 = Math.PI / 2.0;
043: public static final double PI_OVER_4 = Math.PI / 4.0;
044:
045: /** General constant representing counterclockwise orientation */
046: public static int COUNTERCLOCKWISE = 0;
047:
048: /** General constant representing clockwise orientation */
049: public static int CLOCKWISE = 1;
050:
051: /** General constant representing no orientation */
052: public static int NONE = 2;
053:
054: /**
055: * Converts from radians to degrees.
056: * @param radians an angle in radians
057: * @return the angle in degrees
058: */
059: public static double toDegrees(double radians) {
060: return (radians * 180) / (Math.PI);
061: }
062:
063: /**
064: * Returns the angle of the vector from p0 to p1.
065: * The angle will be between -Pi and Pi.
066: * @return the angle (in radians) that p0-p1 makes with the positive x-axis.
067: */
068: public static double angle(Coordinate p0, Coordinate p1) {
069: double dx = p1.x - p0.x;
070: double dy = p1.y - p0.y;
071: return Math.atan2(dy, dx);
072: }
073:
074: /**
075: * Returns the angle of the vector from (0,) to p.
076: * The angle will be between -Pi and Pi.
077: * @return the angle (in radians) that p makes with the positive x-axis.
078: */
079: public static double angle(Coordinate p) {
080: return Math.atan2(p.y, p.x);
081: }
082:
083: /**
084: * Converts from degrees to radians.
085: * @param angleDegrees an angle in degrees
086: * @return the angle in radians
087: */
088: public static double toRadians(double angleDegrees) {
089: return (angleDegrees * Math.PI) / 180.0;
090: }
091:
092: /**
093: * Returns the angle between two vectors. Will be between 0 and Pi.
094: * @param tail the tail of each vector
095: * @param tip1 the tip of one vector
096: * @param tip2 the tip of the other vector
097: * @return the angle between tail-tip1 and tail-tip2
098: */
099: public static double angleBetween(Coordinate tail, Coordinate tip1,
100: Coordinate tip2) {
101: double a1 = angle(tail, tip1);
102: double a2 = angle(tail, tip2);
103:
104: return diff(a1, a2);
105: }
106:
107: /**
108: * Computes the interior angle between two segments of a ring.
109: * The ring is assumed to be oriented in a clockwise direction.
110: * @param p0 a point of the ring
111: * @param p1 the next point of the ring
112: * @param p2 the next point of the ring
113: * @return the interior angle based at <code>p1</code>
114: */
115: public static double interiorAngle(Coordinate p0, Coordinate p1,
116: Coordinate p2) {
117: double anglePrev = Angle.angle(p1, p0);
118: double angleNext = Angle.angle(p1, p2);
119: return Math.abs(angleNext - anglePrev);
120: }
121:
122: /**
123: * Returns whether an angle must turn clockwise or counterclockwise
124: * to overlap another angle.
125: * @param a1 an angle in radians
126: * @param a2 an angle in radians
127: * @return whether a1 must turn CLOCKWISE, COUNTERCLOCKWISE or NONE to
128: * overlap a2.
129: */
130: public static int getTurn(double a1, double a2) {
131: double crossproduct = Math.sin(a2 - a1);
132:
133: if (crossproduct > 0) {
134: return COUNTERCLOCKWISE;
135: }
136:
137: if (crossproduct < 0) {
138: return CLOCKWISE;
139: }
140:
141: return NONE;
142: }
143:
144: /**
145: * Computes the normalized value of an angle, which is the
146: * equivalent angle lying between -Pi and Pi.
147: *
148: * @param angle the angle to compute the normalized value of
149: * @return the normalized value of the angle
150: */
151: public static double normalize(double angle) {
152: while (angle > Math.PI)
153: angle -= PI_TIMES_2;
154: while (angle < -Math.PI)
155: angle += PI_TIMES_2;
156: return angle;
157: }
158:
159: /**
160: * Returns the angle between two vectors.
161: * @param a1 the angle of one vector, between -Pi and Pi
162: * @param a2 the angle of the other vector, between -Pi and Pi
163: * @return the angle (in radians) between the two vectors, between 0 and Pi
164: */
165: public static double diff(double a1, double a2) {
166: double da;
167:
168: if (a1 < a2) {
169: da = a2 - a1;
170: } else {
171: da = a1 - a2;
172: }
173:
174: if (da > Math.PI) {
175: da = (2 * Math.PI) - da;
176: }
177:
178: return da;
179: }
180: }
|