unit calcul;
Interface
const nb_max_segments=150;
      nb_max_faces=40;
      x_max = 40;
      y_max = 30;
      mode_graphique=3;
type matrice_44 = array[1..4,1..4] of real;
     matrice_41 = array[1..4] of real;
     matrice_21 = array[1..2] of real;
     transfo_type = (rotationX,rotationY,rotationZ,homothetie,translation,perspect);
     vecteur = array[1..3] of real;
     point = object
               obj : matrice_41;
               oeil : matrice_41;
               ecran : matrice_21;
               invision : boolean;
               where_pt : byte;
               procedure Calc_repere_oeil;
               procedure perspective;
               procedure init_where_pt;
             end;
     segments = object
                 A, B : point;
                 elimine, in_face : boolean;
                 face, couleur : word;
                 procedure decoupe;
                 procedure calculs;
                 procedure trace(color : word);
               end;
     face = object
              cote : array[1..4] of integer;
              visible : boolean;
              vectoriel : vecteur;
              profondeur : real;
              style, type_face : integer;
              procedure produit_vectoriel;
              procedure on_la_voit;
              procedure calcul_profondeur;
              procedure fill_it;
            end;
     tableau_faces = array[1..nb_max_faces] of face;
     tableau_simple = array[1..nb_max_faces] of integer;
     tableau_segments = array[1..nb_max_segments] of segments;
     tableau_trigo = array[1..360] of real;


var theta, phy, nb_face, nb_segments, mode : integer;
    r, tx, ty, tz, k, d, av, ar : real;
    transformation : matrice_44;
    tab_segments : tableau_segments;
    tab_faces : tableau_faces;
    regard : vecteur;
    cosinus, sinus : tableau_trigo;
    trigo_ready : boolean;

procedure center(y : integer; chaine : string);
procedure multiplier(Trans : matrice_44; point : matrice_41; var result : matrice_41);
procedure make_matrice(nom : transfo_type);
procedure segment(x1,y1,z1,x2,y2,z2 : real; color : word);
procedure quadrilatere(x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4 : real; color : word);
procedure parrallelepipede(x1,y1,z1,x2,y2,z2,x3,y3,z3 : real; color : word);
procedure surface(x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4 : real; style, color : word);
procedure triangle(x1,y1,z1,x2,y2,z2,x3,y3,z3 : real; style, color : word);
procedure move;

Implementation
uses graph, crt, svga, files;

procedure prepare_trigo_tools;
const t = Pi/180;
var w : integer;
begin
  for w:=1 to 360 do
    begin
      cosinus[w]:=trunc(cos(w*t)*100000)/100000;
      sinus[w]:=trunc(sin(w*t)*100000)/100000;
    end;
  trigo_ready:=true;
end;

procedure center(y : integer; chaine : string);
var ini : byte;
begin
  ini:=(80-length(chaine)) div 2;
  if ini >=0 then
    begin
      gotoxy(ini,y);
      write(chaine);
    end;
end;

function ChangeX(x : real) : integer;
begin
  ChangeX:=trunc(10*(x+x_max));
end;

function ChangeY(y : real) : integer;
begin
  ChangeY:=trunc(10*(y+y_max));
end;

procedure multiplier(Trans : matrice_44; point : matrice_41; var result : matrice_41);
var w,e : byte;
begin
  for w:=1 to 4 do result[w]:=0;
  for w:=1 to 4 do for e:=1 to 4 do result[w]:=result[w]+point[e]*trans[e,w];
end;



