diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml index 3bab646322fd4a4653a993ecbc5d3fd14935eb71..5536a0d7400e3648af967956233c4c679e855002 100644 --- a/.idea/checkstyle-idea.xml +++ b/.idea/checkstyle-idea.xml @@ -2,7 +2,7 @@ <project version="4"> <component name="CheckStyle-IDEA" serialisationVersion="2"> <checkstyleVersion>10.13.0</checkstyleVersion> - <scanScope>JavaOnly</scanScope> + <scanScope>JavaOnlyWithTests</scanScope> <option name="thirdPartyClasspath" /> <option name="activeLocationIds" /> <option name="locations"> diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000000000000000000000000000000000..45f76e0205cae24a7ffb39876099e3d534ab39e9 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,org.mockito.MockitoAnnotations,openMocks" /> + </inspection_tool> + </profile> +</component> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index ba62b845c54f748f9ea0f0f7f9d34ef309002d6d..94a25f7f4cb416c083d265558da75d457237d671 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,10 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="VcsDirectoryMappings"> -<<<<<<< HEAD <mapping directory="$PROJECT_DIR$" vcs="Git" /> -======= - <mapping directory="" vcs="Git" /> ->>>>>>> origin/main </component> </project> \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5425b77710f09a22f07dbeb5d1f53e768242bf55..250218dcad301f8f9f114f237cb1409fd19eb9c5 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,26 @@ <artifactId>javafx-media</artifactId> <version>21.0.2</version> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>3.12.4</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-inline</artifactId> + <version>3.12.4</version> + <scope>test</scope> + </dependency> + <!-- Mockito JUnit Jupiter Dependency --> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <version>3.11.2</version> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/src/main/java/org/example/chaosgame/MainApp.java b/src/main/java/org/example/chaosgame/MainApp.java index e4c3ae764236b875119446a6909722ef9cbfd68a..631385a560d104b1cd67bf7c30d9c7b0f8bf09ac 100644 --- a/src/main/java/org/example/chaosgame/MainApp.java +++ b/src/main/java/org/example/chaosgame/MainApp.java @@ -24,13 +24,13 @@ public class MainApp extends Application { new PageController(mainPane, chaosGameController, exploreGameController); Scene scene = new Scene(mainPane, 1200, 800); - mainPane.prefWidthProperty().bind(scene.widthProperty()); - mainPane.prefHeightProperty().bind(scene.heightProperty()); - scene.getStylesheets().add(Objects.requireNonNull( - getClass().getResource("/global.css")).toExternalForm()); + scene.getStylesheets().add( + Objects.requireNonNull(getClass() + .getResource("/global.css")) + .toExternalForm()); - primaryStage.setMinWidth(800); - primaryStage.setMinHeight(600); + primaryStage.setMinWidth(1000); + primaryStage.setMinHeight(650); primaryStage.setScene(scene); primaryStage.setTitle("Chaos Game Canvas"); diff --git a/src/main/java/org/example/chaosgame/controller/ChaosGameController.java b/src/main/java/org/example/chaosgame/controller/ChaosGameController.java index ead31676fc9474c2d777bf4472b85d1fd12025b3..cb8678ba34d88366259e2af8b082804b159dec34 100644 --- a/src/main/java/org/example/chaosgame/controller/ChaosGameController.java +++ b/src/main/java/org/example/chaosgame/controller/ChaosGameController.java @@ -17,12 +17,10 @@ import org.example.chaosgame.controller.interfaces.Observer; import org.example.chaosgame.controller.interfaces.Subject; import org.example.chaosgame.model.chaos.ChaosGame; import org.example.chaosgame.model.chaos.ChaosGameDescription; -import org.example.chaosgame.model.chaos.ChaosGameDescriptionFactory; -import org.example.chaosgame.model.chaos.ChaosGameFileHandler; import org.example.chaosgame.model.chaos.ChaosGameType; import org.example.chaosgame.model.linalg.Complex; -import org.example.chaosgame.model.linalg.Vector2D; import org.example.chaosgame.model.linalg.Matrix2x2; +import org.example.chaosgame.model.linalg.Vector2D; import org.example.chaosgame.model.transformations.AffineTransform2D; import org.example.chaosgame.model.transformations.JuliaTransform; import org.example.chaosgame.model.transformations.Transform2D; @@ -105,26 +103,29 @@ public class ChaosGameController implements Observer, Subject, GameController { String input = stepsField.getText(); try { int steps = Integer.parseInt(input); - if (steps < 1 || steps > 10000000) { - throw new NumberFormatException(); - } - if (chaosGame.getDescription().getTransforms().getFirst() instanceof JuliaTransform && steps > 250000) { - AlertUtility.showErrorDialog("Invalid input", "Please enter a lower amount of steps for Julia transformations."); - return; + chaosGame.setSteps(steps); + if (chaosGame.getDescription().getTransforms().getFirst() + instanceof JuliaTransform && steps > 250000) { + throw new IllegalArgumentException( + "Please enter a lower amount of steps for Julia transformations."); } if (chaosGame.getTotalSteps() > Math.pow(10, 8)) { - AlertUtility.showErrorDialog("Invalid input", "The total number of steps is too high. Please reset the game."); - return; + throw new IllegalArgumentException( + "The total amount of steps is too high. Choose a lower amount."); } - chaosGame.setSteps(steps); chaosGame.addTotalSteps(steps); chaosGame.runSteps(); stepsField.getStyleClass().remove("text-field-invalid"); - } catch (NumberFormatException ex) { + + } catch (NumberFormatException e) { + stepsField.clear(); + stepsField.getStyleClass().add("text-field-invalid"); + AlertUtility.showErrorDialog("Invalid input", "Please enter a valid number."); + } catch (IllegalArgumentException ex) { stepsField.clear(); stepsField.getStyleClass().add("text-field-invalid"); AlertUtility.showErrorDialog( - "Invalid input", "Please enter a number between 1 - 10 000 000."); + "Invalid input", ex.getMessage()); } } @@ -146,46 +147,22 @@ public class ChaosGameController implements Observer, Subject, GameController { Vector2D max = new Vector2D(Double.parseDouble(coords.get(2)), Double.parseDouble(coords.get(3))); - if (validateCoordinates(min) && validateCoordinates(max)) { - updateChaosGame(new ChaosGameDescription( - min, max, - chaosGame.getDescription().getTransforms())); - } else { - AlertUtility.showErrorDialog("Invalid input", - "Please enter a double between -50 and 50."); - } + updateChaosGame(new ChaosGameDescription(min, max, + chaosGame.getDescription().getTransforms())); } catch (NumberFormatException e) { - AlertUtility.showErrorDialog("Invalid input", "Please enter a valid number."); } catch (IndexOutOfBoundsException e) { AlertUtility.showErrorDialog("Invalid input", "Please enter all coordinates."); + } catch (IllegalArgumentException e) { + AlertUtility.showErrorDialog("Invalid input", + e.getMessage()); } } } - /** - * Method for validating the coordinates. - * - * @param vector Vector2D with the coordinates - * @return boolean True if the coordinates are valid, false otherwise - */ - private boolean validateCoordinates(Vector2D vector) { - try { - System.out.println("parsing" + vector.getX() + " " + vector.getY()); - double x = vector.getX(); - double y = vector.getY(); - if (x < -50 || x > 50 || y < -50 || y > 50) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - return true; - } - /** * Method for opening a file with a chaos game description. * @@ -212,10 +189,12 @@ public class ChaosGameController implements Observer, Subject, GameController { } /** - * Method for creating a new fractal. + * Method for user creation of fractal. + * {@link org.example.chaosgame.view.components.CreateFractalDialog} returns either a + * {@link java.util.List}<{@link java.util.List}<{@link java.lang.String}>> + * or a {@link javafx.util.Pair}<{@link java.lang.String}, {@link java.lang.String}>. * * <p>Opens a dialog for the user to create a new fractal. - * */ public void createOwnFractal() { CreateFractalDialog dialog = new CreateFractalDialog(); @@ -226,7 +205,7 @@ public class ChaosGameController implements Observer, Subject, GameController { List<Transform2D> transforms = new ArrayList<>(); if (fractalData instanceof List) { List<List<String>> userInput = (List<List<String>>) fractalData; - for(List<String> input : userInput) { + for (List<String> input : userInput) { try { double a = Double.parseDouble(input.get(0)); double b = Double.parseDouble(input.get(1)); @@ -235,8 +214,8 @@ public class ChaosGameController implements Observer, Subject, GameController { double x = Double.parseDouble(input.get(4)); double y = Double.parseDouble(input.get(5)); - if (a < -5 || a > 5 || b < -5 || b > 5 || c < -5 || c > 5 || - d < -5 || d > 5 || x < -5 || x > 5 || y < -5 || y > 5) { + if (a < -5 || a > 5 || b < -5 || b > 5 || c < -5 || c > 5 + || d < -5 || d > 5 || x < -5 || x > 5 || y < -5 || y > 5) { throw new NumberFormatException(); } else { transforms.add(new AffineTransform2D(new Matrix2x2(a, b, c, d), new Vector2D(x, y))); @@ -249,7 +228,6 @@ public class ChaosGameController implements Observer, Subject, GameController { AlertUtility.showErrorDialog("Invalid input", "Please enter a valid number."); } } -// List<Transform2D> transforms = new ArrayList<>(transformations); } else if (fractalData instanceof Pair) { Pair<String, String> userInput = (Pair<String, String>) fractalData; try { // Check if the input is a valid number diff --git a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactory.java b/src/main/java/org/example/chaosgame/controller/ChaosGameDescriptionFactory.java similarity index 84% rename from src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactory.java rename to src/main/java/org/example/chaosgame/controller/ChaosGameDescriptionFactory.java index 166963ee5f073e835f347d780a82ba25e28c1e87..db3de3d27fb79488505c1f1d61df2ad70bffb01a 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactory.java +++ b/src/main/java/org/example/chaosgame/controller/ChaosGameDescriptionFactory.java @@ -1,6 +1,8 @@ -package org.example.chaosgame.model.chaos; +package org.example.chaosgame.controller; import java.util.List; +import org.example.chaosgame.model.chaos.ChaosGameDescription; +import org.example.chaosgame.model.chaos.ChaosGameType; import org.example.chaosgame.model.linalg.Complex; import org.example.chaosgame.model.linalg.Matrix2x2; import org.example.chaosgame.model.linalg.Vector2D; @@ -13,12 +15,16 @@ import org.example.chaosgame.model.transformations.JuliaTransform; public class ChaosGameDescriptionFactory { /** - * Returns a ChaosGameDescription object based on the description and complex number. + * Returns a ChaosGameDescription object based on the ChaosGameType-enum. * * @param type The description of the chaos game * @return A ChaosGameDescription object + * @throws IllegalArgumentException if the ChaosGameType is null */ - public static ChaosGameDescription get(ChaosGameType type) { + public static ChaosGameDescription get(ChaosGameType type) throws IllegalArgumentException { + if (type == null) { + throw new IllegalArgumentException("ChaosGameType cannot be null"); + } return switch (type) { case JULIA -> createJulia(); case BARNSLEY -> createBarnsley(); diff --git a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameFileHandler.java b/src/main/java/org/example/chaosgame/controller/ChaosGameFileHandler.java similarity index 92% rename from src/main/java/org/example/chaosgame/model/chaos/ChaosGameFileHandler.java rename to src/main/java/org/example/chaosgame/controller/ChaosGameFileHandler.java index e10510ef599e1a7042886c6188b9e242be01da8d..050be3f8ffd4d511b5a88037a999ac96028cce6c 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameFileHandler.java +++ b/src/main/java/org/example/chaosgame/controller/ChaosGameFileHandler.java @@ -1,4 +1,4 @@ -package org.example.chaosgame.model.chaos; +package org.example.chaosgame.controller; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -7,6 +7,7 @@ import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.example.chaosgame.model.chaos.ChaosGameDescription; import org.example.chaosgame.model.linalg.Complex; import org.example.chaosgame.model.linalg.Matrix2x2; import org.example.chaosgame.model.linalg.Vector2D; @@ -42,8 +43,6 @@ public class ChaosGameFileHandler { Vector2D maxCoords; try (BufferedReader reader = new BufferedReader(new FileReader(path))) { String typeOfTransformation = skipComments(reader.readLine()); - System.out.println("Parsing type of transformation: " + typeOfTransformation); - minCoords = parseVector(reader.readLine().trim()); maxCoords = parseVector(reader.readLine().trim()); @@ -132,7 +131,7 @@ public class ChaosGameFileHandler { * @return the transformation * @throws IllegalArgumentException if the type of transformation is unknown */ - private Transform2D selectTransformation(String typeOfTransformation, String line) + public Transform2D selectTransformation(String typeOfTransformation, String line) throws IllegalArgumentException { return switch (typeOfTransformation) { case "Affine2D" -> parseAffine(line); @@ -148,7 +147,7 @@ public class ChaosGameFileHandler { * @param line a line of text * @return the first part of the line */ - private String skipComments(String line) { + public String skipComments(String line) { String[] parts = line.split("#"); return parts[0].trim(); } @@ -159,9 +158,8 @@ public class ChaosGameFileHandler { * @param line a line of text * @return the vector */ - private Vector2D parseVector(String line) { + public Vector2D parseVector(String line) { String numbers = skipComments(line); - System.out.println("Parsing vector: " + numbers); String[] vectorParts = numbers.split(","); double x = Double.parseDouble(vectorParts[0].trim()); double y = Double.parseDouble(vectorParts[1].trim()); @@ -174,9 +172,8 @@ public class ChaosGameFileHandler { * @param line a line of text * @return the transformation */ - private Transform2D parseAffine(String line) { + public Transform2D parseAffine(String line) { String numbers = skipComments(line); - System.out.println("Parsing transform: " + numbers); String[] transformParts = numbers.split(","); double a = Double.parseDouble(transformParts[0].trim()); double b = Double.parseDouble(transformParts[1].trim()); @@ -193,9 +190,8 @@ public class ChaosGameFileHandler { * @param line a line of text * @return the transformation */ - private Transform2D parseJulia(String line) { + public Transform2D parseJulia(String line) { String numbers = skipComments(line); - System.out.println("Parsing transform: " + numbers); String[] parts = numbers.split(","); double r = Double.parseDouble(parts[0].trim()); double i = Double.parseDouble(parts[1].trim()); diff --git a/src/main/java/org/example/chaosgame/controller/ExploreGameController.java b/src/main/java/org/example/chaosgame/controller/ExploreGameController.java index 90a1ba0998309b7cc4be8abf2980f6b85b7ea080..b6ae5e8cfac2ea62c8c1e05d13df1bce44c5b727 100644 --- a/src/main/java/org/example/chaosgame/controller/ExploreGameController.java +++ b/src/main/java/org/example/chaosgame/controller/ExploreGameController.java @@ -40,6 +40,9 @@ public class ExploreGameController implements Observer, Subject, GameController private static final int WIDTH = 1200; private static final int HEIGHT = 800; + private Vector2D minCoords = new Vector2D(-1.6, -1); + private Vector2D maxCoords = new Vector2D(1.6, 1); + /** * Constructor for ExploreGameController. * Initializes the ExploreGame and ExplorePage. @@ -47,9 +50,9 @@ public class ExploreGameController implements Observer, Subject, GameController public ExploreGameController() { ExploreJulia exploreJulia = new ExploreJulia(new Complex(-0.835, 0.2321)); this.trans = List.of(exploreJulia); - this.description = new ChaosGameDescription( - new Vector2D(-1.6, -1), - new Vector2D(1.6, 1), trans); + this.description = new ChaosGameDescription( + minCoords, + maxCoords, trans); this.exploreGame = new ExploreGame(description, WIDTH, HEIGHT); this.chaosCanvas = exploreGame.getCanvas(); this.pageObservers = new ArrayList<>(); @@ -101,10 +104,9 @@ public class ExploreGameController implements Observer, Subject, GameController canvas.getHeight() - event.getY()).subtract(dragStartTemp); Vector2D fractalRange = description.getMaxCoords().subtract(description.getMinCoords()); Vector2D adjustedDragDistance = dragDistance.multiply(fractalRange) - .divide(new Vector2D(canvas.getWidth(), canvas.getHeight())); - Vector2D newMinCoords = description.getMinCoords().subtract(adjustedDragDistance); - Vector2D newMaxCoords = description.getMaxCoords().subtract(adjustedDragDistance); - description = new ChaosGameDescription(newMinCoords, newMaxCoords, trans); + .divide(new Vector2D(canvas.getWidth(), canvas.getHeight())); + minCoords = description.getMinCoords().subtract(adjustedDragDistance); + maxCoords = description.getMaxCoords().subtract(adjustedDragDistance); updateExplorePage(); } @@ -119,12 +121,11 @@ public class ExploreGameController implements Observer, Subject, GameController cumulativeScaleFactor *= scaleFactor; Vector2D canvasCenter = chaosCanvas.transformIndicesToCoords( chaosCanvas.getWidth() / 2, chaosCanvas.getHeight() / 2); - Vector2D newMinCoords = canvasCenter.subtract( - canvasCenter.subtract(description.getMinCoords()).scale(scaleFactor)); - Vector2D newMaxCoords = canvasCenter.add( - description.getMaxCoords().subtract(canvasCenter).scale(scaleFactor)); + minCoords = canvasCenter.subtract(canvasCenter.subtract( + description.getMinCoords()).scale(scaleFactor)); + maxCoords = canvasCenter.add(description.getMaxCoords() + .subtract(canvasCenter).scale(scaleFactor)); - description = new ChaosGameDescription(newMinCoords, newMaxCoords, trans); updateExplorePage(); } @@ -166,29 +167,32 @@ public class ExploreGameController implements Observer, Subject, GameController canvas.setTranslateY(setTranslateY); Vector2D canvasCenter = chaosCanvas.transformIndicesToCoords((int) mouseX, (int) mouseY); - Vector2D newMinCoords = canvasCenter.subtract( - canvasCenter.subtract(description.getMinCoords()).scale(scaleFactor)); - Vector2D newMaxCoords = canvasCenter.add( - description.getMaxCoords().subtract(canvasCenter).scale(scaleFactor)); - - description = new ChaosGameDescription(newMinCoords, newMaxCoords, trans); + minCoords = canvasCenter.subtract(canvasCenter.subtract( + description.getMinCoords()).scale(scaleFactor)); + maxCoords = canvasCenter.add(description.getMaxCoords() + .subtract(canvasCenter).scale(scaleFactor)); updateExplorePage(); } + private void updateExplorePage() { - exploreGame.removeObserver(this); - exploreGame = new ExploreGame(description, (int) canvas.getWidth(), (int) canvas.getHeight()); - exploreGame.registerObserver(this); + this.description.setMinCoords(minCoords); + this.description.setMaxCoords(maxCoords); + this.description.setTransforms(trans); + this.exploreGame = new ExploreGame( + description, (int) canvas.getWidth(), (int) canvas.getHeight()); + this.exploreGame.registerObserver(this); this.chaosCanvas = exploreGame.getCanvas(); exploreGame.exploreFractals(); - explorePage.updateCanvas(this.chaosCanvas); + explorePage.updateCanvas(exploreGame.getCanvas()); - canvas.setTranslateX(0); - canvas.setTranslateY(0); - canvas.setScaleY(1); - canvas.setScaleX(1); + this.canvas.setTranslateX(0); + this.canvas.setTranslateY(0); + this.canvas.setScaleX(1); + this.canvas.setScaleY(1); } + public void homeButtonClicked() { notifyObservers(); } @@ -213,11 +217,12 @@ public class ExploreGameController implements Observer, Subject, GameController * Resets the image to the default position and scale. */ public void resetImage() { - Vector2D newMinCoords = new Vector2D(-1.6, -1); - Vector2D newMaxCoords = new Vector2D(1.6, 1); + minCoords = new Vector2D(-1.6, -1); + maxCoords = new Vector2D(1.6, 1); description = new ChaosGameDescription( - newMinCoords, - newMaxCoords, trans); + minCoords, + maxCoords, trans); + cumulativeScaleFactor = 1; updateExplorePage(); } @@ -250,7 +255,6 @@ public class ExploreGameController implements Observer, Subject, GameController ? value : exploreTransform.getComplex().getY(); trans = List.of(new ExploreJulia(new Complex(realPart, imaginaryPart))); - description.setTransforms(trans); updateExplorePage(); } diff --git a/src/main/java/org/example/chaosgame/model/chaos/ChaosCanvas.java b/src/main/java/org/example/chaosgame/model/chaos/ChaosCanvas.java index 8ec988181845edd96b9cc2dd6ee804e86ed3b172..ed700b4911756bd4d62d8706bedc52679807e21e 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ChaosCanvas.java +++ b/src/main/java/org/example/chaosgame/model/chaos/ChaosCanvas.java @@ -13,7 +13,6 @@ import org.example.chaosgame.model.transformations.AffineTransform2D; * The canvas also has an Affine transformation that maps coordinates, {@link Vector2D}, * to indices in the canvas array. This is used to map points to pixels in the canvas. */ - public class ChaosCanvas { private int width; private int height; @@ -89,6 +88,11 @@ public class ChaosCanvas { this.height = height; } + + public AffineTransform2D getTransformCoordsToIndices() { + return transformCoordsToIndices; + } + /** * Calculates the Affine transformation that maps coordinates to indices in the canvas array. * The transformation is calculated based on the width, height, minimum and maximum coordinates. @@ -105,6 +109,7 @@ public class ChaosCanvas { ((height - 1.0) * maxCoords.getY()) / (maxCoords.getY() - minCoords.getY()), ((width - 1.0) * minCoords.getX()) / (minCoords.getX() - maxCoords.getX()) )); + } /** @@ -166,4 +171,23 @@ public class ChaosCanvas { } } } + + + /** + * Returns the pixel value at the given point. + * If the point is outside the canvas, the method returns 0. + * + * @param point The point to get the pixel value at + * + * @return The pixel value at the given point + */ + public double getPixel(Vector2D point) { + Vector2D indices = transformCoordsToIndices.transform(point); + int x = (int) indices.getX(); + int y = (int) indices.getY(); + if (x >= 0 && x < height && y >= 0 && y < width) { + return canvas[x][y]; + } + return 0.0; + } } diff --git a/src/main/java/org/example/chaosgame/model/chaos/ChaosGame.java b/src/main/java/org/example/chaosgame/model/chaos/ChaosGame.java index 19a06e0757301ce85f84e8d525b225eb8a04cf6a..6ee43e34f845e10696777fb0da4fe27442048edc 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ChaosGame.java +++ b/src/main/java/org/example/chaosgame/model/chaos/ChaosGame.java @@ -79,7 +79,19 @@ public class ChaosGame implements Subject { return totalSteps; } - public void setSteps(int steps) { + /** + * Method for setting the number of steps to run. + * + * @param steps Number of steps to run + * @throws IllegalArgumentException If steps is less than 0 or greater than 1000000 + */ + public void setSteps(int steps) throws IllegalArgumentException { + if (steps < 0) { + throw new IllegalArgumentException("Steps must be a positive number"); + } + if (steps > 1000000) { + throw new IllegalArgumentException("Steps must be less than 1000000"); + } this.steps = steps; } @@ -91,8 +103,13 @@ public class ChaosGame implements Subject { * Method for setting the chaos game description. * * @param newDescription New description of the chaos game + * @throws IllegalArgumentException If newDescription is null */ - public void setChaosGameDescription(ChaosGameDescription newDescription) { + public void setChaosGameDescription(ChaosGameDescription newDescription) + throws IllegalArgumentException { + if (newDescription == null) { + throw new IllegalArgumentException("Description cannot be null"); + } this.description = newDescription; resetTotalSteps(); setChaosCanvas(description.getMinCoords(), description.getMaxCoords()); @@ -112,7 +129,16 @@ public class ChaosGame implements Subject { this.canvas.setTransformCoordsToIndices(); } - public void addTotalSteps(int newSteps) { + /** + * Method for adding steps to the total number of steps. + * + * @param newSteps Number of steps to add + * @throws IllegalArgumentException If newSteps is less than 0 + */ + public void addTotalSteps(int newSteps) throws IllegalArgumentException { + if (newSteps < 0) { + throw new IllegalArgumentException("Steps must be a positive number"); + } this.totalSteps += newSteps; } @@ -122,8 +148,9 @@ public class ChaosGame implements Subject { /** - * Method for running the chaos game. Randomly selects a transformation - * from the description and applies it to the current point. + * Method for running the chaos game. + * Selects which runSteps method to use based if it has a list of probabilities. + * Notifies observers after running the steps. */ public void runSteps() { if (description.getProbabilities() != null) { @@ -134,6 +161,10 @@ public class ChaosGame implements Subject { notifyObservers(); } + /** + * Method for running the chaos game. Randomly selects a transformation + * from the description and applies it to the current point. + */ private void runStepsUniform(int steps) { for (int i = 0; i < steps; i++) { int transformIndex = random.nextInt(description.getTransforms().size()); diff --git a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescription.java b/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescription.java index 7b92582cc4624895b789b626da1e074b04dba77b..8c7177f940833f147f256ae1eea7b859cd1983a2 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescription.java +++ b/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescription.java @@ -1,6 +1,7 @@ package org.example.chaosgame.model.chaos; import java.util.List; +import java.util.Objects; import org.example.chaosgame.model.linalg.Vector2D; import org.example.chaosgame.model.transformations.Transform2D; @@ -28,6 +29,8 @@ public class ChaosGameDescription { */ public ChaosGameDescription(Vector2D minCoords, Vector2D maxCoords, List<Transform2D> transforms) { + validateCoordinates(minCoords, maxCoords); + validateTransforms(transforms); this.minCoords = minCoords; this.maxCoords = maxCoords; this.transforms = transforms; @@ -46,13 +49,55 @@ public class ChaosGameDescription { * @param probabilities List of probabilities for the transformations */ public ChaosGameDescription(Vector2D minCoords, Vector2D maxCoords, - List<Transform2D> transforms, List<Integer> probabilities) { + List<Transform2D> transforms, List<Integer> probabilities) + throws IllegalArgumentException { + validateCoordinates(minCoords, maxCoords); + validateTransforms(transforms); + if (probabilities.size() != transforms.size()) { + throw new IllegalArgumentException("Probabilities must match the number of transformations"); + } this.minCoords = minCoords; this.maxCoords = maxCoords; this.transforms = transforms; this.probabilities = probabilities; } + /** + * Method for validating the coordinates. + * + * @param minCoords Minimum coordinates of the game area + * + * @param maxCoords Maximum coordinates of the game area + */ + private void validateCoordinates(Vector2D minCoords, Vector2D maxCoords) { + if (minCoords.getX() < -50 || minCoords.getY() < -50 + || minCoords.getX() > 50 || minCoords.getY() > 50 + || maxCoords.getX() > 50 || maxCoords.getY() > 50 + || maxCoords.getX() < -50 || maxCoords.getY() < -50) { + throw new IllegalArgumentException("Coordinates must be between -50 and 50"); + } else if (minCoords.getX() > maxCoords.getX() || minCoords.getY() > maxCoords.getY()) { + throw new IllegalArgumentException( + "Minimum coordinates must be less than maximum coordinates"); + } + if (minCoords.equals(maxCoords)) { + throw new IllegalArgumentException("Minimum and maximum coordinates cannot be the same"); + } + } + + /** + * Method for validating the transformations. + * + * @param transforms List of transformations to apply to the points + */ + private void validateTransforms(List<Transform2D> transforms) { + if (transforms == null) { + throw new IllegalArgumentException("Transformations cannot be null"); + } + if (transforms.size() > 4 || transforms.isEmpty()) { + throw new IllegalArgumentException("Number of transformations must be between 1 and 4"); + } + } + public Vector2D getMinCoords() { return minCoords; } @@ -70,6 +115,7 @@ public class ChaosGameDescription { } public void setTransforms(List<Transform2D> transforms) { + validateTransforms(transforms); this.transforms = transforms; } @@ -80,4 +126,24 @@ public class ChaosGameDescription { public void setMaxCoords(Vector2D maxCoords) { this.maxCoords = maxCoords; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ChaosGameDescription that = (ChaosGameDescription) o; + return Objects.equals(minCoords, that.minCoords) + && Objects.equals(maxCoords, that.maxCoords) + && Objects.equals(transforms, that.transforms) + && Objects.equals(probabilities, that.probabilities); + } + + @Override + public int hashCode() { + return Objects.hash(minCoords, maxCoords, transforms, probabilities); + } } diff --git a/src/main/java/org/example/chaosgame/model/chaos/ExploreGame.java b/src/main/java/org/example/chaosgame/model/chaos/ExploreGame.java index 506e325997f5858ad48c1b3eb6b750749a5abc7b..16aa2be8aad9ec1f1d27b468fd3e858ae91be351 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ExploreGame.java +++ b/src/main/java/org/example/chaosgame/model/chaos/ExploreGame.java @@ -7,6 +7,8 @@ import org.example.chaosgame.controller.interfaces.Observer; import org.example.chaosgame.controller.interfaces.Subject; import org.example.chaosgame.model.linalg.Vector2D; + + /** * Class for exploring julia sets. */ @@ -28,11 +30,28 @@ public class ExploreGame implements Subject { */ public ExploreGame(ChaosGameDescription description, int width, int height) { this.description = description; - this.canvas = new ChaosCanvas(width, height, - description.getMinCoords(), description.getMaxCoords()); + this.canvas = new ChaosCanvas( + width, height, + description.getMinCoords(), + description.getMaxCoords()); this.gameObservers = new ArrayList<>(); } + /** + * Method for setting the chaos game. + * + * @param description Description of the chaos game + * + * @param width Width of the canvas + * + * @param height Height of the canvas + */ + public void setExploreGame(ChaosGameDescription description, int width, int height) { + this.description = description; + setChaosCanvas(description.getMinCoords(), description.getMaxCoords(), width, height); + + } + public ChaosCanvas getCanvas() { return canvas; } @@ -41,11 +60,6 @@ public class ExploreGame implements Subject { return description; } - public void setExploreGame(ChaosGameDescription description, int width, int height) { - this.description = description; - setChaosCanvas(description.getMinCoords(), description.getMaxCoords(), width, height); - } - /** * Method for setting the chaos canvas. * diff --git a/src/main/java/org/example/chaosgame/model/linalg/Matrix2x2.java b/src/main/java/org/example/chaosgame/model/linalg/Matrix2x2.java index 24a5f69fd23de18c3a0b064a7053f17b7b1c12c6..ea2b4911ee21761c87b45e26ff255d7333db036e 100644 --- a/src/main/java/org/example/chaosgame/model/linalg/Matrix2x2.java +++ b/src/main/java/org/example/chaosgame/model/linalg/Matrix2x2.java @@ -1,6 +1,8 @@ package org.example.chaosgame.model.linalg; +import java.util.Objects; + /** * Record for 2x2 matrices. * The matrices are represented by four double values: a, b, c, and d. @@ -37,4 +39,39 @@ public record Matrix2x2(double a, double b, double c, double d) { this.c * vector.getX() + this.d * vector.getY() ); } + + /** + * Equals method for Matrix2x2. + * Compares the four double values of two Matrix2x2 objects. + * Generated by IntelliJ IDEA. + * + * @param o the object to compare. + * @return true if the objects are equal, false otherwise. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Matrix2x2 matrix2x2 = (Matrix2x2) o; + return Double.compare(a, matrix2x2.a) == 0 + && Double.compare(b, matrix2x2.b) == 0 + && Double.compare(c, matrix2x2.c) == 0 + && Double.compare(d, matrix2x2.d) == 0; + } + + /** + * Hashcode method for Matrix2x2. + * Use the four double values to generate the hashcode. + * Generated by IntelliJ IDEA. + * + * @return the hashcode of the Matrix2x2 object. + */ + @Override + public int hashCode() { + return Objects.hash(a, b, c, d); + } } diff --git a/src/main/java/org/example/chaosgame/model/linalg/Vector2D.java b/src/main/java/org/example/chaosgame/model/linalg/Vector2D.java index 3d9ec7a2daf3979335f7acb14ebe02923cfe79e3..c06683ae4e49f58196a970b7dfae59891df163d5 100644 --- a/src/main/java/org/example/chaosgame/model/linalg/Vector2D.java +++ b/src/main/java/org/example/chaosgame/model/linalg/Vector2D.java @@ -1,5 +1,7 @@ package org.example.chaosgame.model.linalg; +import java.util.Objects; + /** * Class for 2D vectors. * Vectors are represented by an x-coordinate and a y-coordinate. @@ -98,4 +100,38 @@ public class Vector2D { public double lengthSq() { return x * x + y * y; } + + + /** + * Equals method for Vector2D. + * Compares two vectors for equality. + * Overrides the default equals method. + * Generated by IntelliJ IDEA. + * + * @param o Object to compare + * @return true if the vectors are equal, false otherwise + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Vector2D vector2D = (Vector2D) o; + return Double.compare(x, vector2D.x) == 0 && Double.compare(y, vector2D.y) == 0; + } + + /** + * Hashcode method for Vector2D. + * Overrides the default hashcode method. + * Generated by IntelliJ IDEA. + * + * @return the hashcode of the vector + */ + @Override + public int hashCode() { + return Objects.hash(x, y); + } } diff --git a/src/main/java/org/example/chaosgame/model/transformations/AffineTransform2D.java b/src/main/java/org/example/chaosgame/model/transformations/AffineTransform2D.java index 91c6d239e41630164e9018f27d7efab9aadda68b..da0db148c4a0ca696002e500fa59eec2bb692800 100644 --- a/src/main/java/org/example/chaosgame/model/transformations/AffineTransform2D.java +++ b/src/main/java/org/example/chaosgame/model/transformations/AffineTransform2D.java @@ -1,8 +1,10 @@ package org.example.chaosgame.model.transformations; +import java.util.Objects; import org.example.chaosgame.model.linalg.Matrix2x2; import org.example.chaosgame.model.linalg.Vector2D; + /** * Record for 2D affine transformations. * The transformation is represented by a 2x2 matrix and a 2D vector. @@ -33,4 +35,36 @@ public record AffineTransform2D(Matrix2x2 matrix, Vector2D vector) implements Tr public Vector2D transform(Vector2D point) { return matrix.multiply(point).add(vector); } + + /** + * Equals method for AffineTransform2D. + * Compares the matrix and vector of two AffineTransform2D objects. + * Generated by IntelliJ IDEA. + * + * @param o the object to compare + * @return true if the objects are equal, false otherwise + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AffineTransform2D that = (AffineTransform2D) o; + return Objects.equals(vector, that.vector) && Objects.equals(matrix, that.matrix); + } + + /** + * Hashcode method for AffineTransform2D. + * Use the matrix and vector to generate the hashcode. + * Generated by IntelliJ IDEA. + * + * @return the hashcode of the AffineTransform2D object + */ + @Override + public int hashCode() { + return Objects.hash(matrix, vector); + } } diff --git a/src/main/java/org/example/chaosgame/model/transformations/JuliaTransform.java b/src/main/java/org/example/chaosgame/model/transformations/JuliaTransform.java index 85d619f461c67429aff91032bb02f16864154001..98119ed4144a9a0758c78c3b20e5ec157244db11 100644 --- a/src/main/java/org/example/chaosgame/model/transformations/JuliaTransform.java +++ b/src/main/java/org/example/chaosgame/model/transformations/JuliaTransform.java @@ -1,5 +1,6 @@ package org.example.chaosgame.model.transformations; +import java.util.Objects; import org.example.chaosgame.model.linalg.Complex; import org.example.chaosgame.model.linalg.Vector2D; @@ -49,4 +50,37 @@ public class JuliaTransform implements Transform2D { double b = sign * result.getY(); return new Vector2D(a, b); } + + + /** + * Equals method for JuliaTransform. + * Compares the point and sign of two JuliaTransform objects. + * Generated by IntelliJ IDEA. + * + * @param o the object to compare + * @return true if the objects are equal, false otherwise + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JuliaTransform that = (JuliaTransform) o; + return sign == that.sign && Objects.equals(point, that.point); + } + + /** + * Hashcode method for JuliaTransform. + * Generates a hashcode based on the point and sign of the JuliaTransform object. + * Generated by IntelliJ IDEA. + * + * @return the hashcode of the object + */ + @Override + public int hashCode() { + return Objects.hash(point, sign); + } } diff --git a/src/main/java/org/example/chaosgame/view/ChaosPage.java b/src/main/java/org/example/chaosgame/view/ChaosPage.java index cbb58d76538c913ad304ddca7361c4da086b07b3..5ada76ba90ee8975e1c18c1baf38e51e97a7e8a4 100644 --- a/src/main/java/org/example/chaosgame/view/ChaosPage.java +++ b/src/main/java/org/example/chaosgame/view/ChaosPage.java @@ -30,10 +30,10 @@ public class ChaosPage extends GamePage { this.bottomBar.getStyleClass().add("chaos-text"); SideBar sideBar = new SideBar(chaosGameController); + this.setCenter(gc.getCanvas()); this.setTop(topBar); this.setRight(sideBar); this.setBottom(bottomBar); - this.setLeft(gc.getCanvas()); } /** @@ -45,7 +45,7 @@ public class ChaosPage extends GamePage { * @param max the max coordinates */ public void updateInformation(Transform2D transformation, int steps, Vector2D min, Vector2D max) { - topBar.updateTopBar(transformation, steps, min, max); + topBar.updateTotalTopBar(transformation, steps, min, max); bottomBar.updateBottomBar(transformation); } diff --git a/src/main/java/org/example/chaosgame/view/ExplorePage.java b/src/main/java/org/example/chaosgame/view/ExplorePage.java index 97e6522b52bdae8e577317b26893bcbd8a709747..610dce2870d3932bea90c088c3a446789c10a1ca 100644 --- a/src/main/java/org/example/chaosgame/view/ExplorePage.java +++ b/src/main/java/org/example/chaosgame/view/ExplorePage.java @@ -36,13 +36,7 @@ public class ExplorePage extends GamePage { this.setBottom(bottomBar); this.setTop(topBar); - gc.getCanvas().setOnScroll(event -> { - try { - exploreGameController.onScroll(event); - } catch (Exception e) { - exploreGameController.resetImage(); - } - }); + gc.getCanvas().setOnScroll(exploreGameController::onScroll); this.setOnMousePressed(exploreGameController::mousePressed); this.setOnMouseDragged(exploreGameController::mouseDragged); this.setOnMouseReleased(exploreGameController::mouseReleased); diff --git a/src/main/java/org/example/chaosgame/view/components/BottomBar.java b/src/main/java/org/example/chaosgame/view/components/BottomBar.java index 00d8d3677d5c8916bc5c5d37845ce30af3f82b87..d0117d724f9b5fddba3930c8f23c6808ee85e8c6 100644 --- a/src/main/java/org/example/chaosgame/view/components/BottomBar.java +++ b/src/main/java/org/example/chaosgame/view/components/BottomBar.java @@ -30,16 +30,15 @@ public class BottomBar extends HBox { public BottomBar(GameController gameController) { this.setSpacing(10); this.realPartLabel = new Label(); + this.realPartLabel.getStyleClass().add("top-bottom-padding"); this.imaginaryPartLabel = new Label(); + this.imaginaryPartLabel.getStyleClass().add("top-bottom-padding"); this.sliderRealPart = new SliderRealPart(gameController); this.sliderImaginaryPart = new SliderImaginaryPart(gameController); realPartLabel.setMinSize(200, 20); imaginaryPartLabel.setMinSize(200, 20); - realPartLabel.setAlignment(Pos.CENTER); - imaginaryPartLabel.setAlignment(Pos.CENTER); - - this.setAlignment(javafx.geometry.Pos.CENTER); + this.setAlignment(Pos.CENTER); this.getChildren().addAll(realPartLabel, sliderRealPart, sliderImaginaryPart, imaginaryPartLabel); diff --git a/src/main/java/org/example/chaosgame/view/components/MinMaxDialog.java b/src/main/java/org/example/chaosgame/view/components/MinMaxDialog.java index de0589baaeeaf30a31be6beca32ff59313c70893..3286a6b549c2d5c0a9515c11ae87e07c785e67d1 100644 --- a/src/main/java/org/example/chaosgame/view/components/MinMaxDialog.java +++ b/src/main/java/org/example/chaosgame/view/components/MinMaxDialog.java @@ -1,6 +1,8 @@ package org.example.chaosgame.view.components; import java.util.List; + +import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; import javafx.scene.control.Dialog; import javafx.scene.control.TextField; @@ -24,8 +26,8 @@ public class MinMaxDialog extends Dialog<List<String>> { setTitle("Set Min/Max Coordinates"); setHeaderText("Please enter the min and max coordinates:"); - ButtonType okButtonType = new ButtonType("OK"); - getDialogPane().getButtonTypes().addAll(ButtonType.CANCEL, okButtonType); + ButtonType saveButtonType = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE); + this.getDialogPane().getButtonTypes().addAll(saveButtonType, ButtonType.CANCEL); GridPane grid = new GridPane(); grid.setHgap(10); @@ -49,7 +51,7 @@ public class MinMaxDialog extends Dialog<List<String>> { getDialogPane().setContent(grid); setResultConverter(dialogButton -> { - if (dialogButton == okButtonType) { + if (dialogButton == saveButtonType) { String minX = minXField.getText(); String minY = minYField.getText(); String maxX = maxXField.getText(); diff --git a/src/main/java/org/example/chaosgame/view/components/NumberOfStepsInput.java b/src/main/java/org/example/chaosgame/view/components/NumberOfStepsInput.java index 75bb62a31935405af03581c2e174827dc0bdb991..ee59938e76cb7a23b6a565220a5ee04d958c0103 100644 --- a/src/main/java/org/example/chaosgame/view/components/NumberOfStepsInput.java +++ b/src/main/java/org/example/chaosgame/view/components/NumberOfStepsInput.java @@ -15,7 +15,7 @@ public class NumberOfStepsInput extends TextField { this.setPromptText("Number of steps"); this.setMaxWidth(180); this.setMinWidth(180); - this.setMinHeight(30); - this.setMaxHeight(30); + this.setMinHeight(40); + this.setMaxHeight(40); } } diff --git a/src/main/java/org/example/chaosgame/view/components/SideBar.java b/src/main/java/org/example/chaosgame/view/components/SideBar.java index 7102f0ea6ce0165b4c158394be74adf77fd2bc4e..d866803f75142d8bffb6016ec700a8a849e7959a 100644 --- a/src/main/java/org/example/chaosgame/view/components/SideBar.java +++ b/src/main/java/org/example/chaosgame/view/components/SideBar.java @@ -47,13 +47,13 @@ public class SideBar extends VBox { chaosGameController::updateFractalColor); this.getChildren().addAll( - fractalSelectionBox, colorPicker, numberOfStepsInput, - coordinatesButton, createOwnFractal, saveFractalButton, openFileButton, - runGame, resetGame); + fractalSelectionBox, colorPicker, coordinatesButton, + createOwnFractal, saveFractalButton, openFileButton, + numberOfStepsInput, runGame, resetGame); this.setAlignment(Pos.CENTER_RIGHT); - VBox.setMargin(coordinatesButton, new Insets(50, 0, 0, 0)); - VBox.setMargin(runGame, new Insets(50, 0, 0, 0)); + VBox.setMargin(coordinatesButton, new Insets(30, 0, 0, 0)); + VBox.setMargin(numberOfStepsInput, new Insets(30, 0, 0, 0)); this.getStyleClass().add("side-bar"); } diff --git a/src/main/java/org/example/chaosgame/view/components/SliderImaginaryPart.java b/src/main/java/org/example/chaosgame/view/components/SliderImaginaryPart.java index db2f476fd378b1bacdf468427a8a003184835b20..14649a0f31469f9d64083859a2b53ec1d5b87518 100644 --- a/src/main/java/org/example/chaosgame/view/components/SliderImaginaryPart.java +++ b/src/main/java/org/example/chaosgame/view/components/SliderImaginaryPart.java @@ -14,6 +14,7 @@ public class SliderImaginaryPart extends BaseSlider { */ public SliderImaginaryPart(GameController gameController) { super(gameController); + this.getStyleClass().add("top-bottom-padding"); } /** diff --git a/src/main/java/org/example/chaosgame/view/components/SliderRealPart.java b/src/main/java/org/example/chaosgame/view/components/SliderRealPart.java index 1d9586fee9926401c6ae11fd03afb17777fddf46..48926964ef8a20808c1dc612775d2866df9feeb7 100644 --- a/src/main/java/org/example/chaosgame/view/components/SliderRealPart.java +++ b/src/main/java/org/example/chaosgame/view/components/SliderRealPart.java @@ -14,6 +14,7 @@ public class SliderRealPart extends BaseSlider { */ public SliderRealPart(GameController gameController) { super(gameController); + this.getStyleClass().add("top-bottom-padding"); } @Override diff --git a/src/main/java/org/example/chaosgame/view/components/TopBar.java b/src/main/java/org/example/chaosgame/view/components/TopBar.java index 8ffad6d296c05eadac91c21eea946166b5c5ecf0..90a397acbe8e35a9c228236fa941c7bd80153b9f 100644 --- a/src/main/java/org/example/chaosgame/view/components/TopBar.java +++ b/src/main/java/org/example/chaosgame/view/components/TopBar.java @@ -25,16 +25,16 @@ public class TopBar extends HBox { public TopBar(GameController gameController) { super(); this.gameInfo = new Label(); + this.gameInfo.getStyleClass().add("top-bottom-padding"); this.stepsLabel = new Label(); + this.stepsLabel.getStyleClass().add("top-bottom-padding"); this.coordinatesLabel = new Label(); + this.coordinatesLabel.getStyleClass().add("top-bottom-padding"); Button homeButton = new HomeButton(); homeButton.setOnAction(event -> gameController.homeButtonClicked()); this.getChildren().addAll(homeButton, gameInfo, coordinatesLabel, stepsLabel); - this.setPadding(new javafx.geometry.Insets(10)); - - this.setSpacing(50); this.setAlignment(Pos.CENTER_LEFT); this.getStyleClass().add("top-bottom-bar"); } @@ -47,10 +47,9 @@ public class TopBar extends HBox { * @param min the min coordinates * @param max the max coordinates */ - public void updateTopBar(Transform2D first, int totalSteps, Vector2D min, Vector2D max) { + public void updateTotalTopBar(Transform2D first, int totalSteps, Vector2D min, Vector2D max) { gameInfo.setText("Transformation: " + first.getClass().getSimpleName()); - coordinatesLabel.setText("Min-Coordinate: " + min.getX() + ", " + min.getY() - + ". Max-Coordinate: " + max.getX() + ", " + max.getY()); + updateTopBar(min, max); stepsLabel.setText("Total steps: " + totalSteps); } @@ -61,9 +60,7 @@ public class TopBar extends HBox { * @param max the max coordinates */ public void updateTopBar(Vector2D min, Vector2D max) { - coordinatesLabel.setText("Min-Coordinate: " + (double) Math.round(min.getX() * 100) / 100 - + ", " + (double) Math.round(min.getY() * 100) / 100 - + ". Max-Coordinate: " + (double) Math.round(max.getX() * 100) / 100 + ", " - + (double) Math.round(max.getY() * 100) / 100); + coordinatesLabel.setText("Coordinates: " + (double) Math.round(min.getX() * 100) / 100 + " , " + (double) Math.round(min.getY() * 100) / 100 + + " (min), " + (double) Math.round(max.getX() * 100) / 100 + ", " + (double) Math.round(max.getY() * 100) / 100 + " (max)"); } } diff --git a/src/main/resources/global.css b/src/main/resources/global.css index 94ba268ec19f297e7c129e24839d5b43d31f0061..7957355911a0fff1faa5f148816c061df412d7b4 100644 --- a/src/main/resources/global.css +++ b/src/main/resources/global.css @@ -26,8 +26,12 @@ } .text-field { - -fx-font: 14 arial; - -fx-border-color: black; + -fx-font: 20 arial; + -fx-background-color: white; + -fx-text-fill: black; + -fx-border-color: #3b3b3b; + -fx-background-radius: 20; + -fx-border-radius: 20; -fx-border-width: 2; -fx-background-insets: 1; } @@ -56,7 +60,6 @@ .combo-box .indexed-cell:selected { -fx-text-fill: white; - -fx-background-radius: 20; } .choose-game-button { @@ -125,6 +128,7 @@ -fx-padding: 20; -fx-spacing: 10; } + .top-bottom-bar .text { -fx-font: 20 arial; -fx-fill: #7c7c7c; @@ -138,6 +142,11 @@ -fx-background-insets: 1; } +.top-bottom-padding{ + -fx-padding: 0 10 0 10; +} + + diff --git a/src/test/java/org/example/chaosgame/controller/ChaosGameControllerTest.java b/src/test/java/org/example/chaosgame/controller/ChaosGameControllerTest.java index b8802696767470ba48dad16a0c660907c19fc0bc..1e16946f1d1a9978186686d4afb38f6e343cbe8e 100644 --- a/src/test/java/org/example/chaosgame/controller/ChaosGameControllerTest.java +++ b/src/test/java/org/example/chaosgame/controller/ChaosGameControllerTest.java @@ -2,71 +2,69 @@ package org.example.chaosgame.controller; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - class ChaosGameControllerTest { - @Test - void gameSelection() { - } - - @Test - void runStepsValidation() { - } - - @Test - void openFromFile() { - } - - @Test - void updateFractalColor() { - } - - @Test - void createOwnJuliaFractal() { - } - - @Test - void createOwnAffineFractal() { - } - - @Test - void updateJuliaValue() { - } - - @Test - void isJuliaShowing() { - } - - @Test - void saveFractal() { - } - - @Test - void homeButtonClicked() { - } - - @Test - void update() { - } - - @Test - void registerObserver() { - } - - @Test - void removeObserver() { - } - - @Test - void notifyObservers() { - } - - @Test - void setCanvas() { - } - - @Test - void setBind() { - } + @Test + void gameSelection() { + } + + @Test + void runStepsValidation() { + } + + @Test + void openFromFile() { + } + + @Test + void updateFractalColor() { + } + + @Test + void createOwnJuliaFractal() { + } + + @Test + void createOwnAffineFractal() { + } + + @Test + void updateJuliaValue() { + } + + @Test + void isJuliaShowing() { + } + + @Test + void saveFractal() { + } + + @Test + void homeButtonClicked() { + } + + @Test + void update() { + } + + @Test + void registerObserver() { + } + + @Test + void removeObserver() { + } + + @Test + void notifyObservers() { + } + + @Test + void setCanvas() { + } + + @Test + void setBind() { + } } \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/controller/ExploreGameControllerTest.java b/src/test/java/org/example/chaosgame/controller/ExploreGameControllerTest.java index e999b1b47ee1487ca70f70de3fc999be8830e97d..05f43e043ac30ae963730e3b8c98e4a8d67a78ea 100644 --- a/src/test/java/org/example/chaosgame/controller/ExploreGameControllerTest.java +++ b/src/test/java/org/example/chaosgame/controller/ExploreGameControllerTest.java @@ -2,71 +2,69 @@ package org.example.chaosgame.controller; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - class ExploreGameControllerTest { - @Test - void mousePressed() { - } + @Test + void mousePressed() { + } - @Test - void mouseDragged() { - } + @Test + void mouseDragged() { + } - @Test - void mouseReleased() { - } + @Test + void mouseReleased() { + } - @Test - void zoomButtonClicked() { - } + @Test + void zoomButtonClicked() { + } - @Test - void onScroll() { - } + @Test + void onScroll() { + } - @Test - void homeButtonClicked() { - } + @Test + void homeButtonClicked() { + } - @Test - void getExplorePage() { - } + @Test + void getExplorePage() { + } - @Test - void updateFractalColor() { - } + @Test + void updateFractalColor() { + } - @Test - void update() { - } + @Test + void update() { + } - @Test - void registerObserver() { - } + @Test + void registerObserver() { + } - @Test - void removeObserver() { - } + @Test + void removeObserver() { + } - @Test - void notifyObservers() { - } + @Test + void notifyObservers() { + } - @Test - void updateExploreGame() { - } + @Test + void updateExploreGame() { + } - @Test - void resetImage() { - } + @Test + void resetImage() { + } - @Test - void setCanvas() { - } + @Test + void setCanvas() { + } - @Test - void updateJuliaValue() { - } + @Test + void updateJuliaValue() { + } } \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/controller/HomeControllerTest.java b/src/test/java/org/example/chaosgame/controller/HomeControllerTest.java index 6822a1539696115645d52e73d001d9b55da5a70a..c32f31ad6d11ba9334513ff41893d28879e4a99a 100644 --- a/src/test/java/org/example/chaosgame/controller/HomeControllerTest.java +++ b/src/test/java/org/example/chaosgame/controller/HomeControllerTest.java @@ -2,15 +2,13 @@ package org.example.chaosgame.controller; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - class HomeControllerTest { - @Test - void getHomePage() { - } + @Test + void getHomePage() { + } - @Test - void mouseEvent() { - } + @Test + void mouseEvent() { + } } \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/controller/PageControllerTest.java b/src/test/java/org/example/chaosgame/controller/PageControllerTest.java index 79fca41197a16e135c9f3b8260766221980b9ae3..7cc833bfb1dc27426e54df472030aa6002e7e3ed 100644 --- a/src/test/java/org/example/chaosgame/controller/PageControllerTest.java +++ b/src/test/java/org/example/chaosgame/controller/PageControllerTest.java @@ -6,15 +6,15 @@ import static org.junit.jupiter.api.Assertions.*; class PageControllerTest { - @Test - void goToPage() { - } + @Test + void goToPage() { + } - @Test - void exitGame() { - } + @Test + void exitGame() { + } - @Test - void update() { - } + @Test + void update() { + } } \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/linalg/Vector2DTest.java b/src/test/java/org/example/chaosgame/linalg/Vector2DTest.java deleted file mode 100644 index 5fecb5d6d240041de927b00efe392440a06520a6..0000000000000000000000000000000000000000 --- a/src/test/java/org/example/chaosgame/linalg/Vector2DTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.example.chaosgame.linalg; - -import org.example.chaosgame.model.linalg.Vector2D; -import org.junit.jupiter.api.*; - -import static org.junit.jupiter.api.Assertions.*; - -class Vector2DTest { - private double x; - private double y; - private Vector2D vector; - private Vector2D otherVector; - - @BeforeEach - void setUp() { - x = 1.0; - y = 2.0; - vector = new Vector2D(x, y); - otherVector = new Vector2D(1.0, 1.0); - } - - @AfterEach - void tearDown() { - vector = null; - otherVector = null; - } - - @Nested - @DisplayName("Test getX and getY") - class TestGetters { - @Test - @DisplayName("Test getX") - void getX() { - assertEquals(x, vector.getX()); - } - - @Test - @DisplayName("Test getY") - void getY() { - assertEquals(y, vector.getY()); - } - } - - @Nested - @DisplayName("Test add and subtract") - class TestAddAndSubtract { - @Test - @DisplayName("Test add should work") - void add() { - Vector2D w = vector.add(otherVector); - assertEquals(2.0, w.getX()); - assertEquals(3.0, w.getY()); - } - - @Test - @DisplayName("Test add should not work") - void addFail() { - Vector2D w = vector.add(otherVector); - assertNotEquals(1.0, w.getX()); - assertNotEquals(2.0, w.getY()); - } - - @Test - @DisplayName("Test subtract should work") - void subtract() { - Vector2D w = vector.subtract(otherVector); - assertEquals(0.0, w.getX()); - assertEquals(1.0, w.getY()); - } - - @Test - @DisplayName("Test subtract should not work") - void subtractFail() { - Vector2D w = vector.subtract(otherVector); - assertNotEquals(1.0, w.getX()); - assertNotEquals(2.0, w.getY()); - } - } -} \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/model/chaos/ChaosCanvasTest.java b/src/test/java/org/example/chaosgame/model/chaos/ChaosCanvasTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a55ef4c5ce55787d116f5675911918a616002274 --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/chaos/ChaosCanvasTest.java @@ -0,0 +1,188 @@ +package org.example.chaosgame.model.chaos; + +import org.example.chaosgame.model.linalg.Vector2D; +import org.example.chaosgame.model.transformations.AffineTransform2D; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; + +import static org.junit.jupiter.api.Assertions.*; + +class ChaosCanvasTest { + private static ChaosCanvas chaosCanvas; + private static Vector2D minCoords; + private static Vector2D maxCoords; + private static final int WIDTH = 100; + private static final int HEIGHT = 100; + + @BeforeEach + void setUp() { + minCoords = new Vector2D(0, 0); + maxCoords = new Vector2D(1, 1); + chaosCanvas = new ChaosCanvas(WIDTH, HEIGHT, minCoords, maxCoords); + } + + @AfterEach + void tearDown() { + minCoords = null; + maxCoords = null; + chaosCanvas = null; + } + + @Nested + @DisplayName("Test getPixel") + class TestGetPixel { + @Test + @DisplayName("Test positive getPixel") + void getPixel() { + double pixel = chaosCanvas.getPixel(new Vector2D(0.5, 0.5)); + assertEquals(0.0, pixel); + } + + @Test + @DisplayName("Test negative getPixel") + void getPixelFail() { + double pixel = chaosCanvas.getPixel(new Vector2D(0, 0)); + assertNotEquals(1.0, pixel); + } + } + + @Nested + @DisplayName("Test putPixel") + class TestPutPixel { + @Test + @DisplayName("Test positive putPixel(Vector2D)") + void putPixel() { + chaosCanvas.putPixelChaos(new Vector2D(0, 0)); + assertEquals(1.0, chaosCanvas.getPixel(new Vector2D(0, 0))); + } + + @Test + @DisplayName("Test negative putPixel(Vector2D)") + void putPixelFail() { + chaosCanvas.putPixelChaos(new Vector2D(0, 0)); + assertNotEquals(0.0, chaosCanvas.getPixel(new Vector2D(0, 0))); + } + + @Test + @DisplayName("Test positive putPixel(int, int, double)") + void putPixelInt() { + chaosCanvas.putPixelExplore(0, 0, 1.0); + assertEquals(1.0, chaosCanvas.getPixel(new Vector2D(0, 1))); + } + + @Test + @DisplayName("Test negative putPixel(int, int, double)") + void putPixelIntFail() { + chaosCanvas.putPixelExplore(0, 0, 1.0); + assertNotEquals(0.0, chaosCanvas.getPixel(new Vector2D(0, 1))); + } + } + + @Nested + @DisplayName("Test transformIndicesToCoords") + class TestTransformIndicesToCoords { + @Test + @DisplayName("Test positive transformIndicesToCoords") + void transformIndicesToCoords() { + Vector2D coords = chaosCanvas.transformIndicesToCoords(0, 0); + assertEquals(0.0, coords.getX()); + assertEquals(1.0, coords.getY()); + } + + @Test + @DisplayName("Test negative transformIndicesToCoords") + void transformIndicesToCoordsFail() { + Vector2D coords = chaosCanvas.transformIndicesToCoords(0, 0); + assertNotEquals(1.0, coords.getX()); + assertNotEquals(0.0, coords.getY()); + } + } + + @Nested + @DisplayName("Test clearCanvas") + class TestClearCanvas { + @Test + @DisplayName("Test positive clearCanvas") + void clearCanvas() { + chaosCanvas.putPixelChaos(new Vector2D(0, 0)); + chaosCanvas.clearCanvas(); + assertEquals(0.0, chaosCanvas.getPixel(new Vector2D(0, 0))); + } + + @Test + @DisplayName("Test negative clearCanvas") + void clearCanvasFail() { + chaosCanvas.putPixelChaos(new Vector2D(0, 0)); + chaosCanvas.clearCanvas(); + double[][] canvas = chaosCanvas.getCanvasArray(); + for (double[] row : canvas) { + for (double value : row) { + assertNotEquals(1.0, value); + } + } + } + } + + @Test + void getWidth() { + assertEquals(WIDTH, chaosCanvas.getWidth()); + } + + @Test + void getHeight() { + assertEquals(HEIGHT, chaosCanvas.getHeight()); + } + + @Test + void getMinCoords() { + assertEquals(minCoords, chaosCanvas.getMinCoords()); + } + + @Test + void getMaxCoords() { + assertEquals(maxCoords, chaosCanvas.getMaxCoords()); + } + + @Test + void setMinCoords() { + Vector2D newMinCoords = new Vector2D(-1, -1); + chaosCanvas.setMinCoords(newMinCoords); + assertEquals(newMinCoords, chaosCanvas.getMinCoords()); + } + + @Test + void setMaxCoords() { + Vector2D newMaxCoords = new Vector2D(2, 2); + chaosCanvas.setMaxCoords(newMaxCoords); + assertEquals(newMaxCoords, chaosCanvas.getMaxCoords()); + } + + @Test + void setTransformCoordsToIndices() { + Vector2D newMinCoords = new Vector2D(-1, -1); + Vector2D newMaxCoords = new Vector2D(2, 2); + chaosCanvas.setMinCoords(newMinCoords); + chaosCanvas.setMaxCoords(newMaxCoords); + chaosCanvas.setTransformCoordsToIndices(); + AffineTransform2D transform = chaosCanvas.getTransformCoordsToIndices(); + assertNotNull(transform); + assertEquals(transform, chaosCanvas.getTransformCoordsToIndices()); + } + + @Test + void setWidth() { + int newWidth = 200; + chaosCanvas.setWidth(newWidth); + assertEquals(newWidth, chaosCanvas.getWidth()); + } + + @Test + void setHeight() { + int newHeight = 200; + chaosCanvas.setHeight(newHeight); + assertEquals(newHeight, chaosCanvas.getHeight()); + } +} diff --git a/src/test/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactoryTest.java b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..00e0ad8b31c767e8df81fd56b14cb5b5f93fb962 --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactoryTest.java @@ -0,0 +1,143 @@ +package org.example.chaosgame.model.chaos; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.example.chaosgame.controller.ChaosGameDescriptionFactory; +import org.example.chaosgame.model.linalg.Complex; +import org.example.chaosgame.model.linalg.Matrix2x2; +import org.example.chaosgame.model.linalg.Vector2D; +import org.example.chaosgame.model.transformations.AffineTransform2D; +import org.example.chaosgame.model.transformations.JuliaTransform; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class ChaosGameDescriptionFactoryTest { + private static ChaosGameDescription expectedJulia; + private static ChaosGameDescription expectedSierpinski; + private static ChaosGameDescription expectedBarnsley; + + @BeforeAll + static void setUp() { + expectedJulia = new ChaosGameDescription( + new Vector2D(-1.6, -1.0), + new Vector2D(1.6, 1.0), + List.of( + new JuliaTransform(new Complex(-0.70176, -0.3842), 1) + ) + ); + + expectedSierpinski = new ChaosGameDescription( + new Vector2D(0.0, 0.0), + new Vector2D(1.0, 1.0), + List.of( + new AffineTransform2D(new Matrix2x2(0.5, 0.0, 0.0, 0.5), + new Vector2D(0.0, 0.0)), + new AffineTransform2D(new Matrix2x2(0.5, 0.0, 0.0, 0.5), + new Vector2D(0.25, 0.50)), + new AffineTransform2D(new Matrix2x2(0.5, 0.0, 0.0, 0.5), + new Vector2D(0.5, 0.0)) + ) + ); + + expectedBarnsley = new ChaosGameDescription( + new Vector2D(-2.65, 0.0), + new Vector2D(2.65, 10.0), + List.of( + new AffineTransform2D(new Matrix2x2(0.0, 0.0, 0.0, 0.16), + new Vector2D(0.0, 0.0)), + new AffineTransform2D(new Matrix2x2(0.85, 0.04, -0.04, 0.85), + new Vector2D(0.0, 1.60)), + new AffineTransform2D(new Matrix2x2(0.20, -0.26, 0.23, 0.22), + new Vector2D(0.0, 1.60)), + new AffineTransform2D(new Matrix2x2(-0.15, 0.28, 0.26, 0.24), + new Vector2D(0.0, 0.44)) + ), List.of(2, 84, 7, 7) + ); + } + + + @Nested + class JuliaTests { + @Test + void testJuliaNotNull() { + ChaosGameDescription juliaResult = ChaosGameDescriptionFactory.get(ChaosGameType.JULIA); + assertNotNull(juliaResult); + } + + @Test + void testJuliaInstanceOf() { + ChaosGameDescription juliaResult = ChaosGameDescriptionFactory.get(ChaosGameType.JULIA); + assertInstanceOf(ChaosGameDescription.class, juliaResult); + } + + @Test + void testJuliaEquals() { + ChaosGameDescription juliaResult = ChaosGameDescriptionFactory.get(ChaosGameType.JULIA); + assertEquals(expectedJulia, juliaResult); + } + } + + @Nested + class SierpinskiTests { + @Test + void testSierpinskiNotNull() { + ChaosGameDescription sierpinskiResult = + ChaosGameDescriptionFactory.get(ChaosGameType.SIERPINSKI); + assertNotNull(sierpinskiResult); + } + + @Test + void testSierpinskiInstanceOf() { + ChaosGameDescription sierpinskiResult = + ChaosGameDescriptionFactory.get(ChaosGameType.SIERPINSKI); + assertInstanceOf(ChaosGameDescription.class, sierpinskiResult); + } + + @Test + void testSierpinskiEquals() { + ChaosGameDescription sierpinskiResult = + ChaosGameDescriptionFactory.get(ChaosGameType.SIERPINSKI); + assertEquals(expectedSierpinski, sierpinskiResult); + } + } + + @Nested + class BarnsleyTests { + @Test + void testBarnsleyNotNull() { + ChaosGameDescription barnsleyResult = + ChaosGameDescriptionFactory.get(ChaosGameType.BARNSLEY); + assertNotNull(barnsleyResult); + } + + @Test + void testBarnsleyInstanceOf() { + ChaosGameDescription barnsleyResult = ChaosGameDescriptionFactory.get(ChaosGameType.BARNSLEY); + assertInstanceOf(ChaosGameDescription.class, barnsleyResult); + } + + @Test + void testBarnsleyEquals() { + ChaosGameDescription barnsleyResult = ChaosGameDescriptionFactory.get(ChaosGameType.BARNSLEY); + assertEquals(expectedBarnsley, barnsleyResult); + } + } + + @Test + void testInvalidType() { + assertThrows(IllegalArgumentException.class, () -> ChaosGameDescriptionFactory.get(null)); + } + + @AfterAll + static void tearDown() { + expectedJulia = null; + expectedSierpinski = null; + expectedBarnsley = null; + } +} \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionTest.java b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c85c23b3fcbc496ad9fb13f41cdba2b9932a7eab --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionTest.java @@ -0,0 +1,174 @@ +package org.example.chaosgame.model.chaos; + + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Arrays; +import java.util.List; +import org.example.chaosgame.model.linalg.Matrix2x2; +import org.example.chaosgame.model.linalg.Vector2D; +import org.example.chaosgame.model.transformations.AffineTransform2D; +import org.example.chaosgame.model.transformations.Transform2D; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class ChaosGameDescriptionTest { + private ChaosGameDescription chaosGameDescription; + private Vector2D minCoords; + private Vector2D maxCoords; + private Matrix2x2 matrixA; + private Vector2D vectorPartA; + private Matrix2x2 matrixB; + private Vector2D vectorPartB; + private List<Transform2D> transforms; + private List<Integer> probabilities; + + @BeforeEach + void setUp() { + minCoords = new Vector2D(0, 0); + maxCoords = new Vector2D(1, 1); + + matrixA = new Matrix2x2(0.5, 0.0, 0.0, 0.5); + vectorPartA = new Vector2D(0.0, 0.0); + matrixB = new Matrix2x2(0.5, 0.0, 0.0, 0.5); + vectorPartB = new Vector2D(0.25, 0.50); + transforms = Arrays.asList(new AffineTransform2D(matrixA, vectorPartA), + new AffineTransform2D(matrixB, vectorPartB)); + probabilities = Arrays.asList(50, 50); + chaosGameDescription = new ChaosGameDescription( + minCoords, maxCoords, transforms, probabilities); + } + + @AfterEach + void tearDown() { + minCoords = null; + maxCoords = null; + transforms = null; + probabilities = null; + chaosGameDescription = null; + } + + @Test + void testGetMinCoords() { + assertEquals(minCoords, chaosGameDescription.getMinCoords()); + } + + @Test + void testGetMaxCoords() { + assertEquals(maxCoords, chaosGameDescription.getMaxCoords()); + } + + @Test + void testGetTransforms() { + assertEquals(transforms, chaosGameDescription.getTransforms()); + } + + @Test + void testGetProbabilities() { + assertEquals(probabilities, chaosGameDescription.getProbabilities()); + } + + @Nested + @DisplayName("SetTransforms") + class SetTransforms { + @Test + @DisplayName("Set null transforms") + void testSetNullTransforms() { + assertThrows(IllegalArgumentException.class, () -> chaosGameDescription.setTransforms(null)); + } + + @Test + @DisplayName("Set empty transforms") + void testSetEmptyTransforms() { + assertThrows(IllegalArgumentException.class, + () -> chaosGameDescription.setTransforms(List.of())); + } + + @Test + @DisplayName("Set too many transforms") + void testSetTooManyTransforms() { + List<Transform2D> tooManyTransforms = Arrays.asList( + new AffineTransform2D(matrixA, vectorPartA), + new AffineTransform2D(matrixB, vectorPartB), + new AffineTransform2D(matrixA, vectorPartA), + new AffineTransform2D(matrixB, vectorPartB), + new AffineTransform2D(matrixA, vectorPartA) + ); + assertThrows(IllegalArgumentException.class, + () -> chaosGameDescription.setTransforms(tooManyTransforms)); + } + + @Test + @DisplayName("Set valid transforms") + void testSetValidTransforms() { + Matrix2x2 newMatrix = new Matrix2x2(9.9, 9.9, 9.9, 9.9); + Vector2D newVector = new Vector2D(9.9, 9.9); + List<Transform2D> newTransforms = List.of(new AffineTransform2D(newMatrix, newVector)); + chaosGameDescription.setTransforms(newTransforms); + assertEquals(newTransforms, chaosGameDescription.getTransforms()); + } + } + + @Nested + @DisplayName("Set coordinates") + class SetCoordinates { + @Test + @DisplayName("Set min coordinates") + void testSetMinCoords() { + Vector2D newMinCoords = new Vector2D(-1, -1); + chaosGameDescription.setMinCoords(newMinCoords); + assertEquals(newMinCoords, chaosGameDescription.getMinCoords()); + } + + @Test + @DisplayName("Set max coordinates") + void testSetMaxCoords() { + Vector2D newMaxCoords = new Vector2D(2, 2); + chaosGameDescription.setMaxCoords(newMaxCoords); + assertEquals(newMaxCoords, chaosGameDescription.getMaxCoords()); + } + + @Test + @DisplayName("Set min and max coordinates") + void testSetMinAndMaxCoords() { + Vector2D newMinCoords = new Vector2D(-99, -99); + + assertThrows(IllegalArgumentException.class, + () -> new ChaosGameDescription(newMinCoords, maxCoords, transforms, probabilities)); + assertThrows(IllegalArgumentException.class, + () -> new ChaosGameDescription(maxCoords, minCoords, transforms, probabilities)); + assertThrows(IllegalArgumentException.class, + () -> new ChaosGameDescription(minCoords, minCoords, transforms, probabilities)); + } + } + + @Test + void testEquals() { + ChaosGameDescription sameChaosGameDescription = + new ChaosGameDescription(minCoords, maxCoords, transforms, probabilities); + assertEquals(chaosGameDescription, sameChaosGameDescription); + assertNotNull(chaosGameDescription); + assertNotEquals(chaosGameDescription, new Object()); + ChaosGameDescription differentChaosGameDescription = + new ChaosGameDescription( + new Vector2D(1, 1), + new Vector2D(2, 2), + transforms, probabilities); + assertNotEquals(chaosGameDescription, differentChaosGameDescription); + } + + @Test + void testHashCode() { + ChaosGameDescription sameChaosGameDescription + = new ChaosGameDescription( + minCoords, maxCoords, + transforms, probabilities); + assertEquals(chaosGameDescription.hashCode(), sameChaosGameDescription.hashCode()); + } +} diff --git a/src/test/java/org/example/chaosgame/model/chaos/ChaosGameFileHandlerTest.java b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameFileHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..32c0fa2d95254b55ec7692efd67634e8735a3659 --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameFileHandlerTest.java @@ -0,0 +1,354 @@ +package org.example.chaosgame.model.chaos; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import org.example.chaosgame.controller.ChaosGameFileHandler; +import org.example.chaosgame.model.linalg.Complex; +import org.example.chaosgame.model.linalg.Matrix2x2; +import org.example.chaosgame.model.linalg.Vector2D; +import org.example.chaosgame.model.transformations.AffineTransform2D; +import org.example.chaosgame.model.transformations.JuliaTransform; +import org.example.chaosgame.model.transformations.Transform2D; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + + + + + +class ChaosGameFileHandlerTest { + private static ChaosGameFileHandler fileHandler; + private static String validAffineContent; + private static String validJuliaContent; + private static String invalidNumbersContent; + private static String invalidTypeContent; + + @BeforeAll + static void setUp() { + fileHandler = new ChaosGameFileHandler(); + validAffineContent = """ + Affine2D # Type of Transformation + -2.65, 0.0 # Min-coordinate + 2.65, 10.0 # Max-coordinate + 0.0, 0.0, 0.0, 0.16, 0.0, 0.0 # 1 transformation + 0.85, 0.04, -0.04, 0.85, 0.0, 1.6 # 2 transformation + 0.2, -0.26, 0.23, 0.22, 0.0, 1.6 # 3 transformation + -0.15, 0.28, 0.26, 0.24, 0.0, 0.44 # 4 transformation + """; + + validJuliaContent = """ + Julia # Type of Transformation + -1.6, -1.0 # Min-coordinate + 1.6, 1.0 # Max-coordinate + -0.70176, -0.3842 # Real and imaginary part of the complex number + """; + + invalidNumbersContent = """ + Affine2D # Type of Transformation + INVALID, 0.0 # Min-coordinate + 2.65, 10.0 # Max-coordinate + 0.0, 0.0, 0.0, WRONG, 0.0, 0.0 # 1 transformation + 0.85, 0.04, -0.04, 0.85, 0.0, 1.6 # 2 transformation + 0.2, WRONG, 0.23, 0.22, 0.0, 1.6 # 3 transformation + -0.15, 0.28, 0.26, 0.24ERROROOROR, 0.44 # 4 transformation + """; + + invalidTypeContent = """ + Unknown # Type of Transformation + -2.65, 0.0 # Min-coordinate + 2.65, 10.0 # Max-coordinate + 0.0, 0.0, 0.0, 0.16, 0.0, 0.0 # 1 transformation + 0.85, 0.04, -0.04, 0.85, 0.0, 1.6 # 2 transformation + 0.2, -0.26, 0.23, 0.22, 0.0, 1.6 # 3 transformation + -0.15, 0.28, 0.26, 0.24, 0.0, 0.44 # 4 transformation + """; + + } + + private Path createTempFileWithContent(String content) throws IOException { + Path tempFile = Files.createTempFile("chaosGame", ".txt"); + try (BufferedWriter writer = Files.newBufferedWriter(tempFile)) { + writer.write(content); + } + return tempFile; + } + + @Nested + class ReadFromFileTests { + @Test + void testReadValidAffine() throws IOException { + Path tempFile = createTempFileWithContent(validAffineContent); + ChaosGameDescription description = fileHandler.readFromFile(tempFile.toString()); + + Vector2D expectedMin = new Vector2D(-2.65, 0.0); + Vector2D expectedMax = new Vector2D(2.65, 10.0); + List<Transform2D> expectedTransforms = List.of( + new AffineTransform2D(new Matrix2x2(0.0, 0.0, 0.0, 0.16), new Vector2D(0.0, 0.0)), + new AffineTransform2D(new Matrix2x2(0.85, 0.04, -0.04, 0.85), new Vector2D(0.0, 1.6)), + new AffineTransform2D(new Matrix2x2(0.2, -0.26, 0.23, 0.22), new Vector2D(0.0, 1.6)), + new AffineTransform2D(new Matrix2x2(-0.15, 0.28, 0.26, 0.24), new Vector2D(0.0, 0.44)) + ); + + assertEquals(expectedMin, description.getMinCoords()); + assertEquals(expectedMax, description.getMaxCoords()); + assertEquals(expectedTransforms, description.getTransforms()); + + Files.delete(tempFile); + } + + @Test + void testReadValidJulia() throws IOException { + Path tempFile = createTempFileWithContent(validJuliaContent); + ChaosGameDescription description = fileHandler.readFromFile(tempFile.toString()); + + Vector2D expectedMin = new Vector2D(-1.6, -1.0); + Vector2D expectedMax = new Vector2D(1.6, 1.0); + List<Transform2D> expectedTransforms = List.of( + new JuliaTransform(new Complex(-0.70176, -0.3842), 1) + ); + + assertEquals(expectedMin, description.getMinCoords()); + assertEquals(expectedMax, description.getMaxCoords()); + assertEquals(expectedTransforms, description.getTransforms()); + + Files.delete(tempFile); + } + + @Test + void testReadUnknownType() throws IOException { + Path tempFile = createTempFileWithContent(invalidTypeContent); + assertThrows(IllegalArgumentException.class, + () -> fileHandler.readFromFile(tempFile.toString()), + "Unknown type of transformation should throw IllegalArgumentException"); + + Files.delete(tempFile); + } + + @Test + void testReadInvalidVector() throws IOException { + Path tempFile = createTempFileWithContent(invalidNumbersContent); + assertThrows(NumberFormatException.class, () -> fileHandler.readFromFile(tempFile.toString()), + "Invalid vector data should throw NumberFormatException"); + + Files.delete(tempFile); + } + } + + @Nested + class WriteToFileTests { + @Test + void testWriteValidAffine() throws IOException { + ChaosGameDescription description = new ChaosGameDescription( + new Vector2D(-2.65, 0.0), + new Vector2D(2.65, 10.0), + List.of( + new AffineTransform2D(new Matrix2x2(0.0, 0.0, 0.0, 0.16), + new Vector2D(0.0, 0.0)), + new AffineTransform2D(new Matrix2x2(0.85, 0.04, -0.04, 0.85), + new Vector2D(0.0, 1.6)), + new AffineTransform2D(new Matrix2x2(0.2, -0.26, 0.23, 0.22), + new Vector2D(0.0, 1.6)), + new AffineTransform2D(new Matrix2x2(-0.15, 0.28, 0.26, 0.24), + new Vector2D(0.0, 0.44)) + ) + ); + + Path tempFile = Files.createTempFile("chaosGame", ".txt"); + fileHandler.writeToFile(description, tempFile.toString()); + + String expectedContent = + """ + Affine2D # Type of transformation + -2.65, 0.0 # Min-coordinate + 2.65, 10.0 # Max-coordinate + 0.0, 0.0, 0.0, 0.16, 0.0, 0.0 # 1 transformation + 0.85, 0.04, -0.04, 0.85, 0.0, 1.6 # 2 transformation + 0.2, -0.26, 0.23, 0.22, 0.0, 1.6 # 3 transformation + -0.15, 0.28, 0.26, 0.24, 0.0, 0.44 # 4 transformation + """; + + StringBuilder actualContent = new StringBuilder(); + try (BufferedReader reader = Files.newBufferedReader(tempFile)) { + String line; + while ((line = reader.readLine()) != null) { + actualContent.append(line).append("\n"); + } + } + + assertEquals(expectedContent.trim(), actualContent.toString().trim()); + + Files.delete(tempFile); + } + + @Test + void testWriteValidJulia() throws IOException { + ChaosGameDescription description = new ChaosGameDescription( + new Vector2D(-1.6, -1.0), + new Vector2D(1.6, 1.0), + List.of(new JuliaTransform(new Complex(-0.70176, -0.3842), 1)) + ); + + Path tempFile = Files.createTempFile("chaosGame", ".txt"); + fileHandler.writeToFile(description, tempFile.toString()); + + String expectedContent = + """ + Julia # Type of transformation + -1.6, -1.0 # Min-coordinate + 1.6, 1.0 # Max-coordinate + -0.70176, -0.3842 # Real and imaginary part of the complex number + """; + + StringBuilder actualContent = new StringBuilder(); + try (BufferedReader reader = Files.newBufferedReader(tempFile)) { + String line; + while ((line = reader.readLine()) != null) { + actualContent.append(line).append("\n"); + } + } + + assertEquals(expectedContent.trim(), actualContent.toString().trim()); + + Files.delete(tempFile); + } + } + + @Nested + class SkipCommentsTests { + @Test + void testSkipCommentsWithComment() { + String line = "0.5, 0.0, 0.0, 0.5, 0.0, 0.0 # This is a comment"; + String result = fileHandler.skipComments(line); + assertEquals("0.5, 0.0, 0.0, 0.5, 0.0, 0.0", result); + } + + @Test + void testSkipCommentsWithoutComment() { + String line = "0.5, 0.0, 0.0, 0.5, 0.0, 0.0"; + String result = fileHandler.skipComments(line); + assertEquals(line, result); + } + + @Test + void testSkipCommentsEmptyLine() { + String line = "# This is a comment"; + String result = fileHandler.skipComments(line); + assertEquals("", result); + } + } + + @Nested + class SelectTransformationTests { + @Test + void testSelectAffineTransformation() { + String line = "0.5, 0.0, 0.0, 0.5, 0.0, 0.0"; + Transform2D transform = fileHandler.selectTransformation("Affine2D", line); + assertInstanceOf(AffineTransform2D.class, transform); + } + + @Test + void testSelectJuliaTransformation() { + String line = "-0.70176, -0.3842"; + Transform2D transform = fileHandler.selectTransformation("Julia", line); + assertInstanceOf(JuliaTransform.class, transform); + } + + @Test + void testSelectUnknownTransformation() { + String line = "0.5, 0.0, 0.0, 0.5, 0.0, 0.0"; + assertThrows(IllegalArgumentException.class, () -> + fileHandler.selectTransformation("Unknown", line), + "Unknown type of transformation should throw IllegalArgumentException"); + } + } + + @Nested + class ParseVectorTests { + @Test + void testParseValidVector() { + String line = "0.0, 1.0"; + Vector2D vector = fileHandler.parseVector(line); + assertEquals(new Vector2D(0.0, 1.0), vector); + } + + @Test + void testParseInvalidVector() { + String line = "invalid, data"; + assertThrows(NumberFormatException.class, () -> fileHandler.parseVector(line)); + } + + @Test + void testParseVectorWithComments() { + String line = "0.0, 1.0 # Comment"; + Vector2D vector = fileHandler.parseVector(line); + assertEquals(new Vector2D(0.0, 1.0), vector); + } + } + + @Nested + class ParseAffineTests { + @Test + void testParseValidAffine() { + String line = "0.5, 0.0, 0.0, 0.5, 0.0, 0.0"; + AffineTransform2D affine = (AffineTransform2D) fileHandler.parseAffine(line); + assertEquals(new Matrix2x2(0.5, 0.0, 0.0, 0.5), affine.matrix()); + assertEquals(new Vector2D(0.0, 0.0), affine.vector()); + } + + @Test + void testParseInvalidAffine() { + String line = "invalid, data, 0.0, 0.5, 0.0, 0.0, invalid here too"; + assertThrows(NumberFormatException.class, () -> fileHandler.parseAffine(line)); + } + + @Test + void testParseAffineWithComments() { + String line = "0.5, 0.0, 0.0, 0.5, 0.0, 0.0 # Comment"; + AffineTransform2D affine = (AffineTransform2D) fileHandler.parseAffine(line); + assertEquals(new Matrix2x2(0.5, 0.0, 0.0, 0.5), affine.matrix()); + assertEquals(new Vector2D(0.0, 0.0), affine.vector()); + } + } + + @Nested + class ParseJuliaTests { + @Test + void testParseValidJulia() { + String line = "-0.70176, -0.3842"; + JuliaTransform julia = (JuliaTransform) fileHandler.parseJulia(line); + assertEquals(new Complex(-0.70176, -0.3842), julia.getComplex()); + } + + @Test + void testParseInvalidJulia() { + String line = "invalid, data"; + assertThrows(NumberFormatException.class, () -> fileHandler.parseJulia(line)); + } + + @Test + void testParseJuliaWithComments() { + String line = "-0.70176, -0.3842 # Comment"; + JuliaTransform julia = (JuliaTransform) fileHandler.parseJulia(line); + assertEquals(new Complex(-0.70176, -0.3842), julia.getComplex()); + } + } + + @AfterAll + static void tearDown() { + fileHandler = null; + validAffineContent = null; + validJuliaContent = null; + invalidNumbersContent = null; + invalidTypeContent = null; + } +} \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/model/chaos/ChaosGameTest.java b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5d967a7f1718f0bdc92c934b2abb08212207de42 --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/chaos/ChaosGameTest.java @@ -0,0 +1,216 @@ +package org.example.chaosgame.model.chaos; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import org.example.chaosgame.controller.interfaces.Observer; +import org.example.chaosgame.model.linalg.Complex; +import org.example.chaosgame.model.linalg.Matrix2x2; +import org.example.chaosgame.model.linalg.Vector2D; +import org.example.chaosgame.model.transformations.AffineTransform2D; +import org.example.chaosgame.model.transformations.JuliaTransform; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class ChaosGameTest { + private static ChaosGameDescription juliaDescription; + private static ChaosGameDescription affineDescriptionWithProb; + private static ChaosGame instance; + + @BeforeEach + void init() { + juliaDescription = new ChaosGameDescription( + new Vector2D(-1.6, -1), + new Vector2D(1.6, 1), + List.of( + new JuliaTransform(new Complex(-0.70176, -0.3842), 1) + )); + + affineDescriptionWithProb = new ChaosGameDescription( + new Vector2D(-2.65, 0.0), + new Vector2D(2.65, 10.0), + List.of( + new AffineTransform2D(new Matrix2x2(0.0, 0.0, 0.0, 0.16), + new Vector2D(0.0, 0.0)), + new AffineTransform2D(new Matrix2x2(0.85, 0.04, -0.04, 0.85), + new Vector2D(0.0, 1.60)), + new AffineTransform2D(new Matrix2x2(0.20, -0.26, 0.23, 0.22), + new Vector2D(0.0, 1.60)), + new AffineTransform2D(new Matrix2x2(-0.15, 0.28, 0.26, 0.24), + new Vector2D(0.0, 0.44)) + ), List.of(2, 84, 7, 7)); + instance = ChaosGame.getInstance(juliaDescription, 500, 500); + instance.setChaosGameDescription(juliaDescription); + instance.setSteps(0); + instance.setTotalSteps(0); + } + + @Nested + class SingletonTests { + @Test + void testSingletonInstanceIsNull() { + assertNotNull(instance, "The singleton instance should not be null"); + } + + @Test + void testSingletonInstanceIsNotNull() { + ChaosGame instance2 = ChaosGame.getInstance(juliaDescription, 500, 500); + assertEquals(instance, instance2, "The singleton instance should be the same"); + } + } + + + @Nested + class GetterTests { + @Test + void getCanvas() { + assertNotNull(instance.getCanvas(), "The canvas should not be null"); + } + + @Test + void getDescription() { + assertEquals(juliaDescription, + instance.getDescription(), + "The description should be the same"); + } + + @Test + void getSteps() { + assertEquals(0, instance.getSteps(), "The steps should be 0"); + } + + @Test + void getTotalSteps() { + assertEquals(0, instance.getTotalSteps(), "The total steps should be 0"); + } + } + + @Nested + class TestSteps { + @Test + void setSteps() { + instance.setSteps(10); + assertEquals(10, instance.getSteps(), "The steps should be 10"); + } + + @Test + void setStepsNegative() { + assertThrows(IllegalArgumentException.class, () -> instance.setSteps(-10), + "The steps should be positive"); + } + + @Test + void setStepsTooLarge() { + assertThrows(IllegalArgumentException.class, () -> instance.setSteps(1000001), + "The steps should be less than 1000000"); + } + + @Test + void setTotalSteps() { + instance.setTotalSteps(10); + assertEquals(10, instance.getTotalSteps(), "The total steps should be 10"); + } + + @Test + void addTotalStepsNegative() { + assertThrows(IllegalArgumentException.class, () -> instance.addTotalSteps(-10), + "The steps should be positive"); + } + + @Test + void addTotalSteps() { + instance.addTotalSteps(10); + instance.addTotalSteps(10); + assertEquals(20, instance.getTotalSteps(), "The total steps should be 10"); + } + + @Test + void resetTotalSteps() { + instance.addTotalSteps(10); + instance.resetTotalSteps(); + assertEquals(0, instance.getTotalSteps(), "The total steps should be 0"); + } + + @Test + void runSteps() { + instance.setSteps(10); + instance.runSteps(); + assertEquals(10, instance.getSteps(), "The total steps should be 10"); + } + + @Test + void runStepsUniform() { + instance = ChaosGame.getInstance(juliaDescription, 500, 500); + instance.setSteps(10); + assertEquals(10, instance.getSteps()); + } + + @Test + void runStepsWithProbabilities() { + instance.setChaosGameDescription(affineDescriptionWithProb); + instance.setSteps(10); + instance.runSteps(); + assertEquals(10, instance.getSteps()); + } + } + + @Nested + class SetDescription { + @Test + void setChaosGameDescription() { + instance.setChaosGameDescription(affineDescriptionWithProb); + assertEquals(affineDescriptionWithProb, instance.getDescription(), + "The description should be the same"); + assertEquals(0, instance.getTotalSteps(), + "The total steps should be 0"); + assertEquals(500, instance.getCanvas().getWidth(), + "The width should be 500"); + assertEquals(500, instance.getCanvas().getHeight(), + "The height should be 500"); + } + + @Test + void setChaosGameDescriptionNull() { + assertThrows(IllegalArgumentException.class, () -> instance.setChaosGameDescription(null), + "The description should not be null"); + } + } + + @Test + void setChaosCanvas() { + instance.setChaosCanvas(new Vector2D(-1.6, -1), new Vector2D(1.6, 1)); + assertEquals(new Vector2D(-1.6, -1), + instance.getDescription().getMinCoords(), + "The min coords should be the same"); + assertEquals(new Vector2D(1.6, 1), + instance.getDescription().getMaxCoords(), + "The max coords should be the same"); + } + + @Test + void registerAndNotifyObservers() { + Observer observer = () -> assertTrue(true, "Observer should be notified"); + instance.registerObserver(observer); + instance.notifyObservers(); + } + + @Test + void removeObserver() { + Observer observer = () -> fail("Observer should not be notified"); + instance.registerObserver(observer); + instance.removeObserver(observer); + instance.notifyObservers(); + } + + @AfterEach + void tearDown() { + instance = null; + } + +} \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/model/chaos/ExploreGameTest.java b/src/test/java/org/example/chaosgame/model/chaos/ExploreGameTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ab56bbba654425e121d2513449db4646e630adce --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/chaos/ExploreGameTest.java @@ -0,0 +1,115 @@ +package org.example.chaosgame.model.chaos; + +import org.example.chaosgame.model.linalg.Complex; +import org.example.chaosgame.model.linalg.Vector2D; +import org.example.chaosgame.model.transformations.ExploreJulia; +import org.example.chaosgame.model.transformations.Transform2D; +import org.example.chaosgame.controller.interfaces.Observer; +import org.junit.jupiter.api.*; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ExploreGameTest { + private ExploreGame exploreGame; + private ChaosGameDescription description; + private static final int WIDTH = 100; + private static final int HEIGHT = 100; + private List<Transform2D> transforms; + + private ExploreJulia exploreJulia; + @BeforeEach + void setUp() { + Vector2D minCoords = new Vector2D(-1.6, -1); + Vector2D maxCoords = new Vector2D(1.6, 1); + transforms = new ArrayList<>(); + exploreJulia = new ExploreJulia(new Complex(0, 1)); + transforms.add(exploreJulia); + description = new ChaosGameDescription(minCoords, maxCoords, transforms); + exploreGame = new ExploreGame(description, WIDTH, HEIGHT); + } + + @AfterEach + void tearDown() { + transforms = null; + exploreJulia = null; + exploreGame = null; + description = null; + } + + @Test + void testSetExploreGame() { + Vector2D newMinCoords = new Vector2D(-1.6, -1); + Vector2D newMaxCoords = new Vector2D(1.6, 1); + List<Transform2D> newTransforms = new ArrayList<>(); + newTransforms.add(new ExploreJulia(new Complex(1, 1))); + ChaosGameDescription newDescription = new ChaosGameDescription(newMinCoords, newMaxCoords, newTransforms); + + exploreGame.setExploreGame(newDescription, 200, 200); + + assertEquals(newDescription, exploreGame.getDescription()); + ChaosCanvas canvas = exploreGame.getCanvas(); + assertEquals(200, canvas.getWidth()); + assertEquals(200, canvas.getHeight()); + assertEquals(newMinCoords, canvas.getMinCoords()); + assertEquals(newMaxCoords, canvas.getMaxCoords()); + } + + @Test + void testGetCanvas() { + ChaosCanvas canvas = exploreGame.getCanvas(); + assertNotNull(canvas); + assertEquals(WIDTH, canvas.getWidth()); + assertEquals(HEIGHT, canvas.getHeight()); + } + + @Test + void testGetDescription() { + assertEquals(description, exploreGame.getDescription()); + } + + @Test + void testSetChaosCanvas() { + Vector2D newMinCoords = new Vector2D(-2, -2); + Vector2D newMaxCoords = new Vector2D(2, 2); + exploreGame.setChaosCanvas(newMinCoords, newMaxCoords, 300, 300); + + ChaosCanvas canvas = exploreGame.getCanvas(); + assertEquals(300, canvas.getWidth()); + assertEquals(300, canvas.getHeight()); + assertEquals(newMinCoords, canvas.getMinCoords()); + assertEquals(newMaxCoords, canvas.getMaxCoords()); + } + + @Test + void testExploreFractals() { + + exploreGame.exploreFractals(); + + ChaosCanvas canvas = exploreGame.getCanvas(); + for (int i = 0; i < canvas.getHeight(); i++) { + for (int j = 0; j < canvas.getWidth(); j++) { + assertTrue(canvas.getPixel(new Vector2D(i, j)) >= 0); + } + } + } + + @Test + void testRemoveObserver() { + Observer observer = () -> fail("Observer should not be notified"); + exploreGame.registerObserver(observer); + exploreGame.removeObserver(observer); +// exploreGame.notifyObservers(); + } + + @Test + void testRegisterAndNotifyObservers() { + Observer observer1 = () -> assertTrue(true,"Observer should be notified"); + Observer observer2 = () -> assertTrue(true,"Observer should be notified"); + exploreGame.registerObserver(observer1); + exploreGame.registerObserver(observer2); + exploreGame.notifyObservers(); + } +} diff --git a/src/test/java/org/example/chaosgame/linalg/ComplexTest.java b/src/test/java/org/example/chaosgame/model/linalg/ComplexTest.java similarity index 71% rename from src/test/java/org/example/chaosgame/linalg/ComplexTest.java rename to src/test/java/org/example/chaosgame/model/linalg/ComplexTest.java index 6297a60712700134098be00b67f63ff35c708808..5cb701a87fba88d8556f81947b3abaa1fb582555 100644 --- a/src/test/java/org/example/chaosgame/linalg/ComplexTest.java +++ b/src/test/java/org/example/chaosgame/model/linalg/ComplexTest.java @@ -1,4 +1,4 @@ -package org.example.chaosgame.linalg; +package org.example.chaosgame.model.linalg; import org.example.chaosgame.model.linalg.Complex; import org.junit.jupiter.api.*; @@ -30,8 +30,8 @@ class ComplexTest { @DisplayName("Test sqrt should work") void sqrt() { Complex newComplex = complex.sqrt(x, y); - assertEquals(0.5061178531536732, newComplex.getX()); //r-part is based on the example in the task description. - assertEquals(-0.3951648786024424, newComplex.getY()); //i-part is based on the example in the task description. + assertEquals(Math.abs(0.5061178531536732), Math.abs(newComplex.getX())); //r-part is based on the example in the task description. + assertEquals(Math.abs(-0.3951648786024424), Math.abs(newComplex.getY())); //i-part is based on the example in the task description. } @Test diff --git a/src/test/java/org/example/chaosgame/linalg/Matrix2x2Test.java b/src/test/java/org/example/chaosgame/model/linalg/Matrix2x2Test.java similarity index 97% rename from src/test/java/org/example/chaosgame/linalg/Matrix2x2Test.java rename to src/test/java/org/example/chaosgame/model/linalg/Matrix2x2Test.java index 11ea00fbe6b85631de2e220b425df2ce2aa969f7..bb210cb61a55047bdd3e67f1d9deb27190bd0a40 100644 --- a/src/test/java/org/example/chaosgame/linalg/Matrix2x2Test.java +++ b/src/test/java/org/example/chaosgame/model/linalg/Matrix2x2Test.java @@ -1,4 +1,4 @@ -package org.example.chaosgame.linalg; +package org.example.chaosgame.model.linalg; import org.example.chaosgame.model.linalg.Matrix2x2; import org.example.chaosgame.model.linalg.Vector2D; diff --git a/src/test/java/org/example/chaosgame/model/linalg/Vector2DTest.java b/src/test/java/org/example/chaosgame/model/linalg/Vector2DTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3d432956948a6ea1985ddd1c3c3e02d6d9fd85a2 --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/linalg/Vector2DTest.java @@ -0,0 +1,149 @@ +package org.example.chaosgame.model.linalg; + +import org.example.chaosgame.model.linalg.Vector2D; +import org.junit.jupiter.api.*; + +import static org.junit.jupiter.api.Assertions.*; + +class Vector2DTest { + private double x; + private double y; + private Vector2D vector; + private Vector2D otherVector; + + @BeforeEach + void setUp() { + x = 1.0; + y = 2.0; + vector = new Vector2D(x, y); + otherVector = new Vector2D(1.0, 1.0); + } + + @AfterEach + void tearDown() { + vector = null; + otherVector = null; + } + + @Nested + @DisplayName("Test getX and getY") + class TestGetters { + @Test + @DisplayName("Test getX") + void getX() { + assertEquals(x, vector.getX()); + } + + @Test + @DisplayName("Test getY") + void getY() { + assertEquals(y, vector.getY()); + } + } + + @Nested + @DisplayName("Test add and subtract") + class TestAddAndSubtract { + @Test + @DisplayName("Test add should work") + void add() { + Vector2D w = vector.add(otherVector); + assertEquals(2.0, w.getX()); + assertEquals(3.0, w.getY()); + } + + @Test + @DisplayName("Test add should not work") + void addFail() { + Vector2D w = vector.add(otherVector); + assertNotEquals(1.0, w.getX()); + assertNotEquals(2.0, w.getY()); + } + + @Test + @DisplayName("Test subtract should work") + void subtract() { + Vector2D w = vector.subtract(otherVector); + assertEquals(0.0, w.getX()); + assertEquals(1.0, w.getY()); + } + + @Test + @DisplayName("Test subtract should not work") + void subtractFail() { + Vector2D w = vector.subtract(otherVector); + assertNotEquals(1.0, w.getX()); + assertNotEquals(2.0, w.getY()); + } + } + + @Nested + @DisplayName("Test scale and multiply") + class TestScaleAndMultiply { + @Test + @DisplayName("Test scale should work") + void scale() { + Vector2D w = vector.scale(2.0); + assertEquals(2.0, w.getX()); + assertEquals(4.0, w.getY()); + } + + @Test + @DisplayName("Test scale should not work") + void scaleFail() { + Vector2D w = vector.scale(2.0); + assertNotEquals(1.0, w.getX()); + assertNotEquals(2.0, w.getY()); + } + + @Test + @DisplayName("Test multiply should work") + void multiply() { + Vector2D w = vector.multiply(otherVector); + assertEquals(1.0, w.getX()); + assertEquals(2.0, w.getY()); + } + + @Test + @DisplayName("Test multiply should not work") + void multiplyFail() { + Vector2D w = vector.multiply(otherVector); + assertNotEquals(2.0, w.getX()); + assertNotEquals(4.0, w.getY()); + } + } + + @Nested + @DisplayName("Test divide and length") + class TestDivide { + @Test + @DisplayName("Test divide should work") + void divide() { + Vector2D w = vector.divide(otherVector); + assertEquals(1.0, w.getX()); + assertEquals(2.0, w.getY()); + } + + @Test + @DisplayName("Test divide should not work") + void divideFail() { + Vector2D w = vector.divide(otherVector); + assertNotEquals(2.0, w.getX()); + assertNotEquals(4.0, w.getY()); + } + + @Test + @DisplayName("Test length should work") + void length() { + double l = vector.lengthSq(); + assertEquals(5.0, l); + } + + @Test + @DisplayName("Test length should not work") + void lengthFail() { + double l = vector.lengthSq(); + assertNotEquals(2.0, l); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/example/chaosgame/transformations/AffineTransform2DTest.java b/src/test/java/org/example/chaosgame/model/transformations/AffineTransform2DTest.java similarity index 92% rename from src/test/java/org/example/chaosgame/transformations/AffineTransform2DTest.java rename to src/test/java/org/example/chaosgame/model/transformations/AffineTransform2DTest.java index e19e189d0382ffc5d6d5feac9755bb58b9826600..3ac169cfe2adc38c08c7f344a1d8fad92fb9248b 100644 --- a/src/test/java/org/example/chaosgame/transformations/AffineTransform2DTest.java +++ b/src/test/java/org/example/chaosgame/model/transformations/AffineTransform2DTest.java @@ -1,4 +1,4 @@ -package org.example.chaosgame.transformations; +package org.example.chaosgame.model.transformations; import org.example.chaosgame.model.linalg.Matrix2x2; import org.example.chaosgame.model.linalg.Vector2D; diff --git a/src/test/java/org/example/chaosgame/model/transformations/ExploreJuliaTest.java b/src/test/java/org/example/chaosgame/model/transformations/ExploreJuliaTest.java new file mode 100644 index 0000000000000000000000000000000000000000..55900355656ec5fd5f0d02927ff67eb57adb5392 --- /dev/null +++ b/src/test/java/org/example/chaosgame/model/transformations/ExploreJuliaTest.java @@ -0,0 +1,48 @@ +package org.example.chaosgame.model.transformations; + +import org.example.chaosgame.model.linalg.Complex; +import org.example.chaosgame.model.linalg.Vector2D; +import org.junit.jupiter.api.*; + +import static org.junit.jupiter.api.Assertions.*; + +class ExploreJuliaTest { + private ExploreJulia exploreJulia; + private Complex complexPoint; + + @BeforeEach + void setUp() { + complexPoint = new Complex(0.355, 0.355); + exploreJulia = new ExploreJulia(complexPoint); + } + + @AfterEach + void tearDown() { + complexPoint = null; + exploreJulia = null; + } + + @Test + void testGetComplex() { + assertEquals(complexPoint, exploreJulia.getComplex()); + } + @Nested + @DisplayName("Test transforms") + class TestTransforms { + @Test + @DisplayName("Test positive transform") + void testTransform() { + Vector2D point = new Vector2D(0.5, 0.5); + Vector2D transformedPoint = exploreJulia.transform(point); + assertEquals(new Vector2D(0.355, 0.855), transformedPoint); + } + + @Test + @DisplayName("Test negative transform") + void testTransformFail() { + Vector2D point = new Vector2D(-0.5, -0.5); + Vector2D transformedPoint = exploreJulia.transform(point); + assertNotEquals(new Vector2D(0.355, -0.145), transformedPoint); + } + } +} diff --git a/src/test/java/org/example/chaosgame/transformations/JuliaTransformTest.java b/src/test/java/org/example/chaosgame/model/transformations/JuliaTransformTest.java similarity index 73% rename from src/test/java/org/example/chaosgame/transformations/JuliaTransformTest.java rename to src/test/java/org/example/chaosgame/model/transformations/JuliaTransformTest.java index f1807cd770b536a5a45f7fda20c18377d451ed0d..ef90b6a8acdb156d34e38aa36f7cd84f88f5257b 100644 --- a/src/test/java/org/example/chaosgame/transformations/JuliaTransformTest.java +++ b/src/test/java/org/example/chaosgame/model/transformations/JuliaTransformTest.java @@ -1,4 +1,4 @@ -package org.example.chaosgame.transformations; +package org.example.chaosgame.model.transformations; import org.example.chaosgame.model.linalg.Complex; import org.example.chaosgame.model.linalg.Vector2D; @@ -15,8 +15,7 @@ class JuliaTransformTest { @Test void transform() { Vector2D testJulia = juliaTransform.transform(new Vector2D(0.4, 0.2)); - assertEquals(0.5061178531536732, testJulia.getX()); - assertEquals(-0.3951648786024423, testJulia.getY()); - + assertEquals(Math.abs(0.5061178531536732), Math.abs(testJulia.getX())); + assertEquals(Math.abs(-0.3951648786024423), Math.abs(testJulia.getY())); } } \ No newline at end of file