📄 carmap.java
字号:
package ope.carsim.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.ToolTipManager;
import ope.carsim.Car;
import ope.carsim.Location;
import ope.guikka.DoubleDialog;
import ope.guikka.MessageDialog;
import ope.guikka.Picture;
//////////////// NOTE TO STUDENTS ///////////////////////
// You don't have to understand the source code of this
// class when this project is first encountered.
/////////////////////////////////////////////////////////
/**
* A "car map" is the map shown in a CarSim application window.
* It may contain any number of cars which are drawn onto the map.
* A car map listens for mouse commands that signal the creation
* of cars or execution of car methods. A yellow line on the car
* map shows the most recent movement of a car.
*/
class CarMap extends JComponent implements ActionListener, MouseListener {
// Data model: the cars
private List<Car> cars; // container: all the cars currently being simulated
// GUI components:
private JPopupMenu carMenu; // fixed value
private JMenuItem fuelItem; // fixed value
private JMenuItem fillItem; // fixed value
// GUI mode indicator variables:
private Car selectedCar; // most-recent holder: most recently selected car
private boolean isMouseBeingDragged; // indicates if a mouse button is currently pressed
// Scaling values:
private double westX; // fixed value: westernmost X coordinate displayed
private double southY; // fixed value: southernmost Y coordinate displayed
private double spanX; // fixed value: size of displayed X-coordinate span (width)
private double spanY; // fixed value: size of displayed Y-coordinate span (height)
// Latest drive:
private Location latestDriveStart; // Most-recent holder: start of most recent drive
private Location latestDriveEnd; // Most-recent holder: end of most recent drive
// Other:
private int firstImageIndex; // Fixed value: the index of the car image array that is used for the first car that is created
private static final Picture[] CAR_IMAGES = new Picture[] {
new Picture("pictures/beetle.gif", 120, 68),
new Picture("pictures/4x4.gif", 140, 105),
new Picture("pictures/limo.gif", 185, 38),
new Picture("pictures/ferrari.gif", 140, 56),
new Picture("pictures/sedan.gif", 130, 71),
new Picture("pictures/truck.gif", 155, 95),
};
/**
* Creates a new car map.
*
* @param lowerLeft The lower left corner of the coordinate space to be displayed.
* @param upperRight The upper right corner of the coordinate space to be displayed.
*/
public CarMap(Location lowerLeft, Location upperRight) {
this.cars = new ArrayList<Car>();
this.createMenu();
this.selectedCar = null;
this.isMouseBeingDragged = false;
this.westX = lowerLeft.getXCoordinate();
this.southY = lowerLeft.getYCoordinate();
this.spanX = upperRight.getXCoordinate() - lowerLeft.getXCoordinate();
this.spanY = upperRight.getYCoordinate() - lowerLeft.getYCoordinate();
this.firstImageIndex = new Random().nextInt(CAR_IMAGES.length);
this.setToolTipText("");
ToolTipManager.sharedInstance().setInitialDelay(0);
ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE);
this.addMouseListener(this);
this.updateView(null, null);
}
/**
* Creates the popup menu for car actions.
*/
private void createMenu() {
this.carMenu = new JPopupMenu() {
@Override public void show(Component component, int x, int y) {
CarMap.this.isMouseBeingDragged = false;
CarMap.this.selectedCar = CarMap.this.getCarAtPixel(new Point(x, y));
if (CarMap.this.selectedCar != null) {
super.show(CarMap.this, x, y);
}
}
};
this.fuelItem = new JMenuItem("Fuel");
this.fuelItem.addActionListener(this);
this.carMenu.add(this.fuelItem);
this.fillItem = new JMenuItem("Fill up");
this.fillItem.addActionListener(this);
this.carMenu.add(this.fillItem);
}
/**
* Event handler method: reacts to selection of car popup menu items'
* by invoking the appropriate car fueling methods for the currently
* selected car.
*
* @param item the selected car popup menu item
*/
public void menuItemSelected(JMenuItem item) {
if (item == this.fuelItem) {
double wantedAmount = new DoubleDialog("Add how many liters of fuel?").waitFor();
double actualAmount = this.selectedCar.fuel(wantedAmount);
this.updateView(null, null);
if (actualAmount < wantedAmount && actualAmount == 0.0) {
new MessageDialog("Could not add any fuel.").waitFor();
} else if (actualAmount < wantedAmount) {
new MessageDialog("Could only add " + actualAmount + " liters.").waitFor();
}
} else if (item == this.fillItem) {
this.selectedCar.fillUp();
this.updateView(null, null);
}
}
/**
* Event handler method: reacts to mouse clicks on the map by either
* creating a new car or showing the car popup menu or starting a driving
* operation, depending on the number and type of mouse clicks.
*
* @param event the mouse pressing event
*/
public void mousePressed(MouseEvent event) {
if (event.getClickCount() > 1) {
this.createNewCar(event.getPoint());
} else if (event.isPopupTrigger()) {
this.carMenu.show((JComponent)event.getSource(), event.getX(), event.getY());
} else {
this.selectedCar = this.getCarAtPixel(event.getPoint());
if (this.selectedCar != null) {
this.isMouseBeingDragged = true;
}
}
}
/**
* Event handler method: reacts to mouse releases by displaying the
* car popup menu if appropriate or by driving the car if the mouse release
* happens after a car has been selected for driving.
*
* @param event the mouse release event
*/
public void mouseReleased(MouseEvent event) {
if (event.isPopupTrigger()) {
this.carMenu.show((JComponent)event.getSource(), event.getX(), event.getY());
} else if (this.isMouseBeingDragged && this.selectedCar != null) {
this.driveSelectedCar(event.getPoint());
}
}
/**
* Asks the user for new car data, creates the car and updates the map view.
*
* @param pixel location on map where new car is to be added
*/
private void createNewCar(Point pixel) {
Location location = this.getLocationAtPixel(pixel);
double consumption = new DoubleDialog("Enter fuel consumption (liters/100km) of new car:").waitFor();
double tankSize = new DoubleDialog("Enter tank size in liters:").waitFor();
double fuelInTank = new DoubleDialog("Enter amount of fuel in tank:").waitFor();
this.cars.add(new Car(consumption, tankSize, fuelInTank, location));
this.updateView(null, null);
}
/**
* Drives the currently selected car towards the given pixel.
*
* @param target pixel
*/
private void driveSelectedCar(Point target) {
this.isMouseBeingDragged = false;
Location startingLocation = this.selectedCar.getLocation();
Location destination = this.getLocationAtPixel(target);
boolean reached = this.selectedCar.drive(destination);
this.updateView(startingLocation, this.selectedCar.getLocation());
if (!reached) {
new MessageDialog("Ran out of gas.").waitFor();
}
}
/**
* Called when the state of the simulation has changed to
* update the map view data and schedule it for repainting.
*
* @param latestDriveStart the start of the most recent drive, or null if the latest action was not a driving action
* @param latestDriveEnd the end of the most recent drive, or null if the latest action was not a driving action
*/
private void updateView(Location latestDriveStart, Location latestDriveEnd) {
this.latestDriveStart = latestDriveStart;
this.latestDriveEnd = latestDriveEnd;
CarMap.this.repaint();
}
/**
* Paints the car map component, i.e. background, latest drive line,
* cars.
*
* @param graphics the component's graphics
*/
protected void paintComponent(Graphics graphics) {
Graphics2D graphics2D = (Graphics2D)graphics;
graphics2D.setColor(Color.GRAY);
graphics2D.fillRect(0, 0, this.getWidth(), this.getHeight());
if (this.latestDriveStart != null) {
Point start = this.getPixelAtLocation(this.latestDriveStart);
Point end = this.getPixelAtLocation(this.latestDriveEnd);
graphics2D.setStroke(new BasicStroke(4));
graphics2D.setColor(Color.YELLOW);
graphics2D.drawLine(start.x, start.y, end.x, end.y);
}
for (Car car : this.cars) { // most-recent holder
Point location = this.getPixelAtLocation(car.getLocation());
Icon image = this.getCarImage(car);
image.paintIcon(this, graphics2D, location.x - image.getIconWidth() / 2, location.y - image.getIconHeight() / 2);
}
}
/**
* Returns the pixel location in this component's graphics that corresponds
* to the given location in the car world.
*
* @param location a location
* @return the corresponding pixel location
*/
private Point getPixelAtLocation(Location location) {
int pixelX = (int)((location.getXCoordinate() - this.westX) / this.spanX * this.getWidth());
int pixelY = (int)(this.getHeight() * (1 - (location.getYCoordinate() - this.southY) / this.spanY));
return new Point(pixelX, pixelY);
}
/**
* Returns the car world location that corresponds to the given
* pixel in this map's graphics.
*
* @param pixel a pixel location
* @return the corresponding car world location
*/
private Location getLocationAtPixel(Point pixel) {
double x = this.westX + this.spanX * pixel.x / this.getWidth();
double y = this.southY + this.spanY * (1 - (double)pixel.y / this.getHeight());
return new Location(x, y);
}
/**
* Returns the image of a car.
*
* @param car a car on this car map
* @return car picture
*/
private Picture getCarImage(Car car) {
return CAR_IMAGES[(this.firstImageIndex + this.cars.indexOf(car)) % CAR_IMAGES.length];
}
/**
* Returns the tool tip text for a given location on the map.
* The tool tip is null if there is no car at the location,
* otherwise it is a description of the car's status.
*
* @param event an event detailing where the tooltip was requested
* @return tool tip text
*/
public String getToolTipText(MouseEvent event) {
Point pixel = event.getPoint();
Car car = this.getCarAtPixel(pixel);
if (car == null) {
return null;
} else {
return this.formatCar(car);
}
}
/**
* Formats a car for string output.
*
* @param car a car
* @return a string representation of the car
*/
private String formatCar(Car car) {
DecimalFormat formatter = new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.US));
Location location = car.getLocation();
return "Location: (" + formatter.format(location.getXCoordinate()) + ", " + formatter.format(location.getYCoordinate()) + "), " +
"tank: " + formatter.format(car.getFuelRatio()) + "%, driven: " + formatter.format(car.getKilometersDriven()) + "km.";
}
/**
* Returns the car whose image overlaps with the given pixel. If multiple
* car images overlap with the given pixel, returns the car whose image center is
* the closest to the given pixel.
*
* @param pixel a pixel location
* @return a car at the given pixel location
*/
private Car getCarAtPixel(Point pixel) {
double closestDistance = Double.MAX_VALUE;
Car closest = null;
for (Car candidate : this.cars) { // most-recent holder
Point carPixel = this.getPixelAtLocation(candidate.getLocation());
double distance = pixel.distance(carPixel);
int xDistance = Math.abs(pixel.x - carPixel.x);
int yDistance = Math.abs(pixel.y - carPixel.y);
Icon picture = this.getCarImage(candidate);
if (distance < closestDistance && xDistance <= picture.getIconWidth() / 2 && yDistance <= picture.getIconHeight() / 2) {
closestDistance = distance;
closest = candidate;
}
}
return closest;
}
/**
* Event handler method: reacts to action events which
* are assumed to come from JMenuItems by forwarding them
* to <CODE>menuItemSelected(JMenuItem)</CODE>.
*/
public void actionPerformed(ActionEvent event) {
this.menuItemSelected((JMenuItem)event.getSource());
}
/**
* Does nothing, but must exist to implement <CODE>MouseListener</CODE>.
*
* @event a mouse event
*/
public void mouseEntered(MouseEvent event) {
// do nothing
}
/**
* Does nothing, but must exist to implement <CODE>MouseListener</CODE>.
*
* @event a mouse event
*/
public void mouseClicked(MouseEvent event) {
// do nothing
}
/**
* Does nothing, but must exist to implement <CODE>MouseListener</CODE>.
*
* @event a mouse event
*/
public void mouseExited(MouseEvent event) {
// do nothing
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -