Skip to content

Instantly share code, notes, and snippets.

@Samuel-Retter
Created January 1, 2016 20:49
Show Gist options
  • Save Samuel-Retter/16e8c9883ff6a8b5dadb to your computer and use it in GitHub Desktop.
Save Samuel-Retter/16e8c9883ff6a8b5dadb to your computer and use it in GitHub Desktop.
Turtle graphics, L-ssytems, and fractals!
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.8</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Turtle.iml" filepath="$PROJECT_DIR$/Turtle.iml" />
</modules>
</component>
</project>
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class Box {
static int ITERATIONS;
public Box() { /* compiled code */ }
public static void drawBox(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class CesaroKoch {
static int ITERATIONS;
public CesaroKoch() { /* compiled code */ }
public static void drawCesaroKoch(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class CrystalSquare {
static int ITERATIONS;
public CrystalSquare() { /* compiled code */ }
public static void drawCrystalSquare(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class DragonCurve {
static int ITERATIONS;
public DragonCurve() { /* compiled code */ }
public static void drawDragonCurve(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class FractalPlant {
static int ITERATIONS;
public FractalPlant() { /* compiled code */ }
public static void drawFractalPlant(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class GosperCurve {
static int ITERATIONS;
public GosperCurve() { /* compiled code */ }
public static void drawGosperCurve(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class HilbertCurve {
static int ITERATIONS;
public HilbertCurve() { /* compiled code */ }
public static void drawHilbertCurve(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class LevyCurve {
static int ITERATIONS;
public LevyCurve() { /* compiled code */ }
public static void drawLevyCurve(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class LSystem {
java.util.HashMap<LSystem.Symbol,LSystem.Symbol[]> rules;
java.util.ArrayList<LSystem.Symbol> axiom;
public LSystem(java.util.HashMap<LSystem.Symbol,LSystem.Symbol[]> rules0, java.util.ArrayList<LSystem.Symbol> axiom0) { /* compiled code */ }
public java.util.ArrayList<LSystem.Symbol> expandedList(java.util.ArrayList<LSystem.Symbol> list) { /* compiled code */ }
public static enum Symbol {
A, B, C, F, L, M, N, O, P, Q, R, X, Y;
public static LSystem.Symbol[] values() { /* compiled code */ }
public static LSystem.Symbol valueOf(java.lang.String name) { /* compiled code */ }
private Symbol() { /* compiled code */ }
}
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class PenroseTiling {
static int ITERATIONS;
public PenroseTiling() { /* compiled code */ }
public static void drawPenroseTiling(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class PythagorasTree {
static int ITERATIONS;
public PythagorasTree() { /* compiled code */ }
public static void drawPythagorasTree(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class Rings {
static int ITERATIONS;
public Rings() { /* compiled code */ }
public static void drawRings(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class Seaweed {
static int ITERATIONS;
public Seaweed() { /* compiled code */ }
public static void drawSeaweed(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class Sierpinski {
static int ITERATIONS;
public Sierpinski() { /* compiled code */ }
public static void drawSierpinski(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class Snowflake {
static int ITERATIONS;
public Snowflake() { /* compiled code */ }
public static void drawSnowflake(java.util.ArrayList<LSystem.Symbol> symbols) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public final class StdDraw implements java.awt.event.ActionListener, java.awt.event.MouseListener, java.awt.event.MouseMotionListener, java.awt.event.KeyListener {
public static final java.awt.Color BLACK;
public static final java.awt.Color BLUE;
public static final java.awt.Color CYAN;
public static final java.awt.Color DARK_GRAY;
public static final java.awt.Color GRAY;
public static final java.awt.Color GREEN;
public static final java.awt.Color LIGHT_GRAY;
public static final java.awt.Color MAGENTA;
public static final java.awt.Color ORANGE;
public static final java.awt.Color PINK;
public static final java.awt.Color RED;
public static final java.awt.Color WHITE;
public static final java.awt.Color YELLOW;
public static final java.awt.Color BOOK_BLUE;
public static final java.awt.Color BOOK_LIGHT_BLUE;
public static final java.awt.Color BOOK_RED;
private static final java.awt.Color DEFAULT_PEN_COLOR;
private static final java.awt.Color DEFAULT_CLEAR_COLOR;
private static java.awt.Color penColor;
private static final int DEFAULT_SIZE = 512;
private static int width;
private static int height;
private static final double DEFAULT_PEN_RADIUS = 0.002;
private static double penRadius;
private static boolean defer;
private static final double BORDER = 0.0;
private static final double DEFAULT_XMIN = 0.0;
private static final double DEFAULT_XMAX = 1.0;
private static final double DEFAULT_YMIN = 0.0;
private static final double DEFAULT_YMAX = 1.0;
private static double xmin;
private static double ymin;
private static double xmax;
private static double ymax;
private static java.lang.Object mouseLock;
private static java.lang.Object keyLock;
private static final java.awt.Font DEFAULT_FONT;
private static java.awt.Font font;
private static java.awt.image.BufferedImage offscreenImage;
private static java.awt.image.BufferedImage onscreenImage;
private static java.awt.Graphics2D offscreen;
private static java.awt.Graphics2D onscreen;
private static StdDraw std;
private static javax.swing.JFrame frame;
private static boolean mousePressed;
private static double mouseX;
private static double mouseY;
private static java.util.LinkedList<java.lang.Character> keysTyped;
private static java.util.TreeSet<java.lang.Integer> keysDown;
private static long nextDraw;
private StdDraw() { /* compiled code */ }
public static void setCanvasSize() { /* compiled code */ }
public static void setCanvasSize(int canvasWidth, int canvasHeight) { /* compiled code */ }
private static void init() { /* compiled code */ }
private static javax.swing.JMenuBar createMenuBar() { /* compiled code */ }
public static void setXscale() { /* compiled code */ }
public static void setYscale() { /* compiled code */ }
public static void setScale() { /* compiled code */ }
public static void setXscale(double min, double max) { /* compiled code */ }
public static void setYscale(double min, double max) { /* compiled code */ }
public static void setScale(double min, double max) { /* compiled code */ }
private static double scaleX(double x) { /* compiled code */ }
private static double scaleY(double y) { /* compiled code */ }
private static double factorX(double w) { /* compiled code */ }
private static double factorY(double h) { /* compiled code */ }
private static double userX(double x) { /* compiled code */ }
private static double userY(double y) { /* compiled code */ }
public static void clear() { /* compiled code */ }
public static void clear(java.awt.Color color) { /* compiled code */ }
public static double getPenRadius() { /* compiled code */ }
public static void setPenRadius() { /* compiled code */ }
public static void setPenRadius(double radius) { /* compiled code */ }
public static java.awt.Color getPenColor() { /* compiled code */ }
public static void setPenColor() { /* compiled code */ }
public static void setPenColor(java.awt.Color color) { /* compiled code */ }
public static void setPenColor(int red, int green, int blue) { /* compiled code */ }
public static java.awt.Font getFont() { /* compiled code */ }
public static void setFont() { /* compiled code */ }
public static void setFont(java.awt.Font font) { /* compiled code */ }
public static void line(double x0, double y0, double x1, double y1) { /* compiled code */ }
private static void pixel(double x, double y) { /* compiled code */ }
public static void point(double x, double y) { /* compiled code */ }
public static void circle(double x, double y, double radius) { /* compiled code */ }
public static void filledCircle(double x, double y, double radius) { /* compiled code */ }
public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { /* compiled code */ }
public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { /* compiled code */ }
public static void arc(double x, double y, double radius, double angle1, double angle2) { /* compiled code */ }
public static void square(double x, double y, double halfLength) { /* compiled code */ }
public static void filledSquare(double x, double y, double halfLength) { /* compiled code */ }
public static void rectangle(double x, double y, double halfWidth, double halfHeight) { /* compiled code */ }
public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) { /* compiled code */ }
public static void polygon(double[] x, double[] y) { /* compiled code */ }
public static void filledPolygon(double[] x, double[] y) { /* compiled code */ }
private static java.awt.Image getImage(java.lang.String filename) { /* compiled code */ }
public static void picture(double x, double y, java.lang.String filename) { /* compiled code */ }
public static void picture(double x, double y, java.lang.String filename, double degrees) { /* compiled code */ }
public static void picture(double x, double y, java.lang.String filename, double scaledWidth, double scaledHeight) { /* compiled code */ }
public static void picture(double x, double y, java.lang.String filename, double scaledWidth, double scaledHeight, double degrees) { /* compiled code */ }
public static void text(double x, double y, java.lang.String text) { /* compiled code */ }
public static void text(double x, double y, java.lang.String text, double degrees) { /* compiled code */ }
public static void textLeft(double x, double y, java.lang.String text) { /* compiled code */ }
public static void textRight(double x, double y, java.lang.String text) { /* compiled code */ }
public static void show(int t) { /* compiled code */ }
public static void show() { /* compiled code */ }
private static void draw() { /* compiled code */ }
public static void save(java.lang.String filename) { /* compiled code */ }
public void actionPerformed(java.awt.event.ActionEvent e) { /* compiled code */ }
public static boolean mousePressed() { /* compiled code */ }
public static double mouseX() { /* compiled code */ }
public static double mouseY() { /* compiled code */ }
public void mouseClicked(java.awt.event.MouseEvent e) { /* compiled code */ }
public void mouseEntered(java.awt.event.MouseEvent e) { /* compiled code */ }
public void mouseExited(java.awt.event.MouseEvent e) { /* compiled code */ }
public void mousePressed(java.awt.event.MouseEvent e) { /* compiled code */ }
public void mouseReleased(java.awt.event.MouseEvent e) { /* compiled code */ }
public void mouseDragged(java.awt.event.MouseEvent e) { /* compiled code */ }
public void mouseMoved(java.awt.event.MouseEvent e) { /* compiled code */ }
public static boolean hasNextKeyTyped() { /* compiled code */ }
public static char nextKeyTyped() { /* compiled code */ }
public static boolean isKeyPressed(int keycode) { /* compiled code */ }
public void keyTyped(java.awt.event.KeyEvent e) { /* compiled code */ }
public void keyPressed(java.awt.event.KeyEvent e) { /* compiled code */ }
public void keyReleased(java.awt.event.KeyEvent e) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
public class Turtle {
public double x;
public double y;
public double angle;
public Turtle(double x0, double y0, double a0) { /* compiled code */ }
public void turnLeft(double delta) { /* compiled code */ }
public void goForward(double step) { /* compiled code */ }
public void pause(int t) { /* compiled code */ }
public void setPenColor(java.awt.Color color) { /* compiled code */ }
public void setPenRadius(double radius) { /* compiled code */ }
public void setCanvasSize(int width, int height) { /* compiled code */ }
public void setXscale(double min, double max) { /* compiled code */ }
public void setYscale(double min, double max) { /* compiled code */ }
public static void main(java.lang.String[] args) { /* compiled code */ }
}
This program draws fractals using Lindenmayer Systems (L-Systems). I wrote all of the classes except Turtle.java and
StdDraw.java, both of which I found online.
The program is really simple to use - just run any fractal class (meaning any of them besides Turtle, StdDraw, and
LSystem) and it will draw the fractal. You can mess around with the ITERATIONS field which each class has, which
controls how detailed you want the fractal to be. They are currently set at what I think is an optimal viewing
experience.
DISCLAIMER: I didn't invent any of the actual L-Systems; I just implemented them.
Enjoy!
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/30/2015.
*/
public class Box {
static int ITERATIONS = 4;
// F means move forward, L means turn left 90 deg.
public static void drawBox(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.075,.075,0);
double stepSize = .0035*(Math.pow(.333,ITERATIONS-5));
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(stepSize);
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(90);
} else {
assert false;
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.F});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawBox(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/30/2015.
*/
public class CesaroKoch {
static int ITERATIONS = 7;
// F means move forward, R means turn right 60 deg., L means turn left 60 deg.
public static void drawCesaroKoch(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.03,.4,0);
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(.04*(Math.pow(.46,ITERATIONS-4)));
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(85);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(275); // turn right 85
} else {
assert false;
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawCesaroKoch(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/30/2015.
*/
public class CrystalSquare {
static int ITERATIONS = 5;
// F means move forward, L means turn left 90 deg.
public static void drawCrystalSquare(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.075,.075,0);
double stepSize = .0035*(Math.pow(.333,ITERATIONS-5));
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(stepSize);
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(90);
} else {
assert false;
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawCrystalSquare(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/29/2015.
*/
public class DragonCurve {
static int ITERATIONS = 13;
// F means move forward, R means turn right 90 deg., L means turn left 90 deg.
public static void drawDragonCurve(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle;
int type; // depending on how many iterations, the curve will be oriented dirrerently,
// so we plan around that in order to show the picture nicely
type = ((int)Math.ceil(ITERATIONS/2.0)) % 4; // divides the integers into 4 equivalence classes
double far = .65;
double near = .35;
if (type == 1) {
turtle = new Turtle(near, far, 0);
} else if (type == 2) {
turtle = new Turtle(far, far,0);
} else if (type == 3) {
turtle = new Turtle(far, near,0);
} else if (type == 0) {
turtle = new Turtle(near, near,0);
} else {
turtle = new Turtle(0,0,0);
assert false;
}
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(.005 * Math.pow(1/Math.sqrt(2), ITERATIONS-13));
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(90);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(270); // turn right 90
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.X, new LSystem.Symbol[]{
LSystem.Symbol.X,
LSystem.Symbol.R,
LSystem.Symbol.Y,
LSystem.Symbol.F,
LSystem.Symbol.R});
rules.put(LSystem.Symbol.Y, new LSystem.Symbol[]{
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.X,
LSystem.Symbol.L,
LSystem.Symbol.Y});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.X);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawDragonCurve(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
/**
* Created by Meir on 12/29/2015.
*/
public class FractalPlant {
static int ITERATIONS = 7;
// F means go forward
// L means turn left 25 degrees
// R means turn right 25 degrees
// O (open) means push the current position and angle onto the stack
// C (close) means pop the stack and use that to reset the position and angle
public static void drawFractalPlant(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.1,.02,70);
Stack<double[]> positionStack = new Stack<double[]>();
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(.003 * Math.pow(.5, ITERATIONS - 7));
} else if (s.equals(LSystem.Symbol.O)) {
positionStack.push(new double[]{turtle.x, turtle.y, turtle.angle});
} else if (s.equals(LSystem.Symbol.C)) {
double[] data = positionStack.pop();
turtle.x = data[0];
turtle.y = data[1];
turtle.angle = data[2];
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(25);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(335); // turn right 25 degrees
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.F});
rules.put(LSystem.Symbol.X, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.O,
LSystem.Symbol.O,
LSystem.Symbol.X,
LSystem.Symbol.C,
LSystem.Symbol.R,
LSystem.Symbol.X,
LSystem.Symbol.C,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.O,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.X,
LSystem.Symbol.C,
LSystem.Symbol.L,
LSystem.Symbol.X});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.X);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawFractalPlant(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/30/2015.
*/
public class GosperCurve {
static int ITERATIONS = 4;
// A and B mean move forward, R means turn right 60 deg., L means turn left 60 deg.
public static void drawGosperCurve(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.6,.85,0);
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.A) || s.equals(LSystem.Symbol.B)) {
turtle.goForward(.013 * Math.pow(1/Math.sqrt(7), ITERATIONS - 4));
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(60);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(300); // turn right 60
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.A, new LSystem.Symbol[]{
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.B,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.B,
LSystem.Symbol.L,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.A,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.B,
LSystem.Symbol.R});
rules.put(LSystem.Symbol.B, new LSystem.Symbol[]{
LSystem.Symbol.L,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.B,
LSystem.Symbol.B,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.B,
LSystem.Symbol.R,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.B});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.A);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawGosperCurve(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/30/2015.
*/
public class HilbertCurve {
static int ITERATIONS = 7;
// F means move forward, R means turn right 90 deg., L means turn left 90 deg.
public static void drawHilbertCurve(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.1,.1,0);
double stepSize = .025*(Math.pow(.5,ITERATIONS-5));
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(stepSize);
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(90);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(270); // turn right 90
} else {
assert false;
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.A, new LSystem.Symbol[]{
LSystem.Symbol.L,
LSystem.Symbol.B,
LSystem.Symbol.F,
LSystem.Symbol.R,
LSystem.Symbol.A,
LSystem.Symbol.F,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.B,
LSystem.Symbol.L});
rules.put(LSystem.Symbol.B, new LSystem.Symbol[]{
LSystem.Symbol.R,
LSystem.Symbol.A,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.B,
LSystem.Symbol.F,
LSystem.Symbol.B,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.A,
LSystem.Symbol.R});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.A);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawHilbertCurve(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/30/2015.
*/
public class LevyCurve {
static int ITERATIONS = 13;
// F means move forward, R means turn right 45 deg., L means turn left 45 deg.
public static void drawLevyCurve(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.3,.6,0);
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(.07*(Math.pow(1/Math.sqrt(2),ITERATIONS-4)));
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(45);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(315); // turn right 45
} else {
assert false;
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.R});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawLevyCurve(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/29/2015.
*/
public class LSystem {
public enum Symbol{
A,B,C,F,L,M,N,O,P,Q,R,X,Y
}
HashMap<Symbol, Symbol[]> rules;
ArrayList<Symbol> axiom;
public LSystem(HashMap<Symbol, Symbol[]> rules0, ArrayList<Symbol> axiom0) {
rules = rules0;
axiom = axiom0;
}
public ArrayList<Symbol> expandedList(ArrayList<Symbol> list) {
ArrayList<Symbol> ret = new ArrayList<Symbol>();
for (Symbol s : list) {
if (rules.containsKey(s)) {
for (Symbol s0 : rules.get(s)) {
ret.add(s0);
}
} else {
ret.add(s);
}
}
return ret;
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
/**
* Created by Meir on 12/31/2015.
*/
public class PenroseTiling {
static int ITERATIONS = 6;
// A means go forward
// L means turn left 25 degrees
// R means turn right 25 degrees
// O (open) means push the current position and angle onto the stack
// C (close) means pop the stack and use that to reset the position and angle
public static void drawPenroseTiling(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.5,.5,0);
Stack<double[]> positionStack = new Stack<double[]>();
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.A)) {
turtle.goForward(.1 * Math.pow(.61803, ITERATIONS - 3));
} else if (s.equals(LSystem.Symbol.O)) {
positionStack.push(new double[]{turtle.x, turtle.y, turtle.angle});
} else if (s.equals(LSystem.Symbol.C)) {
double[] data = positionStack.pop();
turtle.x = data[0];
turtle.y = data[1];
turtle.angle = data[2];
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(36);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(324); // turn right 36 degrees
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.M, new LSystem.Symbol[]{
LSystem.Symbol.Q,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.P,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.N,
LSystem.Symbol.A,
LSystem.Symbol.O,
LSystem.Symbol.L,
LSystem.Symbol.Q,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.M,
LSystem.Symbol.A,
LSystem.Symbol.C,
LSystem.Symbol.R,
LSystem.Symbol.R});
rules.put(LSystem.Symbol.N, new LSystem.Symbol[]{
LSystem.Symbol.R,
LSystem.Symbol.Q,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.P,
LSystem.Symbol.A,
LSystem.Symbol.O,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.M,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.N,
LSystem.Symbol.A,
LSystem.Symbol.C,
LSystem.Symbol.R});
rules.put(LSystem.Symbol.Q, new LSystem.Symbol[]{
LSystem.Symbol.L,
LSystem.Symbol.M,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.N,
LSystem.Symbol.A,
LSystem.Symbol.O,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.Q,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.P,
LSystem.Symbol.A,
LSystem.Symbol.C,
LSystem.Symbol.L});
rules.put(LSystem.Symbol.P, new LSystem.Symbol[]{
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.Q,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.M,
LSystem.Symbol.A,
LSystem.Symbol.O,
LSystem.Symbol.R,
LSystem.Symbol.P,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.N,
LSystem.Symbol.A,
LSystem.Symbol.C,
LSystem.Symbol.L,
LSystem.Symbol.L,
LSystem.Symbol.N,
LSystem.Symbol.A});
rules.put(LSystem.Symbol.A, new LSystem.Symbol[]{});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.O);
axiom.add(LSystem.Symbol.N);
axiom.add(LSystem.Symbol.C);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.O);
axiom.add(LSystem.Symbol.N);
axiom.add(LSystem.Symbol.C);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.O);
axiom.add(LSystem.Symbol.N);
axiom.add(LSystem.Symbol.C);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.O);
axiom.add(LSystem.Symbol.N);
axiom.add(LSystem.Symbol.C);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.O);
axiom.add(LSystem.Symbol.N);
axiom.add(LSystem.Symbol.C);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawPenroseTiling(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
/**
* Created by Meir on 12/29/2015.
*/
public class PythagorasTree {
static int ITERATIONS = 10;
// A and B mean move forward (A will draw a leaf, B will draw an interior segment)
// O (open) means push position and angle, turn left 45 degrees
// C (close) means pop position and angle, turn right 45 degrees
public static void drawPythagorasTree(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.5,.02,90);
Stack<double[]> positionStack = new Stack<double[]>();
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.A) || s.equals(LSystem.Symbol.B)) {
turtle.goForward(.008 * Math.pow(.5, ITERATIONS - 7));
} else if (s.equals(LSystem.Symbol.O)) {
positionStack.push(new double[]{turtle.x, turtle.y, turtle.angle});
turtle.turnLeft(45);
} else if (s.equals(LSystem.Symbol.C)) {
double[] data = positionStack.pop();
turtle.x = data[0];
turtle.y = data[1];
turtle.angle = data[2];
turtle.turnLeft(315); // turn right 45 degrees
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.B, new LSystem.Symbol[]{
LSystem.Symbol.B,
LSystem.Symbol.B});
rules.put(LSystem.Symbol.A, new LSystem.Symbol[]{
LSystem.Symbol.B,
LSystem.Symbol.O,
LSystem.Symbol.A,
LSystem.Symbol.C,
LSystem.Symbol.A});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.A);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawPythagorasTree(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/30/2015.
*/
public class Rings {
static int ITERATIONS = 4;
// F means move forward, R means turn right 90 deg., L means turn left 90 deg.
public static void drawRings(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.5,.25,0);
double stepSize = .001*(Math.pow(.285714,ITERATIONS-5));
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(stepSize);
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(90);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(270); // turn right 90
} else {
assert false;
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.R,
LSystem.Symbol.F});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.L);
axiom.add(LSystem.Symbol.F);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawRings(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
/**
* Created by Meir on 12/30/2015.
*/
public class Seaweed {
static int ITERATIONS = 4;
// F means go forward
// L means turn left 25 degrees
// R means turn right 25 degrees
// O (open) means push the current position and angle onto the stack
// C (close) means pop the stack and use that to reset the position and angle
public static void drawSeaweed(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.5,.03,90);
Stack<double[]> positionStack = new Stack<double[]>();
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(.002 * Math.pow(.5, ITERATIONS - 7));
} else if (s.equals(LSystem.Symbol.O)) {
positionStack.push(new double[]{turtle.x, turtle.y, turtle.angle});
} else if (s.equals(LSystem.Symbol.C)) {
double[] data = positionStack.pop();
turtle.x = data[0];
turtle.y = data[1];
turtle.angle = data[2];
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(22);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(338); // turn right 22 degrees
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.O,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.C,
LSystem.Symbol.R,
LSystem.Symbol.O,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.C});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawSeaweed(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/29/2015.
*/
public class Sierpinski {
static int ITERATIONS = 8;
// A and B mean move forward, R means turn right 60 deg., L means turn left 60 deg.
public static void drawSierpinski(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.05,.15,0);
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.A) || s.equals(LSystem.Symbol.B)) {
turtle.goForward(.007 * Math.pow(.5, ITERATIONS - 7));
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(60);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(300); // turn right 60
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.A, new LSystem.Symbol[]{
LSystem.Symbol.L,
LSystem.Symbol.B,
LSystem.Symbol.R,
LSystem.Symbol.A,
LSystem.Symbol.R,
LSystem.Symbol.B,
LSystem.Symbol.L});
rules.put(LSystem.Symbol.B, new LSystem.Symbol[]{
LSystem.Symbol.R,
LSystem.Symbol.A,
LSystem.Symbol.L,
LSystem.Symbol.B,
LSystem.Symbol.L,
LSystem.Symbol.A,
LSystem.Symbol.R});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.A);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawSierpinski(axiom);
}
}
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Meir on 12/29/2015.
*/
public class Snowflake {
static int ITERATIONS = 5;
// F means move forward, R means turn right 60 deg., L means turn left 60 deg.
public static void drawSnowflake(ArrayList<LSystem.Symbol> symbols) {
Turtle turtle = new Turtle(.1,.75,0);
for (LSystem.Symbol s : symbols) {
if (s.equals(LSystem.Symbol.F)) {
turtle.goForward(.01*(Math.pow(.333,ITERATIONS-4)));
} else if (s.equals(LSystem.Symbol.L)) {
turtle.turnLeft(60);
} else if (s.equals(LSystem.Symbol.R)) {
turtle.turnLeft(300); // turn right 60
} else {
assert false;
}
}
}
public static void main(String[] args) {
HashMap<LSystem.Symbol, LSystem.Symbol[]> rules = new HashMap<LSystem.Symbol, LSystem.Symbol[]>();
rules.put(LSystem.Symbol.F, new LSystem.Symbol[]{
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F,
LSystem.Symbol.R,
LSystem.Symbol.R,
LSystem.Symbol.F,
LSystem.Symbol.L,
LSystem.Symbol.F});
ArrayList<LSystem.Symbol> axiom = new ArrayList<LSystem.Symbol>();
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.F);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.R);
axiom.add(LSystem.Symbol.F);
LSystem system = new LSystem(rules, axiom);
for (int i = 0; i < ITERATIONS; i++) {
axiom = system.expandedList(axiom);
}
drawSnowflake(axiom);
}
}
/******************************************************************************
* Compilation: javac StdDraw.java
* Execution: java StdDraw
* Dependencies: none
*
* Standard drawing library. This class provides a basic capability for
* creating drawings with your programs. It uses a simple graphics model that
* allows you to create drawings consisting of points, lines, and curves
* in a window on your computer and to save the drawings to a file.
*
* Todo
* ----
* - Add support for gradient fill, etc.
* - Fix setCanvasSize() so that it can only be called once.
* - On some systems, drawing a line (or other shape) that extends way
* beyond canvas (e.g., to infinity) dimensions does not get drawn.
*
* Remarks
* -------
* - don't use AffineTransform for rescaling since it inverts
* images and strings
*
******************************************************************************/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.DirectColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.TreeSet;
import java.util.NoSuchElementException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
/**
* The {@code StdDraw} class provides a basic capability for
* creating drawings with your programs. It uses a simple graphics model that
* allows you to create drawings consisting of points, lines, squares,
* circles, and other geometric shapes in a window on your computer and
* to save the drawings to a file. Standard drawing also includes
* facilities for text, color, pictures, and animation, along with
* user interaction via the keyboard and mouse.
* <p>
* <b>Getting started.</b>
* To use standard drawing, you must have <tt>StdDraw.class</tt> in your
* Java classpath. If you used our autoinstaller, you should be all set.
* Otherwise, download
* <a href = "http://introcs.cs.princeton.edu/java/stdlib/StdDraw.java">StdDraw.java</a>
* and put a copy in your working directory.
* <p>
* Now, type the following short program into your editor:
* <pre>
* public class TestStdDraw {
* public static void main(String[] args) {
* StdDraw.setPenRadius(0.05);
* StdDraw.setPenColor(StdDraw.BLUE);
* StdDraw.point(0.5, 0.5);
* StdDraw.setPenColor(StdDraw.MAGENTA);
* StdDraw.line(0.2, 0.2, 0.8, 0.2);
* }
* }
* </pre>
* If you compile and execute the program, you should see a window
* appear with a thick magenta line and a blue point.
* This program illustrates the two main types of methods in standard
* drawing&mdash;methods that draw geometric shapes and methods that
* control drawing parameters.
* The methods {@code StdDraw.line()} and {@code StdDraw.point()}
* draw lines and points; the methods {@code StdDraw.setPenRadius()}
* and {@code StdDraw.setPenColor()} control the line thickness and color.
* <p>
* <b>Points and lines.</b>
* You can draw points and line segments with the following methods:
* <ul>
* <li> {@link #point(double x, double y)}
* <li> {@link #line(double x1, double y1, double x2, double y2)}
* </ul>
* <p>
* The <em>x</em>- and <em>y</em>-coordinates must be in the drawing area
* (between 0 and 1 and by default) or the points and lines will not be visible.
* <p>
* <b>Squares, circles, rectangles, and ellipses.</b>
* You can draw squares, circles, rectangles, and ellipses using
* the following methods:
* <ul>
* <li> {@link #circle(double x, double y, double radius)}
* <li> {@link #ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis)}
* <li> {@link #square(double x, double y, double radius)}
* <li> {@link #rectangle(double x, double y, double halfWidth, double halfHeight)}
* </ul>
* <p>
* All of these methods take as arguments the location and size of the shape.
* The location is always specified by the <em>x</em>- and <em>y</em>-coordinates
* of its <em>center</em>.
* The size of a circle is specified by its radius and the size of an ellipse is
* specified by the lengths of its semi-major and semi-minor axes.
* The size of a square or rectangle is specified by its half-width or half-height.
* The convention for drawing squares and rectangles is parallel to those for
* drawing circles and ellipses, but may be unexpected to the uninitiated.
* <p>
* The methods above trace outlines of the given shapes. The following methods
* draw filled versions:
* <ul>
* <li> {@link #filledCircle(double x, double y, double radius)}
* <li> {@link #filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis)}
* <li> {@link #filledSquare(double x, double y, double radius)}
* <li> {@link #filledRectangle(double x, double y, double halfWidth, double halfHeight)}
* </ul>
* <p>
* <b>Circular arcs.</b>
* You can draw circular arcs with the following method:
* <ul>
* <li> {@link #arc(double x, double y, double radius, double angle1, double angle2)}
* </ul>
* <p>
* The arc is from the circle centered at (<em>x</em>, <em>y</em>) of the specified radius.
* The arc extends from angle1 to angle2. By convention, the angles are
* <em>polar</em> (counterclockwise angle from the <em>x</em>-axis)
* and represented in degrees. For example, {@code StdDraw.arc(0.0, 0.0, 1.0, 0, 90)}
* draws the arc of the unit circle from 3 o'clock (0 degrees) to 12 o'clock (90 degrees).
* <p>
* <b>Polygons.</b>
* You can draw polygons with the following methods:
* <ul>
* <li> {@link #polygon(double[] x, double[] y)}
* <li> {@link #filledPolygon(double[] x, double[] y)}
* </ul>
* <p>
* The points in the polygon are ({@code x[i]}, {@code y[i]}).
* For example, the following code fragment draws a filled diamond
* with vertices (0.1, 0.2), (0.2, 0.3), (0.3, 0.2), and (0.2, 0.1):
* <pre>
* double[] x = { 0.1, 0.2, 0.3, 0.2 };
* double[] y = { 0.2, 0.3, 0.2, 0.1 };
* StdDraw.filledPolygon(x, y);
* </pre>
* <p>
* <b>Pen size.</b>
* The pen is circular, so that when you set the pen radius to <em>r</em>
* and draw a point, you get a circle of radius <em>r</em>. Also, lines are
* of thickness 2<em>r</em> and have rounded ends. The default pen radius
* is 0.005 and is not affected by coordinate scaling. This default pen
* radius is about 1/200 the width of the default canvas, so that if
* you draw 100 points equally spaced along a horizontal or vertical line,
* you will be able to see individual circles, but if you draw 200 such
* points, the result will look like a line.
* <ul>
* <li> {@link #setPenRadius(double radius)}
* </ul>
* <p>
* For example, {@code StdDraw.setPenRadius(0.025)} makes
* the thickness of the lines and the size of the points to be five times
* the 0.005 default.
* To draw points with the minimum possible radius (one pixel on typical
* displays), set the pen radius to 0.0.
* <p>
* <b>Pen color.</b>
* All geometric shapes (such as points, lines, and circles) are drawn using
* the current pen color. By default, it is black.
* You can change the pen color with the following methods:
* <ul>
* <li> {@link #setPenColor(int red, int green, int blue)}
* <li> {@link #setPenColor(Color color)}
* </ul>
* <p>
* The first method allows you to specify colors using the RGB color system.
* This <a href = "http://johndyer.name/lab/colorpicker/">color picker</a>
* is a convenient way to find a desired color.
* The second method allows you to specify colors using the
* {@link Color} data type that is discussed in Chapter 3. Until then,
* you can use this method with one of these predefined colors in standard drawing:
* {@link #BLACK}, {@link #BLUE}, {@link #CYAN}, {@link #DARK_GRAY}, {@link #GRAY},
* {@link #GREEN}, {@link #LIGHT_GRAY}, {@link #MAGENTA}, {@link #ORANGE},
* {@link #PINK}, {@link #RED}, {@link #WHITE}, and {@link #YELLOW}.
* For example, {@code StdDraw.setPenColor(StdDraw.MAGENTA)} sets the
* pen color to magenta.
* <p>
* <b>Canvas size.</b>
* By default, all drawing takes places in a 512-by-512 canvas.
* The canvas does not include the window title or window border.
* You can change the size of the canvas with the following method:
* <ul>
* <li> {@link #setCanvasSize(int width, int height)}
* </ul>
* <p>
* This sets the canvas size to be <em>width</em>-by-<em>height</em> pixels.
* It also erases the current drawing and resets the coordinate system,
* pen radius, pen color, and font back to their default values.
* Ordinarly, this method is called once, at the very beginning of a program.
* For example, {@code StdDraw.setCanvasSize(800, 800)}
* sets the canvas size to be 800-by-800 pixels.
* <p>
* <b>Canvas scale and coordinate system.</b>
* By default, all drawing takes places in the unit square, with (0, 0) at
* lower left and (1, 1) at upper right. You can change the default
* coordinate system with the following methods:
* <ul>
* <li> {@link #setXscale(double xmin, double xmax)}
* <li> {@link #setYscale(double ymin, double ymax)}
* <li> {@link #setScale(double min, double max)}
* </ul>
* <p>
* The arguments are the coordinates of the minimum and maximum
* <em>x</em>- or <em>y</em>-coordinates that will appear in the canvas.
* For example, if you wish to use the default coordinate system but
* leave a small margin, you can call {@code StdDraw.setScale(-.05, 1.05)}.
* <p>
* These methods change the coordinate system for subsequent drawing
* commands; they do not affect previous drawings.
* These methods do not change the canvas size; so, if the <em>x</em>-
* and <em>y</em>-scales are different, squares will become rectangles
* and circles will become ellipsoidal.
* <p>
* <b>Text.</b>
* You can use the following methods to annotate your drawings with text:
* <ul>
* <li> {@link #text(double x, double y, String text)}
* <li> {@link #text(double x, double y, String text, double degrees)}
* <li> {@link #textLeft(double x, double y, String text)}
* <li> {@link #textRight(double x, double y, String text)}
* </ul>
* <p>
* The first two methods write the specified text in the current font,
* centered at (<em>x</em>, <em>y</em>).
* The second method allows you to rotate the text.
* The last two methods either left- or right-align the text at (<em>x</em>, <em>y</em>).
* <p>
* The default font is a Sans Serif font with point size 16.
* You can use the following method to change the font:
* <ul>
* <li> {@link #setFont(Font font)}
* </ul>
* <p>
* You use the {@link Font} data type to specify the font. This allows you to
* choose the face, size, and style of the font. For example, the following
* code fragment sets the font to Arial Bold, 60 point.
* <pre>
* Font font = new Font("Arial", Font.BOLD, 60);
* StdDraw.setFont(font);
* StdDraw.text(0.5, 0.5, "Hello, World");
* </pre>
* <p>
* <b>Images.</b>
* You can use the following methods to add images to your drawings:
* <ul>
* <li> {@link #picture(double x, double y, String filename)}
* <li> {@link #picture(double x, double y, String filename, double degrees)}
* <li> {@link #picture(double x, double y, String filename, double width)}
* <li> {@link #picture(double x, double y, String filename, double width, double degrees)}
* </ul>
* <p>
* These methods draw the specified image, centered at (<em>x</em>, <em>y</em>).
* The supported image formats are JPEG, PNG, and GIF.
* The image will display at its native size, independent of the coordinate system.
* Optionally, you can rotate the image a specified number of degrees counterclockwise
* or rescale it to fit inside a width-by-height pixel bounding box.
* <p>
* <b>Saving to a file.</b>
* You save your image to a file using the <em>File -> Save</em> menu option.
* You can also save a file programatically using the following method:
* <ul>
* <li> {@link #save(String filename)}
* </ul>
* <p>
* The supported image formats are JPEG and PNG. The filename must have either the
* extension .jpg or .png.
* We recommend using PNG for drawing that consist solely of geometric shapes and JPEG
* for drawings that contains pictures.
* <p>
* <b>Clearing the canvas.</b>
* To clear the entire drawing canvas, you can use the following methods:
* <ul>
* <li> {@link #clear()}
* <li> {@link #clear(Color color)}
* </ul>
* <p>
* The first method clears the canvas to white; the second method
* allows you to specify a color of your choice. For example,
* {@code StdDraw.clear(StdDraw.LIGHT_GRAY)} clears the canvas to a shade
* of gray. Most often, these two methods are used in conjunction with animation mode.
* <p>
* <b>Animations.</b>
* Animation mode is one of the trickier features of standard drawing.
* The following two methods control the way in which objects are drawn:
* <ul>
* <li> {@link #show()}
* <li> {@link #show(int t)}
* </ul>
* <p>
* By default, animation mode is off, which means that as soon as you
* call a drawing
* method&mdash;such as {@code point()} or {@code line()}&mdash;the
* results appear on the screen. {@code StdDraw.show()} turns off
* animation mode.
* <p>
* You can call {@link #show(int t)} to turn on animation mode. This
* defers all drawing to the screen until you are aready to display them.
* Once you are ready to display them,
* you call {@link #show(int t)} again, which transfer the offscreen
* drawing to the screen and waits for the specified number of milliseconds.
* In conjuction with {@link #clear()}, you can create the illusion
* of movement by iterating the following three steps:
* <ul>
* <li> Clear the background canvas.
* <li> Draw geometric objects.
* <li> Show the drawing and wait for a short while.
* </ul>
* <p>
* Waiting for a short while is essential; otherwise, the drawing will appear
* and disappear so quickly that your animation will flicker.
* <p>
* Here is a simple example of an animation:
* <p>
* <b>Keyboard and mouse inputs.</b>
* Standard drawing has very basic support for keyboard and mouse input.
* It is much less powerful than most user interface libraries provide, but also much simpler.
* You can use the following methods to intercept mouse events:
* <ul>
* <li> {@link #mousePressed()}
* <li> {@link #mouseX()}
* <li> {@link #mouseY()}
* </ul>
* <p>
* The first method tells you whether a mouse button is currently being pressed.
* The last two methods tells you the <em>x</em>- and <em>y</em>-coordinates of the mouse's
* current position, using the same coordinate system as the canvas (the unit square, by default).
* You should use these methods in an animation loop that waits a short while before trying
* to poll the mouse for its current state.
* You can use the following methods to intercept keyboard events:
* <ul>
* <li> {@link #hasNextKeyTyped()}
* <li> {@link #nextKeyTyped()}
* <li> {@link #isKeyPressed(int keycode)}
* </ul>
* <p>
* If the user types lots of keys, they will be saved in a list until you process them.
* The first method tells you whether the user has typed a key (that your program has
* not yet processed).
* The second method returns the next key that the user typed (that your program has
* not yet processed) and removes it from the list of saved keystrokes.
* The third method tells you whether a key is currently being pressed.
* <p>
* <b>Accessing control parameters.</b>
* You can use the following methods to access the current pen color, pen radius,
* and font:
* <ul>
* <li> {@link #getPenColor()}
* <li> {@link #getPenRadius()}
* <li> {@link #getFont()}
* </ul>
* <p>
* These methods are useful when you want to temporarily change a
* control parameter and reset it back to its original value.
* <p>
* <b>Corner cases.</b>
* To avoid clutter, the API doesn't explicitly refer to arguments that are
* null, infinity, or NaN.
* <ul>
* <li> Any method that is passed a {@code null} argument will throw a
* {@link NullPointerException}.
* <li> Except as noted in the APIs, drawing an object outside (or partly outside)
* the canvas is permitted&mdash;however, only the part of the object that
* appears inside the canvas will be visible.
* <li> Except as noted in the APIs, all methods accept {@link Double#NaN},
* {@link Double#POSITIVE_INFINITY}, and {@link Double#NEGATIVE_INFINITY}
* as arugments. An object drawn with an <em>x</em>- or <em>y</em>-coordinate
* that is NaN will behave as if it is outside the canvas, and will not be visible.
* </ul>
* <p>
* <b>Performance tricks.</b>
* Standard drawing is capable of drawing large amounts of data.
* Here are a few tricks and tips:
* <ul>
* <li> Use <em>animation mode</em> for static drawing with a large
* number of objects.
* That is, call {@code StdDraw.show(0)} before
* and after the sequence of drawing commands.
* The bottleneck operation is not drawing the geometric
* shapes but rather drawing them to the screen. By using animation
* mode, you draw all of the shapes to an offscreen buffer, then copy
* them all at once to the screen.
* <li> When using <em>animation mode</em>, call {@code show()}
* only once per frame, not after drawing each object.
* <li> If you call {@code picture()} multiple times with the same filename,
* Java will cache the image, so you do not incur the cost of reading
* from a file each time.
* <li> Do not call {@code setFont()} in an animation loop (unless you really
* need to change the font in each iteration). It can cause flicker.
* </ul>
* <p>
* <b>Known bugs and issues.</b>
* <ul>
* <li> The {@code picture()} methods may not draw the portion of the image that is
* inside the canvas if the center point (<em>x</em>, <em>y</em>) is outside the
* canvas.
* This bug appears only on some systems.
* <li> Some methods may not draw the portion of the geometric object that is inside the
* canvas if the <em>x</em>- or <em>y</em>-coordinates are infinite.
* This bug appears only on some systems.
* </ul>
* <p>
* <b>Reference.</b>
* For additional documentation,
* see <a href="http://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
* <em>Introduction to Programming in Java: An Interdisciplinary Approach</em>
* by Robert Sedgewick and Kevin Wayne.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public final class StdDraw implements ActionListener, MouseListener, MouseMotionListener, KeyListener {
/**
* The color black.
*/
public static final Color BLACK = Color.BLACK;
/**
* The color blue.
*/
public static final Color BLUE = Color.BLUE;
/**
* The color cyan.
*/
public static final Color CYAN = Color.CYAN;
/**
* The color dark gray.
*/
public static final Color DARK_GRAY = Color.DARK_GRAY;
/**
* The color gray.
*/
public static final Color GRAY = Color.GRAY;
/**
* The color green.
*/
public static final Color GREEN = Color.GREEN;
/**
* The color light gray.
*/
public static final Color LIGHT_GRAY = Color.LIGHT_GRAY;
/**
* The color magenta.
*/
public static final Color MAGENTA = Color.MAGENTA;
/**
* The color orange.
*/
public static final Color ORANGE = Color.ORANGE;
/**
* The color pink.
*/
public static final Color PINK = Color.PINK;
/**
* The color red.
*/
public static final Color RED = Color.RED;
/**
* The color white.
*/
public static final Color WHITE = Color.WHITE;
/**
* The color yellow.
*/
public static final Color YELLOW = Color.YELLOW;
/**
* Shade of blue used in <em>Introduction to Programming in Java</em>.
* It is Pantone 300U. The RGB values are approximately (9, 90, 166).
*/
public static final Color BOOK_BLUE = new Color(9, 90, 166);
/**
* Shade of light blue used in <em>Introduction to Programming in Java</em>.
* The RGB values are approximately (103, 198, 243).
*/
public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243);
/**
* Shade of red used in <em>Algorithms, 4th edition</em>.
* It is Pantone 1805U. The RGB values are approximately (150, 35, 31).
*/
public static final Color BOOK_RED = new Color(150, 35, 31);
// default colors
private static final Color DEFAULT_PEN_COLOR = BLACK;
private static final Color DEFAULT_CLEAR_COLOR = WHITE;
// current pen color
private static Color penColor;
// default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE
private static final int DEFAULT_SIZE = 512;
private static int width = DEFAULT_SIZE;
private static int height = DEFAULT_SIZE;
// default pen radius
private static final double DEFAULT_PEN_RADIUS = 0.002;
// current pen radius
private static double penRadius;
// show we draw immediately or wait until next show?
private static boolean defer = false;
// boundary of drawing canvas, 0% border
// private static final double BORDER = 0.05;
private static final double BORDER = 0.00;
private static final double DEFAULT_XMIN = 0.0;
private static final double DEFAULT_XMAX = 1.0;
private static final double DEFAULT_YMIN = 0.0;
private static final double DEFAULT_YMAX = 1.0;
private static double xmin, ymin, xmax, ymax;
// for synchronization
private static Object mouseLock = new Object();
private static Object keyLock = new Object();
// default font
private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16);
// current font
private static Font font;
// double buffered graphics
private static BufferedImage offscreenImage, onscreenImage;
private static Graphics2D offscreen, onscreen;
// singleton for callbacks: avoids generation of extra .class files
private static StdDraw std = new StdDraw();
// the frame for drawing to the screen
private static JFrame frame;
// mouse state
private static boolean mousePressed = false;
private static double mouseX = 0;
private static double mouseY = 0;
// queue of typed key characters
private static LinkedList<Character> keysTyped = new LinkedList<Character>();
// set of key codes currently pressed down
private static TreeSet<Integer> keysDown = new TreeSet<Integer>();
// time in milliseconds (from currentTimeMillis()) when we can draw again
// used to control the frame rate
private static long nextDraw = -1;
// singleton pattern: client can't instantiate
private StdDraw() { }
// static initializer
static {
init();
}
/**
* Sets the canvas (drawing area) to be 512-by-512 pixels.
* This also erases the current drawing and resets the coordinate system,
* pen radius, pen color, and font back to their default values.
* Ordinarly, this method is called once, at the very beginning
* of a program.
*/
public static void setCanvasSize() {
setCanvasSize(DEFAULT_SIZE, DEFAULT_SIZE);
}
/**
* Sets the canvas (drawing area) to be <em>width</em>-by-<em>height</em> pixels.
* This also erases the current drawing and resets the coordinate system,
* pen radius, pen color, and font back to their default values.
* Ordinarly, this method is called once, at the very beginning
* of a program.
*
* @param canvasWidth the width as a number of pixels
* @param canvasHeight the height as a number of pixels
* @throws IllegalArgumentException unless both {@code width} and
* {@code height} are positive
*/
public static void setCanvasSize(int canvasWidth, int canvasHeight) {
if (width <= 0 || height <= 0) throw new IllegalArgumentException("width and height must be positive");
width = canvasWidth;
height = canvasHeight;
init();
}
// init
private static void init() {
if (frame != null) frame.setVisible(false);
frame = new JFrame();
offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
offscreen = offscreenImage.createGraphics();
onscreen = onscreenImage.createGraphics();
setXscale();
setYscale();
offscreen.setColor(DEFAULT_CLEAR_COLOR);
offscreen.fillRect(0, 0, width, height);
setPenColor();
setPenRadius();
setFont();
clear();
// add antialiasing
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
offscreen.addRenderingHints(hints);
// frame stuff
ImageIcon icon = new ImageIcon(onscreenImage);
JLabel draw = new JLabel(icon);
draw.addMouseListener(std);
draw.addMouseMotionListener(std);
frame.setContentPane(draw);
frame.addKeyListener(std); // JLabel cannot get keyboard focus
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows
// frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window
frame.setTitle("Standard Draw");
frame.setJMenuBar(createMenuBar());
frame.pack();
frame.requestFocusInWindow();
frame.setVisible(true);
}
// create the menu bar (changed to private)
private static JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem1 = new JMenuItem(" Save... ");
menuItem1.addActionListener(std);
menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
menu.add(menuItem1);
return menuBar;
}
/***************************************************************************
* User and screen coordinate systems.
***************************************************************************/
/**
* Sets the <em>x</em>-scale to be the default (between 0.0 and 1.0).
*/
public static void setXscale() {
setXscale(DEFAULT_XMIN, DEFAULT_XMAX);
}
/**
* Sets the <em>y</em>-scale to be the default (between 0.0 and 1.0).
*/
public static void setYscale() {
setYscale(DEFAULT_YMIN, DEFAULT_YMAX);
}
/**
* Sets the <em>x</em>-scale and <em>y</em>-scale to be the default
* (between 0.0 and 1.0).
*/
public static void setScale() {
setXscale();
setYscale();
}
/**
* Sets the <em>x</em>-scale to the specified range.
*
* @param min the minimum value of the <em>x</em>-scale
* @param max the maximum value of the <em>x</em>-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public static void setXscale(double min, double max) {
double size = max - min;
if (size == 0.0) throw new IllegalArgumentException("the min and max are the same");
synchronized (mouseLock) {
xmin = min - BORDER * size;
xmax = max + BORDER * size;
}
}
/**
* Sets the <em>y</em>-scale to the specified range.
*
* @param min the minimum value of the <em>y</em>-scale
* @param max the maximum value of the <em>y</em>-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public static void setYscale(double min, double max) {
double size = max - min;
if (size == 0.0) throw new IllegalArgumentException("the min and max are the same");
synchronized (mouseLock) {
ymin = min - BORDER * size;
ymax = max + BORDER * size;
}
}
/**
* Sets both the <em>x</em>-scale and <em>y</em>-scale to the (same) specified range.
*
* @param min the minimum value of the <em>x</em>- and <em>y</em>-scales
* @param max the maximum value of the <em>x</em>- and <em>y</em>-scales
* @throws IllegalArgumentException if {@code (max == min)}
*/
public static void setScale(double min, double max) {
double size = max - min;
if (size == 0.0) throw new IllegalArgumentException("the min and max are the same");
synchronized (mouseLock) {
xmin = min - BORDER * size;
xmax = max + BORDER * size;
ymin = min - BORDER * size;
ymax = max + BORDER * size;
}
}
// helper functions that scale from user coordinates to screen coordinates and back
private static double scaleX(double x) { return width * (x - xmin) / (xmax - xmin); }
private static double scaleY(double y) { return height * (ymax - y) / (ymax - ymin); }
private static double factorX(double w) { return w * width / Math.abs(xmax - xmin); }
private static double factorY(double h) { return h * height / Math.abs(ymax - ymin); }
private static double userX(double x) { return xmin + x * (xmax - xmin) / width; }
private static double userY(double y) { return ymax - y * (ymax - ymin) / height; }
/**
* Clears the screen to the default color (white).
*/
public static void clear() {
clear(DEFAULT_CLEAR_COLOR);
}
/**
* Clears the screen to the specified color.
*
* @param color the color to make the background
*/
public static void clear(Color color) {
offscreen.setColor(color);
offscreen.fillRect(0, 0, width, height);
offscreen.setColor(penColor);
draw();
}
/**
* Returns the current pen radius.
*
* @return the current value of the pen radius
*/
public static double getPenRadius() {
return penRadius;
}
/**
* Sets the pen size to the default size (0.002).
* The pen is circular, so that lines have rounded ends, and when you set the
* pen radius and draw a point, you get a circle of the specified radius.
* The pen radius is not affected by coordinate scaling.
*/
public static void setPenRadius() {
setPenRadius(DEFAULT_PEN_RADIUS);
}
/**
* Sets the radius of the pen to the specified size.
* The pen is circular, so that lines have rounded ends, and when you set the
* pen radius and draw a point, you get a circle of the specified radius.
* The pen radius is not affected by coordinate scaling.
*
* @param radius the radius of the pen
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void setPenRadius(double radius) {
if (!(radius >= 0)) throw new IllegalArgumentException("pen radius must be nonnegative");
penRadius = radius;
float scaledPenRadius = (float) (radius * DEFAULT_SIZE);
BasicStroke stroke = new BasicStroke(scaledPenRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
// BasicStroke stroke = new BasicStroke(scaledPenRadius);
offscreen.setStroke(stroke);
}
/**
* Returns the current pen color.
*
* @return the current pen color
*/
public static Color getPenColor() {
return penColor;
}
/**
* Set the pen color to the default color (black).
*/
public static void setPenColor() {
setPenColor(DEFAULT_PEN_COLOR);
}
/**
* Sets the pen color to the specified color.
* <p>
* The predefined pen colors are
* <tt>StdDraw.BLACK</tt>, <tt>StdDraw.BLUE</tt>, <tt>StdDraw.CYAN</tt>,
* <tt>StdDraw.DARK_GRAY</tt>, <tt>StdDraw.GRAY</tt>, <tt>StdDraw.GREEN</tt>,
* <tt>StdDraw.LIGHT_GRAY</tt>, <tt>StdDraw.MAGENTA</tt>, <tt>StdDraw.ORANGE</tt>,
* <tt>StdDraw.PINK</tt>, <tt>StdDraw.RED</tt>, <tt>StdDraw.WHITE</tt>, and
* <tt>StdDraw.YELLOW</tt>.
*
* @param color the color to make the pen
*/
public static void setPenColor(Color color) {
if (color == null) throw new NullPointerException();
penColor = color;
offscreen.setColor(penColor);
}
/**
* Sets the pen color to the specified RGB color.
*
* @param red the amount of red (between 0 and 255)
* @param green the amount of green (between 0 and 255)
* @param blue the amount of blue (between 0 and 255)
* @throws IllegalArgumentException if {@code red}, {@code green},
* or {@code blue} is outside its prescribed range
*/
public static void setPenColor(int red, int green, int blue) {
if (red < 0 || red >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255");
if (green < 0 || green >= 256) throw new IllegalArgumentException("amount of green must be between 0 and 255");
if (blue < 0 || blue >= 256) throw new IllegalArgumentException("amount of blue must be between 0 and 255");
setPenColor(new Color(red, green, blue));
}
/**
* Returns the current font.
*
* @return the current font
*/
public static Font getFont() {
return font;
}
/**
* Sets the font to the default font (sans serif, 16 point).
*/
public static void setFont() {
setFont(DEFAULT_FONT);
}
/**
* Sets the font to the specified value.
*
* @param font the font
*/
public static void setFont(Font font) {
if (font == null) throw new NullPointerException();
StdDraw.font = font;
}
/***************************************************************************
* Drawing geometric shapes.
***************************************************************************/
/**
* Draws a line segment between (<em>x</em><sub>0</sub>, <em>y</em><sub>0</sub>) and
* (<em>x</em><sub>1</sub>, <em>y</em><sub>1</sub>).
*
* @param x0 the <em>x</em>-coordinate of one endpoint
* @param y0 the <em>y</em>-coordinate of one endpoint
* @param x1 the <em>x</em>-coordinate of the other endpoint
* @param y1 the <em>y</em>-coordinate of the other endpoint
*/
public static void line(double x0, double y0, double x1, double y1) {
offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1)));
draw();
}
/**
* Draws one pixel at (<em>x</em>, <em>y</em>).
* This method is private because pixels depend on the display.
* To achieve the same effect, set the pen radius to 0 and call {@code point()}.
*
* @param x the <em>x</em>-coordinate of the pixel
* @param y the <em>y</em>-coordinate of the pixel
*/
private static void pixel(double x, double y) {
offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1);
}
/**
* Draws a point centered at (<em>x</em>, <em>y</em>).
* The point is a filled circle whose radius is equal to the pen radius.
* To draw a single-pixel point, first set the pen radius to 0.
*
* @param x the <em>x</em>-coordinate of the point
* @param y the <em>y</em>-coordinate of the point
*/
public static void point(double x, double y) {
double xs = scaleX(x);
double ys = scaleY(y);
double r = penRadius;
float scaledPenRadius = (float) (r * DEFAULT_SIZE);
// double ws = factorX(2*r);
// double hs = factorY(2*r);
// if (ws <= 1 && hs <= 1) pixel(x, y);
if (scaledPenRadius <= 1) pixel(x, y);
else offscreen.fill(new Ellipse2D.Double(xs - scaledPenRadius/2, ys - scaledPenRadius/2,
scaledPenRadius, scaledPenRadius));
draw();
}
/**
* Draws a circle of the specified radius, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the circle
* @param y the <em>y</em>-coordinate of the center of the circle
* @param radius the radius of the circle
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void circle(double x, double y, double radius) {
if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*radius);
double hs = factorY(2*radius);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a filled circle of the specified radius, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the circle
* @param y the <em>y</em>-coordinate of the center of the circle
* @param radius the radius of the circle
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void filledCircle(double x, double y, double radius) {
if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*radius);
double hs = factorY(2*radius);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws an ellipse with the specified semimajor and semiminor axes,
* centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the ellipse
* @param y the <em>y</em>-coordinate of the center of the ellipse
* @param semiMajorAxis is the semimajor axis of the ellipse
* @param semiMinorAxis is the semiminor axis of the ellipse
* @throws IllegalArgumentException if either {@code semiMajorAxis}
* or {@code semiMinorAxis} is negative
*/
public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative");
if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*semiMajorAxis);
double hs = factorY(2*semiMinorAxis);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws an ellipse with the specified semimajor and semiminor axes,
* centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the ellipse
* @param y the <em>y</em>-coordinate of the center of the ellipse
* @param semiMajorAxis is the semimajor axis of the ellipse
* @param semiMinorAxis is the semiminor axis of the ellipse
* @throws IllegalArgumentException if either {@code semiMajorAxis}
* or {@code semiMinorAxis} is negative
*/
public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative");
if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*semiMajorAxis);
double hs = factorY(2*semiMinorAxis);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a circular arc of the specified radius,
* centered at (<em>x</em>, <em>y</em>), from angle1 to angle2 (in degrees).
*
* @param x the <em>x</em>-coordinate of the center of the circle
* @param y the <em>y</em>-coordinate of the center of the circle
* @param radius the radius of the circle
* @param angle1 the starting angle. 0 would mean an arc beginning at 3 o'clock.
* @param angle2 the angle at the end of the arc. For example, if
* you want a 90 degree arc, then angle2 should be angle1 + 90.
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void arc(double x, double y, double radius, double angle1, double angle2) {
if (radius < 0) throw new IllegalArgumentException("arc radius must be nonnegative");
while (angle2 < angle1) angle2 += 360;
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*radius);
double hs = factorY(2*radius);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Arc2D.Double(xs - ws/2, ys - hs/2, ws, hs, angle1, angle2 - angle1, Arc2D.OPEN));
draw();
}
/**
* Draws a square of side length 2r, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the square
* @param y the <em>y</em>-coordinate of the center of the square
* @param halfLength one half the length of any side of the square
* @throws IllegalArgumentException if {@code halfLength} is negative
*/
public static void square(double x, double y, double halfLength) {
if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfLength);
double hs = factorY(2*halfLength);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a filled square of the specified size, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the square
* @param y the <em>y</em>-coordinate of the center of the square
* @param halfLength one half the length of any side of the square
* @throws IllegalArgumentException if {@code halfLength} is negative
*/
public static void filledSquare(double x, double y, double halfLength) {
if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfLength);
double hs = factorY(2*halfLength);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a rectangle of the specified size, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the rectangle
* @param y the <em>y</em>-coordinate of the center of the rectangle
* @param halfWidth one half the width of the rectangle
* @param halfHeight one half the height of the rectangle
* @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative
*/
public static void rectangle(double x, double y, double halfWidth, double halfHeight) {
if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative");
if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfWidth);
double hs = factorY(2*halfHeight);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a filled rectangle of the specified size, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the rectangle
* @param y the <em>y</em>-coordinate of the center of the rectangle
* @param halfWidth one half the width of the rectangle
* @param halfHeight one half the height of the rectangle
* @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative
*/
public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) {
if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative");
if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfWidth);
double hs = factorY(2*halfHeight);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a polygon with the vertices
* (<em>x</em><sub>0</sub>, <em>y</em><sub>0</sub>),
* (<em>x</em><sub>1</sub>, <em>y</em><sub>1</sub>), ...,
* (<em>x</em><sub><em>n</em>&minus;1</sub>, <em>y</em><sub><em>n</em>&minus;1</sub>).
*
* @param x an array of all the <em>x</em>-coordinates of the polygon
* @param y an array of all the <em>y</em>-coordinates of the polygon
* @throws IllegalArgumentException unless {@code x[]} and {@code y[]}
* are of the same length
*/
public static void polygon(double[] x, double[] y) {
if (x == null) throw new NullPointerException();
if (y == null) throw new NullPointerException();
int n1 = x.length;
int n2 = y.length;
if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length");
int n = n1;
GeneralPath path = new GeneralPath();
path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
for (int i = 0; i < n; i++)
path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
path.closePath();
offscreen.draw(path);
draw();
}
/**
* Draws a polygon with the vertices
* (<em>x</em><sub>0</sub>, <em>y</em><sub>0</sub>),
* (<em>x</em><sub>1</sub>, <em>y</em><sub>1</sub>), ...,
* (<em>x</em><sub><em>n</em>&minus;1</sub>, <em>y</em><sub><em>n</em>&minus;1</sub>).
*
* @param x an array of all the <em>x</em>-coordinates of the polygon
* @param y an array of all the <em>y</em>-coordinates of the polygon
* @throws IllegalArgumentException unless {@code x[]} and {@code y[]}
* are of the same length
*/
public static void filledPolygon(double[] x, double[] y) {
if (x == null) throw new NullPointerException();
if (y == null) throw new NullPointerException();
int n1 = x.length;
int n2 = y.length;
if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length");
int n = n1;
GeneralPath path = new GeneralPath();
path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
for (int i = 0; i < n; i++)
path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
path.closePath();
offscreen.fill(path);
draw();
}
/***************************************************************************
* Drawing images.
***************************************************************************/
// get an image from the given filename
private static Image getImage(String filename) {
if (filename == null) throw new NullPointerException();
// to read from file
ImageIcon icon = new ImageIcon(filename);
// try to read from URL
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
try {
URL url = new URL(filename);
icon = new ImageIcon(url);
}
catch (Exception e) {
/* not a url */
}
}
// in case file is inside a .jar (classpath relative to StdDraw)
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
URL url = StdDraw.class.getResource(filename);
if (url != null)
icon = new ImageIcon(url);
}
// in case file is inside a .jar (classpath relative to root of jar)
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
URL url = StdDraw.class.getResource("/" + filename);
if (url == null) throw new IllegalArgumentException("image " + filename + " not found");
icon = new ImageIcon(url);
}
return icon.getImage();
}
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>).
* The supported image formats are JPEG, PNG, and GIF.
* As an optimization, the picture is cached, so there is no performance
* penalty for redrawing the same image multiple times (e.g., in an animation).
* However, if you change the picture file after drawing it, subsequent
* calls will draw the original picture.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename) {
Image image = getImage(filename);
double xs = scaleX(x);
double ys = scaleY(y);
int ws = image.getWidth(null);
int hs = image.getHeight(null);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
draw();
}
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>),
* rotated given number of degrees.
* The supported image formats are JPEG, PNG, and GIF.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @param degrees is the number of degrees to rotate counterclockwise
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename, double degrees) {
Image image = getImage(filename);
double xs = scaleX(x);
double ys = scaleY(y);
int ws = image.getWidth(null);
int hs = image.getHeight(null);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
offscreen.rotate(Math.toRadians(-degrees), xs, ys);
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
offscreen.rotate(Math.toRadians(+degrees), xs, ys);
draw();
}
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>),
* rescaled to the specified bounding box.
* The supported image formats are JPEG, PNG, and GIF.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @param scaledWidth the width of the scaled image in pixels
* @param scaledHeight the height of the scaled image in pixels
* @throws IllegalArgumentException if either {@code scaledWidth}
* or {@code scaledHeight} is negative
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight) {
Image image = getImage(filename);
if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth);
if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight);
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(scaledWidth);
double hs = factorY(scaledHeight);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
if (ws <= 1 && hs <= 1) pixel(x, y);
else {
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
(int) Math.round(ys - hs/2.0),
(int) Math.round(ws),
(int) Math.round(hs), null);
}
draw();
}
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>), rotated
* given number of degrees, and rescaled to the specified bounding box.
* The supported image formats are JPEG, PNG, and GIF.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @param scaledWidth the width of the scaled image in pixels
* @param scaledHeight the height of the scaled image in pixels
* @param degrees is the number of degrees to rotate counterclockwise
* @throws IllegalArgumentException if either {@code scaledWidth}
* or {@code scaledHeight} is negative
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight, double degrees) {
if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth);
if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight);
Image image = getImage(filename);
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(scaledWidth);
double hs = factorY(scaledHeight);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
if (ws <= 1 && hs <= 1) pixel(x, y);
offscreen.rotate(Math.toRadians(-degrees), xs, ys);
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
(int) Math.round(ys - hs/2.0),
(int) Math.round(ws),
(int) Math.round(hs), null);
offscreen.rotate(Math.toRadians(+degrees), xs, ys);
draw();
}
/***************************************************************************
* Drawing text.
***************************************************************************/
/**
* Write the given text string in the current font, centered at (<em>x</em>, <em>y</em>).
*
* @param x the center <em>x</em>-coordinate of the text
* @param y the center <em>y</em>-coordinate of the text
* @param text the text to write
*/
public static void text(double x, double y, String text) {
if (text == null) throw new NullPointerException();
offscreen.setFont(font);
FontMetrics metrics = offscreen.getFontMetrics();
double xs = scaleX(x);
double ys = scaleY(y);
int ws = metrics.stringWidth(text);
int hs = metrics.getDescent();
offscreen.drawString(text, (float) (xs - ws/2.0), (float) (ys + hs));
draw();
}
/**
* Write the given text string in the current font, centered at (<em>x</em>, <em>y</em>) and
* rotated by the specified number of degrees.
* @param x the center <em>x</em>-coordinate of the text
* @param y the center <em>y</em>-coordinate of the text
* @param text the text to write
* @param degrees is the number of degrees to rotate counterclockwise
*/
public static void text(double x, double y, String text, double degrees) {
if (text == null) throw new NullPointerException();
double xs = scaleX(x);
double ys = scaleY(y);
offscreen.rotate(Math.toRadians(-degrees), xs, ys);
text(x, y, text);
offscreen.rotate(Math.toRadians(+degrees), xs, ys);
}
/**
* Write the given text string in the current font, left-aligned at (<em>x</em>, <em>y</em>).
* @param x the <em>x</em>-coordinate of the text
* @param y the <em>y</em>-coordinate of the text
* @param text the text
*/
public static void textLeft(double x, double y, String text) {
if (text == null) throw new NullPointerException();
offscreen.setFont(font);
FontMetrics metrics = offscreen.getFontMetrics();
double xs = scaleX(x);
double ys = scaleY(y);
int hs = metrics.getDescent();
offscreen.drawString(text, (float) xs, (float) (ys + hs));
draw();
}
/**
* Write the given text string in the current font, right-aligned at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the text
* @param y the <em>y</em>-coordinate of the text
* @param text the text to write
*/
public static void textRight(double x, double y, String text) {
if (text == null) throw new NullPointerException();
offscreen.setFont(font);
FontMetrics metrics = offscreen.getFontMetrics();
double xs = scaleX(x);
double ys = scaleY(y);
int ws = metrics.stringWidth(text);
int hs = metrics.getDescent();
offscreen.drawString(text, (float) (xs - ws), (float) (ys + hs));
draw();
}
/**
* Display on screen, pause for t milliseconds, and turn on
* <em>animation mode</em>: subsequent calls to
* drawing methods such as {@code line()}, {@code circle()}, and {@code square()}
* will not be displayed on screen until the next call to {@code show()}.
* This is useful for producing animations (clear the screen, draw a bunch of shapes,
* display on screen for a fixed amount of time, and repeat). It also speeds up
* drawing a huge number of shapes (call {@code show(0)} to defer drawing
* on screen, draw the shapes, and call {@code show(0)} to display them all
* on screen at once).
* @param t number of milliseconds
*/
public static void show(int t) {
// sleep until the next time we're allowed to draw
long millis = System.currentTimeMillis();
if (millis < nextDraw) {
try {
Thread.sleep(nextDraw - millis);
}
catch (InterruptedException e) {
System.out.println("Error sleeping");
}
millis = nextDraw;
}
defer = false;
draw();
defer = true;
// when are we allowed to draw again
nextDraw = millis + t;
}
/**
* Display on-screen and turn off animation mode:
* subsequent calls to
* drawing methods such as {@code line()}, {@code circle()},
* and {@code square()} will be displayed on screen when called.
* This is the default.
*/
public static void show() {
defer = false;
nextDraw = -1;
draw();
}
// draw onscreen if defer is false
private static void draw() {
if (defer) return;
onscreen.drawImage(offscreenImage, 0, 0, null);
frame.repaint();
}
/***************************************************************************
* Save drawing to a file.
***************************************************************************/
/**
* Saves the drawing to using the specified filename.
* The supported image formats are JPEG and PNG;
* the filename suffix must be <tt>.jpg</tt> or <tt>.png</tt>.
*
* @param filename the name of the file with one of the required suffixes
*/
public static void save(String filename) {
if (filename == null) throw new NullPointerException();
File file = new File(filename);
String suffix = filename.substring(filename.lastIndexOf('.') + 1);
// png files
if (suffix.toLowerCase().equals("png")) {
try {
ImageIO.write(onscreenImage, suffix, file);
}
catch (IOException e) {
e.printStackTrace();
}
}
// need to change from ARGB to RGB for JPEG
// reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727
else if (suffix.toLowerCase().equals("jpg")) {
WritableRaster raster = onscreenImage.getRaster();
WritableRaster newRaster;
newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2});
DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel();
DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(),
cm.getRedMask(),
cm.getGreenMask(),
cm.getBlueMask());
BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null);
try {
ImageIO.write(rgbBuffer, suffix, file);
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
System.out.println("Invalid image file type: " + suffix);
}
}
/**
* This method cannot be called directly.
*/
@Override
public void actionPerformed(ActionEvent e) {
FileDialog chooser = new FileDialog(StdDraw.frame, "Use a .png or .jpg extension", FileDialog.SAVE);
chooser.setVisible(true);
String filename = chooser.getFile();
if (filename != null) {
StdDraw.save(chooser.getDirectory() + File.separator + chooser.getFile());
}
}
/***************************************************************************
* Mouse interactions.
***************************************************************************/
/**
* Returns true if the mouse is being pressed.
*
* @return <tt>true</tt> if the mouse is being pressed; <tt>false</tt> otherwise
*/
public static boolean mousePressed() {
synchronized (mouseLock) {
return mousePressed;
}
}
/**
* Returns the <em>x</em>-coordinate of the mouse.
*
* @return the <em>x</em>-coordinate of the mouse
*/
public static double mouseX() {
synchronized (mouseLock) {
return mouseX;
}
}
/**
* Returns the <em>y</em>-coordinate of the mouse.
*
* @return <em>y</em>-coordinate of the mouse
*/
public static double mouseY() {
synchronized (mouseLock) {
return mouseY;
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseClicked(MouseEvent e) { }
/**
* This method cannot be called directly.
*/
@Override
public void mouseEntered(MouseEvent e) { }
/**
* This method cannot be called directly.
*/
@Override
public void mouseExited(MouseEvent e) { }
/**
* This method cannot be called directly.
*/
@Override
public void mousePressed(MouseEvent e) {
synchronized (mouseLock) {
mouseX = StdDraw.userX(e.getX());
mouseY = StdDraw.userY(e.getY());
mousePressed = true;
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseReleased(MouseEvent e) {
synchronized (mouseLock) {
mousePressed = false;
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseDragged(MouseEvent e) {
synchronized (mouseLock) {
mouseX = StdDraw.userX(e.getX());
mouseY = StdDraw.userY(e.getY());
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseMoved(MouseEvent e) {
synchronized (mouseLock) {
mouseX = StdDraw.userX(e.getX());
mouseY = StdDraw.userY(e.getY());
}
}
/***************************************************************************
* Keyboard interactions.
***************************************************************************/
/**
* Returns true if the user has typed a key (that has not yet been processed).
*
* @return <tt>true</tt> if the user has typed a key (that has not yet been processed
* by {@link #nextKeyTyped()}; <tt>false</tt> otherwise
*/
public static boolean hasNextKeyTyped() {
synchronized (keyLock) {
return !keysTyped.isEmpty();
}
}
/**
* Returns the next key that was typed by the user (that your program has not already processed).
* This method should be preceded by a call to {@link #hasNextKeyTyped()} to ensure
* that there is a next key to process.
* This method returns a Unicode character corresponding to the key
* typed (such as {@code 'a'} or {@code 'A'}).
* It cannot identify action keys (such as F1 and arrow keys)
* or modifier keys (such as control).
*
* @return the next key typed by the user (that your program has not already processed).
* @throws NoSuchElementException if there is no remaining key
*/
public static char nextKeyTyped() {
synchronized (keyLock) {
if (keysTyped.isEmpty()) {
throw new NoSuchElementException("your program has already processed all keystrokes");
}
return keysTyped.removeLast();
}
}
/**
* Returns true if the given key is being pressed.
* <p>
* This method takes the keycode (corresponding to a physical key)
* as an argument. It can handle action keys
* (such as F1 and arrow keys) and modifier keys (such as shift and control).
* See {@link KeyEvent} for a description of key codes.
*
* @param keycode the key to check if it is being pressed
* @return <tt>true</tt> if {@code keycode} is currently being pressed;
* <tt>false</tt> otherwise
*/
public static boolean isKeyPressed(int keycode) {
synchronized (keyLock) {
return keysDown.contains(keycode);
}
}
/**
* This method cannot be called directly.
*/
@Override
public void keyTyped(KeyEvent e) {
synchronized (keyLock) {
keysTyped.addFirst(e.getKeyChar());
}
}
/**
* This method cannot be called directly.
*/
@Override
public void keyPressed(KeyEvent e) {
synchronized (keyLock) {
keysDown.add(e.getKeyCode());
}
}
/**
* This method cannot be called directly.
*/
@Override
public void keyReleased(KeyEvent e) {
synchronized (keyLock) {
keysDown.remove(e.getKeyCode());
}
}
/**
* Test client.
*/
public static void main(String[] args) {
StdDraw.square(.2, .8, .1);
StdDraw.filledSquare(.8, .8, .2);
StdDraw.circle(.8, .2, .2);
StdDraw.setPenColor(StdDraw.BOOK_RED);
StdDraw.setPenRadius(.02);
StdDraw.arc(.8, .2, .1, 200, 45);
// draw a blue diamond
StdDraw.setPenRadius();
StdDraw.setPenColor(StdDraw.BOOK_BLUE);
double[] x = { .1, .2, .3, .2 };
double[] y = { .2, .3, .2, .1 };
StdDraw.filledPolygon(x, y);
// text
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.text(0.2, 0.5, "black text");
StdDraw.setPenColor(StdDraw.WHITE);
StdDraw.text(0.8, 0.8, "white text");
}
}
import java.awt.Color;
public class Turtle {
public double x, y; // turtle is at (x, y)
public double angle; // facing this many degrees counterclockwise from the x-axis
// start at (x0, y0), facing a0 degrees counterclockwise from the x-axis
public Turtle(double x0, double y0, double a0) {
x = x0;
y = y0;
angle = a0;
}
// rotate orientation delta degrees counterclockwise
public void turnLeft(double delta) {
angle += delta;
}
// move forward the given amount, with the pen down
public void goForward(double step) {
double oldx = x;
double oldy = y;
x += step * Math.cos(Math.toRadians(angle));
y += step * Math.sin(Math.toRadians(angle));
StdDraw.line(oldx, oldy, x, y);
}
// pause t milliseconds
public void pause(int t) {
StdDraw.show(t);
}
public void setPenColor(Color color) {
StdDraw.setPenColor(color);
}
public void setPenRadius(double radius) {
StdDraw.setPenRadius(radius);
}
public void setCanvasSize(int width, int height) {
StdDraw.setCanvasSize(width, height);
}
public void setXscale(double min, double max) {
StdDraw.setXscale(min, max);
}
public void setYscale(double min, double max) {
StdDraw.setYscale(min, max);
}
// sample client for testing
public static void main(String[] args) {
double x0 = 0.5;
double y0 = 0.0;
double a0 = 60.0;
double step = Math.sqrt(3)/2;
Turtle turtle = new Turtle(x0, y0, a0);
turtle.goForward(step);
turtle.turnLeft(120.0);
turtle.goForward(step);
turtle.turnLeft(120.0);
turtle.goForward(step);
turtle.turnLeft(120.0);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment