
// MoveableLimb.java
// Thana Konglikhit, October 2003, s4310170@maliwan.psu.ac.th
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Třída MoveableLimb umožňuje pohyb končetiny kolem os x, y a z.
   Dělá to pomocí uzlů typu TG xAxisTG, yAxisTG a zAxisTG:

    startLimbTG
       |
       |
       V
    orientTG --> xAxisTG --> yAxisTG --> zAxisTG ----> LatheShape3D
                                             |
                                              -------> endLimbTG

   Třída MoveableLimb spravuje údaje pro tyto tři osy a 
   ignoruje rotace, které by dostaly končetinu mimo její rozsah.
   Není-li rozsah stanoven, předpokládá se, že má hodnotu 0 
   (tj. rotace kolem dané osy není možná).

   Rotace zpracovává metoda updateLimb(), která se volá s 
   argumentem osy a úhlu.

   Končetina může být resetována do své původní polohy pomocí
   metody reset().

   Třída MoveableLimb také obsahuje jméno končetiny, které
   se používá jen proto, aby byly chybové hlášky o něco
   smysluplnější.
*/

import javax.media.j3d.*;


public class MoveableLimb extends Limb
{  
  // konstanty pro jednotlivé osy
  private final static int X_AXIS = 0;
  private final static int Y_AXIS = 1;
  private final static int Z_AXIS = 2;

  private String limbName;
  private double xMin, xMax, yMin, yMax, zMin, zMax;   // rozsahy os
  private double xCurrAng, yCurrAng, zCurrAng;  // aktuální úhel ve 3 osách

  private Transform3D currTrans, rotTrans;    // používáme pro opakované výpočty



  public MoveableLimb(String lName, int lNo, String jn0, String jn1, 
                      int axis, double angle, 
                      double[] xs, double[] ys, String tex)
  {
    super(lNo, jn0, jn1, axis, angle, xs, ys, tex);

    rotTrans = new Transform3D();
    currTrans = new Transform3D();

    limbName = lName;
    xMin = 0; xMax = 0;     // tyto výchozí rozsahy brání jakékoliv rotaci
    yMin = 0; yMax = 0; 
    zMin = 0; zMax = 0; 
    xCurrAng = 0; yCurrAng = 0; zCurrAng = 0;  
  } // konec metody: MoveableLimb()



  public void setRanges(double x1, double x2, double y1, double y2, 
                      double z1, double z2)
  // nastaví rozsahy všech os
  { setRange(X_AXIS, x1, x2);
    setRange(Y_AXIS, y1, y2);
    setRange(Z_AXIS, z1, z2);
  } // konec metody: setRanges()



  public void setRange(int axis, double angle1, double angle2)
  // nastav rozsah pouze pro danou osu
  {
    if (angle1 > angle2) {
      System.out.println(limbName + ": range in wrong order... swapping");
      double temp = angle1;
      angle1 = angle2;
      angle2 = temp;
    }
    if (axis == X_AXIS) {
      xMin = angle1;  xMax = angle2;
    }
    else if (axis == Y_AXIS) {
      yMin = angle1;  yMax = angle2;
    }
    else {  // Z_AXIS
      zMin = angle1;  zMax = angle2;
    }
  }  // konec metody: setRange()


  public void printLimb()
  { super.printLimb();
    System.out.println(
      "     < " + limbName +
           " (" + xMin + ", " + xMax + ") (" + yMin + ", " +
                  yMax + ") (" + zMin + ", " + zMax +  ")>"  );
  } // konec metody: printLimb()


  public void updateLimb(int axis, double angleStep)
  // Pokus se otočit tuto končetino o úhel angleStep kolem osy axis
  {
    if (axis == X_AXIS)
      applyAngleStep(angleStep, xCurrAng, axis, xMax, xMin);
    else if (axis == Y_AXIS)
      applyAngleStep(angleStep, yCurrAng, axis, yMax, yMin);
    else    // Z_AXIS
      applyAngleStep(angleStep, zCurrAng, axis, zMax, zMin);
  }  // konec metody: updateLimb()



  private void applyAngleStep(double angleStep, double currAngle, int axis,
                                        double max, double min)
  /* Před jakoukoliv rotací ověří, že parametr angleStep
     nepřetáhne končetinu mimo její rozsah.
     Pokud by přetáhl, pak otočí končetinu jen do této hranice.
  */
  {        
    if ((currAngle >= max) && (angleStep > 0)) {  // překročí maximum
      System.out.println(limbName + ": bez rotace; již na maximu");
      return;
    }

    if (currAngle <= min && (angleStep < 0)) {   // spadne pod minimum
      System.out.println(limbName + ": bez rotace; již na minimu");
      return;
    }
        
    double newAngle = currAngle + angleStep;
    if (newAngle > max) {
      System.out.println(limbName + ": dosaženo maximálního úhlu");
      angleStep = max - currAngle;   // otoč pouze do maximálního úhlu
    }
    else if (newAngle < min) {
      System.out.println(limbName + ": dosaženo minimálního úhlu");  
      angleStep = min - currAngle;   // otoč pouze do minimálního úhlu
    }
    // System.out.println("angleStep: " + angleStep);

    makeUpdate(axis, angleStep);    // proveď rataci
  }  // konec metody: applyAngleStep()



  private void makeUpdate(int axis, double angleStep)
  // otoč končetinou o úhel angleStep kolem osy axis
  {
    if (axis == X_AXIS) {
      rotTrans.rotX( Math.toRadians(angleStep));
      xAxisTG.getTransform(currTrans);
      currTrans.mul(rotTrans);
      xAxisTG.setTransform(currTrans);
      xCurrAng += angleStep;
    }
    else if (axis == Y_AXIS) {
      rotTrans.rotY( Math.toRadians(angleStep));
      yAxisTG.getTransform(currTrans);
      currTrans.mul(rotTrans);
      yAxisTG.setTransform(currTrans);
      yCurrAng += angleStep;
    }
    else {  // osa z
      rotTrans.rotZ( Math.toRadians(angleStep));
      zAxisTG.getTransform(currTrans);
      currTrans.mul(rotTrans);
      zAxisTG.setTransform(currTrans);
      zCurrAng += angleStep;
    }
  }  // konec metody: makeUpdate()



  public void reset()
  // vynuluj the limb to its initial orientation
  {
    rotTrans.rotX( Math.toRadians(-xCurrAng));   // vynuluj úhel x
    xAxisTG.getTransform(currTrans);
    currTrans.mul(rotTrans);
    xAxisTG.setTransform(currTrans);
    xCurrAng = 0;

    rotTrans.rotY( Math.toRadians(-yCurrAng));    // vynuluj úhel y
    yAxisTG.getTransform(currTrans);
    currTrans.mul(rotTrans);
    yAxisTG.setTransform(currTrans);
    yCurrAng = 0;

    rotTrans.rotZ( Math.toRadians(-zCurrAng));    // vynuluj úhel z
    zAxisTG.getTransform(currTrans);
    currTrans.mul(rotTrans);
    zAxisTG.setTransform(currTrans);
    zCurrAng = 0;
  }  // konec metody: reset()

}  // konec třídy: MoveableLimb
