//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "obraz.h"
#include "math.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//  Konstruktor podstawowy
TTrojkat :: TTrojkat( TPunkt Ap1, TPunkt Ap2, TPunkt Ap3)
{
 p1 = Ap1;
 p2 = Ap2;
 p3 = Ap3;
 N = iloczyn_wektorowy( TWektor( p2, p1), TWektor( p3, p1));
 N.unormuj();
}
//---------------------------------------------------------------------------
//  Konstruktor kopiujący
TTrojkat :: TTrojkat( const TTrojkat &t)
{
 p1 = t.p1;
 p2 = t.p2;
 p3 = t.p3;
 N = t.N;
}
//---------------------------------------------------------------------------
//  Operator przypisania
TTrojkat & TTrojkat :: operator= (const TTrojkat &t)
{
 if( this != &t)
 {
    p1 = t.p1;
    p2 = t.p2;
    p3 = t.p3;
    N = t.N;
 }
 return *this;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//  Konstruktor domyślny
TPromien :: TPromien( void)
{
 r0 = TPunkt();
 kierunek = TWektor();
}
//---------------------------------------------------------------------------
//  Konstruktor podstawowy. Promien z punktu Ar0, skierowany w Akierunek
TPromien :: TPromien( TPunkt Ar0, TWektor Akierunek)
{
 r0 = Ar0;
 kierunek = Akierunek;
}
//---------------------------------------------------------------------------
//  Inny konstruktor. Promien skierowany od a do b.
TPromien :: TPromien( TPunkt Aa, TPunkt Ab)
{
 r0 = Aa;
 kierunek = TWektor( Ab.x - Aa.x, Ab.y - Aa.y, Ab.z - Aa.z);
 kierunek.unormuj();
}
//---------------------------------------------------------------------------
//  Konstruktor kopiujący
TPromien :: TPromien( const TPromien &p)
{
 r0 = p.r0;
 kierunek = p.kierunek;
}
//---------------------------------------------------------------------------
//  Operator przypisania
TPromien & TPromien :: operator= (const TPromien &p)
{
 if( this != &p)
 {
    r0 = p.r0;
    kierunek = p.kierunek;
 }
 return *this;
}
//---------------------------------------------------------------------------
//  Wyznaczenie punktu przecięcia promienia z płaszczyzną trójkąta.
//  Za chwilę wyznaczymy, czy punkt ten leży wewnątrz trójkąta,
//  tzn, czy promień trafia w trójkąt.
//  Parametry referencyjne są wypełniane, o ile przecięcie istnieje.
bool TPromien :: czy_trafia_w_plaszczyzne( TTrojkat t, TPunkt &p)
{
 double D = -(t.N.x * t.p1.x +
              t.N.y * t.p1.y +
              t.N.z * t.p1.z);      //współczynnik D r-nia płaszczyzny Ax+By+Cz+D=0
 double a = t.N.x * kierunek.x +
            t.N.y * kierunek.y +
            t.N.z * kierunek.z;     //rzut promienia na kierunek normalny do płaszczyzny
 if( a == 0)
    return false;                   //promień równoległy do płaszczyzny trójkąta.
                                    //Poniższe wynika z podstawienia równania
                                    //promienia do równania płaszczyzny
 double b = t.N.x * r0.x +
            t.N.y * r0.y +
            t.N.z * r0.z;
 double c = (-D - b) / a;

 if( c < 0)
    return false;                   //trójkąt leży z niewłaściwiej strony ekranu

 p.x = r0.x + kierunek.x * c;       //współrzędne przecięcia promienia z płaszczyzną
 p.y = r0.y + kierunek.y * c;
 p.z = r0.z + kierunek.z * c;
 return true;
}
//---------------------------------------------------------------------------
//  Szukanie przecięcia promienia z trójkątem zabudowy sceny.
//  Parametry referencyjne są wypełniane, o ile przecięcie istnieje.
//  Najpierw wyznacza się przecięcie promienia z płaszczyzną trójkąta.
//  Potem wyszukuje się płaszczyznę osi układu XYZ, na którą najwygodniej
//  zrzutować trójkąt i punkt cieniowo.
bool TPromien :: czy_trafia_w_trojkat( TTrojkat t, TPunkt &p, TPromien &r)
{
 if( czy_trafia_w_plaszczyzne( t, p))
 {
    double max_kierunek = max( t.N.x, t.N.y, t.N.z);
    double p1x, p1y, p2x, p2y, p3x, p3y, px, py;    //współrzędne po zrzutowaniu
    double pole, pole1, pole2, pole3;   //polowy test trafiania w trójkąt
    if( max_kierunek == t.N.x)      //rzutowanie na płaszczyznę YZ
    {
        p1x = t.p1.y;
        p1y = t.p1.z;
        p2x = t.p2.y;
        p2y = t.p2.z;
        p3x = t.p3.y;
        p3y = t.p3.z;
        px  = p.y;
        py  = p.z;
    }
    else
    if( max_kierunek == t.N.y)      //rzutowanie na płaszczyznę XZ
    {
        p1x = t.p1.x;
        p1y = t.p1.z;
        p2x = t.p2.x;
        p2y = t.p2.z;
        p3x = t.p3.x;
        p3y = t.p3.z;
        px  = p.x;
        py  = p.z;
    }
    else                            //rzutowanie na XY
    {
        p1x = t.p1.x;
        p1y = t.p1.y;
        p2x = t.p2.x;
        p2y = t.p2.y;
        p3x = t.p3.x;
        p3y = t.p3.y;
        px  = p.x;
        py  = p.y;
    }
    pole  = fabs(( p2x-p1x) * (p3y-p1y) - (p2y-p1y) * (p3x-p1x)) / 2.;
    pole1 = fabs(( p2x-px) * (p3y-py) - (p2y-py) * (p3x-px)) / 2.;
    pole2 = fabs(( p1x-px) * (p3y-py) - (p1y-py) * (p3x-px)) / 2.;
    pole3 = fabs(( p2x-px) * (p1y-py) - (p2y-py) * (p1x-px)) / 2.;

    return (pole >= (pole1 + pole2 + pole3));
 }
 return false;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//  Konstruktor merytoryczny. Uruchamia konstruktora klasy bazowej
//  obs - pozycja obserwatora,
//  eszer, ewys - rozmiary pikselowe pola obrazowego,
//  odl_ekr - odległość pola obrazowego [m],
//  szer, wys - rozmiar pola obrazowego [m].
TObraz :: TObraz( TPunkt Aobs, int eszer, int ewys,
            double Aodl_ekr, double szer, double wys) :
            TSkalowanie( 0, 0, eszer, ewys, 0, 0, szer, wys)
{
 obs = Aobs;
 Rxy = sqrt( obs.x * obs.x + obs.y * obs.y);
 R = sqrt( Rxy * Rxy + obs.z * obs.z);
 odl_ekr = Aodl_ekr;
 if( Rxy != 0)
    sf = obs.y / Rxy, cf = obs.x / Rxy;    //współczynniki macierzy obrotu
 else
    sf = 0, cf = 1;

 st = Rxy / R, ct = obs.z / R;
}
//---------------------------------------------------------------------------
// Prywatna f. pomocnicza, konstruująca promien wychodzący
//  od obserwatora i biegnący przez piksel (i, j) płaszczyzny obrazowej.
TPromien TObraz :: daj_promien( int i, int j)
{
 double eh, ev;                 //metryczne współrzędne piksela (i, j) w płaszczyźnie ekranu
 TPunkt e;                      //metryczne współrzędne piksela w przestrzeni 3d

 eh = daj_real_x( i);           //funkcje odziedziczone z klasy bazowej
 ev = daj_real_y( j);

 TPunkt z( -ev, eh, R-odl_ekr); //gdy ekran jest w zenicie, piksel ma takie współrzędne

 e.x = cf * ct * z.x - sf * z.y + cf * st * z.z;
 e.y = sf * ct * z.x + cf * z.y + sf * st * z.z;
 e.z = -st * z.x + ct * z.z;

 return TPromien( obs, e);
}
//---------------------------------------------------------------------------
// Raczej niepotrzebne. Do testowania.
TPunkt TObraz :: daj_punkt( int i, int j)
{
 double eh, ev;                 //metryczne współrzędne piksela (i, j) w płaszczyźnie ekranu
 TPunkt e;                      //metryczne współrzędne piksela w przestrzeni 3d

 eh = daj_real_x( i);           //funkcje odziedziczone z klasy bazowej
 ev = daj_real_y( j);

 TPunkt z( -ev, eh, R-odl_ekr); //gdy ekran jest w zenicie, piksel ma takie współrzędne

 e.x = cf * ct * z.x - sf * z.y + cf * st * z.z;
 e.y = sf * ct * z.x + cf * z.y + sf * st * z.z;
 e.z = -st * z.x + ct * z.z;

 return e;
}
//---------------------------------------------------------------------------
//  Główna funkcja algorytmu
TColor TObraz :: kolor_piksela( int i, int j)
{
 TColor k = clBlack;
 TPromien p = daj_promien( i, j);
 return k;
}
