public void Setup(int ViewWidth, int ViewHeight)
       /* initial setup for drawing that doesn't need to be done for every frame. */
     {
   if (!SetupDone) {
     ArrowShape = MakeArrow(Shaded);
     SetupDone = true;
   } /*if*/
   ProjectionMatrix =
       Mat4f.frustum(
               /*L =*/ -(float) ViewWidth / ViewHeight,
               /*R =*/ (float) ViewWidth / ViewHeight,
               /*B =*/ -1.0f,
               /*T =*/ 1.0f,
               /*N =*/ 1.0f,
               /*F =*/ 10.0f)
           .mul(Mat4f.translation(new Vec3f(0, 0, -3.0f)));
 } /*Setup*/
 public void Draw(double AtTime)
       /* draws the arrow in its orientation according to the specified time. Setup
       must already have been called on current GL context. */
     {
   final float Azi = (float) (Math.PI * AtTime);
   final float Elev = (float) (Math.PI * Math.sin(0.25 * Math.PI * AtTime));
   final float Roll = (float) (Math.PI / 10.0 * Math.sin(0.25 * Math.PI * AtTime));
   ArrowShape.Draw(
       /*ProjectionMatrix =*/ ProjectionMatrix,
       /*ModelViewMatrix =*/ Mat4f.rotation(Mat4f.AXIS_Z, Azi)
           .mul(Mat4f.rotation(Mat4f.AXIS_X, Elev))
           .mul(Mat4f.rotation(Mat4f.AXIS_Y, Roll))
           .mul(Mat4f.scaling(2.0f, 2.0f, 2.0f)),
       /*Uniforms =*/ new GLUseful.ShaderVarVal[] {
         new GLUseful.ShaderVarVal("light_direction", new float[] {-0.7f, 0.7f, 0.0f}),
         new GLUseful.ShaderVarVal("light_brightness", 1.0f),
         new GLUseful.ShaderVarVal("light_contrast", 0.5f),
         new GLUseful.ShaderVarVal("vertex_color", ArrowColor),
       });
 } /*Draw*/