2 Copyright (C) 2001, 2006, 2007 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
8 package gov
.nasa
.worldwind
.render
;
10 import javax
.media
.opengl
.GL
;
11 import javax
.media
.opengl
.glu
.GLU
;
13 import java
.nio
.DoubleBuffer
;
15 import com
.sun
.opengl
.util
.BufferUtil
;
16 import gov
.nasa
.worldwind
.util
.Logging
;
19 * Static class for drawing 2D frames.
21 * All shapes are drawn inside a bounding rectangle whose lower left corner
22 * is at the origin. Shapes with a leader use an offset point that indicate where the
23 * leader triangle should point at - it usually has a negative y since the leader connects
24 * at the bottom of the frame (at y = 0).
26 * @author Patrick Murris
28 * @see AbstractAnnotation
30 public class FrameFactory
{
31 public static final String SHAPE_RECTANGLE
= "Render.FrameFactory.ShapeRectangle";
32 public static final String SHAPE_ELLIPSE
= "Render.FrameFactory.ShapeEllipse";
33 public static final String SHAPE_NONE
= "Render.FrameFactory.ShapeNone";
35 public static final String LEADER_TRIANGLE
= "Render.FrameFactory.LeaderTriangle";
36 public static final String LEADER_NONE
= "Render.FrameFactory.LeaderNone";
38 private static int cornerSteps
= 5;
39 private static int leaderGapWidth
= 6;
40 private static int circleSteps
= 36;
43 * Draw a shape with the specified width and height, gl mode and corner radius. GL mode came be one of
44 * <code>GL.GL_TRIANGLE_FAN</code> and <code>GL.LINE_STRIP</code>. Corner radius only apply
45 * to <code>SHAPE_RECTANGLE</code> - set to zero for square corners.
46 * @param dc the current <code>DrawContext</code>.
47 * @param shape the shape - can be one of <code>SHAPE_RECTANGLE</code> or <code>SHAPE_ELLIPSE</code>.
48 * @param width the width of the overall shape.
49 * @param height the height of the shape.
50 * @param glMode the GL mode - can be one of <code>GL.GL_TRIANGLE_FAN</code> and <code>GL.LINE_STRIP</code>.
51 * @param cornerRadius the rounded corners radius. Set to zero for square corners.
53 public static void drawShape(DrawContext dc
, String shape
, double width
, double height
, int glMode
, int cornerRadius
)
55 if (shape
.compareTo(SHAPE_NONE
) != 0)
56 drawBuffer(dc
, glMode
, createShapeBuffer(shape
, width
, height
, cornerRadius
));
60 * Draw a shape with the specified width and height, gl mode and corner radius. The shape includes a
61 * leader triangle pointing to a specified point. GL mode came be one of <code>GL.GL_TRIANGLE_FAN</code>
62 * and <code>GL.LINE_STRIP</code>. Corner radius only apply to <code>SHAPE_RECTANGLE</code> - set to zero for square corners.
63 * @param dc the current <code>DrawContext</code>.
64 * @param shape the shape - can be one of <code>SHAPE_RECTANGLE</code> or <code>SHAPE_ELLIPSE</code>.
65 * @param width the width of the overall shape.
66 * @param height the height of the shape excluding the leader.
67 * @param leaderOffset the coordinates of the point to which the leader leads.
68 * @param glMode the GL mode - can be one of <code>GL.GL_TRIANGLE_FAN</code> and <code>GL.LINE_STRIP</code>.
69 * @param cornerRadius the rounded corners radius. Set to zero for square corners.
71 public static void drawShapeWithLeader(DrawContext dc
, String shape
, double width
, double height
, Point leaderOffset
, int glMode
, int cornerRadius
)
73 if (shape
.compareTo(SHAPE_NONE
) != 0)
74 drawBuffer(dc
, glMode
, createShapeWithLeaderBuffer(shape
, width
, height
, leaderOffset
, cornerRadius
));
78 * Create a vertex buffer for a shape with the specified width, height and corner radius. Corner radius only apply
79 * to <code>SHAPE_RECTANGLE</code> - set to zero for square corners.
80 * @param shape the shape - can be one of <code>SHAPE_RECTANGLE</code> or <code>SHAPE_ELLIPSE</code>.
81 * @param width the width of the overall shape.
82 * @param height the height of the shape.
83 * @param cornerRadius the rounded corners radius. Set to zero for square corners.
84 * @return the vertex buffer.
86 public static DoubleBuffer
createShapeBuffer(String shape
, double width
, double height
, int cornerRadius
)
88 if (shape
.equals(SHAPE_RECTANGLE
))
89 return createRoundedRectangleBuffer(width
, height
, cornerRadius
);
90 else if (shape
.equals(SHAPE_ELLIPSE
))
91 return createEllipseBuffer(width
, height
, circleSteps
);
92 else if (shape
.equals(SHAPE_NONE
))
95 // default to rectangle if shape unknown
96 return createRoundedRectangleBuffer(width
, height
, cornerRadius
);
101 * Create a vertex buffer for a shape with the specified width, height and corner radius. The shape includes a
102 * leader triangle pointing to a specified point. Corner radius only apply to <code>SHAPE_RECTANGLE</code>
103 * - set to zero for square corners.
104 * @param shape the shape - can be one of <code>SHAPE_RECTANGLE</code> or <code>SHAPE_ELLIPSE</code>.
105 * @param width the width of the overall shape.
106 * @param height the height of the shape excluding the leader.
107 * @param leaderOffset the coordinates of the point to which the leader leads.
108 * @param cornerRadius the rounded corners radius. Set to zero for square corners.
109 * @return the vertex buffer.
111 public static DoubleBuffer
createShapeWithLeaderBuffer(String shape
, double width
, double height
, Point leaderOffset
, int cornerRadius
)
113 if (shape
.equals(SHAPE_RECTANGLE
))
114 return createRoundedRectangleWithLeaderBuffer(width
, height
, leaderOffset
, cornerRadius
);
115 else if (shape
.equals(SHAPE_ELLIPSE
))
116 return createEllipseWithLeaderBuffer(width
, height
, leaderOffset
, circleSteps
);
117 else if (shape
.equals(SHAPE_NONE
))
120 // default to rectangle if shape unknown
121 return createRoundedRectangleWithLeaderBuffer(width
, height
, leaderOffset
, cornerRadius
);
125 * Draw a vertex buffer in a given gl mode. Vertex buffers coming from the createShapeBuffer() methods support
126 * both <code>GL.GL_TRIANGLE_FAN</code> and <code>GL.LINE_STRIP</code>.
127 * @param dc the current DrawContext.
128 * @param glMode the desired drawing GL mode.
129 * @param verts the vertex buffer to draw.
131 public static void drawBuffer(DrawContext dc
, int glMode
, DoubleBuffer verts
)
135 String message
= Logging
.getMessage("nullValue.DrawContextIsNull");
136 Logging
.logger().severe(message
);
137 throw new IllegalArgumentException(message
);
141 String message
= Logging
.getMessage("nullValue.BufferIsNull");
142 Logging
.logger().severe(message
);
143 throw new IllegalArgumentException(message
);
147 gl
.glPushClientAttrib(GL
.GL_CLIENT_VERTEX_ARRAY_BIT
);
148 gl
.glEnableClientState(GL
.GL_VERTEX_ARRAY
);
149 gl
.glVertexPointer(2, GL
.GL_DOUBLE
, 0, verts
.rewind());
151 gl
.glDrawArrays(glMode
, 0, verts
.limit() / 2);
153 gl
.glPopClientAttrib();
157 * Draw a vertex buffer with texture coordinates in a given gl mode. Vertex buffers coming from the
158 * createShapeBuffer() methods support both <code>GL.GL_TRIANGLE_FAN</code> and <code>GL.LINE_STRIP</code>.
159 * @param dc the current DrawContext.
160 * @param glMode the desired drawing GL mode.
161 * @param verts the vertex buffer to draw.
163 public static void drawBuffer(DrawContext dc
, int glMode
, DoubleBuffer verts
, DoubleBuffer coords
)
167 String message
= Logging
.getMessage("nullValue.DrawContextIsNull");
168 Logging
.logger().severe(message
);
169 throw new IllegalArgumentException(message
);
171 if (verts
== null || coords
== null)
173 String message
= Logging
.getMessage("nullValue.BufferIsNull");
174 Logging
.logger().severe(message
);
175 throw new IllegalArgumentException(message
);
179 gl
.glPushClientAttrib(GL
.GL_CLIENT_VERTEX_ARRAY_BIT
);
180 gl
.glEnableClientState(GL
.GL_VERTEX_ARRAY
);
181 gl
.glVertexPointer(2, GL
.GL_DOUBLE
, 0, verts
.rewind());
182 gl
.glEnableClientState(GL
.GL_TEXTURE_COORD_ARRAY
);
183 gl
.glTexCoordPointer(2, GL
.GL_DOUBLE
, 0, coords
.rewind());
185 gl
.glDrawArrays(glMode
, 0, verts
.limit() / 2);
187 gl
.glPopClientAttrib();
191 //-- Rectangle ------------------------------------------------------------------
193 private static DoubleBuffer
createRoundedRectangleBuffer(double width
, double height
, int cornerRadius
)
195 int numVertices
= 9 + (cornerRadius
< 1 ?
0 : 4 * (cornerSteps
- 2));
196 DoubleBuffer verts
= BufferUtil
.newDoubleBuffer(numVertices
* 2);
198 // Drawing counter clockwise from bottom-left
200 verts
.put(idx
++, (double)cornerRadius
);
201 verts
.put(idx
++, 0d
);
202 verts
.put(idx
++, width
- cornerRadius
);
203 verts
.put(idx
++, 0d
);
204 idx
= drawCorner(width
- cornerRadius
, cornerRadius
, cornerRadius
, -Math
.PI
/ 2, 0, cornerSteps
, verts
, idx
);
206 verts
.put(idx
++, width
);
207 verts
.put(idx
++, (double)cornerRadius
);
208 verts
.put(idx
++, width
);
209 verts
.put(idx
++, height
- cornerRadius
);
210 idx
= drawCorner(width
- cornerRadius
, height
- cornerRadius
, cornerRadius
, 0, Math
.PI
/ 2, cornerSteps
, verts
, idx
);
212 verts
.put(idx
++, width
- cornerRadius
);
213 verts
.put(idx
++, height
);
214 verts
.put(idx
++, (double)cornerRadius
);
215 verts
.put(idx
++, height
);
216 idx
= drawCorner(cornerRadius
, height
- cornerRadius
, cornerRadius
, Math
.PI
/ 2, Math
.PI
, cornerSteps
, verts
, idx
);
218 verts
.put(idx
++, 0d
);
219 verts
.put(idx
++, height
- cornerRadius
);
220 verts
.put(idx
++, 0d
);
221 verts
.put(idx
++, (double)cornerRadius
);
222 idx
= drawCorner(cornerRadius
, cornerRadius
, cornerRadius
, Math
.PI
, Math
.PI
* 1.5, cornerSteps
, verts
, idx
);
223 // Finish up to starting point
224 verts
.put(idx
++, (double)cornerRadius
);
225 verts
.put(idx
++, 0d
);
230 private static DoubleBuffer
createRoundedRectangleWithLeaderBuffer(double width
, double height
, Point leaderOffset
, int cornerRadius
)
232 int numVertices
= 12 + (cornerRadius
< 1 ?
0 : 4 * (cornerSteps
- 2));
233 DoubleBuffer verts
= BufferUtil
.newDoubleBuffer(numVertices
* 2);
235 // Drawing counter clockwise from right leader connection at the bottom
236 // so as to accomodate GL_TRIANGLE_FAN and GL_LINE_STRIP (inside and border)
238 verts
.put(idx
++, width
/ 2 + leaderGapWidth
/ 2);
239 verts
.put(idx
++, 0d
);
240 verts
.put(idx
++, width
- cornerRadius
);
241 verts
.put(idx
++, 0d
);
242 idx
= drawCorner(width
- cornerRadius
, cornerRadius
, cornerRadius
, -Math
.PI
/ 2, 0, cornerSteps
, verts
, idx
);
244 verts
.put(idx
++, width
);
245 verts
.put(idx
++, (double)cornerRadius
);
246 verts
.put(idx
++, width
);
247 verts
.put(idx
++, height
- cornerRadius
);
248 idx
= drawCorner(width
- cornerRadius
, height
- cornerRadius
, cornerRadius
, 0, Math
.PI
/ 2, cornerSteps
, verts
, idx
);
250 verts
.put(idx
++, width
- cornerRadius
);
251 verts
.put(idx
++, height
);
252 verts
.put(idx
++, (double)cornerRadius
);
253 verts
.put(idx
++, height
);
254 idx
= drawCorner(cornerRadius
, height
- cornerRadius
, cornerRadius
, Math
.PI
/ 2, Math
.PI
, cornerSteps
, verts
, idx
);
256 verts
.put(idx
++, 0d
);
257 verts
.put(idx
++, height
- cornerRadius
);
258 verts
.put(idx
++, 0d
);
259 verts
.put(idx
++, (double)cornerRadius
);
260 idx
= drawCorner(cornerRadius
, cornerRadius
, cornerRadius
, Math
.PI
, Math
.PI
* 1.5, cornerSteps
, verts
, idx
);
262 verts
.put(idx
++, (double)cornerRadius
);
263 verts
.put(idx
++, 0d
);
264 verts
.put(idx
++, width
/ 2 - leaderGapWidth
/ 2);
265 verts
.put(idx
++, 0d
);
267 verts
.put(idx
++, leaderOffset
.x
);
268 verts
.put(idx
++, leaderOffset
.y
);
269 verts
.put(idx
++, width
/ 2 + leaderGapWidth
/ 2);
270 verts
.put(idx
++, 0d
);
275 private static int drawCorner(double x0
, double y0
, double cornerRadius
, double start
, double end
, int steps
, DoubleBuffer verts
, int startIdx
)
280 GL gl
= GLU
.getCurrentGL();
281 double step
= (end
- start
) / (steps
- 1);
282 for(int i
= 1; i
< steps
- 1; i
++)
284 double a
= start
+ step
* i
;
285 double x
= x0
+ Math
.cos(a
) * cornerRadius
;
286 double y
= y0
+ Math
.sin(a
) * cornerRadius
;
287 verts
.put(startIdx
++, x
);
288 verts
.put(startIdx
++, y
);
293 //-- Circle / Ellipse -----------------------------------------------------------
295 private static DoubleBuffer
createEllipseBuffer(double width
, double height
, int steps
)
297 int numVertices
= steps
+ 1;
298 DoubleBuffer verts
= BufferUtil
.newDoubleBuffer(numVertices
* 2);
300 // Drawing counter clockwise from bottom-left
301 double halfWidth
= width
/ 2;
302 double halfHeight
= height
/ 2;
303 double halfPI
= Math
.PI
/ 2;
304 double x0
= halfWidth
;
305 double y0
= halfHeight
;
306 double step
= Math
.PI
* 2 / steps
;
307 for(int i
= 0; i
<= steps
; i
++)
309 double a
= step
* i
- halfPI
;
310 double x
= x0
+ Math
.cos(a
) * halfWidth
;
311 double y
= y0
+ Math
.sin(a
) * halfHeight
;
318 private static DoubleBuffer
createEllipseWithLeaderBuffer(double width
, double height
, Point leaderOffset
, int steps
)
320 int numVertices
= steps
+ 3;
321 DoubleBuffer verts
= BufferUtil
.newDoubleBuffer(numVertices
* 2);
323 // Drawing counter clockwise from right leader connection at the bottom
324 // so as to accomodate GL_TRIANGLE_FAN and GL_LINE_STRIP (inside and border)
325 double halfWidth
= width
/ 2;
326 double halfHeight
= height
/ 2;
327 double halfPI
= Math
.PI
/ 2;
328 double x0
= halfWidth
;
329 double y0
= halfHeight
;
330 double step
= Math
.PI
* 2 / steps
;
331 double halfGap
= leaderGapWidth
/ 2 / halfWidth
;
332 for(int i
= 0; i
<= steps
; i
++)
334 double a
= step
* i
- halfPI
;
335 if (i
== 0) a
+= halfGap
;
336 if (i
== steps
) a
-= halfGap
;
337 double x
= x0
+ Math
.cos(a
) * halfWidth
;
338 double y
= y0
+ Math
.sin(a
) * halfHeight
;
343 verts
.put(idx
++, leaderOffset
.x
);
344 verts
.put(idx
++, leaderOffset
.y
);
345 verts
.put(idx
++, x0
+ Math
.cos(halfGap
- halfPI
) * halfWidth
);
346 verts
.put(idx
++, y0
+ Math
.sin(halfGap
- halfPI
) * halfHeight
);
350 //-- Texture coordinates --------------------------------------------------------
353 * Computes texture coordinates for a vertex buffer, a dimension and a texture size. Coordinates are computed
354 * so that the texture image proportions and size are preserved. The texture is aligned at top left corner
355 * of the vertices bounding rectangle.
357 * @param verts the vertex buffer containing the vertices for which texture coordinates have to be computed.
358 * @param width the vertices bounding rectangle width - excluding the leader if any.
359 * @param height the vertices bounding rectangle height - excluding the leader if any.
360 * @param textureWidth the texture width
361 * @param textureHeight the texture height
362 * @return the texture coordinates DoubleBuffer
364 public static DoubleBuffer
getTextureCoordinates(DoubleBuffer verts
, double width
, double height
, double textureWidth
, double textureHeight
)
369 int numVertices
= verts
.limit() / 2 ;
370 DoubleBuffer coords
= BufferUtil
.newDoubleBuffer(numVertices
* 2);
372 for(int i
= 0; i
< verts
.limit(); i
+=2)
375 coords
.put(idx
++, verts
.get(i
) / textureWidth
); // Tu
376 coords
.put(idx
++, (height
- verts
.get(i
+ 1)) / textureHeight
); // Tv