From 00ef9fe937105b9592935e32c344d6e5b88b664e Mon Sep 17 00:00:00 2001
From: BrunoDatoMeneses <bruno.dato.meneses@gmail.com>
Date: Wed, 18 Sep 2019 12:59:41 +0200
Subject: [PATCH] ADD: two window UI with speed bug

---
 AMAKFX/src/fr/irit/smac/amak/Agent.java       |    2 -
 AMAKFX/src/fr/irit/smac/amak/Amas.java        |   16 +-
 .../randomants/AntsLaunchExampleMultiUI.java  |   58 -
 .../amak/examples/randomants/HelloWorld.java  |   37 -
 .../AntExampleMutliUI.java                    |    8 +-
 .../AntHillExampleMultiUI.java                |   13 +-
 .../AntsLaunchExampleMultiUI.java             |   43 +
 .../WorldExampleMultiUI.java                  |    2 +-
 .../irit/smac/amak/ui/AmasMultiUIWindow.java  |    4 +-
 AMAKFX/src/fr/irit/smac/amak/ui/VUI.java      |   11 +-
 AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java |  585 ++++++++
 .../src/fr/irit/smac/amak/ui/VuiExplorer.java |   89 +-
 .../irit/smac/amak/ui/drawables/Drawable.java | 1223 +++++++++--------
 13 files changed, 1358 insertions(+), 733 deletions(-)
 delete mode 100644 AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntsLaunchExampleMultiUI.java
 delete mode 100644 AMAKFX/src/fr/irit/smac/amak/examples/randomants/HelloWorld.java
 rename AMAKFX/src/fr/irit/smac/amak/examples/{randomants => randomantsMultiUi}/AntExampleMutliUI.java (82%)
 rename AMAKFX/src/fr/irit/smac/amak/examples/{randomants => randomantsMultiUi}/AntHillExampleMultiUI.java (52%)
 create mode 100644 AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntsLaunchExampleMultiUI.java
 rename AMAKFX/src/fr/irit/smac/amak/examples/{randomants => randomantsMultiUi}/WorldExampleMultiUI.java (86%)
 create mode 100644 AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java

