Browse Source

let the user choose between Mandelbrot and Julia

develop
Manuel Friedli 4 years ago
parent
commit
467d0c95ba
2 changed files with 91 additions and 28 deletions
  1. +64
    -19
      src/main/java/ch/fritteli/fractal/gui/MainController.java
  2. +27
    -9
      src/main/resources/main.fxml

+ 64
- 19
src/main/java/ch/fritteli/fractal/gui/MainController.java View File

@@ -12,6 +12,9 @@ import javafx.fxml.FXML;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.text.Text;
@@ -23,10 +26,11 @@ import java.io.File;
import java.io.IOException;

/**
* The JavaFX controller class for the main window. As of this writing, this is the only controller class of the
* application, aka "god controller". This is likely to change in the future.<br>This controller handles all user
* interaction with the main window, such as clicking buttons or entering text into text fields. It is also responsible
* for starting the computation and handling the user-request to abort a running computation.
* The JavaFX controller class for the main window. As of this writing, this is the only controller class of
* the application, aka "god controller". This is likely to change in the future.<br>This controller handles
* all user interaction with the main window, such as clicking buttons or entering text into text fields. It
* is also responsible for starting the computation and handling the user-request to abort a running
* computation.
*
* @author Manuel Friedli (manuel@fritteli.ch)
*/
@@ -96,6 +100,21 @@ public class MainController {
*/
@FXML
private FractalCanvas renderArea;
/**
* Toggle group for switching between Mandelbrot and Julia
*/
@FXML
private ToggleGroup typeToggleGroup;
/**
* Radiobutton for choosing the type Mandelbrot
*/
@FXML
private RadioButton typeMandelbrot;
/**
* Radiobutton for choosing the type Julia
*/
@FXML
private RadioButton typeJulia;
/**
* The background service that does the computation and deliver the result
*/
@@ -111,8 +130,8 @@ public class MainController {

/**
* Called by the JavaFX platform after wiring the dependencies. Used to initialize the controller and its
* components. It also triggers an initial computation of the default area so that upon showing the application
* window for the first time, there's already an image showing.
* components. It also triggers an initial computation of the default area so that upon showing the
* application window for the first time, there's already an image showing.
*/
public void initialize() {
this.initConstraints();
@@ -132,6 +151,7 @@ public class MainController {
this.width.set(800);
this.height.set(600);
this.iterations.set(100);
this.typeToggleGroup.selectToggle(this.typeMandelbrot);
}

private void initConstraints() {
@@ -141,13 +161,17 @@ public class MainController {
}

private void initValidationBindings() {
final BooleanBinding allValidBinding = this.minRe.validProperty().and(this.maxRe.validProperty()).and(this
.minIm.validProperty()).and(this.maxIm.validProperty()).and(this.width.validProperty()).and(this
.height.validProperty()).and(this.iterations.validProperty());
final BooleanBinding allValidBinding = this.minRe.validProperty()
.and(this.maxRe.validProperty())
.and(this.minIm.validProperty())
.and(this.maxIm.validProperty())
.and(this.width.validProperty())
.and(this.height.validProperty())
.and(this.iterations.validProperty());
// FIXME strange behavior: if i set a breakpoint here and run in debug mode, it works. in normal mode,
// it doesn't. WEIRD?! somehow, this thread-workaround achieves he same result.
allValidBinding.addListener((observable, oldValue, newValue) -> MainController.this.startButton.setDisable
(!newValue));
// it doesn't. WEIRD?! somehow, this thread-workaround achieves the same result.
allValidBinding.addListener(
(observable, oldValue, newValue) -> MainController.this.startButton.setDisable(!newValue));
final Thread WORKAROUND = new Thread(() -> {
allValidBinding.get();
try {
@@ -165,8 +189,9 @@ public class MainController {
final double maxRe = this.maxRe.get();
final double minIm = this.minIm.get();
final double maxIm = this.maxIm.get();
final FractalType fractalType = this.getSelectedFractalType();
this.service = new FractalService(minRe, maxRe, minIm, maxIm, this.width.get(), this.height.get(),
this.iterations.get(), FractalType.MANDELBROT);
this.iterations.get(), fractalType);
this.service.setOnSucceeded(event -> {
this.enableStartButton();
final Image image = this.service.getValue();
@@ -174,7 +199,8 @@ public class MainController {
gc.clearRect(0, 0, this.renderArea.getWidth(), this.renderArea.getHeight());
this.renderArea.setWidth(this.width.get());
this.renderArea.setHeight(this.height.get());
this.renderArea.setComplexBounds(this.minRe.get(), this.maxRe.get(), this.minIm.get(), this.maxIm.get());
this.renderArea.setComplexBounds(this.minRe.get(), this.maxRe.get(), this.minIm.get(),
this.maxIm.get());
gc.drawImage(image, 0, 0);
this.progressBar.progressProperty().unbind();
this.progressBar.setProgress(0);
@@ -229,14 +255,28 @@ public class MainController {
final int width = this.width.get();
final int height = this.height.get();
final int iterations = this.iterations.get();
this.service.setValues(minRe, maxRe, minIm, maxIm, width, height, iterations, FractalType.MANDELBROT);
final FractalType fractalType = this.getSelectedFractalType();
this.service.setValues(minRe, maxRe, minIm, maxIm, width, height, iterations, fractalType);
this.service.start();
this.hideStartButton();
}

private FractalType getSelectedFractalType() {
final Toggle selectedTaskType = this.typeToggleGroup.getSelectedToggle();
final FractalType fractalType;
if (this.typeMandelbrot.equals(selectedTaskType)) {
fractalType = FractalType.MANDELBROT;
} else if (this.typeJulia.equals(selectedTaskType)) {
fractalType = FractalType.JULIA;
} else {
fractalType = null;
}
return fractalType;
}

/**
* Handler method that is called when the "Cancel" button is clicked (during an ongoing computation, as otherwise
* the cancel button is hidden and can't be clicked).
* Handler method that is called when the "Cancel" button is clicked (during an ongoing computation, as
* otherwise the cancel button is hidden and can't be clicked).
*
* @param actionEvent The button clicked event. Ignored, as it is not used.
*/
@@ -266,8 +306,8 @@ public class MainController {
}

/**
* Handler method that is called when the "Export" button is clicked. This method shows a file chooser dialog and
* saves the current image to the selected file.
* Handler method that is called when the "Export" button is clicked. This method shows a file chooser
* dialog and saves the current image to the selected file.
*
* @param actionEvent The button clicked event. Ignored, as it is not used.
*/
@@ -296,4 +336,9 @@ public class MainController {
}
return this.saveDialog;
}

@FXML
public void typeChanged(final ActionEvent actionEvent) {
this.startButtonClicked(actionEvent);
}
}

+ 27
- 9
src/main/resources/main.fxml View File

@@ -1,10 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>
<?import ch.fritteli.javafx.text.DoubleTextField?>
<?import ch.fritteli.javafx.text.ConstrainedIntegerTextField?>
<?import ch.fritteli.fractal.gui.FractalCanvas?>
<?import ch.fritteli.javafx.text.ConstrainedIntegerTextField?>
<?import ch.fritteli.javafx.text.DoubleTextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Text?>
<VBox fx:controller="ch.fritteli.fractal.gui.MainController"
xmlns:fx="http://javafx.com/fxml" alignment="TOP_LEFT" stylesheets="style.css">
<ToolBar>
@@ -25,20 +33,30 @@
<Label text="Width" GridPane.columnIndex="4" GridPane.rowIndex="1" prefWidth="50"/>
<ConstrainedIntegerTextField fx:id="width" GridPane.columnIndex="5" GridPane.rowIndex="1"/>
<Label text="Height" GridPane.columnIndex="4" GridPane.rowIndex="2"/>
<ConstrainedIntegerTextField fx:id="height" GridPane.columnIndex="5" GridPane.rowIndex="2" prefWidth="50"/>
<ConstrainedIntegerTextField fx:id="height" GridPane.columnIndex="5" GridPane.rowIndex="2"
prefWidth="50"/>
<Label text="Iterations" GridPane.columnIndex="6" GridPane.rowIndex="0"/>
<ConstrainedIntegerTextField fx:id="iterations" GridPane.columnIndex="6" GridPane.rowIndex="1"/>

<Button fx:id="startButton" text="Start!" GridPane.columnIndex="6" GridPane.rowIndex="2"
<fx:define>
<ToggleGroup fx:id="typeToggleGroup"/>
</fx:define>
<HBox GridPane.columnIndex="6" GridPane.rowIndex="2">
<RadioButton fx:id="typeMandelbrot" toggleGroup="$typeToggleGroup" text="Mandelbrot"
visible="true" disable="false" onAction="#typeChanged"/>
<RadioButton fx:id="typeJulia" toggleGroup="$typeToggleGroup" text="Julia" visible="true"
disable="false" onAction="#typeChanged"/>
</HBox>
<Button fx:id="startButton" text="Start!" GridPane.columnIndex="7" GridPane.rowIndex="2"
onAction="#startButtonClicked" visible="true" disable="true"/>
<Button fx:id="cancelButton" text="Stop" GridPane.columnIndex="6" GridPane.rowIndex="2"
<Button fx:id="cancelButton" text="Stop" GridPane.columnIndex="7" GridPane.rowIndex="2"
onAction="#cancelButtonClicked" visible="false" disable="true"/>
<Button text="Export image" GridPane.columnIndex="7"
GridPane.rowIndex="1" onAction="#export"/>
</GridPane>
</ToolBar>
<HBox>
<ProgressBar fx:id="progressBar" progress="0.0f"/>
<ProgressBar fx:id="progressBar" progress="0.0f"/>
<Text fx:id="coordinateOutput"/>
<Text fx:id="status"/>
</HBox>

Loading…
Cancel
Save