home

shop

quotes

optical illusions

tutor

about

links

links

     
MathImage    shop_logo

    


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

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 Lemniscate extends JApplet{

  private aJFrame f;

  public void init() {
    // Set the arguments.
    String[] args = { "2.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("Lemniscate");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.show();
  }
}

class aJFrame extends JFrame {

  public static final int SQUARE = 400;
  public static final int WIDTH = SQUARE;
  public static final int HEIGHT = SQUARE;

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

    setSize(WIDTH,HEIGHT);

    JPanel p = new JPanel();
    p.setLayout(new XYLayout());
    p.add(panelSimulate, new XYConstraints(5, 5, WIDTH - 20, HEIGHT - 65));
    p.add(panelControl, new XYConstraints(5, WIDTH - 55, HEIGHT - 20, 90));

    cp.add(p,BorderLayout.CENTER);
  }

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

  Simulate panelSimulate;
}

class Simulate extends JPanel {

  private double r;
  private String[] args;

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

  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) {
    r = Double.parseDouble(args[0]);

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

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

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

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

    // Use to convert degree to radian.
    degreeToRadian = Math.PI /180.0;

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

    drawLemniscate(g2);
    drawCircle(g2, r * Math.sqrt(2.0), 0, r);
    drawOrginToCircle(g2, 30, r * Math.sqrt(2.0), 0, r);

    drawPoint(g2, 0, 0);
    drawPoint(g2, r * Math.sqrt(2.0), 0);

  }

  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 drawLemniscate(Graphics2D g2) {

    RenderingHints hint = new RenderingHints(null);
    hint.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHints(hint);
    g2.setStroke(new BasicStroke(1));

    // 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.
    for (double degree = 0.0; degree < 360.0; degree += delta) {

      // Convert to radian.
      double radian = degree * degreeToRadian;

      // Find the cosine.
      double cos = Math.cos(2.0 * radian);

      // To avoid the complex plane, use only the positive return values.
      if (0.0 <=cos) {

        // Find the polar value.
        double radius = Math.sqrt(2 * r * r * cos);

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

        // Quadrant 1.
        int x = userToDeviceX(dx);
        int y = userToDeviceY(dy);

        if ((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 void drawOrginToCircle(
    Graphics2D g2,
    double angle,
    double cx,
    double cy,
    double r) {

    RenderingHints hint = new RenderingHints(null);
    hint.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHints(hint);
    g2.setStroke(new BasicStroke(1));

    /*
    In solving for the line to circle points of intersection,
    we can easily call,

    drawPoint(g2, x, y);

    but the challenge remains in computing the other intersect
    point.

    Instead consider equations,

    y = mx + b     and
    x^2 + y^2 = r2

    Since m = tan(angle) and b = 0 substitution leaves,

    x = +- r^2 / sqrt(1 + tan(angle))

    wherein we can solve for the positive values of y

    y = sqrt(r^2 - x^2)

    */

    double theta = Math.toRadians(angle);
    double m = Math.tan(theta);

    double a = 1 + m * m;
    double b = -2.0 * Math.sqrt(2) * r;
    double c = r * r;

    double xLeft = quadratic(-1.0, a, b, c);
    double xRight = quadratic(1.0, a, b, c);

    // Account for translation.
    double xL = xLeft - (Math.sqrt(2) * r);
    double xR = xRight - (Math.sqrt(2) * r);

    double yLeft = Math.sqrt(r * r - xL * xL);
    double yRight = Math.sqrt(r * r - xR * xR);

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

    g2.drawLine(
      userToDeviceX(r * Math.sqrt(2)),
      userToDeviceY(0),
      userToDeviceX(xRight),
      userToDeviceY(yRight));

    g2.drawLine(
      userToDeviceX(0),
      userToDeviceY(0),
      userToDeviceX(xRight),
      userToDeviceY(yRight));

    drawPoint(g2, xLeft, yLeft);
    drawPoint(g2, xRight, yRight);

    double rP = r * Math.sqrt(2.0) * Math.sqrt(Math.cos(2.0 * theta));
    double xP = rP * Math.cos(theta);
    double yP = rP * Math.sin(theta);
    drawPoint(g2, xP, yP);
  }

  private double quadratic(double multi, double a, double b, double c) {

    double operand = b * b - 4.0 * a * c;
    if (0 < operand) { return (-1.0 * b  + multi * Math.sqrt(operand)) / (2.0 * a); }
    else { return 0; }

  }

  private void drawCircle(
    Graphics2D g2,
    double cx,
    double cy,
    double r) {

    RenderingHints hint = new RenderingHints(null);
    hint.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHints(hint);
    g2.setStroke(new BasicStroke(1));

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

    Ellipse2D circle =
      new Ellipse2D.Double(
        userToDeviceX(cx - r),
        userToDeviceY(cy + r),
        2 * r * transMathToDeviceX,
        2 * r * transMathToDeviceY);
    g2.draw(circle);
  }

  private void drawPoint(
    Graphics2D g2,
    double x,
    double y) {

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

    // Radius in device units (pixels).
    double d = 2;

    /*
    Rectangle2D rect = new Rectangle2D.Double();
    rect.setFrameFromDiagonal(
      new Point2D.Double(userToDeviceX(x) - d, userToDeviceY(y) - d),
      new Point2D.Double(userToDeviceX(x) + d, userToDeviceY(y) + d));
    */

    Ellipse2D rect = new Ellipse2D.Double();
    rect.setFrameFromDiagonal(
      new Point2D.Double(userToDeviceX(x) - d, userToDeviceY(y) - d),
      new Point2D.Double(userToDeviceX(x) + d, userToDeviceY(y) + d));

    g2.fill(rect);
    g2.draw(rect);
  }

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