aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/gui/population/GUIGene.java
blob: 5e6107fa8aecdd9e71fb82347aab3ce7521a3200 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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;
		}
	}
}