content: package aima.gui.fx.views;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Objects;
  
  import aima.core.agent.Agent;
  import aima.core.agent.Environment;
  import aima.core.agent.impl.AbstractEnvironment;
  import aima.core.agent.impl.DynamicPercept;
  import aima.core.environment.map.Map;
  import aima.core.environment.map.MapEnvironment;
  import aima.core.environment.map.MoveToAction;
  import aima.core.util.math.geom.shapes.Point2D;
  import javafx.scene.layout.Pane;
  import javafx.scene.layout.StackPane;
  import javafx.scene.paint.Color;
  import javafx.scene.shape.Circle;
  import javafx.scene.shape.Line;
  import javafx.scene.shape.Shape;
  import javafx.scene.text.Font;
  import javafx.scene.text.Text;
  
  /**
   * Environment view controller class which is specialized for map environments.
   * It adds a map environment state view to the text-based view of the
   * {@link SimpleEnvironmentViewCtrl}.
   * 
   * @author Ruediger Lunde
   */
  public class MapEnvironmentViewCtrl extends SimpleEnvironmentViewCtrl<DynamicPercept, MoveToAction> {
  
  	protected Pane envStateView;
  	protected MapEnvironment env;
  	protected String goal;
  	protected List<String> track;
  
  	public MapEnvironmentViewCtrl(StackPane viewRoot) {
  		super(viewRoot);
  		envStateView = new Pane();
  		splitPane.getItems().add(0, envStateView);
  		splitPane.setDividerPosition(0, 0.7);
  		splitPane.setStyle("-fx-background-color: white");
  		envStateView.setMinWidth(0.0);
  		envStateView.widthProperty().addListener((obs, o, n) -> update());
  		envStateView.heightProperty().addListener((obs, o, n) -> update());
  		track = new ArrayList<String>();
  	}
  
  	public void setGoal(String goal) {
  		this.goal = goal;
  	}
  
  	@Override
  	public void initialize(AbstractEnvironment<? extends DynamicPercept, ? extends MoveToAction> env) {
  		if (env instanceof MapEnvironment)
  			this.env = (MapEnvironment) env;
  		track.clear();
  		super.initialize(env);
  	}
  
  	@Override
  	protected void updateEnvStateView(Environment env) {
  		update();
  	}
  
  	protected void update() {
  		envStateView.getChildren().clear();
  		if (env != null) {
  			double scale = adjustTransform();
  			Map map = env.getMap();
  			// print connections
  			for (String loc1 : map.getLocations()) {
  				Point2D pt1 = map.getPosition(loc1);
  				for (String loc2 : map.getPossibleNextLocations(loc1)) {
  					Point2D pt2 = map.getPosition(loc2);
  					Shape line = new Line(pt1.getX(), pt1.getY(), pt2.getX(), pt2.getY());
  					line.setStroke(Color.LIGHTGRAY);
  					envStateView.getChildren().add(line);
  				}
  			}
  			// print track of first agent
  			if (!env.getAgents().isEmpty()) {
  				String aLoc = env.getAgentLocation(env.getAgents().get(0));
  				if (track.isEmpty() || !Objects.equals(track.get(track.size() - 1), aLoc))
  					track.add(aLoc);
  				for (int i = 1; i < track.size(); i++) {
  					Point2D pt1 = map.getPosition(track.get(i - 1));
  					Point2D pt2 = map.getPosition(track.get(i));
  					Shape line = new Line(pt1.getX(), pt1.getY(), pt2.getX(), pt2.getY());
  					line.setStroke(Color.RED);
  					line.setStrokeWidth(2);
  					envStateView.getChildren().add(line);
  				}
  			}
  			// print locations
  			for (String loc : map.getLocations()) {
  				Point2D point = map.getPosition(loc);
  				Text text = new Text(point.getX() + 10 / scale, point.getY(), loc);
  				text.setFont(new Font(12.0 / scale));
  				envStateView.getChildren().add(text);
  				envStateView.getChildren().add(new Circle(point.getX(), point.getY(), 2 / scale));
  			}
  			// print agent locations
  			for (Agent agent : env.getAgents()) {
  				String loc = env.getAgentLocation(agent);
  				if (loc != null) {
  					Point2D pt = map.getPosition(loc);
  					envStateView.getChildren().add(new Circle(pt.getX(), pt.getY(), 8 / scale, Color.RED));
  				}
  			}
  			// print goal
  			if (goal != null) {
  				Point2D pt = map.getPosition(goal);
  				envStateView.getChildren().add(new Circle(pt.getX(), pt.getY(), 6 / scale, Color.GREEN));
  			}
  		}
  	}
  
  	/**
  	 * Computes transforms (translations and scaling) and applies them to the
  	 * environment state view. Those transforms map location positions in the
  	 * map to screen positions in the viewer pane.
  	 * @return The scale value.
  	 */
  	private double adjustTransform() {
  		double xMin = Double.POSITIVE_INFINITY;
  		double xMax = Double.NEGATIVE_INFINITY;
  		double yMin = Double.POSITIVE_INFINITY;
  		double yMax = Double.NEGATIVE_INFINITY;
  		for (String loc : env.getMap().getLocations()) {
  			Point2D point = env.getMap().getPosition(loc);
  			xMin = Math.min(xMin, point.getX());
  			xMax = Math.max(xMax, point.getX());
  			yMin = Math.min(yMin, point.getY());
  			yMax = Math.max(yMax, point.getY());
  		}
  		double scale = Math.min((envStateView.getWidth() - 150) / (xMax - xMin),
  				(envStateView.getHeight() - 60) / (yMax - yMin));
  
  		envStateView.setTranslateX((scale * (envStateView.getWidth() - xMin - xMax) / 2.0 - 30));
  		envStateView.setTranslateY((scale * (envStateView.getHeight() - yMin - yMax) / 2.0 - 10));
  		envStateView.setScaleX(scale);
  		envStateView.setScaleY(scale);
  		return scale;
  	}
  }
  