diff --git a/AMAKFX/src/fr/irit/smac/amak/Agent.java b/AMAKFX/src/fr/irit/smac/amak/Agent.java index 5166056c042ff55d44110d5d52733cc3cc798a0f..63e97500439e0693fce1f6a28ac23f5bfe5d2f9d 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 a92400870625b739704f5ba05350fdf1c119fe35..930f06c0bc146dbdd4a7bbd0f7f1dcf59aa432d8 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 b592877357310be5f1eeaa46361e75acd60a7396..0000000000000000000000000000000000000000 --- 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 a1a015c3cbba9b8dc9cf7ef322d8a70065dde946..0000000000000000000000000000000000000000 --- 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 62c06ef392dc2ac841394a5403f56577efebc6a4..4b09958e305a51b386bf0f6f0d51eef39b1b3edf 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 fe8564924cfa7fa2c1548c56387abfd8da69181b..344561afb24ba67df1e4fa1031eab902844ff01f 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 0000000000000000000000000000000000000000..51e5a90cfd30078cd793704f2b0f1b207051087e --- /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 22f9e6913fffcd74374b7274dc8fe25e1a9ffb21..ffb5c0f1f034f6826152b0bcdd10c42356c5a186 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 53ca4e46eeecf1df1b2ab7a0db9502cffcf173d0..7869e9639551bae54b967bb2c83ed58fdb499b9b 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 e396e9bb3deabfc2b3c05beef8826f5002258568..2037681065b7736b2c48bb4eea8348a843194faf 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 0000000000000000000000000000000000000000..501a6d6c72c64ee7a5dfa2ce254030e0f0789779 --- /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 957e6a8ae9c4507c27f9832abb99d7023ab19883..dbb94088b1911433c82eccdd7a3c5c2a5170c067 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 ec5ce1fe94b59134da30c00edbd7368c6c8470f3..377377c5904d07114052daeb12d959a6664a725d 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; + } +}