home

shop

quotes

optical illusions

tutor

about

links

links

     
MathImage    shop_logo

    


/*
GoldenSquare.java
Any portion of this code may be used without permission from BrainyPage.
www.BrainyPage.com | Created by Raul Aguilar | Last Modified 200205152213

Enjoy!
*/

import java.awt.*;
import javax.swing.*;

import java.awt.event.*;
import java.awt.geom.*;
import java.math.*;

import java.applet.*;
import com.borland.jbcl.layout.*;

import java.util.*;


public class GoldenSquare extends JApplet{

  private aJFrame f;
  public static final int WIDTH = 400;
  public static final int HEIGHT = 400;

  public void init() {
    // Set the arguments.
    String[] args = { "11.0" };
    f = new aJFrame(args, getContentPane());
  }

  public void paint(Graphics g) {
    super.paint(g);
    f.redraw();
  }

  public static void main(String[] args) {
    aJFrame frame = new aJFrame(args);
    frame.setTitle("GoldenSquare");
    frame.setSize(WIDTH,HEIGHT);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.show();
  }
}

class aJFrame extends JFrame {

  public aJFrame(String[] args) {
    buildPanels(args, getContentPane());
  }

  public aJFrame(String[] args, Container cp) {
    buildPanels(args, cp);
  }

  public void buildPanels(String[] args, Container cp) {

    panelSimulate = new Simulate(args);
    Control panelControl = new Control(args, panelSimulate);

    JPanel p = new JPanel();
    p.setLayout(new XYLayout());
    p.add(panelSimulate, new XYConstraints(5, 5, 380, 335));
    p.add(panelControl, new XYConstraints(5, 345, 380, 90));
    cp.add(p,BorderLayout.CENTER);
  }

  public void redraw() {
    panelSimulate.repaint();
  }

  Simulate panelSimulate;
}

class Simulate extends JPanel {

  private double base;
  private String[] args;

  int w;
  int h;
  double w2;
  double h2;
  double cx;
  double cy;
  double positiveUnitsX;
  double positiveUnitsY;
  double transMathToDeviceX;
  double transMathToDeviceY;

  Point2D[] v;

  double Phi;
  double phi;

  public Simulate(String[] args) {
    setParameters(args);

    //Initialize drawing colors
    setBackground(Color.white);
    setForeground(Color.black);

    v = new Point2D[2];
  }

  public void setParameters(String controlValues) {
    StringTokenizer st = new StringTokenizer(controlValues);
    ArrayList al = new ArrayList();
    while (st.hasMoreTokens()) {
      al.add((String) st.nextToken());
    }
    String[] s = (String[]) al.toArray(new String[0]);
    setParameters(s);
  }

  public void setParameters(String[] args) {
    base = Double.parseDouble(args[0]);

    System.out.println("parameter values:");
    System.out.println();
    System.out.println("base = " + base);
  }

  public void fixDimensions() {

    // Find the width and height of the JPanel.
    Dimension d = getSize();
    w = d.width;
    h = d.height;

    // Calculate the half height.
    w2 = w / 2.0;
    h2 = h / 2.0;

    // Set the graph center at center of graph.
    cx = w / 8.0;
    cy = h / 8.0;

    // Set size of positive x-axis.
    positiveUnitsX = 12.0;

    // Find size of positive y-axis.
    positiveUnitsY = positiveUnitsX * h2 / w2;

    // In this code version,
    // transMathToDeviceX is same value as transMathToDeviceY.
    transMathToDeviceX = w2 / positiveUnitsX;
    transMathToDeviceY = h2 / positiveUnitsY;

    Phi = (1.0 + Math.sqrt(5))/2;
    phi = Phi - 1.0;
  }

  public void paint(Graphics g) {

    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;

    fixDimensions();
    drawGrid(g2);

    // Set the pen width.
    int penWidth = 2;
    g2.setStroke(new BasicStroke(penWidth));

    StartSquareOne();
    drawSquare(g2, v);

    for (int i=0; i<5; ++i) {
      MakeSquare();
      drawSquare(g2, v);
    }

    drawLogSpiral(g2);
  }

  private void StartSquareOne() {

    // Make a unit square in quadrant IV.
    v[0] = new Point2D.Double(0.0, 0.0);
    v[1] = new Point2D.Double(base, -1.0 * base);
  }

  private void drawSquare(
    Graphics2D g2,
    Point2D[] p) {

    g2.setPaint(new Color(255, 92, 0));
    Rectangle2D rect = new Rectangle2D.Double();
    rect.setFrameFromDiagonal(
      userToDevice(p[0].getX(), p[0].getY()),
      userToDevice(p[1].getX(), p[1].getY()));
    g2.draw(rect);
  }

