// Implement a Kinematic Virtual Machine
import java.util.*;

public abstract class KVM extends Vector  // Kinematic Virtual Machine Object
{
   // Drawing routines must be implemented externally by an extended class
   public abstract void drawLine(double x1, double y1, double x2, double y2);
   public abstract void drawJoint(String name, double x, double y);

   KVM() {                                                // Constructor
      for (int i = 0 ; i < m.length ; i++)                   // Build the Stack
         m[i] = new Matrix3D();
   }
   //Names in CAPS are constants defined in class KVI below
   public void lineTo(double x,double y,double z) {
      matrix(add(KVI.LINETO)).translate(x,y,z);
   }
   public void moveTo(double x,double y,double z) {
      matrix(add(KVI.JOINT)).translate(x,y,z);
   }

   //adds a JOINT to the stack and sets its name
   public void joint(String name) { instr(add(KVI.JOINT)).name = name; }
   public void pop()              { add(KVI.POP); }
   public void push()             { add(KVI.PUSH); }
   public Matrix3D matrix(int i){ return instr(i).matrix; }  // Get Instr Matrix
   public String   name(int i)  { return instr(i).name; }    // Get Instr Name
   //instr() returns the KVI object at a certain position. It can then be used as a normal KVI object
   private KVI     instr(int i) { return (KVI)elementAt(i); }// Get Instruction
   private int     add(int type){                            // Add Instruction
      addElement(new KVI(type));
      return size()-1;
   }
   private Matrix3D[] m = new Matrix3D[20];               // The Matrix Stack

   // Render the Scene
   public void render() {
      int n = 1, type;
      m[0].identity();
      //loops through all the instructions
      for (int i = 0 ; i < size() ; i++)
	 if ((type = instr(i).type) == KVI.LINETO) {         // Draw a Line
	    double x = m[n-1].get(0,3);                         // From Local
	    double y = m[n-1].get(1,3);                         // Origin to
	    m[n-1].postMultiply(matrix(i));                     // Transformed
            drawLine(x, y, m[n-1].get(0,3), m[n-1].get(1,3));   // Local Origin
         }
	 else if (type == KVI.JOINT) {                       // Transform
            drawJoint(name(i), m[n-1].get(0,3), m[n-1].get(1,3));
            m[n-1].postMultiply(matrix(i));
         }
         //PUSH and POP keep track of which coordinate system the
         //virtual machine is in. At the highest level, it is absolute screen
         //coordinates. Then it progresses by the LOCAL tags to the legs,
         //the arms, the head and so on
         else if (type == KVI.PUSH)                          // PUSH
            m[n++].set(m[n-2]);
	 else                                                // POP
            n--;
   }
}

// One Kinematic Instruction Object
class KVI
{
   static final int LINETO = 0;
   static final int JOINT  = 1;
   static final int POP    = 2;
   static final int PUSH   = 3;
   int type;
   String name = null;
   Matrix3D matrix = new Matrix3D();
   KVI(int type) { this.type = type; }
}