Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / geom / Cylinder.java
blob349c2e2711151b3debd600ea63fa7328f41fef1e
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.geom;
9 import gov.nasa.worldwind.*;
11 import javax.media.opengl.*;
12 import javax.media.opengl.glu.*;
14 /**
15 * Represents a geometric cylinder. <code>Cylinder</code>s are immutable.
17 * @author Tom Gaskins
18 * @version $Id: Cylinder.java 1749 2007-05-06 19:48:14Z tgaskins $
20 public class Cylinder implements Extent, Renderable
22 private final Point bottomCenter; // point at center of cylinder base
23 private final Point topCenter; // point at center of cylinder top
24 private final Point axisUnitDirection; // axis as unit vector from bottomCenter to topCenter
25 private final double cylinderRadius;
26 private final double cylinderHeight;
28 /**
29 * Create a <code>Cylinder</code> from two points and a radius. Does not accept null arguments.
31 * @param bottomCenter represents the centrepoint of the base disc of the <code>Cylinder</code>
32 * @param topCenter represents the centrepoint of the top disc of the <code>Cylinder</code>
33 * @param cylinderRadius the radius of the <code>Cylinder</code>
34 * @throws IllegalArgumentException if either the top or bottom point is null
36 public Cylinder(Point bottomCenter, Point topCenter, double cylinderRadius)
38 if (bottomCenter == null || topCenter == null)
40 String message = WorldWind.retrieveErrMsg("nullValue.EndPointIsNull");
42 WorldWind.logger().log(java.util.logging.Level.FINE, message);
43 throw new IllegalArgumentException(message);
46 if (cylinderRadius <= 0)
48 String message = WorldWind.retrieveErrMsg("geom.Cylinder.RadiusIsZeroOrNegative");
50 WorldWind.logger().log(java.util.logging.Level.FINE, message);
51 throw new IllegalArgumentException(message);
54 this.bottomCenter = bottomCenter;
55 this.topCenter = topCenter;
56 this.cylinderHeight = this.bottomCenter.distanceTo(this.topCenter);
57 this.cylinderRadius = cylinderRadius;
58 this.axisUnitDirection = this.topCenter.subtract(this.bottomCenter).normalize();
61 public String toString()
63 return this.cylinderRadius + ", " + this.bottomCenter.toString() + ", " + this.topCenter.toString() + ", "
64 + this.axisUnitDirection.toString();
67 public Intersection[] intersect(Line line) // TODO: test this method
69 if (line == null)
71 String message = WorldWind.retrieveErrMsg("nullValue.LineIsNull");
73 WorldWind.logger().log(java.util.logging.Level.FINE, message);
74 throw new IllegalArgumentException(message);
77 Point ld = line.getDirection();
78 Point lo = line.getOrigin();
79 double a = ld.x() * ld.x() + ld.y() * ld.y();
80 double b = 2 * (lo.x() * ld.x() + lo.y() * ld.y());
81 double c = lo.x() * lo.x() + lo.y() * lo.y() - this.cylinderRadius * this.cylinderRadius;
83 double discriminant = Cylinder.discriminant(a, b, c);
84 if (discriminant < 0)
85 return null;
87 double discriminantRoot = Math.sqrt(discriminant);
88 if (discriminant == 0)
90 Point p = line.getPointAt((-b - discriminantRoot) / (2 * a));
91 return new Intersection[] {new Intersection(p, true)};
93 else // (discriminant > 0)
95 Point near = line.getPointAt((-b - discriminantRoot) / (2 * a));
96 Point far = line.getPointAt((-b + discriminantRoot) / (2 * a));
97 boolean n = false, f = false;
98 boolean nTangent = false, fTangent = false;
99 if (near.z() >= 0 && near.z() <= this.getHeight())
101 n = true;
102 nTangent = near.z() == 0;
104 if (far.z() >= 0 && far.z() <= this.getHeight())
106 f = true;
107 fTangent = far.z() == 0;
110 // TODO: Test for intersection with planes at cylinder's top and bottom
112 Intersection[] intersections = null;
113 if (n && f)
114 intersections = new Intersection[] {new Intersection(near, nTangent), new Intersection(far, fTangent)};
115 else if (n)
116 intersections = new Intersection[] {new Intersection(near, nTangent)};
117 else if (f)
118 intersections = new Intersection[] {new Intersection(far, fTangent)};
120 return intersections;
124 public boolean intersects(Line line)
126 if (line == null)
128 String message = WorldWind.retrieveErrMsg("nullValue.LineIsNull");
130 WorldWind.logger().log(java.util.logging.Level.FINE, message);
131 throw new IllegalArgumentException(message);
134 Point ld = line.getDirection();
135 Point lo = line.getOrigin();
136 double a = ld.x() * ld.x() + ld.y() * ld.y();
137 double b = 2 * (lo.x() * ld.x() + lo.y() * ld.y());
138 double c = lo.x() * lo.x() + lo.y() * lo.y() - this.cylinderRadius * this.cylinderRadius;
140 double discriminant = Cylinder.discriminant(a, b, c);
142 return discriminant >= 0;
145 static private double discriminant(double a, double b, double c)
147 return b * b - 4 * a * c;
150 private double intersectsAt(Plane plane, double effectiveRadius, double parameter)
152 // Test the distance from the first cylinder end-point.
153 double dq1 = plane.dot(this.bottomCenter);
154 boolean bq1 = dq1 <= -effectiveRadius;
156 // Test the distance from the possibly reduced second cylinder end-point.
157 Point newTop;
158 if (parameter < 1)
159 newTop = this.bottomCenter.add(this.topCenter.subtract(this.bottomCenter).multiply(parameter));
160 else
161 newTop = this.topCenter;
162 double dq2 = plane.dot(newTop);
163 boolean bq2 = dq2 <= -effectiveRadius;
165 if (bq1 && bq2) // both <= effective radius; cylinder is on negative side of plane
166 return -1;
168 if (bq1 == bq2) // both >= effective radius; can't draw any conclusions
169 return parameter;
171 // Compute and return the parameter value at which the plane intersects the cylinder's axis.
172 return (effectiveRadius + plane.dot(this.bottomCenter))
173 / plane.getNormal().dot(this.bottomCenter.subtract(newTop));
176 private double getEffectiveRadius(Plane plane)
178 // Determine the effective radius of the cylinder axis relative to the plane.
179 double dot = plane.getNormal().dot(this.axisUnitDirection);
180 double scale = 1d - dot * dot;
181 if (scale <= 0)
182 return 0;
183 else
184 return this.cylinderRadius * Math.sqrt(scale);
187 public boolean intersects(Plane plane)
189 if (plane == null)
191 String message = WorldWind.retrieveErrMsg("nullValue.PlaneIsNull");
192 WorldWind.logger().log(java.util.logging.Level.FINE, message);
193 throw new IllegalArgumentException(message);
195 double effectiveRadius = this.getEffectiveRadius(plane);
196 double intersectionPoint = this.intersectsAt(plane, effectiveRadius, 1d);
197 return intersectionPoint >= 0;
200 public boolean intersects(Frustum frustum)
202 if (frustum == null)
204 String message = WorldWind.retrieveErrMsg("nullValue.FrustumIsNull");
206 WorldWind.logger().log(java.util.logging.Level.FINE, message);
207 throw new IllegalArgumentException(message);
210 double intersectionPoint;
212 double effectiveRadius = this.getEffectiveRadius(frustum.getNear());
213 intersectionPoint = this.intersectsAt(frustum.getNear(), effectiveRadius, 1d);
214 if (intersectionPoint < 0)
215 return false;
217 // Near and far have the same effective radius.
218 intersectionPoint = this.intersectsAt(frustum.getFar(), effectiveRadius, intersectionPoint);
219 if (intersectionPoint < 0)
220 return false;
222 effectiveRadius = this.getEffectiveRadius(frustum.getLeft());
223 intersectionPoint = this.intersectsAt(frustum.getLeft(), effectiveRadius, intersectionPoint);
224 if (intersectionPoint < 0)
225 return false;
227 effectiveRadius = this.getEffectiveRadius(frustum.getRight());
228 intersectionPoint = this.intersectsAt(frustum.getRight(), effectiveRadius, intersectionPoint);
229 if (intersectionPoint < 0)
230 return false;
232 effectiveRadius = this.getEffectiveRadius(frustum.getTop());
233 intersectionPoint = this.intersectsAt(frustum.getTop(), effectiveRadius, intersectionPoint);
234 if (intersectionPoint < 0)
235 return false;
237 effectiveRadius = this.getEffectiveRadius(frustum.getBottom());
238 intersectionPoint = this.intersectsAt(frustum.getBottom(), effectiveRadius, intersectionPoint);
239 return intersectionPoint >= 0;
242 public Point getCenter()
244 Point b = this.bottomCenter;
245 Point t = this.topCenter;
246 return new Point(0.5 * (b.x() + t.x()), 0.5 * (b.y() + t.y()), 0.5 * (b.z() + t.z()));
249 public double getDiameter()
251 return 2 * this.getRadius();
254 public double getRadius()
256 // return the radius of the enclosing sphere
257 double halfHeight = 0.5 * this.bottomCenter.distanceTo(this.topCenter);
258 return Math.sqrt(halfHeight * halfHeight + this.cylinderRadius * this.cylinderRadius);
262 * Obtain the height of this <code>Cylinder</code>.
264 * @return the distance between the bottom and top of this <code>Cylinder</code>
266 public final double getHeight()
268 return this.cylinderHeight;
271 public void render(DrawContext dc)
273 if (dc == null)
275 String msg = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
276 WorldWind.logger().log(java.util.logging.Level.FINE, msg);
277 throw new IllegalArgumentException(msg);
280 Point center = this.getCenter();
281 PolarPoint p = PolarPoint.fromCartesian(center);
283 javax.media.opengl.GL gl = dc.getGL();
285 gl.glPushAttrib(GL.GL_ENABLE_BIT | GL.GL_TRANSFORM_BIT);
287 gl.glBegin(javax.media.opengl.GL.GL_LINES);
288 gl.glVertex3d(this.bottomCenter.x(), this.bottomCenter.y(), this.bottomCenter.z());
289 gl.glVertex3d(this.topCenter.x(), this.topCenter.y(), this.topCenter.z());
290 gl.glEnd();
292 gl.glEnable(javax.media.opengl.GL.GL_DEPTH_TEST);
293 gl.glMatrixMode(javax.media.opengl.GL.GL_MODELVIEW);
294 gl.glPushMatrix();
295 gl.glTranslated(this.bottomCenter.x(), this.bottomCenter.y(), this.bottomCenter.z());
296 dc.getGL().glRotated(p.getLongitude().getDegrees(), 0, 1, 0);
297 dc.getGL().glRotated(Math.abs(p.getLatitude().getDegrees()), Math.signum(p.getLatitude().getDegrees()) * -1,
298 0, 0);
300 GLUquadric quadric = dc.getGLU().gluNewQuadric();
301 dc.getGLU().gluQuadricDrawStyle(quadric, GLU.GLU_LINE);
302 dc.getGLU().gluCylinder(quadric, this.cylinderRadius, this.cylinderRadius, this.cylinderHeight, 30, 30);
303 dc.getGLU().gluDeleteQuadric(quadric);
305 gl.glPopMatrix();
306 gl.glPopAttrib();