diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java index a9079ced95f9998ed419456d266023719ff949aa..39d7916b49afe4f0210be3feb06d7f66680a7e74 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java @@ -6,11 +6,10 @@ import edu.ntnu.stud.chaosgame.model.game.ChaosGameDescription; import edu.ntnu.stud.chaosgame.view.ChaosGameObserver; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Random; /** - * Class representing the chaos game. + * Class representing the chaos game, responsible for managing its progression. */ public class ChaosGame { @@ -27,7 +26,7 @@ public class ChaosGame { /** * Observers monitoring this ChaosGame. */ - private ArrayList<ChaosGameObserver> observers; + private ArrayList<ChaosGameObserver> observers; /** * The current point in the chaos game. @@ -95,13 +94,6 @@ public class ChaosGame { */ public Vector2D getCurrentPoint() { return this.currentPoint; } - /** - * Get the random number generator for this chaos game. - * - * @return the RNG. - */ - public Random getRandom() { return this.random; } - /** * Get the description for this choas game. * @@ -164,8 +156,6 @@ public class ChaosGame { * @param n the number of iterations. */ public void runSteps(int n) { - ArrayList<Integer> results = new ArrayList<>( - Collections.nCopies(this.description.getTransforms().size(), 0)); // Testing the RNG for (int i = 0; i < n; i++) { @@ -181,10 +171,11 @@ public class ChaosGame { /** * If the point is out of bounds, iterate until it is within bounds. - * If the + * If the method fails to find a valid point within 100 iterations, it returns the invalid + * point anyway to avoid infinite loops. * * @param point the starting point. - * @return the resulting valid point within bounds. + * @return the resulting valid point within bounds, or outside if no valid point is found. */ public Vector2D findValidPoint(Vector2D point) { if (!this.canvas.isPointInCanvasRange(point)) { diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/GuiButtonController.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/GuiButtonController.java index 7ee7424a5bb92af295544277b0c5533616aa8971..b663af9e19bb6c279532ce875f5e3b4bea6da2aa 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/GuiButtonController.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/GuiButtonController.java @@ -16,17 +16,13 @@ import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.ComboBox; -import javafx.scene.image.PixelFormat; import javafx.scene.image.PixelReader; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; -import javafx.stage.DirectoryChooser; import javafx.util.Duration; import javafx.stage.FileChooser; - import javax.imageio.ImageIO; import java.awt.image.BufferedImage; - import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -35,7 +31,7 @@ import java.nio.file.Paths; import java.util.Objects; /** - * Controller class for the GUI buttons. + * Controller class for the GUI buttons, responsible for handling their functionality. */ public class GuiButtonController { @@ -69,17 +65,20 @@ public class GuiButtonController { */ private ChaosGameDescriptionFactory factory; - /** - * The controller which assigns functionality to the popup-window - */ - private PopupButtonController popupButtonController; + /** + * The controller which assigns functionality to the popup-window + */ + private PopupButtonController popupButtonController; - /** - * Default image save path - */ - private String currentImagePath = System.getProperty("user.home"); + /** + * Default image save path + */ + private String currentImagePath = System.getProperty("user.home"); - private int stepCounter = 0; + /** + * The step counter for the controller. + */ + private int stepCounter = 0; /** @@ -90,162 +89,162 @@ public class GuiButtonController { */ public GuiButtonController(ChaosGame game, ChaosGameGui gui) { - this.game = game; - this.gui = gui; - this.timeline = new Timeline(new KeyFrame(Duration.seconds(0.05), event -> this.drawChaosGame())); - this.timeline.setCycleCount(Timeline.INDEFINITE); - this.factory = new ChaosGameDescriptionFactory(); - this.chaosCanvas = new ChaosCanvas(1000, 1000, this.factory.getDescriptions().get(0).getMinCoords(), - this.factory.getDescriptions().get(0).getMaxCoords()); - initializeMainButtons(); - this.fileHandler = new ChaosGameFileHandler(); - this.popupButtonController = new PopupButtonController(); - initializeLoadFractalFromFileButtonHandler(); - initializeWriteToFileButtonHandler(); - initializeModifyGameButtonHandler(); - initializeDescriptionComboBox(); - initializeSaveImageButtons(); + this.game = game; + this.gui = gui; + this.timeline = new Timeline(new KeyFrame(Duration.seconds(0.05), event -> this.drawChaosGame())); + this.timeline.setCycleCount(Timeline.INDEFINITE); + this.factory = new ChaosGameDescriptionFactory(); + this.chaosCanvas = new ChaosCanvas(1000, 1000, this.factory.getDescriptions().get(0).getMinCoords(), + this.factory.getDescriptions().get(0).getMaxCoords()); + initializeMainButtons(); + this.fileHandler = new ChaosGameFileHandler(); + this.popupButtonController = new PopupButtonController(); + initializeLoadFractalFromFileButtonHandler(); + initializeWriteToFileButtonHandler(); + initializeModifyGameButtonHandler(); + initializeDescriptionComboBox(); + initializeSaveImageButtons(); } - private void initializeModifyGameButtonHandler() { - gui.getModifyGameButton().setOnAction(e -> { - ChaosGameDescription description = game.getDescription(); - if (description.getTransforms().get(0) instanceof JuliaTransform) { - JuliaPopup juliaPopup = new JuliaPopup(description); - juliaPopup.getPopupModifyStage().setUserData(this); - popupButtonController.handleUpdateButton(juliaPopup, description); - juliaPopup.display(); - } else { - Affine2DPopup affine2DPopup = new Affine2DPopup(description); - affine2DPopup.getPopupModifyStage().setUserData(this); - popupButtonController.handleUpdateButton(affine2DPopup, description); - affine2DPopup.display(); - } - }); + /** + * Initialize the modify game button handler. + */ + private void initializeModifyGameButtonHandler() { + gui.getModifyGameButton().setOnAction(e -> { + ChaosGameDescription description = game.getDescription(); + if (description.getTransforms().get(0) instanceof JuliaTransform) { + JuliaPopup juliaPopup = new JuliaPopup(description); + juliaPopup.getPopupModifyStage().setUserData(this); + popupButtonController.handleUpdateButton(juliaPopup); + juliaPopup.display(); + } else { + Affine2DPopup affine2DPopup = new Affine2DPopup(description); + affine2DPopup.getPopupModifyStage().setUserData(this); + popupButtonController.handleUpdateButton(affine2DPopup); + affine2DPopup.display(); + } + }); } - private void initializeDescriptionComboBox() { - ComboBox<String> descriptionComboBox = gui.getDescriptionComboBox(); - - descriptionComboBox.setOnShowing(event -> updateDescriptionComboBox()); - - descriptionComboBox.getSelectionModel().selectedItemProperty() - .addListener((observable, oldValue, newValue) -> { - for (int i = 0; i < factory.getDescriptions().size(); i++) { - ChaosGameDescription description = factory.getDescriptions().get(i); - if (description.getName().equals(newValue)) { - updateDescription(i); - break; - } - } - + /** + * Initialize the description combo box. + */ + private void initializeDescriptionComboBox() { + ComboBox<String> descriptionComboBox = gui.getDescriptionComboBox(); + + descriptionComboBox.setOnShowing(event -> updateDescriptionComboBox()); + + descriptionComboBox.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> { + for (int i = 0; i < factory.getDescriptions().size(); i++) { + ChaosGameDescription description = factory.getDescriptions().get(i); + if (description.getName().equals(newValue)) { + updateDescription(i); + break; + } + } - }); + }); } + /* + * Update the description combo box. + */ public void updateDescriptionComboBox() { - factory = new ChaosGameDescriptionFactory(); - ComboBox<String> descriptionComboBox = gui.getDescriptionComboBox(); - descriptionComboBox.getItems().clear(); - for (ChaosGameDescription description : factory.getDescriptions()) { - descriptionComboBox.getItems().add(description.getName()); - } + factory = new ChaosGameDescriptionFactory(); + ComboBox<String> descriptionComboBox = gui.getDescriptionComboBox(); + descriptionComboBox.getItems().clear(); + for (ChaosGameDescription description : factory.getDescriptions()) { + descriptionComboBox.getItems().add(description.getName()); + } } private void initializeSaveImageButtons(){ - gui.getSaveImageButton().setOnAction(event -> saveImage()); + gui.getSaveImageButton().setOnAction(event -> saveImage()); } private void initializeLoadFractalFromFileButtonHandler() { - gui.getLoadFractalFromFileButton().setOnAction(event -> { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Open Fractal File"); - fileChooser.getExtensionFilters().add( - new FileChooser.ExtensionFilter("Text Files", "*.txt")); - File selectedFile = fileChooser.showOpenDialog(gui.getStage()); - if (selectedFile != null) { - Path dest = Paths.get("src/main/resources/descriptions/saved_descriptions/" + selectedFile.getName()); - try { - Files.copy(selectedFile.toPath(), dest); - // Refresh the list of descriptions - factory = new ChaosGameDescriptionFactory(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - } - - private void initializeWriteToFileButtonHandler() { - gui.getWriteToFileButton().setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - // Create a FileChooser object - FileChooser fileChooser = new FileChooser(); - - // Set the title of the FileChooser dialog - fileChooser.setTitle("Save File"); - - // Set the initial directory of the FileChooser - fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); - - // Set the extension filter - fileChooser.getExtensionFilters().addAll( - new FileChooser.ExtensionFilter("Text Files", "*.txt"), - new FileChooser.ExtensionFilter("All Files", "*.*") - ); - - // Show the save file dialog and get the selected file - File file = fileChooser.showSaveDialog(gui.getStage()); - - if (file != null) { - try { - // Create a ChaosGameDescription object - ChaosGameDescription description = game.getDescription(); - - // Call the writeToFile method - fileHandler.writeToFile(description, file.getPath()); - } catch (IOException e) { - // Handle the exception + gui.getLoadFractalFromFileButton().setOnAction(event -> { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Open Fractal File"); + fileChooser.getExtensionFilters().add( + new FileChooser.ExtensionFilter("Text Files", "*.txt")); + File selectedFile = fileChooser.showOpenDialog(gui.getStage()); + if (selectedFile != null) { + Path dest = Paths.get("src/main/resources/descriptions/saved_descriptions/" + selectedFile.getName()); + try { + Files.copy(selectedFile.toPath(), dest); + // Refresh the list of descriptions + factory = new ChaosGameDescriptionFactory(); + } catch (IOException e) { e.printStackTrace(); - } - } } - }); - this.game = game; - this.gui = gui; - this.timeline = new Timeline(new KeyFrame(Duration.seconds(0.05), event -> this.drawChaosGame())); - this.timeline.setCycleCount(Timeline.INDEFINITE); - this.factory = new ChaosGameDescriptionFactory(); - this.chaosCanvas = new ChaosCanvas(1000, 1000, this.factory.getDescriptions().get(0).getMinCoords(), - this.factory.getDescriptions().get(0).getMaxCoords()); - initializeMainButtons(); + } + }); } /** - * Run the game for a specified number of steps. - * - * @param steps the number of steps to run the game for. + * Initialize the write to file button handler. */ - public void runGameSteps(int steps) { - game.runSteps(steps); + private void initializeWriteToFileButtonHandler() { + gui.getWriteToFileButton().setOnAction(new EventHandler<ActionEvent>() { + @Override + public void handle(ActionEvent event) { + // Create a FileChooser object + FileChooser fileChooser = new FileChooser(); + + // Set the title of the FileChooser dialog + fileChooser.setTitle("Save File"); + + // Set the initial directory of the FileChooser + fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + + // Set the extension filter + fileChooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter("Text Files", "*.txt"), + new FileChooser.ExtensionFilter("All Files", "*.*") + ); + + // Show the save file dialog and get the selected file + File file = fileChooser.showSaveDialog(gui.getStage()); + + if (file != null) { + try { + // Create a ChaosGameDescription object + ChaosGameDescription description = game.getDescription(); + + // Call the writeToFile method + fileHandler.writeToFile(description, file.getPath()); + } catch (IOException e) { + // Handle the exception + e.printStackTrace(); + } + } + } + }); + this.timeline = new Timeline(new KeyFrame(Duration.seconds(0.05), event -> this.drawChaosGame())); + this.timeline.setCycleCount(Timeline.INDEFINITE); + this.factory = new ChaosGameDescriptionFactory(); + this.chaosCanvas = new ChaosCanvas(1000, 1000, this.factory.getDescriptions().get(0).getMinCoords(), + this.factory.getDescriptions().get(0).getMaxCoords()); + initializeMainButtons(); } /** * Draw the chaos game on the canvas. */ public void drawChaosGame(){ - int iterationLimit = Integer.parseInt(gui.getIterationLimitTextField().getText()); + int iterationLimit = Integer.parseInt(gui.getIterationLimitTextField().getText()); - if (stepCounter >= iterationLimit) { - timeline.stop(); - return; - } + if (stepCounter >= iterationLimit) { + timeline.stop(); + return; + } // Run the number of steps specified in text field, else 1000. game.runSteps(!Objects.equals(gui.getStepCountTextField().getText(), "") ? - Integer.parseInt(gui.getStepCountTextField().getText()) : 1000); + Integer.parseInt(gui.getStepCountTextField().getText()) : 1000); // Convert the canvas to either an image with coloured pixels based on intensity, or just black and white. ChaosCanvasToImageConverter converter = new ChaosCanvasToImageConverter(chaosCanvas, - gui.getColorCheckBox().isSelected()); + gui.getColorCheckBox().isSelected()); WritableImage image = converter.getImage(); gui.getCanvas().getGraphicsContext2D().drawImage(image, 0, 0); @@ -257,46 +256,50 @@ public class GuiButtonController { * Saves a WritableImage to an image file. */ public void saveImage() { - // Retrieve the WritableImage from the ImageView - WritableImage image = gui.getImageView().snapshot(null, null); - - // Use a FileChooser to let the user specify the filename - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Save Image"); - fileChooser.setInitialDirectory(new File(currentImagePath)); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG Files", "*.png")); - File file = fileChooser.showSaveDialog(gui.getStage()); - - if (file != null) { - try { - BufferedImage bufferedImage = toBufferedImage(image); - ImageIO.write(bufferedImage, "png", file); - } catch (IOException e) { - System.err.println("Error while saving image: " + e.getMessage()); - } + // Retrieve the WritableImage from the ImageView + WritableImage image = gui.getImageView().snapshot(null, null); + + // Use a FileChooser to let the user specify the filename + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save Image"); + fileChooser.setInitialDirectory(new File(currentImagePath)); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG Files", "*.png")); + File file = fileChooser.showSaveDialog(gui.getStage()); + + if (file != null) { + try { + BufferedImage bufferedImage = toBufferedImage(image); + ImageIO.write(bufferedImage, "png", file); + } catch (IOException e) { + System.err.println("Error while saving image: " + e.getMessage()); } + } } - private BufferedImage toBufferedImage(WritableImage image) { - int width = (int) image.getWidth(); - int height = (int) image.getHeight(); - BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - PixelReader reader = image.getPixelReader(); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - Color color = reader.getColor(x, y); - int r = (int) (color.getRed() * 255); - int g = (int) (color.getGreen() * 255); - int b = (int) (color.getBlue() * 255); - int a = (int) (color.getOpacity() * 255); - int argb = (a << 24) | (r << 16) | (g << 8) | b; - bufferedImage.setRGB(x, y, argb); - } - } - return bufferedImage; + /** + * Converts a WritableImage to a BufferedImage. + * + * @param image the WritableImage to convert. + * @return the converted BufferedImage. + */ + private BufferedImage toBufferedImage(WritableImage image) { + int width = (int) image.getWidth(); + int height = (int) image.getHeight(); + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + PixelReader reader = image.getPixelReader(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + Color color = reader.getColor(x, y); + int r = (int) (color.getRed() * 255); + int g = (int) (color.getGreen() * 255); + int b = (int) (color.getBlue() * 255); + int a = (int) (color.getOpacity() * 255); + int argb = (a << 24) | (r << 16) | (g << 8) | b; + bufferedImage.setRGB(x, y, argb); + } } - - + return bufferedImage; + } /** * Start the game. @@ -314,17 +317,6 @@ public class GuiButtonController { gui.getStartButton().setText("Resume"); } - /** - * Start a new game. - */ - public void newGame() { - game.getCanvas().clearCanvas(); - timeline.play(); - } - - - - /** * Clear the image view. */ diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/PopupButtonController.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/PopupButtonController.java index a30431c8c32b734f9af63884e1ccdc210ca1d196..0a964119a3391d7d7beb416d4363b03a1a917af4 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/PopupButtonController.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/PopupButtonController.java @@ -13,102 +13,128 @@ import edu.ntnu.stud.chaosgame.view.modificationpopups.Affine2DPopup; import edu.ntnu.stud.chaosgame.view.modificationpopups.JuliaPopup; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; -import javafx.stage.Stage; - import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +/** + * Controller class for handling the buttons relating to modifying the chaos game in the + * user interface. + */ public class PopupButtonController { - private ChaosGameFileHandler fileHandler; - public PopupButtonController() { - this.fileHandler = new ChaosGameFileHandler(); - } + /** + * File handler for handling file operations. + */ + private final ChaosGameFileHandler fileHandler; + + /** + * Basic constructor for the class. + */ + public PopupButtonController() { + this.fileHandler = new ChaosGameFileHandler(); + } - public void handleUpdateButton(Affine2DPopup popup, ChaosGameDescription originalDescription) { - popup.getUpdateButton().setOnAction(event -> { - // Create new ChaosGameDescription based on the popup fields - String name = popup.getNameTextField().getText(); - Vector2D minCoords = new Vector2D( - Double.parseDouble(popup.getMinXTextField().getText()), - Double.parseDouble(popup.getMinYTextField().getText()) - ); - Vector2D maxCoords = new Vector2D( - Double.parseDouble(popup.getMaxXTextField().getText()), - Double.parseDouble(popup.getMaxYTextField().getText()) - ); + /** + * Handles the update button in the Affine2DPopup. + * + * @param popup the Affine2DPopup to handle the button for. + */ + public void handleUpdateButton(Affine2DPopup popup) { + popup.getUpdateButton().setOnAction(event -> { + // Create new ChaosGameDescription based on the popup fields + String name = popup.getNameTextField().getText(); + Vector2D minCoords = new Vector2D( + Double.parseDouble(popup.getMinXTextField().getText()), + Double.parseDouble(popup.getMinYTextField().getText()) + ); + Vector2D maxCoords = new Vector2D( + Double.parseDouble(popup.getMaxXTextField().getText()), + Double.parseDouble(popup.getMaxYTextField().getText()) + ); - List<Transform2D> transforms = new ArrayList<>(); - for (int i = 0; i < popup.getMatrixTextFields().length; i++) { - double a00 = Double.parseDouble(popup.getMatrixTextFields()[i][0].getText()); - double a01 = Double.parseDouble(popup.getMatrixTextFields()[i][1].getText()); - double a10 = Double.parseDouble(popup.getMatrixTextFields()[i][2].getText()); - double a11 = Double.parseDouble(popup.getMatrixTextFields()[i][3].getText()); - double b0 = Double.parseDouble(popup.getVectorTextFields()[2 * i].getText()); - double b1 = Double.parseDouble(popup.getVectorTextFields()[2 * i + 1].getText()); + List<Transform2D> transforms = new ArrayList<>(); + for (int i = 0; i < popup.getMatrixTextFields().length; i++) { + double a00 = Double.parseDouble(popup.getMatrixTextFields()[i][0].getText()); + double a01 = Double.parseDouble(popup.getMatrixTextFields()[i][1].getText()); + double a10 = Double.parseDouble(popup.getMatrixTextFields()[i][2].getText()); + double a11 = Double.parseDouble(popup.getMatrixTextFields()[i][3].getText()); + double b0 = Double.parseDouble(popup.getVectorTextFields()[2 * i].getText()); + double b1 = Double.parseDouble(popup.getVectorTextFields()[2 * i + 1].getText()); - transforms.add(new AffineTransform2D(new Matrix2x2(a00, a01, a10, a11), new Vector2D(b0, b1))); - } + transforms.add(new AffineTransform2D(new Matrix2x2(a00, a01, a10, a11), + new Vector2D(b0, b1))); + } - // Create new ChaosGameDescription - ChaosGameDescription newDescription = new ChaosGameDescription(minCoords, maxCoords, transforms, name); + // Create new ChaosGameDescription + ChaosGameDescription newDescription = new ChaosGameDescription(minCoords, maxCoords, transforms, name); - // Write new ChaosGameDescription to a file - try { - String filePath = Paths.get("src/main/resources/descriptions/saved_descriptions", name + ".txt").toString(); - fileHandler.writeToFile(newDescription, filePath); - showConfirmationPopup(popup); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } + // Write new ChaosGameDescription to a file + try { + String filePath = Paths.get("src/main/resources/descriptions/saved_descriptions", name + + ".txt").toString(); + fileHandler.writeToFile(newDescription, filePath); + showConfirmationPopup(popup); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } - public void handleUpdateButton(JuliaPopup popup, ChaosGameDescription originalDescription) { - popup.getUpdateButton().setOnAction(event -> { - // Create new ChaosGameDescription based on the popup fields - String name = popup.getNameTextField().getText(); - Vector2D minCoords = new Vector2D( - Double.parseDouble(popup.getMinXTextField().getText()), - Double.parseDouble(popup.getMinYTextField().getText()) - ); - Vector2D maxCoords = new Vector2D( - Double.parseDouble(popup.getMaxXTextField().getText()), - Double.parseDouble(popup.getMaxYTextField().getText()) - ); + /** + * Handles the update button in the JuliaPopup. + * + * @param popup the JuliaPopup to handle the button for. + */ + public void handleUpdateButton(JuliaPopup popup) { + popup.getUpdateButton().setOnAction(event -> { + // Create new ChaosGameDescription based on the popup fields + String name = popup.getNameTextField().getText(); + Vector2D minCoords = new Vector2D( + Double.parseDouble(popup.getMinXTextField().getText()), + Double.parseDouble(popup.getMinYTextField().getText()) + ); + Vector2D maxCoords = new Vector2D( + Double.parseDouble(popup.getMaxXTextField().getText()), + Double.parseDouble(popup.getMaxYTextField().getText()) + ); - double realPart = Double.parseDouble(popup.getRealPartTextField().getText()); - double imagPart = Double.parseDouble(popup.getImagPartTextField().getText()); - JuliaTransform transform = new JuliaTransform(new Complex(realPart, imagPart),1); + double realPart = Double.parseDouble(popup.getRealPartTextField().getText()); + double imagPart = Double.parseDouble(popup.getImagPartTextField().getText()); + JuliaTransform transform = new JuliaTransform(new Complex(realPart, imagPart),1); - // Create new ChaosGameDescription - ChaosGameDescription newDescription = new ChaosGameDescription(minCoords, maxCoords, List.of(transform), name); + // Create new ChaosGameDescription + ChaosGameDescription newDescription = new ChaosGameDescription(minCoords, maxCoords, List.of(transform), name); - // Write new ChaosGameDescription to a file - try { - String filePath = Paths.get("src/main/resources/descriptions/saved_descriptions", name + ".txt").toString(); - fileHandler.writeToFile(newDescription, filePath); - showConfirmationPopup(popup); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } + // Write new ChaosGameDescription to a file + try { + String filePath = Paths.get("src/main/resources/descriptions/saved_descriptions", name + ".txt").toString(); + fileHandler.writeToFile(newDescription, filePath); + showConfirmationPopup(popup); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } - private void showConfirmationPopup(AbstractPopup popup) { - Alert alert = new Alert(Alert.AlertType.INFORMATION, "Updated ChaosGameDescription has been saved.", ButtonType.OK); - alert.initOwner(popup.getPopupModifyStage()); - alert.showAndWait().ifPresent(response -> { - if (response == ButtonType.OK) { - popup.getPopupModifyStage().close(); - GuiButtonController controller = (GuiButtonController) popup.getPopupModifyStage().getUserData(); - if (controller != null) { - controller.updateDescriptionComboBox(); - } - } - }); - } + /** + * Shows a confirmation popup when the ChaosGameDescription has been updated and saved. + * + * @param popup the popup to show the confirmation for. + */ + private void showConfirmationPopup(AbstractPopup popup) { + Alert alert = new Alert(Alert.AlertType.INFORMATION, "Updated ChaosGameDescription has been saved.", ButtonType.OK); + alert.initOwner(popup.getPopupModifyStage()); + alert.showAndWait().ifPresent(response -> { + if (response == ButtonType.OK) { + popup.getPopupModifyStage().close(); + GuiButtonController controller = (GuiButtonController) popup.getPopupModifyStage().getUserData(); + if (controller != null) { + controller.updateDescriptionComboBox(); + } + } + }); + } } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/utility/ChaosGameFileHandler.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/utility/ChaosGameFileHandler.java index 3ed6bbc5f2f28689f4449ab27be605169b410b2d..6f2de5bf26bb4a930cdda8e4923d015553bcc29b 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/controller/utility/ChaosGameFileHandler.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/utility/ChaosGameFileHandler.java @@ -24,6 +24,7 @@ public class ChaosGameFileHandler { /** * Creates a ChaosGameDescription based on the file found at the input path. + * * @param path A String input which gives the path to a file describing a chaos game. * @return A {@link ChaosGameDescription} description of a chaos game. */ @@ -124,8 +125,10 @@ public class ChaosGameFileHandler { } /** - * Reads from a file - * @param description A {@link ChaosGameDescription} description of the chaos game that should be written to file. + * Reads from a file. + * + * @param description A {@link ChaosGameDescription} description + * of the chaos game that should be written to file. * @param path A String describing the path to the file that should be written to. */ public void writeToFile(ChaosGameDescription description, String path) throws IOException{ @@ -162,9 +165,8 @@ public class ChaosGameFileHandler { } } - } catch (IOException e) { - throw new IOException("Failure to write to file", e); + PopupManager.displayError("Write to file error.", "Failed to write to file"); } } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/data/Complex.java b/src/main/java/edu/ntnu/stud/chaosgame/model/data/Complex.java index a926323062b0aa28064ba1841d3dc174a418f3c5..6efe5857859a79ae3e20e1ae259d7f1e30f9815c 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/data/Complex.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/data/Complex.java @@ -22,5 +22,4 @@ public class Complex extends Vector2D { double theta = Math.atan2(getX1(), getX0()) / 2; return new Complex(r * Math.cos(theta), r * Math.sin(theta)); } - } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/data/Matrix2x2.java b/src/main/java/edu/ntnu/stud/chaosgame/model/data/Matrix2x2.java index 8b5e1de1d3f64ce848ab1d6d9d6e65679226d184..992b7a7b9066b5bba247d000e291d1a8b3a1e9b1 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/data/Matrix2x2.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/data/Matrix2x2.java @@ -53,6 +53,7 @@ public class Matrix2x2 { /** * Getter method for {@link ChaosGameDescription} + * * @return a matrix component. */ public double getA00() { @@ -61,6 +62,7 @@ public class Matrix2x2 { /** * Getter method for {@link ChaosGameDescription} + * * @return a matrix component. */ public double getA01() { @@ -69,6 +71,7 @@ public class Matrix2x2 { /** * Getter method for {@link ChaosGameDescription} + * * @return a matrix component. */ public double getA10() { @@ -77,6 +80,7 @@ public class Matrix2x2 { /** * Getter method for {@link ChaosGameDescription} + * * @return a matrix component. */ public double getA11() { diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosCanvas.java b/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosCanvas.java index e344c15afa03278c1277361647ee2c3b4d9fcf7f..900db6fb9a4578553081bb80f9fcc6a86abf74c3 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosCanvas.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosCanvas.java @@ -42,9 +42,6 @@ public class ChaosCanvas { */ private Vector2D maxCoords; - private int centreX; - private int centreY; - /** * Affine transformation for converting coordinates to canvas indices. */ @@ -85,8 +82,6 @@ public class ChaosCanvas { public void putPixel(Vector2D point, boolean countIntensity) { if (point.getX0() > maxCoords.getX0() || point.getX0() < minCoords.getX0() || point.getX1() > maxCoords.getX1() || point.getX1() < minCoords.getX1()) { - //PopupManager.displayError("Point out of bounds", - // "The point is out of bounds of the canvas."); return; } int xIndex = (int) transformCoordsToIndices.transform(point).getX0(); @@ -156,31 +151,6 @@ public class ChaosCanvas { */ public int[][] getCanvasIntensityArray() { return this.canvasIntensityArray; } - /** - * Get the number of points in the canvas with a value of 1. - * - * @return the sum of the points. - */ - public int getNumOfPoints() { - int sum = 0; - - for (int i = 0; i < width; i++) { - for (int j = 0; j < height; j++) { - if (canvas[i][j] == 1) { - sum++; - } - } - } - return sum; - } - - /** - * Get the affine transform representing the conversion from coordinates to indices. - * - * @return the transform. - */ - public AffineTransform2D getTransformCoordsToIndices() { return this.transformCoordsToIndices; } - /** * Clear the canvas of all content. */ @@ -254,21 +224,23 @@ public class ChaosCanvas { && point.getX1() >= minCoords.getX1() && point.getX1() <= maxCoords.getX1(); } + /** + * Get the width of the ChaosCanvas. + * + * @return the width of the ChaosCanvas. + */ public int getWidth() { return width; } - public int getHeight() { - return height; - } - /** - * Get the centre coordinates. + * Get the height of the ChaosCanvas. * - * @return the centre. + * @return the height of the ChaosCanvas. */ - public int[] getCentre() { - return new int[]{this.centreX, this.centreY}; + public int getHeight() { + return height; } + } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosGameDescription.java b/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosGameDescription.java index 6d9775b3a1556b1661eb53329538723f78f058c5..b7bf6bec92023cd033766f809b94942fbe9aa935 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosGameDescription.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/game/ChaosGameDescription.java @@ -6,7 +6,7 @@ import edu.ntnu.stud.chaosgame.model.transformations.Transform2D; import java.util.List; /** - * Description of the chaos game. + * Description of the chaos game, representing its mathematical properties. */ public class ChaosGameDescription { @@ -33,17 +33,21 @@ public class ChaosGameDescription { /** * Constructor for ChaosGameDescription. * - * @param minCoords Inputs a {@link Vector2D} vector of lower left coordinates for the chaos game. - * @param maxCoords Inputs a {@link Vector2D} vector of upper right coordinates for the chaos game. - * @param transforms Inputs a list of transformations {@link edu.ntnu.stud.chaosgame.model.transformations.AffineTransform2D} used in the chaos game. + * @param minCoords Inputs a {@link Vector2D} + * vector of lower left coordinates for the chaos game. + * @param maxCoords Inputs a {@link Vector2D} + * vector of upper right coordinates for the chaos game. + * @param transforms Inputs a list of transformations + * {@link edu.ntnu.stud.chaosgame.model.transformations.AffineTransform2D} + * used in the chaos game. */ public ChaosGameDescription(Vector2D minCoords, Vector2D maxCoords, List<Transform2D> transforms) { - if (minCoords == null || maxCoords == null || transforms == null){ - throw new IllegalArgumentException("Coordinates and transforms must not be null."); - } - this.minCoords = minCoords; - this.maxCoords = maxCoords; - this.transforms = transforms; + if (minCoords == null || maxCoords == null || transforms == null){ + throw new IllegalArgumentException("Coordinates and transforms must not be null."); + } + this.minCoords = minCoords; + this.maxCoords = maxCoords; + this.transforms = transforms; } /** @@ -66,32 +70,32 @@ public class ChaosGameDescription { this.transforms = transforms; } - /** - * Getter method for transforms - * - * @return Returns a list of transforms in the chaos game. - */ - public List<Transform2D> getTransforms(){ - return transforms; - } + /** + * Getter method for transforms + * + * @return Returns a list of transforms in the chaos game. + */ + public List<Transform2D> getTransforms(){ + return transforms; + } - /** - * Getter method for minimum coordinates. - * - * @return Returns a Vector2D containing the minimum coordinates. - */ - public Vector2D getMinCoords(){ - return minCoords; - } + /** + * Getter method for minimum coordinates. + * + * @return Returns a Vector2D containing the minimum coordinates. + */ + public Vector2D getMinCoords(){ + return minCoords; + } - /** - * Getter method for maximum coordinates. - * - * @return Returns a Vector2D containing the maximum coordinates. - */ - public Vector2D getMaxCoords(){ - return maxCoords; - } + /** + * Getter method for maximum coordinates. + * + * @return Returns a Vector2D containing the maximum coordinates. + */ + public Vector2D getMaxCoords(){ + return maxCoords; + } /** * Getter method for the name of the chaos game. @@ -101,6 +105,4 @@ public class ChaosGameDescription { public String getName() { return name; } - - } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/generators/ChaosGameDescriptionFactory.java b/src/main/java/edu/ntnu/stud/chaosgame/model/generators/ChaosGameDescriptionFactory.java index 0763e2cf0c12ee94182aed8abafd22ca01176daf..889d4f7d6d606a97e8e01129b259ea1af83d9451 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/generators/ChaosGameDescriptionFactory.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/generators/ChaosGameDescriptionFactory.java @@ -25,25 +25,22 @@ public class ChaosGameDescriptionFactory { private final ArrayList<ChaosGameDescription> descriptions; /** - * Generate ChaosGameDescription-instances based on data in txt-files found in the directory. - * The naming of the txt-files must follow the pattern "desc_X.txt" where X is an integer - * incremented by 1 for each file in the directory. - * - * + * The file handler for reading the files. */ + private final ChaosGameFileHandler fileHandler; - - private ChaosGameFileHandler fileHandler; - - - - + /** + * Constructor for the class. + */ public ChaosGameDescriptionFactory() { this.descriptions = new ArrayList<>(); this.fileHandler = new ChaosGameFileHandler(); loadDescriptions(); } + /** + * Load the descriptions from the directory. + */ private void loadDescriptions() { Path dir = Paths.get("src/main/resources/descriptions/saved_descriptions/"); try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt")) { diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/AffineTransform2D.java b/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/AffineTransform2D.java index cc38da56f7eaf96f01429c9c7cfc245e510987a8..ba41081568c3ca945b67b8ea85162ec8aa74cbbf 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/AffineTransform2D.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/AffineTransform2D.java @@ -11,53 +11,52 @@ import edu.ntnu.stud.chaosgame.controller.utility.ChaosGameFileHandler; public class AffineTransform2D extends Transform2D { - /** - * The matrix{@link Matrix2x2} which performs the matrix-multiplication part of the affine transformation. - */ - private Matrix2x2 matrix; - - - - /** - * The vector{@link Vector2D} which is added as part of the affine transformation. - */ - private Vector2D vector; - - /** - * Create a type of affine transformation. - * @param inputMatrix A matrix {@link Matrix2x2} which defines the matrix-multiplication part of the affine transformation. - * @param inputVector A vector {@link Vector2D} which defines the vector-addition part of the affine transformation. - */ - public AffineTransform2D(Matrix2x2 inputMatrix, Vector2D inputVector){ - if (inputMatrix == null){ - throw new IllegalArgumentException("Input matrix should not be null"); - } else if (inputVector == null) { - throw new IllegalArgumentException("Input vector should not be null"); - } - this.matrix = inputMatrix; - this.vector = inputVector; + /** + * The matrix{@link Matrix2x2} which performs the matrix-multiplication part of the affine transformation. + */ + private Matrix2x2 matrix; + + /** + * The vector{@link Vector2D} which is added as part of the affine transformation. + */ + private Vector2D vector; + + /** + * Create a type of affine transformation. + * @param inputMatrix A matrix {@link Matrix2x2} which defines the matrix-multiplication part of the affine transformation. + * @param inputVector A vector {@link Vector2D} which defines the vector-addition part of the affine transformation. + */ + public AffineTransform2D(Matrix2x2 inputMatrix, Vector2D inputVector){ + if (inputMatrix == null){ + throw new IllegalArgumentException("Input matrix should not be null"); + } else if (inputVector == null) { + throw new IllegalArgumentException("Input vector should not be null"); } - - /** - * Multiplies the matrix {@link Matrix2x2} matrix by the vector {@link Vector2D} vector and adds the vector {@link Vector2D} point. - * @param point The vector {@link Vector2D} which transformations are performed on. - * @return A new vector {@link Vector2D} which represents a point on a plane. The point represents a new step in the creation of fractal. - */ - public Vector2D transform(Vector2D point){ - if (point == null){ - throw new IllegalArgumentException("Input should not be null"); - } - - return matrix.multiply(point).add(vector); + this.matrix = inputMatrix; + this.vector = inputVector; + } + + /** + * Multiplies the matrix {@link Matrix2x2} matrix by the vector {@link Vector2D} vector and adds the vector {@link Vector2D} point. + * + * @param point The vector {@link Vector2D} which transformations are performed on. + * @return A new vector {@link Vector2D} which represents a point on a plane. The point represents a new step in the creation of fractal. + */ + public Vector2D transform(Vector2D point){ + if (point == null){ + throw new IllegalArgumentException("Input should not be null"); } - /** - * Getter method to use with {@link ChaosGameFileHandler} - * @return The matrix for the transformation - */ - public Matrix2x2 getMatrix(){return this.matrix;} + return matrix.multiply(point).add(vector); + } - public Vector2D getVector() { - return vector; - } + /** + * Getter method to use with {@link ChaosGameFileHandler} + * @return The matrix for the transformation + */ + public Matrix2x2 getMatrix(){return this.matrix;} + + public Vector2D getVector() { + return vector; + } } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/JuliaTransform.java b/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/JuliaTransform.java index 3dc29966e8afff1fa98e627a155d43abc4d8bb02..7767115e5210574b1821103e7dfea4ba188711f4 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/JuliaTransform.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/JuliaTransform.java @@ -6,67 +6,65 @@ import edu.ntnu.stud.chaosgame.controller.utility.ChaosGameFileHandler; public class JuliaTransform extends Transform2D { - /** - * The complex number represented through Complex {@link Complex} which - * is added or subtracted in the Julia transformations. - */ - private Complex c1; + /** + * The complex number represented through Complex {@link Complex} which + * is added or subtracted in the Julia transformations. + */ + private Complex c1; - /** - * The sign used to determine if the Julia transformations adds or subtracts {@link Complex} c1. - */ - int sign; + /** + * The sign used to determine if the Julia transformations adds or subtracts {@link Complex} c1. + */ + int sign; - /** - * Constructs a JuliaTransform object defined by the input. - * @param point The complex number {@link Complex} which is added or subtracted in the transformation. - * @param sign An integer which determines if c1 is added or subtracted in the transformation. - */ - public JuliaTransform(Complex point,int sign) { - this.c1 = point; - this.sign = sign; - } + /** + * Constructs a JuliaTransform object defined by the input. + * + * @param point The complex number {@link Complex} which is added or subtracted in the transformation. + * @param sign An integer which determines if c1 is added or subtracted in the transformation. + */ + public JuliaTransform(Complex point,int sign) { + this.c1 = point; + this.sign = sign; + } - /** - * Performs a Julia-transformation on a point defined by the vector point. - * The transformation will add or subtract c1 relative to point. - * This depends on the sign of the integer sign. - * Then the method performs the sqrt method from Complex {@link Complex}. - * @param point The vector {@link Vector2D} which transformations are performed on. - * @return The transformed point, represented by a vector {@link Vector2D} - */ - public Vector2D transform(Vector2D point){ - if (point == null) { - throw new IllegalArgumentException("Point should not be null"); - } - Vector2D temp1; + /** + * Performs a Julia-transformation on a point defined by the vector point. + * The transformation will add or subtract c1 relative to point. + * This depends on the sign of the integer sign. + * Then the method performs the sqrt method from Complex {@link Complex}. + * + * @param point The vector {@link Vector2D} which transformations are performed on. + * @return The transformed point, represented by a vector {@link Vector2D} + */ + public Vector2D transform(Vector2D point){ + if (point == null) { + throw new IllegalArgumentException("Point should not be null"); + } + Vector2D temp1; - if (sign > 0){ + if (sign > 0){ + temp1 = point.subtract(c1); + return new Complex(temp1.getX0(), temp1.getX1()).sqrt(); + } + else if (sign < 0){ temp1 = point.subtract(c1); - return new Complex(temp1.getX0(), temp1.getX1()).sqrt(); - } - else if (sign < 0){ - temp1 = point.subtract(c1); - Complex sqrtResult = new Complex(temp1.getX0(), temp1.getX1()).sqrt(); - sqrtResult = new Complex(-sqrtResult.getX0(),-sqrtResult.getX1()); - return sqrtResult; - - } else { - // Maybe replace with logger based on SolarLint - System.out.println("Sign is zero. No transformation performed"); - return new Complex(point.getX0(),point.getX1()); - } - //Complex sqrtResult = new Complex(temp1.getX0(), temp1.getX1()).sqrt(); - //System.out.println("Input to sqrt: " + temp1.getX0() + temp1.getX1() + ", Output: " + sqrtResult.getX0() + sqrtResult.getX1()); // Debug line + Complex sqrtResult = new Complex(temp1.getX0(), temp1.getX1()).sqrt(); + sqrtResult = new Complex(-sqrtResult.getX0(),-sqrtResult.getX1()); + return sqrtResult; + } else { + // Maybe replace with logger based on SolarLint + System.out.println("Sign is zero. No transformation performed"); // TODO: Improve + return new Complex(point.getX0(),point.getX1()); } - /** - * Getter method to use with {@link ChaosGameFileHandler} - * @return The complex number used in the transformation. - */ - public Complex getC1(){return this.c1;} - + } + /** + * Getter method to use with {@link ChaosGameFileHandler} + * @return The complex number used in the transformation. + */ + public Complex getC1(){return this.c1;} } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/Transform2D.java b/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/Transform2D.java index d3bf3caa9bcbe99eda96be21f0e840d5b28ea131..a011ae0258d85131f07edd1fde074c4dc71927dd 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/Transform2D.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/model/transformations/Transform2D.java @@ -6,10 +6,11 @@ import edu.ntnu.stud.chaosgame.model.data.Vector2D; * Abstract class representing transformations in a 2D-plane. */ public abstract class Transform2D { - /** - * Abstract method defining transformation in a 2D-plane. - * @param point The vector {@link Vector2D} which transformations are performed on. - * @return - */ - public abstract Vector2D transform(Vector2D point); + /** + * Abstract method defining transformation in a 2D-plane. + * + * @param point The vector {@link Vector2D} which transformations are performed on. + * @return + */ + public abstract Vector2D transform(Vector2D point); } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGui.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGui.java index 499266a7f654359b876c92ce05aba0cad3d78f98..712fe29ea5347cfd375c05d8e27395cdfbd2d0bf 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGui.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGui.java @@ -36,12 +36,12 @@ public class ChaosGameGui implements ChaosGameObserver { /** * The primary stage for the GUI. */ - private Stage primaryStage; + private final Stage primaryStage; /** * The aspect ratio of the GUI. */ - private double aspectRatio; + private final double aspectRatio; /** * The canvas for this GUI. @@ -248,8 +248,7 @@ public class ChaosGameGui implements ChaosGameObserver { this.chaosCanvas = new ChaosCanvas(1000, 1000, this.description.getMinCoords(), this.description.getMaxCoords()); game = new ChaosGame(this.description, chaosCanvas); - //controller.startGame(); // Start the game after it's created -} + } /** * Initialize components related to the image view and zoom function. */ @@ -259,12 +258,10 @@ public class ChaosGameGui implements ChaosGameObserver { width = 1000; height = 1000; this.canvas = new Canvas(width, height); - //this.imageView.setImage(canvas.snapshot(null, null)); canvas.widthProperty().bind(imageView.fitWidthProperty()); canvas.heightProperty().bind(imageView.fitHeightProperty()); - this.clearImageView(); } @@ -297,7 +294,8 @@ public class ChaosGameGui implements ChaosGameObserver { } /** - * Initialize the side menu. + * Initialize the side menu for the GUI, including all its buttons and other + * components. */ private void initializeSideMenu() { @@ -515,43 +513,14 @@ public class ChaosGameGui implements ChaosGameObserver { } /** - * Initialize the color button handler. - */ - private void initializeColorButtonHandler() { - this.colorCheckBox.setOnAction(event -> { - controller.game.setUseColor(colorCheckBox.isSelected()); - this.clearImageView(); - this.chaosCanvas.clearCanvas(); - }); - } - - /** - * Get the chaos canvas of this GUI view. + * Get the image view of this GUI. * - * @return the canvas. + * @return the image view. */ - public ChaosCanvas getChaosCanvas() { - return this.chaosCanvas; - } - - 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 canvas and set a new zoom factor for the image view based on the ratio * between the old and new canvas heights. @@ -560,25 +529,9 @@ public class ChaosGameGui implements ChaosGameObserver { */ @Override public void updateCanvas(ChaosCanvas canvas) { - float zoomRatio = (float) this.chaosCanvas.getHeight() / canvas.getHeight(); - //this.imageView.fixedZoom(zoomRatio); // Set new zoom factor. this.chaosCanvas = canvas; } - /** - * 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); - controller.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. @@ -589,14 +542,30 @@ public class ChaosGameGui implements ChaosGameObserver { public void updateGame(ChaosGame game) { controller.drawChaosGame(); } + + /** + * Get the step count text field for this GUI. + * + * @return the step count text field. + */ public TextField getStepCountTextField() { return this.stepCountTextField; } + + /** + * Get the iteration limit text field for this GUI. + * + * @return the iteration limit text field. + */ public TextField getIterationLimitTextField(){ return this.iterationLimitTextField; } - + /** + * Get the color check box for this GUI. + * + * @return the color check box. + */ public CheckBox getColorCheckBox() { return this.colorCheckBox; } @@ -610,32 +579,63 @@ public class ChaosGameGui implements ChaosGameObserver { return this.canvas; } - + /** + * Get the start button for this GUI. + * + * @return the start button. + */ public Button getStartButton() { return this.startButton; } + + /** + * Get the stop button for this GUI. + * + * @return the stop button. + */ public Button getStopButton() { return this.stopButton; } + + /** + * Get the clear button for this GUI. + * + * @return the clear button. + */ public Button getClearButton() { return this.clearButton; } + /** + * Get the quit button for this GUI. + * + * @return the quit button. + */ public Button getQuitButton() { return this.quitButton; } - + /** + * Get the write fractal to file button. + * + * @return the write fractal to file button. + */ public Button getWriteToFileButton() { return this.writeFractalToFileButton; } + /** + * Get the primary stage for this GUI. + * + * @return the primary stage. + */ public Window getStage() { return this.primaryStage; } - - + /** + * Resize the canvas to fit the new dimensions of the scene. + */ private void resizeCanvas() { double newWidth = scene.getWidth() - sideMenu.getWidth(); double newHeight = scene.getHeight(); @@ -654,18 +654,39 @@ public class ChaosGameGui implements ChaosGameObserver { controller.drawChaosGame(); } + /** + * Get the load fractal from file button. + * + * @return the load fractal from file button. + */ public Button getLoadFractalFromFileButton() { return this.loadFractalFromFileButton; } + + /** + * Get the modify game button. + * + * @return the modify game button. + */ public Button getModifyGameButton(){ return this.modifyGameButton; } + + /** + * Get the description combo box. + * + * @return the description combo box. + */ public ComboBox getDescriptionComboBox(){ return this.descriptionComboBox; } + /** + * Get the save image button. + * + * @return the save image button. + */ public Button getSaveImageButton(){ return this.saveImageButton; } - } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java index 579b7c6c5b4eae029e13d84964a2865117843f3e..00c9fedc89ae5ee72ab5ed937ae571e3bf9cd3b5 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java @@ -17,7 +17,8 @@ public class ChaosGameImageView extends ImageView { private final ChaosGameGui controller; /** - * Affine initialised to the identity matrix. + * Affine initialised to the identity matrix, representing the current transformation + * as a result of zooming. */ private final Affine transform = new Affine(); @@ -53,22 +54,10 @@ public class ChaosGameImageView extends ImageView { this.setOnScroll(this::affineZoom); this.setOnMousePressed(this::mousePressed); this.setOnMouseDragged(this::mouseDragged); - //this.setOnMouseReleased(this::mouseReleased); this.getTransforms().add(transform); this.controller = controller; - //this.lastCentreX = (float) controller.getWidth() / 2; - //this.lastCentreY = (float) -controller.getHeight() / 2; - - //this.setStyle("-fx-background-color: white;"); } - /** - * Get the current zoom level. - * - * @return the integer representing the zoom level. - */ - public int getZoomLevel() {return this.zoomLevel; } - /** * Zooms the image view in or out based on the scroll event. * @@ -127,14 +116,11 @@ public class ChaosGameImageView extends ImageView { transform.setTy(transform.getTy() + deltaY); } - public int getWidth() { - return (int) this.getImage().getWidth(); - } - - public int getHeight() { - return (int) this.getImage().getHeight(); - } - + /** + * Gets the current transform. + * + * @return the current transform. + */ public Affine getTransform() {return this.transform; } } diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java index 9020e30d742ae1a020e60a27d0858a54e5b38b2c..46c8095e99f149e720392df0c6329f217361e41c 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java @@ -9,7 +9,6 @@ import edu.ntnu.stud.chaosgame.model.game.ChaosCanvas; */ public interface ChaosGameObserver { - /** * Update the ChaosCanvas. * diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/AbstractPopup.java b/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/AbstractPopup.java index a745967d9f3047e69d68e3e41762714fa34bffa6..94edf98ddc0b928b3cf2b24666aacca36abae395 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/AbstractPopup.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/AbstractPopup.java @@ -74,14 +74,14 @@ public abstract class AbstractPopup { * @param layout the layout of the popup. */ public AbstractPopup(VBox layout) { - this.popupModifyStage = new Stage(); - this.popupModifyStage.initModality(Modality.APPLICATION_MODAL); - this.popupModifyStage.setAlwaysOnTop(true); - this.layout = layout; - this.scene = new Scene(layout); - this.popupModifyStage.setScene(scene); - - initializeCommonElements(); + this.popupModifyStage = new Stage(); + this.popupModifyStage.initModality(Modality.APPLICATION_MODAL); + this.popupModifyStage.setAlwaysOnTop(true); + this.layout = layout; + this.scene = new Scene(layout); + this.popupModifyStage.setScene(scene); + + initializeCommonElements(); } /** @@ -129,13 +129,13 @@ public abstract class AbstractPopup { * @return the numeric text field. */ protected TextField createNumericTextField() { - TextField textField = new TextField(); - textField.textProperty().addListener((observable, oldValue, newValue) -> { - if (!newValue.matches("-?\\d*(\\.\\d*)?")) { // TODO: replace with Formatter - textField.setText(oldValue); - } - }); - return textField; + TextField textField = new TextField(); + textField.textProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue.matches("-?\\d*(\\.\\d*)?")) { // TODO: replace with Formatter + textField.setText(oldValue); + } + }); + return textField; } /** @@ -144,29 +144,10 @@ public abstract class AbstractPopup { * @return the layout of the popup. */ public static VBox initializeLayout() { - VBox layout = new VBox(); - layout.setPadding(new Insets(10)); - layout.setSpacing(10); - return layout; - } - - /** - * Get the layout of the popup. - * - * @return the layout of the popup. - */ - public VBox getLayout() { - return layout; - } - - /** - * Set the layout of the popup. - * - * @param layout the layout to set. - */ - public void setLayout(VBox layout) { - this.layout = layout; - this.scene.setRoot(layout); + VBox layout = new VBox(); + layout.setPadding(new Insets(10)); + layout.setSpacing(10); + return layout; } /** diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/Affine2DPopup.java b/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/Affine2DPopup.java index a43d47d893084a83d82e08d74456be28bec682ab..27ddbe8c27b5fd4271dafaa8df43c6d9cdbbdb02 100644 --- a/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/Affine2DPopup.java +++ b/src/main/java/edu/ntnu/stud/chaosgame/view/modificationpopups/Affine2DPopup.java @@ -18,12 +18,12 @@ public class Affine2DPopup extends AbstractPopup { /** * The text fields for the matrix, represented as a 2D array. */ - private TextField[][] matrixTextFields; + private final TextField[][] matrixTextFields; /** * The text fields for the vector, represented as a 1D array. */ - private TextField[] vectorTextFields; + private final TextField[] vectorTextFields; /** * Constructor for the Affine2DPopup. @@ -40,8 +40,8 @@ public class Affine2DPopup extends AbstractPopup { for (int i = 0; i < transformCount; i++) { for (int j = 0; j < 4; j++) { - matrixTextFields[i][j] = createNumericTextField(); - matrixTextFields[i][j].setPrefWidth(50); + matrixTextFields[i][j] = createNumericTextField(); + matrixTextFields[i][j].setPrefWidth(50); } vectorTextFields[2 * i] = createNumericTextField(); vectorTextFields[2 * i].setPrefWidth(50); diff --git a/src/main/resources/descriptions/saved_descriptions/Heighway-Dragon.txt b/src/main/resources/descriptions/saved_descriptions/Heighway-Dragon.txt index 4bc9f1dcbbb077ccf44d7f1fc3be9c1820f39f26..8a37171eced495f463c85c5589fb6d2fa8847d6f 100644 --- a/src/main/resources/descriptions/saved_descriptions/Heighway-Dragon.txt +++ b/src/main/resources/descriptions/saved_descriptions/Heighway-Dragon.txt @@ -2,5 +2,5 @@ Affine2D Heighway-Dragon -1.0, -1.0 1.5, 1.5 -0.6, -0.5, 0.5, 0.5, 0.0, 0.0 +0.8, -0.3, 0.5, 0.5, 0.0, 0.0 -0.5, -0.5, 0.6, -0.6, 1.0, 0.0 diff --git a/src/test/java/edu/ntnu/stud/chaosgame/transformations/AffineTransform2DTest.java b/src/test/java/edu/ntnu/stud/chaosgame/transformations/AffineTransform2DTest.java index eead22531db23ad5c286f81accced61427c72581..bbb9b8315e1d5d15536fab9b653426337e7e502c 100644 --- a/src/test/java/edu/ntnu/stud/chaosgame/transformations/AffineTransform2DTest.java +++ b/src/test/java/edu/ntnu/stud/chaosgame/transformations/AffineTransform2DTest.java @@ -9,56 +9,90 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; + +/** + * Test class for the AffineTransform2D class. + + */ class AffineTransform2DTest { + /** + * Matrix2x2 object for testing. + */ + Matrix2x2 matrix2x2; - Matrix2x2 matrix2x2; - Vector2D vector2D; - AffineTransform2D affineTransform2D; + /** + * Vector2D object for testing. + */ + Vector2D vector2D; - @BeforeEach - void setUp() { - matrix2x2 = new Matrix2x2(0.5,1,1,0.5); - vector2D = new Vector2D(3,1); - affineTransform2D = new AffineTransform2D(matrix2x2,vector2D); + /** + * AffineTransform2D object for testing. + */ + AffineTransform2D affineTransform2D; - } - @Nested - @DisplayName("Positive Affine Transform Tests") - class PositiveAffineTransform2DTest { - @Test - void transform() { - Vector2D point = new Vector2D(1, 1); - Vector2D expected = new Vector2D(4.5,2.5); - Vector2D actual = affineTransform2D.transform(point); - System.out.println(expected.getX0()); - System.out.println(expected.getX1()); - assertEquals(expected.getX0(), actual.getX0(), "The transform method should correctly transform the X0 component"); - assertEquals(expected.getX1(), actual.getX1(), "The transform method should correctly transform the X1 component"); + /** + * Set up the test environment by defining the fields. + */ + @BeforeEach + void setUp() { + matrix2x2 = new Matrix2x2(0.5,1,1,0.5); + vector2D = new Vector2D(3,1); + affineTransform2D = new AffineTransform2D(matrix2x2,vector2D); - } + } - @Test - void getMatrix() { - Matrix2x2 actual = affineTransform2D.getMatrix(); - assertEquals(matrix2x2, actual, "The getMatrix method should return the correct matrix"); - } + /** + * Nested class for testing the AffineTransform2D class. + */ + @Nested + @DisplayName("Positive Affine Transform Tests") + class PositiveAffineTransform2DTest { + @Test + void transform() { + Vector2D point = new Vector2D(1, 1); + Vector2D expected = new Vector2D(4.5,2.5); + Vector2D actual = affineTransform2D.transform(point); + System.out.println(expected.getX0()); + System.out.println(expected.getX1()); // TODO: remove + assertEquals(expected.getX0(), actual.getX0(), "The transform method should correctly transform the X0 component"); + assertEquals(expected.getX1(), actual.getX1(), "The transform method should correctly transform the X1 component"); - @Test - void getVector() { - Vector2D actual = affineTransform2D.getVector(); - assertEquals(vector2D, actual, "The getVector method should return the correct vector"); - } } - @Nested - @DisplayName("Negative Affine Transform Tests") - class NegativeAffineTransform2DTests{ - @Test - void transformWithNullInput() { - assertThrows(IllegalArgumentException.class, () -> affineTransform2D.transform(null), - "The transform method should throw IllegalArgumentException when the input is null"); - } + /** + * Test the getMatrix method. + */ + @Test + void getMatrix() { + Matrix2x2 actual = affineTransform2D.getMatrix(); + assertEquals(matrix2x2, actual, "The getMatrix method should return the correct matrix"); + } + /** + * Test the getVector method. + */ + @Test + void getVector() { + Vector2D actual = affineTransform2D.getVector(); + assertEquals(vector2D, actual, "The getVector method should return the correct vector"); } + } + + /** + * Nested class for negative testing of the AffineTransform2D class. + */ + @Nested + @DisplayName("Negative Affine Transform Tests") + class NegativeAffineTransform2DTests{ + + /** + * Test the constructor with null matrix input. + */ + @Test + void transformWithNullInput() { + assertThrows(IllegalArgumentException.class, () -> affineTransform2D.transform(null), + "The transform method should throw IllegalArgumentException when the input is null"); } + } +} diff --git a/src/test/java/edu/ntnu/stud/chaosgame/transformations/JuliaTransformTest.java b/src/test/java/edu/ntnu/stud/chaosgame/transformations/JuliaTransformTest.java index c5b118168c3d5563964493605a930a1287e1de7c..1009ac93bf0009b00d6c4400736982157d81135f 100644 --- a/src/test/java/edu/ntnu/stud/chaosgame/transformations/JuliaTransformTest.java +++ b/src/test/java/edu/ntnu/stud/chaosgame/transformations/JuliaTransformTest.java @@ -12,45 +12,80 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class JuliaTransformTest { + /** + * Complex object for testing. + */ + Complex complex; - Complex complex; - Vector2D vector2D; - JuliaTransform juliaTransform; + /** + * Vector2D object for testing. + */ + Vector2D vector2D; - @BeforeEach - void setUp() { + /** + * JuliaTransform object for testing. - complex = new Complex(0.3,0.6); - vector2D = new Vector2D(3,1); - juliaTransform = new JuliaTransform(complex,1); + */ + JuliaTransform juliaTransform; + + /** + * Set up the test environment by defining the fields. + */ + @BeforeEach + void setUp() { + + complex = new Complex(0.3,0.6); + vector2D = new Vector2D(3,1); + juliaTransform = new JuliaTransform(complex,1); + + } + + /** + * Nested class for testing the JuliaTransform class. + */ + @Nested + @DisplayName("Positive Tests for Julia Transformations") + class JuliaTransformPositiveTests{ + + /** + * Test the getComplex method by asserting that the method returns the correct complex number. + */ + @Test + void getC1(){ + Complex actual = juliaTransform.getC1(); + assertEquals(complex,actual,"The getC1 should return the correct complex number"); } - @Nested - @DisplayName("Positive Tests for Julia Transformations") - class JuliaTransformPositiveTests{ - @Test - void getC1(){ - Complex actual = juliaTransform.getC1(); - assertEquals(complex,actual,"The getC1 should return the correct complex number"); - - } - @Test - void transform(){ - Vector2D expected = new Vector2D(1.647,0.12138); - Vector2D actual = juliaTransform.transform(vector2D); - assertEquals(expected.getX0(),actual.getX0(),0.01); - assertEquals(expected.getX1(),actual.getX1(),0.01); - - } + + /** + * Test the getIterations method by asserting that the method + * returns the correct number of iterations. + */ + @Test + void transform(){ + Vector2D expected = new Vector2D(1.647,0.12138); + Vector2D actual = juliaTransform.transform(vector2D); + assertEquals(expected.getX0(),actual.getX0(),0.01); + assertEquals(expected.getX1(),actual.getX1(),0.01); + } - @Nested - @DisplayName("Negative Tests for Julia Transformations") - class JuliaTransformNegativeTests{ - @Test - void transformZeroSign(){ - JuliaTransform juliaTransformZeroSign = new JuliaTransform(complex,0); - assertEquals(vector2D.getX0(),juliaTransformZeroSign.transform(vector2D).getX0()); - assertEquals(vector2D.getX1(),juliaTransformZeroSign.transform(vector2D).getX1()); - } + } + + /** + * Nested class for testing the JuliaTransform class. + */ + @Nested + @DisplayName("Negative Tests for Julia Transformations") + class JuliaTransformNegativeTests{ + + /** + * Test the getComplex method by asserting that the method returns the correct complex number. + */ + @Test + void transformZeroSign(){ + JuliaTransform juliaTransformZeroSign = new JuliaTransform(complex,0); + assertEquals(vector2D.getX0(),juliaTransformZeroSign.transform(vector2D).getX0()); + assertEquals(vector2D.getX1(),juliaTransformZeroSign.transform(vector2D).getX1()); } + } } \ No newline at end of file