2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
7 package gov
.nasa
.worldwind
.geom
;
9 import gov
.nasa
.worldwind
.*;
11 import javax
.media
.opengl
.*;
12 import javax
.media
.opengl
.glu
.*;
15 * Represents a geometric cylinder. <code>Cylinder</code>s are immutable.
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
;
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
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
);
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())
102 nTangent
= near
.z() == 0;
104 if (far
.z() >= 0 && far
.z() <= this.getHeight())
107 fTangent
= far
.z() == 0;
110 // TODO: Test for intersection with planes at cylinder's top and bottom
112 Intersection
[] intersections
= null;
114 intersections
= new Intersection
[] {new Intersection(near
, nTangent
), new Intersection(far
, fTangent
)};
116 intersections
= new Intersection
[] {new Intersection(near
, nTangent
)};
118 intersections
= new Intersection
[] {new Intersection(far
, fTangent
)};
120 return intersections
;
124 public boolean intersects(Line line
)
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.
159 newTop
= this.bottomCenter
.add(this.topCenter
.subtract(this.bottomCenter
).multiply(parameter
));
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
168 if (bq1
== bq2
) // both >= effective radius; can't draw any conclusions
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
;
184 return this.cylinderRadius
* Math
.sqrt(scale
);
187 public boolean intersects(Plane plane
)
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
)
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)
217 // Near and far have the same effective radius.
218 intersectionPoint
= this.intersectsAt(frustum
.getFar(), effectiveRadius
, intersectionPoint
);
219 if (intersectionPoint
< 0)
222 effectiveRadius
= this.getEffectiveRadius(frustum
.getLeft());
223 intersectionPoint
= this.intersectsAt(frustum
.getLeft(), effectiveRadius
, intersectionPoint
);
224 if (intersectionPoint
< 0)
227 effectiveRadius
= this.getEffectiveRadius(frustum
.getRight());
228 intersectionPoint
= this.intersectsAt(frustum
.getRight(), effectiveRadius
, intersectionPoint
);
229 if (intersectionPoint
< 0)
232 effectiveRadius
= this.getEffectiveRadius(frustum
.getTop());
233 intersectionPoint
= this.intersectsAt(frustum
.getTop(), effectiveRadius
, intersectionPoint
);
234 if (intersectionPoint
< 0)
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
)
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());
292 gl
.glEnable(javax
.media
.opengl
.GL
.GL_DEPTH_TEST
);
293 gl
.glMatrixMode(javax
.media
.opengl
.GL
.GL_MODELVIEW
);
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,
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
);