Recently I found myself in need of some scalable icons for use as nodes in a zoomable graph scene. Because the user would be zooming in and out within the scene, the orbs couldn't be a plain image pixmap, but needed to be vector based so they wouldn't pixelate when zoomed in. I don't know of any repository for Java 2D shapes, so it looked like I was on my own. Before I explain what I did and show some code, here's the final result:

The example method below takes in a single base color for painting, so a simple change in the method parameter can change your orb:

The order of operations are as follows:
- Fill a circle with a gradient, from the base color to about 4 steps darker. Give the gradient about a 45 degree angle to the right
- Create a radial gradient (sorry, Java 1.6+ only) at 1/2 width, 1/4 height, with a radius about equal to the orb radius. Feather from white to nearly transparent.
- Trace a thin white line around the edge of the orb. Use a gradient paint that goes from White at 0, 0 to transparent at width, height
- Throw in a few QuadCurves for effect.
Below is the canvas I used. Simply place it within a JFrame to do a live demo. The code could be tweaked for performance, but this is simpler to understand. In particular, it would be a huge benefit to cache the resulting orb to a BufferedImage for a given base color and canvas width/height.
package com.swinggeek.orbdemo;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.QuadCurve2D;
import javax.swing.JComponent;
/**
* @author Jason Nichols (jason@swinggeek.com)
*/
public class OrbCanvas extends JComponent {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//
// Background fill
//
g2d.setPaint(new Color(10, 10, 10));
g2d.fillRect(0, 0, getWidth(), getHeight());
// Center Point
Point center = new Point(getWidth()/2, getHeight()/2);
int radius = Math.min(getWidth(), getHeight())/3;
Color orbColor = new Color(240, 10, 10);
paintOrb(g2d, orbColor, center, radius);
}
private void paintOrb(Graphics2D g, Color orbColor, Point center, int radius) {
//
// Use a throw away Graphics reference for simplicity
//
Graphics2D g2d = (Graphics2D)g.create();
//
// What we do is paint the orb at a fixed size, and then scale as
// needed to the radius specified above. This makes tweaking the
// orb easier when coding, since we can use hard-coded pixels instead
// of fractions, but still gives us the scalable graphics we need.
//
//
// Scale by whatever factor gives us the appropriate radius, then
// translate so that we can do our painting from 0, 0 to radius, radius.
//
float scale = radius / 8;
g2d.scale(scale, scale);
g2d.translate(center.x / scale - 8, center.y / scale - 8);
//
// Paint the base orb.
//
Ellipse2D orb = new Ellipse2D.Float(0, 0, 16, 16);
g2d.setPaint(new GradientPaint(0, 0, orbColor, 5, 12,
orbColor.darker().darker().darker().darker(), false));
g2d.fill(orb);
//
// Fill our glare at the top. Since we only fill the base shape, having
// a portion of the radial paint outside the orb doesn't have an effect.
//
float[] dist = {0.0f, 0.9f};
Color[] colors ={new Color(255, 255, 255, 150), new Color(255, 255, 255, 0)};
RadialGradientPaint gp = new RadialGradientPaint(8, 4, 8, dist, colors,
CycleMethod.NO_CYCLE);
g2d.setPaint(gp);
g2d.fill(orb);
//
// This simplifies our painting below, when we want to trace the orb and
// throw in a few lines.
//
g2d.setClip(orb);
//
// Trace the top edge of the orb with a white light
//
g2d.setStroke(new BasicStroke(0.3f));
g2d.setPaint(new GradientPaint(0, 0, new Color(255, 255, 255, 40),8, 8,
new Color(255, 255, 255, 0)));
g2d.draw(orb);
//
// Let's throw in a few QuadCurves
//
g2d.setStroke(new BasicStroke(0.1f));
g2d.setPaint(new GradientPaint(16, 0, new Color(255, 255, 255, 40), 6, 0,
new Color(255, 255, 255, 20)));
//
// Base curve used for all lines
//
QuadCurve2D curve = new QuadCurve2D.Float(0,8, 8, 11, 16, 8);
//
// It's easier to just rotate and use the same curve line
//
g2d.rotate(-Math.PI/3, 8, 8);
g2d.draw(curve);
g2d.rotate(Math.PI/6, 8, 8);
g2d.draw(curve);
g2d.dispose();
}
}