Update to Worldwind release 0.4.1
[worldwind-tracker.git] / gov / nasa / worldwind / render / FrameFactory.java
blob0cb6a312337bf2fb72abb17cf71f0048f7f526e9
1 /*
2 Copyright (C) 2001, 2006, 2007 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
8 package gov.nasa.worldwind.render;
10 import javax.media.opengl.GL;
11 import javax.media.opengl.glu.GLU;
12 import java.awt.*;
13 import java.nio.DoubleBuffer;
15 import com.sun.opengl.util.BufferUtil;
16 import gov.nasa.worldwind.util.Logging;
18 /**
19 * Static class for drawing 2D frames.
20 * <p>
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).
25 * </p>
26 * @author Patrick Murris
27 * @version $Id$
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;
42 /**
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));
59 /**
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));
77 /**
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))
93 return null;
94 else
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))
118 return null;
119 else
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)
133 if (dc == null)
135 String message = Logging.getMessage("nullValue.DrawContextIsNull");
136 Logging.logger().severe(message);
137 throw new IllegalArgumentException(message);
139 if (verts == null)
141 String message = Logging.getMessage("nullValue.BufferIsNull");
142 Logging.logger().severe(message);
143 throw new IllegalArgumentException(message);
145 GL gl = dc.getGL();
146 // Set up
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());
150 // Draw
151 gl.glDrawArrays(glMode, 0, verts.limit() / 2);
152 // Restore
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)
165 if (dc == null)
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);
177 GL gl = dc.getGL();
178 // Set up
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());
184 // Draw
185 gl.glDrawArrays(glMode, 0, verts.limit() / 2);
186 // Restore
187 gl.glPopClientAttrib();
190 //-- Shape creation
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);
197 int idx = 0;
198 // Drawing counter clockwise from bottom-left
199 // Bottom
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);
205 // Right
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);
211 // Top
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);
217 // Left
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);
227 return verts;
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);
234 int idx = 0;
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)
237 // Bottom right
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);
243 // Right
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);
249 // Top
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);
255 // Left
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);
261 // Bottom left
262 verts.put(idx++, (double)cornerRadius);
263 verts.put(idx++, 0d);
264 verts.put(idx++, width / 2 - leaderGapWidth / 2);
265 verts.put(idx++, 0d);
266 // Draw leader
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);
272 return verts;
275 private static int drawCorner(double x0, double y0, double cornerRadius, double start, double end, int steps, DoubleBuffer verts, int startIdx)
277 if(cornerRadius < 1)
278 return 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);
290 return startIdx;
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);
299 int idx = 0;
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;
312 verts.put(idx++, x);
313 verts.put(idx++, y);
315 return verts;
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);
322 int idx = 0;
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;
339 verts.put(idx++, x);
340 verts.put(idx++, y);
342 // Draw leader
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);
347 return verts;
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)
366 if (verts == null)
367 return null;
369 int numVertices = verts.limit() / 2 ;
370 DoubleBuffer coords = BufferUtil.newDoubleBuffer(numVertices * 2);
371 int idx = 0;
372 for(int i = 0; i < verts.limit(); i +=2)
374 // Top-left aligned
375 coords.put(idx++, verts.get(i) / textureWidth); // Tu
376 coords.put(idx++, (height - verts.get(i + 1)) / textureHeight); // Tv
379 return coords;