Newer
Older
import edu.ntnu.stud.chaosgame.controller.game.ChaosCanvas;
import edu.ntnu.stud.chaosgame.controller.game.ChaosGame;
import edu.ntnu.stud.chaosgame.controller.game.ChaosGameDescription;
import edu.ntnu.stud.chaosgame.model.data.Vector2D;
import edu.ntnu.stud.chaosgame.model.generators.ChaosGameDescriptionFactory;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.concurrent.atomic.AtomicReference;
public class ChaosGameGui implements ChaosGameObserver {
private Canvas canvas;
private ChaosCanvas chaosCanvas;
/**
* The ChaosGameDescription.
*/
private ChaosGameDescription description;
/**
* The ChaosGameDescriptionFactory.
*/
private ChaosGameDescriptionFactory factory;
/**
* The AtomicReference for the ChaosGameDescription.
*/
private AtomicReference<ChaosGameDescription> descriptionRef;
/**
* The ImageView for the GUI..
*/
private ChaosGameImageView imageView;
/**
* The Scene for the GUI..
*/
private Scene scene;
/**
* The width and height of the GUI.
*/
private int width;
private int height;
/**
* The ChaosGame for this GUI.
*/
private ChaosGame game;
/**
* The Timeline for the GUI.
*/
private Timeline timeline;
/**
* The BorderPane for the GUI.
*/
private BorderPane borderPane;
/**
* The side menu for the GUI.
*/
private VBox sideMenu;
/**
* The start, stop, new, clear, quit and show sidebar buttons for the GUI.
*/
private Button startButton;
private Button stopButton;
private Button newButton;
private Button clearButton;
private Button quitButton;
private Button sideMenuButton;
/**
* The load fractal from file and write fractal to file buttons for the GUI.
*/
private Button loadFractalFromFileButton;
private Button writeFractalToFileButton;
/**
* The radio buttons for the fractal type for the GUI.
*/
private RadioButton sierpinskiRadioButton;
private RadioButton barnsleyRadioButton;
private RadioButton juliaRadioButton;
private RadioButton improvedBarnsleyButton;
public ChaosGameGui(Stage primaryStage) throws IOException {
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
this.initializeComponents();
primaryStage.setTitle("Fractal Chaos Game");
primaryStage.setScene(scene);
primaryStage.setOnShown(event -> this.imageView.requestFocus());
primaryStage.show();
}
/**
* Initialize the components of the GUI.
*/
private void initializeComponents() {
// Timeline
this.timeline = new Timeline(new KeyFrame(Duration.seconds(0.05), event -> this.drawChaosGame()));
this.initializeImageView();
// Side menu
//TEMPORARY CODE to test Chaos Games in GUI
this.initializeGameComponents();
this.initializeMainButtons();
this.initializeFractalButtons();
this.initializeSideMenu();
this.scene = new Scene(this.borderPane,1700,1000);
}
/**
* Initialize the main buttons for the GUI.
*/
private void initializeMainButtons() {
this.startButton = new Button("Start");
startButton.setOnAction(event -> timeline.play());
this.stopButton = new Button("Stop");
stopButton.setOnAction(event -> timeline.stop());
this.newButton = new Button("New");
newButton.setOnAction(event ->{
this.canvas.getGraphicsContext2D().clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
chaosCanvas.clearCanvas();
});
this.clearButton = new Button("Clear");
clearButton.setOnAction(event -> {
getImageView().setImage(null);
setCurrentLine(0);
});
// Quit button
this.quitButton = new Button("Quit");
quitButton.setOnAction(event -> Platform.exit());
}
/**
* Initialize the components related to the chaos game itself.
*/
private void initializeGameComponents() {
// Description
this.factory = new ChaosGameDescriptionFactory();
this.descriptionRef = new AtomicReference<>(factory.getDescriptions().get(0));
this.description = descriptionRef.get();
this.chaosCanvas = new ChaosCanvas(100, 100, descriptionRef.get().getMinCoords(), descriptionRef.get().getMaxCoords());
game = new ChaosGame(this.description, chaosCanvas);
}
/**
* Initialize components related to the image view and zoom function.
*/
private void initializeImageView() {
// Image view
this.imageView = new ChaosGameImageView(this);
this.canvas = new Canvas(width, height);
this.imageView.setImage(canvas.snapshot(null, null));
this.clearImageView();
* Color the entire image view white.
private void clearImageView() {
// Color the image white
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
this.canvas.getGraphicsContext2D().setFill(Color.WHITE);
this.canvas.getGraphicsContext2D().fillRect(i, j, 1, 1);
/**
* Initialize the buttons related to managing the fractals.
*/
private void initializeFractalButtons() {
// Radio buttons for choosing fractal type
ToggleGroup group = new ToggleGroup();
this.sierpinskiRadioButton = new RadioButton("Sierpinski");
sierpinskiRadioButton.setToggleGroup(group);
sierpinskiRadioButton.setSelected(true);
this.barnsleyRadioButton = new RadioButton("Barnsley");
barnsleyRadioButton.setToggleGroup(group);
this.juliaRadioButton = new RadioButton("Julia");
juliaRadioButton.setToggleGroup(group);
this.improvedBarnsleyButton = new RadioButton("Improved Barnsley");
improvedBarnsleyButton.setToggleGroup(group);
AtomicReference<ChaosCanvas> canvasRef = new AtomicReference<>(chaosCanvas);
// Set action for Sierpinski radio button.
sierpinskiRadioButton.setOnAction(event -> {
this.updateDescription(0);
});
// Set action for Barnsley radio button.
barnsleyRadioButton.setOnAction(event -> {
this.updateDescription(1);
});
// Set action for Julia radio button.
juliaRadioButton.setOnAction(event -> {
this.updateDescription(2);
});
improvedBarnsleyButton.setOnAction(event -> {
this.updateDescription(3);
/*for (int i = 0; i < this.description.getTransforms().size(); i++) {
System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA00());
System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA01());
System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA10());
System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA11());
System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getVector().getX0());
System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getVector().getX1());
}*/
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
});
// Load fractal file button
this.loadFractalFromFileButton = new Button("Load Fractal");
// Write fractal to file button
this.writeFractalToFileButton = new Button("Write to File");
}
/**
* Initialize the side menu.
*/
private void initializeSideMenu() {
this.sideMenu = new VBox();
// Parameters
VBox parameterBox = new VBox();
// Step Count GUI
VBox stepCountBox = new VBox();
Label stepCountLabel = new Label("Step Count");
TextArea stepCountTextArea = new TextArea();
stepCountTextArea.setPrefHeight(5);
stepCountTextArea.setPrefWidth(50);
Button changeStepCountButton = new Button("Change Step Count");
stepCountBox.getChildren().addAll(stepCountLabel,stepCountTextArea, changeStepCountButton);
// Minimum Coordinates GUI
VBox minCoordinatesBox = new VBox();
Label minCoordinatesLabel = new Label("Min. Coordinates");
TextArea minimumCoordinatesTextArea = new TextArea();
minimumCoordinatesTextArea.setPrefHeight(5);
minimumCoordinatesTextArea.setPrefWidth(50);
Button changeMinimumCoordinatesButton = new Button("Change Min. Coordinates");
minCoordinatesBox.getChildren().addAll(minCoordinatesLabel,minimumCoordinatesTextArea,changeMinimumCoordinatesButton);
// Maximum Coordinates GUI
VBox maxCoordinatesBox = new VBox();
Label maxCoordinatesLabel = new Label("Max Coordinates");
TextArea maximumCoordinatesTextArea = new TextArea();
maximumCoordinatesTextArea.setPrefHeight(5);
maximumCoordinatesTextArea.setPrefWidth(50);
Button changeMaximumCoordinatesButton = new Button("Change Max Coordinates");
maxCoordinatesBox.getChildren().addAll(maxCoordinatesLabel,maximumCoordinatesTextArea,changeMaximumCoordinatesButton);
// Fill parameter box
parameterBox.getChildren().addAll(stepCountBox, minCoordinatesBox, maxCoordinatesBox);
parameterBox.setPadding(new Insets(10));
// Add basic control buttons
sideMenu.getChildren().addAll(startButton,stopButton,newButton,clearButton);
// Add fractal radio buttons
sideMenu.getChildren().addAll(sierpinskiRadioButton, barnsleyRadioButton, juliaRadioButton, improvedBarnsleyButton);
// Add parameter VBox
sideMenu.getChildren().add(parameterBox);
// Add file buttons
sideMenu.getChildren().addAll(loadFractalFromFileButton,writeFractalToFileButton);
// Add quit button
sideMenu.getChildren().add(quitButton);
// Add padding
sideMenu.setPadding(new Insets(10));
// Create split pane and button to toggle sidebar
this.sideMenuButton = new Button(">>");
this.initializeSideButtonHandler();
Region sideMenuButtonRegion = new Region();
sideMenuButtonRegion.setMinWidth(200);
HBox sideMenuButtonBox = new HBox();
sideMenuButtonBox.getChildren().addAll(sideMenuButtonRegion, sideMenuButton);
// The right VBox containing both the sidebar and the sidebar toggle button.
VBox rightVBox = new VBox();
rightVBox.getChildren().addAll(sideMenuButtonBox, sideMenu);
this.borderPane = new BorderPane();
this.borderPane.setCenter(imageView);
this.borderPane.setRight(rightVBox);
rightVBox.setFocusTraversable(false);
borderPane.setFocusTraversable(false);
}
private void initializeSideButtonHandler() {
TranslateTransition openNav = new TranslateTransition(new Duration(350), sideMenu);
openNav.setToX(0);
TranslateTransition closeNav = new TranslateTransition(new Duration(350), sideMenu);
this.sideMenuButton.setOnAction(e -> {
if(sideMenu.getTranslateX() != 0){
this.sideMenuButton.setText(">>");
openNav.play();
} else {
closeNav.setToX(sideMenu.getWidth());
closeNav.play();
this.sideMenuButton.setText("<<");
}
});
}
/**
* Get the chaos canvas of this GUI view.
*
* @return the canvas.
*/
public ChaosCanvas getChaosCanvas() {
return this.chaosCanvas;
}
game.runSteps(100000000);
ChaosCanvasToImageConverter converter = new ChaosCanvasToImageConverter(this.chaosCanvas);
WritableImage image = converter.getImage();
this.canvas.getGraphicsContext2D().drawImage(image, 0, 0);
this.imageView.setImage(image);
}
public int getWidth(){
return this.width;
}
public int getHeight(){
return this.height;
}
public ImageView getImageView(){
return this.imageView;
}
public void setCurrentLine(int currentLine) {
this.currentLine = currentLine;
}
public void setImageViewFromImage(Image inputView) {
this.imageView.setImage(inputView);
}
/**
* Update the description of the chaos game.
* TODO: this method may need to be changed depending on how we implement the UI. Rename to update?
*
* @param index the index of the new description in the list of factory descriptions.
public void updateDescription(int index) {
AtomicReference<ChaosCanvas> canvasRef = new AtomicReference<>(chaosCanvas);
timeline.stop();
canvasRef.get().clearCanvas();
this.canvas.getGraphicsContext2D().clearRect(0, 0, this.canvas.getGraphicsContext2D().
getCanvas().getWidth(), this.canvas.getGraphicsContext2D().getCanvas().getHeight());
this.clearImageView();
chaosCanvas.clearCanvas();
this.descriptionRef.set(factory.getDescriptions().get(index)); // Assuming the Sierpinski description is at index 0
canvasRef.set(new ChaosCanvas(1000, 1000, this.descriptionRef.get().getMinCoords(),
this.descriptionRef.get().getMaxCoords()));
this.chaosCanvas = canvasRef.get();
game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
//this.game.setDescription(description);
}
/**
* Update which parts of the fractal are rendered and at what level of detail.
*
* @param zoomLevel the number of recursive zoom levels.
* @param centreX the x-coordinate of the centre of the image view.
* @param centreY the y-coordinate of the centre of the image view.
*/
public void updateDetail(int zoomLevel, double centreX, double centreY) {
this.clearImageView();
this.chaosCanvas.clearCanvas();
this.chaosCanvas.updateCoords(centreX, centreY, zoomLevel);
this.game.setCurrentPoint(new Vector2D(centreX, centreY));
}
/**
* Update the observer based on changes to the chaos game.
* TODO: this method may need to be changed depending on how we implement the UI. The update method may need to be split.
*
* @param game the game this observer is monitoring.
*/
@Override
public void update(ChaosGame game) {
//drawChaosGame();
}