Java 2D – Các đối tượng hình học – phần 1

3/5 - (112 votes)

Trong phần này chúng ta sẽ vẽ các đối tượng hình học cơ bản.

Điểm

Đối tượng hình học cơ bản nhất là điểm, là một dấu chấm trên màn hình. Trong Java có sẵn lớp java.awt.Point để biểu diễn điểm nhưng không có phương thức nào để vẽ. Chúng ta sẽ vẽ điểm bằng phương thức drawLine(), phương thức này vẽ một đoạn thẳng từ điểm đầu tới điểm cuối, chúng ta dùng nó để vẽ điểm với hai điểm đầu và cuối trùng nhau.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

class Surface extends JPanel implements ActionListener {

    private final int DELAY = 150;
    private Timer timer;

    public Surface() {

        initTimer();
    }

    private void initTimer() {

        timer = new Timer(DELAY, this);
        timer.start();
    }        

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        g2d.setPaint(Color.blue);

        int w = getWidth();
        int h = getHeight();

        Random r = new Random();

        for (int i = 0; i < 2000; i++) {

            int x = Math.abs(r.nextInt()) % w;
            int y = Math.abs(r.nextInt()) % h;
            g2d.drawLine(x, y, x, y);
        }
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
    }
}

public class PointsEx extends JFrame {

    public PointsEx() {

        initUI();
    }

    private void initUI() {

        final Surface surface = new Surface();
        add(surface);      

        setTitle("Points");
        setSize(350, 250);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                PointsEx ex = new PointsEx();
                ex.setVisible(true);
            }
        });
    }
}

Đoạn code trên vẽ 2000 điểm ở vị trí ngẫu nhiên trên màn hình và thay đổi theo thời gian.

private void initTimer() {

    timer = new Timer(DELAY, this);
    timer.start();
}

Lớp javax.swing.Timer được dùng để tạo hiệu ứng chuyển động. Lớp này sẽ giải phóng sự kiện ActionEvents cứ sau một khoảng thời gian nhất định.

g2d.setPaint(Color.blue);

Điểm được vẽ sẽ có màu xanh lam.

int w = getWidth();
int h = getHeight();

Ở 2 dòng code trên, chúng ta lấy kích thước cửa sổ.

Random r = new Random();
int x = Math.abs(r.nextInt()) % w;
int y = Math.abs(r.nextInt()) % h;

Sau đó tính vị trí ngẫu nhiên dựa vào kích thước đó.

g2d.drawLine(x, y, x, y);

Chúng ta dùng phương thức drawLine() để vẽ điểm.

@Override
public void actionPerformed(ActionEvent e) {
    repaint();
}

Trong phương thức xử lý sự kiện, chúng ta gọi phương thức repaint() để chương trình cập nhật lại vị trí mới của mỗi điểm.

Capture

Đoạn thẳng

Đoạn thẳng là một đối tượng hình học cơ bản. Trong lập trình thì đoạn thẳng là một đối tượng kết nối giữa hai điểm. Chúng ta dùng phương thức drawLine() để vẽ đoạn thẳng.

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        g2d.drawLine(30, 30, 200, 30);
        g2d.drawLine(200, 30, 30, 200);
        g2d.drawLine(30, 200, 200, 200);
        g2d.drawLine(200, 200, 30, 30);
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class LinesEx extends JFrame {

    public LinesEx() {

        initUI();
    }

    private void initUI() {

        add(new Surface());

        setTitle("Lines");
        setSize(350, 250);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                
                LinesEx ex = new LinesEx();
                ex.setVisible(true);
            }
        });
    }
}

Trong đoạn code trên chúng ta vẽ bốn đoạn thẳng.

g2d.drawLine(30, 30, 200, 30);

Phương thức drawLine() nhận vào 4 tham số là tọa độ (x, y) của điểm đầu và điểm cuối.

Capture

Định dạng đoạn thẳng

Định dạng đoạn thẳng là cách mà đoạn thẳng đó được vẽ. Lớp BasicStroke là lớp định nghĩa một tập các cách vẽ khác nhau.

import java.awt.BasicStroke;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        float[] dash1 = {2f, 0f, 2f};
        float[] dash2 = {1f, 1f, 1f};
        float[] dash3 = {4f, 0f, 2f};
        float[] dash4 = {4f, 4f, 1f};

        g2d.drawLine(20, 40, 250, 40);

        BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f);

        BasicStroke bs2 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash2, 2f);

        BasicStroke bs3 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash3, 2f);

        BasicStroke bs4 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash4, 2f);

        g2d.setStroke(bs1);
        g2d.drawLine(20, 80, 250, 80);

        g2d.setStroke(bs2);
        g2d.drawLine(20, 120, 250, 120);

        g2d.setStroke(bs3);
        g2d.drawLine(20, 160, 250, 160);

        g2d.setStroke(bs4);
        g2d.drawLine(20, 200, 250, 200);
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class BasicStrokesEx extends JFrame {

    public BasicStrokesEx() {

        initUI();
    }
    
    private void initUI() {

        add(new Surface());

        setTitle("Basic strokes");
        setSize(280, 270);
        setLocationRelativeTo(null);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                BasicStrokesEx ex = new BasicStrokesEx();
                ex.setVisible(true);
            }
        });
    }
}

Trong đoạn code trên chúng ta vẽ một số kiểu đường thẳng khác nhau.

Graphics2D g2d = (Graphics2D) g.create();

Ở đây chúng ta sẽ thay đổi thuộc tính stroke của đối tượng Graphics, do đó chúng ta nên tạo một bản sao của đối tượng này rồi làm việc với bản sao đó.

float[] dash1 = { 2f, 0f, 2f };
float[] dash2 = { 1f, 1f, 1f };
float[] dash3 = { 4f, 0f, 2f };
float[] dash4 = { 4f, 4f, 1f };

Thuộc tính dash định nghĩa kiểu vẽ giữa các phần hiện và các phần ẩn. Trong đoạn code trên thì dash1 có ý nghĩa là vẽ một đoạn dài 2f pixel, sau đó là khoảng trắng dài 0f pixel rồi lại vẽ đoạn thẳng dài 2f, cứ như thế.

BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT, 
    BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f );

Chúng ta tạo một đối tượng BasicStroke.

g2d.setStroke(bs1);

Phương thức setStroke() quy định kiểu vẽ cho đối tượng Graphics.

g2d.dispose();

Cuối cùng chúng ta gọi phương thức dispose() để hủy đối tượng graphics đã được copy.

Capture

Tham số CAP

Tham số CAP quy định phần viền ở góc đoạn thẳng. Tham số này có 3 giá trị là CAP_BUTT, CAP_ROUND, và CAP_SQUARE.

  • CAP_BUTT: capbutt
  • CAP_ROUND: capround
  • CAP_SQUARE: capsquare
import java.awt.BasicStroke;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        RenderingHints rh = new RenderingHints(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        rh.put(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);

        g2d.setRenderingHints(rh);

        BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs1);
        g2d.drawLine(20, 30, 250, 30);

        BasicStroke bs2 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs2);
        g2d.drawLine(20, 80, 250, 80);

        BasicStroke bs3 = new BasicStroke(8, BasicStroke.CAP_SQUARE,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs3);
        g2d.drawLine(20, 130, 250, 130);

        BasicStroke bs4 = new BasicStroke();
        g2d.setStroke(bs4);

        g2d.drawLine(20, 20, 20, 140);
        g2d.drawLine(250, 20, 250, 140);
        g2d.drawLine(254, 20, 254, 140);
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class CapsEx extends JFrame {

    public CapsEx() {

        initUI();
    }
    
    private void initUI() {
        
        add(new Surface());

        setTitle("Caps");
        setSize(280, 270);
        setLocationRelativeTo(null); 
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                CapsEx ex = new CapsEx();
                ex.setVisible(true);
            }
        });
    }
}

Đoạn code trên ví dụ về 3 tham số CAP khác nhau.

Capture

Tham số JOIN

Tham số JOIN quy định kiểu đường cong nối giữa hai đoạn thẳng cắt nhau. Tham số này có 3 giá trị là JOIN_BEVEL, JOIN_MITER, và JOIN_ROUND.

  • JOIN_BEVEL: joinbevel
  • JOIN_MITER: joinmiter
  • JOIN_ROUND: joinround
import java.awt.BasicStroke;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs1);
        g2d.drawRect(15, 15, 80, 50);

        BasicStroke bs2 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_MITER);
        g2d.setStroke(bs2);
        g2d.drawRect(125, 15, 80, 50);

        BasicStroke bs3 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_ROUND);
        g2d.setStroke(bs3);
        g2d.drawRect(235, 15, 80, 50);
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class JoinsEx extends JFrame {

    public JoinsEx() {

        initUI();
    }
    
    private void initUI() {

        add(new Surface());

        setTitle("Joins");
        setSize(340, 110);
        setLocationRelativeTo(null);  
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                JoinsEx ex = new JoinsEx();
                ex.setVisible(true);
            }
        });
    }
}

Trong đoạn code trên chúng ta vẽ 3 hình chữ nhật với 3 kiểu JOIN khác nhau.

g2d.drawRect(15, 15, 80, 50);

Phương thức drawRect() vẽ hình chữ nhật với 4 tham số là tọa độ góc trái trên và góc phải dưới của hình chữ nhật.

Capture

5 1 vote
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments