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[]={0,120,220,360,240};
        int y[]={0,320,200,330,90};
        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"),

albo (ładowanie z zasobów)

BufferedImage img = ImageIO.read(getClass().getResource("/resources/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 (transformacji afinicznej) układu współrzędnych odpowiadających powinowactwom (~ klasy I LO), czyli:

  • przesuniecia
  • skalowanie
  • rotacje

Postać macierzowa

  • Wszystkie te operacje mogą być reprezentowane w postaci macierzy transformacji (3×3 dla 2D 4×4 dla 3D).
  • Zastosowanie kilku operacji następujących po sobie można uzyskać mnożąc macierze, czyli np. funkcja Grapics2D.rotate() mnoży bieżącą macierz przez macierz rotacji.
  • Macierz transformacji można zapisać w zmiennej lokalnej i załadować z powrotem do kontekstu graficznego.

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 wypełnienia 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,0};
        int y[]={0,131,89,108,};
        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<XmasShape> 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 przesyłanie na UPEL także zrzutów ekranu choinek

Fragmenty kodu

import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
public class Bucket implements XmasShape {
    List<XmasShape> shapes = new ArrayList<>();
    int x;
    int y;
    Bucket(int x,int y){
        this.x=x;
        this.y=y;
        Random r = new Random();
        for(int i=0;i<10;i++){
            Bubble b = new Bubble();
            b.x=r.nextInt(100);
            b.y=r.nextInt(100);
            b.scale=r.nextDouble()*0.2;
            b.fillColor=new Color(r.nextFloat(),r.nextFloat(),r.nextFloat());
            b.lineColor=new Color(r.nextFloat(),r.nextFloat(),r.nextFloat());
            shapes.add(b);
        }
    }
    @Override
    public void transform(Graphics2D g2d) {
        g2d.translate(x,y);
    }
 
    @Override
    public void render(Graphics2D g2d) {
        g2d.setColor(new Color(192,192,192));
        g2d.fillRect(0,0,100,100);
        for(var b:shapes)b.draw(g2d);
    }
}

oraz

DrawPanel(){
        shapes.add(new Bucket(10,10));
        shapes.add(new Bucket(10,400));
        shapes.add(new Bucket(400,10));
        shapes.add(new Bucket(400,400));
    }
pz1/lab10.txt · Last modified: 2023/12/11 18:52 by pszwed
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0