  private void MakeSquare() {

    Point2D[] vNew = new Point2D[2];

    // Let the new vector start at tail of previous square.
    vNew[0] = new Point2D.Double();
    vNew[0].setLocation(v[1]);

    // For the previous vector:

    // find the diagonal vector,
    Point2D dv = sub(v[1], v[0]);

    // find unit vector,
    Point2D u = unit(dv);

    // and find the length
    double len = length(dv) / Math.sqrt(2.0);

    // Find tail of new vector.
    //
    // Add start point of new vector
    // to
    // (little) phi times
    // square root of 2 times
    // the length times
    // the unit vector rotated ninety degrees.
    //
    vNew[1] = new Point2D.Double(
      vNew[0].getX() + phi * Math.sqrt(2) * len * (-1.0 * u.getY()),
      vNew[0].getY() + phi * Math.sqrt(2) * len * ( 1.0 * u.getX()));

    v = vNew;
  }

  private double length(Point2D p) {
    double px = p.getX();
    double py = p.getY();
    return Math.sqrt(px * px + py * py);
  }

  private Point2D unit(Point2D p) {
    double len = Math.sqrt(p.getX() * p.getX() + p.getY() * p.getY());
    return new Point2D.Double(p.getX() / len, p.getY() / len);
  }

  private Point2D sub(Point2D a, Point2D b) {
    return new Point2D.Double(a.getX() - b.getX(), a.getY() - b.getY());
  }

  private void drawGrid(Graphics2D g2) {

    // Use physical xy points.
    double px;
    double py;
    double nx;
    double ny;

    double unitsPerGridLine = 1.0;
    double gridLines = positiveUnitsX/unitsPerGridLine;

    for (int i=0; i < 2*gridLines ; ++i) {

      px = cx + (i * unitsPerGridLine) * transMathToDeviceX;
      py = cy - (i * unitsPerGridLine) * transMathToDeviceY;

      // Find the minus x values for grid lines
      // on the negative side of the graph.
      nx = cx - (i * unitsPerGridLine) * transMathToDeviceX;
      ny = cy + (i * unitsPerGridLine) * transMathToDeviceY;

      if (i == 0) {
        // Draw the xy axis.
        g2.setPaint(new Color(255, 0, 255));
      }
      else {
        g2.setPaint(new Color(0, 255, 255));
        g2.draw(new Line2D.Double( nx, 0, nx, h));
        g2.draw(new Line2D.Double( 0, ny, w, ny));
      }

      g2.draw(new Line2D.Double( px, 0, px, h));
      g2.draw(new Line2D.Double( 0, py, w, py));
    }
  }

  private void drawLogSpiral(Graphics2D g2) {

    double cxLogSpiral = (5.0  + 3.0 * Math.sqrt(5.0)) / 10.0;
    double cyLogSpiral = (Math.sqrt(5.0) - 5.0) / 10.0;

    double a = Math.pow(4.0/5.0, 1.0/4.0) * Math.pow(Phi, Math.atan(2.0)/Math.PI);
    double b = 2.0 * Math.log(Phi)/Math.PI;

    // Set pen color.
    g2.setPaint(new Color(0, 0, 255));

    // Angular change in degree.
    double delta = 1.0;

    // Previous x and y device values.
    int x_prev = 0;
    int y_prev = 0;

    // Use a start angle of -1080 degrees to begin the spiral
    // near the Golden Square convergence point.
    double degreeStart = -1080;
    for (double degree = degreeStart; degree < 14.0; degree += delta) {

      // Convert to radian.
      double radian = Math.toRadians(degree);

      // Find the cosine.
      double radius = base * a * Math.exp(b * radian);

      // Polar to cartesian transform of the positive square root.
      double px = radius * Math.cos(radian);
      double py = radius * Math.sin(radian);

      // Quadrant 1.
      // Switch the coordinates.
      int x = userToDeviceX(-1.0 * px + base * cxLogSpiral);
      int y = userToDeviceY(py + base * cyLogSpiral);

      if ((degreeStart + delta / 2.0) < degree) {

        // Render the normal distribution line.
        Point2D startP = new Point2D.Double(x_prev, y_prev);
        Point2D endP = new Point2D.Double(x, y);
        g2.draw(new Line2D.Double(startP, endP));
      }
      x_prev = x;
      y_prev = y;
    }
  }

  private Point2D userToDevice(double x, double y) {
    double px = cx + x * transMathToDeviceX;
    double py = cy - y * transMathToDeviceY;
    return new Point2D.Double(px, py);
  }

  private int userToDeviceX(double x) {
    return (int) (cx + x * transMathToDeviceX);
  }

  private int userToDeviceY(double y) {
    return (int) (cy - y * transMathToDeviceY);
  }
}

class Control extends JPanel {

  private String[] args;
  private JTextField tf;
  private Simulate s;

  public Control(String[] args, Simulate sp) {

    this.args = args;
    this.setLayout(new XYLayout());

    s = sp;

    JButton b = new JButton("Go");
    ActionListener bl = new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        s.setParameters(tf.getText().trim());
        s.repaint();
      }
    };
    b.addActionListener(bl);
    add(b, new XYConstraints(0, 3, 50, 20));

    tf = new JTextField("11.0",5);
    add(tf, new XYConstraints(60, 3, 50, 20));
  }
}