package aima.gui.swing.applications.robotics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import javax.swing.JFrame;
import aima.core.robotics.MonteCarloLocalization;
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.JavaRandomizer;
import aima.core.util.math.geom.SVGGroupParser;
import aima.gui.swing.applications.robotics.components.AnglePanel;
import aima.gui.swing.applications.robotics.components.AnglePanel.ChangeListener;
import aima.gui.swing.applications.robotics.simple.SimpleMove;
import aima.gui.swing.applications.robotics.simple.SimplePose;
import aima.gui.swing.applications.robotics.simple.SimplePoseFactory;
import aima.gui.swing.applications.robotics.simple.SimpleRangeReading;
import aima.gui.swing.applications.robotics.simple.SimpleRangeReadingFactory;
import aima.gui.swing.applications.robotics.simple.SimpleSettingsListener;
import aima.gui.swing.applications.robotics.simple.VirtualRobot;
import aima.gui.swing.applications.robotics.simple.VirtualRobotGui;
import aima.gui.swing.framework.util.GuiBase;
import aima.gui.swing.applications.robotics.components.IRobotGui;
import aima.gui.swing.applications.robotics.components.Settings;
/**
* Provides the {@link GenericMonteCarloLocalization2DApp} for the simple environment in {@code aima.gui.swing.demo.robotics.simple}.
* This environment is intended for a {@link VirtualRobot}.<br/>
* It can be used for other 2D environments by overriding {@code initialize()} and using a main method with the inheriting class.
*
* @author Arno von Borries
* @author Jan Phillip Kretzschmar
* @author Andreas Walscheid
*
*/
public class MonteCarloLocalizationApp {
protected static final File DEFAULT_SETTINGS_FILE = new File(System.getProperty("user.dir"),"mcl_settings.cache");
protected static final String RANGE_READING_ANGLES_KEY = "RANGE_READING_ANGLES";
protected static final String RANGE_READING_ANGLES_TITLE = "Range reading angles";
protected Settings settingsGui;
protected IRobotGui robotGui;
protected GenericMonteCarloLocalization2DApp<?,?,?> app;
private File settingsFile;
/**
* Starts the application.
* @param args a path to a file containing settings may be passed as the first argument. Otherwise the default settings file {@code mcl_settings.cache} in the working directory is used.
*/
public static void main(String[] args) {
File settingsFile = args.length > 0 ? new File(args[0]) : DEFAULT_SETTINGS_FILE;
MonteCarloLocalizationApp app = new MonteCarloLocalizationApp(settingsFile);
app.constructBasicApplicationFrame();
app.notifyAllListeners();
app.show();
}
protected void notifyAllListeners() {
settingsGui.notifyAllListeners();
}
/**
* Loads the provided settings file into a new {@link Settings} object.
* @param settingsFile the file containing the settings to be loaded.
* @return the {@link Settings} that were loaded.
*/
protected static Settings buildSettings(File settingsFile) {
Settings settingsGui = new Settings();
if(settingsFile != null) {
if(settingsFile.isFile() && settingsFile.canRead()) {
settingsGui.loadSettings(settingsFile);
}
}
return settingsGui;
}
/**
* This Constructor creates a application that does not loads settings from or saves settings to a file.
*/
public MonteCarloLocalizationApp() {
this(null);
}
/**
* @param settingsFile the file containing the settings for this Monte-Carlo-Localization. To that file the settings will be stored when exiting the application.
*/
public MonteCarloLocalizationApp(File settingsFile) {
GuiBase.activateSystemStyle();
GuiBase.setUIColors();
this.settingsFile = settingsFile;
settingsGui = buildSettings(settingsFile);
initialize();
robotGui.loadSettings(settingsGui);
settingsGui.buildGui();
}
/**
* Creates a {@link GenericMonteCarloLocalization2DApp} and stores it in {@code app}.<br/>
* In addition the corresponding {@link IRobotGui} is created and stored in {@code robotGui}. The function {@code robotGui.destructRobot()} will be called when the application window closes to allow closing any open connections gracefully.
*/
protected void initialize() {
SimpleSettingsListener settingsListener = new SimpleSettingsListener(settingsGui);
settingsListener.createSettings();
AnglePanel angles = new AnglePanel(RANGE_READING_ANGLES_TITLE);
settingsGui.registerSpecialSetting(RANGE_READING_ANGLES_KEY, angles);
MclCartesianPlot2D<SimplePose, SimpleMove, AbstractRangeReading> map = new MclCartesianPlot2D<SimplePose,SimpleMove,AbstractRangeReading>(new SVGGroupParser(),new SVGGroupParser(),new SimplePoseFactory(),new SimpleRangeReadingFactory());
VirtualRobot robot = new VirtualRobot(map);
robotGui = new VirtualRobotGui(robot);
MonteCarloLocalization<SimplePose,Angle,SimpleMove,AbstractRangeReading> mcl = new MonteCarloLocalization<SimplePose, Angle, SimpleMove, AbstractRangeReading>(map, new JavaRandomizer());
app = new GenericMonteCarloLocalization2DApp<SimplePose,SimpleMove,SimpleRangeReading>(mcl, map, robot, robotGui, settingsGui);
angles.setChangeListener((ChangeListener) robotGui);
settingsListener.setMap(map);
settingsListener.setMcl(mcl);
settingsListener.setRobot(robot);
}
/**
* Makes the application visible.
*/
public void show() {
app.show();
}
/**
* Creates the {@code JFrame} for the {@link AimaDemoApp}. {@code constructBasicApplicationFrame} should be called in any other case.
* @return the main frame of the application.
*/
public JFrame constructApplicationFrame() {
JFrame frame = constructBasicApplicationFrame();
//Load the virtual environment resource:
try {
app.map.loadMap(this.getClass().getResourceAsStream("virtual_environment.svg"),this.getClass().getResourceAsStream("virtual_environment.svg"));
app.settingsGui.notifyAllListeners();
app.gui.createMap();
app.gui.enableButtons(app.gui.buttonStateNormal);
/* This is bad style.
* On the other hand this is the only situation in which the GUI
* is modified from outside of GenericMonteCarloLocalization to load a map from the resources.
*/
} catch (Exception e) {
/* A Exception may be thrown when this example program is not launched from within a jar file.
* This happens because the ClassLoader does not find the requested resource.
*/
}
return frame;
}
/**
* Creates the {@code JFrame} for the application. A window listener is registered.
* @return the main frame of the application.
*/
public JFrame constructBasicApplicationFrame() {
JFrame frame = app.constructApplicationFrame();
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
robotGui.saveSettings(settingsGui);
if(settingsFile != null) settingsGui.saveSettings(settingsFile);
robotGui.destructRobot();
}
});
return frame;
}
}