///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995-1996 Virtual Design All Rights Reserved.
//
// Permission to use,  copy,  modify,  and  distribute  this  software and its
// documentation for NON-COMMERCIAL purposes and without fee is hereby granted.
//
// VIRTUAL DESIGN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
// OF THE SOFTWARE, EITHER EXPRESS  OR  IMPLIED,  INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
// OR NON-INFRINGEMENT. VIRTUAL DESIGN SHALL  NOT  BE LIABLE FOR ANY DAMAGES
// SUFFERED BY  LICENSEE AS A RESULT OF USING,  MODIFYING OR DISTRIBUTING THIS
// SOFTWARE OR ITS DERIVATIVES.
//
// THIS SOFTWARE  IS   NOT  DESIGNED  OR  INTENDED FOR USE OR RESALE AS ONLINE
// OR NOT ONLINE CONTROL  EQUIPMENT IN HAZARDOUS  ENVIRONMENTS REQUIRING  FAIL-
// SAFE PERFORMANCE,  SUCH  AS  IN   THE   OPERATION   OF  NUCLEAR  FACILITIES,
// AIRCRAFT   NAVIGATION  OR  COMMUNICATION  SYSTEMS,  AIR   TRAFFIC   CONTROL,
// DIRECT LIFE  SUPPORT  MACHINES, OR WEAPONS SYSTEMS, IN  WHICH  THE  FAILURE
// OF  THE SOFTWARE COULD  LEAD DIRECTLY  TO DEATH, PERSONAL INJURY, OR SEVERE
// PHYSICAL OR ENVIRONMENTAL  DAMAGE ("HIGH RISK ACTIVITIES").  VIRTUAL DESIGN
// SPECIFICALLY DISCLAIMS  ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH
// RISK ACTIVITIES.
///////////////////////////////////////////////////////////////////////////////

class modrot
 {
 // Les variables de définition.
 public static final int SUCCESS                              = 0 ;
 public static final int MOD_ROT_ERROR_MEMORY                 = 1 ;
 public static final int MOD_ROT_ERROR_MSG                    = 2 ;
 public static final int MOD_ROT_ERROR_BAD_DATA_CARDINAL      = 3 ;
 public static final int MOD_ROT_ERROR_BAD_DATA_OUT_AXIS      = 4 ;
 public static final int MOD_ROT_ERROR_BAD_DATA_ON_AXIS       = 5 ;
 public static final int MOD_ROT_ERROR_BAD_DATA_INTER         = 6 ;
 public static final int MOD_ROT_ERROR_BAD_ROTATE_PAR         = 7 ;
 public static final int MOD_ROT_ERROR_BAD_PROFIL_COMBINATION = 8 ;

 public static final int NEGATIF = -1 ;
 public static final int POSITIF =  1 ;
 public static final int NEUTRAL =  0 ;

 public static final int  ON  = 1 ;
 public static final int  OFF = 0 ;

 public static final int  COMPLETE_ROTATE = 360 ;
 public static final int  HALF_ROTATE     = 180 ;
 public static final int  QUATER_ROTATE   = 90  ;


/**
 * La méthode de calcul du nombre de nouveaux sommets.
 * Version beta 1.0
 * @author Guillaume Pelletier.
 */

 public int ModRotGetCardSommet( int  p_nbp   ,
			                        int  p_pas   ,
			                        int  p_extr  )
  {
  int  l_nb ;

  if ( p_nbp == 0 || p_pas <= 1 ) return 0 ;

  l_nb =  p_pas * p_nbp ;

  // les deux extremités sont sur l'axe.
  // -----------------------------------
  if ( p_extr == GenerateType.EXTR_BOTH ) l_nb -= (2 * (p_pas-1) ) ;

  // une seule éxtremité sur l'axe.
  // ------------------------------
  if ( p_extr == GenerateType.EXTR_SUP ||
       p_extr == GenerateType.EXTR_INF  ) l_nb -= (p_pas-1) ;

  return l_nb ; }


/**
 * La méthode de génération des sommets.
 * Version beta 1.0
 * @author Guillaume Pelletier.
 */
 public int GenerateVertex( Sommet3DH [] p_profil ,// La table des profils 1.
		                      Sommet3DH [] p_profil1,// La table des profils 2.
		                      Maillage3DH  p_data   ,// Les sommets crées.
		                      GenerateType p_type   ,// Le type d'objet crée
                            float        p_angle  ,// Angle de rotation.
		                      int          p_pas    )// Nb profils.

  { int       l_error    ;
    int       l_nbnewsommet ,
	           l_borneprofilhaut ,
	           l_borneprofilbas  ,
	           l_i , l_j ,
	           l_compt = 0 ;
    float     l_angle_iteration   = 0 , l_angle = p_angle ,
	           l_cosangle , l_sinangle ;

    boolean   l_sameprofil = false ;
    float     l_t = 0, l_hom ;


    // le profil initial et le profil final sont ils similaires ?
    // ----------------------------------------------------------
    if ( p_profil == p_profil1 ) l_sameprofil = true ;


    // On verifis la cohérence des données.
    // -------------------------------------
    if ( (l_error = CheckingProfilRotationZ( p_profil  ,
					                              p_type    )) != SUCCESS )
	   return l_error ;

    // si le profil initial et le profil final sont differents?
    // --------------------------------------------------------
    if ( l_sameprofil == false )
       {
       GenerateType l_type = new GenerateType() ;

       if ( (l_error = CheckingProfilRotationZ( p_profil1   ,
					                                 l_type      )) != SUCCESS )
	    return l_error ;

       // le cas où les profils ont une topologie differents.
       // ---------------------------------------------------
       if ( l_type.m_ponaxis != p_type.m_ponaxis )
	       return MOD_ROT_ERROR_BAD_PROFIL_COMBINATION ;
       }

  // on determine les bornes de traitement pour la génération.
  // ---------------------------------------------------------
  l_borneprofilhaut = (p_type.m_ponaxis == GenerateType.EXTR_SUP  ||
                       p_type.m_ponaxis == GenerateType.EXTR_BOTH  ) ?(1):(0);
  l_borneprofilbas  = (p_type.m_ponaxis == GenerateType.EXTR_INF  ||
                       p_type.m_ponaxis == GenerateType.EXTR_BOTH  ) ?
		                (p_profil.length-2) : (p_profil.length-1) ;

  // les profils sont il des domaine?.
  // cela implique que le sommet initial soit le même que le sommet final.
  // ---------------------------------------------------------------------
  if ( p_profil[0].m_x == p_profil[ p_profil.length -1 ].m_x &&
       p_profil[0].m_y == p_profil[ p_profil.length -1 ].m_y &&
       p_profil[0].m_z == p_profil[ p_profil.length -1 ].m_z )
     {
     p_type.m_pvert = GenerateType.VERT_CLOSE ;
     l_borneprofilbas -- ;
     }
  else p_type.m_pvert = GenerateType.VERT_OPEN ;

 if ( l_sameprofil == false )
    {
    if ( p_profil1[0].m_x == p_profil1[ p_profil1.length -1 ].m_x &&
         p_profil1[0].m_y == p_profil1[ p_profil1.length -1 ].m_y &&
         p_profil1[0].m_z == p_profil1[ p_profil1.length -1 ].m_z )
       {
       if ( p_type.m_pvert != GenerateType.VERT_CLOSE )
          return MOD_ROT_ERROR_BAD_PROFIL_COMBINATION ;
       }
     else
       if ( p_type.m_pvert != GenerateType.VERT_OPEN )
	       return MOD_ROT_ERROR_BAD_PROFIL_COMBINATION ;
    }

  // on recale les angles sur 0-360°.
  // --------------------------------
  while ( p_angle >  360 )  p_angle -= 360  ;
  while ( p_angle < -360 )  p_angle += 360  ;

  // On alloue la table  des sommets .
  // ---------------------------------
  l_nbnewsommet =
  ModRotGetCardSommet((p_type.m_pvert == GenerateType.VERT_OPEN)?
                      (p_profil.length):(p_profil.length-1),
				           p_pas          ,
				           p_type.m_ponaxis );

  if ( l_nbnewsommet == 0 ) return MOD_ROT_ERROR_BAD_ROTATE_PAR ;


  try
   {
   p_data.m_sommets = new Sommet3DH [l_nbnewsommet ];
   }
  catch( OutOfMemoryError p_oome )
   {
   System.out.println( "Erreur d'allocation de la table des sommets." );
   return MOD_ROT_ERROR_MEMORY ;
   }

  // calcul de l'angle d'iteration.
  // ------------------------------
  if ( Math.abs(p_angle) != (double)COMPLETE_ROTATE && l_sameprofil == true )
     l_angle_iteration = (float)(( p_angle / (p_pas-1) ) * Math.PI / 180 );
     // Nb - p_pas -1 car le profil initial est déjà compris.
  else
     l_angle_iteration = (float)(( p_angle / p_pas )* Math.PI / 180 )  ;


  // On commence par copier le profil initial.
  // -----------------------------------------
  l_compt = 0 ;
  for ( l_j = l_borneprofilhaut ; l_j <= l_borneprofilbas ; l_j ++ )
      {
      p_data.m_sommets[l_compt] = new Sommet3DH( p_profil[l_j] );
      l_compt ++ ;
      }

  // On fait une boucle sur les profils restant.
  // -------------------------------------------
  l_hom = 1.0f/(p_pas-1) ;

  Matrice44 l_mat = new Matrice44() ;


  for ( l_i = 1 ; l_i < p_pas ; l_i ++ )
      {
      l_t += l_hom ;

      l_cosangle = (float)Math.cos(l_angle_iteration*l_i);
      l_sinangle = (float)Math.sin(l_angle_iteration*l_i);
      l_mat.setIdentity() ;
      l_mat.setRotateZ (l_cosangle, l_sinangle ) ;

      for ( l_j = l_borneprofilhaut ; l_j <= l_borneprofilbas ; l_j ++ )
	       {
          try
            {
            p_data.m_sommets[l_compt]= new Sommet3DH(p_profil[l_j]);
            }
          catch ( OutOfMemoryError p_oome )
            {
            System.out.println ( "Allocation de sommets avortée" );
            }

          // le passage de profil.
          // ---------------------
          /*
	       p_data.m_sommets[l_compt].m_x = p_profil[l_j].m_x +
		    (p_profil1[l_j].m_x - p_profil[l_j].m_x) * l_t ;

	       p_data.m_sommets[l_compt].m_y = p_profil[l_j].m_y +
		    (p_profil1[l_j].m_y - p_profil[l_j].m_y) * l_t ;

	       p_data.m_sommets[l_compt].m_z = p_profil[l_j].m_z +
		    (p_profil1[l_j].m_z - p_profil[l_j].m_z) * l_t ;
          */


          // la rotation.
          // ------------
          p_data.m_sommets[l_compt].multiplyByMatrix(l_mat) ;

	       l_compt ++  ;
          }
      }

  // on rajoute si necessaire, les points sur l'axe.
  // -----------------------------------------------
  if ( p_type.m_ponaxis == GenerateType.EXTR_SUP  ||
       p_type.m_ponaxis == GenerateType.EXTR_BOTH )
     {
     p_data.m_sommets[l_compt]= new Sommet3DH(p_profil[0]);
     l_compt ++ ;
     }

  if ( p_type.m_ponaxis == GenerateType.EXTR_INF  ||
       p_type.m_ponaxis == GenerateType.EXTR_BOTH )
     {
     p_data.m_sommets[l_compt]=
                        new Sommet3DH(p_profil[p_profil.length-1]);
     }

 // Mise à jour des renseignements sur la forme générée.
 // ----------------------------------------------------
 p_type.m_phor    = (p_angle == COMPLETE_ROTATE && l_sameprofil == true ) ?
					      GenerateType.HOR_CLOSE : GenerateType.HOR_OPEN ;
 p_type.m_type    = (p_type.m_phor == GenerateType.HOR_CLOSE &&
		              (p_type.m_ponaxis == GenerateType.EXTR_BOTH ||
		               p_type.m_pvert == GenerateType.VERT_CLOSE) )?
                     GenerateType.VOLUME : GenerateType.SURFACE ;

  return SUCCESS ; }


/**
 * La méthode utilitaire de contrôle des données.
 * Version beta 1.0
 * @author Guillaume Pelletier.
 */
boolean IsOnAxe ( float a , float b )
{ return ( Math.abs(a) <= Float.MIN_VALUE &&
	        Math.abs(b) <= Float.MIN_VALUE  )? true : false ; }

/**
 * La méthode de contrôle des données.
 * Version beta 1.0
 * @author Guillaume Pelletier.
 */

int CheckingProfilRotationZ( Sommet3D [] p_profil   ,// La table des profils.
			                    GenerateType p_type   )// Sommets sur l'axe?

 {

 int       l_flag1 = GenerateType.EXTR_NONE , // flag premier sommet sur l'axe.
		     l_flag2 = GenerateType.EXTR_NONE , // flag dernier sommet sur l'axe.
		     l_flag  = OFF       ;  // Il y a t'il un sommet hors axe.
 int       l_i,l_r1,l_r2 ;        // compteur boucle.
 float     l_t1, l_t2 ,      // Parametres de segment.
		     l_temp1    ,      // Var intermediaires.
		     l_temp2    ;      // "     "      "

 //  On verifie la cohérence des données.
 //  Le profil est il suceptible de generer quelque chose de correct autour
 //  de l'axe Z ?
 //  Pour ce faire, il doit :
 //    - Avoir un nombre de sommet different de zero.
 //    - Avoir au moins un sommet non confondus avec l'axe.
 //    - Tous ses sommets doivent se trouver dans le même demis plans
 //      limité par l'axe Z. Cela se resume par le fait qu'aucun
 //      segment ne doit couper directement l'axe zz'. Soit le segment
 //      projeté sur le plans xy ne doit pas renfermer le point m(0,0).
 //      On rappelle la définition paramétrique d'un segment :
 //      P(t) = P1 + (P2-P1)t ;
 //      d'ou avec l'axe corespondant on as :
 //      0 = P1 + (P2-P1)t  soit
 //      t = -P1 / (P2-P1) ;
 //      Si t appartient à [0,1], alors il y a intersection.
 //      Dans le cas qui nous interesse, appartenance du point m(0,0)
 //      a un segment ab, on obtient le système suivant:
 //          0 = Xa + ( Xb - Xa ) * T1
 //          0 = Ya + ( Yb - Ya ) * T2
 //      m appartient à ab si T1 = T2 et   T1 appartient à [0,1].
 //      nb : reste les cas particulier ou Xb-Xa = 0 ou Yb-Ya = 0.

 p_type.m_ponaxis = GenerateType.EXTR_NONE ;

 if ( p_profil.length == 0 ) return MOD_ROT_ERROR_BAD_DATA_CARDINAL ;

 // On regarde si le profil est fermé sur l'axe Z.
 // nb - IsOnAxe(...) est une fonction definie plus haut.
 // on verifie qu'il y a au moins un sommet hors axe.
 // ----------------------------------------------------

 // Le point initial.
 if ( IsOnAxe( p_profil[0].m_x , p_profil[0].m_y ))
       l_flag1 = GenerateType.EXTR_SUP ;
 else  l_flag = ON ;

 // les point intermediaires.
 for ( l_i = 1 ; l_i < p_profil.length-1 ; l_i ++ )
     if ( !IsOnAxe( p_profil[l_i].m_x , p_profil[l_i].m_y ))
	     { l_flag = ON ; break ; }

 // Le point final.
 if ( IsOnAxe( p_profil[p_profil.length-1].m_x,
               p_profil[p_profil.length-1].m_y))
       l_flag2 = GenerateType.EXTR_INF ;
 else  l_flag = ON ;

 if ( l_flag == OFF ) return MOD_ROT_ERROR_BAD_DATA_OUT_AXIS ;

 // On verifie tous les arcs.
 // -------------------------
 for ( l_i = 0 ; l_i < p_profil.length-1 ; l_i ++ )
     {
     l_temp1 = p_profil[l_i+1].m_x - p_profil[l_i].m_x ;
     l_temp2 = p_profil[l_i+1].m_y - p_profil[l_i].m_y ;
     l_t1    = - p_profil[l_i].m_x ;
     l_t2    = - p_profil[l_i].m_y ;

     // On elimine le cas trivial de segment confondu avec l'axe de rotation
     // et du même coup, celui des segments parallele à l'axe.
     // --------------------------------------------------------------------
     if ( l_temp1 == 0 && l_temp2 == 0)
	     if ( l_t1 == 0 && l_t2 == 0 )
           {
           return MOD_ROT_ERROR_BAD_DATA_ON_AXIS ;
           }
	     else  continue;


     //  On elimine le cas trivial de segment parallele avec l'axe yy'
     //  Nb- __Test_0 et __Test_1 sont des fonctions qui eliminent les
     //  cas où l'intersection des premier et dernier segment provient
     //  de la fermeture du profil sur l'axe.
     // --------------------------------------------------------------

     if ( l_temp1 == 0.0f )   // parallèle à l'axe des ordonnées
	    if ( l_t1 == 0.0f )    // et le point initial sur l'axe des abscisses.
	     {
	     l_t2 /= l_temp2 ;
        l_r1 = (l_i==0)? ((l_t2>0)?1:0) : ((l_t2>=0)?1:0) ;    //__Test_0
        l_r2 = (l_i==(p_profil.length-2 ))? ((l_t2<1)?1:0):
                                          ((l_t2<=1)?1:0); //__Test_1
	     if ( l_r1 == 1 && l_r2 == 1 )
           {
           return MOD_ROT_ERROR_BAD_DATA_INTER ;
           }
	     continue;
	     }
	    else  continue;

     // On elimine le cas trivial des segments paralleles avec l'axe xx'
     // ----------------------------------------------------------------
     if ( l_temp2 == 0.0f )
	    if ( l_t2 == 0.0f )
	      {
	      l_t1 /= l_temp1 ;
         l_r1 = (l_i==0)? ((l_t1>0)?1:0) : ((l_t1>=0)?1:0) ; //__Test_0
         l_r2 = (l_i==p_profil.length-2)?  ((l_t1<1)?1:0):
                                           ((l_t1<=1)?1:0) ; //__Test_1
	      if ( l_r1 == 1 && l_r2 == 1 )
           {
           return MOD_ROT_ERROR_BAD_DATA_INTER ;
           }
	      continue ;
	      }
	    else  continue ;

     // On cherche l'intersection.
     // ---------------------------
     l_t1 /= l_temp1;
     l_t2 /= l_temp2;

     l_r1 = (l_i==0)? ((l_t1>0)?1:0) : ((l_t1>=0)?1:0) ;              //__Test_0
     l_r2 = (l_i==p_profil.length-2)? ((l_t1<1)?1:0):((l_t1<=1)?1:0); //__Test_1
     if ( ( l_t1 == l_t2  )  &&  l_r1 == 1 && l_r2 == 1  )
        {
        return MOD_ROT_ERROR_BAD_DATA_INTER ;
        }
     }

  // A ce niveau tout est OK. On peut generer les sommets.
  // -----------------------------------------------------
  p_type.m_ponaxis = l_flag1+l_flag2 ;

  return SUCCESS ; }


 }



