aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jcgp')
-rw-r--r--src/jcgp/backend/modules/mutator/PointMutator.java42
-rw-r--r--src/jcgp/backend/modules/mutator/ProbabilisticMutator.java4
-rw-r--r--src/jcgp/backend/parsers/ChromosomeParser.java2
-rw-r--r--src/jcgp/backend/population/Chromosome.java26
-rw-r--r--src/jcgp/backend/population/Mutable.java22
-rw-r--r--src/jcgp/backend/population/Node.java41
-rw-r--r--src/jcgp/backend/population/Output.java51
-rw-r--r--src/jcgp/backend/tests/ChromosomeTests.java6
-rw-r--r--src/jcgp/backend/tests/OutputTests.java4
-rw-r--r--src/jcgp/gui/GUI.java247
-rw-r--r--src/jcgp/gui/console/ConsolePane.java (renamed from src/jcgp/gui/console/GUIConsole.java)4
-rw-r--r--src/jcgp/gui/population/GUIGene.java5
-rw-r--r--src/jcgp/gui/population/GUIOutput.java2
-rw-r--r--src/jcgp/gui/settings/parameters/GUIParameter.java2
14 files changed, 334 insertions, 124 deletions
diff --git a/src/jcgp/backend/modules/mutator/PointMutator.java b/src/jcgp/backend/modules/mutator/PointMutator.java
index 5aba0d1..6ba3e10 100644
--- a/src/jcgp/backend/modules/mutator/PointMutator.java
+++ b/src/jcgp/backend/modules/mutator/PointMutator.java
@@ -4,8 +4,6 @@ import jcgp.backend.parameters.BooleanParameter;
import jcgp.backend.parameters.IntegerParameter;
import jcgp.backend.population.Chromosome;
import jcgp.backend.population.Mutable;
-import jcgp.backend.population.Node;
-import jcgp.backend.population.Output;
import jcgp.backend.resources.Resources;
/**
@@ -40,43 +38,13 @@ public abstract class PointMutator extends Mutator {
// for however many genes must be mutated
for (int i = 0; i < genesMutated.get(); i++) {
+ // choose a random mutable
+ Mutable mutable = chromosome.getRandomMutable();
- Mutable m = chromosome.getRandomMutable();
+ if (report.get()) getResources().report("[Mutator] Mutation " + i + " selected " + mutable);
- if (report.get()) getResources().report("[Mutator] Mutation " + i + " selected " + m + ", ");
-
- // outputs and nodes are mutated differently
- if (m instanceof Output) {
- if (report.get()) getResources().report("changed source from " + ((Output) m).getSource() + " ");
-
- // outputs are easy, simply set to a different random connection, any will do
- m.setConnection(0, chromosome.getRandomConnection());
-
- if (report.get()) getResources().reportln("to " + ((Output) m).getSource());
- } else if (m instanceof Node) {
- /* nodes are more complicated, first we must decide whether to mutate the function
- * or a connection
- * we do this by generating a random int between 0 and 1 + arity
- */
- int geneType = getResources().getRandomInt(1 + getResources().arity());
-
- // if the int is less than 1, mutate function, else mutate connections
- if (geneType < 1) {
- if (report.get()) getResources().report("changed function from " + ((Node) m).getFunction() + " ");
-
- ((Node) m).setFunction(getResources().getRandomFunction());
-
- if (report.get()) getResources().reportln("to " + ((Node) m).getFunction());
- } else {
- // if we decided to mutate connection, subtract 1 from geneType so it fits into the arity range
- geneType--;
- if (report.get()) getResources().report("changed connection " + geneType + " from " + ((Node) m).getConnection(geneType) + " ");
-
- m.setConnection(geneType, chromosome.getRandomConnection(((Node) m).getColumn()));
-
- if (report.get()) getResources().reportln("to " + ((Node) m).getConnection(geneType));
- }
- }
+ // mutate a random gene
+ mutable.mutate();
}
}
diff --git a/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java b/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java
index 9273558..1ea1373 100644
--- a/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java
+++ b/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java
@@ -93,13 +93,13 @@ public class ProbabilisticMutator extends Mutator {
if (report.get()) getResources().report("[Mutator] Mutating " + out +
", changed source from " + out.getSource());
- out.setConnection(0, chromosome.getRandomConnection());
+ out.setSource(chromosome.getRandomConnection());
if (report.get()) getResources().reportln("to " + out.getSource());
}
}
- if (report.get()) getResources().reportln("[Mutator] Mutation finished ");
+ if (report.get()) getResources().reportln("[Mutator] Mutation finished");
}
diff --git a/src/jcgp/backend/parsers/ChromosomeParser.java b/src/jcgp/backend/parsers/ChromosomeParser.java
index 6106dbd..b892182 100644
--- a/src/jcgp/backend/parsers/ChromosomeParser.java
+++ b/src/jcgp/backend/parsers/ChromosomeParser.java
@@ -117,7 +117,7 @@ public abstract class ChromosomeParser {
newConnection = chromosome.getNode((gene - resources.inputs()) % resources.rows(),
(gene - resources.inputs()) / resources.rows());
}
- chromosome.getOutput(o).setConnection(0, newConnection);
+ chromosome.getOutput(o).setSource(newConnection);
}
in.close();
diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java
index 673bb26..e28032c 100644
--- a/src/jcgp/backend/population/Chromosome.java
+++ b/src/jcgp/backend/population/Chromosome.java
@@ -149,7 +149,7 @@ public class Chromosome implements Comparable<Chromosome> {
// set random outputs
for (Output output : outputs) {
- output.setConnection(0, getRandomConnection());
+ output.setSource(getRandomConnection());
}
}
@@ -198,9 +198,9 @@ public class Chromosome implements Comparable<Chromosome> {
for (int o = 0; o < outputs.length; o++) {
copyOutput = clone.getOutput(o).getSource();
if (copyOutput instanceof Input) {
- outputs[o].setConnection(0, inputs[((Input) copyOutput).getIndex()]);
+ outputs[o].setSource(inputs[((Input) copyOutput).getIndex()]);
} else if (copyOutput instanceof Node) {
- outputs[o].setConnection(0, nodes[((Node) copyOutput).getRow()][((Node) copyOutput).getColumn()]);
+ outputs[o].setSource(nodes[((Node) copyOutput).getRow()][((Node) copyOutput).getColumn()]);
} else {
// something bad happened
System.out.println("Warning: Connection of subtype " + copyOutput.getClass().toString() + " is not explicitly handled by copy constructor.");
@@ -212,6 +212,16 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
+ * Returns a reference to the indexed input.
+ *
+ * @param index the input index.
+ * @return the input reference.
+ */
+ public Input getInput(int index) {
+ return inputs[index];
+ }
+
+ /**
* Returns a reference to any node, addressed by row and column.
*
* @param row the row of the node.
@@ -233,16 +243,6 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
- * Returns a reference to the indexed input.
- *
- * @param index the input index.
- * @return the input reference.
- */
- public Input getInput(int index) {
- return inputs[index];
- }
-
- /**
* @return the fitness of the chromosome.
*/
public double getFitness() {
diff --git a/src/jcgp/backend/population/Mutable.java b/src/jcgp/backend/population/Mutable.java
index 3ce7065..a5cbe37 100644
--- a/src/jcgp/backend/population/Mutable.java
+++ b/src/jcgp/backend/population/Mutable.java
@@ -8,8 +8,8 @@ package jcgp.backend.population;
* <br><br>
* This interface provides a way to deal with mutable elements
* generically without having to specify whether they are nodes
- * or outputs. In this way a random mutable element can be picked and
- * dealt with more easily, facilitating mutations.
+ * or outputs. When mutating a mutable, {@code mutate()} is guaranteed
+ * to perform a fair mutation.
*
* @author Eduardo Pedroni
*
@@ -17,14 +17,16 @@ package jcgp.backend.population;
public interface Mutable {
/**
- * This method sets the indexed connection to the specified new connection.
- * Implementing classes may choose to ignore the given index (such as in the
- * case of outputs, which only have one connection).
- *
- * @param index the connection index to set.
- * @param newConnection the chromosome element to connect to.
+ * This method performs an arbitrary mutation on the {@code Mutable}.
+ * <br><br>
+ * In the case of nodes, this chooses to mutate a function or connection
+ * fairly, and carries out the required mutation by using the node's own
+ * reference to chromosome.
+ * <br><br>
+ * In the case of outputs, this simply picks a random connection to serve
+ * as the source - any connection is allowed.
*/
- public void setConnection(int index, Connection newConnection);
+ public void mutate();
/**
* Asserts if the specified element is a copy of the elements
@@ -48,6 +50,6 @@ public interface Mutable {
* @param element the mutable element to compare to.
* @return true if {@code element} is a copy of this element.
*/
- boolean copyOf(Mutable element);
+ public boolean copyOf(Mutable element);
}
diff --git a/src/jcgp/backend/population/Node.java b/src/jcgp/backend/population/Node.java
index 3bcf3da..7712c50 100644
--- a/src/jcgp/backend/population/Node.java
+++ b/src/jcgp/backend/population/Node.java
@@ -3,6 +3,7 @@ package jcgp.backend.population;
import java.util.ArrayList;
import jcgp.backend.function.Function;
+import jcgp.backend.resources.Resources;
/**
* Nodes make up the main part of the chromosome,
@@ -16,6 +17,10 @@ import jcgp.backend.function.Function;
* (determined by the maximum arity of the function set)
* and must be reinstantiated if the experiment arity
* changes.
+ * <br><br>
+ * When mutating a node, it is easiest to use {@code mutate()}.
+ * Alternatively, you may also perform a specific mutation using
+ * {@code setConnection(...)} and {@code setFunction(...)}.
*
* @author Eduardo Pedroni
*
@@ -122,6 +127,24 @@ public class Node implements Mutable, Connection {
}
}
}
+
+ /**
+ * This method sets the indexed connection to the specified new connection.
+ * If the given connection is null or disrespects levels back, it is discarded
+ * and no connections are changed.
+ *
+ * @param index the connection index to set.
+ * @param newConnection the {@code Connection} to connect to.
+ */
+ public void setConnection(int index, Connection newConnection) {
+ // connection must not be null
+ if (newConnection != null) {
+ //if () {
+ connections[index] = newConnection;
+ chromosome.recomputeActiveNodes();
+ //}
+ }
+ }
@Override
public boolean copyOf(Mutable element) {
@@ -174,11 +197,19 @@ public class Node implements Mutable, Connection {
}
@Override
- public void setConnection(int index, Connection newConnection) {
- // connection must not be null
- if (newConnection != null) {
- connections[index] = newConnection;
- chromosome.recomputeActiveNodes();
+ public void mutate() {
+ Resources resources = chromosome.getResources();
+
+ // choose to mutate the function or a connection
+ int geneType = resources.getRandomInt(1 + resources.arity());
+
+ // if the int is less than 1, mutate function, else mutate connections
+ if (geneType < 1) {
+ setFunction(resources.getRandomFunction());
+ } else {
+ // if we decided to mutate connection, subtract 1 from geneType so it fits into the arity range
+ geneType--;
+ setConnection(geneType, chromosome.getRandomConnection(column));
}
}
diff --git a/src/jcgp/backend/population/Output.java b/src/jcgp/backend/population/Output.java
index 938741b..a346d4a 100644
--- a/src/jcgp/backend/population/Output.java
+++ b/src/jcgp/backend/population/Output.java
@@ -8,6 +8,10 @@ import java.util.ArrayList;
* returns the value of its single connection, but it
* may not be connected to - it terminates a chromosome
* active connection path.
+ * <br><br>
+ * When mutating an output, it is easiest to use {@code mutate()}.
+ * Alternatively, you may also perform a specific mutation using
+ * {@code setSource(...)}.
*
* @author Eduardo Pedroni
*
@@ -37,34 +41,47 @@ public class Output implements Mutable {
return source.getValue();
}
+ /**
+ * @return this output's index.
+ */
public int getIndex() {
return index;
}
+
+ /**
+ * This method sets the output source to the specified connection.
+ *
+ * @param newConnection the {@code Connection} to connect to.
+ */
+ public void setSource(Connection newConnection) {
+ source = newConnection;
+ // trigger active path recomputation
+ chromosome.recomputeActiveNodes();
+ }
+ /**
+ * @return the source of this output's value.
+ */
public Connection getSource() {
return source;
}
+ /**
+ * Calls {@code getActive(...)} on this output's
+ * source. This kicks off a recursive process whereby
+ * all nodes connected to this output are added to the
+ * specified list of nodes. This is used to create a
+ * list of all active nodes.
+ *
+ * @param activeNodes the list to add all active nodes to.
+ */
public void getActiveNodes(ArrayList<Node> activeNodes) {
+ // do not add if the source is an input
if (source instanceof Node) {
((Node) source).getActive(activeNodes);
}
}
- /**
- * When mutating an output, the index parameter
- * is simply ignored and the output source is
- * set.
- *
- * @see jcgp.backend.population.Mutable#setConnection(int, jcgp.backend.population.Connection)
- */
- @Override
- public void setConnection(int index, Connection newConnection) {
- source = newConnection;
- // trigger active path recomputation
- chromosome.recomputeActiveNodes();
- }
-
@Override
public boolean copyOf(Mutable m) {
// both cannot be the same instance
@@ -94,6 +111,12 @@ public class Output implements Mutable {
}
@Override
+ public void mutate() {
+ // simply change output to a new, random connection
+ setSource(chromosome.getRandomConnection());
+ }
+
+ @Override
public String toString() {
return "Output " + index;
}
diff --git a/src/jcgp/backend/tests/ChromosomeTests.java b/src/jcgp/backend/tests/ChromosomeTests.java
index b5c1da7..7cbf2e2 100644
--- a/src/jcgp/backend/tests/ChromosomeTests.java
+++ b/src/jcgp/backend/tests/ChromosomeTests.java
@@ -136,7 +136,7 @@ public class ChromosomeTests {
}
// mutate an output in clone, check that the same node in chromosome produces a different output
- clone.getOutput(1).setConnection(resources.getRandomInt(resources.arity()), clone.getInput(2));
+ clone.getOutput(1).setSource(clone.getInput(2));
assertTrue("Mutation affected nodes in both chromosomes.",
clone.getOutput(1).calculate() != chromosome.getOutput(1).calculate());
@@ -332,8 +332,8 @@ public class ChromosomeTests {
c.getNode(1, 1).initialise(resources.getFunction(0), c.getNode(0, 0), c.getInput(1));
c.getNode(1, 2).initialise(resources.getFunction(0), c.getNode(1, 1), c.getInput(2));
- c.getOutput(0).setConnection(0, c.getNode(0, 0));
- c.getOutput(1).setConnection(0, c.getNode(1, 2));
+ c.getOutput(0).setSource(c.getNode(0, 0));
+ c.getOutput(1).setSource(c.getNode(1, 2));
return c;
}
diff --git a/src/jcgp/backend/tests/OutputTests.java b/src/jcgp/backend/tests/OutputTests.java
index 8cc10a8..c5aa6b4 100644
--- a/src/jcgp/backend/tests/OutputTests.java
+++ b/src/jcgp/backend/tests/OutputTests.java
@@ -47,7 +47,7 @@ public class OutputTests {
@Test
public void evaluationsTest() {
// set source connection, check that the appropriate value is returned
- output.setConnection(0, new Connection() {
+ output.setSource(new Connection() {
@Override
public Object getValue() {
@@ -70,7 +70,7 @@ public class OutputTests {
return 0;
}
};
- output.setConnection(0, newConn);
+ output.setSource(newConn);
assertTrue("Incorrect connection returned.", output.getSource() == newConn);
}
diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java
index 437a739..2f09dea 100644
--- a/src/jcgp/gui/GUI.java
+++ b/src/jcgp/gui/GUI.java
@@ -13,7 +13,7 @@ import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import jcgp.JCGP;
import jcgp.backend.modules.problem.TestCaseProblem.TestCase;
-import jcgp.gui.console.GUIConsole;
+import jcgp.gui.console.ConsolePane;
import jcgp.gui.dragresize.HorizontalDragResize;
import jcgp.gui.dragresize.VerticalDragResize;
import jcgp.gui.population.FunctionSelector;
@@ -22,8 +22,25 @@ import jcgp.gui.population.PopulationPane;
import jcgp.gui.settings.SettingsPane;
/**
- * Main class for the graphical user interface (GUI)
- *
+ * Main class for the graphical user interface (GUI).
+ * <br><br>
+ * This class declares the main method used when running the GUI.
+ * In addition, all main GUI panes are declared and instantiated here.
+ * <br><br>
+ * The user interface is divided into 3 main components: the node grid
+ * ({@link PopulationPane}), the control pane ({@link SettingsPane}) and
+ * the console ({@link ConsolePane}). Click on any of the links in
+ * brackets to see more information about each interface component.
+ * <br><br>
+ * This class also contains the instance of JCGP responsible for
+ * running the experiments in GUI mode. JCGP's execution must be delegated
+ * to a separate thread so that the GUI remains unblocked. This is done using
+ * a JavaFX {@code Service} which calls {@code nextGeneration()} in a loop
+ * until it is interrupted by the main JavaFX thread.
+ * <br>
+ * This service also handles flushing the console in a thread safe way. This
+ * is done by synchronizing the {@code nextGeneration()} and {@code flush()}
+ * method calls on a lock object.
*
* @author Eduardo Pedroni
*
@@ -31,72 +48,213 @@ import jcgp.gui.settings.SettingsPane;
public class GUI extends Application {
/* Colours */
+ /**
+ * A string containing the hexadecimal colour used for representing neutrality.
+ */
public static final String NEUTRAL_COLOUR = "#FFFFFF";
+ /**
+ * A string containing the hexadecimal 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";
+ /**
+ * A string containing the hexadecimal 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";
+ /**
+ * A string containing the hexadecimal colour used for a soft highlight.
+ * When hovering over a node, its connections are soft-selected.
+ */
public static final String SOFT_HIGHLIGHT_COLOUR = "#C7DFFF";
+ /**
+ * A string containing the hexadecimal 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";
+ /**
+ * A string containing the hexadecimal 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";
+ /**
+ * A string containing the hexadecimal 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";
/* Sizes and distances */
+ /**
+ * The width or height of the area that can be clicked on
+ * to drag-resize a pane.
+ */
public static final double RESIZE_MARGIN = 5.0;
+ /**
+ * The minimum width of the settings pane, to prevent it
+ * from being resized beyond visibility.
+ */
public static final double SETTINGS_MIN_WIDTH = 200;
+ /**
+ * The minimum width of the console pane, to prevent it
+ * from being resized beyond visibility.
+ */
public static final double CONSOLE_MIN_HEIGHT = 100;
- private final JCGP jcgp;
-
+ /*
+ * Actual GUI elements
+ */
private Stage stage;
-
- private final FunctionSelector functionSelector;
-
private PopulationPane populationPane;
-
- private GUIConsole console;
-
+ private ConsolePane console;
private SettingsPane settingsPane;
+ private final FunctionSelector functionSelector;
+ /*
+ * Flow control objects
+ */
private boolean running = false;
-
private final Object printLock = new Object();
+ private Service<Void> jcgpService;
+ private Runnable consoleFlush;
- private Service<Void> cgpService;
+ /*
+ * The experiment itself
+ */
+ private final JCGP jcgp;
- private Runnable consoleFlush;
+ /**
+ * Start JCGP with the user interface.
+ *
+ * @param args no arguments are used.
+ */
public static void main(String[] args) {
+ // not much to do, simply launch the JavaFX application
launch();
}
+ /**
+ * Makes a new instance of GUI. This initialises the JCGP experiment and
+ * instantiates the function selector. It also creates the console flush task
+ * and the service responsible for running the JCGP experiment.
+ */
public GUI() {
jcgp = new JCGP();
functionSelector = new FunctionSelector(jcgp.getResources().getFunctionSet());
+ /*
+ * This task flushes the console in a thread-safe way.
+ * The problem is that this task is executed using Platform.runLater()
+ * to ensure that the flush itself happens on the JavaFX thread. However,
+ * runLater() is not guaranteed to run anytime soon. If the time taken for
+ * jcgp to perform a single generation is shorter than the time taken for
+ * this task to be executed by the platform, consoleFlush tasks will be
+ * scheduled faster than they can be executed and the console will eventually
+ * freeze.
+ *
+ * This is addressed by synchronizing the flushes with each nextGeneration() call.
+ */
consoleFlush = new Runnable() {
@Override
public void run() {
+ /*
+ * Try to acquire printlock - wait here until jcgpService relinquishes it
+ * by calling wait(). This means that it is finished with the current generation
+ * and will wait for the console to be flushed to move on.
+ * It might be the case that the service has already released the lock by waiting
+ * on it; it makes no difference. In that case this will acquire the lock
+ * immediately and proceed to flush the console.
+ */
synchronized(printLock) {
+ /*
+ * The lock is acquired, at this point we are certain that jcgpService
+ * cannot execute; it is currently waiting to be notified about the lock.
+ * No additional consoleFlush tasks can be scheduled with runLater() because
+ * the service is waiting. We can now take our time to flush the console.
+ */
console.flush();
+ /*
+ * Once the console finishes flushing, we notify jcgpService to perform the
+ * next generation.
+ */
printLock.notifyAll();
}
}
};
- cgpService = new Service<Void> () {
+ /*
+ * This service runs on a separate thread and performs
+ * the experiment, including console prints, in a thread-safe
+ * way. It is synchronized with consoleFlush.
+ */
+ jcgpService = new Service<Void> () {
@Override
protected Task<Void> createTask() {
Task<Void> t = new Task<Void>() {
@Override
protected Void call() throws Exception {
+ /*
+ * Only execute if the experiment isn't finished
+ * and the service hasn't been cancelled.
+ */
while (!isCancelled() && !jcgp.isFinished()) {
+ /*
+ * Attempt to acquire the printlock.
+ * Successfully doing so means no printing
+ * is currently taking place and we are free
+ * to schedule a print task.
+ * This lock acquisition should never block. It should
+ * not be possible to execute this statement without
+ * having been notified by consoleFlush.
+ */
synchronized (printLock) {
+ /*
+ * Lock has been acquired, schedule a print
+ * task ahead of time. The actual print messages
+ * haven't been send to the console yet, that happens
+ * during nextGeneration(), but since we have the lock
+ * consoleFlush() will sit and wait for us to release it
+ * whenever we are finished queueing prints.
+ */
Platform.runLater(consoleFlush);
+ /*
+ * Perform the actual generation. Here zero or more
+ * strings might be sent to the console buffer.
+ */
jcgp.nextGeneration();
+ /*
+ * The generation is complete, relinquish the lock.
+ * By this point chances are the platform is already trying
+ * to execute the consoleFlush task that we scheduled. If it
+ * hasn't already started though, it doesn't matter; we will
+ * wait for a notification on the lock, which will only come
+ * when printing is complete.
+ */
printLock.wait();
+ /*
+ * We have been notified. This means all buffered messages have
+ * been successfully flushed to the actual console control and
+ * we are now ready to perform another generation (or break out
+ * of the loop if the loop conditions are no longer met).
+ */
}
+ /*
+ * We no longer own the lock, but neither does consoleFlush.
+ * The synchrony cycle has returned to its initial state, and we
+ * are free to acquire the lock again.
+ */
}
+ /*
+ * Something happened to break the while loop -
+ * either the experiment finished or the user pressed
+ * pause.
+ */
if (jcgp.isFinished()) {
+ // the experiment has finished, switch to pause mode
Platform.runLater(new Runnable() {
@Override
public void run() {
@@ -114,60 +272,95 @@ public class GUI extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
- console = new GUIConsole();
+ /*
+ * This method gets called when the application launches. Once it
+ * returns, the application falls into the main loop which handles
+ * events, so all elements must be constructed here.
+ */
+
+ // make the console and set it so it is used for JCGP prints
+ console = new ConsolePane();
jcgp.setConsole(console);
+
+ // store reference to the stage
stage = primaryStage;
+
/*
- * Instantiate the various GUI elements here.
- *
- *
+ * The experiment layer contains all of the experiment-related panes.
+ * The only element that sits higher than this is the function selector.
*/
- BorderPane leftFrame = new BorderPane();
BorderPane experimentLayer = new BorderPane();
+ /*
+ * The left frame encapsulates the population pane and the console.
+ * It goes into the center position of the experiment layer, next to the settings pane.
+ */
+ BorderPane leftFrame = new BorderPane();
+ /*
+ * The population pane is a TabPane containing a tab for each chromosome.
+ */
populationPane = new PopulationPane(this);
+ /*
+ * The settings pane is a big class containing the entire control pane
+ */
settingsPane = new SettingsPane(this);
- settingsPane.maxWidthProperty().bind(experimentLayer.widthProperty());
-
- console.maxHeightProperty().bind(experimentLayer.heightProperty());
+ // make control pane and console resizable
HorizontalDragResize.makeDragResizable(settingsPane);
VerticalDragResize.makeDragResizable(console);
-
+ // prevent resizables from growing larger than the experiment layer
+ settingsPane.maxWidthProperty().bind(experimentLayer.widthProperty());
+ console.maxHeightProperty().bind(experimentLayer.heightProperty());
+
+ // put console and population pane in the main frame
leftFrame.setCenter(populationPane);
leftFrame.setBottom(console);
+ // set the main frame and the control pane in the experiment layer
experimentLayer.setCenter(leftFrame);
experimentLayer.setRight(settingsPane);
+ /*
+ * Now we deal with the stage.
+ */
primaryStage.setTitle("JCGP");
+ // this pane holds the entire scene, that is its sole job.
Pane sceneParent = new Pane();
+ // the experiment layer should fill the entire scene parent
experimentLayer.prefHeightProperty().bind(sceneParent.heightProperty());
experimentLayer.prefWidthProperty().bind(sceneParent.widthProperty());
+ // the function selector goes over the experiment layer so it doesn't get covered by other panes
sceneParent.getChildren().addAll(experimentLayer, functionSelector);
+ // set the scene, minimum sizes, show
primaryStage.setScene(new Scene(sceneParent));
primaryStage.setMinWidth(800);
primaryStage.setMinHeight(600);
primaryStage.show();
+ // when the main stage closes, close the test case table as well
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
- settingsPane.getTestCaseTable().close();
+ if (settingsPane.getTestCaseTable() != null) {
+ settingsPane.getTestCaseTable().close();
+ }
}
});
}
+ /**
+ *
+ */
public void runPause() {
if (!jcgp.isFinished() && settingsPane.areParametersValid()) {
if (!running) {
runningMode(true);
- cgpService.restart();
+ jcgpService.restart();
} else {
- cgpService.cancel();
+ jcgpService.cancel();
runningMode(false);
}
}
diff --git a/src/jcgp/gui/console/GUIConsole.java b/src/jcgp/gui/console/ConsolePane.java
index b3d037c..c4cfc21 100644
--- a/src/jcgp/gui/console/GUIConsole.java
+++ b/src/jcgp/gui/console/ConsolePane.java
@@ -26,7 +26,7 @@ import jcgp.gui.GUI;
* @author Eduardo Pedroni
*
*/
-public class GUIConsole extends AnchorPane implements Console {
+public class ConsolePane extends AnchorPane implements Console {
private TextArea textArea = new TextArea("Welcome to JCGP!\n");
private StringBuffer printBuffer = new StringBuffer();
@@ -34,7 +34,7 @@ public class GUIConsole extends AnchorPane implements Console {
/**
* Creates a new instance of this class.
*/
- public GUIConsole() {
+ public ConsolePane() {
super();
textArea.setEditable(false);
diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java
index 0eea045..3d9cffb 100644
--- a/src/jcgp/gui/population/GUIGene.java
+++ b/src/jcgp/gui/population/GUIGene.java
@@ -71,11 +71,6 @@ public abstract class GUIGene extends Group {
public abstract void addLocks(int value);
- /**
- *
- *
- * @param value
- */
public abstract void removeLocks(int value);
public abstract void updateLines();
diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java
index 5a76298..364458b 100644
--- a/src/jcgp/gui/population/GUIOutput.java
+++ b/src/jcgp/gui/population/GUIOutput.java
@@ -266,7 +266,7 @@ public class GUIOutput extends GUIGene {
@Override
public void setChangingConnection(Connection newConnection) {
- output.setConnection(0, newConnection);
+ output.setSource(newConnection);
updateText();
}
diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java
index f896fa3..9188aec 100644
--- a/src/jcgp/gui/settings/parameters/GUIParameter.java
+++ b/src/jcgp/gui/settings/parameters/GUIParameter.java
@@ -231,8 +231,6 @@ public abstract class GUIParameter<T> extends HBox {
* (INVALID_PARAMETER_STYLE, WARNING_PARAMETER_STYLE, VALID_PARAMETER_STYLE)
* provide a way to keep the GUI consistent.
*
- * TODO update this if the strings are externalised
- *
* @see ParameterStatus
*/
protected abstract void setValidityStyle();