diff --git a/AMAKFX/src/fr/irit/smac/amak/Agent.java b/AMAKFX/src/fr/irit/smac/amak/Agent.java
index 5166056c..63e97500 100644
--- a/AMAKFX/src/fr/irit/smac/amak/Agent.java
+++ b/AMAKFX/src/fr/irit/smac/amak/Agent.java
@@ -22,7 +22,6 @@ import fr.irit.smac.amak.ui.AmasMultiUIWindow;
  */
 public abstract class Agent<A extends Amas<E>, E extends Environment> implements Runnable {
 	
-	public AmasMultiUIWindow amasMultiUIWindow;
 	
 	/**
 	 * Neighborhood of the agent (must refer to the same couple amas, environment
@@ -124,7 +123,6 @@ public abstract class Agent<A extends Amas<E>, E extends Environment> implements
 	}
 	
 	public Agent(AmasMultiUIWindow window, A amas, Object... params) {
-		amasMultiUIWindow = window;
 		
 		this.id = uniqueIndex++;
 		this.params = params;
diff --git a/AMAKFX/src/fr/irit/smac/amak/Amas.java b/AMAKFX/src/fr/irit/smac/amak/Amas.java
index a9240087..930f06c0 100644
--- a/AMAKFX/src/fr/irit/smac/amak/Amas.java
+++ b/AMAKFX/src/fr/irit/smac/amak/Amas.java
@@ -17,6 +17,7 @@ import fr.irit.smac.amak.ui.AmasMultiUIWindow;
 import fr.irit.smac.amak.ui.MainWindow;
 import fr.irit.smac.amak.ui.SchedulerToolbar;
 import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
 
 /**
  * This class must be overridden by multi-agent systems
@@ -29,6 +30,7 @@ import fr.irit.smac.amak.ui.VUI;
 public class Amas<E extends Environment> implements Schedulable {
 	
 	public AmasMultiUIWindow amasMultiUIWindow;
+	public VUIMulti vuiMulti;
 	
 	/**
 	 * List of agents present in the system
@@ -160,9 +162,12 @@ public class Amas<E extends Environment> implements Schedulable {
 		this.scheduler.unlock();
 	}
 	
-	public Amas(AmasMultiUIWindow window, E environment, Scheduling scheduling, Object... params) {
+	public Amas(AmasMultiUIWindow window, VUIMulti vui, E environment, Scheduling scheduling, Object... params) {
 		
 		amasMultiUIWindow = window;
+		vuiMulti = vui;
+		vuiMulti.addTabbedDefaultPanel(amasMultiUIWindow);
+		
 		executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(Configuration.allowedSimultaneousAgentsExecution);
 		if (scheduling == Scheduling.DEFAULT) {
 			//MainWindow.instance();
@@ -406,12 +411,19 @@ public class Amas<E extends Environment> implements Schedulable {
 	 */
 	protected void onUpdateRender() {
 		if(Configuration.multiUI) {
-			VUI.get(amasMultiUIWindow).updateCanvas();
+			vuiMulti.updateCanvas();
 		}else {
 			VUI.get().updateCanvas();
 		}
 		
 	}
+	
+	
+
+	
+	public VUIMulti getVUIMulti() {
+		return vuiMulti;
+	}
 
 	/**
 	 * This method is called when all agents have executed a cycle
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntsLaunchExampleMultiUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntsLaunchExampleMultiUI.java
deleted file mode 100644
index b5928773..00000000
--- a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntsLaunchExampleMultiUI.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package fr.irit.smac.amak.examples.randomants;
-
-import fr.irit.smac.amak.Configuration;
-import fr.irit.smac.amak.ui.AmasMultiUIWindow;
-import fr.irit.smac.amak.ui.MainWindow;
-import javafx.application.Application;
-import javafx.scene.control.Label;
-import javafx.scene.layout.Pane;
-import javafx.stage.Stage;
-
-public class AntsLaunchExampleMultiUI extends Application{
-
-	
-	
-	
-	
-	
-	
-	public static void main (String[] args) {
-		
-		
-		Application.launch(args);
-		
-		
-		
-		//MainWindow.instance();		
-		
-		
-	}
-
-	@Override
-	public void start(Stage primaryStage) throws Exception {
-		
-		Configuration.multiUI=true;
-		
-		AmasMultiUIWindow window = new AmasMultiUIWindow();
-		AmasMultiUIWindow window2 = new AmasMultiUIWindow();
-		
-		WorldExampleMultiUI env = new WorldExampleMultiUI(window);
-		WorldExampleMultiUI env2 = new WorldExampleMultiUI(window2);
-		
-
-		new AntHillExampleMultiUI(window, env);
-		new AntHillExampleMultiUI(window2, env2);
-		
-		Pane panel = new Pane();
-		panel.getChildren().add(new Label("AntHill simulation\n"
-				+ "Ants move randomly.\n"
-				+ "This demo is here to show AMAK rendering capacities.\n"));
-		window.setLeftPanel(panel);
-		
-		Pane panel2 = new Pane();
-		panel2.getChildren().add(new Label("AntHill simulation\n"
-				+ "Ants move randomly.\n"
-				+ "This demo is here to show AMAK rendering capacities.\n"));
-		window2.setLeftPanel(panel2);
-	}
-}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/HelloWorld.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomants/HelloWorld.java
deleted file mode 100644
index a1a015c3..00000000
--- a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/HelloWorld.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package fr.irit.smac.amak.examples.randomants;
-
-import javafx.application.Application;
-import javafx.scene.Group;
-import javafx.scene.Scene;
-import javafx.scene.control.Button;
-import javafx.scene.text.Font;
-import javafx.scene.text.Text;
-import javafx.stage.Stage;
-
-public class HelloWorld extends Application {
-
-    @Override public void start(Stage stage) {
-        Text text = new Text(10, 40, "Hello World!");
-        text.setFont(new Font(40));
-        Scene scene = new Scene(new Group(text));
-
-        stage.setTitle("Welcome to JavaFX!"); 
-        stage.setScene(scene); 
-        stage.sizeToScene(); 
-        
-        
-
-        Stage stage2 = new Stage();
-        stage2.setScene(new Scene(new Group(new Button("my second window"))));
-        stage2.show();
-
-        stage.show(); 
-    }
-    
-    
-
-    public static void main(String[] args) {
-        Application.launch(args);
-    }
-}
-
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntExampleMutliUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntExampleMutliUI.java
similarity index 82%
rename from AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntExampleMutliUI.java
rename to AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntExampleMutliUI.java
index 62c06ef3..4b09958e 100644
--- a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntExampleMutliUI.java
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntExampleMutliUI.java
@@ -1,4 +1,4 @@
-package fr.irit.smac.amak.examples.randomants;
+package fr.irit.smac.amak.examples.randomantsMultiUi;
 
 import fr.irit.smac.amak.Agent;
 import fr.irit.smac.amak.ui.AmasMultiUIWindow;
@@ -37,7 +37,6 @@ public class AntExampleMutliUI extends Agent<AntHillExampleMultiUI, WorldExample
 	 */
 	public AntExampleMutliUI(AmasMultiUIWindow window, AntHillExampleMultiUI amas, double startX, double startY) {
 		super(window, amas, startX, startY);
-		System.out.println(amasMultiUIWindow + "            ------------- AntExampleMutliUI 42");
 	}
 	@Override
 	public void onInitialization() {
@@ -47,8 +46,7 @@ public class AntExampleMutliUI extends Agent<AntHillExampleMultiUI, WorldExample
 
 	@Override
 	protected void onRenderingInitialization() {
-		System.out.println(amasMultiUIWindow + "            ------------- AntExampleMutliUI 52");
-		image = VUI.get(amasMultiUIWindow).createAndAddImage(dx, dy, "file:resources/ant.png");
+		image =  getAmas().getVUIMulti().createAndAddImage(dx, dy, "file:resources/ant.png");
 		image.setName("Ant "+getId());
 	}
 
@@ -76,7 +74,7 @@ public class AntExampleMutliUI extends Agent<AntHillExampleMultiUI, WorldExample
 		}
 
 		if (amas.getEnvironment().getRandom().nextDouble() < 0.001) {
-			new AntExampleMutliUI(amasMultiUIWindow, getAmas(), dx, dy);
+			new AntExampleMutliUI(getAmas().amasMultiUIWindow, getAmas(), dx, dy);
 		}
 	}
 
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntHillExampleMultiUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntHillExampleMultiUI.java
similarity index 52%
rename from AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntHillExampleMultiUI.java
rename to AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntHillExampleMultiUI.java
index fe856492..344561af 100644
--- a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/AntHillExampleMultiUI.java
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntHillExampleMultiUI.java
@@ -1,31 +1,30 @@
-package fr.irit.smac.amak.examples.randomants;
+package fr.irit.smac.amak.examples.randomantsMultiUi;
 
 import fr.irit.smac.amak.Amas;
 import fr.irit.smac.amak.Scheduling;
 import fr.irit.smac.amak.tools.RunLaterHelper;
 import fr.irit.smac.amak.ui.AmasMultiUIWindow;
 import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
 import fr.irit.smac.amak.ui.drawables.DrawableString;
 
 public class AntHillExampleMultiUI extends Amas<WorldExampleMultiUI> {
 
 	private DrawableString antsCountLabel;
 
-	public AntHillExampleMultiUI(AmasMultiUIWindow window, WorldExampleMultiUI env) {
-		super(window, env, Scheduling.DEFAULT);
-		System.out.println(window + "            ------------- AntHillExampleMultiUI 16");
+	public AntHillExampleMultiUI(AmasMultiUIWindow window, VUIMulti vui, WorldExampleMultiUI env) {
+		super(window, vui, env, Scheduling.DEFAULT);
 	}
 
 	@Override
 	protected void onRenderingInitialization() {
-		VUI.get(amasMultiUIWindow).createAndAddImage(20, 20, "file:Resources/ant.png").setFixed().setLayer(10).setShowInExplorer(false);
-		antsCountLabel = (DrawableString) VUI.get().createAndAddString(45, 25, "Ants count").setFixed().setLayer(10).setShowInExplorer(false);
+		vuiMulti.createAndAddImage(20, 20, "file:Resources/ant.png").setFixed().setLayer(10).setShowInExplorer(false);
+		antsCountLabel = (DrawableString) vuiMulti.createAndAddString(45, 25, "Ants count").setFixed().setLayer(10).setShowInExplorer(false);
 	}
 
 	@Override
 	protected void onInitialAgentsCreation() {
 		for (int i = 0; i < 50; i++) {
-			System.out.println(amasMultiUIWindow + "            ------------- AntHillExampleMultiUI 28");
 			new AntExampleMutliUI(amasMultiUIWindow, this, 0, 0);
 		}
 			
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntsLaunchExampleMultiUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntsLaunchExampleMultiUI.java
new file mode 100644
index 00000000..51e5a90c
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/AntsLaunchExampleMultiUI.java
@@ -0,0 +1,43 @@
+package fr.irit.smac.amak.examples.randomantsMultiUi;
+
+import fr.irit.smac.amak.Configuration;
+import fr.irit.smac.amak.ui.AmasMultiUIWindow;
+import fr.irit.smac.amak.ui.MainWindow;
+import fr.irit.smac.amak.ui.VUIMulti;
+import javafx.application.Application;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+
+public class AntsLaunchExampleMultiUI extends Application{
+
+	
+	public static void main (String[] args) {
+		
+		
+		Application.launch(args);
+		
+	
+	}
+
+	@Override
+	public void start(Stage primaryStage) throws Exception {
+		
+		Configuration.multiUI=true;
+		
+		
+		AmasMultiUIWindow window = new AmasMultiUIWindow("Random Ants Multi UI 1");
+		AmasMultiUIWindow window2 = new AmasMultiUIWindow("Random Ants Multi UI 2");
+		
+		
+		WorldExampleMultiUI env = new WorldExampleMultiUI(window);
+		WorldExampleMultiUI env2 = new WorldExampleMultiUI(window2);
+		
+
+		AntHillExampleMultiUI amas1 = new AntHillExampleMultiUI(window, VUIMulti.getDefault(), env);
+		
+		
+		AntHillExampleMultiUI amas2 = new AntHillExampleMultiUI(window2, VUIMulti.getDefault(), env2);
+
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/WorldExampleMultiUI.java b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/WorldExampleMultiUI.java
similarity index 86%
rename from AMAKFX/src/fr/irit/smac/amak/examples/randomants/WorldExampleMultiUI.java
rename to AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/WorldExampleMultiUI.java
index 22f9e691..ffb5c0f1 100644
--- a/AMAKFX/src/fr/irit/smac/amak/examples/randomants/WorldExampleMultiUI.java
+++ b/AMAKFX/src/fr/irit/smac/amak/examples/randomantsMultiUi/WorldExampleMultiUI.java
@@ -1,4 +1,4 @@
-package fr.irit.smac.amak.examples.randomants;
+package fr.irit.smac.amak.examples.randomantsMultiUi;
 
 import fr.irit.smac.amak.Environment;
 import fr.irit.smac.amak.Scheduling;
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/AmasMultiUIWindow.java b/AMAKFX/src/fr/irit/smac/amak/ui/AmasMultiUIWindow.java
index 53ca4e46..7869e963 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/AmasMultiUIWindow.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/AmasMultiUIWindow.java
@@ -74,7 +74,7 @@ public class AmasMultiUIWindow extends Stage{
 	 *             if the MainWindow has already been instantiated. This constructor
 	 *             should be used by the Application of JavaFX only.
 	 */
-	public AmasMultiUIWindow() {
+	public AmasMultiUIWindow(String title) {
 		synchronized (startEnded) {
 			VBox root = new VBox();
 			
@@ -89,7 +89,7 @@ public class AmasMultiUIWindow extends Stage{
 			VBox.setVgrow(organizationPane, Priority.ALWAYS);
 			
 			// Creation of scene
-			this.setTitle("AMAS");
+			this.setTitle(title);
 			Scene scene = new Scene(root, 450, 300);
 			//stage = primaryStage;
 			this.setScene(scene);
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java b/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java
index e396e9bb..20376810 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/VUI.java
@@ -146,16 +146,7 @@ public class VUI {
 		return get("Default");
 	}
 	
-	public static VUI get(AmasMultiUIWindow window) {
-		if(!instances.containsKey("Default")) {
-			System.out.println(window + "            ------------- VUI 151");
-			System.out.println(get("Default"));
-			System.out.println(get("Default").getPanel());
-			window.addTabbedPanel("Default VUI", get("Default").getPanel());
-		}
-			
-		return get("Default");
-	}
+	
 
 	/**
 	 * Create or get a VUI.<br/>
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java b/AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java
new file mode 100644
index 00000000..501a6d6c
--- /dev/null
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/VUIMulti.java
@@ -0,0 +1,585 @@
+package fr.irit.smac.amak.ui;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.ReentrantLock;
+
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.drawables.Drawable;
+import fr.irit.smac.amak.ui.drawables.DrawableImage;
+import fr.irit.smac.amak.ui.drawables.DrawablePoint;
+import fr.irit.smac.amak.ui.drawables.DrawableRectangle;
+import fr.irit.smac.amak.ui.drawables.DrawableString;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToolBar;
+import javafx.scene.control.Tooltip;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.input.ScrollEvent;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.BackgroundFill;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.CornerRadii;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.TextAlignment;
+
+/**
+ * 
+ * Vectorial UI: This class allows to create dynamic rendering with zoom and
+ * move capacities
+ * 
+ * @author of original version (the Swing one) perles
+ *
+ */
+public class VUIMulti {
+	/**
+	 * The toolbar of the VUI.
+	 */
+	public ToolBar toolbar;
+	
+	/**
+	 * The VUI explorer.
+	 * @see VuiExplorer
+	 */
+	private VuiExplorer vuiExplorer;
+	
+	/**
+	 * List of objects currently being drawn by the VUI
+	 */
+	private List<Drawable> drawables = new LinkedList<>();
+	/**
+	 * Lock to avoid concurrent modification on the list {@link #drawables}
+	 */
+	private ReentrantLock drawablesLock = new ReentrantLock();
+
+	/**
+	 * A static map to facilitate access to different instances of VUI
+	 */
+	//private static Map<String, VUIMulti> instances = new HashMap<>();
+
+	/**
+	 * The horizontal offset of the drawing zone. Used to allow the user to move the
+	 * view.
+	 */
+	private double worldOffsetX;
+
+	/**
+	 * The vertical offset of the drawing zone. Used to allow the user to move the
+	 * view.
+	 */
+	private double worldOffsetY;
+
+	/**
+	 * The last horizontal position of the mouse when dragging
+	 */
+	protected Double lastDragX;
+
+	/**
+	 * The last vertical position of the mouse when dragging
+	 */
+	protected Double lastDragY;
+
+	/**
+	 * The main panel of the VUI
+	 */
+	private BorderPane panel;
+
+	/**
+	 * The canvas on which all is drawn
+	 */
+	private Pane canvas;
+
+	/**
+	 * Label aiming at showing information about the VUI (zoom and offset)
+	 */
+	private Label statusLabel;
+
+	/**
+	 * The default value of the {@link #zoom}
+	 */
+	private double defaultZoom = 100;
+	/**
+	 * The default horizontal position of the view
+	 */
+	private double defaultWorldCenterX = 0;
+	/**
+	 * The default vertical position of the view
+	 */
+	private double defaultWorldCenterY = 0;
+	/**
+	 * The value of the zoom. 100 means 1/1 scale
+	 */
+	protected double zoom = defaultZoom;
+
+	/**
+	 * The horizontal position of the view
+	 */
+	private double worldCenterX = defaultWorldCenterX;
+
+	/**
+	 * The vertical position of the view
+	 */
+	private double worldCenterY = defaultWorldCenterY;
+
+	/**
+	 * Used to be sure that only one thread at the same time create a VUI
+	 */
+	private static ReentrantLock instanceLock = new ReentrantLock();
+
+	/**
+	 * Get the default VUI
+	 * 
+	 * @return the default VUI
+	 */
+
+	
+	public static VUIMulti getDefault() {		
+		return get("Default");
+	}
+	
+	public void addTabbedDefaultPanel(AmasMultiUIWindow window) {
+			window.addTabbedPanel("Default VUI", getPanel());
+	}
+
+	/**
+	 * Create or get a VUI.<br/>
+	 * You have add its panel to the MainWindow yourself.
+	 * 
+	 * @param id
+	 *            The unique id of the VUI
+	 * @return The VUI with id "id"
+	 */
+	public static VUIMulti get(String id) {
+		instanceLock.lock();
+		VUIMulti value = new VUIMulti(id);
+		instanceLock.unlock();
+		return value;
+	}
+
+	
+	
+	public VUIMulti() {
+		
+	}
+	
+	/**
+	 * Constructor of the VUI. This one is private as it can only be created through
+	 * static method.
+	 * 
+	 * @param title
+	 *            The title used for the vui
+	 */
+	private VUIMulti(String title) {
+		
+		Semaphore done = new Semaphore(0);
+		RunLaterHelper.runLater(() -> {
+			panel = new BorderPane();
+
+			toolbar = new ToolBar();
+			statusLabel = new Label("status");
+			statusLabel.setTextAlignment(TextAlignment.LEFT);
+			toolbar.getItems().add(statusLabel);
+			panel.setBottom(toolbar);
+
+			Button resetButton = new Button("Reset");
+			resetButton.setOnAction(new EventHandler<ActionEvent>() {
+				@Override
+				public void handle(ActionEvent event) {
+					zoom = defaultZoom;
+					worldCenterX = defaultWorldCenterX;
+					worldCenterY = defaultWorldCenterY;
+					updateCanvas();
+				}
+			});
+			toolbar.getItems().add(resetButton);
+
+			canvas = new Pane();
+			canvas.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
+			// clip the canvas (avoid drawing outside of it)
+			Rectangle clip = new Rectangle(0, 0, 0, 0);
+			clip.widthProperty().bind(canvas.widthProperty());
+			clip.heightProperty().bind(canvas.heightProperty());
+			canvas.setClip(clip);
+			
+			canvas.setOnMousePressed(new EventHandler<MouseEvent>() {
+				@Override
+				public void handle(MouseEvent event) {
+					lastDragX = event.getX();
+					lastDragY = event.getY();
+				}
+			});
+			canvas.setOnMouseExited(new EventHandler<MouseEvent>() {
+				@Override
+				public void handle(MouseEvent event) {
+					lastDragX = null;
+					lastDragY = null;
+				}
+			});
+			canvas.setOnMouseDragged(new EventHandler<MouseEvent>() {
+				@Override
+				public void handle(MouseEvent event) {
+					try {
+						double transX = screenToWorldDistance(event.getX() - lastDragX);
+						double transY = screenToWorldDistance(event.getY() - lastDragY);
+						worldCenterX += transX;
+						worldCenterY += transY;
+						worldOffsetX += transX;
+						worldOffsetY += transY;
+						lastDragX = event.getX();
+						lastDragY = event.getY();
+						updateCanvas();
+					} catch (Exception ez) {
+						// Catch exception occurring when mouse is out of the canvas
+					}
+				}
+			});
+
+			canvas.setOnScroll(new EventHandler<ScrollEvent>() {
+				@Override
+				public void handle(ScrollEvent event) {
+					double wdx = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
+					double wdy = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
+					zoom += event.getDeltaY() / event.getMultiplierY() * 10;
+					if (zoom < 10)
+						zoom = 10;
+
+					double wdx2 = screenToWorldDistance(canvas.getWidth() / 2 - event.getX());
+					double wdy2 = screenToWorldDistance(canvas.getHeight() / 2 - event.getY());
+					worldCenterX -= wdx2 - wdx;
+					worldCenterY -= wdy2 - wdy;
+					updateCanvas();
+				}
+			});
+
+			panel.setCenter(canvas);
+			
+			//add VuiExplorer
+			vuiExplorer = new VuiExplorer(this);
+			panel.setLeft(vuiExplorer);
+			Button veButton = new Button("VUI explorer");
+			veButton.setOnAction(new EventHandler<ActionEvent>() {
+				@Override
+				public void handle(ActionEvent event) {
+					panel.setLeft(vuiExplorer);
+				}
+			});
+			veButton.setTooltip(new Tooltip("Show the VUI explorer if it was hidden."));
+			toolbar.getItems().add(veButton);
+			
+			done.release();
+			
+		});
+		try {
+			done.acquire();
+		} catch (InterruptedException e) {
+			System.err.println("Failed to make sure that the VUI is correctly initialized.");
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Convert a distance in the world to its equivalent on the screen
+	 * 
+	 * @param d
+	 *            the in world distance
+	 * @return the on screen distance
+	 */
+	public double worldToScreenDistance(double d) {
+		return d * getZoomFactor();
+	}
+
+	/**
+	 * Convert a distance on the screen to its equivalent in the world
+	 * 
+	 * @param d
+	 *            the on screen distance
+	 * @return the in world distance
+	 */
+	public double screenToWorldDistance(double d) {
+		return d / getZoomFactor();
+	}
+
+	/**
+	 * Convert a X in the world to its equivalent on the screen
+	 * 
+	 * @param x
+	 *            the X in world
+	 *
+	 * @return the X on screen distance
+	 */
+	public double worldToScreenX(double x) {
+		return (x + getWorldOffsetX()) * getZoomFactor();
+	}
+
+	/**
+	 * A value that must be multiplied to scale objects
+	 * 
+	 * @return the zoom factor
+	 */
+	public double getZoomFactor() {
+		return zoom / 100;
+	}
+
+	/**
+	 * Convert a Y in the world to its equivalent on the screen
+	 * 
+	 * @param y
+	 *            the Y in world
+	 *
+	 * @return the Y on screen distance
+	 */
+	public double worldToScreenY(double y) {
+		return (-y + getWorldOffsetY()) * getZoomFactor();
+	}
+
+	/**
+	 * Convert a X on the screen to its equivalent in the world
+	 * 
+	 * @param x
+	 *            the X on screen
+	 *
+	 * @return the X in the world distance
+	 */
+	public double screenToWorldX(double x) {
+		return x / getZoomFactor() - getWorldOffsetX();
+	}
+
+	/**
+	 * Convert a Y on the screen to its equivalent in the world
+	 * 
+	 * @param y
+	 *            the Y on screen
+	 *
+	 * @return the Y in the world distance
+	 */
+	public double screenToWorldY(double y) {
+		return -y / getZoomFactor() + getWorldOffsetY();
+	}
+
+	/**
+	 * Add a drawable to the VUI.
+	 * 
+	 * @param d
+	 *            the new drawable
+	 */
+	public void add(Drawable d) {
+		
+		d.setVUIMulti(this);
+		RunLaterHelper.runLater(()-> canvas.getChildren().add(d.getNode()));
+		drawablesLock.lock();
+		drawables.add(d);
+		drawablesLock.unlock();
+		updateCanvas();
+	}
+	
+	/**
+	 * Remove a drawable from the VUI.
+	 * 
+	 * @param d
+	 *            the new drawable
+	 */
+	public void remove(Drawable d) {
+		drawablesLock.lock();
+		drawables.remove(d);
+		drawablesLock.unlock();
+		RunLaterHelper.runLater(()-> canvas.getChildren().remove(d.getNode()));
+		updateCanvas();
+	}
+	
+	/**
+	 * Remove all drawables from the VUI.
+	 */
+	public void clear() {
+		drawablesLock.lock();
+		drawables.clear();
+		RunLaterHelper.runLater(()->canvas.getChildren().clear());
+		drawablesLock.unlock();
+	}
+
+	/**
+	 * Refresh the canvas
+	 */
+	public void updateCanvas() {
+		final double w = canvas.getWidth();
+		final double h = canvas.getHeight();
+
+		setWorldOffsetX(worldCenterX + screenToWorldDistance(w / 2));
+		setWorldOffsetY(worldCenterY + screenToWorldDistance(h / 2));
+
+		drawablesLock.lock();
+		Collections.sort(drawables, (o1, o2) -> o1.getLayer() - o2.getLayer());
+		for (Drawable d : drawables)
+			RunLaterHelper.runLater(()-> d.onDraw());
+		drawablesLock.unlock();
+
+		RunLaterHelper.runLater(() -> {
+			statusLabel.setText(String.format("Zoom: %.2f Center: (%.2f,%.2f)", zoom, worldCenterX, worldCenterY));
+		});
+		
+		RunLaterHelper.runLater(()-> vuiExplorer.update(true));
+	}
+
+	/**
+	 * Get the width of the canvas
+	 * 
+	 * @return the canvas width
+	 */
+	public double getCanvasWidth() {
+		return canvas.getWidth();
+	}
+
+	/**
+	 * Get the height of the canvas
+	 * 
+	 * @return the canvas height
+	 */
+	public double getCanvasHeight() {
+		return canvas.getHeight();
+	}
+
+	/**
+	 * Get the value that must be added to the X coordinate of in world object
+	 * 
+	 * @return the X offset
+	 */
+	public double getWorldOffsetX() {
+		return worldOffsetX;
+	}
+
+	/**
+	 * Set the value that must be added to the X coordinate of in world object
+	 * 
+	 * @param offsetX
+	 *            the X offset
+	 */
+	public void setWorldOffsetX(double offsetX) {
+		this.worldOffsetX = offsetX;
+	}
+
+	/**
+	 * Get the value that must be added to the Y coordinate of in world object
+	 * 
+	 * @return the Y offset
+	 */
+	public double getWorldOffsetY() {
+		return worldOffsetY;
+	}
+
+	/**
+	 * Set the value that must be added to the Y coordinate of in world object
+	 * 
+	 * @param offsetY
+	 *            the Y offset
+	 */
+	public void setWorldOffsetY(double offsetY) {
+		this.worldOffsetY = offsetY;
+	}
+
+	/**
+	 * Create a point and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @return the point object
+	 */
+	public DrawablePoint createAndAddPoint(double dx, double dy) {
+		DrawablePoint drawablePoint = new DrawablePoint(dx, dy);
+		add(drawablePoint);
+		return drawablePoint;
+	}
+
+	/**
+	 * Create a rectangle and start rendering it
+	 * 
+	 * @param x
+	 *            the x coordinate
+	 * @param y
+	 *            the y coordinate
+	 * @param w
+	 *            the width
+	 * @param h
+	 *            the height
+	 * @return the rectangle object
+	 */
+	public DrawableRectangle createAndAddRectangle(double x, double y, double w, double h) {
+		DrawableRectangle d = new DrawableRectangle(x, y, w, h);
+		add(d);
+		return d;
+	}
+
+	/**
+	 * Set the default configuration of the view
+	 * 
+	 * @param zoom
+	 *            the initial zoom value
+	 * @param worldCenterX
+	 *            the initial X center value
+	 * @param worldCenterY
+	 *            the initial Y center value
+	 */
+	public void setDefaultView(double zoom, double worldCenterX, double worldCenterY) {
+		this.zoom = zoom;
+		this.worldCenterX = worldCenterX;
+		this.worldCenterY = worldCenterY;
+		this.defaultZoom = zoom;
+		this.defaultWorldCenterX = worldCenterX;
+		this.defaultWorldCenterY = worldCenterY;
+	}
+
+	/**
+	 * Create an image and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @param filename
+	 *            the filename of the image
+	 * @return the created image
+	 */
+	public DrawableImage createAndAddImage(double dx, double dy, String filename) {
+		DrawableImage image = new DrawableImage(dx, dy, filename);
+		add(image);
+		return image;
+	}
+
+	/**
+	 * Create a string and start rendering it
+	 * 
+	 * @param dx
+	 *            the x coordinate
+	 * @param dy
+	 *            the y coordinate
+	 * @param text
+	 *            the text to display
+	 * @return the created string
+	 */
+	public DrawableString createAndAddString(int dx, int dy, String text) {
+		DrawableString ds = new DrawableString(dx, dy, text);
+		add(ds);
+		return ds;
+	}
+
+	public Pane getCanvas() {
+		return canvas;
+	}
+	
+	public BorderPane getPanel() {
+		return panel;
+	}
+	
+	public List<Drawable> getDrawables() {
+		return drawables;
+	}
+}
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java b/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java
index 957e6a8a..dbb94088 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/VuiExplorer.java
@@ -23,13 +23,14 @@ import javafx.scene.layout.VBox;
 import javafx.scene.paint.Color;
 
 /**
- * A piece of GUI allowing to see and look for contexts.
+ * A piece of GUI allowing to see and look for agents.
  * @author Hugo
  *
  */
 public class VuiExplorer extends ScrollPane {
 
-	private VUI vui;
+	private VUI vui = null;
+	private VUIMulti vuiMulti = null;
 	
 	private VBox vbox;
 	private TitledPane contextsPane;
@@ -102,6 +103,74 @@ public class VuiExplorer extends ScrollPane {
 		RunLaterHelper.runLater(()->vui.getPanel().setLeft(this));
 
 	}
+	
+	
+	public VuiExplorer(VUIMulti vuiMlt) {
+		this.vuiMulti = vuiMlt;
+
+		this.setMaxWidth(Double.MAX_VALUE);
+		this.setMaxHeight(Double.MAX_VALUE);
+
+		vbox = new VBox();
+		vbox.setFillWidth(true);
+		this.setContent(vbox);
+
+		// refresh, close, and collapseAll button
+		HBox hboxButtons = new HBox();
+		Button refresh = new Button("Refresh");
+		refresh.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				update();
+			}
+		});
+		Button close = new Button("Close");
+		close.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				vuiMulti.getPanel().setLeft(null);
+			}
+		});
+		Button collapseAll = new Button("Collapse all");
+		collapseAll.setOnAction(new EventHandler<ActionEvent>() {
+			@Override
+			public void handle(ActionEvent event) {
+				collapseAll();
+			}
+		});
+		hboxButtons.getChildren().addAll(refresh, close, collapseAll);
+		
+		// check box
+		autoRefresh = new CheckBox("Auto refresh");
+		autoRefresh.setTooltip(new Tooltip("Try to automatically refresh the VUI explorer when the VUI is updated."));
+		
+		// search bar
+		search = new TextField();
+		search.setPromptText("regular expression");
+		// update list on change
+		search.textProperty().addListener(new ChangeListener<String>() {
+			@Override
+			public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+				search.setStyle(null);
+				try {
+					update();
+				} catch (PatternSyntaxException ex) {
+					search.setStyle("-fx-border-color: red;");
+				}
+			}
+		});
+
+		cpVBox = new VBox();
+		contextsPane = new TitledPane("Drawables", cpVBox);
+
+		vbox.getChildren().addAll(hboxButtons, autoRefresh, search, contextsPane);
+		update();
+		
+		// Add to vui
+		RunLaterHelper.runLater(()->vuiMulti.getPanel().setLeft(this));
+
+	}
+	
 
 	public void update(boolean auto) {
 		if(auto && autoRefresh.isSelected()) {
@@ -113,7 +182,13 @@ public class VuiExplorer extends ScrollPane {
 	 * Update the list of context
 	 */
 	public void update() {
-		List<Drawable> drawableList = vui.getDrawables();
+		List<Drawable> drawableList = null;
+		if(vui != null) {
+			drawableList = vui.getDrawables();
+		}
+		if(vuiMulti != null) {
+			drawableList = vuiMulti.getDrawables();
+		}
 		// crude color sort
 		drawableList.sort(new Comparator<Drawable>() {
 			@Override
@@ -142,7 +217,13 @@ public class VuiExplorer extends ScrollPane {
 	}
 	
 	private void collapseAll() {
-		List<Drawable> drawableList = vui.getDrawables();
+		List<Drawable> drawableList = null;
+		if(vui != null) {
+			drawableList = vui.getDrawables();
+		}
+		if(vuiMulti != null) {
+			drawableList = vuiMulti.getDrawables();
+		}
 		for(Drawable d : drawableList) {
 			if(d.showInExplorer && d.isVisible()) {
 				Drawable mini = d.getLinkedDrawable("mini");
diff --git a/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java b/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java
index ec5ce1fe..377377c5 100644
--- a/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java
+++ b/AMAKFX/src/fr/irit/smac/amak/ui/drawables/Drawable.java
@@ -1,605 +1,618 @@
-package fr.irit.smac.amak.ui.drawables;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import fr.irit.smac.amak.tools.RunLaterHelper;
-import fr.irit.smac.amak.ui.VUI;
-import javafx.event.Event;
-import javafx.event.EventHandler;
-import javafx.scene.Node;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.paint.Color;
-
-/**
- * A drawable is an object that can be drawn by the {@link VUI} system
- * 
- * @author of original version (the Swing one) Alexandre Perles
- *
- */
-public abstract class Drawable {
-	
-	/**
-	 * If this drawable should be shown in the vui explorer.
-	 */
-	public boolean showInExplorer = true;
-
-	/**
-	 * Default style applied to drawable node.
-	 */
-	protected static String defaultStyle = "-fx-stroke: black; -fx-stroke-width: 1;";
-	
-	/**
-	 * Linked drawables will receive the same event when using dispatchEvent.
-	 */
-	private HashMap<String, Drawable> linkedDrawable = new HashMap<String, Drawable>();
-	
-	/**
-	 * The horizontal position of the object
-	 */
-	protected double x;
-	/**
-	 * The vertical position of the object
-	 */
-	private double y;
-	/**
-	 * The width of the object
-	 */
-	private double width;
-	
-	/**
-	 * The real height
-	 */
-	protected double height;
-
-	/**
-	 * Does only the border must be displayed ?
-	 */
-	protected boolean strokeMode = false;
-
-	/**
-	 * The color of the object
-	 */
-	protected Color color = Color.BLACK;
-	
-	/**
-	 * The VUI on which the object is drawn
-	 */
-	protected VUI vui;
-	
-	/**
-	 * The order of drawing. An higher layer is drawn on top of the other.
-	 */
-	protected int layer = 0;
-	
-	/**
-	 * The angle of rotation of the object
-	 */
-	private double angle;
-	
-	/**
-	 * A fixed object doesn't move with the view. It can be used for HUD
-	 */
-	private boolean fixed = false;
-	
-	/**
-	 * Must the object be drawn ?
-	 */
-	private boolean visible = true;
-	
-	/**
-	 * Is the drawable expanded ?
-	 * @see Drawable#onMouseClick(MouseEvent)
-	 * @see Drawable#expand()
-	 * @see Drawable#collapse()
-	 */
-	private boolean expanded = false;
-	
-	/**
-	 * If relevant, the name of the drawable, usually it's the name
-	 * of the agent represented by this drawable.
-	 */
-	private String name;
-	
-	/**
-	 * If relevant, additional info on the drawable, usually it's the
-	 * state of the agent represented by this drawable.
-	 */
-	private String info;
-
-	/**
-	 * Constructor of the object
-	 * 
-	 * @param vui
-	 *            the VUI on which the object must be drawn
-	 * @param dx
-	 *            the x real position
-	 * @param dy
-	 *            the y real position
-	 * @param width
-	 *            the real width
-	 * @param height
-	 *            the real height
-	 */
-	protected Drawable(double dx, double dy, double width, double height) {
-		move(dx, dy);
-		setWidth(width);
-		setHeight(height);
-	}
-	
-	/**
-	 * If you wish to use some default settings for your drawable.<br/>
-	 * Must be called AFTER the node for your drawable has been created.
-	 */
-	protected void defaultInit() {
-		getNode().setStyle(defaultStyle);
-		
-		getNode().addEventHandler(MouseEvent.ANY, new EventHandler<MouseEvent>() {
-			@Override
-			public void handle(MouseEvent event) {
-				dispatchEvent(event);
-			}
-		});
-	}
-	
-	/**
-	 * Compute the width as it must be displayed on screen. Given the zoom factor,
-	 * the width displayed can be different than the real width.
-	 * 
-	 * @return the width
-	 */
-	public double getRenderedWidth() {
-		if (isFixed())
-			return width;
-		else
-			return vui.worldToScreenDistance(width);
-	}
-
-	/**
-	 * Set the real width of the object
-	 * 
-	 * @param width
-	 *            The new width
-	 */
-	public void setWidth(double width) {
-		this.width = width;
-	}
-
-	/**
-	 * Compute the height as it must be displayed on screen. Given the zoom factor,
-	 * the height displayed can be different than the real height.
-	 * 
-	 * @return the width
-	 */
-	public double getRenderedHeight() {
-		if (isFixed())
-			return height;
-		else
-			return vui.worldToScreenDistance(height);
-	}
-
-	/**
-	 * Set the real height of the object
-	 * 
-	 * @param height
-	 *            The new height
-	 */
-	public void setHeight(double height) {
-		this.height = height;
-	}
-
-	/**
-	 * Get the real width
-	 * 
-	 * @return the real width
-	 */
-	public double getWidth() {
-		return width;
-	}
-
-	/**
-	 * Get the real height
-	 * 
-	 * @return the real height
-	 */
-	public double getHeight() {
-		return height;
-	}
-
-	
-
-	/**
-	 * Getter for the fixed attribute
-	 * 
-	 * @return if the obejct is fixed
-	 */
-	public boolean isFixed() {
-		return fixed;
-	}
-
-	/**
-	 * Getter for the angle attribute
-	 * 
-	 * @return the angle
-	 */
-	public double getAngle() {
-		return angle;
-	}
-
-	/**
-	 * Getter for the layer attribute
-	 * 
-	 * @return the layer
-	 */
-	public int getLayer() {
-		return layer;
-	}
-
-	/**
-	 * Set the layer and update
-	 * 
-	 * @param layer
-	 *            the new layer
-	 * @return the object for chained methods
-	 */
-	public Drawable setLayer(int layer) {
-		this.layer = layer;
-		return this;
-	}
-
-	/**
-	 * Set the new angle
-	 * 
-	 * @param angle2
-	 *            the new angle
-	 * @return the object for chained methods
-	 */
-	public Drawable setAngle(double angle2) {
-		this.angle = angle2;
-		return this;
-	}
-
-	/**
-	 * Draw the object if visible and if on screen
-	 * 
-	 */
-	public void onDraw() {
-		if (isVisible()) {
-			_onDraw();
-		}
-	}
-
-	/**
-	 * Method that must be overrided to draw
-	 */
-	public abstract void _onDraw();
-
-	/**
-	 * Set the associated VUI
-	 * 
-	 * @param vectorialUI
-	 */
-	public void setVUI(VUI vectorialUI) {
-		vui = vectorialUI;
-	}
-
-	/**
-	 * Get the top y coordinate
-	 * 
-	 * @return the top y coordinate
-	 */
-	public double top() {
-		if (isFixed())
-			return y - height / 2;
-		else
-			return vui.worldToScreenY(y - height / 2);
-	}
-
-	/**
-	 * Get the left x coordinate
-	 * 
-	 * @return the left x coordinate
-	 */
-	public double left() {
-		if (isFixed())
-			return x - width / 2;
-		else
-			return vui.worldToScreenX(x - width / 2);
-	}
-
-	/**
-	 * Get the bottom y coordinate
-	 * 
-	 * @return the bottom y coordinate
-	 */
-	public double bottom() {
-		if (isFixed())
-			return y + height / 2;
-		else
-			return vui.worldToScreenY(y + height / 2);
-	}
-
-	/**
-	 * Get the right x coordinate
-	 * 
-	 * @return the right x coordinate
-	 */
-	public double right() {
-		if (isFixed())
-			return x + width / 2;
-		else
-			return vui.worldToScreenX(x + width / 2);
-	}
-
-	/**
-	 * Only draw the border of the object
-	 * 
-	 * @return the object for chained methods
-	 */
-	public Drawable setStrokeOnly() {
-		strokeMode = true;
-		return this;
-	}
-
-	/**
-	 * 
-	 * @param color
-	 * @return the object for chained methods
-	 */
-	public Drawable setColor(Color color) {
-		if (color == this.color)
-			return this;
-		this.color = color;
-		return this;
-	}
-	
-	/**
-	 * The color of the drawable.
-	 * @return
-	 */
-	public Color getColor() {
-		return color;
-	}
-
-	/**
-	 * 
-	 * @param dx
-	 * @param dy
-	 * @return the object for chained methods
-	 */
-	public Drawable move(double dx, double dy) {
-		if (x == dx && y == dy)
-			return this;
-		this.x = dx;
-		this.y = dy;
-		return this;
-	}
-
-	/**
-	 * 
-	 * @return the object for chained methods
-	 */
-	public Drawable setFixed() {
-		this.fixed = true;
-		return this;
-	}
-
-	/**
-	 * 
-	 * @return the object for chained methods
-	 */
-	public Drawable show() {
-		return this.setVisible(true);
-	}
-	
-	protected abstract void _hide();
-
-	/**
-	 * 
-	 * @return
-	 */
-	public Drawable hide() {
-		_hide();
-		return this.setVisible(false);
-	}
-
-	/**
-	 * 
-	 * @return
-	 */
-	public boolean isVisible() {
-		return visible;
-	}
-	
-	public abstract void _show();
-
-	/**
-	 * 
-	 * @param visible
-	 * @return the object for chained methods
-	 */
-	public Drawable setVisible(boolean visible) {
-		this.visible = visible;
-		if (visible)
-			_show();
-		else
-			_hide();
-		return this;
-	}
-	
-	/**
-	 * The graphical element that is displayed
-	 * @return
-	 */
-	public abstract Node getNode();
-	
-	/**
-	 * Remove the drawable from its VUI
-	 */
-	public void delete() {
-		vui.remove(this);
-	}
-
-	/**
-	 * Get the linked drawable or null if it does not exist.
-	 * @param name name of the linked drawable
-	 * @return the linked drawable or null
-	 */
-	public Drawable getLinkedDrawable(String name) {
-		Drawable ret = null;
-		if(linkedDrawable.containsKey(name)) {
-			ret = linkedDrawable.get(name);
-		}
-		return ret;
-	}
-	
-	/**
-	 * Add a drawable to the list of linked drawables.<br/>
-	 * The relation is not symmetrical.
-	 * @param name
-	 * @param drawable
-	 */
-	public void addLinkedDrawable(String name, Drawable drawable) {
-		linkedDrawable.put(name, drawable);
-	}
-	
-	/**
-	 * Return the list of linked drawables. <br/>
-	 * Linked drawables will receive the same event when using dispatchEvent.
-	 */
-	public List<Drawable> getLinkedDrawables(){
-		return new ArrayList<Drawable>(linkedDrawable.values());
-	}
-	
-	/**
-	 * Used by dispatchEvent. Override if you want to register more event with the dispatchEvent
-	 * @param event
-	 */
-	protected void onEvent(Event event) {
-		switch (event.getEventType().getName()) {
-		case "MOUSE_CLICKED":
-			onMouseClick((MouseEvent)event);
-			break;
-		case "MOUSE_ENTERED":
-			onMouseEntered((MouseEvent)event);
-			break;
-		case "MOUSE_EXITED":
-			onMouseExited((MouseEvent)event);
-			break;
-		default:
-			break;
-		}
-	}
-	
-	/**
-	 * Called when onEvent receive a MOUSE_EXITED event.
-	 * @param event
-	 */
-	protected void onMouseExited(MouseEvent event) {
-		getNode().setStyle(defaultStyle);
-	}
-
-	/**
-	 * Called when onEvent receive a MOUSE_ENTERED event.
-	 * @param event
-	 */
-	protected void onMouseEntered(MouseEvent event) {
-		getNode().setStyle("-fx-stroke: black; -fx-stroke-width: 3;");
-	}
-
-	/**
-	 * Called when onEvent receive a MOUSE_CLICKED event.
-	 * @param event
-	 */
-	protected void onMouseClick(MouseEvent event) {
-		if(expanded) {
-			collapse();
-		} else {
-			expand();
-		}
-	}
-
-	/**
-	 * Dispatch an event to all linked drawable, and this drawable.
-	 * @param event
-	 */
-	public void dispatchEvent(Event event) {
-		for(Drawable d : getLinkedDrawables()) {
-			d.onEvent(event);
-		}
-		onEvent(event);
-	}
-	
-	/**
-	 * If this drawable should be shown in the vui explorer.
-	 */
-	public Drawable setShowInExplorer(boolean showInExplorer) {
-		this.showInExplorer = showInExplorer;
-		return this;
-	}
-	
-	/**
-	 * If relevant, the name of the drawable, usually it's the name
-	 * of the agent represented by this drawable.
-	 */
-	public String getName() {
-		return name == null ? toString() : name;
-	}
-	
-	/**
-	 * If relevant, additional info on the drawable, usually it's the
-	 * state of the agent represented by this drawable.
-	 */
-	public String getInfo() {
-		return info == null ? toString() : info;
-	}
-	
-	/**
-	 * If relevant, the name of the drawable, usually it's the name
-	 * of the agent represented by this drawable.
-	 */
-	public Drawable setName(String name) {
-		this.name = name;
-		return this;
-	}
-	
-	/**
-	 * If relevant, additional info on the drawable, usually it's the
-	 * state of the agent represented by this drawable.
-	 */
-	public Drawable setInfo(String info) {
-		this.info = info;
-		return this;
-	}
-	
-	/**
-	 * Action performed if drawable is clicked while collapsed.<br/>
-	 * By default do nothing
-	 * @see Drawable#collapse()
-	 */
-	public void expand() {
-		expanded = true;
-	}
-	
-	/**
-	 * Action performed if drawable is clicked while expanded.
-	 * @see Drawable#expand()
-	 */
-	public void collapse() {
-		expanded = false;
-	}
-	
-	public boolean isExpanded() {
-		return expanded;
-	}
-	
-	/**
-	 * Set the drawable on top of all others
-	 * @return
-	 */
-	public Drawable toFront() {
-		RunLaterHelper.runLater(()-> getNode().toFront());
-		return this;
-	}
-}
+package fr.irit.smac.amak.ui.drawables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import fr.irit.smac.amak.tools.RunLaterHelper;
+import fr.irit.smac.amak.ui.VUI;
+import fr.irit.smac.amak.ui.VUIMulti;
+import javafx.event.Event;
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Color;
+
+/**
+ * A drawable is an object that can be drawn by the {@link VUI} system
+ * 
+ * @author of original version (the Swing one) Alexandre Perles
+ *
+ */
+public abstract class Drawable {
+	
+	/**
+	 * If this drawable should be shown in the vui explorer.
+	 */
+	public boolean showInExplorer = true;
+
+	/**
+	 * Default style applied to drawable node.
+	 */
+	protected static String defaultStyle = "-fx-stroke: black; -fx-stroke-width: 1;";
+	
+	/**
+	 * Linked drawables will receive the same event when using dispatchEvent.
+	 */
+	private HashMap<String, Drawable> linkedDrawable = new HashMap<String, Drawable>();
+	
+	/**
+	 * The horizontal position of the object
+	 */
+	protected double x;
+	/**
+	 * The vertical position of the object
+	 */
+	private double y;
+	/**
+	 * The width of the object
+	 */
+	private double width;
+	
+	/**
+	 * The real height
+	 */
+	protected double height;
+
+	/**
+	 * Does only the border must be displayed ?
+	 */
+	protected boolean strokeMode = false;
+
+	/**
+	 * The color of the object
+	 */
+	protected Color color = Color.BLACK;
+	
+	/**
+	 * The VUI on which the object is drawn
+	 */
+	protected VUI vui;
+	
+	protected VUIMulti vuiMulti;
+	
+	/**
+	 * The order of drawing. An higher layer is drawn on top of the other.
+	 */
+	protected int layer = 0;
+	
+	/**
+	 * The angle of rotation of the object
+	 */
+	private double angle;
+	
+	/**
+	 * A fixed object doesn't move with the view. It can be used for HUD
+	 */
+	private boolean fixed = false;
+	
+	/**
+	 * Must the object be drawn ?
+	 */
+	private boolean visible = true;
+	
+	/**
+	 * Is the drawable expanded ?
+	 * @see Drawable#onMouseClick(MouseEvent)
+	 * @see Drawable#expand()
+	 * @see Drawable#collapse()
+	 */
+	private boolean expanded = false;
+	
+	/**
+	 * If relevant, the name of the drawable, usually it's the name
+	 * of the agent represented by this drawable.
+	 */
+	private String name;
+	
+	/**
+	 * If relevant, additional info on the drawable, usually it's the
+	 * state of the agent represented by this drawable.
+	 */
+	private String info;
+
+	/**
+	 * Constructor of the object
+	 * 
+	 * @param vui
+	 *            the VUI on which the object must be drawn
+	 * @param dx
+	 *            the x real position
+	 * @param dy
+	 *            the y real position
+	 * @param width
+	 *            the real width
+	 * @param height
+	 *            the real height
+	 */
+	protected Drawable(double dx, double dy, double width, double height) {
+		move(dx, dy);
+		setWidth(width);
+		setHeight(height);
+	}
+	
+	/**
+	 * If you wish to use some default settings for your drawable.<br/>
+	 * Must be called AFTER the node for your drawable has been created.
+	 */
+	protected void defaultInit() {
+		getNode().setStyle(defaultStyle);
+		
+		getNode().addEventHandler(MouseEvent.ANY, new EventHandler<MouseEvent>() {
+			@Override
+			public void handle(MouseEvent event) {
+				dispatchEvent(event);
+			}
+		});
+	}
+	
+	/**
+	 * Compute the width as it must be displayed on screen. Given the zoom factor,
+	 * the width displayed can be different than the real width.
+	 * 
+	 * @return the width
+	 */
+	public double getRenderedWidth() {
+		if (isFixed())
+			return width;
+		else
+			return vui != null ? vui.worldToScreenDistance(width) : vuiMulti.worldToScreenDistance(width);
+		
+	}
+
+	/**
+	 * Set the real width of the object
+	 * 
+	 * @param width
+	 *            The new width
+	 */
+	public void setWidth(double width) {
+		this.width = width;
+	}
+
+	/**
+	 * Compute the height as it must be displayed on screen. Given the zoom factor,
+	 * the height displayed can be different than the real height.
+	 * 
+	 * @return the width
+	 */
+	public double getRenderedHeight() {
+		if (isFixed())
+			return height;
+		else
+			return vui != null ? vui.worldToScreenDistance(height) : vuiMulti.worldToScreenDistance(height);
+	}
+
+	/**
+	 * Set the real height of the object
+	 * 
+	 * @param height
+	 *            The new height
+	 */
+	public void setHeight(double height) {
+		this.height = height;
+	}
+
+	/**
+	 * Get the real width
+	 * 
+	 * @return the real width
+	 */
+	public double getWidth() {
+		return width;
+	}
+
+	/**
+	 * Get the real height
+	 * 
+	 * @return the real height
+	 */
+	public double getHeight() {
+		return height;
+	}
+
+	
+
+	/**
+	 * Getter for the fixed attribute
+	 * 
+	 * @return if the obejct is fixed
+	 */
+	public boolean isFixed() {
+		return fixed;
+	}
+
+	/**
+	 * Getter for the angle attribute
+	 * 
+	 * @return the angle
+	 */
+	public double getAngle() {
+		return angle;
+	}
+
+	/**
+	 * Getter for the layer attribute
+	 * 
+	 * @return the layer
+	 */
+	public int getLayer() {
+		return layer;
+	}
+
+	/**
+	 * Set the layer and update
+	 * 
+	 * @param layer
+	 *            the new layer
+	 * @return the object for chained methods
+	 */
+	public Drawable setLayer(int layer) {
+		this.layer = layer;
+		return this;
+	}
+
+	/**
+	 * Set the new angle
+	 * 
+	 * @param angle2
+	 *            the new angle
+	 * @return the object for chained methods
+	 */
+	public Drawable setAngle(double angle2) {
+		this.angle = angle2;
+		return this;
+	}
+
+	/**
+	 * Draw the object if visible and if on screen
+	 * 
+	 */
+	public void onDraw() {
+		if (isVisible()) {
+			_onDraw();
+		}
+	}
+
+	/**
+	 * Method that must be overrided to draw
+	 */
+	public abstract void _onDraw();
+
+	/**
+	 * Set the associated VUI
+	 * 
+	 * @param vectorialUI
+	 */
+	public void setVUI(VUI vectorialUI) {
+		vui = vectorialUI;
+	}
+	
+	public void setVUIMulti(VUIMulti vectorialUI) {
+		vuiMulti = vectorialUI;
+	}
+
+	/**
+	 * Get the top y coordinate
+	 * 
+	 * @return the top y coordinate
+	 */
+	public double top() {
+		if (isFixed())
+			return y - height / 2;
+		else
+			return vui != null ? vui.worldToScreenY(y - height / 2) : vuiMulti.worldToScreenY(y - height / 2);
+	}
+
+	/**
+	 * Get the left x coordinate
+	 * 
+	 * @return the left x coordinate
+	 */
+	public double left() {
+		if (isFixed())
+			return x - width / 2;
+		else
+			return vui != null ? vui.worldToScreenX(x - width / 2) : vuiMulti.worldToScreenX(x - width / 2);
+		
+	}
+
+	/**
+	 * Get the bottom y coordinate
+	 * 
+	 * @return the bottom y coordinate
+	 */
+	public double bottom() {
+		if (isFixed())
+			return y + height / 2;
+		else
+			return vui != null ? vui.worldToScreenY(y + height / 2) : vuiMulti.worldToScreenY(y + height / 2);
+	}
+
+	/**
+	 * Get the right x coordinate
+	 * 
+	 * @return the right x coordinate
+	 */
+	public double right() {
+		if (isFixed())
+			return x + width / 2;
+		else
+			return vui != null ? vui.worldToScreenX(x + width / 2) : vuiMulti.worldToScreenX(x + width / 2);
+	}
+
+	/**
+	 * Only draw the border of the object
+	 * 
+	 * @return the object for chained methods
+	 */
+	public Drawable setStrokeOnly() {
+		strokeMode = true;
+		return this;
+	}
+
+	/**
+	 * 
+	 * @param color
+	 * @return the object for chained methods
+	 */
+	public Drawable setColor(Color color) {
+		if (color == this.color)
+			return this;
+		this.color = color;
+		return this;
+	}
+	
+	/**
+	 * The color of the drawable.
+	 * @return
+	 */
+	public Color getColor() {
+		return color;
+	}
+
+	/**
+	 * 
+	 * @param dx
+	 * @param dy
+	 * @return the object for chained methods
+	 */
+	public Drawable move(double dx, double dy) {
+		if (x == dx && y == dy)
+			return this;
+		this.x = dx;
+		this.y = dy;
+		return this;
+	}
+
+	/**
+	 * 
+	 * @return the object for chained methods
+	 */
+	public Drawable setFixed() {
+		this.fixed = true;
+		return this;
+	}
+
+	/**
+	 * 
+	 * @return the object for chained methods
+	 */
+	public Drawable show() {
+		return this.setVisible(true);
+	}
+	
+	protected abstract void _hide();
+
+	/**
+	 * 
+	 * @return
+	 */
+	public Drawable hide() {
+		_hide();
+		return this.setVisible(false);
+	}
+
+	/**
+	 * 
+	 * @return
+	 */
+	public boolean isVisible() {
+		return visible;
+	}
+	
+	public abstract void _show();
+
+	/**
+	 * 
+	 * @param visible
+	 * @return the object for chained methods
+	 */
+	public Drawable setVisible(boolean visible) {
+		this.visible = visible;
+		if (visible)
+			_show();
+		else
+			_hide();
+		return this;
+	}
+	
+	/**
+	 * The graphical element that is displayed
+	 * @return
+	 */
+	public abstract Node getNode();
+	
+	/**
+	 * Remove the drawable from its VUI
+	 */
+	public void delete() {
+		if(vui != null)
+			vui.remove(this);
+		
+		if(vuiMulti != null)
+			vuiMulti.remove(this);
+	}
+
+	/**
+	 * Get the linked drawable or null if it does not exist.
+	 * @param name name of the linked drawable
+	 * @return the linked drawable or null
+	 */
+	public Drawable getLinkedDrawable(String name) {
+		Drawable ret = null;
+		if(linkedDrawable.containsKey(name)) {
+			ret = linkedDrawable.get(name);
+		}
+		return ret;
+	}
+	
+	/**
+	 * Add a drawable to the list of linked drawables.<br/>
+	 * The relation is not symmetrical.
+	 * @param name
+	 * @param drawable
+	 */
+	public void addLinkedDrawable(String name, Drawable drawable) {
+		linkedDrawable.put(name, drawable);
+	}
+	
+	/**
+	 * Return the list of linked drawables. <br/>
+	 * Linked drawables will receive the same event when using dispatchEvent.
+	 */
+	public List<Drawable> getLinkedDrawables(){
+		return new ArrayList<Drawable>(linkedDrawable.values());
+	}
+	
+	/**
+	 * Used by dispatchEvent. Override if you want to register more event with the dispatchEvent
+	 * @param event
+	 */
+	protected void onEvent(Event event) {
+		switch (event.getEventType().getName()) {
+		case "MOUSE_CLICKED":
+			onMouseClick((MouseEvent)event);
+			break;
+		case "MOUSE_ENTERED":
+			onMouseEntered((MouseEvent)event);
+			break;
+		case "MOUSE_EXITED":
+			onMouseExited((MouseEvent)event);
+			break;
+		default:
+			break;
+		}
+	}
+	
+	/**
+	 * Called when onEvent receive a MOUSE_EXITED event.
+	 * @param event
+	 */
+	protected void onMouseExited(MouseEvent event) {
+		getNode().setStyle(defaultStyle);
+	}
+
+	/**
+	 * Called when onEvent receive a MOUSE_ENTERED event.
+	 * @param event
+	 */
+	protected void onMouseEntered(MouseEvent event) {
+		getNode().setStyle("-fx-stroke: black; -fx-stroke-width: 3;");
+	}
+
+	/**
+	 * Called when onEvent receive a MOUSE_CLICKED event.
+	 * @param event
+	 */
+	protected void onMouseClick(MouseEvent event) {
+		if(expanded) {
+			collapse();
+		} else {
+			expand();
+		}
+	}
+
+	/**
+	 * Dispatch an event to all linked drawable, and this drawable.
+	 * @param event
+	 */
+	public void dispatchEvent(Event event) {
+		for(Drawable d : getLinkedDrawables()) {
+			d.onEvent(event);
+		}
+		onEvent(event);
+	}
+	
+	/**
+	 * If this drawable should be shown in the vui explorer.
+	 */
+	public Drawable setShowInExplorer(boolean showInExplorer) {
+		this.showInExplorer = showInExplorer;
+		return this;
+	}
+	
+	/**
+	 * If relevant, the name of the drawable, usually it's the name
+	 * of the agent represented by this drawable.
+	 */
+	public String getName() {
+		return name == null ? toString() : name;
+	}
+	
+	/**
+	 * If relevant, additional info on the drawable, usually it's the
+	 * state of the agent represented by this drawable.
+	 */
+	public String getInfo() {
+		return info == null ? toString() : info;
+	}
+	
+	/**
+	 * If relevant, the name of the drawable, usually it's the name
+	 * of the agent represented by this drawable.
+	 */
+	public Drawable setName(String name) {
+		this.name = name;
+		return this;
+	}
+	
+	/**
+	 * If relevant, additional info on the drawable, usually it's the
+	 * state of the agent represented by this drawable.
+	 */
+	public Drawable setInfo(String info) {
+		this.info = info;
+		return this;
+	}
+	
+	/**
+	 * Action performed if drawable is clicked while collapsed.<br/>
+	 * By default do nothing
+	 * @see Drawable#collapse()
+	 */
+	public void expand() {
+		expanded = true;
+	}
+	
+	/**
+	 * Action performed if drawable is clicked while expanded.
+	 * @see Drawable#expand()
+	 */
+	public void collapse() {
+		expanded = false;
+	}
+	
+	public boolean isExpanded() {
+		return expanded;
+	}
+	
+	/**
+	 * Set the drawable on top of all others
+	 * @return
+	 */
+	public Drawable toFront() {
+		RunLaterHelper.runLater(()-> getNode().toFront());
+		return this;
+	}
+}
-- 
GitLab