
PROGRAM Cube;
USES    CRT, Graph;

CONST   Depth = 1000; { Distanta pana la centrul de proiectie }
        Unu = 5;      { Pentru adjustarea dimensiunilor }

TYPE    TVector  = packed array[1..3] of Real;
        TReper   = packed array[0..3] of TVector;   {O, e1, e2, e3}
        TProiec  = (prParalel, prCentral);

VAR     GD, GM     : integer;  {Pentru initierea modului grafic}
        MidX, MidY : word;     {Mijlocul ecranului}
        R          : TReper;   {Reperul figurii}
{---------------------------------------------------------------}
PROCEDURE Proiec_Central(P: TVector; E: TReper; VAR M: TVector); FORWARD;
PROCEDURE Proiec_Paralel(P: TVector; E: TReper; VAR M: TVector);
    {-----------------------------------------------------------}
    PROCEDURE AddVec2D (VAR r: TVector; v1, v2: TVector);
    BEGIN  { r := v1 + v2 }
       r[1] := v1[1] + v2[1];
       r[2] := v1[2] + v2[2];
    END;
    {-----------------------------------------------------------}
    PROCEDURE MulScal2D(VAR r: TVector; v: TVector; scal: Real);
    BEGIN  { r := v * scal }
       r[1] := v[1] * scal;
       r[2] := v[2] * scal;
    END;
    {-----------------------------------------------------------}
BEGIN
   { M = (O + P1*e1 + P2*e2 + P3*e3) }
   MulScal2D(E[1], E[1], P[1]); {e1 := P1 * e1}
   MulScal2D(E[2], E[2], P[2]); {e2 := P2 * e2}
   MulScal2D(E[3], E[3], P[3]); {e3 := P3 * e3}
   AddVec2D(M, e[0], e[1]);     {M := O + e1}
   AddVec2D(M, M,    e[2]);     {M := M + e2}
   AddVec2D(M, M,    e[3]);     {M := M + e3}
END;
{---------------------------------------------------------------}
PROCEDURE CoordAbsol(P: TVector; E: TReper; VAR M: TVector);
    {-----------------------------------------------------------}
    PROCEDURE AddVec(VAR r: TVector; v1, v2: TVector);
    BEGIN  { r := v1 + v2 }
       r[1] := v1[1] + v2[1];
       r[2] := v1[2] + v2[2];
       r[3] := v1[3] + v2[3];
    END;
    {-----------------------------------------------------------}
    PROCEDURE MulScal(VAR r: TVector; v: TVector; scal: Real);
    BEGIN  { r := v * scal }
       r[1] := v[1] * scal;
       r[2] := v[2] * scal;
       r[3] := v[3] * scal;
    END;
    {-----------------------------------------------------------}
BEGIN { M = (O + P1*e1 + P2*e2 +P3*e3) }
   MulScal(e[1], e[1], P[1]); {e1 := P1 * e1}
   MulScal(e[2], e[2], P[2]); {e2 := P2 * e2}
   MulScal(e[3], e[3], P[3]); {e3 := P3 * e3}
   AddVec(M, e[0], e[1]);     {M := O + e1}
   AddVec(M, M,    e[2]);     {M := M + e2}
   AddVec(M, M,    e[3]);     {M := M + e3}
END;
{---------------------------------------------------------------}
PROCEDURE Proiec(xyz: TVector; VAR xy: TVector);
BEGIN  {3D -> 2D}
  { Atentie! xyz[3] <> -Depth, altfel se obtine eroare }
  IF (xyz[3] <> -Depth) THEN  xy[3] := Depth  / (xyz[3]+Depth)
                        else  xy[3] := 1.7e+38; {infinit}

  xy [1] := xyz[1] * xy[3]; { x := x * (Depth / (z+Depth)) }
  xy [2] := xyz[2] * xy[3]; { y := y * (Depth / (z+Depth)) }
END;
{---------------------------------------------------------------}
PROCEDURE Proiec_Central(P: TVector; E: TReper; VAR M: TVector);
BEGIN
  CoordAbsol(P, E, P);
  Proiec(P, M);
END;
{---------------------------------------------------------------}
function Line2d(p1, p2: TVector): boolean;
BEGIN
   {??? punctele pot iesi in afara limitelor ecranului}
   Line( round(p1[1]) + MidX, -round(p1[2]) + MidY,
         round(p2[1]) + MidX, -round(p2[2]) + MidY );
END;
{---------------------------------------------------------------}
PROCEDURE Cub(Sis: TReper; pr: TProiec);
VAR P : array[0..7] of TVector;
    i : integer;
BEGIN
  {Amplasarea punctelor in spatiu (in reper)}
  FOR i:=0 to 7 DO BEGIN
     IF (i and 1) <> 0 THEN p[i][1] := Unu else p[i][1] := -Unu;
     IF (i and 2) <> 0 THEN p[i][2] := Unu else p[i][2] := -Unu;
     IF (i and 4) <> 0 THEN p[i][3] := Unu else p[i][3] := -Unu;
  END;

  {Calcularea coordonatelor plane}
  FOR i:=0 to 7 DO CASE pr OF
    prCentral: Proiec_Central(P[i], Sis, P[i]); {  I metoda }
    prParalel: Proiec_Paralel(P[i], Sis, P[i]); { II metoda }
  END;

  {Desenarea segmentelor figurii}
  FOR i:=0 to 3 DO BEGIN
    Line2D(P[i],           P[i + 4]);
    Line2D(P[2*i],         P[2*i + 1]);
    Line2D(P[i+i and 2+2], P[i+i and 2]);
  END;
END;
{---------------------------------------------------------------}

BEGIN
   GD := Detect;
   InitGraph(GD, GM, '');
   IF (GraphResult <> 0) THEN
   BEGIN
      Writeln('Erroare initializare modul grafic!');
      readkey;
      Halt(1);
   END;

   {Mijlocul ecranului}
   MidX := GetMaxX div 2;
   MidY := GetMaxY div 2;

   {Pozitionarea si orientarea reperului in spatiu}
   R[0][1] :=  0;   R[0][2] := 0;   R[0][3] := 0; { O(0, 0, 0) }
   R[1][1] := 15;   R[1][2] := 0;   R[1][3] :=20; { e1( 15,  0,  20) }
   R[2][1] :=  0;   R[2][2] :=25;   R[2][3] := 0; { e2(  0, 25,   0) }
   R[3][1] :=-20;   R[3][2] := 0;   R[3][3] :=15; { e3(-20,  0,  15) }

   {Deseneaza cubul in reperul ortogonal R}
   Cub(R, prParalel);  ReadKey; ClearViewPort;
   Cub(R, prCentral);  ReadKey; ClearViewPort;

   {Reorientarea reperului in spatiu}
   R[1][1] :=   9;   R[1][2] := 12;   R[1][3] :=  20; { e1(  9,  12,  20) }
   R[2][1] := -12;   R[2][2] := 20;   R[2][3] :=   9; { e2(-12,  20,   9) }
   R[3][1] :=  20;   R[3][2] :=  9;   R[3][3] := -12; { e3( 20,   9, -12) }

   {Deseneaza cubul in reperul neortogonal R}
   Cub(R, prParalel);  ReadKey; ClearViewPort;
   Cub(R, prCentral);  ReadKey; ClearViewPort;

   CloseGraph;
END.
