Skip to content

TD5 — Graphics 2D: Drawing Canvas & 3×3 Grid

← Part 2 Overview

S7 Inf A3 — Java for Graphical and Mobile Programming
Stéphane Derrode — Centrale Lyon
Part 2 — GUI with Swing
Duration: 2h · Create a new Maven project td5


Objectives

  • Override paintComponent() correctly
  • Draw shapes, colors, and text with Graphics2D
  • Capture mouse clicks and convert pixel coordinates to grid positions
  • Build a 3×3 interactive grid — the direct foundation of the Spider Game

Part 1 — Free Drawing Canvas (35 min)

1.1 Project setup

Create a new Maven project td5 (same pom.xml as previous TDs).

1.2 Class DrawPanel

Create src/main/java/com/s7infa3/DrawPanel.java.

DrawPanel extends JPanel and lets the user draw colored dots by clicking on it.

Behaviour: - Each left-click adds a filled circle of diameter 20 px at the click position. - Each right-click clears all dots. - Dots are stored in an ArrayList<Point> (use java.awt.Point). - The background is white. Dots are drawn in Color.INDIGO (use new Color(63, 81, 181)).

package com.s7infa3;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;

public class DrawPanel extends JPanel {

    private List<Point> dots = new ArrayList<>();

    public DrawPanel() {
        setBackground(Color.WHITE);
        setPreferredSize(new Dimension(500, 400));

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON1) {
                    // TODO: add a point at (e.getX(), e.getY()) and repaint
                } else if (e.getButton() == MouseEvent.BUTTON3) {
                    // TODO: clear all dots and repaint
                }
            }
        });
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

        // TODO: draw each dot as a filled circle, diameter 20px
        // Hint: g2.fillOval(p.x - 10, p.y - 10, 20, 20)
    }
}

1.3 Main class

Create src/main/java/com/s7infa3/App.java:

package com.s7infa3;

import javax.swing.*;

public class App {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Drawing Canvas");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new DrawPanel());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

1.4 Extensions

Once the basic dots work:

a) Draw a coordinate cross (thin gray lines) through the click position — a horizontal and a vertical line spanning the whole panel — that disappears on the next click.

b) Show the click count in the top-left corner:

g2.setColor(Color.GRAY);
g2.setFont(new Font("Monospaced", Font.PLAIN, 12));
g2.drawString("Dots: " + dots.size(), 8, 18);

c) On mouse drag (mouseDragged in MouseMotionAdapter), continuously add dots to create a free-hand drawing effect.


Part 2 — 3×3 Interactive Grid (50 min)

This is the direct foundation of the Spider Game. You will build a reusable GridPanel component.

2.1 Specification

Create src/main/java/com/s7infa3/GridPanel.java.

Feature Description
Constant CELL = 120 Each cell is 120×120 pixels → total 360×360
Grid lines Dark gray, 2px stroke, 4 lines horizontal and 4 vertical
Click detection Left click → compute (row, col) from pixel position
Selection highlight The clicked cell gets a light-blue background
Cell label Draw the cell coordinates (row,col) centered in each cell
Right click Deselect (reset selectedRow = -1)

2.2 Skeleton

package com.s7infa3;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class GridPanel extends JPanel {

    private static final int CELL = 120;
    private static final int N    = 3;      // grid size

    private int selectedRow = -1;
    private int selectedCol = -1;

    public GridPanel() {
        setPreferredSize(new Dimension(CELL * N, CELL * N));
        setBackground(Color.WHITE);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON1) {
                    // TODO: compute row and col from e.getX(), e.getY()
                    // Guard: check 0 <= col < N  and  0 <= row < N
                    // Store in selectedRow, selectedCol
                    // Call repaint()
                } else {
                    // TODO: right-click → selectedRow = -1, repaint()
                }
            }
        });
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

        // Step 1: highlight selected cell (light blue fill)
        if (selectedRow >= 0) {
            // TODO: g2.setColor(new Color(200, 220, 255))
            // g2.fillRect(selectedCol*CELL, selectedRow*CELL, CELL, CELL)
        }

        // Step 2: draw grid lines
        // TODO: g2.setColor(Color.DARK_GRAY)
        // g2.setStroke(new BasicStroke(2f))
        // draw (N+1) vertical lines and (N+1) horizontal lines

        // Step 3: draw coordinate labels in each cell
        g2.setColor(Color.GRAY);
        g2.setFont(new Font("Monospaced", Font.PLAIN, 11));
        for (int r = 0; r < N; r++) {
            for (int c = 0; c < N; c++) {
                // TODO: center "(r,c)" text in cell (r,c)
                // Hint: use FontMetrics for centering
            }
        }
    }

    /** Returns the currently selected row, or -1 if none. */
    public int getSelectedRow() { return selectedRow; }

    /** Returns the currently selected col, or -1 if none. */
    public int getSelectedCol() { return selectedCol; }
}

