import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.Line2D; import java.util.ArrayList; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.swing.JFrame; /** * Make a colorful tree. */ public class Tree extends JFrame { /** * A private class to capture all of the information used to describe a branch * in a tree */ private class Limb { /** The starting x location */ double sx; /** The starting y location */ double sy; /** The length */ double len; /** The angle of the branch. 0 horizontal, left to right. Measured in radians */ double angle; /** The width of the branch */ double width; /** The color or the branch */ Color limbColor; /** * Constructor That initializes the values describing a branch * @param ssx * @param ssy * @param llen * @param aangle * @param wd * @param c */ public Limb(double ssx, double ssy, double llen, double aangle, double wd, Color c) { this.sx=ssx; this.sy=ssy; this.len=llen; this.angle=aangle; this.width=wd; this.limbColor = c; } /** The x location of the end of the branch */ public double ex() { return this.sx+Math.cos(angle)*len; } /** The y location of the end of the branch */ public double ey() { return this.sy+Math.sin(angle)*len; } } /** A set of colors */ Color[] colors = { Color.BLACK, Color.BLUE, new Color(1.0f, 1.0f, 0.0f), Color.PINK}; int lastLimb = 0; /** Used to control redrawing */ AtomicInteger lcount; /** Also used in redrawing. */ AtomicBoolean ab; /** A list of every branch in the tree */ ArrayList ls; /** a random number generator */ Random rand; /** Set up */ public Tree(){ rand = new Random(); lcount = new AtomicInteger(); ab = new AtomicBoolean(); ls = new ArrayList<>(); lastLimb=0; setSize(820, 820); setTitle("JFrame"); setBackground(Color.WHITE); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } /** How many limbs needs to be added between redraws. Redraws look odd, so infrequent redraws are better. */ private final int REPAINT_FREQ = 40000; /** * Called to determine if I want to redraw */ private void painter() { if (lcount.get()>REPAINT_FREQ) { ab.set(true); repaint(); lcount.set(0); int wc=0; while (ab.get()) { try { Thread.sleep(5); } catch (Exception e) {} } } } /** * Add a limb. This is a recursive function that add limbs depth first * @param ssx the x position of the start of a limb * @param ssy the Y position of the start of a limb * @param llen the length of the limb * @param aangle the angle of the limb (in radians) */ public void addL(double ssx, double ssy, double llen, double aangle) { if (llen<2) return; lcount.incrementAndGet(); //System.out.println(lcount.get()); if (llen<1) { painter(); return; } Limb l = new Limb(ssx, ssy, llen, aangle, rand.nextDouble()*10, colors[rand.nextInt(colors.length)]); ls.add(l); painter(); // very simple, for every limb, add 3 addL(l.ex(), l.ey(), llen*0.60, aangle+Math.PI/6); addL(l.ex(), l.ey(), llen*0.80, aangle); addL(l.ex(), l.ey(), llen*0.60, aangle-Math.PI/6); } /** * Be sure that the final complete tree is drawn */ public void finalize() { lcount.set(REPAINT_FREQ+1); repaint(); } public static void main(String[] args) { Tree m = new Tree(); //m.repaint(); m.addL(410, 800, 150, -Math.PI/2); m.finalize(); } /** * This is called in a separate thread from repaint(). * */ public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; // this loop will only draw the limbs added to the tree for (int i=lastLimb; i