
// Bezier.java;  Implements Bezier Spline / Nurb Patch for older VRML plug-ins (Cosmo)

/*
  Intent:  Create Higher Quality 3d VRML renderings from smaller files for good Web 3d 
  and diplay them in the fast, familiar, standard VRML plug-in viewers ( eg. Cosmo ).
    
   Future Features: Free Portable VRML Editor with Splines & Mesh Reduction.
   
   Motivation: Make VRML animation better, more portable, better supported, more widely used.
   
   Future Ports: 'pure' java, Java3D, VRML EAI (current version  Script Node to VRML plug-in)
   
  Evgeny Demidov,   _5 Feb 01: Java / Script curve mesh made & displayed in VRML viewer.
  Paul Flavin___,   18 Feb 01: No additional funcionality, more OOPs & read-able.
                    21 Feb 01:
    
    My 2nd revision, no attempt to add functionality, added diagnostics & speed -- PF
                
*/

import vrml.*;              //  note: won't compile without these Class files in Path.
import vrml.field.*;        //  
import vrml.node.Script;    //  not for newbies: this is vrml-script-java-plug-in stuff. 
import vrml.node.Node;      //  you could save a line by using 'vrml.node.*' ;^)


public class Bezier  extends Script                         // implements Runnable
{                                                           // VRML Field Names / Tag / Id
static final String Field_Int_LOD_N         = "num";        // Level Of Detail : [ 4..7.. ]
static final String Field_FA_ControlPoints  = "CPoint";
static       int    number_of_fields_read   = 0;
static       int    timesConstructorCalled  = 0, timesInitializeCalled = 0;    

 static final int   ControlPointsArrayLength = 3 * 16;  // Demitry's Magic Number: not needed? 
                                                        // should be 'static': for speed    
 static int         level_of_detail;                    // bezier patch dimension, originally "n" 
 static float [][]  bezierMatrix       = null;          // originally double ( Demitry's code )
 static int   []    splineIndexValues  = null;          //
                                            // Thread engine = null;  
                                            //   VRMLHelper VrmlHelper = new VRMLHelper( ); 

    public void initialize( )
    {                                          //   do ONE time only! : is called many times.
        if(     timesInitializeCalled++ < 3 )  {    println( "initialize-ing : called   " ); 
            if( timesInitializeCalled  == 1 )  {    println( "initialize-ing : _ initializing _" );  
                                                       
                    level_of_detail     = getIntField(               Field_Int_LOD_N  ); // level_of_detail = 4; // lod changable
                    bezierMatrix        = generateBezierMatrix(      level_of_detail  );
                    splineIndexValues   = generateSplineIndexValues( level_of_detail  );    
                    
                                                println( Field_Int_LOD_N + "  " +    
                                                        level_of_detail + " <-- Level of Detail " );  
        }    }
                                            
          float [ ] splineControlPoints = getFloatArrayFromField( Field_FA_ControlPoints, ControlPointsArrayLength );               
          float [ ] splinePatchPoints   = calculatePatches( splineControlPoints,  bezierMatrix      );  
            
        setVRMLSplineCoordPoints(   splinePatchPoints, splineIndexValues  );                        
    }                                                                     //___ end intitialize __
    
    
        
    public void  setVRMLCoordPointIndexValues(            int   [ ]           splineIndexValues )
    { ( (MFInt32)                                         ( (Node) ( (SFNode)         
        getField(                             "spline"         ) ).getValue()
                  ).getEventIn(               "set_coordIndex" ) ).setValue(  splineIndexValues );
    }

    public void  setVRMLCoordPointValues(                 float [ ]           splinePatchPoints )
    { ( (MFVec3f)                                         ( (Node) ( (SFNode)     
        getField(                             "spCoord"        ) ).getValue() 
                  ).getExposedField(          "point"          ) ).setValue(  splinePatchPoints );
    }                                                   //  see  VRML  below  
         
         /*    "spline"  <---(  getEventIn(        "set_coordIndex"  ) )--  splineIndexValues    
          *    "spCoord" <---(  getExposedField(   "point"           ) )--  splinePatchPoints        
          */        
         /*                             #
             children [                 #  Note:  spline  Node contains:      spCoord  #____ VRML ____
                Shape {                 # __ DEF  spline  __          __ DEF  spCoord  __
                    appearance  IS ap
                    geometry                 DEF  spline IndexedFaceSet { 
                                                             coord       DEF  spCoord Coordinate { }
                                                                                    solid FALSE  
                                                                                    creaseAngle 1 }} 
             ]  }  #  */    //  VRML from:  http://www.frontiernet.net/~imaging/tall_ship_jsn.wrl   
                            //  THIS file:  http://www.frontiernet.net/~imaging/Bezier.java
    
