///////////////////////////////////////////////////////////////////////////////
// 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.
///////////////////////////////////////////////////////////////////////////////

import java.awt.Graphics ;

/**
 * La classe des fenêtres de projection.
 * @author Guillaume Pelletier.
 * @version alpha
 */

class Fenetre3D
 {
 public Pixel m_center   ,
              m_halfsize ;

 public Fenetre3D()
  {
  m_center   = new Pixel() ;
  m_halfsize = new Pixel() ;
  }

 public Fenetre3D(int p_x, int p_y, int p_dx, int p_dy )
  {
  m_center   = new Pixel(p_x,p_y)   ;
  m_halfsize = new Pixel(p_dx,p_dy) ;
  }
 }

/**
 * La classe des boites limites.
 * @author Guillaume Pelletier.
 * @version alpha
 */
class BoiteLimite3D
  {
  public Sommet3DH m_csg, m_cid ;

  public BoiteLimite3D()
   {
   m_csg = new Sommet3DH() ;
   m_cid = new Sommet3DH() ;
   }

  public BoiteLimite3D( Sommet3DH p_csg, Sommet3DH p_cid )
   {
   m_csg = p_csg ;
   m_cid = p_csg ;
   }

  public BoiteLimite3D( float p_x1, float p_y1, float p_z1,
                        float p_x2, float p_y2, float p_z2)
   {
   m_csg = new Sommet3DH(p_x1,p_y1,p_z1,1);
   m_cid = new Sommet3DH(p_x2,p_y2,p_z2,1);
   }

  }

/**
 * La classe des sphères limites.
 * @author Guillaume Pelletier.
 * @version alpha
 */
class SphereLimite3D
  {
  public Sommet3DH m_center ;
  public float     m_rayon  ;

  public SphereLimite3D()
   {
   m_center = new Sommet3DH() ;
   m_rayon = 0 ;
   }

  public SphereLimite3D( Sommet3DH p_s, float p_r)
   {
   m_center = p_s ;
   m_rayon  = p_r ;
   }

  public SphereLimite3D( float p_x1, float p_y1, float p_z1, float p_r)
   {
   m_center = new Sommet3DH(p_x1,p_y1,p_z1,1);
   m_rayon  = p_r ;
   }
  }

/**
 * Classe de base des objets tridimensionnels.
 * @author Guillaume Pelletier.
 * @version alpha
 */
class Objet3D
  {
  public Maillage3DH    m_data   ; // le maillage.
  public BoiteLimite3D  m_boite  ; // la boite limite.
  public Scene3D        m_dans   ; // la scene dans laquelle se trouve l'objet.

  public Objet3D()
   {
   m_data  = new Maillage3DH  () ;
   m_boite = new BoiteLimite3D() ;
   m_dans  = null ;
   }

  public Objet3D(Scene3D p_dans)
   {
   m_data  = new Maillage3DH  () ;
   m_boite = new BoiteLimite3D() ;
   m_dans  = p_dans ;
   }
  }

/**
 * Classe des capteurs visuels tridimensionnels.
 * @author Guillaume Pelletier.
 * @version alpha
 */
class CapteurVisuel3D extends Objet3D
  {
  public float      m_spin        , // rolling initial.
                    m_ouverture   , // l'ouverture du capteur en degré.
                    m_profondeur  ; // la profondeur du capteur.
  public Fenetre3D  m_fenetre     ; // la fenêtre de visualisation du capteur.
  public Vecteur3DH m_direction   ; // le vecteur vision.
  public Matrice44  m_perspective ; // la matrice de transformation perspective.

  public CapteurVisuel3D()
   {
   m_fenetre     = new Fenetre3D () ;
   m_direction   = new Vecteur3DH() ;
   m_perspective = new Matrice44 () ;
   m_spin        = 0 ;
   m_ouverture   = 0 ;
   m_profondeur  = 0 ;
   }

  public CapteurVisuel3D( Scene3D p_dans )
   {
   super(p_dans);
   m_fenetre     = new Fenetre3D () ;
   m_direction   = new Vecteur3DH() ;
   m_perspective = new Matrice44 () ;
   m_spin        = 0 ;
   m_ouverture   = 0 ;
   m_profondeur  = 0 ;
   }

  public void CalculPerspective ()
   {
   // on prend pour le calcul, le vecteur inverse.
   Vecteur3DH l_direction = new Vecteur3DH( m_direction.m_b, m_direction.m_a );

   // Calcul de la distance D du point de visée à l'observateur.
   float l_D = l_direction.norme();

   // theta: angle entre le vecteur direction et sa projection sur le plan XOY.
   // Calcul des sin et cos.
   float l_costheta = l_direction.cos_angle_ver(),
         l_sintheta = l_direction.sin_angle_ver();

 
   // phi : angle entre la projection du vecteur direction sur le plans XOY et
   // l'axe des abscisses.
   // Calcul des sin et cos.
   // Nb - le cas particulier où la visée est verticale rend la projection sur
   //      le plans XOY égale à un point. Dans ce cas, il n'y à pas de calcul
   //      d'angle possible. On suppose donc phi=0;

   float l_cosphi = l_direction.cos_angle_hor() ,
         l_sinphi = l_direction.sin_angle_hor() ;

   // on intialise la matrice perspective
   m_perspective.setIdentity();

   // On initialise la matrice de changement de repère.
   Matrice44 l_mat = new Matrice44();
   l_mat.setIdentity();

   // Etape 1. On effectue la première translation.
   l_mat.setTranslateX(-l_direction.compox());
   l_mat.setTranslateY(-l_direction.compoy());
   l_mat.setTranslateZ(-l_direction.compoz());

   m_perspective.multiplyByMatrix(l_mat);

   // Etape 2. Rotation négative autour de l'axe Z0 de PI/2-phi.
   // nous avons cos PI/2-phi = sin phi
   //            sin PI/2-phi = -cos phi
   // or nous effectuons un changement de repère ce qui implique la matrice
   // inverse d'ou phi devient -phi
   // nous avons cos -phi = cos phi
   //            sin -phi = -sin phi
   //     |sinphi  cosphi 0 0|
   //     |-cosphi sinphi 0 0|
   // B = |  0       0    1 0|
   //     |  0       0    0 1|
   //
   l_mat.setIdentity();
   l_mat.setRotateZ(l_sinphi,
                    l_cosphi);
   m_perspective.multiplyByMatrix(l_mat);

   // Etape 3. Rotation positive autour de l'axe X1 de PI/2+theta
   // nous avons cos PI/2+theta = -sin theta
   //            sin PI/2+theta = cos theta
   // or nous effectuons un changement de repère ce qui implique la matrice
   // inverse d'ou phi devient -theta
   // nous avons cos -theta = cos theta
   //            sin -theta = -sin theta
   //     |  1       0         0       0|
   //     |  0  -sintheta  -costheta   0|
   // B = |  0   costheta  -sintheta   0|
   //     |  0       0         0       1|
   //
   l_mat.setIdentity();
   l_mat.setRotateX(-l_sintheta,
                    -l_costheta );
   m_perspective.multiplyByMatrix(l_mat);

   // Etape 4. Changement de sens d'axe X. repère direct.
   l_mat.setIdentity();
   l_mat.m_xx = -1.0f ;

   m_perspective.multiplyByMatrix(l_mat);

   // Etape 5. Rotation du spin.
   float l_spin    = (float)(Math.PI / 180.0f) * m_spin ,
         l_cosspin = (float)Math.cos( l_spin ),
         l_sinspin = (float)Math.sin( l_spin );

   l_mat.setIdentity() ;
   l_mat.setRotateZ (l_cosspin , -l_sinspin);

   m_perspective.multiplyByMatrix(l_mat);

   // Etape 6. Normalisation et projection perspective à un point de fuite.
   l_mat.setIdentity() ;
   float l_ouverture     = (float)(Math.PI / 180.0f) * m_ouverture    ,
         l_halfsizeprojy = (float)Math.tan(l_ouverture) * l_D         ,
         l_halfsizeprojx = l_halfsizeprojy*( m_fenetre.m_halfsize.m_x/
                                             m_fenetre.m_halfsize.m_y);

   // 6a - Normalisation xy
   l_mat.m_xx = 1.0f/l_halfsizeprojx ;
   l_mat.m_yy = 1.0f/l_halfsizeprojy ;

   // 6b - perspective Z.
   l_mat.m_zw = 1.0f / l_D ;

   m_perspective.multiplyByMatrix(l_mat);

   }

  public void VisualisationFilaireObjet( Graphics p_g, Objet3D p_obj )
   {
   int        l_cardsommet = p_obj.m_data.m_sommets.length ;

   Pixel []  l_pixels  = null ;
   Sommet3DH l_transform  = null ,
             l_sommet     = null ;
   int l_x, l_y ;

   try
    {
    l_transform = new Sommet3DH()        ;
    l_pixels = new Pixel[ l_cardsommet ] ;
    }
   catch (OutOfMemoryError p_oome )
    {
    System.out.println("problème d'allocation de " + l_cardsommet + " pixels.");
    return ;
    }


   // transformation des sommets.
   for ( int l_i = 0 ; l_i < l_cardsommet ; l_i ++ )
     {
     l_sommet = p_obj.m_data.m_sommets[l_i];
     l_transform.m_x = l_sommet.m_x ;
     l_transform.m_y = l_sommet.m_y ;
     l_transform.m_z = l_sommet.m_z ;
     l_transform.m_w = l_sommet.m_w ;

     l_transform.multiplyByMatrix(m_perspective);

     // on initialise les coordonées écran.
     l_x = (int)((l_transform.m_x/l_transform.m_w)* m_fenetre.m_halfsize.m_x) ;
     l_y = (int)((l_transform.m_y/l_transform.m_w)* m_fenetre.m_halfsize.m_y) ;

     // on centre le dessin dans la fenêtre.
     l_x+=m_fenetre.m_center.m_x ;
     l_y+=m_fenetre.m_center.m_y ;

     // on inverse l'axe des y.
     l_y = m_fenetre.m_halfsize.m_y*2 - l_y ;

     l_pixels[l_i] = new Pixel( l_x, l_y );
     }

/*   Pixel l_pixel ;

   // affichage des sommets.
   for ( int l_i = 0 ; l_i < p_obj.m_data.m_sommets.length ; l_i ++ )
     {
     l_pixel = l_pixels[l_i];// la numérotation commence à 1

     p_g.drawRect( l_pixel.m_x-2, l_pixel.m_y-2,4,4);
     }
*/ 
   int   l_cardarc = p_obj.m_data.m_arcs.length ;
   ArcD  l_arc ;
   Pixel l_pixelini, l_pixelfin ;

   // affichage des arcs.
   for ( int l_i = 0 ; l_i < l_cardarc ; l_i ++ )
     {
     l_arc = p_obj.m_data.m_arcs[l_i] ;
     l_pixelini = l_pixels[l_arc.m_sini-1];// la numérotation commence à 1
     l_pixelfin = l_pixels[l_arc.m_sfin-1];// la table à 0. D'où - 1.

     p_g.drawLine( l_pixelini.m_x, l_pixelini.m_y,
                   l_pixelfin.m_x, l_pixelfin.m_y);
     } 

   }

  public void VisualisationFilaireScene( Graphics p_g )
   {
   if ( m_dans == null ) return ;
   m_dans.VisualisationFilaire( p_g, this );
   }

  }

/**
 * Classe des scènes tridimensionnelles.
 * Dans cette première version la scène est une scène basique avec un seul
 * capteur visuel et un seul objet.
 * @author Guillaume Pelletier.
 * @version alpha 1
 */
class Scene3D
  {
  public Objet3D          m_obj ;
  public CapteurVisuel3D  m_obs ;

  public Scene3D()
   {
   m_obj = null ;
   m_obs = null ;
   }

  public Scene3D(Objet3D p_obj, CapteurVisuel3D p_obs)
   {
   m_obj = p_obj ;
   m_obs = p_obs ;
   }

  public void VisualisationFilaire( Graphics p_g, CapteurVisuel3D p_obs )
   {
   p_obs.VisualisationFilaireObjet(p_g, m_obj) ;
   }
  }
 
