/*=============================================================================
 *     Copyright Rob Clark 2000.  All Rights Reserved.
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
 */

pkg.system.declareJavaPackage("java.awt");

var swing = new JavaPackage("javax.swing");
var Math  = java.lang.Math;
var Point = java.awt.Point;
var Dimension   = java.awt.Dimension;
var BasicStroke = java.awt.BasicStroke;

function sin(d)
{
  return Math.sin( Math.PI * d / 180.0 );
}

function cos(d)
{
  return Math.cos( Math.PI * d / 180.0 );
}


var min = java.lang.Math.min;
var max = java.lang.Math.max;


/**
 * A widget that given an inner radius, outer radius, and theta, will calulate
 * and display the widest arc through the specified curve.
 * 
 * @param ri           inner radius (Ri)
 * @param ro           outer radius (Ro)
 * @param theta        angle turn sweeps through
 */
public function CurveDisplay( ri, ro, theta )
  extends swing.JComponent()
{
  /**
   * ri  ->  Ri (inner radius)
   * ro  ->  Ro (outer radius)
   * rw  ->  Rw (widest arc radius)
   * 
   * if center of Ci (arc describing inner edge of curve) and Co (arc describing
   * outer edge of curve) are (0,0), then center of Cw (arc describign widest
   * line) is (-x,-y)
   * 
   * 
   *          ro - ri cos(theta/2)
   *   rw  = ----------------------
   *            1 - cos(theta/2)
   * 
   *    x  = rw - ro
   *    z  = rw - ri
   *    y  = z sin(theta/2)
   */

  var rw;
  var x;
  var y;
  var z;

  /**
   * When any of ri, ro, or theta change, this shoudl be called to recalculate
   * widest arc through curve.
   */
  function recalculate()
  {
    rw = (ro - (ri * cos(theta/2.0))) / (1.0 - cos(theta/2.0));
    x  = rw - ro;
    z  = rw - ri;
    y  = z * sin(theta/2.0);

    repaint();
  }

  /**
   * Set the Ri and Ro parameters.  Must be less than outer radius.
   */
  public function setRadius( _ri, _ro )
  {
    ri = _ri;
    ro = _ro;
    recalculate();
  }

  /**
   * Set the angle the curve sweeps through.  This parameter is in degrees and
   * should be between 0 and 180.
   */
  public function setTheta( _theta )
  {
    theta = _theta;
    recalculate();
  }

  /**
   */
  public function getWidestArcRadius()
  {
    return rw;
  }

  /**
   */
  public function paint( g )
  {
    writeln("ro="+ro+",ri="+ri+",rw="+rw);

    /**
     * @param o            origin
     * @param r            radius
     * @param sa           start-angle
     * @param aa           sweep-angle
     */
    function drawArc( o, r, sa, aa )
    {
      g.drawArc( o.x - r, o.y - r,
                 r * 2.0, r * 2.0,
                 sa, aa );
    }

    /**
     */
    function drawLine( x, y, dx, dy )
    {
      g.drawLine( x, y, x + dx, y + dy );
    }

    /* the swing coord system has (0,0) as the upper left corner, so everything
     * needs to be translated relative to that...
     */

    var w = getWidth();
    var h = getHeight();

    var o = new Point( w/2, h/2 );  // the origin, center of curve

    //g.drawLine( o.x, o.y, o.x + ro, o.y );

    g.setStroke( new BasicStroke(2) );

    // draw inner-curve:
    drawArc( o, ri, 0, theta );

    // draw outer-curve:
    drawArc( o, ro, 0, theta );

    // draw straight leading into turn:
    drawLine( o.x + ri * cos(theta), o.y - ri * sin(theta),
              -y * cos(90.0-theta), -y * sin(90.0-theta) );
    drawLine( o.x + ro * cos(theta), o.y - ro * sin(theta),
              -y * cos(90.0-theta), -y * sin(90.0-theta) );

    // draw straight coming out of turn:
    drawLine( o.x + ri, o.y, 0, y );
    drawLine( o.x + ro, o.y, 0, y );

    // draw the widest-curve:
    g.setStroke( new BasicStroke( 2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10, [2,5], 0 ) );
    drawArc( new Point( o.x - x, o.y + y ), rw, 0, theta );
  }

  recalculate();
}

/**
 * This is the window that has the curve display calc plus sliders and stuff...
 */
function CurveCalc()
  extends swing.JFrame()
{
  var innerRadius = 80.0;
  var trackWidth  = 40.0;
  var angle       = 140.0;

  var box = new swing.Box( swing.BoxLayout.Y_AXIS );

  // the curve-calculator:
  var curveDisp  = new CurveDisplay( innerRadius, innerRadius + trackWidth, angle );
  curveDisp.setPreferredSize( new Dimension( 400, 400 ) );
  box.add(curveDisp);

  // the angle slider:
  box.add( new swing.JLabel("Angle:") );
  var angleSlider = new swing.JSlider( 15, 165, angle );
  angleSlider.addChangeListener( new (function() extends swing.event.ChangeListener() {
    public function stateChanged( evt )
    {
      angle = angleSlider.getValue();
      updateAll();
    }
  })() );
  box.add(angleSlider);

  // the radius slider:
  box.add( new swing.JLabel("Radius:") );
  var radiusSlider = new swing.JSlider( 10, 90, innerRadius );
  radiusSlider.addChangeListener( new (function() extends swing.event.ChangeListener() {
    public function stateChanged( evt )
    {
      innerRadius = 0.0 + radiusSlider.getValue();
      updateAll();
    }
  })() );
  box.add(radiusSlider);

  // the width slider:
  box.add( new swing.JLabel("Width:") );
  var widthSlider = new swing.JSlider( 10, 70, trackWidth );
  widthSlider.addChangeListener( new (function() extends swing.event.ChangeListener() {
    public function stateChanged( evt )
    {
      trackWidth = widthSlider.getValue();
      updateAll();
    }
  })() );
  box.add(widthSlider);

  var statusLabel = new swing.JLabel();
  box.add(statusLabel);

  getContentPane().add(box);

  function updateAll()
  {
    var rw = curveDisp.getWidestArcRadius();
    var r  = ((2 * innerRadius) + trackWidth) / 2;

    curveDisp.setTheta(angle);
    curveDisp.setRadius( innerRadius, innerRadius + trackWidth );
    statusLabel.setText("<html>R<sub>i</sub> = " + innerRadius +
                        "<br>R<sub>o</sub> = " + (innerRadius + trackWidth) +
                        "<br>R<sub>w</sub> = " + rw +
                        "<br>widest arc is " +  (((rw-r)/r)*100.0) + "% larger" +
                        "</html>" );
  }

  updateAll();

  pack();
}


var window = new CurveCalc();
window.setVisible(true);