Skip to content
Snippets Groups Projects
ChaosGameGuiView.java 15.4 KiB
Newer Older
Magnus Eik's avatar
Magnus Eik committed
package edu.ntnu.stud.chaosgame.view;

import edu.ntnu.stud.chaosgame.controller.game.ChaosCanvas;
import edu.ntnu.stud.chaosgame.controller.game.ChaosGame;
import edu.ntnu.stud.chaosgame.controller.game.ChaosGameDescription;
Magnus Eik's avatar
Magnus Eik committed
import edu.ntnu.stud.chaosgame.model.generators.ChaosGameDescriptionFactory;

Håvard Daleng's avatar
Håvard Daleng committed
import edu.ntnu.stud.chaosgame.model.transformations.AffineTransform2D;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.geometry.Insets;
Magnus Eik's avatar
Magnus Eik committed
import javafx.scene.Scene;
import javafx.scene.control.*;
Magnus Eik's avatar
Magnus Eik committed
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
Magnus Eik's avatar
Magnus Eik committed

import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
public class ChaosGameGuiView implements ChaosGameObserver {
Håvard Daleng's avatar
Håvard Daleng committed
  private int currentLine = 0;

  /**
   * The ChaosCanvas for this GUI.
   */
  private ChaosCanvas canvas;

  /**
   * The ChaosGameDescription.
   */
  private ChaosGameDescription description;

  /**
   * The ChaosGameDescriptionFactory.
   */
  private ChaosGameDescriptionFactory factory;

  /**
   * The AtomicReference for the ChaosGameDescription.
   */
  private AtomicReference<ChaosGameDescription> descriptionRef;

  /**
   * The PixelWriter for the GUI.
   */
  private PixelWriter pixelWriter;

  /**
   * The ImageView for the GUI.
Håvard Daleng's avatar
Håvard Daleng committed
   */
  private ChaosGameImageView imageView;
   * The Scene for the GUI.
Håvard Daleng's avatar
Håvard Daleng committed
   */
  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, and quit buttons for the GUI.
   */
  private Button startButton;
  private Button stopButton;
  private Button newButton;
  private Button clearButton;
  private Button quitButton;

  /**
   * 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;

  private final int resolutionHeight = 1000;
  private final int resolutionWidth = 1000;

  public ChaosGameGuiView(Stage primaryStage) throws IOException {
Håvard Daleng's avatar
Håvard Daleng committed

    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();
    this.timeline.setCycleCount(Timeline.INDEFINITE);

Håvard Daleng's avatar
Håvard Daleng committed

    // 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 ->{
      WritableImage newWritableImage = new WritableImage(width, height);
      setPixelWriter(newWritableImage.getPixelWriter());
      setImageViewFromImage(newWritableImage);
      canvas.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.canvas = new ChaosCanvas(resolutionWidth, resolutionHeight, descriptionRef.get().getMinCoords(), descriptionRef.get().getMaxCoords());
Håvard Daleng's avatar
Håvard Daleng committed
    game = new ChaosGame(this.description, canvas);
  }

  /**
   * Initialize components related to the image view and zoom function.
   */
  private void initializeImageView() {
    // Image view
    this.imageView = new ChaosGameImageView(this);
    width = resolutionWidth;
    height = resolutionHeight;
Håvard Daleng's avatar
Håvard Daleng committed
    WritableImage writableImage = new WritableImage(width, height);
    pixelWriter = writableImage.getPixelWriter();
    this.imageView.setImage(writableImage);
    this.imageView.setFitHeight(1000);
    this.imageView.setFitWidth(1000);
    this.fillImageView();
    /*imageView.setOnScroll(event -> {
Håvard Daleng's avatar
Håvard Daleng committed
      double zoomFactor = 1.05; // This is the zoom factor per scroll
      double movement = event.getDeltaY();
Håvard Daleng's avatar
Håvard Daleng committed

      imageView.setTranslateX(imageView.getTranslateX() - event.getDeltaX());
      imageView.setTranslateY(imageView.getTranslateY() - event.getDeltaY());

      if (movement > 0) {
Håvard Daleng's avatar
Håvard Daleng committed
        imageView.setScaleX(imageView.getScaleX() * zoomFactor);
        imageView.setScaleY(imageView.getScaleY() * zoomFactor);
      } else {
        imageView.setScaleX(imageView.getScaleX() / zoomFactor);
        imageView.setScaleY(imageView.getScaleY() / zoomFactor);
      }

      // Limit the zoom so the scale doesn't become too big or too small
      imageView.setScaleX(Math.max(imageView.getScaleX(), 1.0));
      imageView.setScaleY(Math.max(imageView.getScaleY(), 1.0));
      imageView.setScaleX(Math.min(imageView.getScaleX(), 10.0)); // max scale
      imageView.setScaleY(Math.min(imageView.getScaleY(), 10.0)); // max scale

      event.consume(); // consume the event so it doesn't propagate further
    });*/

  }

