kburnik

Kristijan Burnik: JAVA primjer za geometrijsko crtanje

Jul 27th, 2012
134
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×