
package stand;

import java.applet.Applet;
import java.awt.*;
import java.io.*;

import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.geometry.Cylinder;
import com.sun.j3d.utils.universe.*;

import javax.media.j3d.*;
import javax.vecmath.*;
import java.util.Enumeration;

public class Test extends Applet 
{

  // Constants for type of light to use

  private static final int DIRECTIONAL_LIGHT = 0;
  private static final int POINT_LIGHT = 1;
  private static final int SPOT_LIGHT = 2;

  private static final float LATTICE_QUANT = 1.0f;
  private static final int lightType = POINT_LIGHT;

  Color3f eColor    = new Color3f(0.0f, 0.0f, 0.0f);
  Color3f sColor    = new Color3f(1.0f, 1.0f, 1.0f);

  Color3f lColor1   = new Color3f(1.0f, 1.0f, 1.0f);
  Color3f alColor   = new Color3f(0.1f, 0.1f, 0.4f);
  Color3f bgColor   = new Color3f(0.01f, 0.01f, 0.05f);

  Color3f hColor  = new Color3f(0.6f, 0.1f, 0.1f);    // Red
  Color3f pColor  = new Color3f(0.1f, 0.1f, 0.6f);    // Blue
  Color3f gColor  = new Color3f(0.6f, 0.6f, 0.6f);    // Grey

  private SimpleUniverse u = null;

  BranchGroup         scene;
  TransformGroup      objScale;

  public BranchGroup createSceneGraph(SimpleUniverse u)
  {
    BranchGroup objRoot = new BranchGroup();

    // Global transform group...

    objScale = new TransformGroup();

    Transform3D t3d = new Transform3D();
    t3d.setScale(0.2);
    objScale.setTransform(t3d);

    // Set up the background

    // Create a bounds for the background and lights
    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

    Background bg = new Background(bgColor);
    bg.setApplicationBounds(bounds);

    objScale.addChild(bg);
    objRoot.addChild(objScale);

    return objRoot;
  }

  private void createLights()
  {
    Transform3D             t;

    // Create a transformation for the light

    t = new Transform3D();
    Vector3d lPos1 =  new Vector3d(10.0, 10.0, 10.0);
    t.set(lPos1);
    TransformGroup l1Trans = new TransformGroup(t);
    objScale.addChild(l1Trans);

    // Create a Geometry for the light

    ColoringAttributes caL1 = new ColoringAttributes();
    caL1.setColor(lColor1);
    Appearance appL1 = new Appearance();
    appL1.setColoringAttributes(caL1);
    l1Trans.addChild(new Sphere(0.05f, appL1));

    // Create light

    AmbientLight aLgt = new AmbientLight(alColor);

    Light lgt1 = null;

    Point3f lPoint  = new Point3f(0.0f, 0.0f, 0.0f);
    Point3f atten = new Point3f(10.0f, 0.0f, 0.0f);
    Vector3f lDirect1 = new Vector3f(lPos1);
    lDirect1.negate();

    lgt1 = new DirectionalLight(lColor1, lDirect1);

    // Set the influencing bounds

    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

    aLgt.setInfluencingBounds(bounds);
    lgt1.setInfluencingBounds(bounds);

    // Add the lights into the scene graph

    objScale.addChild(aLgt);
    objScale.addChild(lgt1);
  }

  private void makeBall(double x, double y, double z, Color3f color)
  {
    Transform3D     t;

    t = new Transform3D();
    Vector3d pos =  new Vector3d(x, y, z);
    t.set(pos);

    TransformGroup transGroup1 = new TransformGroup(t);
    transGroup1.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    transGroup1.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

    Material m = new Material(color, eColor, color, sColor, 100.0f);
    m.setLightingEnable(true);

    Appearance a = new Appearance();
    a.setMaterial(m);

//        Sphere sph = new Sphere(0.5f, Sphere.GENERATE_NORMALS, a);
    Sphere sph = new Sphere(0.25f, a);

    transGroup1.addChild(sph);
    objScale.addChild(transGroup1);
  }

  private double          testx[];
  private double          testy[];
  private double          testz[];
  
  private int             n = 3;

  private void buildTests()
  {

    testx = new double[n];
    testy = new double[n];
    testz = new double[n];

    this.loadit(0, 0.0, 0.0, 0.0);
    this.loadit(1, 1.5, 1.5, 0.0);
    this.loadit(2, -1.5, 1.5, 0.0);
  }
  
