⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 carmap.java

📁 CarSim is an application for the simulating the (simplified) movement of cars on a two-dimensional s
💻 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 + -