    public void  setVRMLSplineCoordPoints(  float [ ] splinePts,    int [ ]   splineIndexValues )
    {            
                 setVRMLCoordPointIndexValues(                                splineIndexValues );
                 setVRMLCoordPointValues(             splinePts                                 ); 
    }    
    
 /***************************  
  *    Bezier Spline math   *  
  ***************************/
      
    public float [ ]    calculatePatches( float [ ] p,    float [ ] b0, float [ ] b1, 
                                                       float [ ] b2, float [ ] b3 )
    {                                               
    int         n   = b0.length;   
    float [ ]   pt  = new float[ 3 * n * n ];
                                                    //  println( "calculating Patches" );
    for(      int j,  k = j = 0;  j < n; j++ ) {
      for(    int         i = 0;  i < n; i++ ) {

        pt[ k++ ] = (float)(

            ( p[  0 ] * b0[i] + p[ 3 ] * b1[i] + p[ 6] * b2[i] + p[ 9] * b3[i]) * b0[j] +
            ( p[ 12 ] * b0[i] + p[15 ] * b1[i] + p[18] * b2[i] + p[21] * b3[i]) * b1[j] +
            ( p[ 24 ] * b0[i] + p[27 ] * b1[i] + p[30] * b2[i] + p[33] * b3[i]) * b2[j] +
            ( p[ 36 ] * b0[i] + p[39 ] * b1[i] + p[42] * b2[i] + p[45] * b3[i]) * b3[j] );

        pt[ k++ ] = (float)(

            ( p[ 1] * b0[i] + p[ 4]*b1[i] + p[ 7] * b2[i] + p[10] * b3[i]) * b0[j] +
            ( p[13] * b0[i] + p[16]*b1[i] + p[19] * b2[i] + p[22] * b3[i]) * b1[j] +
            ( p[25] * b0[i] + p[28]*b1[i] + p[31] * b2[i] + p[34] * b3[i]) * b2[j] +
            ( p[37] * b0[i] + p[40]*b1[i] + p[43] * b2[i] + p[46] * b3[i]) * b3[j] );

        pt[ k++ ] = (float)(

            ( p[ 2] * b0[i] + p[ 5]*b1[i] + p[ 8] * b2[i] + p[11] * b3[i]) * b0[j] +
            ( p[14] * b0[i] + p[17]*b1[i] + p[20] * b2[i] + p[23] * b3[i]) * b1[j] +
            ( p[26] * b0[i] + p[29]*b1[i] + p[32] * b2[i] + p[35] * b3[i]) * b2[j] +
            ( p[38] * b0[i] + p[41]*b1[i] + p[44] * b2[i] + p[47] * b3[i]) * b3[j] );
        }                                                                    
    }           
    return( pt );
 }                                      //____      end calculatePatches method     ___
 
 
    float [ ]  calculatePatches(  float [ ] p, float  [][] bezierMatrix )    // polymorphism 
    {
       return( calculatePatches( p, bezierMatrix[0],  bezierMatrix[1],  bezierMatrix[2],  bezierMatrix[3] ) );
    }    
 
 
    
    public float [ ][ ] generateBezierMatrix( int n )
    {
    float          u, u1, u12, u2;                  // original code used all doubles here
    float [ ][ ]   valMatrix = new float[n][n];     // 
    float          st = (float) ( 1.0 / ( n - 1 ) );
            
            for( int i = 0; i < n; i++ ){

                    u   =  i * st;
                    u1  =  1 - u;
                    u12 = u1 * u1;
                    u2  =  u * u;

                    valMatrix[0][i] =     u1 * u12; 
                    valMatrix[1][i] = 3 * u  * u12; 
                    valMatrix[2][i] = 3 * u2 * u1; 
                    valMatrix[3][i] =     u  * u2;                    
                        //    b0[i]                 =     u1 * u12;     // original code
                        //    b1[i]                 = 3 * u  * u12; 
                        //    b2[i]                 = 3 * u2 * u1; 
                        //    b3[i]                 =     u  * u2;
            }
        return( valMatrix );
    }           //  end generateBezierMatrix method        


    public int [ ] generateSplineIndexValues( int n )
    {
        int                  NumTriangles       = 2 * (n-1) * (n-1);
        int                  NumVertexValues    = 6 * (n-1) * (n-1);
        int                  NumVertexEntries   = 8 * (n-1) * (n-1);
        
            // Level of Detail : 4 ; 72
        
        println( "Level of Detail : " + n + " ;  ( " +  NumTriangles        + ", " +
                                                        NumVertexValues     + ", " + 
                                                        NumVertexEntries    + " ) : " +
               "  =  2*(n-1)^2 Triangles, 6*(n-1)^2 Values,  8*(n^2) Entries " );
        
        int [ ] s = new int[ NumVertexEntries  ];

        for(        int j, k = j = 0;  j < n - 1;  j++ ) {
            for(    int        i = 0;  i < n - 1;  i++ ) {

                s[k++] = i + n *  j + 1; 
                s[k++] = i + n *  j; 
                s[k++] = i + n * (j+1); 
                s[k++] = -1;
                s[k++] = i + n * (j+1); 
                s[k++] = i + n * (j+1) + 1; 
                s[k++] = i + n *  j    + 1; 
                s[k++] = -1;
            }
        }   
        return( s );
    }                       //_____  end generateSplineIndexValues method  ___

    
                    public Bezier( )                // Constructor for class 
                    {                                             //  super( );       
                        if( timesConstructorCalled++ < 3 ) {
                                println( " Bezier : __ Instatiating __ " );                    
                        }                        
                    }                               //__ end Bezier Constructor __ 
    
                                public void    println( String  s ) // open your Java Console Window !
                                {   System.err.println(         s );     // showStatus( s );
                                }                  
    
    
                                        
 
  /**************************  Methods:  Note: tried, failed to put in "vrml helper class"
   *                        *       getIntField( fieldName );
   *  VRML Interface Stuff  *       getFloatArrayFromField( fileName, arrayLength );
   *                        *       setVRMLCoordPointValues
   **************************/ //   setVRMLCoordPointIndexValues   
 
    public  int getIntField( String fieldName  )    // used to get lod [ 4..7 ] was: n
    {
          int   val = ( (SFInt32) getField( fieldName   ) ).getValue( );    // 
    //    int   n   = ( (SFInt32) getField( "num"       ) ).getValue( );    // original
        return( val );
    }


    public  float [ ] getFloatArrayFromField(   String fieldName,   int     arrayLen  )
    {       
            float [ ]                           valFloatArray = new float[  arrayLen  ];     
        ( (MFFloat) getField(      fieldName  ) 
                    ).getValue(                 valFloatArray   );   //  arrayLen was: 3 * 16
             
                                if( ( number_of_fields_read++      < 5 ) ||
                                    ( number_of_fields_read % 100 == 0 )    ) {
                                    println( fieldName + ": " +  number_of_fields_read );         
                                }    
        return(                                 valFloatArray   );
    //  ( (MFFloat) getField( "CPoint" ) ).getValue( p );    // p <-- value ( 48 vals )
    }


    
    

 
        public void processEvent ( vrml.Event  e )      // not called presently
        {       
                        String eventName = e.getName( );           
            println( "pe: " +  eventName );
   
        // from robot initialization( ) -- set up event observer             
        // shoulder_rotation1 = (vrml.field.SFRotation ) getEventOut( "shoulder_rotation1_changed" );                
        //    if(			E.equals( "rotation_changed" ) ) { do_rotation( ); 	}       
        //    else if (	E.equals( "isActive"		 ) ) { do_touch( ); 	}    
        } 
        
    

}       //  end Bezier class

/*      
                                                // failed attempts for helper class
class VRMLHelper  extends vrml.node.Node
// class VRMLHelper  extends EventObserver
                    // vrml.BaseNode // extends vrml.node.Node // Script
{
//    public          VRMLHelper( )       { super( ); }   //  constructor

    // public void     initialize( )       { }             //  required ? for Script
    
//    public VRMLHelper( ) { super( ); }; 
    
}    

    float [ ]   calculatePatches(  float [ ] p, double [][] bezierMatrix )    // polymorphism 
    {                                                       // now unused, converts double to float
        int             n           = bezierMatrix.length;  // bezierMatrix should be square.
        float [ ] [ ]   floatMatrix = new float[ n ][ n ];      
     // float [ ] [ ]   floatMatrix = new float[ bezierMatrix.length ][ bezierMatrix[0].length ];
        for(     int i = 0;  i <  n;  i++ ) {
            for( int j = 0;  j <  n;  j++ ) {
                floatMatrix[i][j] = (float) bezierMatrix[i][j];
            }    
        }           
       return( calculatePatches( p, floatMatrix[0],  floatMatrix[1],  floatMatrix[2],  floatMatrix[3] ) );
    }                   //  end calculatePatchesMethod ( Polymorphic / translation )


*/
    
    //_____________  end of file ___________________