TD5 — Graphics 2D: Drawing Canvas & 3×3 Grid
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:
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 |