gov.nasa.worldwind.render.FrameFactory Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.render;
import com.jogamp.common.nio.Buffers;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.util.Logging;
import com.jogamp.opengl.*;
import java.awt.*;
import java.nio.DoubleBuffer;
/**
* Static class for drawing 2D frames. All shapes are drawn inside a bounding rectangle whose lower left corner is
* at the origin. Shapes with a leader use an offset point that indicate where the leader triangle should point at - it
* usually has a negative y since the leader connects at the bottom of the frame (at y = 0).
*
* @author Patrick Murris
* @version $Id: FrameFactory.java 1171 2013-02-11 21:45:02Z dcollins $
* @see AbstractAnnotation
*/
public class FrameFactory
{
/** @deprecated Use {@link AVKey#SHAPE_RECTANGLE} instead. */
@Deprecated
public static final String SHAPE_RECTANGLE = AVKey.SHAPE_RECTANGLE;
/** @deprecated Use {@link AVKey#SHAPE_ELLIPSE} instead. */
@Deprecated
public static final String SHAPE_ELLIPSE = AVKey.SHAPE_ELLIPSE;
/** @deprecated Use {@link AVKey#SHAPE_NONE} instead. */
@Deprecated
public static final String SHAPE_NONE = AVKey.SHAPE_NONE;
/** @deprecated Use {@link AVKey#SHAPE_TRIANGLE} instead. */
@Deprecated
public static final String LEADER_TRIANGLE = AVKey.SHAPE_TRIANGLE;
/** @deprecated Use {@link AVKey#SHAPE_NONE} instead. */
@Deprecated
public static final String LEADER_NONE = AVKey.SHAPE_NONE;
private static int cornerSteps = 16;
private static int circleSteps = 64;
/**
* Draw a shape with the specified width and height, gl mode and corner radius. GL mode came be one of
* GL.GL_TRIANGLE_FAN
and GL.LINE_STRIP
. Corner radius only apply to
* SHAPE_RECTANGLE
- set to zero for square corners.
*
* @param dc the current DrawContext
.
* @param shape the shape - can be one of SHAPE_RECTANGLE
or SHAPE_ELLIPSE
.
* @param width the width of the overall shape.
* @param height the height of the shape.
* @param glMode the GL mode - can be one of GL.GL_TRIANGLE_FAN
and GL.LINE_STRIP
.
* @param cornerRadius the rounded corners radius. Set to zero for square corners.
*/
public static void drawShape(DrawContext dc, String shape, double width, double height, int glMode,
int cornerRadius)
{
if (!shape.equals(AVKey.SHAPE_NONE))
drawBuffer(dc, glMode, createShapeBuffer(shape, width, height, cornerRadius, null));
}
/**
* Draw a shape with the specified width and height, gl mode and corner radius. The shape includes a leader triangle
* pointing to a specified point. GL mode came be one of GL.GL_TRIANGLE_FAN
and
* GL.LINE_STRIP
. Corner radius only apply to SHAPE_RECTANGLE
- set to zero for square
* corners.
*
* @param dc the current DrawContext
.
* @param shape the shape - can be one of SHAPE_RECTANGLE
or SHAPE_ELLIPSE
.
* @param width the width of the overall shape.
* @param height the height of the shape excluding the leader.
* @param leaderOffset the coordinates of the point to which the leader leads.
* @param leaderGapWidth the starting width of the leader shape.
* @param glMode the GL mode - can be one of GL.GL_TRIANGLE_FAN
and
* GL.LINE_STRIP
.
* @param cornerRadius the rounded corners radius. Set to zero for square corners.
*/
public static void drawShapeWithLeader(DrawContext dc, String shape, double width, double height,
Point leaderOffset, double leaderGapWidth, int glMode, int cornerRadius)
{
if (!shape.equals(AVKey.SHAPE_NONE))
drawBuffer(dc, glMode,
createShapeWithLeaderBuffer(shape, width, height, leaderOffset, leaderGapWidth, cornerRadius, null));
}
/**
* Create a vertex buffer for a shape with the specified width, height and corner radius. Corner radius only apply
* to SHAPE_RECTANGLE
- set to zero for square corners.
*
* @param shape the shape - can be one of SHAPE_RECTANGLE
or SHAPE_ELLIPSE
.
* @param width the width of the overall shape.
* @param height the height of the shape.
* @param cornerRadius the rounded corners radius. Set to zero for square corners.
* @param buffer the buffer to store shape vertices, or null to allocate a new buffer.
*
* @return the vertex buffer.
*/
public static DoubleBuffer createShapeBuffer(String shape, double width, double height, int cornerRadius,
DoubleBuffer buffer)
{
if (shape.equals(AVKey.SHAPE_RECTANGLE))
return createRoundedRectangleBuffer(width, height, cornerRadius, buffer);
else if (shape.equals(AVKey.SHAPE_ELLIPSE))
return createEllipseBuffer(width, height, circleSteps, buffer);
else if (shape.equals(AVKey.SHAPE_NONE))
return null;
else
// default to rectangle if shape unknown
return createRoundedRectangleBuffer(width, height, cornerRadius, buffer);
}
/**
* Create a vertex buffer for a shape with the specified width, height and corner radius. The shape includes a
* leader triangle pointing to a specified point. Corner radius only apply to SHAPE_RECTANGLE
- set to
* zero for square corners.
*
* @param shape the shape - can be one of SHAPE_RECTANGLE
or SHAPE_ELLIPSE
.
* @param width the width of the overall shape.
* @param height the height of the shape excluding the leader.
* @param leaderOffset the coordinates of the point to which the leader leads.
* @param leaderGapWidth the starting width of the leader shape.
* @param cornerRadius the rounded corners radius. Set to zero for square corners.
* @param buffer the buffer to store shape vertices, or null to allocate a new buffer.
*
* @return the vertex buffer.
*/
public static DoubleBuffer createShapeWithLeaderBuffer(String shape, double width, double height,
Point leaderOffset, double leaderGapWidth, int cornerRadius, DoubleBuffer buffer)
{
if (shape.equals(AVKey.SHAPE_RECTANGLE))
return createRoundedRectangleWithLeaderBuffer(width, height, leaderOffset, leaderGapWidth, cornerRadius,
buffer);
else if (shape.equals(AVKey.SHAPE_ELLIPSE))
return createEllipseWithLeaderBuffer(width, height, leaderOffset, leaderGapWidth, circleSteps, buffer);
else if (shape.equals(AVKey.SHAPE_NONE))
return null;
else
// default to rectangle if shape unknown
return createRoundedRectangleWithLeaderBuffer(width, height, leaderOffset, leaderGapWidth, cornerRadius,
buffer);
}
/**
* Draw a vertex buffer in a given gl mode. Vertex buffers coming from the createShapeBuffer() methods support both
* GL.GL_TRIANGLE_FAN
and GL.LINE_STRIP
.
*
* @param dc the current DrawContext.
* @param mode the desired drawing GL mode.
* @param count the number of vertices to draw.
* @param verts the vertex buffer to draw.
*/
public static void drawBuffer(DrawContext dc, int mode, int count, DoubleBuffer verts)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (verts == null)
{
String message = Logging.getMessage("nullValue.BufferIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
// Set up
gl.glPushClientAttrib(GL2.GL_CLIENT_VERTEX_ARRAY_BIT);
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
gl.glVertexPointer(2, GL2.GL_DOUBLE, 0, verts);
// Draw
gl.glDrawArrays(mode, 0, count);
// Restore
gl.glPopClientAttrib();
}
/**
* Draw a vertex buffer with texture coordinates in a given gl mode. Vertex buffers coming from the
* createShapeBuffer() methods support both GL.GL_TRIANGLE_FAN
and GL.LINE_STRIP
.
*
* @param dc the current DrawContext.
* @param mode the desired drawing GL mode.
* @param count the number of vertices to draw.
* @param verts the vertex buffer to draw.
* @param coords the buffer containing the shape texture coordinates.
*/
public static void drawBuffer(DrawContext dc, int mode, int count, DoubleBuffer verts, DoubleBuffer coords)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (verts == null || coords == null)
{
String message = Logging.getMessage("nullValue.BufferIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
// Set up
gl.glPushClientAttrib(GL2.GL_CLIENT_VERTEX_ARRAY_BIT);
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(2, GL2.GL_DOUBLE, 0, verts);
gl.glTexCoordPointer(2, GL2.GL_DOUBLE, 0, coords);
// Draw
gl.glDrawArrays(mode, 0, count);
// Restore
gl.glPopClientAttrib();
}
public static void drawBuffer(DrawContext dc, int mode, DoubleBuffer verts)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (verts == null)
{
String message = Logging.getMessage("nullValue.BufferIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int count = verts.remaining() / 2;
drawBuffer(dc, mode, count, verts);
}
//-- Shape creation
//-- Rectangle ------------------------------------------------------------------
private static DoubleBuffer createRoundedRectangleBuffer(double width, double height, int cornerRadius,
DoubleBuffer buffer)
{
int numVertices = 9 + (cornerRadius < 1 ? 0 : 4 * (cornerSteps - 2));
buffer = allocateVertexBuffer(numVertices, buffer);
int idx = 0;
// Drawing counter clockwise from bottom-left
// Bottom
buffer.put(idx++, (double) cornerRadius);
buffer.put(idx++, 0d);
buffer.put(idx++, width - cornerRadius);
buffer.put(idx++, 0d);
idx = drawCorner(width - cornerRadius, cornerRadius, cornerRadius, -Math.PI / 2, 0, cornerSteps, buffer, idx);
// Right
buffer.put(idx++, width);
buffer.put(idx++, (double) cornerRadius);
buffer.put(idx++, width);
buffer.put(idx++, height - cornerRadius);
idx = drawCorner(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, cornerSteps, buffer,
idx);
// Top
buffer.put(idx++, width - cornerRadius);
buffer.put(idx++, height);
buffer.put(idx++, (double) cornerRadius);
buffer.put(idx++, height);
idx = drawCorner(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, cornerSteps, buffer,
idx);
// Left
buffer.put(idx++, 0d);
buffer.put(idx++, height - cornerRadius);
buffer.put(idx++, 0d);
buffer.put(idx++, (double) cornerRadius);
idx = drawCorner(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 1.5, cornerSteps, buffer, idx);
// Finish up to starting point
buffer.put(idx++, (double) cornerRadius);
buffer.put(idx++, 0d);
buffer.limit(idx);
return buffer;
}
private static DoubleBuffer createRoundedRectangleWithLeaderBuffer(double width, double height, Point leaderOffset,
double leaderGapWidth, int cornerRadius, DoubleBuffer buffer)
{
int numVertices = 12 + (cornerRadius < 1 ? 0 : 4 * (cornerSteps - 2));
buffer = allocateVertexBuffer(numVertices, buffer);
int idx = 0;
// Drawing counter clockwise from right leader connection at the bottom
// so as to accommodate GL_TRIANGLE_FAN and GL_LINE_STRIP (inside and border)
// Bottom right
buffer.put(idx++, width / 2 + leaderGapWidth / 2);
buffer.put(idx++, 0d);
buffer.put(idx++, width - cornerRadius);
buffer.put(idx++, 0d);
idx = drawCorner(width - cornerRadius, cornerRadius, cornerRadius, -Math.PI / 2, 0, cornerSteps, buffer, idx);
// Right
buffer.put(idx++, width);
buffer.put(idx++, (double) cornerRadius);
buffer.put(idx++, width);
buffer.put(idx++, height - cornerRadius);
idx = drawCorner(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, cornerSteps, buffer,
idx);
// Top
buffer.put(idx++, width - cornerRadius);
buffer.put(idx++, height);
buffer.put(idx++, (double) cornerRadius);
buffer.put(idx++, height);
idx = drawCorner(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, cornerSteps, buffer,
idx);
// Left
buffer.put(idx++, 0d);
buffer.put(idx++, height - cornerRadius);
buffer.put(idx++, 0d);
buffer.put(idx++, (double) cornerRadius);
idx = drawCorner(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 1.5, cornerSteps, buffer, idx);
// Bottom left
buffer.put(idx++, (double) cornerRadius);
buffer.put(idx++, 0d);
buffer.put(idx++, width / 2 - leaderGapWidth / 2);
buffer.put(idx++, 0d);
// Draw leader
buffer.put(idx++, leaderOffset.x);
buffer.put(idx++, leaderOffset.y);
buffer.put(idx++, width / 2 + leaderGapWidth / 2);
buffer.put(idx++, 0d);
buffer.limit(idx);
return buffer;
}
private static int drawCorner(double x0, double y0, double cornerRadius, double start, double end, int steps,
DoubleBuffer buffer, int startIdx)
{
if (cornerRadius < 1)
return startIdx;
double step = (end - start) / (steps - 1);
for (int i = 1; i < steps - 1; i++)
{
double a = start + step * i;
double x = x0 + Math.cos(a) * cornerRadius;
double y = y0 + Math.sin(a) * cornerRadius;
buffer.put(startIdx++, x);
buffer.put(startIdx++, y);
}
return startIdx;
}
//-- Circle / Ellipse -----------------------------------------------------------
private static DoubleBuffer createEllipseBuffer(double width, double height, int steps, DoubleBuffer buffer)
{
int numVertices = steps + 1;
buffer = allocateVertexBuffer(numVertices, buffer);
// Drawing counter clockwise from bottom-left
double halfWidth = width / 2;
double halfHeight = height / 2;
double halfPI = Math.PI / 2;
double x0 = halfWidth;
double y0 = halfHeight;
double step = Math.PI * 2 / steps;
int idx = 0;
for (int i = 0; i <= steps; i++)
{
double a = step * i - halfPI;
double x = x0 + Math.cos(a) * halfWidth;
double y = y0 + Math.sin(a) * halfHeight;
buffer.put(idx++, x);
buffer.put(idx++, y);
}
buffer.limit(idx);
return buffer;
}
private static DoubleBuffer createEllipseWithLeaderBuffer(double width, double height, Point leaderOffset,
double leaderGapWidth, int steps, DoubleBuffer buffer)
{
int numVertices = steps + 3;
buffer = allocateVertexBuffer(numVertices, buffer);
// Drawing counter clockwise from right leader connection at the bottom
// so as to accomodate GL_TRIANGLE_FAN and GL_LINE_STRIP (inside and border)
double halfWidth = width / 2;
double halfHeight = height / 2;
double halfPI = Math.PI / 2;
double x0 = halfWidth;
double y0 = halfHeight;
double step = Math.PI * 2 / steps;
double halfGap = leaderGapWidth / 2 / halfWidth;
int idx = 0;
for (int i = 0; i <= steps; i++)
{
double a = step * i - halfPI;
if (i == 0)
a += halfGap;
if (i == steps)
a -= halfGap;
double x = x0 + Math.cos(a) * halfWidth;
double y = y0 + Math.sin(a) * halfHeight;
buffer.put(idx++, x);
buffer.put(idx++, y);
}
// Draw leader
buffer.put(idx++, leaderOffset.x);
buffer.put(idx++, leaderOffset.y);
buffer.put(idx++, x0 + Math.cos(halfGap - halfPI) * halfWidth);
buffer.put(idx++, y0 + Math.sin(halfGap - halfPI) * halfHeight);
buffer.limit(idx);
return buffer;
}
//-- Utility Methods
private static DoubleBuffer allocateVertexBuffer(int numVertices, DoubleBuffer buffer)
{
int numCoords = 2 * numVertices;
if (buffer != null)
buffer.clear();
if (buffer == null || buffer.capacity() < numCoords)
buffer = Buffers.newDirectDoubleBuffer(numCoords);
return buffer;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy