diff options
Diffstat (limited to 'src/jcgp/gui/population/GUIGene.java')
-rw-r--r-- | src/jcgp/gui/population/GUIGene.java | 187 |
1 files changed, 187 insertions, 0 deletions
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; + } + } +} |