Analitička geometrija i C# programiranje dio 6/n


Problem 6: Ugao između prave i ravni.

Pretpostavimo da imamo pravu p na kojoj poznajemo koordinate tačaka A i B. Također pretpostavimo da imamo ravan Π koja je definisana tačkom na ravni R i vektorom normale V. Potrebno je izračunati ugao alpha  kojeg obrazuje prava i ravan.

angprogr_slproblem6

Rješenje: Rješenje zadatka svodi se na izračunavanje ugla između vekktora prave p i njene projektcije na ravan Π p'. Kako već imamo vektor prave p, još je samo potrebno da se odredi vektor prave p’ koja predstavlja projekciju prave na ravan. S druge strane znamo da je vektor normale okomut na vektor smijera projektovane prave, pa je traženi ugao komplement ugla koji zatvara parava i vektor normale. Na osnovu ovog imamo:

cos (90- \alpha) = \frac {|p \cdot V|}{\left | p \right |\left | V \right |}

Implementacija u C# izgleda kao na sljedećem listingu:

static void Main(string[] args)
{
    //Zadatak 1: data je ravan 2x-y+z-6=0 i prava (x)/-1 = (y-1)/-1 ) (z-6)/1
    //ulazni podaci za zadatak 1.
    Point B  = new Point(2f,  4f,  0f);      //tacka na pravoj
    Point A  = new Point(0f,  1f, -1f);   //tačka na pravoj
    Point R  = new Point(0f,  0f,  6f);     //tacka na ravni
    Point Vn = new Point(2f, -1f,  1f);   //vektor normalne ravni

    //izracunavanje ugla
    //Vektor pravca kojeg obrazuju tacke A i B
    Point zraka = Point.Vector(A,B);
    //
    var vec = Point.CrossProduct(zraka, Vn);
    float scos_alpha = (float)(Point.Modul(vec) / (Point.Modul(Vn) * Point.Modul(zraka)));
    Console.WriteLine("Cosinus ugla alpha iznosi={0}", scos_alpha);
    Console.Read();
}

Klasa Point je implementirana u prethodnom postu.

Advertisements

Analitička geometrija i C# programiranje dio 5/n


Problem 6: Presjek prave i ravni.

Pretpostavimo da imamo pravu p na kojoj poznajemo koordinate tačaka A i B. Također pretpostavimo da imamo ravan Π koja je definisana tačkom na ravni R i vektorom normale V. Potrebno je izračunati koordinate tačke A’ odnosno probodišta.

angprogr_slproblem5

Rješenje:

Za rješenje ovog problem potrebno je da znamo parametarski oblik jednačine prave, odnosno jednačinu ravi. Pored toga potrebno je da znamo vektorski odnosno skalarni proizvod dva vektora.

Ukoliko imamo dvije tačke A i B, jednačina prave kroz dvije tačke data je kao:

\frac{x-X_A}{X_B-X_A}=\frac{y-Y_A}{Y_B-Y_A}=\frac{z-Z_A}{Z_B-Z_A},

gdje je (X_A,Y_A,Z_A) koordinate tačke A, a (X_B,Y_B,Z_B) koordinate tačke B.

Opći oblik jednačine ravni koja je definisana jednom tačkom i vektorom normale data je u obliku:

A(x-X_R)+B(y-Y_R)+C(z-Z_R)=0 ,

gdje je (A,B,C) predstavlja koordinate vektora normale, a R(X_R,Y_R, Z_R) koordinate tačke R koja pripada toj ravni.

Postupak rješavanje probojne tačke je slijedeći:

 

1. Formirajmo parametarski oblike jednačine prave p, gdje t predstavlja parametar.

p(t)=A+t(B-A), ………(1)

2. Uvrštavajući gornju jednačinu prave u jednačinu ravni imamo:

