====== Laboratorium 10: rysujemy choinkę ======
Użyjemy biblioteki graficznej Swing, a dokładniej klasy potomnej ''JPanel''.
Umieść w klasie głównej poniższą funkcję ''main()''
public static void main(String[] args) {
// write your code here
JFrame frame = new JFrame("Choinka");
frame.setContentPane(new DrawPanel());
frame.setSize(1000, 700);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setResizable(true);
frame.setVisible(true);
}
===== DrawPanel =====
''DrawPanel()'' to klasa, której na razie brakuje. Utwórz ją w projekcie i dodaj funkcję ''paint(Graphics g)'' z przykładowym kodem wypisującym tekst.
public class DrawPanel extends JPanel {
public void paintComponent(Graphics g){
g.setFont(new Font("Helvetica", Font.BOLD, 18));
g.drawString("Hello World", 20, 20);
System.out.println("painting");
}
}
Zaobserwuj, kiedy na konsoli wypisywane jest ''painting''.
''Graphics'' to kontekst graficzny. Obiekt przechowuje informacje o bieżących atrybutach rysowania (font, kolor) oraz pozwala wywołać funkcje umożliwiające rysowanie wektorów oraz obrazów.
Wypróbuj kilka funkcji do rysowania. Na przykład
== Rysowanie linii ==
g.drawLine(10,10,100,100);
== Rysowanie elips ==
g.setColor(Color.yellow);
g.fillOval(100,101,30,30);
g.setColor(Color.black);
g.drawOval(100,101,30,30);
== Rysowanie wieloboków ==
int x[]={286,253,223,200,148,119,69,45,0};
int y[]={0,101,89,108,79,95,66,80,56};
g.fillPolygon(x,y,x.length);
== Rysowanie obrazków ==
Jeżeli chciałbyś wyświetlić obrazek, należy wcześniej go załadować, np. przechowywać jako atrybut klasy. (Nie należy ładować go w funkcji ''paint'').
Image img = Toolkit.getDefaultToolkit().getImage("bird1.jpg"),
a następnie wyświetlić w funkcji paint()
g.drawImage(img,0,0,getWidth(),getHeight(),this);
===== Graphics2D =====
''Graphics2D'' jest rozszerzoną wersją kontekstu graficznego pozwalającą na bardziej zaawansowane operacje.
Aby uzyskać dostęp do obiektu ''Graphics2D'' wystarczy rzutowanie.
public void paintComponent(Graphics g){
Graphics2D g2d= (Graphics2D)g;
}
Ważną cechą ''Graphics2D'' jest możliwość transformacji układu współrzędnych odpowiadających powinowactwom (~ klasy I LO), czyli:
*przesuniecia
*skalowanie
*rotacje
Poniższy kod rysuje 12 linii obracając je o 30 stopni
// zachowaj macierz przekształcenia
AffineTransform mat = g2d.getTransform();
// przesuń początek układu
g2d.translate(100,100);
// zastosuj skalowanie
g2d.scale(.2,.2);
// narysuj linie
for(int i=0;i<12;i++){
g2d.drawLine(0,0,100,100);
g2d.rotate(2*Math.PI/12);
}
//oddtwórz poprzednie ustawienia transformacji układu współrzędnych
g2d.setTransform(mat);
Analogicznie możesz obrócić tekst podczas rysowania...
g2d.translate(200,200);
// zastosuj skalowanie
g2d.scale(.2,.2);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Serif", Font.PLAIN, 96);
g2d.setFont(font);
for(int i=0;i<12;i++){
g2d.drawString("Happy new year",150,0);
g2d.rotate(2*Math.PI/12);
}
Możesz ustawić atrybuty linii....
// zachowaj macierz przekształcenia
AffineTransform mat = g2d.getTransform();
// przesuń początek układu
g2d.translate(200,200);
// zastosuj skalowanie
g2d.scale(.2,.2);
g2d.setStroke(new BasicStroke(50, CAP_ROUND,JOIN_MITER));
for(int i=0;i<12;i++){
//g2d.drawString("Happy new year",150,0);
g2d.drawLine(0,0,100,100);
g2d.rotate(2*Math.PI/12);
}
//oddtwórz poprzednie ustawienia transformacji układu współrzędnych
g2d.setTransform(mat);
Możesz też użyć gradientowego wypenienia dla wieloboków
Graphics2D g2d= (Graphics2D)g;
AffineTransform mat = g2d.getTransform();
GradientPaint grad = new GradientPaint(0,0,new Color(0,255,0),0,100, new Color(0,10,0));
g2d.setPaint(grad);
g2d.translate(0,50);
g2d.scale(0.7,0.5);
int x[]={286,286,223,200,148,119,69,45,0};
int y[]={0,131,89,108,79,95,66,80,56};
g2d.fillPolygon(x,y,x.length);
g2d.translate(670,0);
g2d.scale(-1,1);
g2d.fillPolygon(x,y,x.length);
g2d.setTransform(mat);
==== Tło ====
Możesz ustawić tło wołając metodę ''setBackground()'' klasy ''JPanel''
Na przykład
DrawPanel(){
setBackground(new Color(0,0,50));
// setOpaque(true);
}
Nie zapomnij na początku ''paintComponent()'' wywołać metodę ''paintComponent()'' nadklasy
===== Rysowanie choinki =====
W zasadzie można narysować choinkę składając ją z powtarzalnych elementów: gałęzie, ozdoby, świeczki, itp.
Może być to jedna długa funkcja, w której wołany jest np. kod do rysowania odpowiednio przesuniętych i przeskalowanych gałęzi, ozdób, lampek itp.
Zamiast tego, zdefiniujemy klasy elementów składowych
==== Klasa bazowa (interfejs bazowy) ====
W zasadzie powinien nazywać sie ''Shape'', ale taki już jest [[https://docs.oracle.com/javase/8/docs/api/java/awt/Shape.html]]
Nazwiemy go ''XmasShape''
public interface XmasShape {
/**
* Przesuwa poczatek układu w zadane miejsce, skaluje, jeśli trzeba obraca
* @param g2d Graphics2D - kontekst graficzny
*/
void transform(Graphics2D g2d);
/**
* Zawiera kod, który rysuje elementy
* @param g2d Graphics2D - kontekst graficzny
*/
void render(Graphics2D g2d);
/**
* Standardowa implementacja metody
* @param g2d
*/
default void draw(Graphics2D g2d){
// Get the current transform
AffineTransform saveAT = g2d.getTransform();
// Perform transformation
transform(g2d);
// Render
render(g2d);
// Restore original transform
g2d.setTransform(saveAT);
}
}
==== Napisz klasę Bubble ====
To ma być jedna z ozdób
public class Bubble implements XmasShape {
int x;
int y;
double scale;
Color lineColor;
Color fillColor;
@Override
public void render(Graphics2D g2d) {
// ustaw kolor wypełnienia
g2d.fillOval(0,0,100,100);
// ustaw kolor obramowania
g2d.drawOval(0,0,100,100);
}
@Override
public void transform(Graphics2D g2d) {
g2d.translate(x,y);
g2d.scale(scale,scale);
}
}
Uwagi:
*obiekt zawsze jest rysowany tak samo (funkcja ''render'')
*zmienia się położenie i skala obiektu; jest ona interpretowana w funkcji ''transform''
==== Refaktoryzacja DrawPanel ====
Dodaj atrybut
List shapes = new ArrayList<>();
Zmień funkcję ''paintComponent()'' na następującą:
public void paintComponent(Graphics g){
super.paintComponent(g);
for(XmasShape s:shapes){
s.draw((Graphics2D)g);
}
}
=== Dodaj obiekty ===
Dodaj kilka obiektów klasy ''Bubble'' i sprawdź, czy wyświetlane są zgodnie z oczekiwaniami.
Napisz osobną funkcje do dodawania obiektów w ''DrawPanel'' i wywołaj ją w konstruktorze lub funkcji ''main()''. Nie dodawaj obiektów w ''paintCpmponent()''
==== Kolejne klasy ====
*Dodaj klasę ''Branch'' - zielona gałąź drzewa
*Możesz utworzyć klasę ''Tree'' zawierającą listę ''XmasShape'' i złożyć gotowe drzewko z gałęzi
*Dodaj klasę ''Light''
*Możesz dodać gwiazdki i inne ozdoby...
Za każdym razem:
* określ, jakie atrybuty są potrzebne, aby przeprowadzić transformację układu współrzędnych
* napisz funkcję ''transform()''
* napisz kod rysujący element -- ''render()''
===== Proszę o umieszczenie na git zrzutów ekranu choinek =====