Itβs not clear what your real question is. There are quite a few operations that you often need when doing this kind of graphical programming, and the built-in Java2D functionality here is pretty rudimentary. You can create Point2D and Line2D , and basically have all the structures available to you, but some calculations ... are inconvenient (to say the least), and some are not supported properly at all. For example, you can only check if two Line2D objects Line2D . But there is no built-in way to check where they intersect.
However, when I saw the site you contacted, I thought, βHey, that can be fun.β
And it was fun :-)

I think most of the questions that may arise about this implicitly answer the code below (sorry if there arenβt enough comments), but feel free to ask a more focused question about details that are not clear).
For the reasons stated above, I began to create a small library of "commonly used tools for working with geometry." Some of the classes from this library are partially included in the example below, so this is a separate example.
package stackoverflow.shadows; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.FlatteningPathIterator; import java.awt.geom.Line2D; import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class ShadowsTest { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } private static void createAndShowGUI() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().add(new ShadowsTestPanel()); f.setSize(500,500); f.setLocationRelativeTo(null); f.setVisible(true); } } class ShadowsTestPanel extends JPanel implements MouseMotionListener { private final List<Shape> shapes; private final Point2D lightPosition; private final List<Line2D> borderLineSegments; private final List<List<Line2D>> shapesLineSegments; private final BufferedImage smileyImage; private final BufferedImage skullImage; private final BufferedImage blendedImage; ShadowsTestPanel() { addMouseMotionListener(this); shapes = new ArrayList<Shape>(); shapes.add(new Rectangle2D.Double(160, 70, 80, 50)); shapes.add(new Ellipse2D.Double(290, 120, 50, 30)); AffineTransform at0 = AffineTransform.getRotateInstance( Math.toRadians(45), 320, 290); shapes.add( at0.createTransformedShape( new Rectangle2D.Double(300, 270, 40, 40))); shapes.add(new Ellipse2D.Double(60, 240, 80, 110)); shapesLineSegments = new ArrayList<List<Line2D>>(); for (Shape shape : shapes) { shapesLineSegments.add(Shapes.computeLineSegments(shape, 1.0)); } borderLineSegments = new ArrayList<Line2D>(); shapesLineSegments.add(borderLineSegments); lightPosition = new Point2D.Double(); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { borderLineSegments.clear(); borderLineSegments.add( new Line2D.Double(0,0,getWidth(),0)); borderLineSegments.add( new Line2D.Double(getWidth(),0,getWidth(),getHeight())); borderLineSegments.add( new Line2D.Double(getWidth(),getHeight(),0,getHeight())); borderLineSegments.add( new Line2D.Double(0,getHeight(),0,0)); } }); smileyImage = createSmileyImage(); skullImage = createSkullImage(); blendedImage = createSmileyImage(); } private static BufferedImage createSmileyImage() { BufferedImage image = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB); Graphics2D g = image.createGraphics(); g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setStroke(new BasicStroke(5)); g.setColor(Color.YELLOW); g.fill(new Ellipse2D.Double(5, 5, 140, 140)); g.setColor(Color.BLACK); g.draw(new Ellipse2D.Double(5, 5, 140, 140)); g.fill(new Ellipse2D.Double( 50-15, 50-15, 30, 30)); g.fill(new Ellipse2D.Double(100-15, 50-15, 30, 30)); g.draw(new Arc2D.Double(25, 25, 100, 100, 190, 160, Arc2D.OPEN)); g.dispose(); return image; } private static BufferedImage createSkullImage() { BufferedImage image = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB); Graphics2D g = image.createGraphics(); g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setStroke(new BasicStroke(5)); g.setColor(Color.WHITE); g.fill(new Ellipse2D.Double(5, 5, 140, 140)); g.setColor(Color.BLACK); g.draw(new Ellipse2D.Double(5, 5, 140, 140)); g.fill(new Ellipse2D.Double( 50-15, 50-15, 30, 30)); g.fill(new Ellipse2D.Double(100-15, 50-15, 30, 30)); Shape mouth = new Arc2D.Double(25, 25, 100, 100, 190, 160, Arc2D.OPEN); List<Line2D> lineSegments = Shapes.computeLineSegments(mouth, 2); for (int i=0; i<lineSegments.size(); i++) { Line2D line = lineSegments.get(i); Rectangle b = line.getBounds(); Rectangle r = new Rectangle(bx, by-8, b.width, 16); g.setColor(Color.WHITE); g.fill(r); g.setColor(Color.BLACK); g.draw(r); } g.dispose(); return image; } @Override protected void paintComponent(Graphics gr) { super.paintComponent(gr); Graphics2D g = (Graphics2D)gr; g.setColor(new Color(0,0,0,200)); g.fillRect(0,0,getWidth(),getHeight()); g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(Color.BLACK); for (Shape shape : shapes) { g.draw(shape); } List<Line2D> rays = createRays(lightPosition);