procedure make_matrice(nom : transfo_type);
begin
  case nom of
        rotationX : begin
                      transformation[1,1]:=1;
                      transformation[1,2]:=0;
                      transformation[1,3]:=0;
                      transformation[1,4]:=0;
                      transformation[2,1]:=0;
                      transformation[2,2]:=cosinus[theta];
                      transformation[2,3]:=sinus[theta];
                      transformation[2,4]:=0;
                      transformation[3,1]:=0;
                      transformation[3,2]:=-sinus[theta];
                      transformation[3,3]:=cosinus[theta];
                      transformation[3,4]:=0;
                      transformation[4,1]:=0;
                      transformation[4,2]:=0;
                      transformation[4,3]:=0;
                      transformation[4,4]:=1;
                    end;
        rotationY : begin
                      transformation[1,1]:=cosinus[theta];
                      transformation[1,2]:=0;
                      transformation[1,3]:=sinus[theta];
                      transformation[1,4]:=0;
                      transformation[2,1]:=0;
                      transformation[2,2]:=1;
                      transformation[2,3]:=0;
                      transformation[2,4]:=0;
                      transformation[3,1]:=-sinus[theta];
                      transformation[3,2]:=0;
                      transformation[3,3]:=cosinus[theta];
                      transformation[3,4]:=0;
                      transformation[4,1]:=0;
                      transformation[4,2]:=0;
                      transformation[4,3]:=0;
                      transformation[4,4]:=1;
                    end;
        rotationZ : begin
                      transformation[1,1]:=cosinus[theta];
                      transformation[1,2]:=-sinus[theta];
                      transformation[1,3]:=0;
                      transformation[1,4]:=0;
                      transformation[2,1]:=sinus[theta];
                      transformation[2,2]:=cosinus[theta];
                      transformation[2,3]:=0;
                      transformation[2,4]:=0;
                      transformation[3,1]:=0;
                      transformation[3,2]:=0;
                      transformation[3,3]:=1;
                      transformation[3,4]:=0;
                      transformation[4,1]:=0;
                      transformation[4,2]:=0;
                      transformation[4,3]:=0;
                      transformation[4,4]:=1;
                    end;
        translation : begin
                        transformation[1,1]:=1;
                        transformation[1,2]:=0;
                        transformation[1,3]:=0;
                        transformation[1,4]:=0;
                        transformation[2,1]:=0;
                        transformation[2,2]:=1;
                        transformation[2,3]:=0;
                        transformation[2,4]:=0;
                        transformation[3,1]:=0;
                        transformation[3,2]:=0;
                        transformation[3,3]:=1;
                        transformation[3,4]:=0;
                        transformation[4,1]:=tx;
                        transformation[4,2]:=ty;
                        transformation[4,3]:=tz;
                        transformation[4,4]:=1;
                      end;
        homothetie : begin
                       transformation[1,1]:=k;
                       transformation[1,2]:=0;
                       transformation[1,3]:=0;
                       transformation[1,4]:=0;
                       transformation[2,1]:=0;
                       transformation[2,2]:=k;
                       transformation[2,3]:=0;
                       transformation[2,4]:=0;
                       transformation[3,1]:=0;
                       transformation[3,2]:=0;
                       transformation[3,3]:=k;
                       transformation[3,4]:=0;
                       transformation[4,1]:=0;
                       transformation[4,2]:=0;
                       transformation[4,3]:=0;
                       transformation[4,4]:=1;
                     end;
        perspect : begin
                     transformation[1,1]:=-sinus[theta];
                     transformation[1,2]:=-cosinus[theta]*sinus[phy];
                     transformation[1,3]:=-cosinus[theta]*cosinus[phy];
                     transformation[1,4]:=0;
                     transformation[2,1]:=cosinus[theta];
                     transformation[2,2]:=-sinus[theta]*sinus[phy];
                     transformation[2,3]:=-sinus[theta]*cosinus[phy];
                     transformation[2,4]:=0;
                     transformation[3,1]:=0;
                     transformation[3,2]:=cosinus[phy];
                     transformation[3,3]:=sinus[phy];
                     transformation[3,4]:=0;
                     transformation[4,1]:=0;
                     transformation[4,2]:=0;
                     transformation[4,3]:=r;
                     transformation[4,4]:=1;
                   end;

  end;
end;

{************ FIN Procedures concernant les points **********}
procedure point.perspective;
var w : byte;
begin
  if oeil[3]<>0 then for w:=1 to 2 do ecran[w]:=d*oeil[w]/oeil[3]
    else
      begin
        ecran[1]:=oeil[1];
        ecran[2]:=oeil[2];
      end;
end;

procedure point.Calc_repere_oeil;
begin
  multiplier(transformation,obj,oeil);
end;

procedure point.init_where_pt;
var x,y,z : real;
begin
  where_pt:=0;
  x:=oeil[1];
  y:=oeil[2];
  z:=oeil[3];

  if x<-x_max*z/d then where_pt:=where_pt or 1;
  if x>x_max*z/d then where_pt:=where_pt or 2;
  if y<-y_max*z/d then where_pt:=where_pt or 4;
  if y>y_max*z/d then where_pt:=where_pt or 8;
  if zar then where_pt:=where_pt or 32;
  if where_pt<>0 then invision:=false else invision:=true;
