Advertisement
kburnik

Kristijan Burnik: JAVA primjer za geometrijsko crtanje

Jul 27th, 2012
174
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 16.95 KB | None | 0 0
  1. /////////////////////////////////////////////////////////////////////////////////////////////////////
  2. // JAVA primjer za crtanje geometrijskih likova
  3. // Autor: Kristijan Burnik, udruga informatičara Božo Težak
  4. // Mail: kristijanburnik [at] gmail [dot] com
  5. /////////////////////////////////////////////////////////////////////////////////////////////////////
  6. // Sljedeći java program nije testiran tako da zasigurno ima pogrešaka
  7. // međutim svrha je pokazati kako riješiti određeni problem pomoću objektno orijentiranog programiranja
  8. //
  9. // Program je veoma jednostavan primjer geometrijskog crtanja.
  10. // Cilj je pokazati kako pomoću klasa i objekata reprezentirati geometrijske likove i druge entitete
  11. // te ostvariti osnovno ponašanje istih i pružati mogućnost iscrtavanja na neki medij pomoću raster grafike
  12. // ista implementacija se može lako prilagoditi i za vektorsko iscrtavanje.
  13. //
  14. // Napominjem da ovo nije funkcionalna implementacija nego služi više kao skup smjernica
  15. // kod izrade programa u objektno orijentiranoj paradigmi - čitatelju se preporuča da rabi ovaj primjer
  16. // isključivo kao uzorak pomoću kojeg piše vlastite programe rabeći i poštujući svojstva objektne pardigme
  17. //
  18. // Ispravke i pitanja molim slati na gore naveden e-mail
  19. /////////////////////////////////////////////////////////////////////////////////////////////////////
  20.  
  21. // veoma jednostavna klasa koja predstavlja bilo kakvu točku u ravnini
  22. public class Tocka {
  23.        
  24.     // ovo su zaštićene varijable (x i y),
  25.     // što znači da se smiju čitati i pisati samo
  26.     // iz ove klase i svake klase koja se nasljeduje iz ove (npr. kvadrat)
  27.     // to radimo namjerno kako bi promjenu x ili y koordinate mogli pratiti
  28.     // te ne dopustiti vanjskim pozivateljima da izravno mijenjaju vrijednosti ovih varijabli
  29.     // dakle nije moguće napisati  Tocka t = new Tocka(); t.x = 100; jer je varijabla x zaštićena
  30.     protected float x; // X koordinata u ravnini
  31.     protected float y; // Y koordinata u ravnini
  32.    
  33.    
  34.     // definiramo konstruktor za tocku, to je metoda koja se poziva jednom kada stvorimo objekt
  35.     // tj. pomoću nje možemo inicijalizirati varijable i pripremiti objekt za upotrebu
  36.     public Tocka() {
  37.         x = 0;
  38.         y = 0;
  39.         // mogli smo pozvati i metodu Pomakni(0,0) umjesto ručnog podešavanja
  40.     }
  41.  
  42.     // mozemo imati vise od jednog konstruktora (npr. kada unaprijed znamo koordinate točke)
  43.     // međutim pri stvaranju objekta (sa ključnom riječi "new") možemo pozvati isključivo jedan konstruktor
  44.     // tako možemo pisati Tocka t = new Tocka(); ako želimo točku na poziciji (x = 0, y = 0)
  45.     // ili možeemo pisati Tocka t = new Tocka(100,300); ako želimo točku na poziciji (x = 100, y = 300)
  46.     public Tocka(float pocetniX, float pocetniY) {
  47.             x = pocetniX;
  48.             y = pocetniY;
  49.             // mogli smo pozvati i metodu Pomakni(pocetniX,pocetniY) umjesto ručnog podešavanja
  50.     }  
  51.    
  52.    
  53.     // vraća x koordinatu (Getter za X)
  54.     // budući je varijabla x zaštićena, ne možemo je drukčije pročitati izvan ove klase
  55.     // a ovaj način čitanja zaštićenih varijabli, tj. metoda se naziva "Getter" jer pomoću nje dobivamo vrijednost nekog svojstva
  56.     // isto tako po potrebi postoji i tzv. "Setter" metoda kojom možemo postaviti vrijednost nekoj zaštićenoj varijabli (svojstvu)
  57.     public float X() {
  58.         return x;
  59.     }
  60.    
  61.     // vraća y koordinatu (Getter za Y)
  62.     public float Y() {
  63.         return y;
  64.     }
  65.    
  66.     // metoda kojom pomičemo točku
  67.     // (npr. želimo napraviti da se pri pomaku dogodi crtanje lika ili sl. )
  68.     // ovu metodu bi teoretski mogli zvati "Setterom" međutim Setter metode utječu isključivo na jedno svojstvo,
  69.     // a ovdje mijenjamo oba svojstva X i Y
  70.     public void Pomakni(float noviX, float noviY) {
  71.         x = noviX;
  72.         y = noviY;
  73.     }
  74.    
  75. }
  76.  
  77. // Platno je klasa koju koristimo za crtanje bilo na ekran, bilo u PDF, u datoteku, na printer - nije vazno.
  78. public class Platno {
  79.  
  80.     // visina i sirina povrsine na kojoj crtamo
  81.     private int sirina;
  82.     private int visina;
  83.    
  84.    
  85.     // popis geometrijskih likova koji ce se crtati na platnu
  86.     private ArrayList<GeometrijskiLik> likovi = new ArrayList<GeometrijskiLik>();
  87.    
  88.    
  89.     // matrica koja predstavlja crtež (raster)
  90.     // tu matricu možemo kasnije proslijediti na ekran, u datoteku, na printer itd.    kako bi se sve iscrtalo
  91.     boolean[][] matrica;
  92.    
  93.     // metoda kojom brišemo "ekran"
  94.     public void Brisi() {
  95.         for (int i = 0 ; i < sirina; i++) {
  96.             for (int j = 0 ; j < visina; j++) {
  97.                 // ovaj pixel postaje "bijeli"
  98.                 matrica[i][j] = false;
  99.             }
  100.         }
  101.     }
  102.    
  103.     // konstruktor za platno, moramo znati dimenzije
  104.     public Platno(int sirina, int visina) {
  105.        
  106.         this.sirina = sirina;
  107.         this.visina = visina;
  108.        
  109.         // pripremamo matricu na kojoj ce se sve "crtati"
  110.         matrica = new boolean[sirina][visina];
  111.        
  112.         // brisemo "ekran" - iako ce vec biti "prazan"
  113.         Brisi();
  114.     }
  115.    
  116.     // vraca matricu koja predstavlja "ekran", "papir" ili štoveć
  117.     // tu metodu će pozivati objekt koji će u konačnici crtati sve što je na platnu
  118.     // tj. tu matricu možemo proslijediti na printer ili u datoteku i sl.
  119.     public boolean[][] Matrica() {
  120.         return matrica;
  121.     }
  122.    
  123.    
  124.     // dodajemo novi lik na platno
  125.     public void DodajLik(GeometrijskiLik geolik) {
  126.         likovi.Add(geolik);
  127.     }
  128.    
  129.     // metoda kojom crtamo sve likove na platno
  130.     public void Crtaj() {
  131.    
  132.         // prvo brisemo platno kako ne bi imali zaostatke od "prijašnjih" crtanja
  133.         Brisi();
  134.        
  135.         // crtaj likove redom kako su bili dodani
  136.         for (int i = 0; i < likovi.size() ; i++) {
  137.             // pozivamo metodu crtaj, naravno prosljeđujemo "ovo" platno jer se na njemu sve crta
  138.             likovi.get(i).Crtaj(this);
  139.         }
  140.     }
  141.  
  142.     // pomocu ovih metoda se likovi mogu crtati
  143.    
  144.    
  145.     public void CrtajLiniju(Tocka pocetna, Tocka krajnja) {
  146.         // naredbe za crtanje linije
  147.         // potrebno je rijesiti jednadzbu za pravac kroz dvije točke
  148.         // svaka x i y koordinata koja zadovoljava jednadžbu će biti crtana kao točka
  149.     }
  150.    
  151.     public void CrtajKruznicu (Tocka srediste, float polumjer) {
  152.         // naredbe za crtanje kruznice
  153.         // potrebno riješiti jednadžbu za kružnicu
  154.         // svaka x i y koordinata koja zadovoljava jednadžbu će biti nacrtana
  155.     }
  156.    
  157.     public void CrtajTocku(Tocka t) {
  158.         // dovoljno je postaviti pixel na toj poziciji u "crnu" boju
  159.         matrica[(int) t.X()][ (int) t.Y()] = true
  160.     }
  161.    
  162. }
  163.  
  164.  
  165. // definiramo klasu koja može predstavljati bilo koji geometrijski lik u ravnini (dakle veoma općenito)
  166. // nasljedujemo svojstva od točke budući geometrijski lik ima HVATIŠTE, odnosno točku koja predstavlja njegovu poziciju u ravnini
  167. // to znači da klasa GeometrijskiLik i sve klase koje ju "extend-aju" (nasljeđuju, tj. proširuju)
  168. // također imaju javno dostupne metode:
  169. // - X()
  170. // - Y()
  171. // - pomakni(float noviX, float noviY)
  172. public class GeometrijskiLik extends Tocka {
  173.    
  174.     // koristimo ovu varijablu kako bi mogli rotirati lik
  175.     protected float stupnjevi = 0;
  176.    
  177.     // ne znamo o kojem geometrijskom liku se radi, niti kako se crta (jer ovo je veoma općenita klasa)
  178.     // međutim znamo da bi se svaki KONKRETNI geometrijski lik trebao moći nacrtati stoga bi svaka KONKRETNA KLASA
  179.     // koja nasljeduje ovu klasu trebala IMPLEMENTIRATI OVU METODU (imati naredbe za crtanje)
  180.     // da bi smo crtali lik, moramo znati gdje ga crtamo, pa je potrebno definirati i platno na kojem ce biti crtan
  181.     // apstraktne metode kao takve nemaju implementaciju (nemaju tijelo funkcije, odnosno vitičaste zagrade)
  182.     public abstract void Crtaj(Platno platno);
  183.    
  184.     // jednako tako ne znamo kako će se računati površina, međutim svaki lik ima površinu
  185.     public abstract float Povrsina();
  186.    
  187.     // isto tretiramo i opseg
  188.     public abstract float Opseg();
  189.    
  190.     // isto tako bi bilo korisni dobiti težište lika:
  191.     public abstract Tocka Teziste();
  192.    
  193.     // korisno je imati i metodu kojom možemo povećati ili smanjiti lik
  194.     // međutim posebno treba implementirati za kvadrat, kružnicu, pravokutnik, elipsu itd.
  195.     public abstract void Skaliraj(float faktorSkaliranja);
  196.    
  197.    
  198.     // znamo da bi bilo korisno da možemo rotirati bilo koji geometrijski lik
  199.     // općenito je dovoljno imati pojam o tome za koliko stupnjeva je zarotiran lik od svoje uobičajene rotacije
  200.     // s obzirom na svoje hvatište
  201.     public void Rotiraj(float zaKolikoStupnjeva) {
  202.         stupnjevi += zaKolikoStupnjeva;
  203.         stupnjevi %= 360;
  204.     }
  205.        
  206.    
  207. }
  208.  
  209. // klasa Kvadrat nasljeđuje apstraktnu klasu GeometrijskiLik te ubacuje dodatne metode
  210. // dakle Kvadrat samim time što "extend-a" GeometrijskiLik već ima ugrađene javne metode:
  211.  
  212. // nasljeđeno od Tocka:
  213. // - X()
  214. // - Y()
  215. // - pomakni(float noviX, float noviY)
  216.  
  217. // nasljeđeno od GeometrijskiLik:
  218. // - Crtaj(Platno platno)
  219. // - Povrsina()
  220. // - Opseg()
  221. // - Teziste()
  222. // - Skaliraj()
  223. // - Rotiraj()
  224.  
  225. // a također nasljeđuje i zaštićene varijable (x i y koordinate od točke) te varijablu stupnjevi iz GeometrijskiLik
  226. // kada kažemo da se nešto nasljeđuje (metoda ili varijabla), to znači da je to isto također dio i nasljeđujuće klase
  227.  
  228. public class Kvadrat extends GeometrijskiLik {
  229.     // rotaciju necemo implementirati u ovom primjeru
  230.    
  231.     // Kvadrat je dovoljno opisati sa dvije točke
  232.     // jedna je njegovo hvatište (nasljedeno iz klase Tocka koju nasljeđuje GeometrijskiLik)
  233.     // nazovimo to gornjim lijevim vrhom
  234.     // a druga je tocka donji desni vrh
  235.     // varijablu označujemo privatnom, a to znači da isključivo klasa Kvadrat ima pristup ovoj varijabli
  236.     // dakle kad bismo napravili klasu PosebanKvadrat koja nasljeduje Kvadrat, ona ne bi mogla pristupiti ovoj varijabli
  237.     private Tocka DonjiDesniVrh;
  238.    
  239.     // definiramo konstruktor za kvadrat, dovoljno je znati točku hvatišta i duljinu stranice
  240.     // kako bismo jedinstveno odredili bilo koji konkretni kvadrat
  241.     public Kvadrat(Tocka GornjiLijeviVrh, float duljinaStranice) {
  242.         // budući Kvadrat nasljeđuje sve iz klase Tocka preko klase GeometrijskiLik, možemo pomaknuti "ovu" točku
  243.         // možemo još reći i da je Kvadrat djelomično sastavljen i od točke (hvatišta)!
  244.         this.pomakni(GornjiLijeviVrh.X(), GornjiLijeviVrh.Y());
  245.         DonjiDesniVrh = new Tocka(GornjiLijeviVrh.X() + duljinaStranice, GornjiLijeviVrh.Y() + duljinaStranice);
  246.     }
  247.    
  248.    
  249.     // ova metoda prepisuje (override-a) metodu Crtaj iz GeometrijskiLik
  250.     // dakle Crtaj u klasi Kvadrat sada više nije apstraktna metoda koja ne radi ništa
  251.     // već implementiramo postupak potreban za iscrtavanje (implementirati == funkcionalno ostvariti postupak)
  252.     public void Crtaj(Platno platno) {
  253.         // kvadrat crtamo pomoću četiri linije // "this" predstavlja ovaj objekt (samog sebe)
  254.         Tocka goreLijevo = new Tocka(this.X(), this.Y());
  255.         Tocka goreDesno = new Tocka(DonjiDesniVrh.X(), this.Y());
  256.         Tocka doljeLijevo = new Tocka(this.X(),DonjiDesniVrh.Y());
  257.         Tocka doljeDesno = DonjiDesniVrh;
  258.        
  259.         // crtamo redom linije:  gore, desno, dolje, lijevo
  260.         // prosljeđujemo točke vrhova jer se linija crta pomoću dvije krajnje točke
  261.         platno.CrtajLiniju( goreLijevo, goreDesno );
  262.         platno.CrtajLiniju( goreDesno, doljeDesno );
  263.         platno.CrtajLiniju( doljeDesno, doljeLijevo );
  264.         platno.CrtajLiniju( doljeLijevo, goreLijevo );
  265.     }
  266.    
  267.     // kvadrat ima svoju stranicu, a to je svojstveno za mnogokute, međutim ne i za krivulje poput kružnice
  268.     public float Stranica() {
  269.         return DonjiDesniVrh.X() - X();
  270.     }
  271.    
  272.     // mozda je korisno znati i dijagonalu : stranica * (korijen iz 2)
  273.     public float DuljinaDijagonale() {
  274.         return Stranica() * Math.sqrt( 2);
  275.     }
  276.    
  277.     // povrsinu "override-amo" - pisemo jednostavnu formulu za povrsinu kvadrata
  278.     public float Povrsina() {
  279.         return Stranica() * Stranica() ;
  280.     }
  281.    
  282.     // slično tretiramo i opseg
  283.     public float Opseg() {
  284.         return Stranica() * 4;
  285.     }
  286.    
  287.     // Težište kvadrata je središnja točka
  288.     public Tocka Teziste() {
  289.         return new Tocka( X() + Stranica() / 2 , Y() + Stranica() / 2 );
  290.     }
  291.    
  292.     // Skaliramo tako da pomicemo tocku donjeg desnog vrha
  293.     // dakle znamo da se tako to radi za kvadrat,
  294.     // međutim za druge likove je implementacija drukčija
  295.     // npr. za kružnicu bi bilo dovoljno pomnožiti polumjer s faktorom skaliranja
  296.     public void Skaliraj(float faktorSkaliranja) {
  297.         DonjiDesniVrh.Pomakni( DonjiDesniVrh.X() * faktorSkaliranja, DonjiDesniVrh.Y() * faktorSkaliranja );
  298.     }
  299.  
  300. }
  301.  
  302.  
  303. // definiramo klasu Kruznica koja je također geometrijski lik
  304. // međutim ona se malo drukčije ponaša i ima drukčija svojstva od kvadrata
  305. public class Kruznica extends GeometrijskiLik {
  306.  
  307.     // broj PI je konstanta koja je javno dostupna
  308.     // to je statička varijabla što znači da joj možemo pristupiti direktno preko klase (ne moramo stvarati instancu kružnice)
  309.     // npr. ako želimo dohvatiti ovu konstantu, dovoljno je bilo gdje u programu pisati Kruznica.PI
  310.     public static final float PI = 3.14159;
  311.    
  312.     // štitimo polumjer kružnice,
  313.     private float polumjer;
  314.    
  315.     // konstruktor za kružnicu, slično kao i za kvadrat
  316.     public Kruznica(Tocka srediste, float polumjerKruznice) {
  317.         this.Pomakni(srediste.X(),srediste.Y());
  318.         polumjer = polumjerKruznice;
  319.     }
  320.    
  321.     // metoda za crtanje kružnice je već "implementirana" u klasi platno
  322.     public void Crtaj(Platno platno) {
  323.         platno.CrtajKruznicu( Srediste() , polumjer);
  324.     }
  325.    
  326.     // ovo je "Getter" metoda za polumjer
  327.     public float Polumjer() {
  328.         return polumjer;
  329.     }
  330.    
  331.     // imamo i središte koja je ista kao i hvatište
  332.     public Tocka Srediste() {
  333.         return new Tocka(X(),Y());
  334.     }
  335.    
  336.     // - povrsinu "override-amo" - pisemo jednostavnu formulu za povrsinu kružnice r^2 * PI
  337.     public float Povrsina() {
  338.         return polumjer * polumjer * PI ;
  339.     }
  340.    
  341.     // - slično tretiramo i opseg // 2 * r * Pi
  342.     public float Opseg() {
  343.         return 2 * polumjer * PI ;
  344.     }
  345.    
  346.     // Težište kružnice je središnja točka
  347.     public Tocka Teziste() {
  348.         return Srediste();
  349.     }
  350.    
  351.     // za kružnicu je dovoljno pomnožiti polumjer s faktorom skaliranja
  352.     public void Skaliraj(float faktorSkaliranja) {
  353.         polumjer *= faktorSkaliranja;
  354.     }
  355.  
  356.  
  357. }
  358.  
  359. // glava klasa tj klasa koja se poziva pri pokretanju programa
  360. class GlavnaKlasa
  361. {  
  362.     public static void main(String args[])
  363.     {
  364.        
  365.        // kreiramo novo platno, to znači da stvaramo NOVI OBJEKT, tj. instancu klase Platno
  366.        Platno platno = new Platno(1024,768);
  367.        
  368.        // kreiramo nekoliko kvadrata, ovdje dolaze do izražaja konstruktori za točku i kvadrat
  369.        Kvadrat a = new Kvadrat( new Tocka(10,10), 100 );
  370.        Kvadrat b = new Kvadrat( new Tocka(200,200), 200 );
  371.        Kvadrat c = new Kvadrat( new Tocka(200,0), 30 );
  372.        
  373.        // kreiramo nekoliko kruznica, konstruktor je veoma sličan: točka hvatišta te polumjer
  374.        Kruznica d = new Kruznica( new Tocka (300,500), 300 );
  375.        Kruznica e = new Kruznica( new Tocka (300,500), 100 );
  376.        
  377.        // postavljamo sve likove na platno, neovisno da li je to kvadrat, kružnica ili neki drugi konkretni lik
  378.        platno.DodajLik(a);
  379.        platno.DodajLik(b);
  380.        platno.DodajLik(c);
  381.        platno.DodajLik(d);
  382.        platno.DodajLik(e);
  383.        
  384.        // iscrtavamo platno (sve likove na njemu)
  385.        platno.Crtaj();
  386.        
  387.        // matricu od platna šaljemo na printer
  388.        Printer printer = new Printer(" [LPT1] /* Canon Pixma MX300 */");
  389.        printer.PostaviMatricu(  platno.Matrica() );
  390.        printer.ZapocniIspis();
  391.        
  392.         // Klasa printer kao takva ne postoji, ali zamišljamo da je nešto poput:
  393.        /*
  394.         public class Printer {
  395.             private boolean[][] matrica;
  396.             private String opisnik; // opisnik pomocu kojeg hardverski identificiramo printer
  397.            
  398.             public Printer(String opisnikPrintera) {
  399.                 opisnik = opisnikPrintera;
  400.                 // povezivanje sa stvarnim printerom
  401.                 // ...
  402.             }
  403.            
  404.             public void PostaviMatricu(boolean[][] mat) {
  405.                 matrica = mat;
  406.             }
  407.            
  408.             public void ZapocniIspis() {
  409.                 // naredbe za slanje informacija na stvarni printer
  410.                 // ...
  411.             }
  412.         }
  413.        */
  414.        
  415.        // Sada zelimo ispisati povrsinu i opseg svakog geometrijskog lika na konzolu
  416.        // prvo napravimo polje gdje ubacimo sve likove
  417.        GeometrijskiLik[] likovi = { a, b, c, d, e};
  418.        
  419.        // uočiti da iako su varijable a,b i c tipa Kvadrat, a "d" i "e" Kruznica,
  420.        // svi zajedno su instance klase GeometrijskiLik te ih u tom smislu možemo tako tretirati
  421.        // te znamo da svi imaju metode za opseg i površinu
  422.        // sličan primjer je implementiran u klasi Platno gdje ono sadrži ArrayList geometrijskih likova
  423.        // a samom platnu uopće nije važno da li crta kvadrat, kružnicu ili nešto treće
  424.        // - njoj je jedino važno da taj lik implementira metodu "Crtaj", a za pojedinosti crtanja se brine
  425.        // taj konkretni geometrijski lik.
  426.        
  427.        for (int i = 0; i < 5; i++) {
  428.             // budući ne znamo kojeg je tipa trenutni geometrijski lik (likovi[i])
  429.             // moramo rabiti ključnu riječ "instanceof" kako bi znali da je nešto Kvadat, Kruznica ili nešto drugo
  430.             if ( likovi[i] instanceof Kvadrat ) {
  431.                 System.out.println("Kvadat");
  432.             } else if (  likovi[i] instanceof Kruznica ) {
  433.                 System.out.println("Kruznica");
  434.             } else {
  435.                 System.out.println("Neki drugi geometrijski lik");
  436.             }
  437.             System.out.print(  "Opseg je " );
  438.             System.out.print(  likovi[i].Opseg() );
  439.             System.out.println();
  440.             System.out.print(  "Povrsina je " );
  441.             System.out.print(  likovi[i].Povrsina() );
  442.        }
  443.        
  444.     }
  445. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement