Compare commits

...

5 Commits

20 changed files with 921 additions and 63 deletions

BIN
hw4/diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

80
hw4/diagram.uml Normal file
View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Diagram>
<ID>JAVA</ID>
<OriginalElement />
<nodes>
<node x="-90.5" y="176.0">ShapeIcon</node>
<node x="-76.0" y="-4.0">CompositeShape</node>
<node x="-50.0" y="296.0">ShapeDisplayer</node>
<node x="12.5" y="116.0">CarShape</node>
<node x="208.0" y="116.0">SnowmanShape</node>
<node x="101.5" y="176.0">HouseShape</node>
</nodes>
<notes />
<edges>
<edge source="CarShape" target="CompositeShape" relationship="REALIZATION">
<point x="62.5" y="0.0" />
<point x="173.0" y="130.0" />
<point x="173.0" y="10.0" />
<point x="91.0" y="0.0" />
</edge>
<edge source="ShapeDisplayer" target="CompositeShape" relationship="TO_ONE">
<point x="-2.0" y="-14.0" />
<point x="33.0" y="221.5" />
<point x="-108.0" y="221.5" />
<point x="-108.0" y="10.0" />
<point x="-91.0" y="0.0" />
</edge>
<edge source="ShapeDisplayer" target="SnowmanShape" relationship="CREATE">
<point x="85.0" y="0.0" />
<point x="295.0" y="310.0" />
<point x="0.0" y="14.0" />
</edge>
<edge source="ShapeDisplayer" target="HouseShape" relationship="CREATE">
<point x="77.5" y="-14.0" />
<point x="-62.5" y="14.0" />
</edge>
<edge source="ShapeIcon" target="CompositeShape" relationship="TO_ONE">
<point x="98.125" y="-0.08333333333334281" />
<point x="-298.1666666666667" y="189.91666666666666" />
<point x="-298.1666666666667" y="212.33333333333337" />
<point x="-108.0" y="212.33333333333337" />
<point x="-108.0" y="10.0" />
<point x="-91.0" y="0.0" />
</edge>
<edge source="SnowmanShape" target="CompositeShape" relationship="REALIZATION">
<point x="-87.0" y="0.0" />
<point x="173.0" y="130.0" />
<point x="173.0" y="10.0" />
<point x="91.0" y="0.0" />
</edge>
<edge source="ShapeDisplayer" target="ShapeIcon" relationship="TO_ONE">
<point x="-60.0" y="-14.0" />
<point x="0.0" y="14.0" />
</edge>
<edge source="ShapeDisplayer" target="CarShape" relationship="CREATE">
<point x="-85.0" y="0.0" />
<point x="-123.0" y="310.0" />
<point x="-123.0" y="130.0" />
<point x="-62.5" y="0.0" />
</edge>
<edge source="ShapeDisplayer" target="ShapeIcon" relationship="CREATE">
<point x="-45.0" y="-14.0" />
<point x="15.0" y="14.0" />
</edge>
<edge source="HouseShape" target="CompositeShape" relationship="REALIZATION">
<point x="73.5" y="0.0" />
<point x="399.5" y="190.0" />
<point x="399.5" y="60.5" />
<point x="173.0" y="60.5" />
<point x="173.0" y="10.0" />
<point x="91.0" y="0.0" />
</edge>
</edges>
<settings layout="Hierarchic Compact" zoom="1.2" showDependencies="true" x="233.54166666666663" y="134.91666666666666" />
<SelectedNodes />
<Categories />
<SCOPE>All</SCOPE>
<VISIBILITY>private</VISIBILITY>
</Diagram>

77
hw4/src/CarShape.java Normal file
View File

@ -0,0 +1,77 @@
import java.awt.*;
import java.awt.geom.*;
/**
* A car shape.
* Partially taken from the textbook (Cay S. Horstmann - OO Design & Patterns, 2nd ed.)
*/
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;
public CarShape() {
this(0, 0, 60); // Default position and width
}
/**
* Constructs a car shape.
*
* @param x the left of the bounding rectangle
* @param y the top of the bounding rectangle
* @param width the width of the bounding rectangle
*/
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) {
Rectangle2D.Double body = new Rectangle2D.Double(x, y + width / 6.0, width - 1, width / 6.0);
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 / 3.0,
y + width / 3.0,
width / 6.0,
width / 6.0
);
// The bottom of the front windshield
Point2D.Double r1 = new Point2D.Double(x + width / 6.0, y + width / 6.0);
// The front of the roof
Point2D.Double r2 = new Point2D.Double(x + width / 3.0, y);
// The rear of the roof
Point2D.Double r3 = new Point2D.Double(x + width * 2 / 3.0, y);
// The bottom of the rear windshield
Point2D.Double r4 = new Point2D.Double(x + width * 5 / 6.0, y + width / 6.0);
Line2D.Double frontWindshield = new Line2D.Double(r1, r2);
Line2D.Double roofTop = new Line2D.Double(r2, r3);
Line2D.Double rearWindshield = new Line2D.Double(r3, r4);
// Draw the parts
g2.draw(body);
g2.draw(frontTire);
g2.draw(rearTire);
g2.draw(frontWindshield);
g2.draw(roofTop);
g2.draw(rearWindshield);
}
}

View 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
View 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 final int width; // Width of the house body
private final 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);
}
}

View File

@ -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 final DrawingPanel drawingPanel;
private CompositeShape currentShape = null; // The shape currently selected
private final JButton carButton;
private final JButton snowmanButton;
private final JButton houseButton;
private final ShapeIcon carIcon;
private final ShapeIcon snowmanIcon;
private final 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 antialiasing 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;
}
}

90
hw4/src/ShapeIcon.java Normal file
View File

@ -0,0 +1,90 @@
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 antialiasing for smoother graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Translate to the icon's drawing position
g2.translate(x, y);
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);
// 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
View 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 final 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);
}
}

29
midterm2/.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
### IntelliJ IDEA ###
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

8
midterm2/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

6
midterm2/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
midterm2/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/midterm2.iml" filepath="$PROJECT_DIR$/midterm2.iml" />
</modules>
</component>
</project>

6
midterm2/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

11
midterm2/midterm2.iml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,96 @@
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
/**
* A class that implements an Observer object that displays a barchart view of
* a data model.
*/
public class BarFrame extends JFrame implements ChangeListener {
/**
* Constructs a BarFrame object
*
* @param dataModel the data that is displayed in the barchart
*/
public BarFrame(DataModel dataModel) {
this.dataModel = dataModel;
a = dataModel.getData();
setLocation(0, 200);
setLayout(new BorderLayout());
JLabel barLabel = new JLabel();
barLabel.setIcon(new BarIcon());
barLabel.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
double max = a.stream().max(Double::compare).orElse(1.0);
double barHeight = ICON_HEIGHT / (double) a.size();
// Find the nearest bar to the mouse click
int index = (int) (mouseY / barHeight);
if (index >= 0 && index < a.size()) {
double newValue = max * mouseX / ICON_WIDTH;
dataModel.update(index, newValue);
}
}
});
add(barLabel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
/**
* Called when the data in the model is changed.
*
* @param e the event representing the change
*/
public void stateChanged(ChangeEvent e) {
a = dataModel.getData();
repaint();
}
private class BarIcon implements Icon {
public int getIconWidth() {
return ICON_WIDTH;
}
public int getIconHeight() {
return ICON_HEIGHT;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.red);
double max = a.stream().max(Double::compare).orElse(1.0);
double barHeight = getIconHeight() / (double) a.size();
int i = 0;
for (Double value : a) {
double barLength = getIconWidth() * value / max;
Rectangle2D.Double rectangle = new Rectangle2D.Double(
0, barHeight * i, barLength, barHeight);
i++;
g2.fill(rectangle);
}
}
}
private ArrayList<Double> a;
private DataModel dataModel;
private static final int ICON_WIDTH = 200;
private static final int ICON_HEIGHT = 200;
}

View File

@ -0,0 +1,51 @@
import java.util.ArrayList;
import javax.swing.event.*;
/**
* A Subject class for the observer pattern.
*/
public class DataModel {
/**
* Constructs a DataModel object
*
* @param d the data to model
*/
public DataModel(ArrayList<Double> d) {
data = d;
listeners = new ArrayList<ChangeListener>();
}
/**
* Constructs a DataModel object
*
* @return the data in an ArrayList
*/
public ArrayList<Double> getData() {
return (ArrayList<Double>) (data.clone());
}
/**
* Attach a listener to the Model
*
* @param c the listener
*/
public void attach(ChangeListener c) {
listeners.add(c);
}
/**
* Change the data in the model at a particular location
*
* @param location the index of the field to change
* @param value the new value
*/
public void update(int location, double value) {
data.set(location, new Double(value));
for (ChangeListener l : listeners) {
l.stateChanged(new ChangeEvent(this));
}
}
ArrayList<Double> data;
ArrayList<ChangeListener> listeners;
}

14
midterm2/src/Main.java Normal file
View File

@ -0,0 +1,14 @@
/**
* Write a program that contains two frames, one with a column of text fields
* containing numbers, and another that draws a bar graph showing the values of
* the numbers. When the user edits one of the numbers, the graph should be
* redrawn. Use the observer pattern. Store the data in a model. Attach the graph
* view as a listener. When a number is updated, the number view should update the
* model, and the model should tell the graph view that a change has occured. As a
* result, the graph view should repaint itself.
*/
public class Main {
public static void main(String[] args) {
}
}

View File

@ -0,0 +1,29 @@
import java.util.ArrayList;
/**
* A class for testing an implementation of the Observer pattern.
*/
public class ObserverTester {
/**
* Creates a DataModel and attaches barchart and textfield listeners
*
* @param args unused
*/
public static void main(String[] args) {
ArrayList<Double> data = new ArrayList<Double>();
data.add(33.0);
data.add(44.0);
data.add(22.0);
data.add(22.0);
DataModel model = new DataModel(data);
TextFrame frame = new TextFrame(model);
BarFrame barFrame = new BarFrame(model);
model.attach(frame);
model.attach(barFrame);
}
}

View File

@ -0,0 +1,76 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.util.ArrayList;
/**
* A class for displaying the model as a column of textfields in a frame.
*/
public class TextFrame extends JFrame implements ChangeListener {
/**
* Constructs a JFrame that contains the textfields containing the data
* in the model.
*
* @param d the model to display
*/
public TextFrame(DataModel d) {
dataModel = d;
final Container contentPane = this.getContentPane();
setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
ArrayList<Double> a = dataModel.getData();
fieldList = new JTextField[a.size()];
// A listener for action events in the text fields
ActionListener l = new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Figure out which field generated the event
JTextField c = (JTextField) e.getSource();
int i = 0;
int count = fieldList.length;
while (i < count && fieldList[i] != c)
i++;
String text = c.getText().trim();
try {
double value = Double.parseDouble(text);
dataModel.update(i, value);
} catch (Exception exc) {
c.setText("Error. No update");
}
}
};
final int FIELD_WIDTH = 11;
for (int i = 0; i < a.size(); i++) {
JTextField textField = new JTextField(a.get(i).toString(), FIELD_WIDTH);
textField.addActionListener(l);
add(textField);
fieldList[i] = textField;
}
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
/**
* Called when the data in the model is changed.
*
* @param e the event representing the change
*/
@Override
public void stateChanged(ChangeEvent e) {
ArrayList<Double> a = dataModel.getData();
for (int i = 0; i < a.size(); i++) {
fieldList[i].setText(a.get(i).toString());
}
}
DataModel dataModel;
JTextField[] fieldList;
}