diff --git a/src/main/java/org/example/chaosgame/controller/ChaosGameController.java b/src/main/java/org/example/chaosgame/controller/ChaosGameController.java index e47d5e13030dddd4b1030db77b4440a072ba1fb6..a6be1570bb49bc3fc5593c0f061b015e2ca0d77a 100644 --- a/src/main/java/org/example/chaosgame/controller/ChaosGameController.java +++ b/src/main/java/org/example/chaosgame/controller/ChaosGameController.java @@ -12,7 +12,6 @@ public class ChaosGameController implements Observer{ public ChaosGameController(ChaosGame chaosGame) { this.chaosGame = chaosGame; - chaosGame.registerObserver(this); } @Override 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 19101023bdabacdb662f49c42459a9dc025ba611..c528be113cbbda347b24ae01070f945fc18d9a31 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ChaosGame.java +++ b/src/main/java/org/example/chaosgame/model/chaos/ChaosGame.java @@ -20,7 +20,7 @@ import org.example.chaosgame.model.linalg.Vector2D; public class ChaosGame implements Subject { private final ChaosCanvas canvas; - private final ChaosGameDescription description; + private ChaosGameDescription description; private Vector2D currentPoint = new Vector2D(0.0, 0.0); @@ -98,6 +98,11 @@ public class ChaosGame implements Subject { } } + public void setChaosGameDescription(ChaosGameDescription description) { + this.description = description; + notifyObservers(); + } + @Override public void registerObserver(Observer observer) { observers.add(observer); diff --git a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactory.java b/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactory.java index cb1f822017780742f1d7c23102b808b32be8b536..6e3d1a0bba1d1baf4fc9f231cbc44ca9f0c838ee 100644 --- a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactory.java +++ b/src/main/java/org/example/chaosgame/model/chaos/ChaosGameDescriptionFactory.java @@ -21,13 +21,12 @@ public class ChaosGameDescriptionFactory { * Returns a ChaosGameDescription object based on the description and complex number. * * @param description The description of the chaos game - * @param c The complex number for the Julia set * @return A ChaosGameDescription object */ - public static ChaosGameDescription get(String description, Complex c) { + public static ChaosGameDescription get(String description) { ChaosGameType type = ChaosGameType.valueOf(description.toUpperCase().trim()); return switch (type) { - case JULIA -> createJulia(c); + case JULIA -> createJulia(); case BARNSLEY-> createBarnsley(); case SIERPINSKI -> createSierpinski(); }; @@ -36,15 +35,14 @@ public class ChaosGameDescriptionFactory { /** * Creates a ChaosGameDescription object for the Julia set. * - * @param c The complex number for the Julia set * @return A ChaosGameDescription object */ - private static ChaosGameDescription createJulia(Complex c) { + private static ChaosGameDescription createJulia() { return new ChaosGameDescription( new Vector2D(-1.6, -1), new Vector2D(1.6, 1), List.of( - new JuliaTransform(c, 1) + new JuliaTransform(new Complex(-0.70176, -0.3842), 1) ) ); } diff --git a/src/main/java/org/example/chaosgame/model/chaos/ChaosGameType.java b/src/main/java/org/example/chaosgame/model/chaos/ChaosGameType.java new file mode 100644 index 0000000000000000000000000000000000000000..af663667319e60864671ed5d3b4648d6419a3100 --- /dev/null +++ b/src/main/java/org/example/chaosgame/model/chaos/ChaosGameType.java @@ -0,0 +1,8 @@ +package org.example.chaosgame.model.chaos; + +public enum ChaosGameType { + JULIA, + BARNSLEY, + SIERPINSKI, + MAKE_YOUR_OWN +} diff --git a/src/main/java/org/example/chaosgame/view/ChaosPage.java b/src/main/java/org/example/chaosgame/view/ChaosPage.java index 3fdcf472af1274e02635c4aed526c2e2fef23ee7..c3509141a5c496b2689a028e6a07b2f9b12a5907 100644 --- a/src/main/java/org/example/chaosgame/view/ChaosPage.java +++ b/src/main/java/org/example/chaosgame/view/ChaosPage.java @@ -22,11 +22,10 @@ import java.io.BufferedReader; import java.io.FileReader; public class ChaosPage { - ChaosGameController chaosGameController; + private ChaosGameController chaosGameController; private final StackPane chaosContent; private ChaosGame chaosGame; private ChaosCanvas chaosCanvas; - private Complex c = new Complex(-0.70176, -0.3842); private final Button runStepsButton = new Button("Run Steps"); private final Canvas canvas = new Canvas(1200, 800); private final GraphicsContext gc ; @@ -39,7 +38,7 @@ public class ChaosPage { gc = canvas.getGraphicsContext2D(); updateChaosGame("Julia"); chaosCanvas = chaosGame.getCanvas(); - + chaosGame.registerObserver(chaosGameController); TextField stepsField = new TextField(); @@ -52,18 +51,18 @@ public class ChaosPage { contextMenu.getItems().addAll("Julia", "Sierpinski", "Barnsley", "Make your own"); - contextMenu.setOnAction(e -> { + contextMenu.setOnAction(event -> { String selectedGame = contextMenu.getValue(); if(selectedGame.equals("Make your own")) { - chaosGame = new ChaosGame(ChaosGameDescriptionFactory.get("Julia", c), 1200, 800); + chaosGame = new ChaosGame(ChaosGameDescriptionFactory.get("Julia"), 1200, 800); } else { updateChaosGame(selectedGame); } updateCanvas(); }); - runStepsButton.setOnAction(e5 -> { + runStepsButton.setOnAction(event -> { if (!stepsField.getText().isEmpty()) { try { chaosGame.runSteps(Integer.parseInt(stepsField.getText())); @@ -132,11 +131,10 @@ openFileButton.setOnAction(e -> { gc.drawImage(offScreenImage, 0, 0, cellWidth * chaosCanvas.getWidth(), cellHeight * chaosCanvas.getHeight()); long end = System.currentTimeMillis(); - System.out.println("Time taken to display: " + (end - start) + "ms"); } private void updateChaosGame(String chaosGameType) { - chaosGame = new ChaosGame(ChaosGameDescriptionFactory.get(chaosGameType, c), 1200, 800); + chaosGame = new ChaosGame(ChaosGameDescriptionFactory.get(chaosGameType), 1200, 800); chaosGameController = new ChaosGameController(chaosGame); chaosCanvas = chaosGame.getCanvas(); gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight()); diff --git a/src/main/java/org/example/chaosgame/view/ExplorePage.java b/src/main/java/org/example/chaosgame/view/ExplorePage.java index 3aae4b46684b97979d13b53dc3f527c5f80554ee..e04221ba807a8331aea5e11b022cc04f7fe3909c 100644 --- a/src/main/java/org/example/chaosgame/view/ExplorePage.java +++ b/src/main/java/org/example/chaosgame/view/ExplorePage.java @@ -9,9 +9,11 @@ import javafx.scene.control.Button; import javafx.scene.image.PixelWriter; import javafx.scene.image.WritableImage; import javafx.scene.input.MouseEvent; +import javafx.scene.input.ZoomEvent; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; +import org.example.chaosgame.controller.ChaosGameController; import org.example.chaosgame.model.chaos.ChaosCanvas; import org.example.chaosgame.model.chaos.ChaosGameDescription; import org.example.chaosgame.model.chaos.ExploreGame; @@ -55,9 +57,11 @@ public class ExplorePage { exploreContent = new StackPane(); - exploreGame = new ExploreGame(description, 1200, 800); + exploreGame = new ExploreGame(description, 1500, 1000); chaosCanvas = exploreGame.getCanvas(); - canvas = new Canvas(chaosCanvas.getWidth(), chaosCanvas.getHeight()); + canvas = new Canvas(1200, 800); + canvas.widthProperty().bind(exploreContent.widthProperty()); + canvas.heightProperty().bind(exploreContent.heightProperty()); canvas.requestFocus(); offScreenImage = new WritableImage(chaosCanvas.getWidth(), chaosCanvas.getHeight()); pixelWriter = offScreenImage.getPixelWriter(); @@ -86,7 +90,7 @@ public class ExplorePage { Vector2D newMinCoords = fractalCenter.subtract(fractalCenter.subtract(description.getMinCoords()).scale(1 / scaleFactor)); Vector2D newMaxCoords = fractalCenter.add(description.getMaxCoords().subtract(fractalCenter).scale(1 / scaleFactor)); description = new ChaosGameDescription(newMinCoords, newMaxCoords, trans); - exploreGame = new ExploreGame(description, 1200, 800); + exploreGame = new ExploreGame(description, 1500, 1000); exploreGame.exploreFractals(); updateCanvas(); } @@ -106,13 +110,14 @@ public class ExplorePage { AnimationTimer zoomOutTimer = new AnimationTimer() { @Override public void handle(long now) { + double scaleFactor = 1.0 / 1.05; Vector2D canvasCenter = new Vector2D(canvas.getWidth() / 2, canvas.getHeight() / 2); Vector2D fractalCenter = chaosCanvas.transformIndicesToCoords((int)canvasCenter.getX(), (int)canvasCenter.getY()); Vector2D newMinCoords = fractalCenter.subtract(fractalCenter.subtract(description.getMinCoords()).scale(1 / scaleFactor)); Vector2D newMaxCoords = fractalCenter.add(description.getMaxCoords().subtract(fractalCenter).scale(1 / scaleFactor)); description = new ChaosGameDescription(newMinCoords, newMaxCoords, trans); - exploreGame = new ExploreGame(description, 1200, 800); + exploreGame = new ExploreGame(description, 1500,1000); exploreGame.exploreFractals(); updateCanvas(); } @@ -128,7 +133,35 @@ public class ExplorePage { } }); + exploreContent.setOnScroll(event -> { + double scaleFactor = (event.getDeltaY() > 0) ? (1.0 / 1.01) : 1.01; + double mouseX = event.getX() - (double) chaosCanvas.getWidth() / 2; + double mouseY = event.getY() - (double) chaosCanvas.getHeight() / 2; + + + canvas.setScaleX(canvas.getScaleX() * scaleFactor); + canvas.setScaleY(canvas.getScaleX() * scaleFactor); + + double newTranslateX = (mouseX - canvas.getTranslateX()) * (scaleFactor - 1); + double newTranslateY = (mouseY - canvas.getTranslateY()) * (scaleFactor - 1); + canvas.setTranslateX(canvas.getTranslateX() - newTranslateX); + canvas.setTranslateY(canvas.getTranslateY() - newTranslateY); + }); + + exploreContent.setOnScrollFinished(event -> { + double mouseX = event.getX() - (double) chaosCanvas.getWidth() / 2; + double mouseY = - (event.getY() - (double) chaosCanvas.getHeight() / 2); +// System.out.println("Mouse position: " + mouseX + ", " + mouseY); + Vector2D fractalCenter = chaosCanvas.transformIndicesToCoords((int) mouseX, (int) mouseY); + Vector2D newMinCoords = fractalCenter.subtract(fractalCenter.subtract(description.getMinCoords()).scale(1 / canvas.getScaleX())); + Vector2D newMaxCoords = fractalCenter.add(description.getMaxCoords().subtract(fractalCenter).scale(1 / canvas.getScaleX())); + description = new ChaosGameDescription(newMinCoords, newMaxCoords, trans); + exploreGame = new ExploreGame(description, 1500, 1000); + exploreGame.exploreFractals(); + updateCanvas(); +// System.out.println("Scroll finished at: " + event.getX() + ", " + event.getY()); + }); VBox buttons = new VBox(zoomInButton, zoomOutButton, removeImage); buttons.setAlignment(Pos.CENTER_RIGHT); @@ -136,17 +169,16 @@ public class ExplorePage { exploreContent.getChildren().addAll(canvas, buttons); - Platform.runLater(() -> { - buttons.setLayoutX(canvas.getWidth() - buttons.getWidth()); - buttons.setLayoutY(0); - }); exploreContent.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { try { - dragStart = new Vector2D(event.getX(), event.getY()); - double mouseX = event.getX() - canvas.getLayoutX(); - double mouseY = event.getY() - canvas.getLayoutY(); + double mouseX = event.getX(); + double mouseY = event.getY(); + dragStart = new Vector2D(mouseX, mouseY); + + + dragStartTemp = new Vector2D(mouseX, canvas.getHeight() - mouseY); // initialMousePosition = new Vector2D(mouseX, canvas.getHeight() - mouseY); } catch (Exception e) { @@ -155,23 +187,6 @@ public class ExplorePage { }); exploreContent.addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> { -// Vector2D currentMousePosition = new Vector2D(event.getX(),canvas.getHeight() - event.getY()); -// Vector2D dragDistance = currentMousePosition.subtract(initialMousePosition); -// -// // Adjust the drag distance based on the zoom level -//// Convert the drag distance from canvas coordinates to fractal coordinates -// 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.setMinCoords(newMinCoords); -// description.setMaxCoords(newMaxCoords); -// exploreGame = new ExploreGame(description, 1200, 800); -// exploreGame.exploreFractals(); -// updateCanvas(); -// -// initialMousePosition = currentMousePosition; Vector2D dragEnd = new Vector2D(event.getX(), event.getY()); dragDistance = dragEnd.subtract(dragStart); @@ -181,27 +196,20 @@ exploreContent.addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> { dragStart = dragEnd; - - - }); exploreContent.setOnMouseReleased(event -> { dragDistance = new Vector2D(event.getX(), canvas.getHeight() - event.getY()).subtract(dragStartTemp); // Reset the position where the drag started Vector2D fractalRange = description.getMaxCoords().subtract(description.getMinCoords()); - System.out.println("drag distance: " + dragDistance.getX() + ", " + dragDistance.getY()); Vector2D adjustedDragDistance = dragDistance.multiply(fractalRange).divide(new Vector2D(canvas.getWidth(), canvas.getHeight())); - System.out.println("Adjusted drag distance: " + adjustedDragDistance.getX() + ", " + adjustedDragDistance.getY()); Vector2D newMinCoords = description.getMinCoords().subtract(adjustedDragDistance); Vector2D newMaxCoords = description.getMaxCoords().subtract(adjustedDragDistance); description.setMinCoords(newMinCoords); description.setMaxCoords(newMaxCoords); - exploreGame = new ExploreGame(description, 1200, 800); + exploreGame = new ExploreGame(description, 1500, 1000); exploreGame.exploreFractals(); updateCanvas(); - System.out.println("Mouse released"); - dragStart = null; canvas.setTranslateX(0); canvas.setTranslateY(0); @@ -245,9 +253,9 @@ exploreContent.addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> { for (int i = 0; i < chaosCanvas.getHeight(); i++) { for (int j = 0; j < chaosCanvas.getWidth(); j++) { double color = Math.min(canvasArray[i][j] * 3, 255); -// if (color >= 0 && color <= 255) { + if (color >= 0 && color <= 255) { pixelWriter.setColor(j, i, Color.rgb((int) color, 0, 0)); -// } + } } } @@ -256,6 +264,6 @@ exploreContent.addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> { gc.drawImage(offScreenImage, 0, 0, cellWidth * chaosCanvas.getWidth(), cellHeight * chaosCanvas.getHeight()); long end = System.currentTimeMillis(); - System.out.println("Time taken to display: " + (end - start) + "ms"); +// System.out.println("Time taken to display: " + (end - start) + "ms"); } } diff --git a/src/main/resources/global.css b/src/main/resources/global.css index 3fd87f3401ac03967294d726f30ba8404445c09d..742f78978783fa0e9b27da8a4379d79fd1ad8b31 100644 --- a/src/main/resources/global.css +++ b/src/main/resources/global.css @@ -7,16 +7,17 @@ -fx-border-color: black; -fx-border-width: 2; -fx-max-height: 50; + -fx-background-insets: 1; } .combo-box { - -fx-text-fill: white; -fx-font: 22 arial; -fx-background-color: #0d2d3a; -fx-border-radius: 20; -fx-background-radius: 20; -fx-border-color: black; -fx-border-width: 2; + -fx-background-insets: 1; } .button:hover { @@ -28,5 +29,7 @@ } .combo-box .indexed-cell:selected { - -fx-text-fill: WHITE; -} \ No newline at end of file + -fx-text-fill: white; + -fx-font: 22 arial; + -fx-background-radius: 20; +}