V x (A'-V)=0. ………(2).

Zamjenom 1 u 2 možemo izračunati parametar t:

t= V x (V-A)/(N x (B-A)) .

Kada imamo vrijednost parametra t, sada je moguće izračunati koordinate tražene tačke A’.

C#  implementacija problema 6 izgleda na slijedeći način.

Implementacije klase Point koja implementira tačku u 3D prostoru preko koordinata x,z,y, te skupa metoda za skalarni, vektorski proizvod, kao i modul vektora.

class Point
{
    public float x;
    public float y;
    public float z;
    public Point() { }
    public Point(float xx, float yy, float zz)
    {
        x = xx;
        y = yy;
        z = zz;
    }

    /// <summary>
    /// Izracunavanje vektorskog proizvoda dva vektora
    /// </summary>
    /// <param name="vectorA"></param>
    /// <param name="vectorB"></param>
    /// <returns>vektorski proizvod</returns>
    public static Point CrossProduct(Point vectorA, Point vectorB)
    {
//                | i  j  k  |
//                | x1 y1 z1 |=i*(y1*z2-z1*y2)-j*(x1*z2-z1*x2)+k*(x1*y2-y1*x2)
//                | x2 y2 z2 |

        return new Point()
        {
            x = vectorA.y * vectorB.z - vectorA.z * vectorB.y,
            y = -vectorA.x * vectorB.z + vectorA.z * vectorB.x,
            z = vectorA.x * vectorB.y - vectorA.y * vectorB.x
        };
    }

    /// <summary>
    /// Izracunavanje skalarnog proizvoda dva vektora
    /// </summary>
    /// <param name="vectorA"></param>
    /// <param name="vectorB"></param>
    /// <returns></returns>
    public static float ScalarProduct(Point vectorA, Point vectorB)
    {
//      proizvod=x1*x2+y1*y2+z1*z2
        float retVal = vectorA.x * vectorB.x + vectorA.y * vectorB.y + vectorA.z * vectorB.z;
        return retVal;
    }
    /// <summary>
    /// Odredjivanje vektora kroz dvije tacke
    /// </summary>
    /// <param name="A"></param>
    /// <param name="B"></param>
    /// <returns></returns>
    public static Point Vector(Point A, Point B)
    {
        //referentni vektor ili pravac kroz referentne tačke na pravoj
        Point vektor = new Point();
        vektor.x = B.x - A.x;
        vektor.y = B.y - A.y;
        vektor.z = B.z - A.z;

        return vektor;
    }

    /// <summary>
    /// Odredjivanje modula vektora
    /// </summary>
    /// <param name="A"></param>
    /// <returns></returns>
    public static float Modul(Point A)
    {
        return (float)Math.Sqrt(A.x*A.x+A.y*A.y);
    }
}

Kada imamo ovako implementiranu klasu onda je određivanje probojne tačke prave i ravni dato kao:

static void Main(string[] args)
{
    //Zadatak 1: data je ravan 2x-y+z-6=0 i prava (x)/-1 = (y-1)/-1 ) (z-6)/1
    //ulazni podaci za zadatak 1.
    Point B  = new Point(2f,  4f,  0f);      //tacka na pravoj
    Point A  = new Point(0f,  1f, -1f);   //tačka na pravoj
    Point R  = new Point(0f,  0f,  6f);     //tacka na ravni
    Point Vn = new Point(2f, -1f,  1f);   //vektor normalne ravni

    //Zadatak 2: data je ravan 2x-y+z-6=0 i prava (x-1)/-1 = (y+1)/-1 ) = (z-4)/1
    //ulazni podaci za zadatak 2.
    //Point B = new Point(0f, -2f, 5f);      //tacka na pravoj
    //Point A = new Point(1f, -1f, 4f);   //tačka na pravoj
    //Point R = new Point(0f, 0f, 6f);     //tacka na ravni
    //Point Vn = new Point(2f, -1f, 1f);   //vektor normalne ravni
           
    //Probojna tačka koju treba odrediti
    Point proboj = new Point();
          
    //Vektor pravca kojeg obrazuju tacke A i B
    Point zraka = Point.Vector(A,B);

    //ako je skalarni proizvod vektora prave i normale jednak nuli
    // tada prava ne sijece ravan
    float scalVal = Point.ScalarProduct(Vn,zraka);
    if(scalVal==0)
    {
        Console.WriteLine("Prava ne sijece ravan");
        Console.Read();
        return;
    }

    //skalarni proizvod tačke na ravni sa
    float D = Point.ScalarProduct(Vn,R);
    float AR = Point.ScalarProduct(A,Vn);
    float zrakaR = Point.ScalarProduct(zraka,Vn);
    //
    float t = (D - AR) / (zrakaR);

    //tacka probodistva
    proboj.x = A.x + zraka.x * t;
    proboj.y = A.y + zraka.y * t;
    proboj.z = A.z + zraka.z * t;

    Console.WriteLine("Tacka probodista je A'({0},{1},{2})",proboj.x,proboj.y,proboj.z);
    Console.Read();
}

Analitička geometrija i C# programiranje dio 4/n


Problem 5: Potrebno je pronaći najkraću udaljenost između tačke i prave.

shortestpointLine

Iz analitičke geometrije znamo da je najkraća udaljenost između prave i tačke ona udaljenost koja se poklapa sa pravom koja prolazi tačkom A i normalna je na pravu p.  U tom smislu pretpostavimo da normala povučena iz tačke A siječe pravu p u tački A’ . Posmatrajmo vektore \vec{P_{1}A} i \vec{P_{1}P_{2}'} .

Izračunavanjem vektorskog proizvoda ova dva vektora dobićemo površinu paralelograma razapetog na tim vektorima. Kako znamo da je površina paralelograma u našem slučaju: P=AA’ x P1P2, to lahko možemo izračunati dužinu stranice AA’. U stvari ona predstavlja visinu paralelograma.

Sada možemo pisati:

AA' = \frac{P1A x P1P2}{|P1A|}. Ovim smo izračunali najkraću udaljenost između tačke i prave. Implementacija u C# programskom jeziku moze izgledati na sljedeći način:

static float shortestDistance(Point A, Point P1, Point P2)
{
    //referentni vektor ili pravac kroz referentne tačke na pravoj
    Point vektor = new Point();
    vektor.x = P2.x - P1.x;
    vektor.y = P2.y - P1.y;

    //Pravimo vektor od referentne tacke 1 i date tacke a
    Point vektor2 = new Point();
    vektor2.x = P1.x - A.x;
    vektor2.y = P1.y - A.y;

    //Izračunavamo vektorski proizvod dva verktora
    float vecProd = vektor.y * vektor2.x - vektor.x * vektor2.y;
    //Izračunavamo modul vektora P1P2
    float mod1=(float)Math.Sqrt(vektor.x * vektor.x + vektor.y * vektor.y);

    //udaljenost d== vektorski proizvod/dizina vektora P1P2
    float dist = vecProd / mod1;

    return dist;
}

Analitička geometrija i C# programiranje dio 3/n


Problem 3 i 4: Pripadnost tačke kružnici ili pravilnom poligonu

 

tacka_u_kruznici tacka_u_poligonu

 

Implementacija problema pripadnosti tačke u kružnici je vrlo prosta i zahtjeva samo provjeru da li je dužina centra kružnice do posmatrane tačke manja ili jednaka radijusu kružnice. Implementacija se može napisati na sljedeci način:

/// <summary>
/// provjerava da i se tačka nalazi u kružnici
/// </summary>
/// <param name="a">koordinate tačke </param>
/// <param name="o1">koordinate centra kružnice</param>
/// <param name="radius">radijus kružnica</param>
/// <returns></returns>
bool isInCircle(Point a, Point o1, float radius)
{
    float length = (float)Math.Sqrt((a.x - o1.x) * (a.x - o1.x) + (a.y - o1.y) * (a.y - o1.y));

    if (radius >= length)
        return true;
    else
        return false;
}

Implementacija problema tačke u pravilnom poligonu je malo komplikovanija, a bazira se na činjenici da u koliko je tačka unutar poligona, tada tačka mora biti sa desne strane svih njegovih stranica, u koliko se stranica obilaze u smjeru kretanja kazaljke na satu. Implementacija izgleda kao na sljedećem listingu:

/// <summary>
/// vraća true ako se tačka A nalazi u poligonu
/// </summary>
/// <param name="a"></param>
/// <param name="o1">centar poligona</param>
/// <param name="polyedges">tačke tjemena poligona poredane u smjeru kretanja kazaljke na satu.</param>
/// <returns></returns>
bool isInPolygon(Point a, Point o1, Point[]polyedges)
{
    for (int i = 0; i < polyedges.Length; i++)
    {
        int j = i++;
        if (j == polyedges.Length)
            j = 0;
        if (isOnRightSide(a, polyedges[i], polyedges[j])==-1)
            return false;
    }
    return true;
}

Iz zadnjeg listinga vidimo da smo koristili metodu isOnRightSide iz prethodnog posta.

Analitička geometrija i C# programiranje dio 2/n


U prethodnom dijelu smo dali osnovne teorijske osnove za implementaciju problema analitičke geometrije.

Problem 2: Pripadnost tačke kružnom segmentu?

tacka_u_kruznom_segmentu
Posmatrajmo gornju sliku na kojoj imamo kružni isječak, iz kojeg su povučene zrake z1 i z2. Pretpostavimo također da imamo tačku A koja se nalazi u kružnom isječku, dok tačka B se nalazi izvan isječka. Da bi odredili pripadnost tačke kružnom isječku potrebno je provjeriti sljedeće:

1. da se tačka nalazi s desne strane zrake z1

2. da se tačka nalazi sa lijeve strane zrake z2

3. da je dužina iz centra isječka do tačke A manja ili jednaka od radijusa kružnog isječka.

U koliko su sva tri uslova ispunjena, tada se tačka nalazi u kružnom isječku.

Kako smo već u prethodnom postu implementirali metodu za određivanje strane tačke, potrebno je još samo implementirati 3 slučaj.

/// <summary>
///
/// </summary>
/// <param name="a">posmatrana tačka</param>
/// <param name="o1">centar kruđnog isječka</param>
/// <param name="radius">radijus kružnog isječka</param>
/// <returns>tru ako je duzina manja od radijusa</returns>
bool isLessThanRadius(Point a, Point o1, float radius)
{
    float length= (float)Math.Sqrt((a.x-o1.x)*(a.x-o1.x) + (a.y-o1.y)*(a.y-o1.y));

    if (radius >= length)
        return true;
    else
        return false;
}

Na kraju je potrebno implementirati metodu koja će objediniti sva tri slučaja u vratiti true ako sva tri slučaja vrate true, u protivnom će vratiti false.

Ostavlja se čitaocu da sublimira prethodnu i ovu implementaciju shodno zaključcima.

Analitička geometrija i C# programiranje dio 1/n


Analitička geometrija

Iz matematike znamo da analitička geometrija predstavlja algebarski način opisivanja Euklidove geometrije. U analitičkoj geometriji pojmovi Euklidove geometrije se definišu na algebarski način. Prije konkretnih implementacija određenih problema analitičke geometrije, ponovimo na brzinu osnovne principe na kojim se temelji analitička geometrija.

Tačka

Osnovu analitičke geometrije čini koordinatni sistemi, koji mogu biti pravougli, polarni, sferni koordinatni sastem. Svakoj tački u euklidovom prostoru može se pridružiti jedna i samo jedna tačka koordinatnog sistema.

Npr. tačku A u analitičkoj geometriji možemo predstaviti kao:

A(x,y,z),

gdje x,y,z predstavljaju realne brojeve, koje zovemo još i koordinate.

Tačku A možemo predstaviti i preko polarnog (cilindričnog ) koordinatnog sistema kao:

A(r,\rho,z),

gdje su r,\rho,z – koordinate tačke.

Prava

Prava u Euklidovoj geometriji predstavlja osnovni pojam, a u analitičkoj geometriji se definiše preko jednačine prave. Najčešći oblik jednačine prave u analitičkoj geometriji prikazujemo na sljedeći način:

y=ax+b.

Jednačin prave u prostoru definišemo kao:

\frac{(x-x_0)} {a}=\frac{(y-y_0)} {b}=\frac{(z-z_0)} {c}..

Vektor

U analitičkoj geometriji vektor se definiše kao usmjerena duž. Npr.u koliko imamo dvije tačke A(x_A,y_A) i B(x_B,y_B), vektor \vec{AB} predstavlja usmjerenu duž čiji je početak u tački A a kraj u tački B.

\vec{AB} nije isto što i \vec{BA}.

U analitičkoj geometriji vektor se definiše kao:

\vec{AB}= (x_B-x_A, y_B-y_A)

Operacije nad vektorima

Kao što je poznato operacije nad vektorima se razliku od operacija nad brojevima odnosno skalarima. U koliko želimo da saberemo dva vektora \vec{a} i \vec{b} to možemo uraditi preko pravila:
1.Paralelograma i
2. Trougla.

Skalarni proizvod dva vektora

Skalarni proizvod dva vektora predstavlja proizvod dužine prvog i drugog vektora pomnožen sa kosinusom ugla koje vektori međusobno zatvaraj ili:

\vec{a} \dot \vec{b}= |a|\dot |b| cos (\alpha)

Vektorski proizvod dva vektora

Predstavlja jednu od najznačajniji osobina u analitičkoj geometriji, kao i kompjuterskoj grafici.
Vektorski proizvod predstavlja novi vektor:

  1. čija dužina predstavlja proizvod dužina vektora pomnožen sa sinusom ugla kojeg zatvaraju,
  2. pravac novog vektora okomit je na ravan kojeg čine vektori,
  3. smijer novog vektora se određuje pravilom desne ruke ili desnog koordinatnog sistema.
220px-Right_hand_rule_cross_product.svg

Određivanje smijera vektorskog proizvoda

U koliko imamo vektore \vec{a} i \vec{b} koji su definisani kao: \vec{a}(x_1,y_1,z_1) i \vec{b}(x_2,y_2,z_2), vektorski proizvod definišemo kao:

\vec{c}=\vec{a}\bigotimes \vec{b}=\begin{bmatrix} i & j & k\\ x_1 & y_1 & z_1\\ x_2 & y_2 & z_2 \end{bmatrix}

Primjeri

Problem 1: Položaj tačke u odnosu na pravu p

Pretpostavimo da imamo pravu p i tačku A koja nije kolinearna s pravom p. Potrebno je implementirati algoritam koji će dati odgovor na pitanje s koje strane se ona nalazi. Da bi riješili ovaj problem potrebno je iskoristiti osobinu vektorskog proizvoda dva vektora. Potrebno je napomenuti da je smijer gledanja duž prave važan u definisanju stane. U protivnom ne bi smo znali koja je lijeva, a koja desna strana.

Pretpostavimo da imamo situaciju kao na donjoj slici. Također primjetimo da smo označili smijer gledanaj duž prave p. Uočimo dvije tačke na pravoj p, P1 i P2. Da bi odrediti s koje strane se nalazi tačka A, prvo trebamo formirati vektor \vec(P_1P_2), a zatim vektor \vec(P_1A). Na kraju je potrebno izračunati njihov vektorski proizvod. Primjetimo da nam je P_1  početna tačka za oba vektora shodno pretpostavljenom smijeru gledanja duž prave.

tacka_s_sastrane_prave

 

U koliko sada primjenimo pravilo određivanja smijera vektorskog proizvoda (vidi gore sliku) možemo riješiti problem.  U koliko poznajemo koordinate tačke A, odnosno tačaka P1 i P2 te izračunamo vrijednost vektorskog proizvoda.

Pozitivan rezultat vektorskog proizvoda odgovorit će nam na pitanje  da li je tačka sa desne strane, dok će negativna vrijednost indicirati da se tačka nalazi s lijeve strane.

U koliko dobijemo proizvod nula, to znači se tačka A nalazi na pravoj p, odnosno da je kolinearna sa pravom p.

class Point
{
    public float x;
    public float y;
    public float z;
    public Point() { }
    public Point(float xx, float yy, float zz)
    {
        x = xx;
        y = yy;
        z = zz;
    }
}
/// <summary>
/// da li je tačka s desne strane prave
/// </summary>
/// <param name="A">tačka</param>
/// <param name="p1">početna tačka na pravoj p</param>
/// <param name="p2">krajnja tačka na pravoj p</param>
/// <returns>1 tačka s desne strane, -1 tačka s lijeve strane, 0 tačka na pravoj p.</returns>
static int isOnRightSide(Point A, Point p1, Point p2)
{
    //referentni vektor ili pravac kroz referentne tačke na pravoj
    Point vektor = new Point();
    vektor.x = p2.x - p1.x;
    vektor.y = p2.y - p1.y;

    //Pravimo vektor od referentne tacke 1 i date tacke a
    Point vektor2= new Point();
    vektor2.x = A.x - p1.x;
    vektor2.y = A.y - p1.y;

    //Izračunavamo vektorski proizvod dva verktora
    //ako je prozvod veći od nule tada je tačka s desne strane prave p

    float rezultat = vektor.y * vektor2.x - vektor.x * vektor2.y;

    if (rezultat > 0)
        return 1;//tačka se nalazi s desne strane
    else if (rezultat < 0)
        return -1;//tačka se nalazi s lijeve strane
    else
        return 0; //tačka lezi na pravoj p
}