  private void loadit(int i, double x, double y, double z)
  {
    testx[i] = x;
    testy[i] = y;
    testz[i] = z;
  }

  private void doit()
  {
    this.buildTests();

    for ( int i = 0; i < n; i++ ) 
    {

      double x = testx[i];
      double y = testy[i];
      double z = testz[i];

      if ( i % 2 == 0 )
      {
        this.makeBall(x, y, z, hColor);
      }
      else
      {
        this.makeBall(x, y, z, pColor);
      }

      if ( i < n - 1 )
      {
        makeConnector(
                        x,
                        y,
                        z,
                        testx[ i + 1 ],
                        testy[ i + 1 ],
                        testz[ i + 1 ]
                        );
      }
    }
  }

  private void makeConnector(double lastx, double lasty, double lastz,
                                double x, double y, double z)
  {
    Transform3D     t;
    Vector3d        pos;
    float           radius = 0.05f;

    // Origin and termination vector...

    Vector3d originVec = new Vector3d(
                    lastx,
                    lasty,
                    lastz
                    );

    Vector3d termVec = new Vector3d(
                    x,
                    y,
                    z
                    );

    Vector3d diffVec = new Vector3d();
    diffVec.sub(termVec, originVec);

    // Length of the cylinder...

    float height = (float) diffVec.length();

    // Define end points as new vectors

    Vector3d originAxis = new Vector3d(0.0, 1.0, 0.0);   // MOD GF - need to calc rotation from
                                                         //          cylindars axis

    // Compute the value of the angle between them...

    double rotInRadians = originAxis.angle(diffVec);     // MOD GF - as above

    // Compute an orthagonal vector to later define the rotation axis

    Vector3d orthagonalAxis = new Vector3d();            // MOD GF - orthagonalAxis calced between
    orthagonalAxis.cross(originAxis, diffVec);           //          cylindars axis and required axis
    orthagonalAxis.normalize();                          //          (diffVec)

    // Create new rotation matrix

    AxisAngle4d rotAxis = new AxisAngle4d(orthagonalAxis, rotInRadians);

    // TransformGroup2 will handle the rotation ...  // MOD GF do rotation second

    TransformGroup transGroup2 = new TransformGroup();
    transGroup2.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    transGroup2.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

    // Now set the transform up...

    t = new Transform3D();
    transGroup2.getTransform(t);

    t.setRotation(rotAxis);

    transGroup2.setTransform(t);

    // Calculate the translaton from the cylinder origin...

    diffVec.scale(0.5);

    originVec.add(diffVec);    // MOD GF - changed to add
//    termVec.sub(diffVec);    // MOD GF - not reqd

    // TransformGroup2 handles the translation...  // MOD GF do translation first

    TransformGroup transGroup1 = new TransformGroup();
    transGroup1.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    transGroup1.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

    // Now set the transform up...

    t = new Transform3D();
    t.setTranslation(originVec);
    transGroup1.setTransform(t);

    // Appearance and materials...

    Material m = new Material(gColor, eColor, gColor, sColor, 100.0f);
    m.setLightingEnable(true);

    Appearance a = new Appearance();
    a.setMaterial(m);

    // Now build the cylinder...

    Cylinder cyl = new Cylinder(radius, height, a);

    transGroup2.addChild(cyl);
    transGroup1.addChild(transGroup2);
    objScale.addChild(transGroup1);
  }

  public Test()
  {
  }

  public void init()
  {
    setLayout(new BorderLayout());
    GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();

    Canvas3D c = new Canvas3D(config);
    add("Center", c);

    // Set up the universe...

    u = new SimpleUniverse(c);
    scene = this.createSceneGraph(u);

    this.doit();

    this.createLights();

    // Let Java 3D perform optimizations on this scene graph.

    scene.compile();

    // This will move the ViewPlatform back a bit so the
    // objects in the scene can be viewed.
    u.getViewingPlatform().setNominalViewingTransform();

    u.addBranchGraph(scene);
  }

  public void destroy()
  {
    u.removeAllLocales();
  }

  protected void finalize() throws Throwable
  {
    System.err.println("Finalizing");
    super.finalize();
  }

  // The following allows Vis to be run as an application
  // as well as an applet.
  
  public static void main(String[] args)
  {
    new MainFrame(new Test(), 700, 700);
  }

}
