From d0718fe4762f6a50ec851085cb5d0e6d39ccc1b0 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Sat, 15 Nov 2014 23:30:20 +0000 Subject: Added GUIConnection and GUIMutable, started reintroducing lines but positioning is not working correctly. --- src/jcgp/gui/GUI.java | 4 ++ src/jcgp/gui/handlers/GUIHandlers.java | 3 - src/jcgp/gui/population/ChromosomePane.java | 106 +++++++++++++++++++++------- src/jcgp/gui/population/GUIConnection.java | 7 ++ src/jcgp/gui/population/GUIGene.java | 44 ++++++++++-- src/jcgp/gui/population/GUIInput.java | 18 ++++- src/jcgp/gui/population/GUIMutable.java | 9 +++ src/jcgp/gui/population/GUINode.java | 67 ++++++++++++++++-- src/jcgp/gui/population/GUIOutput.java | 23 ++++-- src/jcgp/gui/population/PopulationPane.java | 2 +- 10 files changed, 232 insertions(+), 51 deletions(-) create mode 100644 src/jcgp/gui/population/GUIConnection.java create mode 100644 src/jcgp/gui/population/GUIMutable.java diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java index fde0c65..689f38c 100644 --- a/src/jcgp/gui/GUI.java +++ b/src/jcgp/gui/GUI.java @@ -13,6 +13,7 @@ import javafx.stage.Stage; import javafx.stage.WindowEvent; import jcgp.JCGP; import jcgp.backend.modules.problem.TestCaseProblem.TestCase; +import jcgp.backend.resources.Resources; import jcgp.gui.console.ConsolePane; import jcgp.gui.dragresize.HorizontalDragResize; import jcgp.gui.dragresize.VerticalDragResize; @@ -69,6 +70,8 @@ public class GUI extends Application { */ private final JCGP jcgp; + public static Resources resources; + /** * Start JCGP with the user interface. @@ -87,6 +90,7 @@ public class GUI extends Application { */ public GUI() { jcgp = new JCGP(); + resources = jcgp.getResources(); functionSelector = new FunctionSelector(jcgp.getResources().getFunctionSet()); /* diff --git a/src/jcgp/gui/handlers/GUIHandlers.java b/src/jcgp/gui/handlers/GUIHandlers.java index d5b48b6..ca2a324 100644 --- a/src/jcgp/gui/handlers/GUIHandlers.java +++ b/src/jcgp/gui/handlers/GUIHandlers.java @@ -20,11 +20,8 @@ public class GUIHandlers { } }; - public static void addHandlers(GUIGene gene) { gene.addEventHandler(MouseEvent.MOUSE_ENTERED, mouseEnteredHandler); gene.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler); } - - } diff --git a/src/jcgp/gui/population/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java index f09d452..ede01e0 100644 --- a/src/jcgp/gui/population/ChromosomePane.java +++ b/src/jcgp/gui/population/ChromosomePane.java @@ -1,7 +1,10 @@ package jcgp.gui.population; +import java.util.ArrayList; + import javafx.scene.control.ScrollPane; import javafx.scene.layout.Pane; +import javafx.scene.shape.Line; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; @@ -26,16 +29,20 @@ public class ChromosomePane extends ScrollPane { private GUIInput[] guiInputs; private GUIOutput[] guiOutputs; + private ArrayList connectionLines; + private Pane content; private int rows, columns; private boolean target = false; - public ChromosomePane(Chromosome chromosome, GUI gui, PopulationPane parent) { + public ChromosomePane(Chromosome chromosome) { super(); - final Resources resources = gui.getExperiment().getResources(); + final Resources resources = GUI.resources; + + connectionLines = new ArrayList(); rows = resources.rows(); columns = resources.columns(); @@ -44,7 +51,9 @@ public class ChromosomePane extends ScrollPane { content.setId("content pane for genes"); // generate the GUIGenes - // inputs + /* + * inputs + */ guiInputs = new GUIInput[resources.inputs()]; for (int i = 0; i < guiInputs.length; i++) { // make the GUI elements @@ -54,43 +63,91 @@ public class ChromosomePane extends ScrollPane { GUIHandlers.addHandlers(guiInputs[i]); } content.getChildren().addAll(guiInputs); - // nodes + + /* + * nodes + */ guiNodes = new GUINode[rows][columns]; + double angle, xPos, yPos; for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { + // make the connection lines + Line lines[] = new Line[resources.arity()]; + for (int l = 0; l < lines.length; l++) { + angle = ((((double) (l + 1)) / ((double) (lines.length + 1))) * Constants.THETA) - (Constants.THETA / 2); + xPos = (-Math.cos(angle) * Constants.NODE_RADIUS) + (((c + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS); + yPos = (Math.sin(angle) * Constants.NODE_RADIUS) + ((r * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS); + + lines[l] = new Line(xPos, yPos, xPos, yPos); + lines[l].setMouseTransparent(true); + //lines[l].setVisible(false); + connectionLines.add(lines[l]); + } // make the GUI elements - guiNodes[r][c] = new GUINode(chromosome.getNode(r, c)); + guiNodes[r][c] = new GUINode(chromosome.getNode(r, c), lines) { + @Override + public GUIConnection getGUIConnection(Connection connection) { + if (connection instanceof Input) { + return guiInputs[((Input) connection).getIndex()]; + } else if (connection instanceof Node) { + return guiNodes[((Node) connection).getRow()][((Node) connection).getColumn()]; + } else { + // something bad happened! + throw new ClassCastException(); + } + } + }; guiNodes[r][c].relocate(((chromosome.getNode(r, c).getColumn() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS, (chromosome.getNode(r, c).getRow() * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS); GUIHandlers.addHandlers(guiNodes[r][c]); } content.getChildren().addAll(guiNodes[r]); } - // outputs + + /* + * outputs + */ guiOutputs = new GUIOutput[resources.outputs()]; for (int i = 0; i < guiOutputs.length; i++) { // make the GUI elements - guiOutputs[i] = new GUIOutput(chromosome.getOutput(i)); + guiOutputs[i] = new GUIOutput(chromosome.getOutput(i)) { + + @Override + public GUIConnection getGUIConnection(Connection connection) { + if (connection instanceof Input) { + return guiInputs[((Input) connection).getIndex()]; + } else if (connection instanceof Node) { + return guiNodes[((Node) connection).getRow()][((Node) connection).getColumn()]; + } else { + // something bad happened! + throw new ClassCastException(); + } + } + + }; guiOutputs[i].relocate(((resources.columns() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS, (chromosome.getOutput(i).getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS); GUIHandlers.addHandlers(guiOutputs[i]); } content.getChildren().addAll(guiOutputs); + // add lines to the pane as on top of genes + content.getChildren().addAll(connectionLines); + setPrefWidth(620); setContent(content); } - protected GUIGene getGuiGene(Connection gene) { - if (gene instanceof Input) { - return guiInputs[((Input) gene).getIndex()]; - } else if (gene instanceof Node) { - return guiNodes[((Node) gene).getRow()][((Node) gene).getColumn()]; - } else { - // something bad happened! - throw new ClassCastException(); - } - } +// protected GUIGene getGuiGene(Connection gene) { +// if (gene instanceof Input) { +// return guiInputs[((Input) gene).getIndex()]; +// } else if (gene instanceof Node) { +// return guiNodes[((Node) gene).getRow()][((Node) gene).getColumn()]; +// } else { +// // something bad happened! +// throw new ClassCastException(); +// } +// } protected boolean isTarget() { return target; @@ -111,7 +168,7 @@ public class ChromosomePane extends ScrollPane { } } - public static boolean isAllowed(GUIGene source, GUIGene target) { + public static boolean isAllowed(GUIMutable source, GUIConnection target) { if (source instanceof GUINode) { // if the source is a node, all inputs and some nodes are valid if (target instanceof GUIInput) { @@ -119,23 +176,18 @@ public class ChromosomePane extends ScrollPane { } 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() <= 1 /* TODO this should be levels back */) { + if (s.getColumn() - t.getColumn() > 0 && s.getColumn() - t.getColumn() <= GUI.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."); } + return false; } 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."); + // this should never happen... + return false; } } // if the source was neither node nor output, something bad is happening diff --git a/src/jcgp/gui/population/GUIConnection.java b/src/jcgp/gui/population/GUIConnection.java new file mode 100644 index 0000000..76d4a41 --- /dev/null +++ b/src/jcgp/gui/population/GUIConnection.java @@ -0,0 +1,7 @@ +package jcgp.gui.population; + +public abstract class GUIConnection extends GUIGene { + + abstract void activeHover(boolean value); + +} diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java index eb7070c..6e61875 100644 --- a/src/jcgp/gui/population/GUIGene.java +++ b/src/jcgp/gui/population/GUIGene.java @@ -21,9 +21,17 @@ import jcgp.gui.constants.Constants; */ public abstract class GUIGene extends Group { + public enum GUIGeneState { + NEUTRAL, + HOVER, + EXTENDED_HOVER + } + + private GUIGeneState currentState = GUIGeneState.NEUTRAL; + private Text text = new Text(); - protected final Circle mainCircle = new Circle(Constants.NODE_RADIUS, Paint.valueOf(Constants.NEUTRAL_COLOUR)); - + private Circle mainCircle = new Circle(Constants.NODE_RADIUS, Paint.valueOf(Constants.NEUTRAL_COLOUR)); + /** * Initialises the {@code Text} and {@code Circle} objects so that all genes are standardised. */ @@ -34,12 +42,12 @@ public abstract class GUIGene extends Group { text.setWrappingWidth(Constants.NODE_RADIUS * 2); text.setX(-Constants.NODE_RADIUS); text.setVisible(true); - + mainCircle.setStroke(Paint.valueOf("black")); - + getChildren().addAll(mainCircle, text); } - + /** * Sets the gene's text field. * @@ -48,8 +56,30 @@ public abstract class GUIGene extends Group { public void setText(String newText) { text.setText(newText); } - + + public GUIGeneState getState() { + return currentState; + } + + void setState(GUIGeneState newState) { + switch (newState) { + case NEUTRAL: + mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR)); + break; + case HOVER: + mainCircle.setFill(Paint.valueOf(Constants.MEDIUM_HIGHLIGHT_COLOUR)); + break; + case EXTENDED_HOVER: + mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR)); + break; + + default: + break; + } + currentState = newState; + } + public abstract void mouseEnter(); public abstract void mouseExit(); - + } diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java index d29851e..0fdf841 100644 --- a/src/jcgp/gui/population/GUIInput.java +++ b/src/jcgp/gui/population/GUIInput.java @@ -1,6 +1,7 @@ package jcgp.gui.population; import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; import jcgp.backend.population.Input; import jcgp.gui.constants.Constants; @@ -10,23 +11,34 @@ import jcgp.gui.constants.Constants; * @author Eduardo Pedroni * */ -public class GUIInput extends GUIGene { +public class GUIInput extends GUIConnection { /** * @param input */ public GUIInput(final Input input) { super(); + + Circle outputSocket = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white")); + outputSocket.setId(String.valueOf(0)); + outputSocket.setStroke(Paint.valueOf("black")); + + getChildren().addAll(outputSocket); } @Override public void mouseEnter() { - mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR)); + setState(GUIGeneState.HOVER); } @Override public void mouseExit() { - mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR)); + setState(GUIGeneState.NEUTRAL); + } + + @Override + public void activeHover(boolean value) { + setState(value ? GUIGeneState.EXTENDED_HOVER : GUIGeneState.NEUTRAL); } } diff --git a/src/jcgp/gui/population/GUIMutable.java b/src/jcgp/gui/population/GUIMutable.java new file mode 100644 index 0000000..0a28ed6 --- /dev/null +++ b/src/jcgp/gui/population/GUIMutable.java @@ -0,0 +1,9 @@ +package jcgp.gui.population; + +import jcgp.backend.population.Connection; + +public interface GUIMutable { + + GUIConnection getGUIConnection(Connection connection); + +} diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java index 7b59162..6b6bafc 100644 --- a/src/jcgp/gui/population/GUINode.java +++ b/src/jcgp/gui/population/GUINode.java @@ -1,19 +1,60 @@ package jcgp.gui.population; +import javafx.scene.control.Label; import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Line; +import jcgp.backend.population.Input; import jcgp.backend.population.Node; +import jcgp.gui.GUI; import jcgp.gui.constants.Constants; -public class GUINode extends GUIGene { +public abstract class GUINode extends GUIConnection implements GUIMutable { private Node node; + private Line[] connectionLines; - - public GUINode(final Node node) { + public GUINode(Node node, Line[] connectionLines) { super(); - // store references this.node = node; + this.connectionLines = connectionLines; + + 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")); + + Circle[] sockets = new Circle[GUI.resources.arity()]; + double angle, xPos, yPos; + for (int l = 0; l < sockets.length; l++) { + angle = (((l + 1) / ((double) (GUI.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)); + sockets[l].setStroke(Paint.valueOf("black")); + } + + getChildren().addAll(sockets); + getChildren().addAll(output, connectionNumber); + + for (int l = 0; l < connectionLines.length; l++) { + if (node.getConnection(l) instanceof Node) { + int row = ((Node) node.getConnection(l)).getRow(), + column = ((Node) node.getConnection(l)).getColumn(); + connectionLines[l].setEndX(((column + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + 2 * Constants.NODE_RADIUS); + connectionLines[l].setEndY((row * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS); + } else if (node.getConnection(l) instanceof Input) { + int inputIndex = ((Input) node.getConnection(l)).getIndex(); + connectionLines[l].setEndX(2 * Constants.NODE_RADIUS); + connectionLines[l].setEndY(inputIndex * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.NODE_RADIUS); + } + } + } public Node getNode() { @@ -27,12 +68,26 @@ public class GUINode extends GUIGene { @Override public void mouseEnter() { - mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR)); + setState(GUIGeneState.HOVER); + for (int i = 0; i < GUI.resources.arity(); i++) { + getGUIConnection(node.getConnection(i)).setState(GUIGeneState.EXTENDED_HOVER); + } } @Override public void mouseExit() { - mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR)); + setState(GUIGeneState.NEUTRAL); + for (int i = 0; i < GUI.resources.arity(); i++) { + getGUIConnection(node.getConnection(i)).setState(GUIGeneState.NEUTRAL); + } + } + + @Override + public void activeHover(boolean value) { + setState(value ? GUIGeneState.EXTENDED_HOVER : GUIGeneState.NEUTRAL); + for (int i = 0; i < GUI.resources.arity(); i++) { + getGUIConnection(node.getConnection(i)).activeHover(value); + } } } diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java index ccc96c3..8ae9485 100644 --- a/src/jcgp/gui/population/GUIOutput.java +++ b/src/jcgp/gui/population/GUIOutput.java @@ -1,17 +1,29 @@ package jcgp.gui.population; +import javafx.scene.control.Label; import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; import jcgp.backend.population.Output; import jcgp.gui.constants.Constants; -public class GUIOutput extends GUIGene { +public abstract class GUIOutput extends GUIGene implements GUIMutable { private Output output; public GUIOutput(final Output output) { super(); - this.output = output; + + Circle socket = new Circle(-Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white")); + socket.setId(String.valueOf(0)); + socket.setStroke(Paint.valueOf("black")); + + Label connectionLabel = new Label("S"); + connectionLabel.setStyle("-fx-background-color:rgb(255, 255, 255); -fx-border-color:rgba(0, 0, 0, 0.5);"); + connectionLabel.relocate(socket.getCenterX() + 5, socket.getCenterY() - 10); + connectionLabel.setVisible(false); + + getChildren().addAll(socket, connectionLabel); } void setOutput(Output output2) { @@ -21,11 +33,14 @@ public class GUIOutput extends GUIGene { @Override public void mouseEnter() { - mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR)); + setState(GUIGeneState.HOVER); + getGUIConnection(output.getSource()).activeHover(true); } @Override public void mouseExit() { - mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR)); + setState(GUIGeneState.NEUTRAL); + getGUIConnection(output.getSource()).activeHover(false); } + } diff --git a/src/jcgp/gui/population/PopulationPane.java b/src/jcgp/gui/population/PopulationPane.java index 31f6c33..51b5ba4 100644 --- a/src/jcgp/gui/population/PopulationPane.java +++ b/src/jcgp/gui/population/PopulationPane.java @@ -27,7 +27,7 @@ public class PopulationPane extends TabPane { Tab tab; ChromosomePane cp; for (int i = 0; i < jcgp.getResources().populationSize(); i++) { - cp = new ChromosomePane(jcgp.getPopulation().get(i), gui, this); + cp = new ChromosomePane(jcgp.getPopulation().get(i)); tab = new Tab("Chr " + i); tab.setContent(cp); getTabs().add(tab); -- cgit v1.2.3