diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
deleted file mode 100644
index a92506c..0000000
--- a/.github/workflows/maven.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: Java CI
-
-on: [push, pull_request]
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v2
- - name: Set up JDK 11
- uses: actions/setup-java@v1
- with:
- java-version: 11
- - name: Build with Maven
- run: mvn -B package --file pom.xml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bba7b53
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target/
+/.idea/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..771c465
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,2 @@
+language: java
+sudo: false
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index b8563fb..ea6eb37 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,201 @@
-MIT License
-
-Copyright (c) 2020 Artyom Aleksyuk
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2020 Mikhail Shomov
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d8fc31d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+### Planetary System Simulator || Симулятор планетной системы
+
+
+Данная программа является наглядным 2D симулятором планетной системы.
+Предоставляет следующие настройки для системы:
+- Масса звезды
+- Радиус звезды
+- Гравитационная постоянная
+- Число планет
+
+А также индивидуальные настройки для каждой планеты:
+- Название планеты
+- Цвет планеты
+- Радиус
+- Начальная скорость и её угол
+- Позиция относительно звезды по X и Y
+
+Реализована возможность сохранения парамнтров системы и загрузки из сохранения.
+
+Также пользователю предоставляется возможность в процессе симулиции менять скорость движения.
+Приложение ведёт логирование.
+
+
+------------
+
+Приложение является практической работой, выполненной в рамках курса "Технологии программирования".
+
+Шомов Михаил
+mikle@shomov.spb.ru
\ No newline at end of file
diff --git a/configuration.pss b/configuration.pss
new file mode 100644
index 0000000..1703b92
--- /dev/null
+++ b/configuration.pss
@@ -0,0 +1,3 @@
+90.0 80.0 7.0 2
+'First' 0x99cc99ff 50.0 310.7369249695762 -181.039863412287 0.4505375374305923 0.8976612689488579
+'Second' 0x99ccccff 30.0 -450.0816019710493 16.43956060718608 -0.04172910763892559 0.989453604281171
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..dc9639c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,100 @@
+
+
+ 4.0.0
+
+ Main
+ planet
+ 1.0
+
+
+ UTF-8
+ 11
+ ${maven.compiler.source}
+ 5.6.0
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.jupiter.version}
+ test
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.13.1
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.13.1
+
+
+ junit
+ junit
+ 4.9
+ test
+
+
+ args4j
+ args4j
+ 2.33
+
+
+ org.openjfx
+ javafx-controls
+ 11.0.2
+
+
+ org.openjfx
+ javafx-fxml
+ 11.0.2
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 11
+ 11
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.0
+
+
+
+ true
+ simulator.Main
+
+
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ 0.0.3
+
+ simulator.Main
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/simulator/Main.java b/src/main/java/simulator/Main.java
new file mode 100644
index 0000000..5d83974
--- /dev/null
+++ b/src/main/java/simulator/Main.java
@@ -0,0 +1,10 @@
+package simulator;
+
+import simulator.view.App;
+
+public class Main {
+
+ public static void main(String[] args) {
+ App.main(args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/simulator/controller/ControllerOfThePlanet.java b/src/main/java/simulator/controller/ControllerOfThePlanet.java
new file mode 100644
index 0000000..22fe530
--- /dev/null
+++ b/src/main/java/simulator/controller/ControllerOfThePlanet.java
@@ -0,0 +1,99 @@
+package simulator.controller;
+
+import simulator.model.PlanetCharacteristic;
+import simulator.model.SystemCharacteristic;
+import simulator.view.App;
+import javafx.fxml.FXML;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.control.ColorPicker;
+import javafx.scene.control.TextField;
+import javafx.scene.input.MouseEvent;
+import javafx.stage.Stage;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class ControllerOfThePlanet {
+
+ Logger log = LogManager.getLogger(ControllerOfThePlanet.class.getName());
+
+ @FXML
+ private Button help;
+ @FXML
+ private Button applyPl;
+ @FXML
+ private TextField namePl;
+ @FXML
+ private ColorPicker colorPl;
+ @FXML
+ private TextField radiusPl;
+ @FXML
+ private TextField positionXPl;
+ @FXML
+ private TextField positionYPl;
+ @FXML
+ private TextField degreesPl;
+ @FXML
+ private TextField speedPl;
+
+ boolean[] completion = new boolean[6];
+
+ private boolean check = false;
+
+ private void btnEnabler(boolean test, int num) {
+ completion[num] = test;
+ for (var b : completion)
+ if (!b) {
+ check = false;
+ break;
+ } else check = true;
+ applyPl.setDisable(!check);
+ }
+
+ @FXML
+ public void initialize(SystemCharacteristic system){
+ help.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
+ var alert = new Alert(Alert.AlertType.INFORMATION);
+ alert.setTitle("Help");
+ alert.setContentText("For example, radius = 20, position x= 200, position y = -300, speed = 3, degrees= -3");
+ alert.showAndWait();
+ });
+
+ var regex = "[0-9]+([.,][0-9]+)?";
+ namePl.setOnKeyTyped(event -> btnEnabler(!namePl.getText().contains(" "), 0));
+ radiusPl.setOnKeyTyped(event -> btnEnabler(radiusPl.getText().matches(regex), 1));
+ positionXPl.setOnKeyTyped(event -> btnEnabler(positionXPl.getText().matches("-?" + regex), 2));
+ positionYPl.setOnKeyTyped(event -> btnEnabler(positionYPl.getText().matches("-?" + regex), 3));
+ degreesPl.setOnKeyTyped(event -> btnEnabler(degreesPl.getText().matches("-?" + regex), 4));
+ speedPl.setOnKeyTyped(event -> btnEnabler(speedPl.getText().matches(regex), 5));
+
+ applyPl.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
+ var stage = (Stage) applyPl.getScene().getWindow();
+ stage.close();
+ var planet = new PlanetCharacteristic();
+ planet.setName(namePl.getText());
+ planet.setColor(colorPl.getValue());
+ try {
+ planet.setRadius(radiusPl.getText());
+ planet.setPositionX(positionXPl.getText());
+ planet.setPositionY(positionYPl.getText());
+ planet.setSpeed(speedPl.getText(), degreesPl.getText());
+
+ } catch (Exception e) {
+ log.error("Exception " + e);
+ e.printStackTrace();
+ }
+ system.planet.add(planet);
+ log.info(planet.toString());
+ var main = new App();
+ try {
+ if (system.planet.size() == system.numberOfPlanets) main.space(system);
+ else main.planetSetup(system);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/simulator/controller/ControllerOfTheSystem.java b/src/main/java/simulator/controller/ControllerOfTheSystem.java
new file mode 100644
index 0000000..baa1d1b
--- /dev/null
+++ b/src/main/java/simulator/controller/ControllerOfTheSystem.java
@@ -0,0 +1,91 @@
+package simulator.controller;
+
+import simulator.model.FileManager;
+import simulator.model.SystemCharacteristic;
+import simulator.view.App;
+import javafx.fxml.FXML;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.control.Slider;
+import javafx.scene.control.TextField;
+import javafx.scene.input.MouseEvent;
+import javafx.stage.Stage;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class ControllerOfTheSystem {
+
+ Logger log = LogManager.getLogger(ControllerOfTheSystem.class.getName());
+
+ @FXML
+ private Button help;
+ @FXML
+ private Button apply;
+ @FXML
+ private TextField mass;
+ @FXML
+ private TextField radius;
+ @FXML
+ public TextField G;
+ @FXML
+ private Slider number;
+ @FXML
+ public Button load;
+
+ boolean[] completion = new boolean[3];
+
+ private boolean check = false;
+
+ private void btnEnabler(boolean test, int num) {
+ completion[num] = test;
+ for (var b : completion)
+ if (!b) {
+ check = false;
+ break;
+ } else check = true;
+ apply.setDisable(!check);
+ }
+
+ @FXML
+ public void initialize() {
+ help.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
+ var alert = new Alert(Alert.AlertType.INFORMATION);
+ alert.setTitle("Help");
+ alert.setContentText("For example, mass = 900, radius = 80, Gravitational constant = 7");
+ alert.showAndWait();
+ });
+
+ load.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
+ var file = new FileManager();
+ file.open((Stage) apply.getScene().getWindow());
+ });
+
+ var regex = "[0-9]+([.,][0-9]+)?";
+ mass.setOnKeyTyped(event -> btnEnabler(mass.getText().matches(regex), 0));
+ radius.setOnKeyTyped(event -> btnEnabler(radius.getText().matches(regex), 1));
+ G.setOnKeyTyped(event -> btnEnabler(G.getText().matches(regex), 2));
+
+ apply.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
+ ((Stage) apply.getScene().getWindow()).close();
+ var system = new SystemCharacteristic();
+ try {
+ system.setMassOfStar(mass.getText());
+ system.setRadiusOfStar(radius.getText());
+ system.setGC(G.getText());
+ } catch (Exception e) {
+ log.error("Exception " + e);
+ e.printStackTrace();
+ }
+ system.setNumberOfPlanets(number.getValue());
+
+ log.info(system.toString());
+ var planet = new App();
+ try {
+ planet.planetSetup(system);
+ } catch (Exception e) {
+ log.error("Exception " + e);
+ e.printStackTrace();
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/simulator/model/FileManager.java b/src/main/java/simulator/model/FileManager.java
new file mode 100644
index 0000000..8f01a5c
--- /dev/null
+++ b/src/main/java/simulator/model/FileManager.java
@@ -0,0 +1,121 @@
+package simulator.model;
+
+import javafx.stage.Stage;
+import simulator.view.App;
+import javafx.scene.control.Alert;
+import javafx.stage.FileChooser;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class FileManager {
+ static Logger log = LogManager.getLogger(FileManager.class.getName());
+
+ public void save(SystemCharacteristic system) {
+ var fileChooser = new FileChooser();
+ fileChooser.setTitle("Select directory for save");
+ fileChooser.setInitialFileName("configuration");
+ fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Configuration file .pss", "*.pss"));
+ var file = fileChooser.showSaveDialog(App.stageFile);
+ if (file == null) {
+ log.info("The user closed the saving window");
+ return;
+ }
+ try (var writer = new FileWriter(file)) {
+ writer.write(system.toStringFile());
+ log.info("Save to " + file.getAbsolutePath());
+ } catch (IOException e) {
+ error(false);
+ log.error(e);
+ }
+ }
+
+ public void open(Stage stage) {
+ try {
+ var fileChooser = new FileChooser();
+ fileChooser.setTitle("Open config file");
+ var filter = new FileChooser.ExtensionFilter("Configuration file .pss", "*.pss");
+ fileChooser.getExtensionFilters().add(filter);
+ var file = fileChooser.showOpenDialog(App.stageFile);
+ if (file == null) {
+ log.info("The user closed the opening window");
+ return;
+ }
+ file = file.getAbsoluteFile();
+ var system = new SystemCharacteristic();
+ var size = 0;
+ var fr = new FileReader(file);
+ var reader = new BufferedReader(fr);
+ var line = reader.readLine();
+ while (line != null) {
+ if (line.matches("([0-9]+([.,][0-9]+)? ){4}")) {
+ var list = line.split(" ");
+ system.massOfStar = Double.parseDouble(list[0]);
+ system.radiusOfStar = Double.parseDouble(list[1]);
+ system.GC = Double.parseDouble(list[2]);
+ system.numberOfPlanets = Integer.parseInt(list[3]);
+ }
+ else if (line.matches("'\\S+' 0x([a-f0-9]){8} (-?[0-9]+([.,][0-9]+)? ){5}")) {
+ var list = line.split(" ");
+ var planet = new PlanetCharacteristic();
+ planet.setName(list[0].substring(1, list[0].length() - 1));
+ planet.color = list[1];
+ if (list[2].contains("-")) {
+ log.error("The file " + file.getAbsolutePath() + "has '-'");
+ error(true);
+ return;
+ }
+ planet.setRadius(list[2]);
+ planet.setPositionX(list[3]);
+ planet.setPositionY(list[4]);
+ planet.speedX = Double.parseDouble(list[5]);
+ planet.speedY = Double.parseDouble(list[6]);
+ system.planet.add(planet);
+ size++;
+ }
+ else {
+ log.error("The file " + file.getAbsolutePath() + " is invalid");
+ error(true);
+ return;
+ }
+ line = reader.readLine();
+ }
+ if (size != system.numberOfPlanets) {
+ log.error("In this file " + file.getAbsolutePath() + " the number of available planets does not match the specified number.");
+ error(true);
+ return;
+ }
+ stage.close();
+ var app = new App();
+ app.space(system);
+ } catch (IOException e) {
+ error(true);
+ log.error(e);
+ }
+ }
+
+ public static void error(boolean type) {
+ String headerText;
+ String contentText;
+
+ if (type) {
+ headerText = "File invalid";
+ contentText = "This file cannot be used by the program.";
+ }
+ else {
+ headerText = "Saving failed";
+ contentText = "Try again. Try specifying a different directory.";
+ }
+
+ var alert = new Alert(Alert.AlertType.ERROR);
+ alert.setTitle("Error");
+ alert.setHeaderText(headerText);
+ alert.setContentText(contentText);
+ alert.showAndWait();
+ }
+
+}
diff --git a/src/main/java/simulator/model/LogicManager.java b/src/main/java/simulator/model/LogicManager.java
new file mode 100644
index 0000000..e4c818c
--- /dev/null
+++ b/src/main/java/simulator/model/LogicManager.java
@@ -0,0 +1,41 @@
+package simulator.model;
+
+public class LogicManager {
+
+ public double speedX(double speed, double deg) {
+ return speed * Math.cos(deg);
+ }
+
+ public double speedY(double speed, double deg) {
+ return speed * Math.sin(deg);
+ }
+
+ public double acceleration(double G, double weight, double starPosition, double planetPosition, double distance){
+ return (G * weight * (starPosition - planetPosition) / Math.pow(distance, 3));
+ }
+
+ public double distance(double starX, double starY, double x, double y) {
+ return Math.sqrt(Math.pow(starX - x, 2) + Math.pow(starY - y, 2));
+ }
+
+ public double[] timePortation(double a, double x, double y, double vx, double vy, double sx, double sy, double g, double w) {
+ var tpXY = new double[4];
+ tpXY[0] = x;
+ tpXY[1] = y;
+ tpXY[2] = vx;
+ tpXY[3] = vy;
+ var i = a;
+
+ while (i > 0) {
+ var dist = distance(sx, sy, tpXY[0], tpXY[1]);
+ tpXY[2] += acceleration(g, w, sx, tpXY[0], dist);
+ tpXY[3] += acceleration(g, w, sy, tpXY[1], dist);
+ tpXY[0] += tpXY[2];
+ tpXY[1] += tpXY[3];
+
+ i -= 0.1;
+ }
+
+ return tpXY;
+ }
+}
diff --git a/src/main/java/simulator/model/PlanetCharacteristic.java b/src/main/java/simulator/model/PlanetCharacteristic.java
new file mode 100644
index 0000000..3372588
--- /dev/null
+++ b/src/main/java/simulator/model/PlanetCharacteristic.java
@@ -0,0 +1,63 @@
+package simulator.model;
+
+import javafx.scene.paint.Color;
+
+public class PlanetCharacteristic {
+ public String name;
+ public String color;
+ public double radius;
+ public double positionX;
+ public double positionY;
+ public double speedX;
+ public double speedY;
+
+
+ public void setName (String nameU){
+ name = nameU;
+ }
+
+ public void setColor (Color colorU){
+ color = colorU.toString();
+ }
+
+ public void setPositionX (String positionXU) {
+ positionX = Double.parseDouble(formatter(positionXU));
+ }
+
+ public void setPositionY (String positionYU) {
+ positionY = Double.parseDouble(formatter(positionYU));
+ }
+
+ public void setRadius (String radiusU) {
+ radius = Double.parseDouble(formatter(radiusU));
+ }
+
+ public void setSpeed (String speedU, String deg) {
+ var logic = new LogicManager();
+ speedX = logic.speedX(Double.parseDouble(formatter(speedU)), Double.parseDouble(formatter(deg)));
+ speedY = logic.speedY(Double.parseDouble(formatter(speedU)), Double.parseDouble(formatter(deg)));
+ }
+
+ public String formatter(String in) {
+ return in.replace(',', '.').trim();
+ }
+
+ public String toShortString() {
+ return "Planet " + name +
+ "\nColor " + color +
+ "\nRadius " + radius;
+ }
+
+ public String toString() {
+ return "Model.PlanetCharacteristic{" +
+ "name=" + name +
+ ", color=" + color +
+ ", radius=" + radius +
+ ", speedX=" + speedX +
+ ", speedY=" + speedY +
+ ", positionX=" + positionX +
+ ", positionY=" + positionY +
+ '}';
+ }
+
+}
diff --git a/src/main/java/simulator/model/SystemCharacteristic.java b/src/main/java/simulator/model/SystemCharacteristic.java
new file mode 100644
index 0000000..92e9acb
--- /dev/null
+++ b/src/main/java/simulator/model/SystemCharacteristic.java
@@ -0,0 +1,51 @@
+package simulator.model;
+
+import java.util.ArrayList;
+
+public class SystemCharacteristic {
+ public ArrayList planet = new ArrayList<>();
+ public double massOfStar;
+ public double radiusOfStar;
+ public int numberOfPlanets;
+ public double GC;
+
+ public void setMassOfStar(String mass) {
+ massOfStar = Double.parseDouble(formatter(mass));
+ }
+
+ public void setNumberOfPlanets (double num) {
+ numberOfPlanets = (int) num;
+ }
+
+ public void setRadiusOfStar (String rad) {
+ radiusOfStar = Double.parseDouble(formatter(rad));
+ }
+
+ public void setGC(String GU) {
+ GC = Double.parseDouble(formatter(GU));
+ }
+
+ private String formatter(String in) {
+ return in.replace(',', '.').trim();
+ }
+
+ public String toString() {
+ return "Model.SystemCharacteristic{" +
+ "planet=" + planet.toString() +
+ ", massOfStar=" + massOfStar +
+ ", radiusOfStar=" + radiusOfStar +
+ ", gravitation constant=" + GC +
+ ", numberOfPlanets=" + numberOfPlanets +
+ '}';
+ }
+
+ public String toStringFile() {
+ var result = new StringBuilder(massOfStar + " " + radiusOfStar + " " + GC + " " + numberOfPlanets + " \n");
+ for (var i: planet)
+ result.append("'").append(i.name).append("'").append(" ")
+ .append(i.color).append(" ").append(i.radius).append(" ")
+ .append(i.positionX).append(" ").append(i.positionY).append(" ")
+ .append(i.speedX).append(" ").append(i.speedY).append(" \n");
+ return result.toString();
+ }
+}
diff --git a/src/main/java/simulator/view/App.java b/src/main/java/simulator/view/App.java
new file mode 100644
index 0000000..f65936e
--- /dev/null
+++ b/src/main/java/simulator/view/App.java
@@ -0,0 +1,204 @@
+package simulator.view;
+
+import simulator.controller.ControllerOfThePlanet;
+import simulator.model.FileManager;
+import simulator.model.LogicManager;
+import simulator.model.SystemCharacteristic;
+import javafx.animation.Animation;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.*;
+import javafx.scene.shape.Circle;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.IntStream;
+
+public class App extends Application{
+
+ public static void main(String[] args) {
+ Application.launch(args);
+ }
+
+ public static Stage stageFile;
+ static LogicManager logic = new LogicManager();
+
+ public void start(Stage stage) throws Exception {
+ var loader = new FXMLLoader(new File("src/main/resources/SystemParameters.fxml").toURI().toURL());
+ Parent root = loader.load();
+ var scene = new Scene(root);
+ stage.setTitle("Configuring the system");
+ stage.setScene(scene);
+ stage.show();
+ stageFile = stage;
+ }
+
+ public void planetSetup(SystemCharacteristic system) throws Exception {
+ var loader = new FXMLLoader(new File("src/main/resources/PlanetParameters.fxml").toURI().toURL());
+ Parent root = loader.load();
+ ControllerOfThePlanet controller = loader.getController();
+ controller.initialize(system);
+ var stage = new Stage();
+ stage.setTitle("Configuring the planet");
+ stage.setScene(new Scene(root));
+ var rand = (1 + (int) (Math.random() * 6));
+ root.setStyle(String.format("-fx-background-image: url(images/%d.jpg)", rand));
+ stage.show();
+ }
+
+ public void space(SystemCharacteristic system) throws FileNotFoundException {
+ var canvas = new Pane();
+ canvas.setStyle("-fx-background-image: url(images/background.jpg)");
+ var stage = new Stage();
+ stage.setTitle("Planet System");
+ var radius = system.radiusOfStar / 2;
+ var scene = new Scene(canvas, 1200, 600, Color.BLACK);
+
+ var image = new Image(new FileInputStream("src/main/resources/images/star.png"));
+ var star = new ImageView(image);
+ star.setX(scene.getWidth() / 2 - radius);
+ star.setY(scene.getHeight() / 2 - radius);
+ star.setFitHeight(radius * 2);
+ star.setFitWidth(radius * 2);
+ star.setPreserveRatio(true);
+ var starCenterX = star.getX() + star.getFitWidth() / 2;
+ var starCenterY = star.getY() + star.getFitHeight() / 2;
+ canvas.getChildren().add(star);
+
+
+
+ Tooltip.install(star, new Tooltip("Star \nMass: " + system.massOfStar + "\n" +
+ "Radius: " + system.radiusOfStar));
+
+ var pause = new Button("Pause");
+ pause.setPrefWidth(80);
+ pause.setLayoutX(1100);
+ canvas.getChildren().addAll(pause);
+
+ var p = new AtomicBoolean(false);
+
+ pause.setOnAction(event -> {
+ if (!p.get()) {
+ p.set(true);
+ pause.setText("Play");
+ } else {
+ p.set(false);
+ pause.setText("Pause");
+ }
+ });
+ var animationSpeed = new Pagination(10, 0);
+ var timePortation = new TextField();
+ timePortation.setMaxWidth(50);
+ timePortation.setLayoutX(75);
+ timePortation.setLayoutY(50);
+ var tPBtn = new Button("Portation");
+ tPBtn.setLayoutY(50);
+ tPBtn.setLayoutX(135);
+
+ var timePort = new boolean[system.numberOfPlanets];
+ var a = new AtomicReference<>((double) 0);
+ tPBtn.setOnAction(event -> {
+ if (timePortation.getText().matches("[0-9]+(,.[0-9]+)?")) {
+ Arrays.fill(timePort, true);
+ a.set(Double.parseDouble(timePortation.getText().replace(',', '.')));
+ }
+ });
+
+ var saveBtn = new Button("Save config");
+ saveBtn.setLayoutY(570);
+ saveBtn.setLayoutX(10);
+ saveBtn.setOnAction(event -> {
+ p.set(true);
+ IntStream.range(0, system.planet.size()).forEach(i -> {
+ system.planet.get(i).positionX -= starCenterX;
+ system.planet.get(i).positionY -= starCenterY;
+ });
+ var file = new FileManager();
+ file.save(system);
+ IntStream.range(0, system.planet.size()).forEach(i -> {
+ system.planet.get(i).positionX += starCenterX;
+ system.planet.get(i).positionY += starCenterY;
+ });
+ try {
+ Thread.sleep(100 * system.numberOfPlanets);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ p.set(false);
+ });
+
+ canvas.getChildren().addAll(animationSpeed, timePortation, tPBtn, saveBtn);
+
+ IntStream.range(0, system.planet.size()).forEach(i -> {
+ var planet = new Circle();
+ planet.setRadius(system.planet.get(i).radius / 2);
+ canvas.getChildren().add(planet);
+ system.planet.get(i).positionX += starCenterX;
+ system.planet.get(i).positionY += starCenterY;
+ var timeline = new Timeline(new KeyFrame(Duration.seconds(0.1), event -> {
+ var distance = logic.distance(starCenterX, starCenterY, system.planet.get(i).positionX, system.planet.get(i).positionY);
+ system.planet.get(i).speedX += logic.acceleration(system.GC, system.massOfStar, starCenterX, system.planet.get(i).positionX, distance);
+ system.planet.get(i).speedY += logic.acceleration(system.GC, system.massOfStar, starCenterY, system.planet.get(i).positionY, distance);
+ system.planet.get(i).positionX += system.planet.get(i).speedX;
+ system.planet.get(i).positionY += system.planet.get(i).speedY;
+ planet.setCenterX(system.planet.get(i).positionX);
+ planet.setCenterY(system.planet.get(i).positionY);
+ canvas.requestLayout();
+
+ if (timePort[i]) {
+ var tpXY = logic.timePortation(a.get(), system.planet.get(i).positionX, system.planet.get(i).positionY, system.planet.get(i).speedX, system.planet.get(i).speedY, starCenterX, starCenterY, system.GC, system.massOfStar);
+ system.planet.get(i).positionX = tpXY[0];
+ system.planet.get(i).positionY = tpXY[1];
+ system.planet.get(i).speedX = tpXY[2];
+ system.planet.get(i).speedY = tpXY[3];
+ timePort[i] = false;
+ }
+
+ var gradient = new RadialGradient(0,
+ .1,
+ planet.getCenterX(),
+ planet.getCenterY(),
+ planet.getRadius(),
+ false,
+ CycleMethod.NO_CYCLE,
+ new Stop(0, (Color) Paint.valueOf(system.planet.get(i).color)),
+ new Stop(0.9, Color.BLACK));
+
+ planet.setFill(gradient);
+
+ Tooltip.install(planet, new Tooltip(system.planet.get(i).toShortString() + "\n"
+ + "Distance to the star " + distance));
+ }));
+ timeline.setCycleCount(Animation.INDEFINITE);
+ timeline.play();
+ var timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ if (p.get()) timeline.stop();
+ else timeline.play();
+ timeline.setRate(10 * (animationSpeed.getCurrentPageIndex() + 1));
+ }
+ }, 0, 1);
+ });
+ stage.setScene(scene);
+ stage.show();
+ stage.setOnCloseRequest(windowEvent -> System.exit(0));
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..ce32a75
--- /dev/null
+++ b/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: simulator.Main
+
diff --git a/src/main/resources/PlanetParameters.fxml b/src/main/resources/PlanetParameters.fxml
new file mode 100644
index 0000000..042d139
--- /dev/null
+++ b/src/main/resources/PlanetParameters.fxml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/SystemParameters.fxml b/src/main/resources/SystemParameters.fxml
new file mode 100644
index 0000000..3364de7
--- /dev/null
+++ b/src/main/resources/SystemParameters.fxml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/images/1.jpg b/src/main/resources/images/1.jpg
new file mode 100644
index 0000000..a2ef278
Binary files /dev/null and b/src/main/resources/images/1.jpg differ
diff --git a/src/main/resources/images/2.jpg b/src/main/resources/images/2.jpg
new file mode 100644
index 0000000..5f3684e
Binary files /dev/null and b/src/main/resources/images/2.jpg differ
diff --git a/src/main/resources/images/3.jpg b/src/main/resources/images/3.jpg
new file mode 100644
index 0000000..47f979d
Binary files /dev/null and b/src/main/resources/images/3.jpg differ
diff --git a/src/main/resources/images/4.jpg b/src/main/resources/images/4.jpg
new file mode 100644
index 0000000..c4268a3
Binary files /dev/null and b/src/main/resources/images/4.jpg differ
diff --git a/src/main/resources/images/5.jpg b/src/main/resources/images/5.jpg
new file mode 100644
index 0000000..25fc7b5
Binary files /dev/null and b/src/main/resources/images/5.jpg differ
diff --git a/src/main/resources/images/6.jpg b/src/main/resources/images/6.jpg
new file mode 100644
index 0000000..693fe73
Binary files /dev/null and b/src/main/resources/images/6.jpg differ
diff --git a/src/main/resources/images/background.jpg b/src/main/resources/images/background.jpg
new file mode 100644
index 0000000..3f769c3
Binary files /dev/null and b/src/main/resources/images/background.jpg differ
diff --git a/src/main/resources/images/star.png b/src/main/resources/images/star.png
new file mode 100644
index 0000000..11314fb
Binary files /dev/null and b/src/main/resources/images/star.png differ
diff --git a/src/main/resources/images/system.jpg b/src/main/resources/images/system.jpg
new file mode 100644
index 0000000..774940f
Binary files /dev/null and b/src/main/resources/images/system.jpg differ
diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties
new file mode 100644
index 0000000..cad8296
--- /dev/null
+++ b/src/main/resources/log4j2.properties
@@ -0,0 +1,23 @@
+name=PropertiesConfig
+appenders = console, file
+
+appender.console.type = Console
+appender.console.name = STDOUT
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
+
+appender.file.type = File
+appender.file.name = LOGFILE
+appender.file.fileName=/tmp/planetSystemSimulator.log
+appender.file.layout.type=PatternLayout
+appender.file.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
+
+loggers=file
+logger.file.name=guru.springframework.blog.log4j2properties
+logger.file.level = debug
+logger.file.appenderRefs = file
+logger.file.appenderRef.file.ref = LOGFILE
+
+rootLogger.level = debug
+rootLogger.appenderRefs = stdout
+rootLogger.appenderRef.file.ref = LOGFILE
\ No newline at end of file
diff --git a/src/main/resources/style.css b/src/main/resources/style.css
new file mode 100644
index 0000000..7529d6f
--- /dev/null
+++ b/src/main/resources/style.css
@@ -0,0 +1,26 @@
+.window {
+ -fx-background-image: url("images/system.jpg");
+}
+.field {
+ -fx-opacity: 0.9;
+}
+.help {
+ -fx-border-width: 1px;
+}
+.help:hover {
+ -fx-background-color: #2A4480;
+ -fx-border-width: 1px;
+ -fx-text-fill: white;
+}
+
+.load {
+ -fx-border-width: 1px;
+}
+.load:hover {
+ -fx-background-color: #204913;
+ -fx-border-width: 1px;
+ -fx-text-fill: white;
+}
+.text {
+ -fx-text-fill: white;
+}
\ No newline at end of file
diff --git a/src/test/java/LogicTest.java b/src/test/java/LogicTest.java
new file mode 100644
index 0000000..475a0e4
--- /dev/null
+++ b/src/test/java/LogicTest.java
@@ -0,0 +1,42 @@
+import simulator.model.LogicManager;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class LogicTest {
+ static LogicManager logic = new LogicManager();
+
+ @Test
+ public void speedX() {
+ assertEquals(0, logic.speedX(0, 0));
+ assertEquals(-3, Math.round(logic.speedX(3, -3)));
+ }
+
+ @Test
+ public void speedY() {
+ assertEquals(0, logic.speedY(0, 0));
+ assertEquals(0, Math.round(logic.speedY(3, -3)));
+ }
+
+ @Test
+ public void acceleration() {
+ assertEquals(0, Math.abs(logic.acceleration(0, 80, 400, 500, 80)));
+ assertEquals(-0.0625, logic.acceleration(4, 80, 400, 500, 80));
+ }
+
+ @Test
+ public void distance() {
+ assertEquals(5, logic.distance(0, 0, 3, 4));
+ }
+
+ @Test
+ public void timePortation() {
+ double x = 200;
+ var tpXY = logic.timePortation(1, x, 451.2, 1.25, 2.64, 0.27, 1.33, 6.67, 255);
+ assertEquals(214, Math.round(tpXY[0]));
+ assertEquals(480, Math.round(tpXY[1]));
+ assertEquals(1, Math.round(tpXY[2]));
+ assertEquals(3, Math.round(tpXY[3]));
+ assertNotEquals(x, tpXY[0], 0.0);
+ }
+}
\ No newline at end of file