content: package aima.gui.swing.applications.robotics.simple;
  
  import aima.core.robotics.IMclRobot;
  import aima.core.robotics.impl.datatypes.AbstractRangeReading;
  import aima.core.robotics.impl.datatypes.Angle;
  import aima.core.robotics.impl.map.MclCartesianPlot2D;
  import aima.core.util.math.geom.shapes.Vector2D;
  
  /**
   * This class implements functionality for a robot in a virtual environment.<br/>
   * It implements {@link IMclRobot}.
   * 
   * @author Arno von Borries
   * @author Jan Phillip Kretzschmar
   * @author Andreas Walscheid
   *
   */
  public final class VirtualRobot implements IMclRobot<Angle,SimpleMove,AbstractRangeReading> {
  
  	private double maxMoveDistance; 
  	private double minMoveDistance;
  	private Angle[] rangeReadingAngles;
  	private MclCartesianPlot2D<SimplePose,SimpleMove,AbstractRangeReading> map;
  	private SimplePose pose;
  	
  	/**
  	 * @param map the virtual environment in which the robot "moves" and performs range readings.
  	 */
  	public VirtualRobot(MclCartesianPlot2D<SimplePose, SimpleMove, AbstractRangeReading> map) {
  		this.map = map;
  	}
  	
  	/**
  	 * Set the angles at which range readings will be performed.
  	 * @param angles the array of angles to read ranges at.
  	 */
  	public void setRangeReadingAngles(Angle[] angles) {
  		this.rangeReadingAngles = angles;
  	}
  	
  	/**
  	 * Sets the maximum distance that the robot moves in one move.
  	 * @param distance the distance that the robot should move at max.
  	 */
  	public void setMaxMoveDistance(double distance) {
  		this.maxMoveDistance = distance;
  	}
  	
  	/**
  	 * Sets the minimum distance that the robot moves in one move.
  	 * @param distance the distance that the robot should move at least.
  	 */
  	public void setMinMoveDistance(double distance) {
  		this.minMoveDistance = distance;
  	}
  	
  	/**
  	 * Moves the robot to a new random position on the map.
  	 */
  	public void setRandomPose() {
  		pose = map.randomPose(); 
  	}
  	
  	/**
  	 * Gets the current pose of the robot.
  	 * @return the current pose of the robot.
  	 */
  	public SimplePose getPose() {
  		return pose;
  	}
  	
  	@Override
  	public AbstractRangeReading[] getRangeReadings() {
  		AbstractRangeReading[] ranges = new SimpleRangeReading[rangeReadingAngles.length];
  		for(int i=0; i<rangeReadingAngles.length;i++) {
  			SimplePose rangePose = pose.addAngle(rangeReadingAngles[i]);
  			ranges[i] = map.rayCast(rangePose);
  			ranges[i] = new SimpleRangeReading(ranges[i].getValue(), rangeReadingAngles[i]);
  		}
  		return ranges;
  	}
  	
  	/**
  	 * Lets the robot perform a move.<br/>
  	 * As it is hard for a virtual robot to see which of its movements is valid without additional functionality in the map class,<br/>
  	 * a randomly new pose on the map is selected. The rotation and the vector to the pose form the move.
  	 */
  	@Override
  	public SimpleMove performMove() {
  		SimplePose newPose;
  		Vector2D vector;
  		do {
  			newPose = map.randomPose();
  			vector = pose.getPosition().vec(newPose.getPosition());
  		} while(vector.length() > maxMoveDistance || vector.length() < minMoveDistance);
  		final double vectorAngle = vector.angleTo(Vector2D.X_VECTOR);
  		final double firstRotation = vectorAngle - pose.getHeading();
  		final double lastRotation = newPose.getHeading() - vectorAngle;
  		pose = newPose;
  		return new SimpleMove(firstRotation,vector.length(),lastRotation);
  	}
  }
  