2.3 Centering text in a cell — hint

FontMetrics fm = g2.getFontMetrics();
String label = "(" + r + "," + c + ")";
int textWidth  = fm.stringWidth(label);
int textHeight = fm.getAscent();
int cellCenterX = c * CELL + CELL / 2;
int cellCenterY = r * CELL + CELL / 2;
g2.drawString(label,
    cellCenterX - textWidth / 2,
    cellCenterY + textHeight / 2);

2.4 Test in App.java

Update App.java to show the GridPanel and print the selected cell:

GridPanel grid = new GridPanel();

JLabel info = new JLabel("Click a cell");
info.setHorizontalAlignment(SwingConstants.CENTER);

// Update info label when cell is selected
grid.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        int r = grid.getSelectedRow();
        int c = grid.getSelectedCol();
        if (r >= 0)
            info.setText("Selected: (" + r + "," + c + ")");
        else
            info.setText("No selection");
    }
});

JFrame frame = new JFrame("3×3 Grid");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(grid, BorderLayout.CENTER);
frame.add(info, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

Expected behaviour: clicking each cell highlights it in light blue and shows Selected: (row,col) below.


Part 3 — Add Pieces to the Grid (20 min)

Extend GridPanel to display colored circular pieces.

3.1 Data structure

Add a 2D array to GridPanel:

// 0 = empty, 1 = player 1 (blue), 2 = player 2 (red)
private int[][] board = new int[N][N];

3.2 Place pieces on click

Modify the left-click handler: instead of just selecting the cell, cycle the cell's value 0 → 1 → 2 → 0 each time it is clicked.

public void mouseClicked(MouseEvent e) {
    int col = e.getX() / CELL;
    int row = e.getY() / CELL;
    if (row < N && col < N) {
        board[row][col] = (board[row][col] + 1) % 3;
        repaint();
    }
}

3.3 Draw pieces

In paintComponent(), after drawing the grid, add:

// Draw pieces
for (int r = 0; r < N; r++) {
    for (int c = 0; c < N; c++) {
        if (board[r][c] == 1)
            g2.setColor(new Color(63, 81, 181));  // player 1: indigo
        else if (board[r][c] == 2)
            g2.setColor(new Color(229, 57, 53));   // player 2: red
        else
            continue;                              // empty cell

        int margin = 12;
        g2.fillOval(
            c*CELL + margin,
            r*CELL + margin,
            CELL - 2*margin,
            CELL - 2*margin);
    }
}

Expected result: clicking a cell cycles it through empty → blue piece → red piece → empty.


Part 4 — Exploration (remaining time)

4.1 Add a border/outline to the selected piece: when a piece is in the selected cell, draw an additional g2.drawOval(...) with a thick white stroke (new BasicStroke(3f)).

4.2 Add a JLabel counter below the grid: "Player 1: X pieces Player 2: Y pieces". Update it in refresh() or directly when board changes.

4.3 Experiment with BasicStroke for the grid lines: make the outer border thicker (4px) than the inner lines (2px).


Deliverable

No formal submission. Make sure Part 2 is complete (grid with click detection and labels) before leaving — you will build directly on GridPanel in the Spider Game project.


Common Errors

Error Cause Fix
Grid doesn't appear setPreferredSize() not called Always set preferred size before pack()
Old drawings accumulate Missing super.paintComponent(g) Always call it first in paintComponent()
Click outside grid registers No bounds check on row < N && col < N Add the guard condition
Circles look jagged Antialiasing not set Add setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON)
Text appears at wrong position Using cell top-left instead of center Use CELL/2 offset + FontMetrics centering formula