diff --git a/build.gradle b/build.gradle index 0056f70f00671c765e2c4da22dc105fef5c663a0..178b0ec6fe93a0d8a4f19e8536dc11573fe95b3b 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ repositories { dependencies { testImplementation('org.junit.jupiter:junit-jupiter-api:5.7.2', - 'org.hamcrest:hamcrest-library:2.2', 'net.obvj:junit-utils:1.3.1') + 'org.assertj:assertj-core:3.11.1') testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } @@ -23,5 +23,5 @@ test { } application { - mainClassName = "viewer.Main" + mainClassName = "viewer.MainAppLauncher" } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 2dc959b880d8cefffdb8f392d079bae1cd56a6e7..b96841a5b1a16e6ddf2bc095b88077b796f51488 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'color-image' +rootProject.name = 'formula' diff --git a/src/main/java/formula/Formula.java b/src/main/java/formula/Formula.java new file mode 100644 index 0000000000000000000000000000000000000000..d6c48c1259658ace00cb139a813a4b456e2cb696 --- /dev/null +++ b/src/main/java/formula/Formula.java @@ -0,0 +1,24 @@ +package formula; + +public interface Formula { + + /** + * Compute the value of the formula + * + * @param xValue the value of the variable x + * @return the value of the function when the variable x has value {@code xValue} + */ + double eval(double xValue); + + /** + * Compute a {@code String} representation of the formula. + * @return the formula as a {@code String} + */ + String toString(); + + /** + * Compute the derivative of the formula. + * @return the derivative of the formula + */ + Formula derivative(); +} diff --git a/src/main/java/image/BlankImage.java b/src/main/java/image/BlankImage.java deleted file mode 100644 index 64934365f028b22affe2bdaed244a5cb364a43d9..0000000000000000000000000000000000000000 --- a/src/main/java/image/BlankImage.java +++ /dev/null @@ -1,28 +0,0 @@ -package image; - -import javafx.scene.paint.Color; - -public class BlankImage implements Image{ - private final int width; - private final int height; - - public BlankImage(int width, int height) { - this.width = width; - this.height = height; - } - - @Override - public Color getPixelColor(int x, int y) { - return Color.WHITE; - } - - @Override - public int getWidth() { - return width; - } - - @Override - public int getHeight() { - return height; - } -} diff --git a/src/main/java/image/BlankImageFactory.java b/src/main/java/image/BlankImageFactory.java deleted file mode 100644 index 77403f2858bb68f38e286c8321ebe4819bc3267c..0000000000000000000000000000000000000000 --- a/src/main/java/image/BlankImageFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package image; - -public class BlankImageFactory implements ImageFactory { - @Override - public Image makeImage() { - return new BlankImage(1000, 800); - } -} diff --git a/src/main/java/image/Image.java b/src/main/java/image/Image.java deleted file mode 100644 index 5764c8adb331bdd61e6fe700a2afffcd42bb363e..0000000000000000000000000000000000000000 --- a/src/main/java/image/Image.java +++ /dev/null @@ -1,12 +0,0 @@ -package image; - -import javafx.scene.paint.Color; - -/** - * Created by Arnaud Labourel on 09/11/2018. - */ -public interface Image { - Color getPixelColor(int x, int y); - int getWidth(); - int getHeight(); -} diff --git a/src/main/java/image/ImageFactory.java b/src/main/java/image/ImageFactory.java deleted file mode 100644 index fa2530dfb945570fd9b379daed91d51656d35676..0000000000000000000000000000000000000000 --- a/src/main/java/image/ImageFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package image; - -/** - * Created by Arnaud Labourel on 23/11/2018. - */ -public interface ImageFactory { - Image makeImage(); -} diff --git a/src/main/java/image/Pixel.java b/src/main/java/image/Pixel.java deleted file mode 100644 index adbfdfa58af27cca2cd137ca8fed47de35d54daf..0000000000000000000000000000000000000000 --- a/src/main/java/image/Pixel.java +++ /dev/null @@ -1,19 +0,0 @@ -package image; - -import javafx.scene.paint.Color; - -/** - * Created by Arnaud Labourel on 09/11/2018. - */ -public class Pixel extends Point{ - private Color color; - - Pixel(int x, int y, Color color) { - super(x, y); - this.color = color; - } - - public Color getColor() { - return color; - } -} diff --git a/src/main/java/image/Point.java b/src/main/java/image/Point.java deleted file mode 100644 index 68ceaaac38e00336a9980325625bc3bd5d7abecc..0000000000000000000000000000000000000000 --- a/src/main/java/image/Point.java +++ /dev/null @@ -1,30 +0,0 @@ -package image; - -import java.util.Objects; - -/** - * Created by Arnaud Labourel on 09/11/2018. - */ -public class Point { - public final int x, y; - - Point(int x, int y) { - this.x = x; - this.y = y; - } - - @Override - public final boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Point point)) return false; - return x == point.x && - y == point.y; - } - - @Override - public final int hashCode() { - return Objects.hash(x, y); - } - - -} diff --git a/src/main/java/image/Shape.java b/src/main/java/image/Shape.java deleted file mode 100644 index 1b3240702a021d32778823d9cc4245763485bf7f..0000000000000000000000000000000000000000 --- a/src/main/java/image/Shape.java +++ /dev/null @@ -1,18 +0,0 @@ -package image; - -import javafx.scene.paint.Color; - -public interface Shape { - /** - * Tests if a specified Point is inside the boundary of the Shape. - * - * @return true if the point is inside the shape and false otherwise. - */ - boolean contains(Point point); - /** - * Return the color of the interior of the Shape. - * - * @return the color of the shape. - */ - Color getColor(); -} diff --git a/src/main/java/util/Matrices.java b/src/main/java/util/Matrices.java deleted file mode 100644 index d49992675ad5e0ce32eda0b9a6dd051e6c384d0d..0000000000000000000000000000000000000000 --- a/src/main/java/util/Matrices.java +++ /dev/null @@ -1,77 +0,0 @@ -package util; - -import java.util.Objects; - -/** - * Created by Arnaud Labourel on 23/11/2018. - */ -public class Matrices { - - /** - * Ensures that the given matrix does not have null parts : itself being null, having null row or having - * null values. - * - * @throws NullPointerException if there are null parts in the matrix. - * @param matrix the matrix to be tested. - */ - - public static void requiresNonNull(Object[][] matrix) { - Objects.requireNonNull(matrix, "The matrix must not be null."); - for (int x = 0; x < getRowCount(matrix); x++) { - Objects.requireNonNull(matrix[x], "The matrix must not have rows equals to null."); - for (int y = 0; y < matrix[x].length; y++) { - Objects.requireNonNull(matrix[x][y], "The matrix must not have values equals to null."); - } - } - } - - /** - * Ensures that the given matrix (assumed to be rectangular) does not have zero rows or zero columns. - * - * @throws IllegalArgumentException if the matrix have zero rows or zero columns. - * @param matrix the matrix to be tested. - */ - public static void requiresNonZeroDimensions(Object[][] matrix) { - if (getRowCount(matrix) == 0) { - throw new IllegalArgumentException("The matrix must not have zero rows."); - } - if (getColumnCount(matrix) == 0) { - throw new IllegalArgumentException("The matrix must not have zero columns."); - } - } - - - /** - * Ensures that the given matrix is rectangular, i.e., all rows have the same size. - * - * @throws IllegalArgumentException if the matrix have rows with different sizes. - * @param matrix the matrix to be tested. - */ - public static void requiresRectangularMatrix(Object[][] matrix) { - for (int x = 1; x < getRowCount(matrix); x++) { - if (matrix[x].length != matrix[0].length) - throw new IllegalArgumentException("The matrix must be rectangular."); - } - } - - /** - * Give the number of rows of a matrix. - * - * @param matrix the matrix. - * @return the number of rows of the matrix. - */ - public static int getRowCount(Object[][] matrix){ - return matrix.length; - } - - /** - * Give the number of columns of a matrix (assumed to be rectangular). - * - * @param matrix the matrix. - * @return the number of rows of the matrix. - */ - public static int getColumnCount(Object[][] matrix){ - return matrix[0].length; - } - -} diff --git a/src/main/java/viewer/Display.java b/src/main/java/viewer/Display.java deleted file mode 100644 index 377b5162a7c21a765bbb1e5c572add36e3a62c75..0000000000000000000000000000000000000000 --- a/src/main/java/viewer/Display.java +++ /dev/null @@ -1,54 +0,0 @@ -package viewer; - -import image.*; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.canvas.Canvas; -import javafx.scene.canvas.GraphicsContext; -import javafx.scene.image.PixelWriter; -import javafx.scene.paint.Color; - -import java.net.URL; -import java.util.ResourceBundle; - -/** - * Created by Arnaud Labourel on 04/10/2018. - */ -public class Display implements Initializable { - @FXML - private Canvas canvas; - - private Image image; - - @Override - public void initialize(URL location, ResourceBundle resources) { - ImageFactory imageFactory = new BlankImageFactory(); - // TODO : changer la fabrique d'image pour construire des images. - - this.image = imageFactory.makeImage(); - - render(); - } - - private void render() { - int pixelWidth = image.getWidth(); - int pixelHeight = image.getHeight(); - - canvas.setWidth(pixelWidth); - canvas.setHeight(pixelHeight); - - GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); - PixelWriter pixelWriter = graphicsContext.getPixelWriter(); - - for (int i = 0; i < pixelWidth; i++) { - for (int j = 0; j < pixelHeight; j++) { - renderPixel(i, j, pixelWriter); - } - } - } - - private void renderPixel(int x, int y, PixelWriter pixelWriter) { - pixelWriter.setColor(x, y, image.getPixelColor(x, y)); - } - -} diff --git a/src/main/java/viewer/FunctionChart.java b/src/main/java/viewer/FunctionChart.java new file mode 100644 index 0000000000000000000000000000000000000000..872885935695d8c0ea7fac2401a8e3442e1b864d --- /dev/null +++ b/src/main/java/viewer/FunctionChart.java @@ -0,0 +1,45 @@ +package viewer; + + +import javafx.geometry.Side; +import javafx.scene.chart.LineChart; +import javafx.scene.chart.NumberAxis; + +class FunctionChart extends LineChart<Number, Number> { + + private static final int TICK_UNIT = 1; + private static final int LOWER_BOUND = -10; + private static final int UPPER_BOUND = 10; + + int getLowerBound() { + return LOWER_BOUND; + } + + int getUpperBound() { + return UPPER_BOUND; + } + + FunctionChart(){ + super(new NumberAxis(LOWER_BOUND, UPPER_BOUND, TICK_UNIT), new NumberAxis(LOWER_BOUND, UPPER_BOUND, TICK_UNIT)); + getXAxis().setSide(Side.BOTTOM); + getYAxis().setSide(Side.LEFT); + setPrefWidth(900); + setPrefHeight(900); + setCreateSymbols(false); + } + + private Series<Number, Number> getSeries(String name){ + for (Series<Number, Number> series : getData()) + if(series.getName().equals(name)){ + return series; + } + return null; + } + + void removeSeries(String name){ + Series<Number, Number> series = getSeries(name); + if(series != null){ + getData().remove(series); + } + } +} diff --git a/src/main/java/viewer/FunctionList.java b/src/main/java/viewer/FunctionList.java new file mode 100644 index 0000000000000000000000000000000000000000..85ccac46af98bd93e3c756f30eb48f5964c7e938 --- /dev/null +++ b/src/main/java/viewer/FunctionList.java @@ -0,0 +1,72 @@ +package viewer; + +import javafx.scene.chart.XYChart; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +class FunctionList { + private final List<PlottableFunction> functions = new ArrayList<>(); + + private final FunctionChart functionChart; + private final double lowerBound; + private final double upperBound; + + FunctionList(FunctionChart functionChart) { + this.functionChart = functionChart; + this.lowerBound = functionChart.getLowerBound(); + this.upperBound = functionChart.getUpperBound(); + + // TODO: add functions + + + } + + void toggleFunction(PlottableFunction function) { + if (function.isPlotted()){ + unplot(function); + } + else{ + plot(function); + } + } + + private void unplot(PlottableFunction function) { + functionChart.removeSeries(function.toString()); + function.setPlotted(false); + } + + List<PlottableFunction> getFunctions(){ + return functions; + } + + private void plot(PlottableFunction function){ + XYChart.Series<Number, Number> series = function.getData(lowerBound, upperBound); + series.setName(function.toString()); + functionChart.getData().add(series); + function.setPlotted(true); + } + + private void addFunctionsAndTheirDerivative(Collection<PlottableFunction> functions){ + for(PlottableFunction function: functions){ + addFunctionAndItsDerivative(function); + } + } + + private void addFunctionAndItsDerivative(PlottableFunction function){ + add(function); + add(function.derivative()); + } + + private void add(PlottableFunction function) { + functions.add(function); + } + + void clear() { + functionChart.getData().clear(); + for(PlottableFunction function: functions){ + function.setPlotted(false); + } + } +} diff --git a/src/main/java/viewer/Main.java b/src/main/java/viewer/Main.java deleted file mode 100644 index da5c52b505bbc484ae32dff56641f069ed8c900c..0000000000000000000000000000000000000000 --- a/src/main/java/viewer/Main.java +++ /dev/null @@ -1,28 +0,0 @@ -package viewer; - -import javafx.application.Application; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.stage.Stage; - -import java.io.IOException; -import java.util.Objects; - - -public class Main extends Application -{ - - public static void main(String[] args) { - launch(args); - } - - @Override - public void start(Stage primaryStage) throws IOException { - Parent root =FXMLLoader.load(Objects.requireNonNull(getClass().getClassLoader().getResource("fxml/Display.fxml"))); - primaryStage.setTitle("Image display"); - primaryStage.setScene(new Scene(root)); - primaryStage.show(); - - } -} diff --git a/src/main/java/viewer/MainAppController.java b/src/main/java/viewer/MainAppController.java new file mode 100644 index 0000000000000000000000000000000000000000..fa240197e00ec9a2e97d1aa9f8052e886d1056c6 --- /dev/null +++ b/src/main/java/viewer/MainAppController.java @@ -0,0 +1,58 @@ +package viewer; + +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; + +import java.net.URL; +import java.util.ResourceBundle; + +public class MainAppController implements Initializable { + + private static final int BUTTON_WIDTH = 500; + @FXML + private AnchorPane anchorPane; + + @FXML + private VBox vBox; + + private FunctionList functionList; + + @Override + public void initialize(final URL url, final ResourceBundle rb) { + FunctionChart functionChart = new FunctionChart(); + functionList = new FunctionList(functionChart); + anchorPane.getChildren().add(functionChart); + addFunctionButtons(); + addClearButton(); + } + + private void addClearButton() { + Button clearButton = new Button("Clear"); + clearButton.setOnAction(event -> functionList.clear()); + addButton(clearButton); + } + + private void addFunctionButtons() { + for(PlottableFunction function : functionList.getFunctions()){ + addFunctionButton(function); + } + } + + private void addFunctionButton(PlottableFunction function) { + Button button = new Button(function.toString()); + addButton(button); + button.setOnAction(event -> toggleFunction(function)); + } + + private void toggleFunction(PlottableFunction function){ + functionList.toggleFunction(function); + } + + private void addButton(Button button) { + button.setPrefWidth(BUTTON_WIDTH); + vBox.getChildren().add(button); + } +} diff --git a/src/main/java/viewer/MainAppLauncher.java b/src/main/java/viewer/MainAppLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..205159bdc3a40af04a91ba3caf37d6d6b4e8fc48 --- /dev/null +++ b/src/main/java/viewer/MainAppLauncher.java @@ -0,0 +1,29 @@ +package viewer; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import java.util.Objects; + +public class MainAppLauncher extends Application { + + public static void main(String[] args) { + Application.launch(MainAppLauncher.class, args); + } + + @Override + public void start(Stage stage) { + try { + Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getClassLoader() + .getResource("MainApp.fxml"))); + stage.setScene(new Scene(root)); + stage.setTitle("Formulas"); + stage.show(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/viewer/PlottableFunction.java b/src/main/java/viewer/PlottableFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..98a4e92065bb640dfc4394b4d816ba9635a4d24a --- /dev/null +++ b/src/main/java/viewer/PlottableFunction.java @@ -0,0 +1,42 @@ +package viewer; + +import formula.Formula; +import javafx.scene.chart.XYChart; + +public class PlottableFunction { + private final Formula formula; + private final String name; + private static final double PRECISION = 0.01; + private boolean isPlotted = false; + + PlottableFunction(Formula formula, String name) { + this.formula = formula; + this.name = name; + } + + @Override + public String toString() { + return name + "(x) = " + formula; + } + + public PlottableFunction derivative(){ + return new PlottableFunction(formula.derivative(), name + "'"); + } + + XYChart.Series<Number, Number> getData(double lowerBound, double upperBound) { + final XYChart.Series<Number, Number> series = new XYChart.Series<>(); + for (int index = 0; index <= 2 * (upperBound-lowerBound) / PRECISION; index++) { + double x = lowerBound + index * PRECISION; + series.getData().add(new XYChart.Data<>(x, formula.eval(x))); + } + return series; + } + + boolean isPlotted() { + return isPlotted; + } + + void setPlotted(boolean plotted) { + isPlotted = plotted; + } +} diff --git a/src/main/resources/MainApp.fxml b/src/main/resources/MainApp.fxml new file mode 100644 index 0000000000000000000000000000000000000000..766293e2d94239180a1f9ecb2500e840b366e3e1 --- /dev/null +++ b/src/main/resources/MainApp.fxml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.VBox?> + +<AnchorPane maxHeight="900.0" maxWidth="1200.0" minHeight="900.0" minWidth="1400.0" + prefHeight="900.0" prefWidth="1000.0" + styleClass="root" stylesheets="stylesheet.css" + xmlns:fx="http://javafx.com/fxml" + fx:controller="viewer.MainAppController"> + <AnchorPane layoutX="900.0" minHeight="0.0" minWidth="0.0" prefHeight="900.0" prefWidth="300.0"> + <VBox fx:id="vBox" prefHeight="800.0" prefWidth="500.0"/> + </AnchorPane> + <AnchorPane fx:id="anchorPane" layoutX="-7.0" prefHeight="900.0" prefWidth="900.0"/> +</AnchorPane> \ No newline at end of file diff --git a/src/main/resources/fxml/Display.fxml b/src/main/resources/fxml/Display.fxml deleted file mode 100644 index b472c98e31fd1807900b22c90de2f360048bb0d9..0000000000000000000000000000000000000000 --- a/src/main/resources/fxml/Display.fxml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<?import javafx.scene.layout.*?> - - -<?import javafx.scene.canvas.Canvas?> -<AnchorPane xmlns="http://javafx.com/javafx" - xmlns:fx="http://javafx.com/fxml" - fx:controller="viewer.Display"> - <Canvas fx:id="canvas"/> -</AnchorPane> diff --git a/src/main/resources/stylesheet.css b/src/main/resources/stylesheet.css new file mode 100644 index 0000000000000000000000000000000000000000..cdd92f0b7636df0295c88255ad557772807db9c9 --- /dev/null +++ b/src/main/resources/stylesheet.css @@ -0,0 +1,84 @@ +#pane, .root, .split-pane{ + -fx-background-color: #353434; + -fx-foreground-color: #353434; +} + +.chart-vertical-zero-line { + -fx-stroke: white; +} +.chart-horizontal-zero-line { + -fx-stroke: white; +} + +.chart-plot-background { + -fx-background-color: #575758; + -fx-foreground-color: white; + -fx-stroke: white; +} + +.chart-vertical-grid-lines { + -fx-stroke: #898887; +} +.chart-horizontal-grid-lines { + -fx-stroke: #898887; +} +.chart-alternative-row-fill { + -fx-fill: transparent; + -fx-stroke: transparent; + -fx-stroke-width: 1; +} + +.axis { + -fx-stroke: white; + -fx-fill: white; + -fx-font-size: 1.4em; + -fx-tick-label-fill: white; + -fx-font-family: Tahoma; + -fx-tick-length: 0; + -fx-minor-tick-length: 0; +} + +.background { + -fx-background-color: #674A44; + -fx-foreground-color: #353434; +} + +.button { + -fx-padding: 5 22 5 22; + -fx-border-color: #353434; + -fx-border-width: 0; + -fx-background-radius: 0; + -fx-background-color: derive(#353434,20%); + -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; + -fx-font-size: 11pt; + -fx-text-fill: #d8d8d8; + -fx-background-insets: 0 0 0 0, 0, 1, 2; +} + +.button:hover { + -fx-background-color: #3a3a3a; +} + +.button:pressed, .button:default:hover:pressed { + -fx-background-color: #bdbcbc; + -fx-text-fill: black; +} + +.button:disabled, .button:default:disabled { + -fx-opacity: 0.4; + -fx-background-color: #353434; + -fx-text-fill: white; +} + +.button:default { + -fx-background-color: -fx-focus-color; + -fx-text-fill: #ffffff; +} + +.button:default:hover { + -fx-background-color: derive(-fx-focus-color,30%); +} + +.text-area .content { + -fx-background-color: #575758; +} \ No newline at end of file diff --git a/src/test/java/BlankImageTest.java b/src/test/java/BlankImageTest.java deleted file mode 100644 index b7030780d0acc1400103c178df92edb1bbaf050b..0000000000000000000000000000000000000000 --- a/src/test/java/BlankImageTest.java +++ /dev/null @@ -1,29 +0,0 @@ -import image.BlankImage; -import javafx.scene.paint.Color; -import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - - -public class BlankImageTest { - @Test - public void testBlankImageGetWidth(){ - BlankImage blankImage = new BlankImage(200, 300); - assertThat(blankImage.getWidth(), is(equalTo(200))); - } - - @Test - public void testBlankImageGetHeight(){ - BlankImage blankImage = new BlankImage(200, 300); - assertThat(blankImage.getHeight(), is(equalTo(300))); - } - - @Test - public void testBlankImageGetPixelColor(){ - BlankImage blankImage = new BlankImage(200, 300); - assertThat(blankImage.getPixelColor(0,0), is(equalTo(Color.WHITE))); - assertThat(blankImage.getPixelColor(100,100), is(equalTo(Color.WHITE))); - assertThat(blankImage.getPixelColor(199,299), is(equalTo(Color.WHITE))); - } - -}