home

shop

quotes

optical illusions

tutor

about

links

links

     
MathImage    shop_logo

    


/*
GoldenTriangle.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 GoldenTriangle 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 = { "3.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("GoldenTriangle");
    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 double a;
  private double b;
  private String[] args;

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

  Point2D[] p;

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

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

  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 / 3.0;
    cy = 2.0 * h / 3.0;

    // Set size of positive x-axis.
    positiveUnitsX = 4.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;
  }

  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));

    MakeTriangle();
    convergedCenterP = convergedCenter(p);

    // Adjust center of made triangle.
    p[0] = sub(p[0], convergedCenterP);
    p[1] = sub(p[1], convergedCenterP);
    p[2] = sub(p[2], convergedCenterP);

    drawTriangle(g2, p);

    // Find the logarithmic spiral a and b.
    Point2D xaxis = new Point2D.Double(1.0, 0.0);
    double theta0 = angle(xaxis, p[0]);
    double theta1 = -1.0 * angle(xaxis, p[1]);
    double thetaDegrees0 = Math.toDegrees(theta0);
    double thetaDegrees1 = Math.toDegrees(theta1);
    double length0 = length(p[0]);
    double length1 = length(p[1]);
    b = Math.log(length0/length1)/(theta0-theta1);
    a = length0 / Math.exp(b * theta0);

    Point2D q;
    for (int i=0; i<5; ++i) {
      p[0] = findNextPoint(p);
      q = p[0];
      p[0] = p[1];
      p[1] = p[2];
      p[2] = q;
      drawTriangle(g2, p);
    }

    drawLogSpiral(g2);
  }

  private Point2D convergedCenter(Point2D[] p) {

    // Make a copy of the p points.
    Point2D[] pc = (Point2D[]) p.clone();

    Point2D q;

    // Use twenty iterations to converge on an approximate center.
    for (int i=0; i<20; ++i) {
      pc[0] = findNextPoint(pc);
      q = pc[0];
      pc[0] = pc[1];
      pc[1] = pc[2];
      pc[2] = q;
    }

    // Select the last point found p[0] to return.
    return pc[0];
  }

  private void MakeTriangle() {

    // Consider an osocillies triangle with
    // base length of 2 and angles of 72 degrees.
    double b2 = base / 2.0;
    double t = b2 * Math.tan(Math.toRadians(72));

    p = new Point2D[3];

    p[0] = new Point2D.Double(b2, t);
    p[1] = new Point2D.Double(base, 0.0);
    p[2] = new Point2D.Double(0.0, 0.0);
  }

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

    g2.setPaint(new Color(255, 92, 0));
    g2.draw(new Line2D.Double(userToDevice(p[0]), userToDevice(p[1])));
    g2.draw(new Line2D.Double(userToDevice(p[1]), userToDevice(p[2])));
    g2.draw(new Line2D.Double(userToDevice(p[2]), userToDevice(p[0])));
  }

  private Point2D findNextPoint(Point2D[] p) {

    // Create vector from bisecting triangle point.
    Point2D v01 = unit(vector(p[1], p[0]));
    Point2D v21 = unit(vector(p[1], p[2]));

    Point2D v = unit(avg(v01, v21));
    Point2D s = p[1];

    Point2D u = unit(vector(p[2], p[0]));
    Point2D r = p[2];

    double srx = s.getX() - r.getX();
    double sry = s.getY() - r.getY();

    double ux = u.getX();
    double uy = u.getY();
    double vx = v.getX();
    double vy = v.getY();

    double nextP =
      (srx * (-1.0 * vy) - sry * (-1.0 * vx)) /
      (ux * (-1.0 * vy) - uy * (-1.0 * vx));

    return new Point2D.Double(
      r.getX() + u.getX() * nextP,
      r.getY() + u.getY() * nextP);

  }

  private double angle(Point2D p0, Point2D p1) {

    // Convert both vectors to unit.
    Point2D up0 = unit(p0);
    Point2D up1 = unit(p1);

    // Get the arc cosine of the dot product.
    return Math.acos(up0.getX() * up1.getX() + up0.getY() * up1.getY());
  }

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

  private Point2D vector(Point2D pFrom, Point2D pTo) {
    return new Point2D.Double(pTo.getX() - pFrom.getX(), pTo.getY() - pFrom.getY());
  }

  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 avg(Point2D a, Point2D b) {
    return new Point2D.Double((a.getX() + b.getX()) / 2.0, (a.getY() + b.getY()) / 2.0);
  }

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

  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 = .2;
    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) {

    // 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;

    // Span one quadrant 0 to 90 degree.
    // Account for the square root only producing a positive radius.
    double degreeStart = -720;
    for (double degree = degreeStart; degree < 90.0; degree += delta) {

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

      // Find the cosine.
      double radius = 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.
      int x = userToDeviceX(px);
      int y = userToDeviceY(py);

      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(Point2D p) {
    double px = (cx + p.getX() * transMathToDeviceX);
    double py = (cy - p.getY() * 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("2.0",5);
    add(tf, new XYConstraints(60, 3, 50, 20));
  }
}