From 98e02b48ea5b83fa6c3247869b841b0afd260a89 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Sun, 23 Nov 2014 16:06:59 +0000 Subject: Commented some new files, optimised some methods, moved as much setting up as possible into the GUIGene constructors - ChromosomePane is slightly tidier now. --- src/jcgp/backend/population/Gene.java | 15 ++++- src/jcgp/gui/constants/Constants.java | 52 +++++++++------ src/jcgp/gui/constants/Position.java | 79 ++++++++++++++++++++--- src/jcgp/gui/population/ChromosomePane.java | 90 +++++--------------------- src/jcgp/gui/population/GUIConnection.java | 15 +++++ src/jcgp/gui/population/GUIGene.java | 63 ++++++++++++++++--- src/jcgp/gui/population/GUIInput.java | 45 ++++++++++--- src/jcgp/gui/population/GUIMutable.java | 10 +++ src/jcgp/gui/population/GUINode.java | 98 ++++++++++++++++++----------- src/jcgp/gui/population/GUIOutput.java | 55 ++++++++++++---- 10 files changed, 352 insertions(+), 170 deletions(-) diff --git a/src/jcgp/backend/population/Gene.java b/src/jcgp/backend/population/Gene.java index 47c8dfd..a22abc6 100644 --- a/src/jcgp/backend/population/Gene.java +++ b/src/jcgp/backend/population/Gene.java @@ -1,7 +1,12 @@ package jcgp.backend.population; /** - * TODO comment + * This abstract class defines a generic CGP gene. + * Three types of gene exist, primarily: {@code Input}, {@code Node} and {@code Output}. + *

+ * In practice, this class facilitates support for a graphical user interface. An arbitrary + * object can be associate with each gene using {@code setGUIObject(...)} and retrieved using + * {@code getGUIObject()}. * * @author Eduardo Pedroni * @@ -10,10 +15,18 @@ public abstract class Gene { private Object guiObject; + /** + * Sets a new GUI object. + * + * @param guiObject the object to set. + */ public void setGUIObject(Object guiObject) { this.guiObject = guiObject; } + /** + * @return the current GUI object associated with this instance. + */ public Object getGUIObject() { return guiObject; } diff --git a/src/jcgp/gui/constants/Constants.java b/src/jcgp/gui/constants/Constants.java index 350f8b1..d53b1ca 100644 --- a/src/jcgp/gui/constants/Constants.java +++ b/src/jcgp/gui/constants/Constants.java @@ -1,52 +1,62 @@ package jcgp.gui.constants; +import javafx.scene.paint.Paint; + /** * Holds the constants used in the GUI. * * @author Eduardo Pedroni * */ -public abstract class Constants { +public final class Constants { + /** + * Private constructor to prevent instantiation. + */ + private Constants(){} + /* Colours */ /** - * A string containing the hexadecimal colour used for representing neutrality. + * A {@code Paint} containing the colour used for representing neutrality. */ - public static final String NEUTRAL_COLOUR = "#FFFFFF"; + public static final Paint NEUTRAL_COLOUR = Paint.valueOf("#FFFFFF"); /** - * A string containing the hexadecimal colour used for representing a hard highlight. + * A {@code Paint} containing the colour used for representing a hard highlight. * A "hard" select, for instance, happens when an output path is locked on the chromosome * pane. */ - public static final String HARD_HIGHLIGHT_COLOUR = "#5496FF"; + public static final Paint HARD_HIGHLIGHT_COLOUR = Paint.valueOf("#5496FF"); /** - * A string containing the hexadecimal colour used for a medium highlight. + * A {@code Paint} containing the colour used for a medium highlight. * One example of such a selection is the colour applied to a node * when it is hovered over. */ - public static final String MEDIUM_HIGHLIGHT_COLOUR = "#75BAFF"; + public static final Paint MEDIUM_HIGHLIGHT_COLOUR = Paint.valueOf("#75BAFF"); /** - * A string containing the hexadecimal colour used for a soft highlight. + * A {@code Paint} containing the colour used for a soft highlight. * When hovering over a node, its connections are soft-selected. */ - public static final String SOFT_HIGHLIGHT_COLOUR = "#C7DFFF"; + public static final Paint SOFT_HIGHLIGHT_COLOUR = Paint.valueOf("#C7DFFF"); /** - * A string containing the hexadecimal colour used for representing a good selection. + * A {@code Paint} containing the colour used for representing a good selection. * Ideally a shade of green, used for instance when a manual connection is valid. */ - public static final String GOOD_SELECTION_COLOUR = "#38C25B"; + public static final Paint GOOD_SELECTION_COLOUR = Paint.valueOf("#38C25B"); /** - * A string containing the hexadecimal colour used for representing a neutral selection. + * A {@code Paint} containing the colour used for representing a neutral selection. * Ideally a shade of yellow, used for instance when a manual connection is already the * current connection. */ - public static final String NEUTRAL_SELECTION_COLOUR = "#FFEF73"; + public static final Paint NEUTRAL_SELECTION_COLOUR = Paint.valueOf("#FFEF73"); /** - * A string containing the hexadecimal colour used for representing a bad selection. + * A {@code Paint} containing the colour used for representing a bad selection. * Ideally a shade of red, use for instance when a manual connection is not valid. */ - public static final String BAD_SELECTION_COLOUR = "#FF5C5C"; - + public static final Paint BAD_SELECTION_COLOUR = Paint.valueOf("#FF5C5C"); + /** + * A {@code Paint} containing the colour used for the gene sockets. + */ + public static final Paint SOCKET_COLOUR = Paint.valueOf("#FFFFFF"); /* Sizes and distances */ @@ -74,13 +84,15 @@ public abstract class Constants { */ public static final double SPACING = 15; /** - * The angle across which the node's sockets are evently distributed. + * The margin between the genes and the edge of the chromosome pane. + */ + public static final double CHROMOSOME_PANE_MARGIN = 10; + /** + * The angle across which the node's sockets are evenly distributed. */ public static final double THETA = Math.PI / 1.4; /** - * The radius of the connection sockets, calculated as a function of - * NODE_RADIUS. - * + * The radius of the connection sockets, calculated as a function of NODE_RADIUS. */ public static final double SOCKET_RADIUS = Math.sqrt(NODE_RADIUS) / 1.8; /** diff --git a/src/jcgp/gui/constants/Position.java b/src/jcgp/gui/constants/Position.java index a13d21e..144ba6d 100644 --- a/src/jcgp/gui/constants/Position.java +++ b/src/jcgp/gui/constants/Position.java @@ -1,5 +1,6 @@ package jcgp.gui.constants; +import javafx.scene.shape.Circle; import javafx.scene.shape.Line; import jcgp.gui.GUI; import jcgp.gui.population.GUIGene; @@ -7,34 +8,98 @@ import jcgp.gui.population.GUIInput; import jcgp.gui.population.GUINode; import jcgp.gui.population.GUIOutput; +/** + * Abstracts the task of positioning GUI components. + *
+ * Do not instantiate this class; instead, use the {@code public static} methods provided. + * + * @author Eduardo Pedroni + * + */ public final class Position { + /** + * Private constructor to prevent instantiation. + */ + private Position() {} + + /** + * Sets the X and Y layouts of the specified input to the appropriate values, according to its index. + * + * @param input the {@code GUIInput} instance to relocate. + */ public static void place(GUIInput input) { - input.relocate(0, - input.getInput().getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING)); + // inputs are the first column, so we only worry about the margin and their index + input.relocate(Constants.CHROMOSOME_PANE_MARGIN, + input.getInput().getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN); } + /** + * Sets the X and Y layouts of the specified node to the appropriate values, according to its row and column values. + * This also connects the start of every line with its respective socket. Therefore, this method should be called at least + * once when the {@code GUINode} is instantiated. + * + * @param node the {@code GUINode} instance to relocate. + */ public static void place(GUINode node) { - // TODO cut down method calls - double xOffset = ((node.getNode().getColumn() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)); - double yOffset = node.getNode().getRow() * (2 * Constants.NODE_RADIUS + Constants.SPACING); + // calculate x and y offsets, in relation to the layout origin + double xOffset = (node.getNode().getColumn() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN; + double yOffset = node.getNode().getRow() * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN; + + // move node node.relocate(xOffset, yOffset); + // use the offset and the socket positions to connect the lines for (int i = 0; i < GUI.resources.arity(); i++) { node.getLine(i).setStartX(node.getSocket(i).getCenterX() + xOffset + Constants.NODE_RADIUS + Constants.SOCKET_RADIUS); node.getLine(i).setStartY(node.getSocket(i).getCenterY() + yOffset + Constants.NODE_RADIUS); } } + /** + * Sets the X and Y layouts of the specified output to the appropriate values, according to its index. + * This also connects the start of the output's single line to its single input socket.Therefore, + * this method should be called at least once when the {@code GUIOutput} is instantiated. + * + * @param output the {@code GUIOutput} instance to relocate. + */ public static void place(GUIOutput output) { - output.relocate(((GUI.resources.columns() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)), - output.getOutput().getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING)); + // the output's position is a function of the number of columns and its own index + output.relocate(((GUI.resources.columns() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.CHROMOSOME_PANE_MARGIN, + output.getOutput().getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN); output.getLine().setStartX(output.getLayoutX() - Constants.NODE_RADIUS); output.getLine().setStartY(output.getLayoutY()); } + + + /** + * Connects the end of a specified line to the specified gene. + * + * @param line the line to connect. + * @param target the target gene to connect to. + */ public static void connect(Line line, GUIGene target) { + // set line ends based on the layout position of the target line.setEndX(target.getLayoutX() + Constants.NODE_RADIUS); line.setEndY(target.getLayoutY()); } + + /** + * Relocates the given socket to the appropriate position given the + * socket's index. + * + * @param index the socket index. + * @param socket the {@code Circle} instance to relocate. + */ + public static void placeSocket(int index, Circle socket) { + // calculate the angle with respect to the x-axis + double angle = (((index + 1) / ((double) (GUI.resources.arity() + 1))) * Constants.THETA) - (Constants.THETA / 2); + // convert to cartesian form + double xPos = -Math.cos(angle) * Constants.NODE_RADIUS; + double yPos = Math.sin(angle) * Constants.NODE_RADIUS; + // set centre + socket.setCenterX(xPos); + socket.setCenterY(yPos); + } } diff --git a/src/jcgp/gui/population/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java index bf9db72..7826dcc 100644 --- a/src/jcgp/gui/population/ChromosomePane.java +++ b/src/jcgp/gui/population/ChromosomePane.java @@ -6,20 +6,13 @@ import javafx.scene.control.ScrollPane; import javafx.scene.layout.Pane; import javafx.scene.shape.Line; import jcgp.backend.population.Chromosome; -import jcgp.backend.population.Gene; -import jcgp.backend.population.Input; import jcgp.backend.population.Node; -import jcgp.backend.population.Output; import jcgp.gui.GUI; -import jcgp.gui.constants.Position; -import jcgp.gui.handlers.InputHandlers; -import jcgp.gui.handlers.NodeHandlers; -import jcgp.gui.handlers.OutputHandlers; /** * This extension of {@code ScrollPane} contains a series of * nodes, inputs and outputs spread across a grid. It also contains - * all of the connection lines overlaid over the nodes, inputs and outputs. + * all of the connection lines laid over the nodes, inputs and outputs. * * * @author Eduardo Pedroni @@ -33,8 +26,6 @@ public class ChromosomePane extends ScrollPane { private Pane content; - private int rows, columns; - private boolean target = false; public ChromosomePane(Chromosome chromosome) { @@ -42,58 +33,39 @@ public class ChromosomePane extends ScrollPane { ArrayList connectionLines = new ArrayList(); - rows = GUI.resources.rows(); - columns = GUI.resources.columns(); + int rows = GUI.resources.rows(); + int columns = GUI.resources.columns(); content = new Pane(); content.setId("content pane for genes"); - // generate the GUIGenes /* * inputs */ guiInputs = new GUIInput[GUI.resources.inputs()]; for (int i = 0; i < guiInputs.length; i++) { - // get the backend input - Input input = chromosome.getInput(i); - // make the GUI elements - guiInputs[i] = new GUIInput(input); - // assign the GUI object to the associated backend element - input.setGUIObject(guiInputs[i]); - // position, handlers - Position.place(guiInputs[i]); - InputHandlers.addHandlers(guiInputs[i]); + guiInputs[i] = new GUIInput(chromosome.getInput(i)); } + // add inputs to content pane content.getChildren().addAll(guiInputs); /* * nodes */ guiNodes = new GUINode[rows][columns]; - //double angle, xPos, yPos; - for (int c = 0; c < columns; c++) { for (int r = 0; r < rows; r++) { - // get the backend node - Node node = chromosome.getNode(r, c); // make the connection lines Line lines[] = new Line[GUI.resources.arity()]; for (int l = 0; l < lines.length; l++) { lines[l] = new Line(); - - Position.connect(lines[l], (GUIGene) ((Gene) node.getConnection(l)).getGUIObject()); - lines[l].setMouseTransparent(true); lines[l].setVisible(false); connectionLines.add(lines[l]); } - // make the GUI elements - guiNodes[r][c] = new GUINode(node, lines); - // assign the GUI object to the associated backend element - node.setGUIObject(guiNodes[r][c]); - // position, handlers - Position.place(guiNodes[r][c]); - NodeHandlers.addHandlers(guiNodes[r][c]); + // make the GUI element + guiNodes[r][c] = new GUINode(chromosome.getNode(r, c), lines); + // add node to content pane content.getChildren().add(guiNodes[r][c]); } } @@ -103,45 +75,24 @@ public class ChromosomePane extends ScrollPane { */ guiOutputs = new GUIOutput[GUI.resources.outputs()]; for (int i = 0; i < guiOutputs.length; i++) { - // get the backend output - Output output = chromosome.getOutput(i); - // make the GUI elements + // make the connection line Line line = new Line(); - Position.connect(line, (GUIGene) ((Gene) output.getSource()).getGUIObject()); line.setVisible(false); line.setMouseTransparent(true); - guiOutputs[i] = new GUIOutput(output, line); - // assign the GUI object to the associated backend element - output.setGUIObject(guiOutputs[i]); - // position, handlers - Position.place(guiOutputs[i]); - OutputHandlers.addHandlers(guiOutputs[i]); connectionLines.add(line); + // make the GUI element + guiOutputs[i] = new GUIOutput(chromosome.getOutput(i), line); } + // add outputs to content pane content.getChildren().addAll(guiOutputs); // add lines to the pane on top of genes content.getChildren().addAll(connectionLines); - setPrefWidth(620); + setPrefWidth(620); setContent(content); } - - /* - * does this work lol - */ - // 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; } @@ -151,8 +102,8 @@ public class ChromosomePane extends ScrollPane { } public void updateGenes(Chromosome chr) { - for (int r = 0; r < rows; r++) { - for (int c = 0; c < columns; c++) { + for (int r = 0; r < GUI.resources.rows(); r++) { + for (int c = 0; c < GUI.resources.columns(); c++) { guiNodes[r][c].setNode(chr.getNode(r, c)); } } @@ -186,15 +137,4 @@ public class ChromosomePane extends ScrollPane { // if the source was neither node nor output, something bad is happening throw new ClassCastException("Source was neither GUINode nor GUIOutput."); } - - // private 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(); - // } - // } } diff --git a/src/jcgp/gui/population/GUIConnection.java b/src/jcgp/gui/population/GUIConnection.java index 5fc857b..c17033a 100644 --- a/src/jcgp/gui/population/GUIConnection.java +++ b/src/jcgp/gui/population/GUIConnection.java @@ -2,8 +2,23 @@ package jcgp.gui.population; import jcgp.gui.population.GUIGene.GUIGeneState; +/** + * A loose equivalent to {@link jcgp.backend.population.Connection}. + *
+ * This defines behaviour that all GUI representations of connections + * should be capable of. + * + * @author Eduardo Pedroni + * + */ public interface GUIConnection { + /** + * Set the connection's state, but also recursively propagate that state + * all the way back to the inputs. + * + * @param state the state to set. + */ public void setStateRecursively(GUIGeneState state); } diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java index 032d217..d6f9638 100644 --- a/src/jcgp/gui/population/GUIGene.java +++ b/src/jcgp/gui/population/GUIGene.java @@ -21,33 +21,55 @@ import jcgp.gui.constants.Constants; */ public abstract class GUIGene extends Group { + /** + * This {@code enum} type defines a finite list of all states + * a gene can take. Each state represents a particular steady + * situation, and has its own GUI appearance associated with it: + * a combination of connection line visibility, gene background colour + * and other visual characteristics. + * + * @author Eduardo Pedroni + * + */ public enum GUIGeneState { + /** + * No user interaction at all. + */ NEUTRAL, + /** + * User is simply hovering over the node. + */ HOVER, + /** + * User is hovering over a node connected to this one. + */ EXTENDED_HOVER, + /** + * User is hovering over an output connected to this gene. + */ ACTIVE_HOVER } private GUIGeneState currentState = GUIGeneState.NEUTRAL; - private Text text = new Text(); - private Circle mainCircle = new Circle(Constants.NODE_RADIUS, Paint.valueOf(Constants.NEUTRAL_COLOUR)); + private Text text; + private Circle mainCircle; /** * Initialises the {@code Text} and {@code Circle} objects so that all genes are standardised. */ protected GUIGene() { + text = new Text(); text.setFont(Font.font("Arial", 12)); text.setTextOrigin(VPos.CENTER); text.setTextAlignment(TextAlignment.CENTER); text.setWrappingWidth(Constants.NODE_RADIUS * 2); text.setX(-Constants.NODE_RADIUS); - text.setVisible(true); + mainCircle = new Circle(Constants.NODE_RADIUS, Constants.NEUTRAL_COLOUR); mainCircle.setStroke(Paint.valueOf("black")); getChildren().addAll(mainCircle, text); - } /** @@ -59,26 +81,43 @@ public abstract class GUIGene extends Group { text.setText(newText); } + /** + * @return the gene's current state. + */ public GUIGeneState getState() { return currentState; } - public void setState(GUIGeneState newState) { + /** + * Gene states are standardised: all gene subclasses behave the same way in each state. + *
+ * This design choice was made for the sake of consistency. Rather than controlling the + * appearance of the genes with logic in the state transition method AND the mouse handlers, + * the states are now consistent across all types of gene. The mouse handlers implement + * whatever logic is necessary to determine the gene's new state given a certain user input, + * but the states themselves are the same for all genes. + *
+ * The transition logic for each type of gene is defined in its respective handler class: + * {@code InputHandlers}, {@code NodeHandlers} and {@code OutputHandlers}. + * + * @param newState the gene's new state. + */ + public final void setState(GUIGeneState newState) { switch (newState) { case NEUTRAL: - mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR)); + mainCircle.setFill(Constants.NEUTRAL_COLOUR); setLinesVisible(false); break; case HOVER: - mainCircle.setFill(Paint.valueOf(Constants.MEDIUM_HIGHLIGHT_COLOUR)); + mainCircle.setFill(Constants.MEDIUM_HIGHLIGHT_COLOUR); setLinesVisible(true); break; case EXTENDED_HOVER: - mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR)); + mainCircle.setFill(Constants.SOFT_HIGHLIGHT_COLOUR); setLinesVisible(false); break; case ACTIVE_HOVER: - mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR)); + mainCircle.setFill(Constants.SOFT_HIGHLIGHT_COLOUR); setLinesVisible(true); break; @@ -88,5 +127,11 @@ public abstract class GUIGene extends Group { currentState = newState; } + /** + * For the sake of practicality, all {@code GUIGene} instances must implement this + * method. It sets the visibility of all of the gene's lines, if it has any. + * + * @param value the visibility value. + */ protected abstract void setLinesVisible(boolean value); } diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java index 68952f6..9b5f567 100644 --- a/src/jcgp/gui/population/GUIInput.java +++ b/src/jcgp/gui/population/GUIInput.java @@ -4,43 +4,68 @@ import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import jcgp.backend.population.Input; import jcgp.gui.constants.Constants; +import jcgp.gui.constants.Position; +import jcgp.gui.handlers.InputHandlers; /** - * + * The GUI counterpart of {@link jcgp.backend.population.Input}. This is a + * subclass of {@code GUIGene} which represents a chromosome input. * * @author Eduardo Pedroni - * */ public class GUIInput extends GUIGene implements GUIConnection { private Input input; /** - * @param input + * Instantiate {@code GUIInput} given an {@code Input}. + * + * @param input the associated backend input. */ public GUIInput(final Input input) { super(); - + // store the input, associate itself with it this.input = input; + input.setGUIObject(this); + // inputs only have a single output socket 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); + outputSocket.setId(String.valueOf(0)); + getChildren().add(outputSocket); + + // relocate to the right position, add mouse handlers + Position.place(this); + InputHandlers.addHandlers(this); } + /** + * @return the {@code Input} instance associated with this object. + */ public Input getInput() { return input; } + + /** + * Associates this instance with a new input. + * + * @param input the new input. + */ + void setInput(Input input) { + this.input = input; + } + /* (non-Javadoc) + * @see jcgp.gui.population.GUIConnection#setStateRecursively(jcgp.gui.population.GUIGene.GUIGeneState) + */ @Override public void setStateRecursively(GUIGeneState state) { setState(state); } + /* (non-Javadoc) + * @see jcgp.gui.population.GUIGene#setLinesVisible(boolean) + */ @Override - protected void setLinesVisible(boolean value) { - // blank - } + protected void setLinesVisible(boolean value) {} } diff --git a/src/jcgp/gui/population/GUIMutable.java b/src/jcgp/gui/population/GUIMutable.java index e51b54e..61a8f48 100644 --- a/src/jcgp/gui/population/GUIMutable.java +++ b/src/jcgp/gui/population/GUIMutable.java @@ -1,6 +1,16 @@ package jcgp.gui.population; +/** + * A loose equivalent to {@link jcgp.backend.population.Mutable}. + *
+ * This defines behaviour that all GUI representations of mutables + * should be capable of. + * + * @author Eduardo Pedroni + * + */ public interface GUIMutable { + } diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java index f8f2e20..230d167 100644 --- a/src/jcgp/gui/population/GUINode.java +++ b/src/jcgp/gui/population/GUINode.java @@ -1,6 +1,5 @@ package jcgp.gui.population; -import javafx.scene.control.Label; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; @@ -8,63 +7,109 @@ import jcgp.backend.population.Gene; import jcgp.backend.population.Node; 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 { private Node node; private Line[] lines; private Circle[] sockets; + /** + * 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) { super(); - // store references + // store references, associate with node this.node = node; this.lines = lines; - - 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); + node.setGUIObject(this); - Circle output = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white")); + // create the output socket + Circle output = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_COLOUR); output.setStroke(Paint.valueOf("black")); + // create input sockets 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] = new Circle(Constants.SOCKET_RADIUS, Constants.SOCKET_COLOUR); sockets[l].setStroke(Paint.valueOf("black")); + sockets[l].setId(String.valueOf(l)); + // relocate them + Position.placeSocket(l, sockets[l]); + Position.connect(lines[l], (GUIGene) ((Gene) node.getConnection(l)).getGUIObject()); } + // add elements getChildren().addAll(sockets); - getChildren().addAll(output, connectionNumber); + getChildren().add(output); + + // relocate node, add handlers + Position.place(this); + NodeHandlers.addHandlers(this); } + /** + * @return the {@code Node} instance associated with this object. + */ public Node getNode() { return node; } - void setNode(Node node2) { - // TODO Auto-generated method stub - + /** + * Associates this instance with a new node. + * + * @param node the new node. + */ + void setNode(Node node) { + this.node = node; } + /** + * Returns one of this object's connection lines. Lines are + * indexed in the same order as sockets and the connections + * they represent. + * + * @param index the line to return. + * @return the indexed line object. + */ public Line getLine(int index) { return lines[index]; } + /** + * @return the entire {@code Line} array. + */ public Line[] getLines() { return lines; } + /** + * Returns one of this object's connection sockets. They are + * indexed in the same order as lines and the connections + * they represent. + * + * @param index the socket to return. + * @return the indexed socket object. + */ public Circle getSocket(int index) { return sockets[index]; } + /** + * @return the entire {@code Socket} array. + */ public Circle[] getSockets() { return sockets; } @@ -83,21 +128,4 @@ public class GUINode extends GUIGene implements GUIMutable, GUIConnection { lines[i].setVisible(value); } } - -// @Override -// public void updateLines() { -// 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) + Constants.SOCKET_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); -// } -// } -// } - } diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java index a07fd90..b281833 100644 --- a/src/jcgp/gui/population/GUIOutput.java +++ b/src/jcgp/gui/population/GUIOutput.java @@ -1,42 +1,71 @@ 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.Gene; import jcgp.backend.population.Output; import jcgp.gui.constants.Constants; +import jcgp.gui.constants.Position; +import jcgp.gui.handlers.OutputHandlers; +/** + * The GUI counterpart of {@link jcgp.backend.population.Output}. This is a + * subclass of {@code GUIGene} which represents a chromosome output. + * + * @author Eduardo Pedroni + */ public class GUIOutput extends GUIGene implements GUIMutable { private Output output; private Line line; + /** + * Instantiate {@code GUIOutput} given an {@code Output} and the line needed + * to show its connection. + * + * @param output the associated backend output. + * @param line the line used to display connection. + */ public GUIOutput(final Output output, Line line) { super(); + // store references, associate with backend object this.output = output; this.line = line; - - Circle socket = new Circle(-Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white")); - socket.setId(String.valueOf(0)); + output.setGUIObject(this); + + // create input socket + Circle socket = new Circle(-Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_COLOUR); socket.setStroke(Paint.valueOf("black")); + socket.setId(String.valueOf(0)); + Position.connect(line, (GUIGene) ((Gene) output.getSource()).getGUIObject()); + getChildren().add(socket); - 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); + // relocate output, add handlers + Position.place(this); + OutputHandlers.addHandlers(this); } - void setOutput(Output output2) { - + /** + * Associates this instance with a new output. + * + * @param output the new output. + */ + void setOutput(Output output) { + this.output = output; } - + + /** + * @return the {@code Output} instance associated with this object. + */ public Output getOutput() { return output; } + + /** + * @return this output's single connection line. + */ public Line getLine() { return line; } -- cgit v1.2.3