end;
{************ FIN Procedures concernant les points **********}

{************ Procedures concernant les segments **********}
procedure save(var a,b : point; t : real);
var x, y, z : real;
begin
  x:=t*(b.oeil[1]-a.oeil[1])+a.oeil[1];
  y:=t*(b.oeil[2]-a.oeil[2])+a.oeil[2];
  z:=t*(b.oeil[3]-a.oeil[3])+a.oeil[3];

  a.oeil[1]:=x;
  a.oeil[2]:=y;
  a.oeil[3]:=z;
end;

procedure intersection(var a,b : point);
var t : real;
begin
  t:=0;
  if (a.where_pt and 1)=1 then
        begin
          t:=(-x_max/d*a.oeil[3]-a.oeil[1])/(b.oeil[1]-a.oeil[1]+x_max/d*(b.oeil[3]-a.oeil[3]));
          save(a,b,t);
          a.init_where_pt;
        end;
  if (a.where_pt and 2)=2 then
        begin
          t:=(x_max/d*a.oeil[3]-a.oeil[1])/(b.oeil[1]-a.oeil[1]-x_max/d*(b.oeil[3]-a.oeil[3]));
          save(a,b,t);
          a.init_where_pt;
        end;
  if (a.where_pt and 4)=4 then
        begin
          t:=(-y_max/d*a.oeil[3]-a.oeil[2])/(b.oeil[2]-a.oeil[2]+y_max/d*(b.oeil[3]-a.oeil[3]));
          save(a,b,t);
          a.init_where_pt;
        end;
  if (a.where_pt and 8)=8 then
        begin
          t:=(y_max/d*a.oeil[3]-a.oeil[2])/(b.oeil[2]-a.oeil[2]-y_max/d*(b.oeil[3]-a.oeil[3]));
          save(a,b,t);
          a.init_where_pt;
        end;
 if (a.where_pt and 16)=16 then
        begin
          t:=(av-a.oeil[3])/(b.oeil[3]-a.oeil[3]);
          save(a,b,t);
          a.oeil[3]:=av;
          a.init_where_pt;
        end;
  if (a.where_pt and 32)=32 then
        begin
          t:=(ar-a.oeil[3])/(b.oeil[3]-a.oeil[3]);
          save(a,b,t);
          a.oeil[3]:=ar;
          a.init_where_pt;
        end;
end;

procedure segments.decoupe;
begin
  a.init_where_pt;
  b.init_where_pt;

  if (a.where_pt and b.where_pt)<>0
    then elimine:=true
    else
      begin
        elimine:=false;
        if ((a.invision) and (b.invision)) then exit;

        if a.invision and not b.invision
            then intersection(b,a)
          else if b.invision and not a.invision
            then intersection(a,b)
          else
            begin
              intersection(a,b);
              intersection(b,a);
            end;
        end;
end;

procedure segments.calculs;
begin
  segments.decoupe;
  if not elimine then
    begin
      a.perspective;
      b.perspective;
    end;
end;

procedure segments.trace(color : word);
begin
  setcolor(color);
  if not elimine then line(changeX(a.ecran[1]),changeY(a.ecran[2]),changeX(b.ecran[1]),changeY(b.ecran[2]));
end;
{************ FIN Procedures concernant les segments **********}


{************ Procedures concernant les Faces **********}
procedure face.on_la_voit;
var produit_scalaire : real;
    w : integer;
begin
  regard[1]:=tab_segments[cote[1]].a.oeil[1];
  regard[2]:=tab_segments[cote[1]].a.oeil[2];
  regard[3]:=tab_segments[cote[1]].a.oeil[3];

  


produit_scalaire:=regard[1]*vectoriel[1]+regard[2]*vectoriel[2]+regard[3]*vectoriel[3];
  if produit_scalaire<0 then visible:=false else visible:=true;
end;

procedure face.calcul_profondeur;
var w : integer;
begin
  profondeur:=0;
  for w:=1 to type_face do profondeur:=profondeur+sqrt(sqr(tab_segments[cote[w]].a.oeil[1])+
    sqr(tab_segments[cote[w]].a.oeil[2])+sqr(tab_segments[cote[w]].a.oeil[3]));
  profondeur:=profondeur/type_face;
