package net.puppygames.applet.widgets; import java.util.HashMap; import java.util.Map; import net.puppygames.applet.Factory; import net.puppygames.applet.Pool; import net.puppygames.applet.SimplePool; import com.shavenpuppy.jglib.sprites.SimpleRenderable; import com.shavenpuppy.jglib.sprites.SimpleRenderer; import static org.lwjgl.opengl.GL11.*; /** * Renders a circle using a triangle strip. */ public class RenderedCircle implements SimpleRenderable { /** Number of segments per radius */ private static final float DEFAULT_SEGMENTS_PER_RADIUS = 0.25f; /** Minimum number of segments */ private static final int MIN_SEGMENTS = 8; /** Maximum number of segments */ private static final int MAX_SEGMENTS = 256; /** Pools of indicies. Note: could be optimised to use sparse int mapping. */ private static Map<Integer, Pool<short[]>> INDEX_POOL = new HashMap<Integer, Pool<short[]>>(); /** Segments per radius */ private float segmentsPerRadius = DEFAULT_SEGMENTS_PER_RADIUS; /** Location */ private float x, y; /** Radius */ private float radius; /** Thickness */ private float thickness = 1.0f; /** * C'tor */ public RenderedCircle() { } /** * C'tor * @param x * @param y * @param radius * @param thickness */ public RenderedCircle(float x, float y, float radius, float thickness) { this.x = x; this.y = y; this.radius = radius; this.thickness = thickness; } public void setSegmentsPerRadius(float segmentsPerRadius) { this.segmentsPerRadius = segmentsPerRadius; } public void setRadius(float radius) { this.radius = radius; } public void setThickness(float thickness) { this.thickness = thickness; } public void setLocation(float x, float y) { this.x = x; this.y = y; } @Override public void render(SimpleRenderer renderer) { if (radius <= 0.0f || thickness <= 0.0f) { return; } int segments = (int) Math.max(MIN_SEGMENTS, Math.max(MAX_SEGMENTS, radius * segmentsPerRadius)); // Find indices in the pool short[] indices = obtainIndices(segments * 2 + 2); short offset = renderer.getVertexOffset(); int index = 0; for (int i = 0; i <= segments; i ++) { double angle = i * Math.PI * 2.0 / segments; renderer.glTexCoord2f(0.0f, 0.0f); renderer.glVertex2f(x + (float) Math.cos(angle) * radius, y + (float) Math.sin(angle) * radius); renderer.glTexCoord2f(1.0f, 0.0f); renderer.glVertex2f(x + (float) Math.cos(angle) * (radius - thickness), y + (float) Math.sin(angle) * (radius - thickness)); indices[index ++] = (short) (offset + index); } indices[index ++] = offset; indices[index ++] = (short) (offset + 1); renderer.glRender(GL_TRIANGLE_STRIP, indices); // Release indices back to the pool releaseIndices(indices); } private static short[] obtainIndices(final int size) { Integer s = new Integer(size); Pool<short[]> pooled = INDEX_POOL.get(s); if (pooled == null) { pooled = new SimplePool<short[]>(new Factory<short[]>() { @Override public short[] createNew() { return new short[size]; } }, 1); INDEX_POOL.put(s, pooled); } return pooled.obtain(); } private static void releaseIndices(short[] indices) { Integer s = new Integer(indices.length); Pool<short[]> pooled = INDEX_POOL.get(s); pooled.release(indices); } }