aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/gui/population/GUINode.java
diff options
context:
space:
mode:
authorEduardo Pedroni <e.pedroni91@gmail.com>2015-05-27 13:24:23 +0200
committerEduardo Pedroni <e.pedroni91@gmail.com>2015-05-27 13:24:23 +0200
commit72f96333f0dd0bad05f3b5d10cf343cf21aa7e39 (patch)
tree1f138ac604b3d5f703301912d2c3863785a40792 /src/jcgp/gui/population/GUINode.java
parent1e7d7a9b09ca9d24b56cf33a168953ca033d7c53 (diff)
Brought back the old GUI, refactorings are now in dev and will be merged when completeHEADmaster
Diffstat (limited to 'src/jcgp/gui/population/GUINode.java')
-rw-r--r--src/jcgp/gui/population/GUINode.java494
1 files changed, 416 insertions, 78 deletions
diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java
index 1a32426..4d420ea 100644
--- a/src/jcgp/gui/population/GUINode.java
+++ b/src/jcgp/gui/population/GUINode.java
@@ -1,134 +1,472 @@
package jcgp.gui.population;
+import javafx.event.EventHandler;
+import javafx.scene.control.Label;
+import javafx.scene.input.MouseDragEvent;
+import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
-import jcgp.backend.population.Gene;
+import jcgp.backend.function.Function;
+import jcgp.backend.population.Connection;
+import jcgp.backend.population.Input;
import jcgp.backend.population.Node;
+import jcgp.backend.resources.Resources;
import jcgp.gui.GUI;
import jcgp.gui.constants.Constants;
-import jcgp.gui.constants.Position;
-import jcgp.gui.handlers.NodeHandlers;
-/**
- * The GUI counterpart of {@link jcgp.backend.population.Node}. This is a
- * subclass of {@code GUIGene} which represents a chromosome node.
- *
- * @author Eduardo Pedroni
- */
-public class GUINode extends GUIGene implements GUIMutable, GUIConnection {
+public class GUINode extends GUIGene {
- private Node node;
private Line[] lines;
- private Circle[] sockets;
+ private Node node;
+ private Resources resources;
+ private int connectionIndex = 0;
- /**
- * Instantiate {@code GUINode} given a {@code Node} and the lines needed
- * to show its connections.
- *
- * @param node the associated backend node.
- * @param lines the lines used to display connections.
- */
- public GUINode(Node node, Line[] lines) {
+
+ public GUINode(ChromosomePane parentRef, final Node node, Line[] connectionLines, final GUI gui) {
super();
- // store references, associate with node
+
+ // store references
+ this.parent = parentRef;
this.node = node;
- this.lines = lines;
- node.setGUIObject(this);
+ this.lines = connectionLines;
+ this.resources = gui.getExperiment().getResources();
+
+ // move the GUIGene to the right position
+ relocate(((node.getColumn() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS,
+ (node.getRow() * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS);
+
+ // set the line ends correctly
+ updateLines();
- // create the output socket
- Circle output = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT);
+ final Label connectionNumber = new Label();
+ connectionNumber.setStyle("-fx-background-color:rgb(255, 255, 255); -fx-border-color:rgba(0, 0, 0, 0.5); ");
+ connectionNumber.setVisible(false);
+
+ Circle output = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white"));
output.setStroke(Paint.valueOf("black"));
-
- // create input sockets
- sockets = new Circle[GUI.resources.arity()];
+
+ updateText();
+
+ Circle[] sockets = new Circle[resources.arity()];
+ double angle, xPos, yPos;
for (int l = 0; l < sockets.length; l++) {
- sockets[l] = new Circle(Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT);
- sockets[l].setStroke(Paint.valueOf("black"));
+ angle = (((l + 1) / ((double) (resources.arity() + 1))) * Constants.THETA) - (Constants.THETA / 2);
+ xPos = -Math.cos(angle) * Constants.NODE_RADIUS;
+ yPos = Math.sin(angle) * Constants.NODE_RADIUS;
+
+ sockets[l] = new Circle(xPos, yPos, Constants.SOCKET_RADIUS, Paint.valueOf("white"));
sockets[l].setId(String.valueOf(l));
- // relocate them
- Position.placeSocket(l, sockets[l]);
- Position.connect(lines[l], (GUIGene) ((Gene) node.getConnection(l)).getGUIObject());
+ sockets[l].setStroke(Paint.valueOf("black"));
+
+ final Circle s = sockets[l];
+ final int index = l;
+
+ /*
+ * Mouse event handlers on sockets
+ *
+ */
+ s.addEventFilter(MouseEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // the mouse has been dragged out of the socket, this means a full drag is in progress
+ startFullDrag();
+ }
+ });
+
+ s.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // user is hovering over connection socket
+ connectionNumber.setText("C: " + s.getId());
+ connectionNumber.relocate(s.getCenterX() + 5, s.getCenterY() - 10);
+ connectionNumber.setVisible(true);
+ }
+ });
+
+ s.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // user exits the connection socket
+ connectionNumber.setVisible(false);
+ }
+ });
+
+ s.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // mouse was pressed on the socket
+ setState(GUIGeneState.SOURCE);
+ connectionIndex = index;
+ }
+ });
+
+ s.addEventFilter(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ if (!parent.isTarget()) {
+ lines[connectionIndex].setEndX(event.getX() + ((Circle) event.getSource()).getParent().getLayoutX());
+ lines[connectionIndex].setEndY(event.getY() + ((Circle) event.getSource()).getParent().getLayoutY());
+ }
+ }
+ });
+
+ s.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ if (event.isStillSincePress()) {
+ // mouse was released before dragging out of the socket
+ updateLine(index);
+ setState(GUIGeneState.HOVER);
+ } else if (getState() == GUIGeneState.SOURCE) {
+ // no connection has been made, fallback
+ resetState();
+ updateLines();
+ }
+ }
+ });
}
+
+ /*
+ * Mouse event handlers on whole gene
+ */
+ addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ gui.bringFunctionSelector(event, (GUINode) event.getSource());
+ }
+ });
- // add elements
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has entered this node, react appropriately
+ // this happens even if we are the source of the drag
+ if (isAllowed((GUIGene) event.getGestureSource(), (GUIGene) event.getSource())) {
+ ((GUIGene) event.getGestureSource()).setConnectionLine((GUIGene) event.getSource());
+
+ Connection source = ((GUIGene) event.getGestureSource()).getChangingConnection();
+ if (node == source) {
+ setState(GUIGeneState.NO_CHANGE_TARGET);
+ } else {
+ setState(GUIGeneState.VALID_TARGET);
+ }
+ } else {
+ setState(GUIGeneState.INVALID_TARGET);
+ }
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_EXITED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has exited this node, react appropriately
+ // this happens even if we are the source of the drag
+ parent.setTarget(false);
+ if (event.isPrimaryButtonDown()) {
+ if (event.getGestureSource() == event.getSource()) {
+ setState(GUIGeneState.SOURCE);
+ } else {
+ if (getState() == GUIGeneState.NO_CHANGE_TARGET) {
+ setState(GUIGeneState.INDIRECT_HOVER);
+ } else {
+ setState(GUIGeneState.NEUTRAL);
+ ((GUIGene) event.getGestureSource()).setConnectionStates(GUIGeneState.INDIRECT_HOVER);
+ }
+ }
+ }
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ GUIGene source = ((GUIGene) event.getGestureSource());
+ // set states to reflect the new situation
+ if (source.isLocked()) {
+ source.setState(GUIGeneState.HOVER);
+ source.setConnectionStates(GUIGeneState.HOVER);
+ } else {
+ source.setState(GUIGeneState.NEUTRAL);
+ source.setConnectionStates(GUIGeneState.NEUTRAL);
+ }
+
+ // the user released the drag gesture on this node, react appropriately
+ if (isAllowed((GUIGene) event.getGestureSource(), (GUIGene) event.getSource())) {
+ if (source.isLocked()) {
+ // remove locks from the old connection, add the to setConnethe new
+ // note that the old connection may still have locks after this
+ parent.getGuiGene(source.getChangingConnection()).removeLocks(source.getLocks());
+ addLocks(source.getLocks());
+ } else {
+ if (source instanceof GUIOutput) {
+ source.resetState();
+ }
+ }
+ source.setChangingConnection(node);
+
+ }
+ source.updateLines();
+ setState(GUIGeneState.HOVER);
+ }
+ });
+
+ addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has entered this node without dragging, or it is dragging and this is the source
+ if (getState() == GUIGeneState.NEUTRAL) {
+ setState(GUIGeneState.HOVER);
+ } else if (locked > 0) {
+ setConnectionStates(GUIGeneState.LOCKED_HOVER);
+ }
+ }
+ });
+
+ addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has left this node without dragging, or it is dragging and this is the source
+ if (getState() == GUIGeneState.HOVER && locked <= 0) {
+ setState(GUIGeneState.NEUTRAL);
+ setConnectionStates(GUIGeneState.NEUTRAL);
+ } else if (locked > 0) {
+ if (getState() == GUIGeneState.SOURCE || getState() == GUIGeneState.INVALID_TARGET) {
+ setConnectionStates(GUIGeneState.INDIRECT_HOVER);
+ } else {
+ setConnectionStates(GUIGeneState.HOVER);
+ }
+
+ }
+ }
+ });
+
+ getChildren().addAll(mainCircle, text);
getChildren().addAll(sockets);
- getChildren().add(output);
-
- // relocate node, add handlers
- Position.place(this);
- NodeHandlers.addHandlers(this);
+ getChildren().addAll(output, connectionNumber);
+
+ }
+
+ @Override
+ public void setState(GUIGeneState newState) {
+ switch (newState) {
+ case ACTIVE_HOVER:
+ if (locked > 0) {
+ setState(GUIGeneState.LOCKED_HOVER);
+ } else {
+ mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR));
+ showLines(true);
+ }
+ setConnectionStates(GUIGeneState.ACTIVE_HOVER);
+ break;
+ case LOCKED_HOVER:
+ mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR));
+ break;
+ case INVALID_TARGET:
+ mainCircle.setFill(Paint.valueOf(Constants.BAD_SELECTION_COLOUR));
+ break;
+ case HOVER:
+ mainCircle.setFill(Paint.valueOf(Constants.MEDIUM_HIGHLIGHT_COLOUR));
+ showLines(true);
+ if (locked <= 0) {
+ setConnectionStates(GUIGeneState.INDIRECT_HOVER);
+ } else {
+ setConnectionStates(GUIGeneState.HOVER);
+ }
+ break;
+ case INDIRECT_HOVER:
+ mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR));
+ break;
+ case NEUTRAL:
+ if (locked > 0) {
+ setState(GUIGeneState.HOVER);
+ } else {
+ mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR));
+ showLines(false);
+ if (getState() == GUIGeneState.ACTIVE_HOVER) {
+ setConnectionStates(GUIGeneState.NEUTRAL);
+ }
+ }
+ break;
+ case NO_CHANGE_TARGET:
+ parent.setTarget(true);
+ mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_SELECTION_COLOUR));
+ break;
+ case SOURCE:
+ mainCircle.setFill(Paint.valueOf(Constants.HARD_HIGHLIGHT_COLOUR));
+ break;
+ case VALID_TARGET:
+ parent.setTarget(true);
+ mainCircle.setFill(Paint.valueOf(Constants.GOOD_SELECTION_COLOUR));
+ break;
+ default:
+ break;
+ }
+
+ super.setState(newState);
}
- /**
- * @return the {@code Node} instance associated with this object.
- */
+ @Override
+ public Connection getChangingConnection() {
+ return node.getConnection(connectionIndex);
+ }
+
+ private boolean isAllowed(GUIGene source, GUIGene target) {
+ if (source instanceof GUINode) {
+ // if the source is a node, all inputs and some nodes are valid
+ if (target instanceof GUIInput) {
+ return true;
+ } else if (target instanceof GUINode) {
+ // target and source are nodes, let's look at levels back
+ Node t = ((GUINode) target).getNode(), s = ((GUINode) source).getNode();
+ if (s.getColumn() - t.getColumn() > 0 && s.getColumn() - t.getColumn() <= resources.levelsBack()) {
+ return true;
+ }
+ return false;
+ } else if (target instanceof GUIOutput) {
+ return false;
+ } else {
+ throw new ClassCastException("Target was neither GUINode nor GUIInput nor GUIOutput.");
+ }
+ } else if (source instanceof GUIOutput) {
+ // if the source is an output, any node or input is valid
+ if (target instanceof GUINode || target instanceof GUIInput) {
+ return true;
+ } else if (target instanceof GUIOutput) {
+ return false;
+ } else {
+ throw new ClassCastException("Target was neither GUINode nor GUIInput nor GUIOutput.");
+ }
+ }
+ // if the source was neither node nor output, something bad is happening
+ throw new ClassCastException("Source was neither GUINode nor GUIOutput.");
+ }
+
+
public Node getNode() {
return node;
}
/**
- * Associates this instance with a new node.
+ * Place the end of the specified line on the output of the associated connection.
*
- * @param node the new node.
+ * @param index the line to be updated.
*/
- void setNode(Node node) {
- this.node = node;
+ public void updateLine(int index) {
+ if (node.getConnection(index) instanceof Node) {
+ int row = ((Node) node.getConnection(index)).getRow(),
+ column = ((Node) node.getConnection(index)).getColumn();
+ lines[index].setEndX(((column + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + 2 * Constants.NODE_RADIUS);
+ lines[index].setEndY((row * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS);
+ } else if (node.getConnection(index) instanceof Input) {
+ int inputIndex = ((Input) node.getConnection(index)).getIndex();
+ lines[index].setEndX(2 * Constants.NODE_RADIUS);
+ lines[index].setEndY(inputIndex * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.NODE_RADIUS);
+ }
}
-
+
+ /**
+ * Updates the end of all lines to match the associated connections.
+ */
@Override
- public Line[] getLines() {
- return lines;
+ public void updateLines() {
+ for (int c = 0; c < lines.length; c++) {
+ updateLine(c);
+ }
}
-
+
/**
- * Returns one of this object's connection sockets. They are
- * indexed in the same order as lines and the connections
- * they represent.
+ * Toggle visibility of all connection lines.
*
- * @param index the socket to return.
- * @return the indexed socket object.
+ * @param value whether to show the lines or not.
*/
- public Circle getSocket(int index) {
- return sockets[index];
+ private void showLines(boolean value) {
+ for (int i = 0; i < lines.length; i++) {
+ lines[i].setVisible(value);
+ }
}
-
- /**
- * @return the entire {@code Socket} array.
- */
- public Circle[] getSockets() {
- return sockets;
+
+ @Override
+ public void setConnectionStates(GUIGeneState newState) {
+ for (int i = 0; i < lines.length; i++) {
+ parent.getGuiGene(node.getConnection(i)).setState(newState);
+ }
+ }
+
+ @Override
+ public void setChangingConnection(Connection newConnection) {
+ node.setConnection(connectionIndex, newConnection);
+ if (parent.isEvaluating()) {
+ parent.updateValues();
+ }
}
+
@Override
- public void setStateRecursively(GUIGeneState state) {
- setState(state);
- for (int i = 0; i < GUI.resources.arity(); i++) {
- ((GUIConnection) ((Gene) node.getConnection(i)).getGUIObject()).setStateRecursively(state);
+ public void resetState() {
+ if (locked > 0) {
+ setState(GUIGeneState.HOVER);
+ } else {
+ setState(GUIGeneState.NEUTRAL);
+ setConnectionStates(GUIGeneState.NEUTRAL);
}
+
}
@Override
- protected void setLinesVisible(boolean value) {
+ protected void setLocked(boolean value) {
+ locked += value ? 1 : -1;
+ setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER);
+
for (int i = 0; i < lines.length; i++) {
- lines[i].setVisible(value);
+ parent.getGuiGene(node.getConnection(i)).setLocked(value);
+ }
+ }
+
+ @Override
+ public void addLocks(int value) {
+ locked += value;
+ setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER);
+
+ for (int i = 0; i < lines.length; i++) {
+ parent.getGuiGene(node.getConnection(i)).addLocks(value);
}
}
@Override
- public void setLockRecursively(boolean value) {
- setLock(value);
- for (int i = 0; i < GUI.resources.arity(); i++) {
- ((GUIConnection) ((Gene) node.getConnection(i)).getGUIObject()).setLockRecursively(value);
+ public void removeLocks(int value) {
+ locked -= value;
+ setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL);
+
+ for (int i = 0; i < lines.length; i++) {
+ parent.getGuiGene(node.getConnection(i)).removeLocks(value);
}
}
@Override
- public GUIConnection[] getConnections() {
- GUIConnection[] connections = new GUIConnection[GUI.resources.arity()];
- for (int c = 0; c < connections.length; c++) {
- connections[c] = (GUIConnection) ((Gene) node.getConnection(c)).getGUIObject();
+ public void setConnectionLine(GUIGene gene) {
+ lines[connectionIndex].setEndX(gene.getLayoutX() + Constants.NODE_RADIUS);
+ lines[connectionIndex].setEndY(gene.getLayoutY());
+ }
+
+ public void updateText() {
+ if (parent.isEvaluating()) {
+ text.setText(node.getFunction() + "\n" + node.getValue().toString());
+ } else {
+ text.setText(node.getFunction().toString());
}
- return connections;
+ }
+
+ public void setFunction(Function function) {
+ node.setFunction(function);
+ if (parent.isEvaluating()) {
+ parent.updateValues();
+ } else {
+ updateText();
+ }
+ }
+
+ public void setNode(Node newNode) {
+ node = newNode;
}
}