end;

procedure tri_face(var tableau_trie : tableau_simple);
var w : integer;

  procedure quick_sort(var tableau_trie : tableau_simple; deb, fin : integer);
  var i, j, temp : integer;
      milieu : real;
  begin
    i:=deb;
    j:=fin;
    milieu:=tab_faces[tableau_trie[(deb+fin) div 2]].profondeur;
    while i<=j do
      begin
        while tab_faces[tableau_trie[i]].profondeur > milieu do inc(i);
        while tab_faces[tableau_trie[j]].profondeur < milieu do dec(j);
        if i<=j then
          begin
            temp:=tableau_trie[i];
            tableau_trie[i]:=tableau_trie[j];
            tableau_trie[j]:=temp;
            inc(i);
            dec(j);
          end;
        end;
    if i3 then tab_segments[w].trace(0);
            tab_segments[w].calculs;
          end;
  for w:=1 to nb_face do
          begin
              tab_faces[w].produit_vectoriel;
              tab_faces[w].on_la_voit;
              if mode=3 then tab_faces[w].calcul_profondeur;
            end;
  if mode=3 then
    begin
      tri_face(tab_trie_face);
      cleardevice;
    end
   else bar(2,540,110,600);
  info;

  if mode=3 then
              begin
                for w:=1 to nb_face do
                  begin
                    if tab_faces[tab_trie_face[w]].visible then
                      begin
                        couleur:=tab_segments[tab_faces[tab_trie_face[w]].cote[1]].couleur;
                        setcolor(couleur);
                        setfillstyle(tab_faces[w].style,couleur);
                        tab_faces[tab_trie_face[w]].fill_it;
                      end;
                  end;
                for w:=1 to nb_segments do
                  if ((not tab_segments[w].in_face) and (tab_faces[tab_segments[w].face].visible))
                    then tab_segments[w].trace(tab_segments[w].couleur);
                end
             else
                for w:=1 to nb_segments do
                  if tab_segments[w].face=0 then tab_segments[w].trace(tab_segments[w].couleur) else
                    if tab_faces[tab_segments[w].face].visible then tab_segments[w].trace(tab_segments[w].couleur);
end;

procedure anim(is_theta : boolean;delai, increment : integer);
var w : integer;
begin
  repeat
    if is_theta then inc(theta,increment) else inc(phy,increment);
    attention;
    mouvement;
    delay(delai);
  until keypressed;
end;

procedure move;
var touche : char;
    w, temp : integer;
begin
  if not fileexist('svga256.bgi') then extract('svga256.bgi',0,0);
  if not trigo_ready then prepare_trigo_tools;
  initsvga(mode_graphique);
  SettextStyle(DefaultFont,HorizDir,1);
  touche:=chr(13);
  repeat
    if ord(touche) in [13,51,57,49,55,42,47,45,43,80,72,77,75,ord('t')] then mouvement;
    touche:=readkey;
    case ord(touche) of
       75 : if mode=3 then inc(theta,2) else anim(true,14,2);
       77 : if mode=3 then dec(theta,2) else anim(true,14,-2);
       72 : if mode=3 then inc(phy,2) else anim(false,14,2);
       80 : if mode=3 then dec(phy,2) else anim(false,14,-2);
       43 : d:=d+0.5;
       45 : if d>0.5 then d:=d-0.5;
       47 : r:=r+0.5;
       42 : if r>0.5 then r:=r-0.5;
       ord('7') : if av>=0.5 then av:=av+0.5 else av:=av+0.1;
       ord('1') : if av>0.5 then av:=av-0.5 else av:=av-0.1;
       ord('9') : if ar>=0.5 then ar:=ar+0.5 else ar:=ar-0.1;
       ord('3') : if ar>0.5 then ar:=ar-0.5 else ar:=ar-0.1;
       13 : begin
              d:=24;
              r:=54.5;
              theta:=227;
              phy:=187;
              av:=0;
              ar:=100;
            end;
    end;
    attention;
    until ord(touche)=27;
  closegraph;
  nb_segments:=0;
  nb_face:=0;
end;

begin
  nb_segments:=0;
  nb_face:=0;
  trigo_ready:=false;
end.