aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/gui/population
diff options
context:
space:
mode:
Diffstat (limited to 'src/jcgp/gui/population')
-rw-r--r--src/jcgp/gui/population/ChromosomePane.java140
-rw-r--r--src/jcgp/gui/population/FunctionSelector.java80
-rw-r--r--src/jcgp/gui/population/GUIConnection.java32
-rw-r--r--src/jcgp/gui/population/GUIGene.java187
-rw-r--r--src/jcgp/gui/population/GUIInput.java70
-rw-r--r--src/jcgp/gui/population/GUIMutable.java20
-rw-r--r--src/jcgp/gui/population/GUINode.java134
-rw-r--r--src/jcgp/gui/population/GUIOutput.java79
-rw-r--r--src/jcgp/gui/population/PopulationPane.java75
9 files changed, 817 insertions, 0 deletions
diff --git a/src/jcgp/gui/population/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java
new file mode 100644
index 0000000..a87a054
--- /dev/null
+++ b/src/jcgp/gui/population/ChromosomePane.java
@@ -0,0 +1,140 @@
+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.Node;
+import jcgp.gui.GUI;
+
+/**
+ * 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 laid over the nodes, inputs and outputs.
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class ChromosomePane extends ScrollPane {
+
+ private GUIInput[] guiInputs;
+ private GUINode[][] guiNodes;
+ private GUIOutput[] guiOutputs;
+
+ private Pane content;
+
+ private boolean target = false;
+
+ public ChromosomePane(Chromosome chromosome) {
+ super();
+
+ ArrayList<Line> connectionLines = new ArrayList<Line>();
+
+ int rows = GUI.resources.rows();
+ int columns = GUI.resources.columns();
+
+ content = new Pane();
+ content.setId("content pane for genes");
+
+ /*
+ * inputs
+ */
+ guiInputs = new GUIInput[GUI.resources.inputs()];
+ for (int i = 0; i < guiInputs.length; i++) {
+ guiInputs[i] = new GUIInput(chromosome.getInput(i));
+ }
+ // add inputs to content pane
+ content.getChildren().addAll(guiInputs);
+
+ /*
+ * nodes
+ */
+ guiNodes = new GUINode[rows][columns];
+ for (int c = 0; c < columns; c++) {
+ for (int r = 0; r < rows; r++) {
+ // make the connection lines
+ Line lines[] = new Line[GUI.resources.arity()];
+ for (int l = 0; l < lines.length; l++) {
+ lines[l] = new Line();
+ lines[l].setMouseTransparent(true);
+ lines[l].setVisible(false);
+ connectionLines.add(lines[l]);
+ }
+ // 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]);
+ }
+ }
+
+ /*
+ * outputs
+ */
+ guiOutputs = new GUIOutput[GUI.resources.outputs()];
+ for (int i = 0; i < guiOutputs.length; i++) {
+ // make the connection line
+ Line line = new Line();
+ line.setVisible(false);
+ line.setMouseTransparent(true);
+ 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);
+ setContent(content);
+ }
+
+ protected boolean isTarget() {
+ return target;
+ }
+
+ protected void setTarget(boolean newValue) {
+ target = newValue;
+ }
+
+ public void updateGenes(Chromosome chr) {
+ 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));
+ }
+ }
+ for (int i = 0; i < guiOutputs.length; i++) {
+ guiOutputs[i].setOutput(chr.getOutput(i));
+ }
+ }
+
+ 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) {
+ 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() <= GUI.resources.levelsBack()) {
+ return true;
+ }
+ }
+ 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 {
+ // this should never happen...
+ return false;
+ }
+ }
+ // if the source was neither node nor output, something bad is happening
+ throw new ClassCastException("Source was neither GUINode nor GUIOutput.");
+ }
+}
diff --git a/src/jcgp/gui/population/FunctionSelector.java b/src/jcgp/gui/population/FunctionSelector.java
new file mode 100644
index 0000000..14614e5
--- /dev/null
+++ b/src/jcgp/gui/population/FunctionSelector.java
@@ -0,0 +1,80 @@
+package jcgp.gui.population;
+
+import javafx.event.EventHandler;
+import javafx.scene.control.Label;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.VBox;
+import jcgp.backend.function.FunctionSet;
+import jcgp.gui.constants.Constants;
+
+/**
+ * A menu class, exposes all of the allowed functions
+ * when called by a node, so that the node function can be changed.
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class FunctionSelector extends VBox {
+
+ private GUINode target;
+
+ public FunctionSelector(FunctionSet functionSet) {
+ setFillWidth(true);
+ setVisible(false);
+ setStyle("-fx-border-color: #A0A0A0; -fx-border-width: 1 1 0 1");
+
+ remakeFunctions(functionSet);
+
+ addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ dismiss();
+ }
+ });
+ }
+
+ public void remakeFunctions(final FunctionSet fs) {
+ getChildren().clear();
+
+ for (int i = 0; i < fs.getAllowedFunctionCount(); i++) {
+ final int index = i;
+ Label l = new Label(fs.getAllowedFunction(i).toString());
+ l.setMaxWidth(Double.MAX_VALUE);
+ l.setStyle("-fx-background-color: #FFFFFF; -fx-border-color: #A0A0A0; -fx-border-width: 0 0 1 0; -fx-padding: 2");
+
+ l.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ ((Label) event.getSource()).setStyle("-fx-background-color: " + Constants.SOFT_HIGHLIGHT_PAINT + "; -fx-border-color: #B0B0B0; -fx-border-width: 0 0 1 0; -fx-padding: 2");
+ }
+ });
+ l.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ ((Label) event.getSource()).setStyle("-fx-background-color: #FFFFFF; -fx-border-color: #A0A0A0; -fx-border-width: 0 0 1 0; -fx-padding: 2");
+ }
+ });
+ l.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ //target.setFunction(fs.getAllowedFunction(index));
+ dismiss();
+ }
+ });
+
+ getChildren().add(l);
+ }
+ }
+
+ public void relocateAndShow(MouseEvent event, GUINode node) {
+ relocate(event.getSceneX() - 5, event.getSceneY() - 5);
+ target = node;
+ setVisible(true);
+ }
+
+ private void dismiss() {
+ setVisible(false);
+ }
+
+}
diff --git a/src/jcgp/gui/population/GUIConnection.java b/src/jcgp/gui/population/GUIConnection.java
new file mode 100644
index 0000000..dc7fcc8
--- /dev/null
+++ b/src/jcgp/gui/population/GUIConnection.java
@@ -0,0 +1,32 @@
+package jcgp.gui.population;
+
+import jcgp.gui.population.GUIGene.GUIGeneState;
+
+/**
+ * A loose equivalent to {@link jcgp.backend.population.Connection}.
+ * <br>
+ * 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);
+
+ /**
+ * Add or remove a lock, but also recursively propagate that change
+ * all the way back to the inputs.
+ *
+ * @param value true to lock, false to unlock.
+ */
+ public void setLockRecursively(boolean value);
+
+}
diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java
new file mode 100644
index 0000000..5e6107f
--- /dev/null
+++ b/src/jcgp/gui/population/GUIGene.java
@@ -0,0 +1,187 @@
+package jcgp.gui.population;
+
+import javafx.geometry.VPos;
+import javafx.scene.Group;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Circle;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+import javafx.scene.text.TextAlignment;
+import jcgp.gui.constants.Constants;
+
+/**
+ * Defines the general behaviour of the visual representation of each chromosome gene.
+ * <br><br>
+ * In practice, this is subclass of {@code javafx.scene.Group} containing a {@code Circle}
+ * object and a {@code Text} object. Subclasses may add further elements to the group, for
+ * instance to display connection input and output sockets.
+ * <br><br>
+ * Genes also contain a locked property. When locked, some gene states behave slightly
+ * differently. This is used so genes remain highlighted even in the neutral state. The
+ * gene lock is in fact recursive; a gene can be locked multiple times and only unlocking
+ * it as many times will actually revert it back to its unlocked state. This allows multiple
+ * pathways to lock the same gene independently without affecting each other; the gene remains
+ * locked until no pathways are locking it.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+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,
+
+ GOOD_TARGET,
+
+ NEUTRAL_TARGET,
+
+ BAD_TARGET
+ }
+
+ private GUIGeneState currentState = GUIGeneState.NEUTRAL;
+
+ private Text text;
+ private Circle mainCircle;
+
+ /**
+ * Recursive lock; lock == 0 means unlocked, lock > 0 means locked.
+ * Accessing using {@code setLock(...)}.
+ */
+ private int lock = 0;
+
+ /**
+ * 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);
+
+ mainCircle = new Circle(Constants.NODE_RADIUS, Constants.NEUTRAL_PAINT);
+ mainCircle.setStroke(Paint.valueOf("black"));
+
+ getChildren().addAll(mainCircle, text);
+ }
+
+ /**
+ * Sets the gene's text field.
+ *
+ * @param newText the text string to be displayed.
+ */
+ public void setText(String newText) {
+ text.setText(newText);
+ }
+
+ /**
+ * @return the gene's current state.
+ */
+ public GUIGeneState getState() {
+ return currentState;
+ }
+
+ /**
+ * Gene states are standardised: all gene subclasses behave the same way in each state.
+ * <br>
+ * 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.
+ * <br>
+ * 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(isLocked() ? Constants.HARD_HIGHLIGHT_PAINT : Constants.NEUTRAL_PAINT);
+ setLinesVisible(isLocked());
+ break;
+ case HOVER:
+ mainCircle.setFill(Constants.MEDIUM_HIGHLIGHT_PAINT);
+ setLinesVisible(true);
+ break;
+ case EXTENDED_HOVER:
+ mainCircle.setFill(Constants.SOFT_HIGHLIGHT_PAINT);
+ setLinesVisible(isLocked());
+ break;
+ case ACTIVE_HOVER:
+ mainCircle.setFill(Constants.SOFT_HIGHLIGHT_PAINT);
+ setLinesVisible(true);
+ break;
+ case GOOD_TARGET:
+ mainCircle.setFill(Constants.GOOD_SELECTION_PAINT);
+ break;
+ case NEUTRAL_TARGET:
+ mainCircle.setFill(Constants.NEUTRAL_SELECTION_PAINT);
+ break;
+ case BAD_TARGET:
+ mainCircle.setFill(Constants.BAD_SELECTION_PAINT);
+ break;
+ }
+ 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);
+
+ /**
+ * @return true if the gene is locked, false otherwise.
+ */
+ public boolean isLocked() {
+ return lock > 0;
+ }
+
+ /**
+ * Locks or unlocks the gene once. Locked genes
+ * behave slightly differently in some states.
+ * <br>
+ * Unlocking an already unlocked gene does nothing.
+ *
+ * @param value true to lock, false to unlock;
+ */
+ public void setLock(boolean value) {
+ if (value) {
+ lock++;
+ } else if (lock > 0) {
+ lock--;
+ } else {
+ lock = 0;
+ }
+ }
+}
diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java
new file mode 100644
index 0000000..3db7416
--- /dev/null
+++ b/src/jcgp/gui/population/GUIInput.java
@@ -0,0 +1,70 @@
+package jcgp.gui.population;
+
+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;
+
+ /**
+ * 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.setStroke(Paint.valueOf("black"));
+ 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;
+ }
+
+ @Override
+ public void setStateRecursively(GUIGeneState state) {
+ setState(state);
+ }
+
+ @Override
+ protected void setLinesVisible(boolean value) {}
+
+ @Override
+ public void setLockRecursively(boolean value) {
+ setLock(value);
+ }
+}
diff --git a/src/jcgp/gui/population/GUIMutable.java b/src/jcgp/gui/population/GUIMutable.java
new file mode 100644
index 0000000..fa996e2
--- /dev/null
+++ b/src/jcgp/gui/population/GUIMutable.java
@@ -0,0 +1,20 @@
+package jcgp.gui.population;
+
+import javafx.scene.shape.Line;
+
+/**
+ * A loose equivalent to {@link jcgp.backend.population.Mutable}.
+ * <br>
+ * This defines behaviour that all GUI representations of mutables
+ * should be capable of.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public interface GUIMutable {
+
+ public Line[] getLines();
+
+ public GUIConnection[] getConnections();
+
+}
diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java
new file mode 100644
index 0000000..1a32426
--- /dev/null
+++ b/src/jcgp/gui/population/GUINode.java
@@ -0,0 +1,134 @@
+package jcgp.gui.population;
+
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.Line;
+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, associate with node
+ this.node = node;
+ this.lines = lines;
+ node.setGUIObject(this);
+
+ // create the output socket
+ Circle output = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT);
+ output.setStroke(Paint.valueOf("black"));
+
+ // create input sockets
+ sockets = new Circle[GUI.resources.arity()];
+ for (int l = 0; l < sockets.length; l++) {
+ sockets[l] = new Circle(Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT);
+ 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().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;
+ }
+
+ /**
+ * Associates this instance with a new node.
+ *
+ * @param node the new node.
+ */
+ void setNode(Node node) {
+ this.node = node;
+ }
+
+ @Override
+ 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;
+ }
+
+ @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);
+ }
+ }
+
+ @Override
+ protected void setLinesVisible(boolean value) {
+ for (int i = 0; i < lines.length; i++) {
+ lines[i].setVisible(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);
+ }
+ }
+
+ @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();
+ }
+ return connections;
+ }
+}
diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java
new file mode 100644
index 0000000..f023d00
--- /dev/null
+++ b/src/jcgp/gui/population/GUIOutput.java
@@ -0,0 +1,79 @@
+package jcgp.gui.population;
+
+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;
+ output.setGUIObject(this);
+
+ // create input socket
+ Circle socket = new Circle(-Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT);
+ socket.setStroke(Paint.valueOf("black"));
+ socket.setId(String.valueOf(0));
+ Position.connect(line, (GUIGene) ((Gene) output.getSource()).getGUIObject());
+ getChildren().add(socket);
+
+ // relocate output, add handlers
+ Position.place(this);
+ OutputHandlers.addHandlers(this);
+ }
+
+ /**
+ * @return the {@code Output} instance associated with this object.
+ */
+ public Output getOutput() {
+ return output;
+ }
+
+ /**
+ * Associates this instance with a new output.
+ *
+ * @param output the new output.
+ */
+ void setOutput(Output output) {
+ this.output = output;
+ }
+
+ @Override
+ public Line[] getLines() {
+ return new Line[] {line};
+ }
+
+ @Override
+ protected void setLinesVisible(boolean value) {
+ line.setVisible(value);
+ }
+
+ @Override
+ public GUIConnection[] getConnections() {
+ return new GUIConnection[] {(GUIConnection) output.getGUIObject()};
+ }
+}
diff --git a/src/jcgp/gui/population/PopulationPane.java b/src/jcgp/gui/population/PopulationPane.java
new file mode 100644
index 0000000..51b5ba4
--- /dev/null
+++ b/src/jcgp/gui/population/PopulationPane.java
@@ -0,0 +1,75 @@
+package jcgp.gui.population;
+
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import jcgp.JCGP;
+import jcgp.backend.modules.problem.TestCaseProblem;
+import jcgp.backend.modules.problem.TestCaseProblem.TestCase;
+import jcgp.gui.GUI;
+
+public class PopulationPane extends TabPane {
+
+ private GUI gui;
+ private TestCase<Object> currentTestCase;
+ private boolean evaluating = false;
+
+ public PopulationPane(GUI gui) {
+ super();
+ this.gui = gui;
+ setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
+ remakeTabs();
+ }
+
+ public void remakeTabs() {
+ getTabs().clear();
+ JCGP jcgp = gui.getExperiment();
+
+ Tab tab;
+ ChromosomePane cp;
+ for (int i = 0; i < jcgp.getResources().populationSize(); i++) {
+ cp = new ChromosomePane(jcgp.getPopulation().get(i));
+ tab = new Tab("Chr " + i);
+ tab.setContent(cp);
+ getTabs().add(tab);
+ }
+ }
+
+ public void updateGenes() {
+ if (evaluating) {
+ evaluateTestCase(currentTestCase);
+ }
+ for (int i = 0; i < getTabs().size(); i++) {
+ ((ChromosomePane) getTabs().get(i).getContent()).updateGenes(gui.getExperiment().getPopulation().get(i));
+ }
+ }
+
+ public void evaluateTestCase(TestCase<Object> testCase) {
+ if (gui.getExperiment().getProblem() instanceof TestCaseProblem && testCase != null) {
+ currentTestCase = testCase;
+ if (testCase.getInputs().length == gui.getExperiment().getResources().inputs()) {
+ evaluating = true;
+ for (int i = 0; i < getTabs().size(); i++) {
+ //((ChromosomePane) getTabs().get(i).getContent()).setInputs(testCase.getInputs());
+ }
+ } else {
+ throw new IllegalArgumentException("Test case has " + testCase.getInputs().length
+ + " inputs and chromosome has " + gui.getExperiment().getResources().inputs());
+ }
+ }
+ }
+
+ public void hideValues() {
+ evaluating = false;
+ for (int i = 0; i < getTabs().size(); i++) {
+ //((ChromosomePane) getTabs().get(i).getContent()).updateValues();
+ }
+ }
+
+ public boolean isEvaluating() {
+ return evaluating;
+ }
+
+ public void setEvaluating(boolean value) {
+ evaluating = value;
+ }
+}