hw4: initial implementation (gemini 2.5 pro)
This commit is contained in:
parent
34a71d5ef7
commit
857802227d
66
hw4/src/CarShape.java
Normal file
66
hw4/src/CarShape.java
Normal file
@ -0,0 +1,66 @@
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
|
||||
/**
|
||||
* A car shape composed of primitive shapes.
|
||||
* NOTE: This code structure is based on typical textbook examples for drawing composite shapes.
|
||||
* Specific implementation details might vary based on the exact textbook source.
|
||||
*/
|
||||
public class CarShape implements CompositeShape {
|
||||
private final int x; // Base x-coordinate (relative to drawing origin)
|
||||
private final int y; // Base y-coordinate (relative to drawing origin)
|
||||
private final int width;
|
||||
|
||||
// Default constructor positions at (0,0) with default width
|
||||
public CarShape() {
|
||||
this(0, 0, 60); // Default position and width
|
||||
}
|
||||
|
||||
// Constructor for position and width (not strictly needed if always drawn at origin for icon/panel)
|
||||
public CarShape(int x, int y, int width) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width; // Width of the car body
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
// Approximate height: body height + wheel radius
|
||||
return width / 2 + width / 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2) {
|
||||
// All drawing is relative to (0,0) because translation happens externally
|
||||
|
||||
// Car body
|
||||
Rectangle2D.Double body = new Rectangle2D.Double(x, y + width / 6.0, width - 1, width / 6.0);
|
||||
|
||||
// Roof
|
||||
Point2D.Double r1 = new Point2D.Double(x + width / 6.0, y + width / 6.0);
|
||||
Point2D.Double r2 = new Point2D.Double(x + width / 3.0, y);
|
||||
Point2D.Double r3 = new Point2D.Double(x + width * 2.0 / 3.0, y);
|
||||
Point2D.Double r4 = new Point2D.Double(x + width * 5.0 / 6.0, y + width / 6.0);
|
||||
Line2D.Double L1 = new Line2D.Double(r1,r2);
|
||||
Line2D.Double L2 = new Line2D.Double(r2,r3);
|
||||
Line2D.Double L3 = new Line2D.Double(r3,r4);
|
||||
|
||||
|
||||
// Wheels
|
||||
Ellipse2D.Double frontTire = new Ellipse2D.Double(x + width / 6.0, y + width / 3.0, width / 6.0, width / 6.0);
|
||||
Ellipse2D.Double rearTire = new Ellipse2D.Double(x + width * 2.0 / 3.0, y + width / 3.0, width / 6.0, width / 6.0);
|
||||
|
||||
// Draw the parts
|
||||
g2.draw(body);
|
||||
g2.draw(L1);
|
||||
g2.draw(L2);
|
||||
g2.draw(L3);
|
||||
g2.draw(frontTire);
|
||||
g2.draw(rearTire);
|
||||
}
|
||||
}
|
27
hw4/src/CompositeShape.java
Normal file
27
hw4/src/CompositeShape.java
Normal file
@ -0,0 +1,27 @@
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
/**
|
||||
* An interface for shapes that can be composed of multiple primitive shapes
|
||||
* and can be drawn.
|
||||
*/
|
||||
public interface CompositeShape {
|
||||
/**
|
||||
* Draws the shape.
|
||||
* @param g2 the graphics context
|
||||
*/
|
||||
void draw(Graphics2D g2);
|
||||
|
||||
/**
|
||||
* Gets the width of the shape's bounding box.
|
||||
* Used for creating icons of a consistent size.
|
||||
* @return the width
|
||||
*/
|
||||
int getWidth();
|
||||
|
||||
/**
|
||||
* Gets the height of the shape's bounding box.
|
||||
* Used for creating icons of a consistent size.
|
||||
* @return the height
|
||||
*/
|
||||
int getHeight();
|
||||
}
|
68
hw4/src/HouseShape.java
Normal file
68
hw4/src/HouseShape.java
Normal file
@ -0,0 +1,68 @@
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/**
|
||||
* A simple house shape composed of a body (rectangle), roof (triangle),
|
||||
* door (rectangle), and window (rectangle).
|
||||
*/
|
||||
public class HouseShape implements CompositeShape {
|
||||
private int width; // Width of the house body
|
||||
private int height; // Height of the house body (excluding roof)
|
||||
|
||||
public HouseShape() {
|
||||
this(50, 40); // Default width and body height
|
||||
}
|
||||
|
||||
public HouseShape(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
// Total height = body height + roof height (approx 0.5 * width)
|
||||
return height + (int) (width * 0.5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2) {
|
||||
// Drawing relative to (0,0) - top-left of bounding box.
|
||||
|
||||
// Body
|
||||
double bodyY = width * 0.5; // Start body below roof peak
|
||||
Rectangle2D.Double body = new Rectangle2D.Double(0, bodyY, width, height);
|
||||
|
||||
// Roof (triangle using Path2D)
|
||||
Path2D.Double roof = new Path2D.Double();
|
||||
roof.moveTo(0, bodyY); // Bottom-left corner of roof
|
||||
roof.lineTo(width / 2.0, 0); // Peak of roof
|
||||
roof.lineTo(width, bodyY); // Bottom-right corner of roof
|
||||
// No need to close path for drawing outline
|
||||
|
||||
// Door (small rectangle)
|
||||
double doorWidth = width * 0.25;
|
||||
double doorHeight = height * 0.5;
|
||||
double doorX = (width - doorWidth) / 2.0; // Centered horizontally
|
||||
double doorY = bodyY + height - doorHeight; // At the bottom of the body
|
||||
Rectangle2D.Double door = new Rectangle2D.Double(doorX, doorY, doorWidth, doorHeight);
|
||||
|
||||
// Window (small square rectangle)
|
||||
double windowSize = width * 0.2;
|
||||
double windowX = width * 0.15; // Positioned left of center
|
||||
double windowY = bodyY + height * 0.2; // Positioned near top of body
|
||||
Rectangle2D.Double window = new Rectangle2D.Double(windowX, windowY, windowSize, windowSize);
|
||||
|
||||
|
||||
// Draw parts
|
||||
g2.draw(body);
|
||||
g2.draw(roof);
|
||||
g2.draw(door);
|
||||
g2.draw(window);
|
||||
}
|
||||
}
|
@ -1,71 +1,187 @@
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ShapeDisplayer extends JFrame {
|
||||
|
||||
private static final int ICON_WIDTH = 50;
|
||||
private static final int ICON_HEIGHT = 50;
|
||||
|
||||
private DrawingPanel drawingPanel;
|
||||
private CompositeShape currentShape = null; // The shape currently selected
|
||||
|
||||
private JButton carButton;
|
||||
private JButton snowmanButton;
|
||||
private JButton houseButton;
|
||||
private ShapeIcon carIcon;
|
||||
private ShapeIcon snowmanIcon;
|
||||
private ShapeIcon houseIcon;
|
||||
|
||||
// Helper class to store drawn shapes and their positions
|
||||
private static class PlacedShape {
|
||||
final CompositeShape shape;
|
||||
final Point position; // Top-left corner where the shape should be drawn
|
||||
|
||||
PlacedShape(CompositeShape shape, Point position) {
|
||||
this.shape = shape;
|
||||
this.position = position;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom JPanel for drawing
|
||||
private class DrawingPanel extends JPanel {
|
||||
private final List<PlacedShape> drawnShapes = new ArrayList<>();
|
||||
|
||||
public DrawingPanel() {
|
||||
setBackground(Color.WHITE);
|
||||
setPreferredSize(new Dimension(600, 400)); // Set preferred size
|
||||
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (currentShape != null) {
|
||||
// Create a *new instance* for drawing, if shapes had state.
|
||||
// For these stateless shapes, reusing is fine, but creating
|
||||
// new instances is conceptually safer if shapes could change.
|
||||
// Example: drawnShapes.add(new PlacedShape(createNewShapeInstance(currentShape), e.getPoint()));
|
||||
|
||||
// Simpler approach for current stateless shapes:
|
||||
drawnShapes.add(new PlacedShape(currentShape, e.getPoint()));
|
||||
repaint(); // Trigger redraw
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setCurrentShape(CompositeShape shape) {
|
||||
currentShape = shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g.create(); // Work on a copy
|
||||
|
||||
// Enable anti-aliasing for smoother graphics
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
|
||||
// Draw all the placed shapes
|
||||
for (PlacedShape ps : drawnShapes) {
|
||||
// Save the current transform state
|
||||
AffineTransform savedTransform = g2.getTransform();
|
||||
|
||||
// Translate to the position where the mouse was clicked
|
||||
g2.translate(ps.position.x, ps.position.y);
|
||||
|
||||
// Draw the shape (its draw method assumes drawing at 0,0)
|
||||
ps.shape.draw(g2);
|
||||
|
||||
// Restore the original transform for the next shape
|
||||
g2.setTransform(savedTransform);
|
||||
}
|
||||
|
||||
g2.dispose(); // Clean up the graphics copy
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ShapeDisplayer() {
|
||||
super("Shape Displayer"); // Frame title
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
// --- Create Shapes ---
|
||||
CompositeShape car = new CarShape();
|
||||
CompositeShape snowman = new SnowmanShape();
|
||||
CompositeShape house = new HouseShape(); // Your custom shape
|
||||
|
||||
// --- Create Icons ---
|
||||
carIcon = new ShapeIcon(car, ICON_WIDTH, ICON_HEIGHT);
|
||||
snowmanIcon = new ShapeIcon(snowman, ICON_WIDTH, ICON_HEIGHT);
|
||||
houseIcon = new ShapeIcon(house, ICON_WIDTH, ICON_HEIGHT);
|
||||
|
||||
// --- Create Buttons ---
|
||||
carButton = new JButton(carIcon);
|
||||
snowmanButton = new JButton(snowmanIcon);
|
||||
houseButton = new JButton(houseIcon);
|
||||
|
||||
// Configure buttons (remove text, set margins if needed)
|
||||
carButton.setMargin(new Insets(2, 2, 2, 2));
|
||||
snowmanButton.setMargin(new Insets(2, 2, 2, 2));
|
||||
houseButton.setMargin(new Insets(2, 2, 2, 2));
|
||||
|
||||
// --- Create Panels ---
|
||||
drawingPanel = new DrawingPanel();
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); // Align buttons left
|
||||
buttonPanel.add(carButton);
|
||||
buttonPanel.add(snowmanButton);
|
||||
buttonPanel.add(houseButton);
|
||||
|
||||
// --- Layout ---
|
||||
setLayout(new BorderLayout());
|
||||
add(buttonPanel, BorderLayout.NORTH);
|
||||
add(drawingPanel, BorderLayout.CENTER);
|
||||
|
||||
// --- Add Action Listeners ---
|
||||
carButton.addActionListener(e -> selectShape(car, carIcon));
|
||||
snowmanButton.addActionListener(e -> selectShape(snowman, snowmanIcon));
|
||||
houseButton.addActionListener(e -> selectShape(house, houseIcon));
|
||||
|
||||
|
||||
// Initially, no shape is selected
|
||||
deselectAllIcons();
|
||||
|
||||
pack(); // Size the frame based on components
|
||||
setLocationRelativeTo(null); // Center on screen
|
||||
}
|
||||
|
||||
// Method to handle shape selection
|
||||
private void selectShape(CompositeShape shape, ShapeIcon icon) {
|
||||
drawingPanel.setCurrentShape(shape);
|
||||
deselectAllIcons(); // Clear previous selection outline
|
||||
icon.setOutline(true); // Set outline on the selected icon
|
||||
// Force repaint of the button containing the icon
|
||||
if (icon == carIcon) carButton.repaint();
|
||||
else if (icon == snowmanIcon) snowmanButton.repaint();
|
||||
else if (icon == houseIcon) houseButton.repaint();
|
||||
|
||||
// Optional: Add a border to the button itself for clearer selection
|
||||
// resetBorders(); // Reset borders on all buttons
|
||||
// if (icon == carIcon) carButton.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
|
||||
// else if (icon == snowManIcon) snowManButton.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
|
||||
// else if (icon == houseIcon) houseButton.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
|
||||
|
||||
}
|
||||
|
||||
// Method to remove outline from all icons
|
||||
private void deselectAllIcons() {
|
||||
carIcon.setOutline(false);
|
||||
snowmanIcon.setOutline(false);
|
||||
houseIcon.setOutline(false);
|
||||
// Repaint all buttons to remove outlines
|
||||
carButton.repaint();
|
||||
snowmanButton.repaint();
|
||||
houseButton.repaint();
|
||||
}
|
||||
|
||||
// Optional: Helper to reset button borders
|
||||
// private void resetBorders() {
|
||||
// Border defaultBorder = UIManager.getBorder("Button.border");
|
||||
// carButton.setBorder(defaultBorder);
|
||||
// snowManButton.setBorder(defaultBorder);
|
||||
// houseButton.setBorder(defaultBorder);
|
||||
// }
|
||||
|
||||
|
||||
public class ShapeDisplayer {
|
||||
public static void main(String[] args) {
|
||||
// Create and configure the main frame on the Event Dispatch Thread
|
||||
// Run the GUI creation on the Event Dispatch Thread (EDT)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JFrame frame = new JFrame("Mancala");
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setSize(800, 400);
|
||||
|
||||
// Create buttons panel
|
||||
JPanel buttonPanel = new JPanel();
|
||||
buttonPanel.setLayout(new FlowLayout());
|
||||
|
||||
// Create placeholder shapes
|
||||
JButton carButton = new JButton(new PlaceholderIcon(Color.RED));
|
||||
JButton snowmanButton = new JButton(new PlaceholderIcon(Color.BLUE));
|
||||
JButton compositeButton = new JButton(new PlaceholderIcon(Color.GREEN));
|
||||
|
||||
|
||||
// Add buttons to the panel
|
||||
buttonPanel.add(carButton);
|
||||
buttonPanel.add(snowmanButton);
|
||||
buttonPanel.add(compositeButton);
|
||||
|
||||
// Add buttons panel to the top of the frame
|
||||
frame.add(buttonPanel, BorderLayout.NORTH);
|
||||
|
||||
// Add drawing panel
|
||||
JPanel drawingPanel = new JPanel();
|
||||
drawingPanel.setBackground(Color.WHITE);
|
||||
frame.add(drawingPanel, BorderLayout.CENTER);
|
||||
|
||||
// Center the frame on the screen and make it visible
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setVisible(true);
|
||||
ShapeDisplayer displayer = new ShapeDisplayer();
|
||||
displayer.setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceholderIcon implements Icon {
|
||||
private final Color color;
|
||||
private static final int WIDTH = 60;
|
||||
private static final int HEIGHT = 40;
|
||||
|
||||
public PlaceholderIcon(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
Rectangle2D.Double rect = new Rectangle2D.Double(x + 5, y + 5, WIDTH - 10, HEIGHT - 10);
|
||||
g2.setColor(color);
|
||||
g2.fill(rect);
|
||||
g2.setColor(Color.BLACK);
|
||||
g2.draw(rect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return WIDTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return HEIGHT;
|
||||
}
|
||||
}
|
||||
|
92
hw4/src/ShapeIcon.java
Normal file
92
hw4/src/ShapeIcon.java
Normal file
@ -0,0 +1,92 @@
|
||||
import javax.swing.Icon;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.AffineTransform;
|
||||
|
||||
/**
|
||||
* An icon that draws a CompositeShape.
|
||||
*/
|
||||
public class ShapeIcon implements Icon {
|
||||
private final CompositeShape shape;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private boolean outline = false; // Flag to indicate if the icon should be outlined
|
||||
|
||||
/**
|
||||
* Constructs a ShapeIcon.
|
||||
* @param shape the shape to draw
|
||||
* @param width the desired width of the icon
|
||||
* @param height the desired height of the icon
|
||||
*/
|
||||
public ShapeIcon(CompositeShape shape, int width, int height) {
|
||||
this.shape = shape;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this icon should draw an outline (e.g., when selected).
|
||||
* @param outline true to draw outline, false otherwise.
|
||||
*/
|
||||
public void setOutline(boolean outline) {
|
||||
this.outline = outline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
Graphics2D g2 = (Graphics2D) g.create(); // Work on a copy
|
||||
|
||||
// Enable anti-aliasing for smoother graphics
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// Translate to the icon's drawing position
|
||||
g2.translate(x, y);
|
||||
|
||||
// --- Centering and Scaling Logic ---
|
||||
double shapeWidth = shape.getWidth();
|
||||
double shapeHeight = shape.getHeight();
|
||||
|
||||
// Calculate scaling factor to fit shape within icon bounds
|
||||
double scaleX = (shapeWidth == 0) ? 1 : (double) width / shapeWidth;
|
||||
double scaleY = (shapeHeight == 0) ? 1 : (double) height / shapeHeight;
|
||||
double scale = Math.min(scaleX, scaleY) * 0.8; // Use 80% of space for padding
|
||||
|
||||
// Calculate translation to center the scaled shape
|
||||
double dx = (width - shapeWidth * scale) / 2.0;
|
||||
double dy = (height - shapeHeight * scale) / 2.0;
|
||||
|
||||
// Apply centering translation and scaling
|
||||
AffineTransform originalTransform = g2.getTransform();
|
||||
g2.translate(dx, dy);
|
||||
g2.scale(scale, scale);
|
||||
// --- End Centering and Scaling ---
|
||||
|
||||
// Draw the shape
|
||||
shape.draw(g2);
|
||||
|
||||
// Restore original transform before drawing outline (if any)
|
||||
g2.setTransform(originalTransform);
|
||||
|
||||
// Draw outline if selected
|
||||
if (outline) {
|
||||
// Simple rectangle outline around the icon area
|
||||
g2.setColor(java.awt.Color.GRAY);
|
||||
g2.drawRect(0, 0, width - 1, height - 1);
|
||||
g2.drawRect(1, 1, width - 3, height - 3); // Inner outline
|
||||
}
|
||||
|
||||
g2.dispose(); // Clean up the graphics copy
|
||||
}
|
||||
}
|
60
hw4/src/SnowmanShape.java
Normal file
60
hw4/src/SnowmanShape.java
Normal file
@ -0,0 +1,60 @@
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
|
||||
/**
|
||||
* A snowman shape composed of three circles.
|
||||
*/
|
||||
public class SnowmanShape implements CompositeShape {
|
||||
private int width; // Base diameter of the bottom circle
|
||||
|
||||
public SnowmanShape() {
|
||||
this(40); // Default width
|
||||
}
|
||||
|
||||
public SnowmanShape(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
// Total height is approx sum of diameters: w + 0.75w + 0.5w
|
||||
return (int) (width + width * 0.75 + width * 0.5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2) {
|
||||
// All drawing relative to (0,0) which represents the top-left
|
||||
// of the bounding box for the *icon*. The actual drawing will be centered.
|
||||
|
||||
// Calculate diameters
|
||||
double bottomDiameter = width;
|
||||
double middleDiameter = width * 0.75;
|
||||
double topDiameter = width * 0.5;
|
||||
|
||||
// Calculate positions (y-coordinates are offset from the top)
|
||||
double bottomY = topDiameter + middleDiameter;
|
||||
double middleY = topDiameter;
|
||||
double topY = 0;
|
||||
|
||||
// Center horizontally (x-coordinates)
|
||||
double bottomX = (width - bottomDiameter) / 2.0;
|
||||
double middleX = (width - middleDiameter) / 2.0;
|
||||
double topX = (width - topDiameter) / 2.0;
|
||||
|
||||
|
||||
// Create circles
|
||||
Ellipse2D.Double bottomCircle = new Ellipse2D.Double(bottomX, bottomY, bottomDiameter, bottomDiameter);
|
||||
Ellipse2D.Double middleCircle = new Ellipse2D.Double(middleX, middleY, middleDiameter, middleDiameter);
|
||||
Ellipse2D.Double topCircle = new Ellipse2D.Double(topX, topY, topDiameter, topDiameter);
|
||||
|
||||
// Draw circles
|
||||
g2.draw(bottomCircle);
|
||||
g2.draw(middleCircle);
|
||||
g2.draw(topCircle);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user