  /**
   * Color the entire image view white to facilitate scrolling.
   */
  private void fillImageView() {
    // Color the image white
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        pixelWriter.setColor(x, y, Color.WHITE);
      }
    }
Håvard Daleng's avatar
Håvard Daleng committed

  }

  /**
   * 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<>(canvas);

    // Set action for Sierpinski radio button.
    sierpinskiRadioButton.setOnAction(event -> {
      timeline.stop();
      //canvasRef.get().clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed
      WritableImage newWritableImage = new WritableImage(width, height);
      setPixelWriter(newWritableImage.getPixelWriter());
      setImageViewFromImage(newWritableImage);
      this.fillImageView();
      //canvas.clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed

      this.descriptionRef.set(factory.getDescriptions().get(0)); // Assuming the Sierpinski description is at index 0
      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
Håvard Daleng's avatar
Håvard Daleng committed
      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
    });

    // Set action for Barnsley radio button.
    barnsleyRadioButton.setOnAction(event -> {
      timeline.stop();
      //canvasRef.get().clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed
      WritableImage newWritableImage = new WritableImage(width, height);
      setPixelWriter(newWritableImage.getPixelWriter());
      setImageViewFromImage(newWritableImage);
      this.fillImageView();
      //canvas.clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed
      this.descriptionRef.set(factory.getDescriptions().get(1)); // Assuming the Sierpinski description is at index 0
      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
Håvard Daleng's avatar
Håvard Daleng committed
      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
    });

    // Set action for Julia radio button.
    juliaRadioButton.setOnAction(event -> {
      timeline.stop();
      //canvasRef.get().clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed
      WritableImage newWritableImage = new WritableImage(width, height);
      setPixelWriter(newWritableImage.getPixelWriter());
      setImageViewFromImage(newWritableImage);
      this.fillImageView();
      //canvas.clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed
      this.descriptionRef.set(factory.getDescriptions().get(2)); // Assuming the Sierpinski description is at index 0
      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
Håvard Daleng's avatar
Håvard Daleng committed
      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
    });

    improvedBarnsleyButton.setOnAction(event -> {
      timeline.stop();
      //canvasRef.get().clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed
      WritableImage newWritableImage = new WritableImage(width, height);
      setPixelWriter(newWritableImage.getPixelWriter());
      setImageViewFromImage(newWritableImage);
      this.fillImageView();
      //canvas.clearCanvas();
Håvard Daleng's avatar
Håvard Daleng committed

      // Test
      this.descriptionRef.set(factory.getDescriptions().get(3)); // Assuming the Sierpinski description is at index 0
      this.description = factory.getDescriptions().get(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());
      }


      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
Håvard Daleng's avatar
Håvard Daleng committed
      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
    });

    // 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));

    this.borderPane = new BorderPane();
    this.borderPane.setCenter(imageView);
    this.borderPane.setRight(sideMenu);
    imageView.setFocusTraversable(true);
    sideMenu.setFocusTraversable(false);
    borderPane.setFocusTraversable(false);

  }

  public void drawChaosGame(){
    ChaosCanvas canvas = game.getCanvas();


    game.runSteps(100000);
Håvard Daleng's avatar
Håvard Daleng committed

    // Test implementation for drawing fractals
    int[][] betaArray = canvas.getCanvasArray();
    for (int i = 0; i < canvas.getWidth(); i++) {
      for (int j = 0; j < canvas.getHeight(); j++) {
        if (betaArray[i][j] == 1) {
          pixelWriter.setColor(j,i,Color.BLACK);
Håvard Daleng's avatar
Håvard Daleng committed
  }

  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 setPixelWriter(PixelWriter pixelWriter) {
    this.pixelWriter = pixelWriter;
  }

  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.
   *
   * @param description the description.
   */
  @Override
  public void updateDescription(ChaosGameDescription description) {
    this.game.setDescription(description);
  }

  /**
   * 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();
  }