Skip to content
Snippets Groups Projects
Commit 88e3ee10 authored by Edvard Berdal Eek's avatar Edvard Berdal Eek
Browse files

Add open from file button in ChaosGame

Add button that when pressed opens a FileChooser that filters through and
only allows to open .txt files. The game is updated if a correctly formatted
text file is selected. In addition, the drag and move event in ExplorePage
is improved to not render through entire drag movement. Now the image only renders
when the user releases the image.
parent b45e774b
No related branches found
No related tags found
1 merge request!14Add open from file button in ChaosGame
Pipeline #288205 passed
......@@ -6,10 +6,7 @@ import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import org.example.chaosgame.controller.MainController;
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.view.ChaosPage;
import org.example.chaosgame.view.ExplorePage;
......
package org.example.chaosgame.controller;
import javafx.scene.layout.StackPane;
import org.example.chaosgame.model.chaos.ChaosGame;
import org.example.chaosgame.view.ChaosPage;
public class MainController implements Observer{
public class ChaosGameController implements Observer{
private final ChaosGame chaosGame;
private final ChaosPage chaosPage;
// private final ChaosPage chaosPage;
public MainController(ChaosGame chaosGame) {
public ChaosGameController(ChaosGame chaosGame) {
this.chaosGame = chaosGame;
this.chaosPage = new ChaosPage();
chaosGame.registerObserver(this);
}
@Override
public void update() {
chaosPage.updateCanvas();
// chaosPage.updateCanvas();
System.out.println("ChaosGameController.update");
}
}
package org.example.chaosgame.controller;
public interface ChaosGameSubject {
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
......
......@@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.example.chaosgame.controller.ChaosGameSubject;
import org.example.chaosgame.controller.Subject;
import org.example.chaosgame.controller.Observer;
import org.example.chaosgame.model.linalg.Vector2D;
......@@ -17,7 +17,7 @@ import org.example.chaosgame.model.linalg.Vector2D;
* The new point is then drawn on the canvas.
* This process is repeated a selected amount of steps.
*/
public class ChaosGame implements ChaosGameSubject {
public class ChaosGame implements Subject {
private final ChaosCanvas canvas;
private final ChaosGameDescription description;
......
......@@ -11,8 +11,8 @@ import org.example.chaosgame.model.transformations.Transform2D;
* and a list of transformations to apply to the points.
*/
public class ChaosGameDescription {
private final Vector2D minCoords;
private final Vector2D maxCoords;
private Vector2D minCoords;
private Vector2D maxCoords;
private final List<Transform2D> transforms;
private final List<Integer> probabilities;
......@@ -50,6 +50,13 @@ public class ChaosGameDescription {
return maxCoords;
}
public void setMinCoords(Vector2D minCoords) {
this.minCoords = minCoords;
}
public void setMaxCoords(Vector2D maxCoords) {
this.maxCoords = maxCoords;
}
public List<Transform2D> getTransforms() {
return transforms;
}
......
......@@ -53,23 +53,7 @@ public class ExploreGame{
}
});
// for (int y = 0; y < canvas.getHeight(); y++) {
// for (int x = 0; x < canvas.getWidth(); x++) {
// int iter = 0;
// currentPoint = canvas.transformIndicesToCoords(x, y);
// Vector2D tempPoint = currentPoint;
// while (iter < MAX_ITER && tempPoint.lengthSQ() < 4){
// tempPoint = description.getTransforms().getFirst().transform(tempPoint);
// iter++;
// }
// double abs = Math.sqrt(tempPoint.lengthSQ());
// double smooth = iter - Math.log(Math.log(abs)) / Math.log(2);
//
// canvas.putPixel(x, y, smooth);
//
// }
//
// }
long end = System.currentTimeMillis();
System.out.println("Time taken: " + (end - start) + "ms");
}
......
......@@ -12,28 +12,34 @@ import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import org.example.chaosgame.model.chaos.ChaosCanvas;
import org.example.chaosgame.model.chaos.ChaosGame;
import org.example.chaosgame.model.chaos.ChaosGameDescriptionFactory;
import org.example.chaosgame.controller.ChaosGameController;
import org.example.chaosgame.model.chaos.*;
import org.example.chaosgame.model.linalg.Complex;
import java.io.File;
import java.io.IOException;
import javafx.stage.FileChooser;
import java.io.BufferedReader;
import java.io.FileReader;
public class ChaosPage {
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;
private final GraphicsContext gc;
private final Canvas canvas = new Canvas(1200, 800);
private final GraphicsContext gc ;
private final Label errorLabel = new Label("Invalid input. Please enter a valid number.");
private final VBox runStepsBox = new VBox();
public ChaosPage() {
chaosContent = new StackPane();
gc = canvas.getGraphicsContext2D();
updateChaosGame("Julia");
chaosCanvas = chaosGame.getCanvas();
canvas = new Canvas(chaosCanvas.getWidth(), chaosCanvas.getHeight());
gc = canvas.getGraphicsContext2D();
TextField stepsField = new TextField();
......@@ -54,9 +60,9 @@ public class ChaosPage {
} else {
updateChaosGame(selectedGame);
}
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
updateCanvas();
});
runStepsButton.setOnAction(e5 -> {
if (!stepsField.getText().isEmpty()) {
try {
......@@ -71,7 +77,25 @@ public class ChaosPage {
}
});
runStepsBox.getChildren().addAll(contextMenu,stepsField, runStepsButton);
Button openFileButton = new Button("Open File");
openFileButton.setOnAction(e -> {
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt"));
File selectedFile = fileChooser.showOpenDialog(null);
if (selectedFile != null) {
try {
ChaosGameFileHandler fileHandler = new ChaosGameFileHandler();
ChaosGameDescription description = fileHandler.readFromFile(selectedFile.getAbsolutePath());
updateChaosGame(description);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
runStepsBox.getChildren().addAll(contextMenu,stepsField, runStepsButton, openFileButton);
runStepsBox.setSpacing(10);
runStepsBox.setPadding(new Insets(10));
runStepsBox.setAlignment(Pos.CENTER_RIGHT);
......@@ -113,6 +137,14 @@ public class ChaosPage {
private void updateChaosGame(String chaosGameType) {
chaosGame = new ChaosGame(ChaosGameDescriptionFactory.get(chaosGameType, c), 1200, 800);
chaosGameController = new ChaosGameController(chaosGame);
chaosCanvas = chaosGame.getCanvas();
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
private void updateChaosGame(ChaosGameDescription description){
chaosGame = new ChaosGame(description, 1200, 800);
chaosGameController = new ChaosGameController(chaosGame);
chaosCanvas = chaosGame.getCanvas();
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
}
package org.example.chaosgame.view;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
......@@ -27,6 +29,9 @@ public class ExplorePage {
private Complex c = new Complex(-0.835, 0.2321);
private final Canvas canvas;
private final GraphicsContext gc;
private Vector2D dragStart;
private Vector2D dragStartTemp;
private final List<Transform2D> trans = List.of(
new ExploreJulia(c)
);
......@@ -42,6 +47,7 @@ public class ExplorePage {
private WritableImage offScreenImage;
private PixelWriter pixelWriter;
private Vector2D dragDistance;
public ExplorePage() {
......@@ -71,27 +77,55 @@ public class ExplorePage {
zoomInButton.setOnMousePressed(event -> {
double scaleFactor = 1.05;
Vector2D newMinCoords = description.getMinCoords().scale(1 / scaleFactor);
Vector2D newMaxCoords = description.getMaxCoords().scale(1 / scaleFactor);
description = new ChaosGameDescription(newMinCoords, newMaxCoords, trans);
exploreGame = new ExploreGame(description, 1200, 800);
//
exploreGame.exploreFractals();
updateCanvas();
AnimationTimer zoomInTimer = new AnimationTimer() {
@Override
public void handle(long now) {
double scaleFactor = 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.exploreFractals();
updateCanvas();
}
};
zoomInTimer.start();
zoomInButton.setUserData(zoomInTimer); // Store the timer in the button's user data
});
zoomInButton.setOnMouseReleased(event -> {
AnimationTimer zoomInTimer = (AnimationTimer) zoomInButton.getUserData();
if (zoomInTimer != null) {
zoomInTimer.stop();
}
});
zoomOutButton.setOnAction(event -> {
double scaleFactor = 1 / 1.05;
Vector2D newMinCoords = description.getMinCoords().scale(1 / scaleFactor);
Vector2D newMaxCoords = description.getMaxCoords().scale(1 / scaleFactor);
zoomOutButton.setOnMousePressed(event -> {
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);
// chaosCanvas = exploreGame.getCanvas();
exploreGame.exploreFractals();
updateCanvas();
}
};
zoomOutTimer.start();
zoomOutButton.setUserData(zoomOutTimer); // Store the timer in the button's user data
});
zoomOutButton.setOnMouseReleased(event -> {
AnimationTimer zoomOutTimer = (AnimationTimer) zoomOutButton.getUserData();
if (zoomOutTimer != null) {
zoomOutTimer.stop();
}
});
......@@ -102,35 +136,94 @@ 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();
initialMousePosition = new Vector2D(mouseX, canvas.getHeight() - mouseY);
dragStartTemp = new Vector2D(mouseX, canvas.getHeight() - mouseY);
// initialMousePosition = new Vector2D(mouseX, canvas.getHeight() - mouseY);
} catch (Exception e) {
e.printStackTrace();
}
});
exploreContent.addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> {
Vector2D currentMousePosition = new Vector2D(event.getX(),canvas.getHeight() - event.getY());
Vector2D dragDistance = currentMousePosition.subtract(initialMousePosition);
// 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);
canvas.setTranslateX(canvas.getTranslateX() + dragDistance.getX());
canvas.setTranslateY(canvas.getTranslateY() + dragDistance.getY());
dragStart = dragEnd;
// 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()));
});
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 = new ChaosGameDescription(newMinCoords, newMaxCoords, trans);
description.setMinCoords(newMinCoords);
description.setMaxCoords(newMaxCoords);
exploreGame = new ExploreGame(description, 1200, 800);
exploreGame.exploreFractals();
updateCanvas();
System.out.println("Mouse released");
initialMousePosition = currentMousePosition;
});
dragStart = null;
canvas.setTranslateX(0);
canvas.setTranslateY(0);
});
//exploreContent.addEventFilter(MouseEvent.MOUSE_RELEASED, 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
// 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.setDescription(description);
// exploreGame.exploreFractals();
// updateCanvas();
// });
}
public StackPane getExploreContent() {
......@@ -152,9 +245,10 @@ 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) {
pixelWriter.setColor(j, i, Color.rgb((int) color, (int) color, (int) color));
}
// if (color >= 0 && color <= 255) {
pixelWriter.setColor(j, i, Color.rgb((int) color, 0, 0));
// }
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment