├── CMakeLists.txt ├── LICENSE ├── README.md ├── include ├── Camera.hpp ├── DynamicSystemParser │ ├── DynamicSystemParser.hpp │ └── Impl │ │ └── DynamicSystemParserImpl.hpp ├── DynamicSystemWrapper.hpp ├── DynamicSystems │ ├── DynamicSystem.hpp │ ├── Impl │ │ ├── DynamicSystemImpl.hpp │ │ └── DynamicSystemInternal.hpp │ └── SystemsBase │ │ ├── SystemsBase.hpp │ │ └── SystemsBaseGetImpl.hpp ├── Locus.hpp ├── Model │ ├── Impl │ │ └── ModelImpl.hpp │ └── Model.hpp ├── Parser │ ├── Lexer.hpp │ ├── Parser.hpp │ ├── ParserException.hpp │ └── ParserNodes.hpp ├── PointsViewQGLWidget.hpp ├── Preferences.hpp ├── ShaderController.hpp ├── StoppableTask.hpp ├── VideoEncoder.hpp ├── Window.hpp └── WindowPreferences.hpp ├── materials ├── FragmentShader.fsh ├── GeometryShaderLines.gsh ├── GeometryShaderPoints.gsh ├── Resources.qrc └── VertexShader.vsh ├── src ├── Camera.cpp ├── DynamicSystemParser │ └── DynamicSystemParser.cpp ├── Locus.cpp ├── Parser │ ├── Lexer.cpp │ └── Parser.cpp ├── PointsViewQGLWidget.cpp ├── Preferences.cpp ├── ShaderController.cpp ├── VideoEncoder.cpp ├── Window.cpp ├── WindowPreferences.cpp ├── form.ui ├── formPreferences.ui └── main.cpp └── test ├── CMakeLists.txt ├── testAll.cpp ├── testParser.cpp └── testSystems.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 4 | 5 | set(CMAKE_AUTOUIC ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Ofast -pipe -Wall -Wextra") 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS") 14 | 15 | project(DynamicSystems) 16 | 17 | include_directories(include/) 18 | 19 | find_package(Qt5Widgets REQUIRED) 20 | find_package(Qt5OpenGL REQUIRED) 21 | find_package(OpenGL REQUIRED) 22 | find_package(Threads REQUIRED) 23 | 24 | 25 | find_path(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h) 26 | find_library(AVCODEC_LIBRARY avcodec) 27 | 28 | find_path(AVFORMAT_INCLUDE_DIR libavformat/avformat.h) 29 | find_library(AVFORMAT_LIBRARY avformat) 30 | 31 | find_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h) 32 | find_library(AVUTIL_LIBRARY avutil) 33 | 34 | find_path(AVSWS_INCLUDE_DIR libswscale/swscale.h) 35 | find_library(AVSWS_LIBRARY swscale) 36 | 37 | add_executable(DynamicSystems 38 | src/main.cpp 39 | src/WindowPreferences.cpp 40 | src/Camera.cpp 41 | src/PointsViewQGLWidget.cpp 42 | src/Window.cpp 43 | src/Locus.cpp 44 | src/Preferences.cpp 45 | src/VideoEncoder.cpp 46 | src/ShaderController.cpp 47 | src/DynamicSystemParser/DynamicSystemParser.cpp 48 | src/Parser/Parser.cpp 49 | src/Parser/Lexer.cpp 50 | include/Preferences.hpp 51 | include/Camera.hpp 52 | include/WindowPreferences.hpp 53 | include/Model/Model.hpp 54 | include/Model/Impl/ModelImpl.hpp 55 | include/DynamicSystems/DynamicSystem.hpp 56 | include/DynamicSystems/SystemsBase/SystemsBase.hpp 57 | include/DynamicSystems/SystemsBase/SystemsBaseGetImpl.hpp 58 | include/DynamicSystems/Impl/DynamicSystemImpl.hpp 59 | include/DynamicSystems/Impl/DynamicSystemInternal.hpp 60 | include/DynamicSystemParser/DynamicSystemParser.hpp 61 | include/DynamicSystemParser/Impl/DynamicSystemParserImpl.hpp 62 | include/Window.hpp 63 | include/DynamicSystemWrapper.hpp 64 | include/PointsViewQGLWidget.hpp 65 | include/StoppableTask.hpp 66 | include/Locus.hpp 67 | include/VideoEncoder.hpp 68 | include/ShaderController.hpp 69 | include/Parser/Parser.hpp 70 | include/Parser/ParserException.hpp 71 | include/Parser/ParserNodes.hpp 72 | include/Parser/Lexer.hpp 73 | src/form.ui 74 | src/formPreferences.ui 75 | materials/Resources.qrc 76 | ) 77 | 78 | qt5_use_modules(DynamicSystems Widgets OpenGL) 79 | 80 | target_include_directories(DynamicSystems PRIVATE 81 | ${AVCODEC_INCLUDE_DIR} 82 | ${AVFORMAT_INCLUDE_DIR} 83 | ${AVUTIL_INCLUDE_DIR} 84 | ${AVSWS_INCLUDE_DIR} 85 | ) 86 | 87 | target_link_libraries(DynamicSystems 88 | ${QT_LIBRARIES} 89 | ${OPENGL_LIBRARIES} 90 | ${AVCODEC_LIBRARY} 91 | ${AVFORMAT_LIBRARY} 92 | ${AVUTIL_LIBRARY} 93 | ${AVSWS_LIBRARY} 94 | Threads::Threads 95 | ) 96 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 vladnosiv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modeling and visualization of dynamic systems 2 | 3 | ![Hedley Attractor](https://github.com/chaoticsyst/dynsys/blob/master/media/Hadley.gif) 4 | 5 | ## Introduction 6 | 7 | There are first-order differential equation systems on coordinates, which describe the position of points in space over time. Our application simulates them, thus allowing a convenient exploration of this area. 8 | 9 | In particular, the application already contains 35 systems that simulate classic strange attractors. 10 | 11 | Examples of equation systems: [Lorenz Attractor](https://en.wikipedia.org/wiki/Lorenz_system) and [Rössler Attractor](https://en.wikipedia.org/wiki/R%C3%B6ssler_attractor) 12 | 13 | ## UI 14 | 15 | The interface is implemented using Qt5. It includes: 16 | * Model selection 17 | * Selection of constants characterizing the model 18 | * Detailed settings of modeling and visualization 19 | * Video recording capability 20 | * Possibility to pause and fast-forward time 21 | 22 | ![UI screenshot](https://github.com/chaoticsyst/dynsys/blob/master/media/Interface.png) 23 | 24 | ## Simulation 25 | 26 | Simulation is carried out using the fourth-order Runge-Kutta method, with a constant step. Written in the style of metaprogramming to achieve maximum performance. 27 | 28 | To support models that are not integrated into the application, a mathematical equation parser has been implemented, which supports standard operations (+ - * /), brackets, and basic mathematical functions (sin, cos, exp, log, etc.). 29 | 30 | No additional libraries are used for calculations. 31 | 32 | ## Visualization 33 | 34 | Visualization is performed using OpenGL and the Qt wrapper over it. 35 | 36 | The application takes several points around the initial position, calculates their trajectories, and then displays the points with some tail of their trajectories. Points calculated by the model are interpolated by the Catmull-Rom curve. 37 | 38 | Free movement is implemented. 39 | 40 | For convenience, it is possible to record video in avi format. This uses the libav library. You can also take screenshots. 41 | 42 | ## OS Support 43 | 44 | The application is supported by the following operating systems: 45 | * Linux 46 | * Windows 47 | * MacOS 48 | 49 | ## Installation Instructions 50 | Required library versions: 51 | * Qt5 — 5.10 and higher. 52 | * libav (libavcodec, libavformat, libavutil, libswscale) — 57 and higher (but it is better to have at least 58). 53 | * OpenGL — 3.3 and higher. 54 | 55 | Next are instructions for specific OSs. 56 | 57 |
58 | 59 | Linux (U)buntu 60 | 61 | 62 | * Installing Qt: 63 | ``` 64 | sudo apt-get install qt5-default 65 | ``` 66 | 67 | * Installing libav: 68 | ``` 69 | sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev 70 | ``` 71 | 72 | * Updating OpenGL: 73 | * To check the version: 74 | ``` 75 | glxinfo | grep "OpenGL version" 76 | ``` 77 | * If the version is below 3.3, you need to update: 78 | ``` 79 | sudo add-apt-repository ppa:oibaf/graphics-drivers 80 | sudo apt-get update 81 | sudo apt-get upgrade 82 | ``` 83 | 84 |
85 | 86 |
87 | 88 | Windows 89 | 90 | 91 | * To get the necessary version of C++ (g++7 and above), download and install [MinGW](https://sourceforge.net/projects/mingw-w64/files/Multilib%20Toolchains%28Targetting%20Win32%20and%20Win64%29/ray_linn/gcc-9.x-with-ada/), and also add `/path/to/MinGW/bin/` to `PATH`. 92 | 93 | * Install [Cmake](https://cmake.org/download/). It should be added to `PATH` automatically. 94 | 95 | * Install Qt5, following the instructions [from here](https://doc.qt.io/archives/qt-4.8/install-win.html). 96 | 97 | * Download [libav](http://builds.libav.org/windows/nightly-gpl/libav-x86_64-w64-mingw32-20180108.7z) and unpack it in a convenient location for you. Then add `/path/to/libav/bin` and `/path/to/libav/include` to `PATH`. 98 | 99 |
100 | 101 | ## How to use? 102 | 103 | To visualize a model, one needs to: 104 | 105 | * Select the model to be simulated: 106 | * You can choose one of the 35 presented models 107 | * You can enter your own differential equations 108 | * Select the constants that characterize the model: 109 | * You can choose classical values 110 | * You can enter your own values 111 | * Set the necessary settings: 112 | * Camera settings: 113 | * Modeling settings: 114 | * Visualization settings: 115 | 116 | Controls: 117 | * `WASDQE` — move forward / left / backward / right / up / down 118 | 119 | * `F` — return to the original position 120 | * `R` — take a screenshot 121 | 122 | ## Examples 123 | 124 | ![Dequan Li Attractor](https://github.com/chaoticsyst/dynsys/blob/master/media/Dequan%20Li.gif) 125 | 126 | ![Example 1](https://github.com/chaoticsyst/dynsys/blob/master/media/Example1.png) 127 | 128 | ![Example 2](https://github.com/chaoticsyst/dynsys/blob/master/media/Example2.png) 129 | 130 | ## Links 131 | 132 | Channels: 133 | * [YouTube](https://www.youtube.com/channel/UCL6pzFtbNd7fZKhABJCvV8g?view_as=subscriber) 134 | 135 | * [Telegram](https://t.me/strangeattractors) 136 | 137 | ## About us 138 | 139 | The application is being developed as part of a project work at the HSE University in St. Petersburg. 140 | 141 | Authors: [Vladislav Nosivskoy](https://github.com/vladnosiv), [Roman Venediktov](https://github.com/e2e4b6b7), [Kirill Karnaukhov](https://github.com/kkarnauk) 142 | -------------------------------------------------------------------------------- /include/Camera.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Preferences.hpp" 13 | 14 | namespace Camera { 15 | 16 | class Camera final { 17 | public: 18 | Camera(); 19 | ~Camera() = default; 20 | 21 | Camera(const Camera &) = delete; 22 | Camera(Camera &&) = delete; 23 | Camera &operator=(const Camera &) = delete; 24 | Camera &operator=(Camera &&) = delete; 25 | 26 | 27 | void applyDeltaPosition(const QVector3D &delta); 28 | void recalculatePerspective(int width, int height); 29 | void recalculateTarget(const QPoint &newMousePosition, float mouseSensitivity); 30 | 31 | void setPosition(const QVector3D &position); 32 | void setTarget(const QVector3D &target); 33 | 34 | void moveForward(float force); 35 | void moveRight(float force); 36 | void moveUp(float force); 37 | 38 | void resetMousePosition(const QPoint &point); 39 | 40 | void setDefault(); 41 | 42 | QMatrix4x4 getMatrix() const; 43 | QVector3D getPosition() const; 44 | QVector3D getTarget() const; 45 | 46 | private: 47 | constexpr static QVector3D worldUp = QVector3D(0, 1, 0); 48 | 49 | QVector3D cameraPosition; 50 | QVector3D cameraTarget; 51 | 52 | QVector3D cameraForward; 53 | QVector3D cameraUp; 54 | QVector3D cameraRight; 55 | 56 | QMatrix4x4 perspectiveMatrix; 57 | 58 | float pitch; 59 | float yaw; 60 | QPoint lastMousePosition; 61 | 62 | bool invalidState; 63 | 64 | void recalculateVectors(); 65 | void normalizeAngles(); 66 | }; 67 | 68 | class KeyboardAndMouseController final : public QObject { 69 | public: 70 | KeyboardAndMouseController(); 71 | ~KeyboardAndMouseController() = default; 72 | 73 | KeyboardAndMouseController(const KeyboardAndMouseController &) = delete; 74 | KeyboardAndMouseController(KeyboardAndMouseController &&) = delete; 75 | KeyboardAndMouseController &operator=(const KeyboardAndMouseController &) = delete; 76 | KeyboardAndMouseController &operator=(KeyboardAndMouseController &&) = delete; 77 | 78 | 79 | void applyKeyPressEvent(QKeyEvent *event); 80 | void applyKeyReleaseEvent(QKeyEvent *event); 81 | void applyMousePressEvent(QMouseEvent *event); 82 | void applyMouseMoveEvent(QMouseEvent *event); 83 | 84 | void recalculatePerspective(int width, int height); 85 | 86 | QMatrix4x4 getMatrix() const; 87 | QVector3D getPosition() const; 88 | QVector3D getTarget() const; 89 | 90 | void setPreferences(const Preferences::Preferences *prefs); 91 | 92 | private slots: 93 | void updateKeys(); 94 | 95 | private: 96 | Q_OBJECT 97 | 98 | Camera camera; 99 | QSet keys; 100 | QTimer *timer; 101 | 102 | const Preferences::Preferences *prefs; 103 | }; 104 | 105 | } //namespace Camera 106 | -------------------------------------------------------------------------------- /include/DynamicSystemParser/DynamicSystemParser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "DynamicSystems/DynamicSystem.hpp" 8 | 9 | namespace DynamicSystemParser { 10 | 11 | template 12 | DynamicSystems::DynamicSystem getDynamicSystem( 13 | const std::string &attractorName, 14 | const std::array &formulae, 15 | const std::vector &variablesNames = {}, 16 | const std::vector>> &interestingConstants = {}, 17 | const std::map &customConstVariables = {}); 18 | 19 | } // namespace DynamicSystemParser 20 | 21 | #include "DynamicSystemParser/Impl/DynamicSystemParserImpl.hpp" -------------------------------------------------------------------------------- /include/DynamicSystemParser/Impl/DynamicSystemParserImpl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "DynamicSystemParser/DynamicSystemParser.hpp" 9 | #include "Parser/Parser.hpp" 10 | 11 | namespace DynamicSystemParser { 12 | 13 | namespace Impl { 14 | 15 | class ParserDerivativesWrapper final { 16 | public: 17 | ParserDerivativesWrapper(std::unique_ptr> variables, 18 | std::unique_ptr xFunc, 19 | std::unique_ptr yFunc, 20 | std::unique_ptr zFunc); 21 | 22 | auto operator()(std::vector &) { 23 | return [&xFuncGet = *xFunc, &yFuncGet = *yFunc, &zFuncGet = *zFunc, &variablesGet = *variables] //TODO 24 | (const Model::Point &point) { 25 | variablesGet[0] = point.x; 26 | variablesGet[1] = point.y; 27 | variablesGet[2] = point.z; 28 | return Model::Point{xFuncGet.calc(), yFuncGet.calc(), zFuncGet.calc()}; 29 | }; 30 | } 31 | 32 | private: 33 | std::shared_ptr> variables; 34 | std::shared_ptr xFunc, yFunc, zFunc; 35 | }; 36 | 37 | ParserDerivativesWrapper parseExpressions(const std::string &xExpr, const std::string &yExpr, const std::string &zExpr, 38 | const std::map &customConstVariables); 39 | 40 | } // namespace Impl 41 | 42 | template 43 | DynamicSystems::DynamicSystem getDynamicSystem( 44 | const std::string &attractorName, 45 | const std::array &formulae, 46 | const std::vector &variablesNames, 47 | const std::vector>> &interestingConstants, 48 | const std::map &customConstVariables) { 49 | using DynamicSystem = DynamicSystems::DynamicSystem; 50 | using LambdaDerivatives = decltype(std::declval().operator()(std::declval &>())); 51 | using DynamicSystemInternal = DynamicSystems::DynamicSystemInternal; 52 | return DynamicSystem{attractorName, formulae, variablesNames, interestingConstants, 53 | DynamicSystemInternal{Impl::parseExpressions(formulae[0], formulae[1], formulae[2], customConstVariables)}}; 54 | } 55 | 56 | } // namespace DynamicSystemParser 57 | -------------------------------------------------------------------------------- /include/DynamicSystemWrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "Model/Model.hpp" 6 | 7 | namespace DynamicSystemWrapper_n { 8 | 9 | inline auto getPushBackAndNormalizeLambda(QVector &vector, float normalizeConstant) { 10 | return [&vector, normalizeConstant](const Model::Point &point) { 11 | vector.push_back( 12 | QVector3D(static_cast(point.x) / normalizeConstant, 13 | static_cast(point.y) / normalizeConstant, 14 | static_cast(point.z) / normalizeConstant) 15 | ); 16 | }; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /include/DynamicSystems/DynamicSystem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Model/Model.hpp" 9 | #include "DynamicSystems/Impl/DynamicSystemInternal.hpp" 10 | 11 | 12 | namespace DynamicSystems { 13 | 14 | 15 | template 16 | class DynamicSystem; 17 | 18 | 19 | template 20 | std::vector> getDefaultSystems(); 21 | 22 | 23 | template 24 | class DynamicSystem final { 25 | public: 26 | template 27 | DynamicSystem(std::string attractorName, 28 | std::array formulae, 29 | std::vector variablesNames, 30 | std::vector>> interestingConstants, 31 | DynamicSystemInternal systemInternal); 32 | 33 | 34 | std::string_view getAttractorName() const; 35 | 36 | std::array getFormulae() const; 37 | 38 | std::vector getVariablesNames() const; 39 | 40 | std::size_t constantsCount() const; 41 | 42 | const std::vector>> &getInterestingConstants() const; 43 | 44 | const std::function &constantValues)> compute; 49 | 50 | private: 51 | const std::string attractorName_; 52 | const std::array formulae_; 53 | const std::vector variablesNames_; 54 | const std::vector>> interestingConstants_; 55 | }; 56 | 57 | 58 | } // namespace DynamicSystem 59 | 60 | #include "DynamicSystems/Impl/DynamicSystemImpl.hpp" 61 | #include "DynamicSystems/SystemsBase/SystemsBaseGetImpl.hpp" -------------------------------------------------------------------------------- /include/DynamicSystems/Impl/DynamicSystemImpl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Model/Model.hpp" 9 | #include "DynamicSystems/DynamicSystem.hpp" 10 | 11 | 12 | namespace DynamicSystems { 13 | 14 | 15 | template 16 | template 17 | DynamicSystem::DynamicSystem( 18 | std::string attractorName, 19 | std::array formulae, 20 | std::vector variablesNames, 21 | std::vector>> interestingConstants, 22 | DynamicSystemInternal systemInternal) : 23 | compute{ 24 | [system = std::move(systemInternal)](LambdaNewPointAction &&newPointAction, 25 | Model::Point point, 26 | int pointsCount, 27 | long double timeDelta, 28 | std::vector &variableValue) { 29 | system.compute(std::forward(newPointAction), 30 | point, pointsCount, timeDelta, variableValue); 31 | } 32 | }, 33 | attractorName_{std::move(attractorName)}, 34 | formulae_{std::move(formulae)}, 35 | variablesNames_{std::move(variablesNames)}, 36 | interestingConstants_{std::move(interestingConstants)} {} 37 | 38 | 39 | template 40 | std::string_view DynamicSystem::getAttractorName() const { 41 | return attractorName_; 42 | } 43 | 44 | template 45 | std::array DynamicSystem::getFormulae() const { 46 | return {formulae_[0], formulae_[1], formulae_[2]}; 47 | } 48 | 49 | template 50 | std::vector DynamicSystem::getVariablesNames() const { 51 | std::vector variablesNamesView; 52 | for (auto &varName : variablesNames_) { 53 | variablesNamesView.push_back(varName); 54 | } 55 | return variablesNamesView; 56 | } 57 | 58 | template 59 | std::size_t DynamicSystem::constantsCount() const { 60 | return variablesNames_.size(); 61 | } 62 | 63 | template 64 | const std::vector>> & 65 | DynamicSystem::getInterestingConstants() const { 66 | return interestingConstants_; 67 | } 68 | 69 | 70 | } // namespace DynamicSystem -------------------------------------------------------------------------------- /include/DynamicSystems/Impl/DynamicSystemInternal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Model/Model.hpp" 8 | 9 | 10 | namespace DynamicSystems { 11 | 12 | 13 | template 14 | class DynamicSystemInternal final { 15 | public: 16 | explicit DynamicSystemInternal( 17 | std::function &)> derivativesFunctionGetter) : 18 | getDerivativesFunction{std::move(derivativesFunctionGetter)} {} 19 | 20 | void compute(LambdaNewPointAction &&newPointAction, 21 | Model::Point point, 22 | int pointsCount, 23 | long double timeDelta, 24 | std::vector &constantValues) const { 25 | Model::generatePoints(std::forward(newPointAction), 26 | point, pointsCount, timeDelta, 27 | getDerivativesFunction(constantValues)); 28 | } 29 | 30 | private: 31 | const std::function &)> getDerivativesFunction; 32 | }; 33 | 34 | 35 | } // namespace DynamicSystem -------------------------------------------------------------------------------- /include/DynamicSystems/SystemsBase/SystemsBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "DynamicSystems/DynamicSystem.hpp" 9 | 10 | 11 | namespace DynamicSystems::AllSystems { 12 | 13 | 14 | template 15 | DynamicSystem getSystemLorenz() { 16 | std::string attractorName = "The Lorenz attractor"; 17 | std::array formulae = {"a*(y - x)", 18 | "x*(r - z) - y", 19 | "x*y - b*z"}; 20 | std::vector constantsNames = {"a", "r", "b"}; 21 | std::vector>> interestingConstants = { 22 | {"Classic values", {10, 28, 8.0 / 3.0}}, 23 | {"Cycle", {10, 100, 8.0 / 3.0}} 24 | }; 25 | auto derivativesFunctionGetter = [](std::vector constValues) { 26 | return [sigma = constValues[0], r = constValues[1], b = constValues[2]](const Model::Point &values) { 27 | return Model::Point{ 28 | sigma * (values.y - values.x), 29 | values.x * (r - values.z) - values.y, 30 | values.x * values.y - b * values.z 31 | }; 32 | }; 33 | }; 34 | return {attractorName, formulae, constantsNames, interestingConstants, 35 | DynamicSystemInternal>()))> 36 | {derivativesFunctionGetter}}; 37 | } 38 | 39 | 40 | template 41 | DynamicSystem getSystemRossler() { 42 | std::string attractorName = "The Rossler attractor"; 43 | std::array formulae = {"-z - x", 44 | "x + a*y", 45 | "b + z*(x - c)"}; 46 | std::vector constantsNames = {"a", "b", "c"}; 47 | std::vector>> interestingConstants = { 48 | {"Classic values", {0.2, 0.2, 5.7}}, 49 | {"Cycle", {0.2, 0.2, 3}}, 50 | {"Classic values 2", {0.1, 0.1, 14}} 51 | }; 52 | auto derivativesFunctionGetter = [](std::vector constValues) { 53 | return [a = constValues[0], b = constValues[1], c = constValues[2]](const Model::Point &values) { 54 | return Model::Point{ 55 | -values.y - values.z, 56 | values.x + a * values.y, 57 | b + values.z * (values.x - c) 58 | }; 59 | }; 60 | }; 61 | return {attractorName, formulae, constantsNames, interestingConstants, 62 | DynamicSystemInternal>()))> 63 | {derivativesFunctionGetter}}; 64 | } 65 | 66 | 67 | template 68 | DynamicSystem getSystemChua() { 69 | std::string attractorName = "The Chua attractor"; 70 | std::array formulae = { 71 | "s*(y - x - (c*x + 0.5*(b - c)(|x + d| - |x - d|) + 0.5*(a - b)(|x + 1| - |x - 1|)))", 72 | "x - y + z", 73 | "-r*y"}; 74 | std::vector constantsNames = {"s", "r", "a", "b", "c", "d"}; 75 | std::vector>> interestingConstants = { 76 | {"Classic values", {11, 14, -0.713, -0.455, 4.6, 7.2}}, 77 | {"Classic values 2", {15.6, 25.58, -2, 0, 0, 0}}, 78 | }; 79 | auto derivativesFunctionGetter = [](std::vector constValues) { 80 | return [s = constValues[0], r = constValues[1], 81 | a = (constValues[2] - constValues[3]) / 2, 82 | b = (constValues[3] - constValues[4]) / 2, 83 | c = 1 + constValues[4], d = constValues[5]](const Model::Point &values) { 84 | return Model::Point{ 85 | s * (values.y - c * values.x 86 | - (b * (std::abs(values.x + d) - std::abs(values.x - d)) + 87 | a * (std::abs(values.x + 1) - std::abs(values.x - 1)))), 88 | values.x - values.y + values.z, 89 | -r * values.y 90 | }; 91 | }; 92 | }; 93 | return {attractorName, formulae, constantsNames, interestingConstants, 94 | DynamicSystemInternal>()))> 95 | {derivativesFunctionGetter}}; 96 | } 97 | 98 | 99 | template 100 | DynamicSystem getSystemHR3() { 101 | std::string attractorName = "The Hindmarsh-Rose system"; 102 | std::array formulae = {"y - a*x^3 + b*x^2 - z + I", 103 | "c - d*x^2 - y", 104 | "r*(s*(x - alpha) - z)"}; 105 | std::vector constantsNames = {"a", "b", "I", "c", "d", "r", "s", "alpha"}; 106 | std::vector>> interestingConstants = { 107 | {"Classic values", {1, 3, 2.7, 1, 5, 0.003, 4, -1.6}} 108 | }; 109 | auto derivativesFunctionGetter = [](std::vector constValues) { 110 | return [a = constValues[0], b = constValues[1], I = constValues[2], c = constValues[3], d = constValues[4], 111 | r = constValues[5], s = constValues[6], alpha = constValues[7]](const Model::Point &values) { 112 | return Model::Point{ 113 | values.y - a * values.x * values.x * values.x + b * values.x * values.x - values.z + I, 114 | c - d * values.x * values.x - values.y, 115 | r * (s * (values.x - alpha) - values.z) 116 | }; 117 | }; 118 | }; 119 | return {attractorName, formulae, constantsNames, interestingConstants, 120 | DynamicSystemInternal>()))> 121 | {derivativesFunctionGetter}}; 122 | } 123 | 124 | 125 | template 126 | DynamicSystem getSystemAizawa() { 127 | std::string attractorName = "The Aizawa attractor"; 128 | std::array formulae = {"(z - b)*x - d*y", 129 | "d*x + (z - b)*y", 130 | "c + a*z - (z^3 / 3) - (x^2 + y^2)*(1+f*z) + g*z*x^3"}; 131 | std::vector constantsNames = {"a", "b", "c", "d", "f", "g"}; 132 | std::vector>> interestingConstants = { 133 | {"Classic values", {0.95, 0.7, 0.6, 3.5, 0.25, 0.1}} 134 | }; 135 | auto derivativesFunctionGetter = [](std::vector constValues) { 136 | return [a = constValues[0], b = constValues[1], c = constValues[2], d = constValues[3], 137 | e = constValues[4], f = constValues[5]](const Model::Point &values) { 138 | return Model::Point{ 139 | values.x * (values.z - b) - d * values.y, 140 | d * values.x + values.y * (values.z - b), 141 | c + a * values.z - values.z * values.z * values.z / 3 - 142 | (values.x * values.x + values.y * values.y) * (1 + e * values.z) + 143 | f * values.z * values.x * values.x * values.x 144 | }; 145 | }; 146 | }; 147 | return {attractorName, formulae, constantsNames, interestingConstants, 148 | DynamicSystemInternal>()))> 149 | {derivativesFunctionGetter}}; 150 | } 151 | 152 | 153 | template 154 | DynamicSystem getSystemChenLee() { 155 | std::string attractorName = "The Chen-Lee attractor"; 156 | std::array formulae = {"a*x - y*z", 157 | "b*y + x*z", 158 | "c*z + x*y/3"}; 159 | std::vector constantsNames = {"a", "b", "c"}; 160 | std::vector>> interestingConstants = { 161 | {"Classic values", {5, -10, -0.38}} 162 | }; 163 | auto derivativesFunctionGetter = [](std::vector constValues) { 164 | return [a = constValues[0], b = constValues[1], c = constValues[2]](const Model::Point &values) { 165 | return Model::Point{ 166 | a * values.x - values.y * values.z, 167 | b * values.y + values.x * values.z, 168 | c * values.z + values.x * values.y / 3 169 | 170 | }; 171 | }; 172 | }; 173 | return {attractorName, formulae, constantsNames, interestingConstants, 174 | DynamicSystemInternal>()))> 175 | {derivativesFunctionGetter}}; 176 | } 177 | 178 | 179 | template 180 | DynamicSystem getSystemAnishenkoAstakhov() { 181 | std::string attractorName = "The Anishenko-Astakhov attractor"; 182 | std::array formulae = {"x*(a - z) + y", 183 | "-x", 184 | "b*(x*(x + |x|)/2 - z)"}; 185 | std::vector constantsNames = {"a", "b"}; 186 | std::vector>> interestingConstants = { 187 | {"Classic values", {1.2, 0.5}} 188 | }; 189 | auto derivativesFunctionGetter = [](std::vector constValues) { 190 | return [a = constValues[0], b = constValues[1]](const Model::Point &values) { 191 | return Model::Point{ 192 | values.x * (a - values.z) + values.y, 193 | -values.x, 194 | b * (-values.z + (values.x > 0 ? values.x * values.x : 0)) 195 | }; 196 | }; 197 | }; 198 | return {attractorName, formulae, constantsNames, interestingConstants, 199 | DynamicSystemInternal>()))> 200 | {derivativesFunctionGetter}}; 201 | } 202 | 203 | 204 | template 205 | DynamicSystem getSystemBouali2() { 206 | std::string attractorName = "The second Bouali attractor"; 207 | std::array formulae = {"x*(4 - y) + a*z", 208 | "-y*(1 - x^2)", 209 | "-x*(1.5 - b*z) - 0.05*z"}; 210 | std::vector constantsNames = {"a", "b"}; 211 | std::vector>> interestingConstants = { 212 | {"Classic values", {0.3, 1}} 213 | }; 214 | auto derivativesFunctionGetter = [](std::vector constValues) { 215 | return [a = constValues[0], b = constValues[1]](const Model::Point &values) { 216 | return Model::Point{ 217 | values.x * (4 - values.y) + a * values.z, 218 | -values.y * (1 - values.x * values.x), 219 | -values.x * (1.5 - b * values.z) - 0.05 * values.z 220 | }; 221 | }; 222 | }; 223 | return {attractorName, formulae, constantsNames, interestingConstants, 224 | DynamicSystemInternal>()))> 225 | {derivativesFunctionGetter}}; 226 | } 227 | 228 | 229 | template 230 | DynamicSystem getSystemBurkeShaw() { 231 | std::string attractorName = "The Burke-Shaw attractor"; 232 | std::array formulae = {"-a*(x + y)", 233 | "-y - a*x*z", 234 | "a*x*z + b"}; 235 | std::vector constantsNames = {"a", "b"}; 236 | std::vector>> interestingConstants = { 237 | {"Classic values", {10, 4.272}} 238 | }; 239 | auto derivativesFunctionGetter = [](std::vector constValues) { 240 | return [a = constValues[0], b = constValues[1]](const Model::Point &values) { 241 | return Model::Point{ 242 | -a * (values.x + values.y), 243 | -values.y - a * values.x * values.z, 244 | a * values.x * values.y + b 245 | }; 246 | }; 247 | }; 248 | return {attractorName, formulae, constantsNames, interestingConstants, 249 | DynamicSystemInternal>()))> 250 | {derivativesFunctionGetter}}; 251 | } 252 | 253 | 254 | template 255 | DynamicSystem getSystemChenCelikovsky() { 256 | std::string attractorName = "The Chen-Celikovsky attractor"; 257 | std::array formulae = {"a*(y - x)", 258 | "-x*z + c*y", 259 | "x*y - b*z"}; 260 | std::vector constantsNames = {"a", "b", "c"}; 261 | std::vector>> interestingConstants = { 262 | {"Classic values", {36, 3, 20}} 263 | }; 264 | auto derivativesFunctionGetter = [](std::vector constValues) { 265 | return [a = constValues[0], b = constValues[1], c = constValues[2]](const Model::Point &values) { 266 | return Model::Point{ 267 | a * (values.y - values.x), 268 | -values.x * values.z + c * values.y, 269 | values.x * values.y - b * values.z 270 | }; 271 | }; 272 | }; 273 | return { 274 | attractorName, formulae, constantsNames, interestingConstants, 275 | DynamicSystemInternal>()))> 276 | {derivativesFunctionGetter} 277 | }; 278 | } 279 | 280 | 281 | template 282 | DynamicSystem getSystemCoullet() { 283 | std::string attractorName = "The Collet attractor"; 284 | std::array formulae = {"y", 285 | "z", 286 | "a*x + b*y + c*z + d*x^3"}; 287 | std::vector constantsNames = {"a", "b", "c", "d"}; 288 | std::vector>> interestingConstants = { 289 | {"Classic values", {0.8, -1.1, -0.45, -1}} 290 | }; 291 | auto derivativesFunctionGetter = [](std::vector constValues) { 292 | return [a = constValues[0], b = constValues[1], c = constValues[2], 293 | d = constValues[3]](const Model::Point &values) { 294 | return Model::Point{ 295 | values.y, 296 | values.z, 297 | values.x * (d * values.x * values.x + a) + b * values.y + c * values.z 298 | }; 299 | }; 300 | }; 301 | return {attractorName, formulae, constantsNames, interestingConstants, 302 | DynamicSystemInternal>()))> 303 | {derivativesFunctionGetter}}; 304 | } 305 | 306 | 307 | template 308 | DynamicSystem getSystemDadras() { 309 | std::string attractorName = "The Dadras attractor"; 310 | std::array formulae = {"y - a*x + b*y*z", 311 | "c*y - x*z + z", 312 | "d*x*y - f*z"}; 313 | std::vector constantsNames = {"a", "b", "c", "d", "f"}; 314 | std::vector>> interestingConstants = { 315 | {"Classic values", {3, 2.7, 1.7, 2, 9}} 316 | }; 317 | auto derivativesFunctionGetter = [](std::vector constValues) { 318 | return [a = constValues[0], b = constValues[1], c = constValues[2], d = constValues[3], 319 | e = constValues[4]](const Model::Point &values) { 320 | return Model::Point{ 321 | values.y * (1 + b * values.z) - a * values.x, 322 | c * values.y + values.z * (1 - values.x), 323 | d * values.x * values.y - e * values.z 324 | }; 325 | }; 326 | }; 327 | return {attractorName, formulae, constantsNames, interestingConstants, 328 | DynamicSystemInternal>()))> 329 | {derivativesFunctionGetter}}; 330 | } 331 | 332 | 333 | template 334 | DynamicSystem getSystemDequanLi() { 335 | std::string attractorName = "The Dequan Li attractor"; 336 | std::array formulae = {"a*(y - z) + c*x*z", 337 | "f*x + g*y - x*z", 338 | "b*z + x*y - d*x^2"}; 339 | std::vector constantsNames = {"a", "b", "c", "d", "f", "g"}; 340 | std::vector>> interestingConstants = { 341 | {"Classic values", {40, 1.833, 0.16, 0.65, 55, 20}} 342 | }; 343 | auto derivativesFunctionGetter = [](std::vector constValues) { 344 | return [a = constValues[0], b = constValues[1], c = constValues[2], d = constValues[3], 345 | e = constValues[4], f = constValues[5]](const Model::Point &values) { 346 | return Model::Point{ 347 | a * (values.y - values.x) + c * values.x * values.z, 348 | values.x * (e - values.z) + f * values.y, 349 | b * values.z + values.x * (values.y - d * values.x) 350 | }; 351 | }; 352 | }; 353 | return {attractorName, formulae, constantsNames, interestingConstants, 354 | DynamicSystemInternal>()))> 355 | {derivativesFunctionGetter}}; 356 | } 357 | 358 | 359 | template 360 | DynamicSystem getSystemFinance() { 361 | std::string attractorName = "The Finance attractor"; 362 | std::array formulae = {"(1/b - a)*x + z + x*y", 363 | "-b*y - x^2", 364 | "-x - c*z"}; 365 | std::vector constantsNames = {"a", "b", "c"}; 366 | std::vector>> interestingConstants = { 367 | {"Classic values", {0.001, 0.2, 1.1}} 368 | }; 369 | auto derivativesFunctionGetter = [](std::vector constValues) { 370 | return [a_ = (1 / constValues[1] - constValues[0]), b = constValues[1], 371 | c = constValues[2]](const Model::Point &values) { 372 | return Model::Point{ 373 | values.x * (a_ + values.y) + values.z, 374 | -b * values.y - values.x * values.x, 375 | -values.x - c * values.z 376 | }; 377 | }; 378 | }; 379 | return {attractorName, formulae, constantsNames, interestingConstants, 380 | DynamicSystemInternal>()))> 381 | {derivativesFunctionGetter}}; 382 | } 383 | 384 | 385 | template 386 | DynamicSystem getSystemFourWing() { 387 | std::string attractorName = "The Four-Wing attractor"; 388 | std::array formulae = {"a*x - b*y*z", 389 | "c*y + x*z", 390 | "f*x - d*z + x*z"}; 391 | std::vector constantsNames = {"a", "b", "c", "d", "f"}; 392 | std::vector>> interestingConstants = { 393 | {"Classic values", {4, 6, 10, 5, 1}} 394 | }; 395 | auto derivativesFunctionGetter = [](std::vector constValues) { 396 | return [a = constValues[0], b = constValues[1], c = constValues[2], d = constValues[3], 397 | e = constValues[4]](const Model::Point &values) { 398 | return Model::Point{ 399 | a * values.x - b * values.y * values.z, 400 | c * values.y + values.x * values.z, 401 | values.x * (e + values.z) - d * values.z 402 | }; 403 | }; 404 | }; 405 | return {attractorName, formulae, constantsNames, interestingConstants, 406 | DynamicSystemInternal>()))> 407 | {derivativesFunctionGetter}}; 408 | } 409 | 410 | 411 | template 412 | DynamicSystem getSystemGenesioTesi() { 413 | std::string attractorName = "The Genesio-Tesi attractor"; 414 | std::array formulae = {"y", 415 | "z", 416 | "a*x + b*y + c*z + x^2"}; 417 | std::vector constantsNames = {"a", "b", "c"}; 418 | std::vector>> interestingConstants = { 419 | {"Classic values", {0.44, 1.1, 1}} 420 | }; 421 | auto derivativesFunctionGetter = [](std::vector constValues) { 422 | return [a = constValues[0], b = constValues[1], c = constValues[2]](const Model::Point &values) { 423 | return Model::Point{ 424 | values.y, 425 | values.z, 426 | values.x * (values.x - c) - a * values.z - b * values.y 427 | }; 428 | }; 429 | }; 430 | return {attractorName, formulae, constantsNames, interestingConstants, 431 | DynamicSystemInternal>()))> 432 | {derivativesFunctionGetter}}; 433 | } 434 | 435 | 436 | template 437 | DynamicSystem getSystemHadley() { 438 | std::string attractorName = "The Hadley attractor"; 439 | std::array formulae = {"-y^2 - z^2 - a*x + a*c", 440 | "x*y - b*x*y - y + d", 441 | "b*x*y + x*z - z"}; 442 | std::vector constantsNames = {"a", "b", "c", "d"}; 443 | std::vector>> interestingConstants = { 444 | {"Classic values", {0.2, 4, 8, 1}} 445 | }; 446 | auto derivativesFunctionGetter = [](std::vector constValues) { 447 | return [a = constValues[0], b = constValues[1], c_ = constValues[0] * constValues[2], 448 | d = constValues[3]](const Model::Point &values) { 449 | return Model::Point{ 450 | -values.y * values.y - values.z * values.z - a * values.x + c_, 451 | values.x * (values.y - b * values.z) - values.y + d, 452 | values.x * (b * values.y + values.z) - values.z 453 | }; 454 | }; 455 | }; 456 | return {attractorName, formulae, constantsNames, interestingConstants, 457 | DynamicSystemInternal>()))> 458 | {derivativesFunctionGetter}}; 459 | } 460 | 461 | 462 | template 463 | DynamicSystem getSystemHalvorsen() { 464 | std::string attractorName = "The Halvorsen attractor"; 465 | std::array formulae = {"-a*x - 4*(y + z) - y^2", 466 | "-a*y - 4*(x + x) - z^2", 467 | "-a*z - 4*(x + y) - x^2"}; 468 | std::vector constantsNames = {"a"}; 469 | std::vector>> interestingConstants = { 470 | {"Classic values", {1.4}} 471 | }; 472 | auto derivativesFunctionGetter = [](std::vector constValues) { 473 | return [a = constValues[0]](const Model::Point &values) { 474 | return Model::Point{ 475 | -a * values.x - 4 * (values.y + values.z) - values.y * values.y, 476 | -a * values.y - 4 * (values.z + values.x) - values.z * values.z, 477 | -a * values.z - 4 * (values.x + values.y) - values.x * values.x 478 | }; 479 | }; 480 | }; 481 | return {attractorName, formulae, constantsNames, interestingConstants, 482 | DynamicSystemInternal>()))> 483 | {derivativesFunctionGetter}}; 484 | } 485 | 486 | 487 | template 488 | DynamicSystem getSystemLiuChen() { 489 | std::string attractorName = "The Liu-Chen attractor"; 490 | std::array formulae = {"a*y + b*x + c*y*z", 491 | "d*y - z + f*x*z", 492 | "g*z + h*x*y"}; 493 | std::vector constantsNames = {"a", "b", "c", "d", "f", "g", "h"}; 494 | std::vector>> interestingConstants = { 495 | {"Classic values", {2.4, -3.78, 14, -11, 4, 5.58, 1}} 496 | }; 497 | auto derivativesFunctionGetter = [](std::vector constValues) { 498 | return [a = constValues[0], b = constValues[1], c = constValues[2], d = constValues[3], 499 | e = constValues[4], f = constValues[5], g = constValues[6]](const Model::Point &values) { 500 | return Model::Point{ 501 | values.y * (a + c * values.z) + b * values.x, 502 | d * values.y + values.z * (e * values.x - 1), 503 | f * values.z + g * values.x * values.y 504 | }; 505 | }; 506 | }; 507 | return {attractorName, formulae, constantsNames, interestingConstants, 508 | DynamicSystemInternal>()))> 509 | {derivativesFunctionGetter}}; 510 | } 511 | 512 | 513 | template 514 | DynamicSystem getSystemLorenzMod1() { 515 | std::string attractorName = "The Lorenz Mod 1 attractor"; 516 | std::array formulae = {"-a*x + y^2 - z^2 + a*c", 517 | "x*(y - b*z) + d", 518 | "z + x*(b*y + z)"}; 519 | std::vector constantsNames = {"a", "b", "c", "d"}; 520 | std::vector>> interestingConstants = { 521 | {"Classic values", {0.1, 4, 14, 0.08}} 522 | }; 523 | auto derivativesFunctionGetter = [](std::vector constValues) { 524 | return [a = constValues[0], b = constValues[1], c_ = constValues[0] * constValues[2], 525 | d = constValues[3]](const Model::Point &values) { 526 | return Model::Point{ 527 | -a * values.x + values.y * values.y - values.z * values.z + c_, 528 | values.x * (values.y - b * values.z) + d, 529 | values.z + values.x * (b * values.y + values.z) 530 | }; 531 | }; 532 | }; 533 | return {attractorName, formulae, constantsNames, interestingConstants, 534 | DynamicSystemInternal>()))> 535 | {derivativesFunctionGetter}}; 536 | } 537 | 538 | 539 | template 540 | DynamicSystem getSystemLorenzMod2() { 541 | std::string attractorName = "The Lorenz Mod 2 attractor"; 542 | std::array formulae = {"-a*x + y^2 - z^2 + a*c", 543 | "x*(y - b*z) + d", 544 | "-z + x*(b*y + z)"}; 545 | std::vector constantsNames = {"a", "b", "c", "d"}; 546 | std::vector>> interestingConstants = { 547 | {"Classic values", {0.9, 5, 9.9, 1}} 548 | }; 549 | auto derivativesFunctionGetter = [](std::vector constValues) { 550 | return [a = constValues[0], b = constValues[1], c_ = constValues[0] * constValues[2], 551 | d = constValues[3]](const Model::Point &values) { 552 | return Model::Point{ 553 | -a * values.x + values.y * values.y - values.z * values.z + c_, 554 | values.x * (values.y - b * values.z) + d, 555 | -values.z + values.x * (b * values.y + values.z) 556 | }; 557 | }; 558 | }; 559 | return {attractorName, formulae, constantsNames, interestingConstants, 560 | DynamicSystemInternal>()))> 561 | {derivativesFunctionGetter}}; 562 | } 563 | 564 | 565 | template 566 | DynamicSystem getSystemLuChen() { 567 | std::string attractorName = "The Lu-Chen attractor"; 568 | std::array formulae = {"(-a*b*x)/(a+b) - y*z + c", 569 | "a*y + x*z", 570 | "b*z + x*y"}; 571 | std::vector constantsNames = {"a", "b", "c"}; 572 | std::vector>> interestingConstants = { 573 | {"Classic values", {-10, -4, 18.1}} 574 | }; 575 | auto derivativesFunctionGetter = [](std::vector constValues) { 576 | return [d = constValues[0] * constValues[1] / (constValues[0] + constValues[1]), 577 | a = constValues[0], b = constValues[1], c = constValues[2]](const Model::Point &values) { 578 | return Model::Point{ 579 | d * values.x - values.y * values.z + c, 580 | a * values.y + values.x * values.z, 581 | b * values.z + values.x * values.y 582 | }; 583 | }; 584 | }; 585 | return {attractorName, formulae, constantsNames, interestingConstants, 586 | DynamicSystemInternal>()))> 587 | {derivativesFunctionGetter}}; 588 | } 589 | 590 | 591 | template 592 | DynamicSystem getSystemNewtonLeipnik() { 593 | std::string attractorName = "The Newton-Leipnik attractor"; 594 | std::array formulae = {"-a*x + y + 10*y*z", 595 | "-x - 0.4*y + 5*x*z", 596 | "b*z - 5*x*y"}; 597 | std::vector constantsNames = {"a", "b"}; 598 | std::vector>> interestingConstants = { 599 | {"Classic values", {0.4, 0.175}} 600 | }; 601 | auto derivativesFunctionGetter = [](std::vector constValues) { 602 | return [a = constValues[0], b = constValues[1]](const Model::Point &values) { 603 | return Model::Point{ 604 | -a * values.x + values.y * (1 + 10 * values.z), 605 | values.x * (5 * values.z - 1) - 0.4 * values.y, 606 | b * values.z - 5 * values.x * values.z 607 | }; 608 | }; 609 | }; 610 | return {attractorName, formulae, constantsNames, interestingConstants, 611 | DynamicSystemInternal>()))> 612 | {derivativesFunctionGetter}}; 613 | } 614 | 615 | 616 | template 617 | DynamicSystem getSystemNoseHoover() { 618 | std::string attractorName = "The Nose-Hoover attractor"; 619 | std::array formulae = {"y", 620 | "y*z - x", 621 | "a - y^2"}; 622 | std::vector constantsNames = {"a"}; 623 | std::vector>> interestingConstants = { 624 | {"Classic values", {1.5}} 625 | }; 626 | auto derivativesFunctionGetter = [](std::vector constValues) { 627 | return [a = constValues[0]](const Model::Point &values) { 628 | return Model::Point{ 629 | values.y, 630 | values.y * values.z - values.x, 631 | a - values.y * values.y 632 | }; 633 | }; 634 | }; 635 | return {attractorName, formulae, constantsNames, interestingConstants, 636 | DynamicSystemInternal>()))> 637 | {derivativesFunctionGetter}}; 638 | } 639 | 640 | 641 | template 642 | DynamicSystem getSystemQiChen() { 643 | std::string attractorName = "The Qi-Chen attractor"; 644 | std::array formulae = {"a*(y - x) + x*z", 645 | "x*(c - z) + y", 646 | "x*y - b*z"}; 647 | std::vector constantsNames = {"a", "b", "c"}; 648 | std::vector>> interestingConstants = { 649 | {"Classic values", {38, 8.0 / 3, 80}} 650 | }; 651 | auto derivativesFunctionGetter = [](std::vector constValues) { 652 | return [a = constValues[0], b = constValues[1], c = constValues[2]](const Model::Point &values) { 653 | return Model::Point{ 654 | a * (values.y - values.x) + values.y * values.z, 655 | values.x * (c - values.z) + values.y, 656 | values.x * values.y - b * values.z 657 | }; 658 | }; 659 | }; 660 | return {attractorName, formulae, constantsNames, interestingConstants, 661 | DynamicSystemInternal>()))> 662 | {derivativesFunctionGetter}}; 663 | } 664 | 665 | 666 | template 667 | DynamicSystem getSystemRayleighBenard() { 668 | std::string attractorName = "The Rayleigh-Benard attractor"; 669 | std::array formulae = {"a*(y - x)", 670 | "b*x - y - x*z", 671 | "x*y - c*z"}; 672 | std::vector constantsNames = {"a", "b", "c"}; 673 | std::vector>> interestingConstants = { 674 | {"Classic values", {9, 12, 5}} 675 | }; 676 | auto derivativesFunctionGetter = [](std::vector constValues) { 677 | return [a = constValues[0], b = constValues[1], c = constValues[2]](const Model::Point &values) { 678 | return Model::Point{ 679 | a * (values.y - values.x), 680 | values.x * (b - values.z) - values.y, 681 | values.x * values.y - c * values.z 682 | }; 683 | }; 684 | }; 685 | return {attractorName, formulae, constantsNames, interestingConstants, 686 | DynamicSystemInternal>()))> 687 | {derivativesFunctionGetter}}; 688 | } 689 | 690 | 691 | template 692 | DynamicSystem getSystemRucklige() { 693 | std::string attractorName = "The Rucklige attractor"; 694 | std::array formulae = {"-a*x + b*y - y*z", 695 | "x", 696 | "y^2 - z"}; 697 | std::vector constantsNames = {"a", "b"}; 698 | std::vector>> interestingConstants = { 699 | {"Classic values", {2, 6.7}} 700 | }; 701 | auto derivativesFunctionGetter = [](std::vector constValues) { 702 | return [a = constValues[0], b = constValues[1]](const Model::Point &values) { 703 | return Model::Point{ 704 | values.y * (b - values.z) - a * values.x, 705 | values.x, 706 | values.y * values.y - values.z 707 | }; 708 | }; 709 | }; 710 | return {attractorName, formulae, constantsNames, interestingConstants, 711 | DynamicSystemInternal>()))> 712 | {derivativesFunctionGetter}}; 713 | } 714 | 715 | 716 | template 717 | DynamicSystem getSystemSakaraya() { 718 | std::string attractorName = "The Sakaraya attractor"; 719 | std::array formulae = {"y - x + y*z", 720 | "-x - y + a*x*z", 721 | "z - b*x*y"}; 722 | std::vector constantsNames = {"a", "b"}; 723 | std::vector>> interestingConstants = { 724 | {"Classic values", {0.4, 0.3}} 725 | }; 726 | auto derivativesFunctionGetter = [](std::vector constValues) { 727 | return [a = constValues[0], b = constValues[1]](const Model::Point &values) { 728 | return Model::Point{ 729 | values.y * (values.z + 1) - values.x, 730 | values.x * (a * values.z - 1) - values.y, 731 | values.z - b * values.x * values.y 732 | }; 733 | }; 734 | }; 735 | return {attractorName, formulae, constantsNames, interestingConstants, 736 | DynamicSystemInternal>()))> 737 | {derivativesFunctionGetter}}; 738 | } 739 | 740 | 741 | template 742 | DynamicSystem getSystemShimizuMorioka() { 743 | std::string attractorName = "The Shimizu-Morioka attractor"; 744 | std::array formulae = {"y", 745 | "x*(1 - z) - a*y", 746 | "x^2 - b*z"}; 747 | std::vector constantsNames = {"a", "b"}; 748 | std::vector>> interestingConstants = { 749 | {"Classic values", {0.75, 0.45}} 750 | }; 751 | auto derivativesFunctionGetter = [](std::vector constValues) { 752 | return [a = constValues[0], b = constValues[1]](const Model::Point &values) { 753 | return Model::Point{ 754 | values.y, 755 | values.x * (1 - values.z) - a * values.y, 756 | values.x * values.x - b * values.z 757 | }; 758 | }; 759 | }; 760 | return {attractorName, formulae, constantsNames, interestingConstants, 761 | DynamicSystemInternal>()))> 762 | {derivativesFunctionGetter}}; 763 | } 764 | 765 | 766 | template 767 | DynamicSystem getSystemThomas() { 768 | std::string attractorName = "The Thomas attractor"; 769 | std::array formulae = {"a*x + sin(y)", 770 | "-a*y + sin(z)", 771 | "-a*z + sin(x)"}; 772 | std::vector constantsNames = {"a"}; 773 | std::vector>> interestingConstants = { 774 | {"Classic values", {0.19}} 775 | }; 776 | auto derivativesFunctionGetter = [](std::vector constValues) { 777 | return [a = constValues[0]](const Model::Point &values) { 778 | return Model::Point{ 779 | a * values.x + std::sin(values.y), 780 | -a * values.y + std::sin(values.z), 781 | -a * values.z + std::sin(values.x), 782 | }; 783 | }; 784 | }; 785 | return {attractorName, formulae, constantsNames, interestingConstants, 786 | DynamicSystemInternal>()))> 787 | {derivativesFunctionGetter}}; 788 | } 789 | 790 | 791 | template 792 | DynamicSystem getSystemTSUCS1() { 793 | std::string attractorName = "The TSUCS1 attractor"; 794 | std::array formulae = {"a*(y - z) + c*x*z", 795 | "f*y - x*z", 796 | "b*z + x*y - d*x^2"}; 797 | std::vector constantsNames = {"a", "b", "c", "d", "f"}; 798 | std::vector>> interestingConstants = { 799 | {"Classic values", {40, 0.833, 0.5, 0.65, 20}} 800 | }; 801 | auto derivativesFunctionGetter = [](std::vector constValues) { 802 | return [a = constValues[0], b = constValues[1], c = constValues[2], 803 | d = constValues[3], e = constValues[4]](const Model::Point &values) { 804 | return Model::Point{ 805 | a * (values.y - values.x) + c * values.x * values.z, 806 | e * values.y - values.x * values.z, 807 | b * values.z + values.x * (values.y - d * values.x) 808 | }; 809 | }; 810 | }; 811 | return {attractorName, formulae, constantsNames, interestingConstants, 812 | DynamicSystemInternal>()))> 813 | {derivativesFunctionGetter}}; 814 | } 815 | 816 | 817 | template 818 | DynamicSystem getSystemTSUCS2() { 819 | std::string attractorName = "The TSUCS2 attractor"; 820 | std::array formulae = {"a*(y - z) + d*x*z", 821 | "g*y + b*x - x*z", 822 | "c*z + x*y - f*x^2"}; 823 | std::vector constantsNames = {"a", "b", "c", "d", "f", "g"}; 824 | std::vector>> interestingConstants = { 825 | {"Classic values", {40, 55, 1.833, 0.16, 0.65, 20}} 826 | }; 827 | auto derivativesFunctionGetter = [](std::vector constValues) { 828 | return [a = constValues[0], b = constValues[1], c = constValues[2], d = constValues[3], 829 | e = constValues[4], f = constValues[5]](const Model::Point &values) { 830 | return Model::Point{ 831 | a * (values.y - values.x) + d * values.x * values.z, 832 | f * values.y + values.x * (b - values.z), 833 | c * values.z + values.x * (values.y - e * values.x) 834 | }; 835 | }; 836 | }; 837 | return {attractorName, formulae, constantsNames, interestingConstants, 838 | DynamicSystemInternal>()))> 839 | {derivativesFunctionGetter}}; 840 | } 841 | 842 | 843 | template 844 | DynamicSystem getSystemWangSun() { 845 | std::string attractorName = "The Wang-Sun attractor"; 846 | std::array formulae = {"a*x + c*y*z", 847 | "b*x + d*y - x*z", 848 | "f*z + g*x*y"}; 849 | std::vector constantsNames = {"a", "b", "c", "d", "f", "g"}; 850 | std::vector>> interestingConstants = { 851 | {"Classic values", {0.2, -0.01, 1, -0.4, -1, -1}} 852 | }; 853 | auto derivativesFunctionGetter = [](std::vector constValues) { 854 | return [a = constValues[0], b = constValues[1], c = constValues[2], d = constValues[3], 855 | e = constValues[4], f = constValues[5]](const Model::Point &values) { 856 | return Model::Point{ 857 | a * values.x + c * values.y * values.z, 858 | values.x * (b - values.z) + d * values.y, 859 | e * values.z + f * values.x * values.y 860 | }; 861 | }; 862 | }; 863 | return {attractorName, formulae, constantsNames, interestingConstants, 864 | DynamicSystemInternal>()))> 865 | {derivativesFunctionGetter}}; 866 | } 867 | 868 | 869 | template 870 | DynamicSystem getSystemWimolBanlue() { 871 | std::string attractorName = "The Wimol-Banlue attractor"; 872 | std::array formulae = {"y - x", 873 | "-z*tanh(x)", 874 | "x*y + |y| - a"}; 875 | std::vector constantsNames = {"a"}; 876 | std::vector>> interestingConstants = { 877 | {"Classic values", {2}} 878 | }; 879 | auto derivativesFunctionGetter = [](std::vector constValues) { 880 | return [a = constValues[0]](const Model::Point &values) { 881 | return Model::Point{ 882 | values.y - values.x, 883 | -values.z * std::tanh(values.x), 884 | values.x * values.y + std::abs(values.y) - a 885 | }; 886 | }; 887 | }; 888 | return {attractorName, formulae, constantsNames, interestingConstants, 889 | DynamicSystemInternal>()))> 890 | {derivativesFunctionGetter}}; 891 | } 892 | 893 | 894 | template 895 | DynamicSystem getSystemYuWang() { 896 | std::string attractorName = "The Yu-Wang attractor"; 897 | std::array formulae = {"a*(y - x)", 898 | "b*x - c*x*z", 899 | "e^(x*y) - d*z"}; 900 | std::vector constantsNames = {"a", "b", "c", "d"}; 901 | std::vector>> interestingConstants = { 902 | {"Classic values", {10, 40, 2, 2.5}} 903 | }; 904 | auto derivativesFunctionGetter = [](std::vector constValues) { 905 | return [a = constValues[0], b = constValues[1], c = constValues[2], 906 | d = constValues[3]](const Model::Point &values) { 907 | return Model::Point{ 908 | a * (values.y - values.x), 909 | values.x * (b - c * values.z), 910 | std::exp(values.x * values.y) - d * values.z 911 | }; 912 | }; 913 | }; 914 | return {attractorName, formulae, constantsNames, interestingConstants, 915 | DynamicSystemInternal>()))> 916 | {derivativesFunctionGetter}}; 917 | } 918 | 919 | 920 | } // namespace DynamicSystem::AllSystems 921 | -------------------------------------------------------------------------------- /include/DynamicSystems/SystemsBase/SystemsBaseGetImpl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "DynamicSystems/DynamicSystem.hpp" 6 | #include "DynamicSystems/SystemsBase/SystemsBase.hpp" 7 | 8 | 9 | namespace DynamicSystems { 10 | 11 | 12 | template 13 | std::vector> getDefaultSystems() { 14 | std::vector> systemsVector; 15 | systemsVector.push_back(AllSystems::getSystemLorenz()); 16 | systemsVector.push_back(AllSystems::getSystemRossler()); 17 | systemsVector.push_back(AllSystems::getSystemChua()); 18 | systemsVector.push_back(AllSystems::getSystemHR3()); 19 | systemsVector.push_back(AllSystems::getSystemAizawa()); 20 | systemsVector.push_back(AllSystems::getSystemChenLee()); 21 | systemsVector.push_back(AllSystems::getSystemAnishenkoAstakhov()); 22 | systemsVector.push_back(AllSystems::getSystemBouali2()); 23 | systemsVector.push_back(AllSystems::getSystemBurkeShaw()); 24 | systemsVector.push_back(AllSystems::getSystemChenCelikovsky()); 25 | systemsVector.push_back(AllSystems::getSystemCoullet()); 26 | systemsVector.push_back(AllSystems::getSystemDadras()); 27 | systemsVector.push_back(AllSystems::getSystemDequanLi()); 28 | systemsVector.push_back(AllSystems::getSystemFinance()); 29 | systemsVector.push_back(AllSystems::getSystemFourWing()); 30 | systemsVector.push_back(AllSystems::getSystemGenesioTesi()); 31 | systemsVector.push_back(AllSystems::getSystemHadley()); 32 | systemsVector.push_back(AllSystems::getSystemHalvorsen()); 33 | systemsVector.push_back(AllSystems::getSystemLiuChen()); 34 | systemsVector.push_back(AllSystems::getSystemLorenzMod1()); 35 | systemsVector.push_back(AllSystems::getSystemLorenzMod2()); 36 | systemsVector.push_back(AllSystems::getSystemLuChen()); 37 | systemsVector.push_back(AllSystems::getSystemNewtonLeipnik()); 38 | systemsVector.push_back(AllSystems::getSystemNoseHoover()); 39 | systemsVector.push_back(AllSystems::getSystemQiChen()); 40 | systemsVector.push_back(AllSystems::getSystemRayleighBenard()); 41 | systemsVector.push_back(AllSystems::getSystemRucklige()); 42 | systemsVector.push_back(AllSystems::getSystemSakaraya()); 43 | systemsVector.push_back(AllSystems::getSystemShimizuMorioka()); 44 | systemsVector.push_back(AllSystems::getSystemThomas()); 45 | systemsVector.push_back(AllSystems::getSystemTSUCS1()); 46 | systemsVector.push_back(AllSystems::getSystemTSUCS2()); 47 | systemsVector.push_back(AllSystems::getSystemWangSun()); 48 | systemsVector.push_back(AllSystems::getSystemWimolBanlue()); 49 | systemsVector.push_back(AllSystems::getSystemYuWang()); 50 | return systemsVector; 51 | } 52 | 53 | 54 | } // namespace DynamicSystem 55 | -------------------------------------------------------------------------------- /include/Locus.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Preferences.hpp" 9 | #include "ShaderController.hpp" 10 | 11 | namespace Locus { 12 | 13 | class Locus final { 14 | public: 15 | Locus(QVector &&points_); 16 | Locus() = default; 17 | ~Locus() = default; 18 | 19 | size_t size() const; 20 | 21 | void startWork(); 22 | void endWork(); 23 | 24 | private: 25 | QOpenGLBuffer pointsBuffer; 26 | QVector startIndexes; 27 | }; 28 | 29 | class LocusController final { 30 | public: 31 | LocusController(); 32 | ~LocusController() = default; 33 | 34 | LocusController(const LocusController &) = delete; 35 | LocusController(LocusController &&) = delete; 36 | LocusController &operator=(const LocusController &) = delete; 37 | LocusController &operator=(LocusController &&) = delete; 38 | 39 | void initialize(); 40 | 41 | size_t size() const; 42 | 43 | void addLocus(QVector &&points_); 44 | 45 | void clear(); 46 | 47 | void draw(const QMatrix4x4 &projMatrix, size_t time); 48 | 49 | void setPreferences(const Preferences::Preferences *prefs); 50 | 51 | private: 52 | QVector data; 53 | ShaderController::ShaderController shaderController; 54 | 55 | const Preferences::Preferences *prefs; 56 | }; 57 | 58 | } //namespace Locus 59 | -------------------------------------------------------------------------------- /include/Model/Impl/ModelImpl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Model/Model.hpp" 7 | 8 | namespace Model { 9 | 10 | namespace Impl { 11 | 12 | constexpr long double COORDINATE_VALUE_LIMIT = 1e3; 13 | 14 | template 15 | void generatePointsMainloop(LambdaNewPointAction &&newPointAction, 16 | Point point, 17 | int pointsCount, 18 | LambdaNextPointGenerator &&nextPoint) { 19 | for (int i = 0; i < pointsCount && 20 | std::abs(point.x) < COORDINATE_VALUE_LIMIT && 21 | std::abs(point.y) < COORDINATE_VALUE_LIMIT && 22 | std::abs(point.z) < COORDINATE_VALUE_LIMIT; ++i) { 23 | newPointAction(point = nextPoint(point)); 24 | } 25 | } 26 | 27 | 28 | template 29 | void setNextPointGenerator(LambdaNewPointAction &&newPointAction, 30 | Point point, 31 | int pointsCount, 32 | long double tau, 33 | LambdaDerivatives &&countDerivatives) { 34 | auto nextPoint = [tau, countDerivatives = std::forward(countDerivatives)] 35 | (const Point &point) { 36 | Point k1 = countDerivatives(point); 37 | Point send{ 38 | point.x + k1.x * (tau / 2), 39 | point.y + k1.y * (tau / 2), 40 | point.z + k1.z * (tau / 2) 41 | }; 42 | Point k2 = countDerivatives(send); 43 | send.x = point.x + k2.x * (tau / 2); 44 | send.y = point.y + k2.y * (tau / 2); 45 | send.z = point.z + k2.z * (tau / 2); 46 | Point k3 = countDerivatives(send); 47 | send.x = point.x + k3.x * tau; 48 | send.y = point.y + k3.y * tau; 49 | send.z = point.z + k3.z * tau; 50 | Point k4 = countDerivatives(send); 51 | return Point{ 52 | point.x + (tau / 6) * (k1.x + k4.x + 2 * (k2.x + k3.x)), 53 | point.y + (tau / 6) * (k1.y + k4.y + 2 * (k2.y + k3.y)), 54 | point.z + (tau / 6) * (k1.z + k4.z + 2 * (k2.z + k3.z)) 55 | }; 56 | }; 57 | generatePointsMainloop(std::forward(newPointAction), 58 | point, 59 | pointsCount, 60 | std::move(nextPoint)); 61 | } 62 | 63 | } // namespace Impl 64 | 65 | template 66 | void generatePoints(LambdaNewPointAction &&newPointAction, 67 | Point point, 68 | int pointsCount, 69 | long double tau, 70 | LambdaDerivatives &&countDerivatives) { 71 | Impl::setNextPointGenerator(std::forward(newPointAction), 72 | point, 73 | pointsCount, 74 | tau, 75 | std::forward(countDerivatives)); 76 | } 77 | 78 | }//namespace Model 79 | -------------------------------------------------------------------------------- /include/Model/Model.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Model { 4 | 5 | struct Point { 6 | long double x, y, z; 7 | }; 8 | 9 | template 10 | void generatePoints(LambdaNewPointAction &&newPointAction, 11 | Point point, 12 | int pointsCount, 13 | long double tau, 14 | LambdaDerivatives &&countDerivatives); 15 | 16 | } // namespace Model 17 | 18 | #include "Model/Impl/ModelImpl.hpp" -------------------------------------------------------------------------------- /include/Parser/Lexer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Lexer { 8 | 9 | enum class Lexema { 10 | identifier, constant, 11 | add, subtract, multiply, divide, 12 | power, openParens, closeParens, 13 | end 14 | }; 15 | 16 | class Lexer final { 17 | public: 18 | explicit Lexer(const std::string &expression); 19 | 20 | Lexema getCurrentLexema() const noexcept; 21 | long double getCurrentConstant() const noexcept; 22 | const std::string &getCurrentIdentifier() const noexcept; 23 | 24 | void goNextLexema(); 25 | 26 | private: 27 | std::string::const_iterator curExprIterator; 28 | std::string::const_iterator endExprIterator; 29 | 30 | Lexema currentLexema; 31 | char currentChar; 32 | long double currentConstant; 33 | std::string currentIdentifier; 34 | 35 | void goNextChar(); 36 | }; 37 | 38 | } //namespace Lexer 39 | -------------------------------------------------------------------------------- /include/Parser/Parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Parser/ParserException.hpp" 7 | 8 | namespace Parser { 9 | 10 | struct Node { 11 | virtual long double calc() const noexcept = 0; 12 | 13 | virtual ~Node() = default; 14 | }; 15 | 16 | /// Order of variable addresses: [x,y,z] 17 | std::unique_ptr parseExpression(const std::string &expression, 18 | const std::array &variableAddresses, 19 | const std::map &customConstVariables = {}); 20 | 21 | } // namespace Parser 22 | -------------------------------------------------------------------------------- /include/Parser/ParserException.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Parser { 6 | 7 | class ParserException final : public std::logic_error { 8 | using std::logic_error::logic_error; 9 | }; 10 | 11 | } //namespace Parser 12 | -------------------------------------------------------------------------------- /include/Parser/ParserNodes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Parser.hpp" 6 | 7 | namespace Parser { 8 | 9 | struct NodeVariable final : Node { 10 | explicit NodeVariable(const long double *variableAddress) : 11 | value{variableAddress} {} 12 | 13 | long double calc() const noexcept override { 14 | return *value; 15 | } 16 | 17 | const long double *value; 18 | }; 19 | 20 | struct NodeConstant final : Node { 21 | explicit NodeConstant(long double constantValue) : 22 | value{constantValue} {} 23 | 24 | const long double value; 25 | 26 | long double calc() const noexcept override { 27 | return value; 28 | } 29 | }; 30 | 31 | enum class BinaryOperation { 32 | add, subtract, multiply, divide, power 33 | }; 34 | 35 | template 36 | struct NodeBinaryOperation : Node { 37 | NodeBinaryOperation(std::unique_ptr leftNode, std::unique_ptr rightNode) : 38 | left{std::move(leftNode)}, right{std::move(rightNode)} {} 39 | 40 | long double calc() const noexcept override; 41 | 42 | std::unique_ptr left; 43 | std::unique_ptr right; 44 | }; 45 | 46 | template<> 47 | long double NodeBinaryOperation::calc() const noexcept { 48 | return left->calc() - right->calc(); 49 | } 50 | 51 | template<> 52 | long double NodeBinaryOperation::calc() const noexcept { 53 | return left->calc() + right->calc(); 54 | } 55 | 56 | template<> 57 | long double NodeBinaryOperation::calc() const noexcept { 58 | return left->calc() * right->calc(); 59 | } 60 | 61 | template<> 62 | long double NodeBinaryOperation::calc() const noexcept { 63 | return left->calc() / right->calc(); 64 | } 65 | 66 | template<> 67 | long double NodeBinaryOperation::calc() const noexcept { 68 | return std::pow(left->calc(), right->calc()); 69 | } 70 | 71 | enum class Function { 72 | cos, sin, tan, 73 | acos, asin, atan, 74 | cosh, sinh, tanh, 75 | acosh, asinh, atanh, 76 | exp, sqrt, abs, 77 | ln, log, negative 78 | }; 79 | 80 | template 81 | struct NodeFunction final : Node { 82 | explicit NodeFunction(std::unique_ptr argumentNode) : 83 | argument{std::move(argumentNode)} {} 84 | 85 | long double calc() const noexcept override; 86 | 87 | std::unique_ptr argument; 88 | }; 89 | 90 | template<> 91 | long double NodeFunction::calc() const noexcept { 92 | return std::cos(argument->calc()); 93 | } 94 | 95 | template<> 96 | long double NodeFunction::calc() const noexcept { 97 | return std::sin(argument->calc()); 98 | } 99 | 100 | template<> 101 | long double NodeFunction::calc() const noexcept { 102 | return std::tan(argument->calc()); 103 | } 104 | 105 | template<> 106 | long double NodeFunction::calc() const noexcept { 107 | return std::acos(argument->calc()); 108 | } 109 | 110 | template<> 111 | long double NodeFunction::calc() const noexcept { 112 | return std::asin(argument->calc()); 113 | } 114 | 115 | template<> 116 | long double NodeFunction::calc() const noexcept { 117 | return std::atan(argument->calc()); 118 | } 119 | 120 | template<> 121 | long double NodeFunction::calc() const noexcept { 122 | return std::cosh(argument->calc()); 123 | } 124 | 125 | template<> 126 | long double NodeFunction::calc() const noexcept { 127 | return std::sinh(argument->calc()); 128 | } 129 | 130 | template<> 131 | long double NodeFunction::calc() const noexcept { 132 | return std::tanh(argument->calc()); 133 | } 134 | 135 | template<> 136 | long double NodeFunction::calc() const noexcept { 137 | return std::acosh(argument->calc()); 138 | } 139 | 140 | template<> 141 | long double NodeFunction::calc() const noexcept { 142 | return std::asinh(argument->calc()); 143 | } 144 | 145 | template<> 146 | long double NodeFunction::calc() const noexcept { 147 | return std::atanh(argument->calc()); 148 | } 149 | 150 | template<> 151 | long double NodeFunction::calc() const noexcept { 152 | return std::exp(argument->calc()); 153 | } 154 | 155 | template<> 156 | long double NodeFunction::calc() const noexcept { 157 | return std::sqrt(argument->calc()); 158 | } 159 | 160 | template<> 161 | long double NodeFunction::calc() const noexcept { 162 | return std::abs(argument->calc()); 163 | } 164 | 165 | template<> 166 | long double NodeFunction::calc() const noexcept { 167 | return std::log(argument->calc()); 168 | } 169 | 170 | template<> 171 | long double NodeFunction::calc() const noexcept { 172 | return std::log10(argument->calc()); 173 | } 174 | 175 | template<> 176 | long double NodeFunction::calc() const noexcept { 177 | return -(argument->calc()); 178 | } 179 | 180 | } //namespace Parser 181 | -------------------------------------------------------------------------------- /include/PointsViewQGLWidget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Camera.hpp" 11 | #include "Locus.hpp" 12 | #include "Preferences.hpp" 13 | #include "VideoEncoder.hpp" 14 | #include "Window.hpp" 15 | 16 | class PointsViewQGLWidget : public QGLWidget { 17 | public: 18 | explicit PointsViewQGLWidget(QWidget *parent = nullptr); 19 | ~PointsViewQGLWidget() = default; 20 | 21 | void clearAll(); 22 | 23 | void keyPressEvent(QKeyEvent *event) override; 24 | void keyReleaseEvent(QKeyEvent *event) override; 25 | 26 | void setCurrentTime(const int currentTime_); 27 | 28 | void addNewLocus(QVector &&points); 29 | 30 | void setPreferences(const Preferences::Preferences *prefs); 31 | 32 | bool startVideoRecording(const QString &filename); 33 | 34 | void endVideoRecording(std::function callback); 35 | 36 | protected: 37 | void initializeGL() override; 38 | void resizeGL(int width, int height) override; 39 | void paintGL() override; 40 | 41 | void mouseMoveEvent(QMouseEvent *event) override; 42 | void mousePressEvent(QMouseEvent *event) override; 43 | 44 | QSize sizeHint() const override; 45 | QSize minimumSizeHint() const override; 46 | 47 | 48 | private: 49 | Q_OBJECT 50 | 51 | const Preferences::Preferences *prefs; 52 | 53 | Locus::LocusController locusController; 54 | 55 | Camera::KeyboardAndMouseController cameraController; 56 | 57 | VideoEncoder::VideoEncoder videoEncoder; 58 | 59 | size_t currentTime; 60 | }; 61 | -------------------------------------------------------------------------------- /include/Preferences.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Model/Model.hpp" 7 | 8 | namespace Preferences { 9 | 10 | class Preferences final { 11 | private: 12 | struct CameraPreferences final { 13 | float speed = 0.08; 14 | float sensitivity = 0.01; 15 | }; 16 | 17 | struct VisualizationPreferences final { 18 | size_t tailPointsNumber = 100; 19 | size_t locusNumber = 200; 20 | 21 | float interpolationDistance = 0.15; 22 | float startPointSize = 0; 23 | float finalPointSize = 10; 24 | 25 | GLenum primitive = GL_LINE_STRIP; 26 | 27 | bool arcadeMode = false; 28 | bool tailColoringMode = true; 29 | 30 | QVector colors = { { 0, 1, 1, 1 }, { 0, 0, 1, 1 }, { 1, 0, 0, 1 } }; 31 | }; 32 | 33 | struct VideoPreferences final { 34 | size_t width = 1920; 35 | size_t height = 1080; 36 | }; 37 | 38 | struct ControllerPreferences final { 39 | int sliderTimeInterval = 1; 40 | int deltaTimePerStep = 1; 41 | 42 | bool preferencesChanged = false; 43 | }; 44 | 45 | struct ModelPreferences final { 46 | Model::Point startPoint = { 0.1, 0.2, 0.3 }; 47 | int pointsNumber = 20000; 48 | double deltaTime = 0.01; 49 | float divNormalization = 8; 50 | float startPointDelta = 0.05; 51 | }; 52 | 53 | public: 54 | VideoPreferences video; 55 | ModelPreferences model; 56 | CameraPreferences camera; 57 | ControllerPreferences controller; 58 | VisualizationPreferences visualization; 59 | 60 | void enableArcadeMode(); 61 | void disableArcadeMode(); 62 | }; 63 | 64 | inline const Preferences defaultPreferences{}; 65 | 66 | } //namespace Preferences 67 | -------------------------------------------------------------------------------- /include/ShaderController.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ShaderController { 7 | 8 | class ShaderController final { 9 | public: 10 | ShaderController() = default; 11 | ~ShaderController() = default; 12 | 13 | ShaderController(const ShaderController &) = delete; 14 | ShaderController(ShaderController &&) = delete; 15 | ShaderController &operator=(const ShaderController &) = delete; 16 | ShaderController &operator=(ShaderController &&) = delete; 17 | 18 | void initialize(); 19 | 20 | void startWork(); 21 | void endWork(); 22 | 23 | void setMatrix(const QMatrix4x4 &matrix); 24 | void setVertex(); 25 | void setArcadeMode(bool enabled); 26 | void setStartTailSize(float size); 27 | void setFinalTailSize(float size); 28 | void setTailLength(size_t length); 29 | void setStartVertexIndex(size_t index); 30 | void setTailColoringMode(bool enabled); 31 | void setTrajectoryIndex(size_t index); 32 | void setTrajectoriesNumber(size_t number); 33 | void setColors(const QVector &colors); 34 | void setInterpolationDistance(float distance); 35 | void setPrimitive(GLenum primitive); 36 | private: 37 | QGLShaderProgram shaderProgram; 38 | 39 | QGLShader* gshPoints; 40 | QGLShader* gshLines; 41 | }; 42 | 43 | } //class ShaderController 44 | -------------------------------------------------------------------------------- /include/StoppableTask.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | class StoppableTask : public QObject { 9 | Q_OBJECT 10 | signals: 11 | void afterRunSignal(); 12 | private: 13 | std::atomic stopFlag; 14 | std::thread taskThread; 15 | public: 16 | StoppableTask() : stopFlag(false) { 17 | QObject::connect(this, &StoppableTask::afterRunSignal, this, &StoppableTask::afterRun, Qt::QueuedConnection); 18 | } 19 | 20 | virtual void beforeRun() {} 21 | virtual void run() = 0; 22 | 23 | void operator()() { 24 | beforeRun(); 25 | std::thread taskThread([&]() { 26 | try { 27 | run(); 28 | } catch (...) {} 29 | }); 30 | taskThread.detach(); 31 | emit afterRunSignal(); 32 | } 33 | 34 | bool isStopped() { 35 | return stopFlag.load(); 36 | } 37 | 38 | void stopTask() { 39 | stopFlag.store(true); 40 | } 41 | public slots: 42 | void stopTaskSlot() { 43 | stopTask(); 44 | } 45 | 46 | virtual void afterRun() {} 47 | }; 48 | -------------------------------------------------------------------------------- /include/VideoEncoder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | extern "C" { 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | } 15 | 16 | namespace VideoEncoder { 17 | 18 | struct FrameState { 19 | QVector3D position; 20 | QVector3D target; 21 | size_t time; 22 | }; 23 | 24 | class VideoEncoder final { 25 | public: 26 | VideoEncoder(); 27 | ~VideoEncoder(); 28 | 29 | VideoEncoder(const VideoEncoder &other) = delete; 30 | VideoEncoder(VideoEncoder &&other) = delete; 31 | VideoEncoder &operator=(const VideoEncoder &other) = delete; 32 | VideoEncoder &operator=(VideoEncoder &&other) = delete; 33 | 34 | void startEncoding(int videoWidth, int videoHeight, const char *filename); 35 | void endEncoding(std::function drawFunc, 36 | std::function callback); 37 | 38 | void endEncoding(); 39 | 40 | void writeState(const FrameState &state); 41 | 42 | bool isWorking() const; 43 | 44 | private: 45 | AVFormatContext *formatContext; 46 | AVStream *stream; 47 | AVFrame *yuvFrame; 48 | AVFrame *rgbFrame; 49 | AVCodecContext *codecContext; 50 | SwsContext *convertFramesContext; 51 | 52 | size_t frameNumber; 53 | int width; 54 | int height; 55 | 56 | bool working; 57 | bool built; 58 | 59 | QVector allStates; 60 | 61 | bool writeFrame(AVFrame *frame); 62 | void writeFrame(const QImage &image); 63 | }; 64 | 65 | } //namespace VideoEncoder 66 | -------------------------------------------------------------------------------- /include/Window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "DynamicSystems/DynamicSystem.hpp" 9 | #include "Preferences.hpp" 10 | #include "WindowPreferences.hpp" 11 | #include "DynamicSystemWrapper.hpp" 12 | #include "StoppableTask.hpp" 13 | 14 | namespace Ui { 15 | class Window; 16 | } 17 | 18 | class CountPointsTask; 19 | 20 | class Window : public QWidget { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit Window(QWidget *parent = nullptr); 25 | ~Window(); 26 | 27 | public slots: 28 | void updateOpenGLWidget(QVector); 29 | 30 | void updateVideoRecordingState(); 31 | void slot_restart_button(); 32 | void slot_time_slider(int); 33 | void slot_model_selection(QString); 34 | void slot_constants_selection(QString); 35 | void slot_pause_button(); 36 | void slot_open_preferences(); 37 | void updateSlider(); 38 | 39 | protected: 40 | void keyPressEvent(QKeyEvent *event) override; 41 | void keyReleaseEvent(QKeyEvent *event) override; 42 | 43 | private: 44 | friend class CountPointsTask; 45 | void afterCountPointsUIUpdate(); 46 | 47 | void insertConstants(const std::vector>> &); 48 | 49 | void insertExpressions(std::array array, bool); 50 | 51 | void addNewConstant(const std::string_view &name, long double initValue); 52 | 53 | using LambdaPushBackAction = decltype(DynamicSystemWrapper_n::getPushBackAndNormalizeLambda(std::declval &>(), 54 | std::declval())); 55 | using DynamicSystemWrapper = DynamicSystems::DynamicSystem; 56 | 57 | static DynamicSystemWrapper getCustomSystem(const std::array &); 58 | 59 | std::map dynamicSystems; 60 | 61 | int timeValue = 0; 62 | 63 | bool pauseState = false; 64 | 65 | Preferences::Preferences prefs; 66 | 67 | QTimer *sliderTimer; 68 | 69 | WindowPreferences *windowPreferences; 70 | Ui::Window *ui; 71 | CountPointsTask *task; 72 | }; 73 | 74 | 75 | class CountPointsTask : public StoppableTask { 76 | Q_OBJECT 77 | 78 | signals: 79 | void updater(QVector); 80 | public: 81 | CountPointsTask(Window& wind_); 82 | 83 | void run() override; 84 | 85 | public slots: 86 | void afterRun() override { 87 | wind.afterCountPointsUIUpdate(); 88 | } 89 | private: 90 | Window& wind; 91 | }; 92 | -------------------------------------------------------------------------------- /include/WindowPreferences.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Preferences.hpp" 7 | 8 | namespace Ui { 9 | class WindowPreferences; 10 | } 11 | 12 | class WindowPreferences : public QWidget { 13 | Q_OBJECT 14 | public: 15 | explicit WindowPreferences(QWidget *parent, Preferences::Preferences *prefs); 16 | ~WindowPreferences(); 17 | 18 | public slots: 19 | void slot_cancel_button(); 20 | void slot_set_default_button(); 21 | void slot_apply_button(); 22 | 23 | void addNewColor(); 24 | void removeLastColor(); 25 | 26 | private slots: 27 | void pickButtonColor(); 28 | 29 | private: 30 | void setCurrentStateInUI(); 31 | void setStateFromUI(); 32 | void addNewColorButton(QColor initColor); 33 | 34 | Ui::WindowPreferences *ui; 35 | 36 | Preferences::Preferences *prefs; 37 | }; 38 | -------------------------------------------------------------------------------- /materials/FragmentShader.fsh: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | #undef highp 3 | 4 | uniform bool tailColoringMode; 5 | 6 | uniform highp int tailLength; 7 | uniform highp int startIndex; 8 | uniform highp int trajectoryIndex; 9 | uniform highp int trajectoriesNumber; 10 | 11 | uniform highp int colorsNumber; 12 | uniform highp vec4 colors[20]; 13 | 14 | flat in highp int vertexID_FSH; 15 | flat in highp float vertexOffset; 16 | 17 | out highp vec4 fragColor; 18 | 19 | void main(void) { 20 | if (colorsNumber == 1) { 21 | fragColor = colors[0]; 22 | return; 23 | } 24 | highp int index; 25 | highp int bunchSize; 26 | if (tailColoringMode == true) { 27 | index = vertexID_FSH - startIndex; 28 | bunchSize = (tailLength + colorsNumber - 2) / (colorsNumber - 1); 29 | } else { 30 | index = trajectoryIndex; 31 | bunchSize = (trajectoriesNumber + colorsNumber - 2) / (colorsNumber - 1); 32 | } 33 | highp int colorIndex = index / bunchSize; 34 | highp float colorPart = (float(index % bunchSize) + vertexOffset) / float(bunchSize); 35 | fragColor = colors[colorIndex] + colorPart * (colors[colorIndex + 1] - colors[colorIndex]); 36 | } 37 | -------------------------------------------------------------------------------- /materials/GeometryShaderLines.gsh: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | layout(lines_adjacency) in; 4 | layout(line_strip, max_vertices = 256) out; 5 | 6 | uniform highp mat4 matrix; 7 | uniform highp float interpolationDist; 8 | 9 | flat in highp int vertexID_GSH[]; 10 | flat out highp int vertexID_FSH; 11 | flat out highp float vertexOffset; 12 | 13 | void main(void) { 14 | vertexID_FSH = vertexID_GSH[1]; 15 | 16 | highp float distance = distance(gl_in[1].gl_Position.xyz, gl_in[2].gl_Position.xyz); 17 | highp int cuts = int(min(255.0, max(3.0, distance / interpolationDist))); 18 | if (cuts % 2 != 1) { 19 | cuts--; 20 | } 21 | 22 | highp mat4 lineMatrix = transpose(mat4(gl_in[0].gl_Position, 23 | gl_in[1].gl_Position, 24 | gl_in[2].gl_Position, 25 | gl_in[3].gl_Position)); 26 | highp mat4 curveMatrix = transpose(mat4( 0.0, 2.0, 0.0, 0.0, 27 | -1.0, 0.0, 1.0, 0.0, 28 | 2.0, -5.0, 4.0, -1.0, 29 | -1.0, 3.0, -3.0, 1.0)); 30 | 31 | for (int i = 0; i <= cuts; i++) { 32 | highp float offset = float(i) / float (cuts); 33 | highp vec4 offsetVec = vec4(1.0, offset, offset * offset, offset * offset * offset); 34 | highp vec4 pos = 0.5 * offsetVec * curveMatrix * lineMatrix; 35 | gl_Position = matrix * pos; 36 | gl_PointSize = gl_in[1].gl_PointSize + float(offset) * (gl_in[2].gl_PointSize - gl_in[1].gl_PointSize); 37 | 38 | vertexOffset = offset; 39 | EmitVertex(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /materials/GeometryShaderPoints.gsh: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | layout(lines_adjacency) in; 4 | layout(points, max_vertices = 256) out; 5 | 6 | uniform highp mat4 matrix; 7 | uniform highp float interpolationDist; 8 | 9 | flat in highp int vertexID_GSH[]; 10 | flat out highp int vertexID_FSH; 11 | flat out highp float vertexOffset; 12 | 13 | void main(void) { 14 | vertexID_FSH = vertexID_GSH[1]; 15 | 16 | highp float distance = distance(gl_in[1].gl_Position.xyz, gl_in[2].gl_Position.xyz); 17 | highp int cuts = int(min(256.0, max(3.0, distance / interpolationDist))); 18 | highp mat4 lineMatrix = transpose(mat4(gl_in[0].gl_Position, 19 | gl_in[1].gl_Position, 20 | gl_in[2].gl_Position, 21 | gl_in[3].gl_Position)); 22 | highp mat4 curveMatrix = transpose(mat4( 0.0, 2.0, 0.0, 0.0, 23 | -1.0, 0.0, 1.0, 0.0, 24 | 2.0, -5.0, 4.0, -1.0, 25 | -1.0, 3.0, -3.0, 1.0)); 26 | 27 | for (int i = 0; i <= cuts; i++) { 28 | highp float offset = float(i) / float (cuts); 29 | highp vec4 offsetVec = vec4(1.0, offset, offset * offset, offset * offset * offset); 30 | highp vec4 pos = 0.5 * offsetVec * curveMatrix * lineMatrix; 31 | gl_Position = matrix * pos; 32 | gl_PointSize = gl_in[1].gl_PointSize + float(offset) * (gl_in[2].gl_PointSize - gl_in[1].gl_PointSize); 33 | 34 | vertexOffset = offset; 35 | EmitVertex(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /materials/Resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | FragmentShader.fsh 4 | VertexShader.vsh 5 | GeometryShaderPoints.gsh 6 | GeometryShaderLines.gsh 7 | 8 | 9 | -------------------------------------------------------------------------------- /materials/VertexShader.vsh: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in highp vec4 vertex; 4 | uniform highp mat4 matrix; 5 | 6 | uniform bool arcadeMode; 7 | uniform highp float startTailSize; 8 | uniform highp float finalTailSize; 9 | 10 | uniform highp int tailLength; 11 | uniform highp int startIndex; 12 | 13 | flat out highp int vertexID_GSH; 14 | 15 | void main(void) { 16 | gl_Position = vertex; 17 | if (arcadeMode == true) { 18 | float delta = (finalTailSize - startTailSize) / float(tailLength); 19 | gl_PointSize = startTailSize + delta * float(gl_VertexID - startIndex); 20 | } else { 21 | gl_PointSize = 2.0; 22 | } 23 | 24 | vertexID_GSH = gl_VertexID; 25 | } 26 | -------------------------------------------------------------------------------- /src/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Camera.hpp" 4 | 5 | namespace Camera { 6 | 7 | constexpr QVector3D INIT_CAMERA_POSITION = { 0, 0, 5 }; 8 | constexpr QVector3D INIT_CAMERA_TARGET = -INIT_CAMERA_POSITION; 9 | 10 | constexpr float INIT_YAW = -M_PI / 2; 11 | constexpr float INIT_PITCH = 0; 12 | 13 | constexpr float VERTICAL_ANGLE = 60; 14 | constexpr float NEAR_PLANE = 0.001; 15 | constexpr float FAR_PLANE = 1000; 16 | 17 | constexpr float EPS = 0.001; 18 | constexpr float MAX_PITCH = M_PI / 2 - EPS; 19 | 20 | Camera::Camera() : 21 | cameraPosition{INIT_CAMERA_POSITION}, 22 | cameraTarget{INIT_CAMERA_TARGET}, 23 | pitch{INIT_PITCH}, 24 | yaw{INIT_YAW}, 25 | invalidState{false} { 26 | 27 | recalculateVectors(); 28 | } 29 | 30 | void Camera::recalculateVectors() { 31 | cameraForward = (-cameraTarget).normalized(); 32 | cameraRight = QVector3D::crossProduct(worldUp, cameraForward).normalized(); 33 | cameraUp = QVector3D::crossProduct(cameraForward, cameraRight).normalized(); 34 | } 35 | 36 | void Camera::recalculatePerspective(int width, int height) { 37 | perspectiveMatrix.setToIdentity(); 38 | perspectiveMatrix.perspective(VERTICAL_ANGLE, static_cast(width) / height, NEAR_PLANE, FAR_PLANE); 39 | glViewport(0, 0, width, height); 40 | } 41 | 42 | void Camera::recalculateTarget(const QPoint &newMousePosition, float mouseSensitivity) { 43 | if (invalidState) { 44 | lastMousePosition = newMousePosition; 45 | invalidState = false; 46 | return; 47 | } 48 | float deltaX = (newMousePosition.x() - lastMousePosition.x()) * mouseSensitivity; 49 | float deltaY = (lastMousePosition.y() - newMousePosition.y()) * mouseSensitivity; 50 | yaw += deltaX; 51 | pitch += deltaY; 52 | normalizeAngles(); 53 | 54 | cameraTarget.setX(std::cos(pitch) * std::cos(yaw)); 55 | cameraTarget.setY(std::sin(pitch)); 56 | cameraTarget.setZ(std::cos(pitch) * std::sin(yaw)); 57 | cameraTarget.normalize(); 58 | 59 | recalculateVectors(); 60 | lastMousePosition = newMousePosition; 61 | } 62 | 63 | void Camera::normalizeAngles() { 64 | if (pitch > MAX_PITCH) { 65 | pitch = MAX_PITCH; 66 | } 67 | if (pitch < -MAX_PITCH) { 68 | pitch = -MAX_PITCH; 69 | } 70 | } 71 | 72 | QMatrix4x4 Camera::getMatrix() const { 73 | QMatrix4x4 matrix; 74 | matrix.lookAt(cameraPosition, cameraPosition + cameraTarget, worldUp); 75 | return perspectiveMatrix * matrix; 76 | } 77 | 78 | QVector3D Camera::getPosition() const { 79 | return cameraPosition; 80 | } 81 | 82 | QVector3D Camera::getTarget() const { 83 | return cameraTarget; 84 | } 85 | 86 | void Camera::applyDeltaPosition (const QVector3D &delta) { 87 | cameraPosition += delta; 88 | recalculateVectors(); 89 | } 90 | 91 | void Camera::setPosition(const QVector3D &position) { 92 | cameraPosition = position; 93 | recalculateVectors(); 94 | } 95 | 96 | void Camera::setTarget(const QVector3D &target) { 97 | cameraTarget = target; 98 | recalculateVectors(); 99 | } 100 | 101 | void Camera::moveForward(float force) { 102 | cameraPosition += cameraForward * (-force); 103 | recalculateVectors(); 104 | } 105 | 106 | void Camera::moveRight(float force) { 107 | cameraPosition += cameraRight * force; 108 | recalculateVectors(); 109 | } 110 | 111 | void Camera::moveUp(float force) { 112 | cameraPosition += cameraUp * force; 113 | recalculateVectors(); 114 | } 115 | 116 | void Camera::resetMousePosition(const QPoint &newMousePosition) { 117 | lastMousePosition = newMousePosition; 118 | } 119 | 120 | void Camera::setDefault() { 121 | cameraPosition = INIT_CAMERA_POSITION; 122 | cameraTarget = INIT_CAMERA_TARGET; 123 | pitch = INIT_PITCH; 124 | yaw = INIT_YAW; 125 | invalidState = true; 126 | 127 | recalculateVectors(); 128 | } 129 | 130 | 131 | KeyboardAndMouseController::KeyboardAndMouseController() : 132 | prefs{&Preferences::defaultPreferences} { 133 | 134 | timer = new QTimer(this); 135 | timer->setInterval(1); 136 | connect(timer, SIGNAL(timeout()), this, SLOT(updateKeys())); 137 | timer->start(); 138 | } 139 | 140 | void KeyboardAndMouseController::setPreferences(const Preferences::Preferences *prefs_) { 141 | prefs = prefs_; 142 | } 143 | 144 | QMatrix4x4 KeyboardAndMouseController::getMatrix() const { 145 | return camera.getMatrix(); 146 | } 147 | 148 | QVector3D KeyboardAndMouseController::getPosition() const { 149 | return camera.getPosition(); 150 | } 151 | 152 | QVector3D KeyboardAndMouseController::getTarget() const { 153 | return camera.getTarget(); 154 | } 155 | 156 | void KeyboardAndMouseController::recalculatePerspective(int width, int height) { 157 | camera.recalculatePerspective(width, height); 158 | } 159 | 160 | void KeyboardAndMouseController::applyKeyPressEvent(QKeyEvent *event) { 161 | keys.insert(event->key()); 162 | event->accept(); 163 | } 164 | 165 | void KeyboardAndMouseController::applyKeyReleaseEvent(QKeyEvent *event) { 166 | keys.remove(event->key()); 167 | event->accept(); 168 | } 169 | 170 | void KeyboardAndMouseController::applyMousePressEvent(QMouseEvent *event) { 171 | camera.resetMousePosition(event->pos()); 172 | event->accept(); 173 | } 174 | 175 | void KeyboardAndMouseController::applyMouseMoveEvent(QMouseEvent *event) { 176 | camera.recalculateTarget(event->pos(), prefs->camera.sensitivity); 177 | event->accept(); 178 | } 179 | 180 | void KeyboardAndMouseController::updateKeys() { 181 | float force = 1; 182 | if (keys.contains(Qt::Key_Shift)) { 183 | force *= 2; 184 | } 185 | if (keys.contains(Qt::Key_Control)) { 186 | force /= 2; 187 | } 188 | force *= prefs->camera.speed; 189 | 190 | if (keys.contains(Qt::Key_W)) { 191 | camera.moveForward(force); 192 | } 193 | if (keys.contains(Qt::Key_S)) { 194 | camera.moveForward(-force); 195 | } 196 | if (keys.contains(Qt::Key_D)) { 197 | camera.moveRight(force); 198 | } 199 | if (keys.contains(Qt::Key_A)) { 200 | camera.moveRight(-force); 201 | } 202 | if (keys.contains(Qt::Key_Q)) { 203 | camera.moveUp(force); 204 | } 205 | if (keys.contains(Qt::Key_E)) { 206 | camera.moveUp(-force); 207 | } 208 | if (keys.contains(Qt::Key_F)) { 209 | camera.setDefault(); 210 | } 211 | } 212 | 213 | } //namespace Camera 214 | -------------------------------------------------------------------------------- /src/DynamicSystemParser/DynamicSystemParser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "DynamicSystemParser/DynamicSystemParser.hpp" 7 | 8 | namespace DynamicSystemParser::Impl { 9 | 10 | ParserDerivativesWrapper::ParserDerivativesWrapper(std::unique_ptr> variables_, 11 | std::unique_ptr xFunc_, 12 | std::unique_ptr yFunc_, 13 | std::unique_ptr zFunc_) : 14 | variables{std::move(variables_)}, 15 | xFunc{std::move(xFunc_)}, 16 | yFunc{std::move(yFunc_)}, 17 | zFunc{std::move(zFunc_)} {} 18 | 19 | 20 | ParserDerivativesWrapper parseExpressions(const std::string &xExpr, const std::string &yExpr, const std::string &zExpr, 21 | const std::map &customConstVariables) { 22 | 23 | auto variableArray = std::make_unique>(); 24 | std::array variableAddresses = {&(*variableArray)[0], &(*variableArray)[1], &(*variableArray)[2]}; 25 | std::unique_ptr xFunc, yFunc, zFunc; 26 | 27 | try { 28 | xFunc = Parser::parseExpression(xExpr, variableAddresses, customConstVariables); 29 | } catch (const std::exception &exception) { 30 | throw Parser::ParserException(std::string{"In first expression: "} + std::string{exception.what()}); 31 | } 32 | 33 | try { 34 | yFunc = Parser::parseExpression(yExpr, variableAddresses, customConstVariables); 35 | } catch (const std::exception &exception) { 36 | throw Parser::ParserException(std::string{"In second expression: "} + std::string{exception.what()}); 37 | } 38 | 39 | try { 40 | zFunc = Parser::parseExpression(zExpr, variableAddresses, customConstVariables); 41 | } catch (const std::exception &exception) { 42 | throw Parser::ParserException(std::string{"In third expression: "} + std::string{exception.what()}); 43 | } 44 | 45 | return ParserDerivativesWrapper{std::move(variableArray), std::move(xFunc), std::move(yFunc), std::move(zFunc)}; 46 | } 47 | 48 | } // namespace DynamicSystemParser::Impl -------------------------------------------------------------------------------- /src/Locus.cpp: -------------------------------------------------------------------------------- 1 | #include "Locus.hpp" 2 | 3 | namespace Locus { 4 | 5 | Locus::Locus(QVector &&points) { 6 | pointsBuffer = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); 7 | pointsBuffer.create(); 8 | pointsBuffer.bind(); 9 | pointsBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); 10 | pointsBuffer.allocate(points.begin(), points.size() * 3 * sizeof(float)); 11 | pointsBuffer.release(); 12 | } 13 | 14 | void Locus::startWork() { 15 | pointsBuffer.bind(); 16 | } 17 | 18 | void Locus::endWork() { 19 | pointsBuffer.release(); 20 | } 21 | 22 | size_t Locus::size() const { 23 | return static_cast(pointsBuffer.size() / 3 / sizeof(float)); 24 | } 25 | 26 | 27 | LocusController::LocusController() : 28 | prefs{&Preferences::defaultPreferences} {} 29 | 30 | void LocusController::setPreferences(const Preferences::Preferences *prefs_) { 31 | prefs = prefs_; 32 | } 33 | 34 | void LocusController::initialize() { 35 | shaderController.initialize(); 36 | } 37 | 38 | size_t LocusController::size() const { 39 | return static_cast(data.size()); 40 | } 41 | 42 | void LocusController::addLocus(QVector &&points_) { 43 | shaderController.startWork(); 44 | data.push_back(Locus(std::move(points_))); 45 | shaderController.endWork(); 46 | } 47 | 48 | void LocusController::clear() { 49 | data.clear(); 50 | } 51 | 52 | void LocusController::draw(const QMatrix4x4 &projMatrix, size_t time) { 53 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 54 | 55 | shaderController.startWork(); 56 | shaderController.setMatrix(projMatrix); 57 | 58 | shaderController.setArcadeMode(prefs->visualization.arcadeMode); 59 | 60 | shaderController.setTailColoringMode(prefs->visualization.tailColoringMode); 61 | shaderController.setTrajectoriesNumber(static_cast(data.size())); 62 | shaderController.setColors(prefs->visualization.colors); 63 | 64 | shaderController.setInterpolationDistance(prefs->visualization.interpolationDistance); 65 | 66 | shaderController.setPrimitive(prefs->visualization.primitive); 67 | 68 | for (size_t i = 0; i < static_cast(data.size()); i++) { 69 | auto &locus = data[i]; 70 | 71 | locus.startWork(); 72 | 73 | if (locus.size() == 0) { 74 | continue; 75 | } 76 | 77 | size_t start = std::max(0, static_cast(time) - static_cast(prefs->visualization.tailPointsNumber)); 78 | if (start >= locus.size()) { 79 | locus.endWork(); 80 | 81 | continue; 82 | } 83 | size_t actualLength = std::min(time, locus.size()) - start; 84 | 85 | shaderController.setVertex(); 86 | 87 | shaderController.setStartTailSize(prefs->visualization.startPointSize); 88 | shaderController.setFinalTailSize(prefs->visualization.finalPointSize); 89 | shaderController.setTailLength(actualLength); 90 | shaderController.setStartVertexIndex(start); 91 | shaderController.setTrajectoryIndex(i); 92 | 93 | glDrawArrays(GL_LINE_STRIP_ADJACENCY, start, actualLength); 94 | 95 | locus.endWork(); 96 | } 97 | shaderController.endWork(); 98 | } 99 | 100 | } //namespace Locus 101 | -------------------------------------------------------------------------------- /src/Parser/Lexer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Parser/Lexer.hpp" 4 | #include "Parser/ParserException.hpp" 5 | 6 | namespace Lexer { 7 | 8 | long double convertStringToLongDouble(const std::string &number) { 9 | std::stringstream stream(number); 10 | 11 | long double result = 0; 12 | stream >> result; 13 | 14 | return result; 15 | } 16 | 17 | Lexer::Lexer(const std::string &expression_) : 18 | curExprIterator{expression_.begin()}, endExprIterator{expression_.end()} { 19 | 20 | goNextChar(); 21 | goNextLexema(); 22 | } 23 | 24 | void Lexer::goNextChar() { 25 | if (curExprIterator == endExprIterator) { 26 | currentChar = '\0'; 27 | } else { 28 | currentChar = *curExprIterator; 29 | curExprIterator++; 30 | } 31 | } 32 | 33 | void Lexer::goNextLexema() { 34 | while (isspace(currentChar)) { 35 | goNextChar(); 36 | } 37 | 38 | switch (currentChar) { 39 | case '\0': 40 | currentLexema = Lexema::end; 41 | return; 42 | case '+': 43 | goNextChar(); 44 | currentLexema = Lexema::add; 45 | return; 46 | case '-': 47 | goNextChar(); 48 | currentLexema = Lexema::subtract; 49 | return; 50 | case '*': 51 | goNextChar(); 52 | currentLexema = Lexema::multiply; 53 | return; 54 | case '/': 55 | goNextChar(); 56 | currentLexema = Lexema::divide; 57 | return; 58 | case '^': 59 | goNextChar(); 60 | currentLexema = Lexema::power; 61 | return; 62 | case '(': 63 | goNextChar(); 64 | currentLexema = Lexema::openParens; 65 | return; 66 | case ')': 67 | goNextChar(); 68 | currentLexema = Lexema::closeParens; 69 | return; 70 | } 71 | 72 | if (isdigit(currentChar) || currentChar == '.' || currentChar == ',') { 73 | std::string constant; 74 | bool wasDecimalPoint = false; 75 | while (isdigit(currentChar) || currentChar == '.' || currentChar == ',') { 76 | if ((currentChar == '.' || currentChar == ',') && wasDecimalPoint) { 77 | throw Parser::ParserException("Unexpected two or more decimal points."); 78 | } 79 | if (currentChar == '.' || currentChar == ',') { 80 | constant += '.'; 81 | wasDecimalPoint = true; 82 | } else { 83 | constant += currentChar; 84 | } 85 | goNextChar(); 86 | } 87 | 88 | currentConstant = convertStringToLongDouble(constant); 89 | currentLexema = Lexema::constant; 90 | 91 | return; 92 | } 93 | 94 | if (isalpha(currentChar)) { 95 | currentIdentifier.clear(); 96 | while (isalpha(currentChar)) { 97 | currentIdentifier += currentChar; 98 | goNextChar(); 99 | } 100 | 101 | currentLexema = Lexema::identifier; 102 | 103 | return; 104 | } 105 | 106 | throw Parser::ParserException("Unexpected character."); 107 | } 108 | 109 | Lexema Lexer::getCurrentLexema() const noexcept { 110 | return currentLexema; 111 | } 112 | 113 | long double Lexer::getCurrentConstant() const noexcept { 114 | return currentConstant; 115 | } 116 | 117 | const std::string &Lexer::getCurrentIdentifier() const noexcept { 118 | return currentIdentifier; 119 | } 120 | 121 | } //namespace Lexer 122 | -------------------------------------------------------------------------------- /src/Parser/Parser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Parser/Lexer.hpp" 7 | #include "Parser/Parser.hpp" 8 | #include "Parser/ParserNodes.hpp" 9 | 10 | namespace Parser { 11 | 12 | constexpr std::size_t X_VAR_POS = 0, Y_VAR_POS = 1, Z_VAR_POS = 2; 13 | 14 | class Parser final { 15 | public: 16 | explicit Parser(const std::string &expression, 17 | const std::array &variableAddresses, 18 | const std::map &customConstVariables) : 19 | lexer{expression}, variables{variableAddresses}, constVariables{customConstVariables} {} 20 | 21 | Parser(const Parser &) = delete; 22 | Parser(Parser &&) = delete; 23 | Parser &operator=(const Parser &) = delete; 24 | Parser &operator=(Parser &&) = delete; 25 | 26 | std::unique_ptr parse() { 27 | return parseAddSubtract(); 28 | } 29 | 30 | private: 31 | Lexer::Lexer lexer; 32 | const std::array &variables; 33 | std::map constVariables; 34 | 35 | std::unique_ptr parseAddSubtract() { 36 | std::unique_ptr leftPart = parseMultiplyDivide(); 37 | 38 | while (true) { 39 | if (lexer.getCurrentLexema() == Lexer::Lexema::add) { 40 | lexer.goNextLexema(); 41 | std::unique_ptr rightPart = parseMultiplyDivide(); 42 | leftPart = std::make_unique>(std::move(leftPart), std::move(rightPart)); 43 | } else if (lexer.getCurrentLexema() == Lexer::Lexema::subtract) { 44 | lexer.goNextLexema(); 45 | std::unique_ptr rightPart = parseMultiplyDivide(); 46 | leftPart = std::make_unique>(std::move(leftPart), std::move(rightPart)); 47 | } else if (lexer.getCurrentLexema() != Lexer::Lexema::openParens && 48 | lexer.getCurrentLexema() != Lexer::Lexema::identifier) { 49 | 50 | break; 51 | } else { 52 | throw ParserException("Unexpected character."); 53 | } 54 | } 55 | 56 | return leftPart; 57 | } 58 | 59 | std::unique_ptr parseMultiplyDivide() { 60 | std::unique_ptr leftPart = parsePower(); 61 | 62 | while (true) { 63 | if (lexer.getCurrentLexema() == Lexer::Lexema::multiply) { 64 | lexer.goNextLexema(); 65 | std::unique_ptr rightPart = parsePower(); 66 | leftPart = std::make_unique>(std::move(leftPart), std::move(rightPart)); 67 | } else if (lexer.getCurrentLexema() == Lexer::Lexema::divide) { 68 | lexer.goNextLexema(); 69 | std::unique_ptr rightPart = parsePower(); 70 | leftPart = std::make_unique>(std::move(leftPart), std::move(rightPart)); 71 | } else if (lexer.getCurrentLexema() != Lexer::Lexema::openParens && 72 | lexer.getCurrentLexema() != Lexer::Lexema::identifier) { 73 | 74 | break; 75 | } else { 76 | throw ParserException("Unexpected character."); 77 | } 78 | } 79 | 80 | return leftPart; 81 | } 82 | 83 | std::unique_ptr parsePower() { 84 | std::vector> parts; 85 | parts.push_back(parseUnary()); 86 | while (true) { 87 | if (lexer.getCurrentLexema() == Lexer::Lexema::power) { 88 | lexer.goNextLexema(); 89 | parts.push_back(parseUnary()); 90 | } else if (lexer.getCurrentLexema() != Lexer::Lexema::openParens && 91 | lexer.getCurrentLexema() != Lexer::Lexema::identifier) { 92 | 93 | break; 94 | } else { 95 | throw ParserException("Unexpected character."); 96 | } 97 | } 98 | 99 | std::unique_ptr operation = std::move(parts.back()); 100 | for (int i = static_cast(parts.size()) - 2; i >= 0; i--) { 101 | operation = std::make_unique>(std::move(parts[i]), std::move(operation)); 102 | } 103 | 104 | return operation; 105 | } 106 | 107 | std::unique_ptr parseUnary() { 108 | if (lexer.getCurrentLexema() == Lexer::Lexema::subtract) { 109 | lexer.goNextLexema(); 110 | 111 | return std::make_unique>(parseUnary()); 112 | } 113 | 114 | return parseLeaf(); 115 | } 116 | 117 | std::unique_ptr parseLeaf() { 118 | if (lexer.getCurrentLexema() == Lexer::Lexema::constant) { 119 | std::unique_ptr leaf = std::make_unique(lexer.getCurrentConstant()); 120 | lexer.goNextLexema(); 121 | 122 | return leaf; 123 | } 124 | if (lexer.getCurrentLexema() == Lexer::Lexema::identifier) { 125 | std::string identifier = lexer.getCurrentIdentifier(); 126 | lexer.goNextLexema(); 127 | if (lexer.getCurrentLexema() == Lexer::Lexema::openParens) { //it is a function 128 | lexer.goNextLexema(); 129 | 130 | std::unique_ptr func; 131 | std::unique_ptr argument = parseAddSubtract(); 132 | if (identifier == "sin") { 133 | func = std::make_unique>(std::move(argument)); 134 | } else if (identifier == "cos") { 135 | func = std::make_unique>(std::move(argument)); 136 | } else if (identifier == "tan") { 137 | func = std::make_unique>(std::move(argument)); 138 | } else if (identifier == "asin") { 139 | func = std::make_unique>(std::move(argument)); 140 | } else if (identifier == "acos") { 141 | func = std::make_unique>(std::move(argument)); 142 | } else if (identifier == "atan") { 143 | func = std::make_unique>(std::move(argument)); 144 | } else if (identifier == "sinh") { 145 | func = std::make_unique>(std::move(argument)); 146 | } else if (identifier == "cosh") { 147 | func = std::make_unique>(std::move(argument)); 148 | } else if (identifier == "tanh") { 149 | func = std::make_unique>(std::move(argument)); 150 | } else if (identifier == "asinh") { 151 | func = std::make_unique>(std::move(argument)); 152 | } else if (identifier == "acosh") { 153 | func = std::make_unique>(std::move(argument)); 154 | } else if (identifier == "atanh") { 155 | func = std::make_unique>(std::move(argument)); 156 | } else if (identifier == "sqrt") { 157 | func = std::make_unique>(std::move(argument)); 158 | } else if (identifier == "exp") { 159 | func = std::make_unique>(std::move(argument)); 160 | } else if (identifier == "abs") { 161 | func = std::make_unique>(std::move(argument)); 162 | } else if (identifier == "log") { 163 | func = std::make_unique>(std::move(argument)); 164 | } else if (identifier == "ln") { 165 | func = std::make_unique>(std::move(argument)); 166 | } else { 167 | throw ParserException("Unexpected function \'" + identifier + "\'."); 168 | } 169 | 170 | if (lexer.getCurrentLexema() != Lexer::Lexema::closeParens) { 171 | throw ParserException("Expected a close parenthesis after a function call \'" + identifier + "\'."); 172 | } 173 | lexer.goNextLexema(); 174 | 175 | return func; 176 | } else { //it is a variable 177 | if (identifier == "x") { 178 | return std::make_unique(variables[X_VAR_POS]); 179 | } else if (identifier == "y") { 180 | return std::make_unique(variables[Y_VAR_POS]); 181 | } else if (identifier == "z") { 182 | return std::make_unique(variables[Z_VAR_POS]); 183 | } else if (identifier == "pi") { 184 | return std::make_unique(std::atan(1) * 4); 185 | } else if (identifier == "e") { 186 | return std::make_unique(std::exp(1)); 187 | } else if (constVariables.count(identifier) != 0) { 188 | return std::make_unique(constVariables[identifier]); 189 | } else { 190 | throw ParserException("Unexpected variable \'" + identifier + "\'."); 191 | } 192 | } 193 | } 194 | if (lexer.getCurrentLexema() == Lexer::Lexema::openParens) { 195 | lexer.goNextLexema(); 196 | std::unique_ptr leaf = parseAddSubtract(); 197 | 198 | if (lexer.getCurrentLexema() != Lexer::Lexema::closeParens) { 199 | throw ParserException("Expected a close parenthesis."); 200 | } 201 | lexer.goNextLexema(); 202 | 203 | return leaf; 204 | } 205 | 206 | throw ParserException("Unexpected character."); 207 | } 208 | }; 209 | 210 | std::unique_ptr parseExpression(const std::string &expression, 211 | const std::array &variableAddresses, 212 | const std::map &customConstVariables) { 213 | 214 | Parser parser(expression, variableAddresses, customConstVariables); 215 | 216 | return parser.parse(); 217 | } 218 | 219 | } // namespace Parser 220 | -------------------------------------------------------------------------------- /src/PointsViewQGLWidget.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "PointsViewQGLWidget.hpp" 7 | 8 | PointsViewQGLWidget::PointsViewQGLWidget(QWidget *parent) : 9 | QGLWidget{QGLFormat(), parent}, 10 | prefs{&Preferences::defaultPreferences} {} 11 | 12 | void PointsViewQGLWidget::setPreferences(const Preferences::Preferences *prefs_) { 13 | prefs = prefs_; 14 | locusController.setPreferences(prefs); 15 | cameraController.setPreferences(prefs); 16 | } 17 | 18 | QSize PointsViewQGLWidget::minimumSizeHint() const { 19 | return { 640, 480 }; 20 | } 21 | 22 | QSize PointsViewQGLWidget::sizeHint() const { 23 | return { 1080, 720 }; 24 | } 25 | 26 | void PointsViewQGLWidget::addNewLocus(QVector &&points) { 27 | locusController.addLocus(std::move(points)); 28 | } 29 | 30 | void PointsViewQGLWidget::setCurrentTime(const int currentTime_) { 31 | currentTime = currentTime_; 32 | } 33 | 34 | bool PointsViewQGLWidget::startVideoRecording(const QString &filename) { 35 | try { 36 | videoEncoder.startEncoding(prefs->video.width, prefs->video.height, filename.toStdString().c_str()); 37 | 38 | return true; 39 | } catch (const std::exception &e) { 40 | videoEncoder.endEncoding(); 41 | 42 | return false; 43 | } 44 | } 45 | 46 | void PointsViewQGLWidget::endVideoRecording(std::function callback) { 47 | auto drawFunc = [&lc = locusController](const QMatrix4x4 &projMatrix, size_t time) { 48 | lc.draw(projMatrix, time); 49 | }; 50 | videoEncoder.endEncoding(std::move(drawFunc), std::move(callback)); 51 | } 52 | 53 | void PointsViewQGLWidget::clearAll() { 54 | locusController.clear(); 55 | } 56 | 57 | void PointsViewQGLWidget::initializeGL() { 58 | glEnable(GL_DEPTH_TEST); 59 | glEnable(GL_CULL_FACE); 60 | glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); 61 | glEnable(GL_POINT_SMOOTH); 62 | 63 | qglClearColor(QColor(Qt::black)); 64 | 65 | locusController.initialize(); 66 | } 67 | 68 | void PointsViewQGLWidget::resizeGL(int width, int height) { 69 | cameraController.recalculatePerspective(width, height); 70 | } 71 | 72 | void PointsViewQGLWidget::paintGL() { 73 | locusController.draw(cameraController.getMatrix(), currentTime); 74 | 75 | if (videoEncoder.isWorking()) { 76 | videoEncoder.writeState({cameraController.getPosition(), 77 | cameraController.getTarget(), 78 | currentTime}); 79 | } 80 | } 81 | 82 | void PointsViewQGLWidget::mouseMoveEvent(QMouseEvent *event) { 83 | cameraController.applyMouseMoveEvent(event); 84 | } 85 | 86 | void PointsViewQGLWidget::mousePressEvent(QMouseEvent *event) { 87 | cameraController.applyMousePressEvent(event); 88 | } 89 | 90 | void PointsViewQGLWidget::keyPressEvent(QKeyEvent *event) { 91 | cameraController.applyKeyPressEvent(event); 92 | 93 | if (event->key() == Qt::Key_R) { 94 | static auto getFileName = [](size_t number) { 95 | return QString::fromStdString("screenshot_" + std::to_string(number) + ".png"); 96 | }; 97 | 98 | static size_t screenshotNumber = 0; 99 | while (QFileInfo(getFileName(screenshotNumber)).exists()) { 100 | screenshotNumber++; 101 | } 102 | 103 | grabFrameBuffer().save(getFileName(screenshotNumber), "PNG"); 104 | } 105 | } 106 | 107 | void PointsViewQGLWidget::keyReleaseEvent(QKeyEvent *event) { 108 | cameraController.applyKeyReleaseEvent(event); 109 | } 110 | -------------------------------------------------------------------------------- /src/Preferences.cpp: -------------------------------------------------------------------------------- 1 | #include "Preferences.hpp" 2 | 3 | namespace Preferences { 4 | 5 | void Preferences::enableArcadeMode() { 6 | visualization.primitive = GL_POINTS; 7 | visualization.arcadeMode = true; 8 | } 9 | 10 | void Preferences::disableArcadeMode() { 11 | visualization.primitive = GL_LINE_STRIP; 12 | visualization.arcadeMode = false; 13 | } 14 | 15 | } //namespace Preferences 16 | -------------------------------------------------------------------------------- /src/ShaderController.cpp: -------------------------------------------------------------------------------- 1 | #include "ShaderController.hpp" 2 | 3 | namespace ShaderController { 4 | 5 | void ShaderController::initialize() { 6 | gshPoints = new QGLShader(QGLShader::Geometry, &shaderProgram); 7 | gshLines = new QGLShader(QGLShader::Geometry, &shaderProgram); 8 | 9 | gshPoints->compileSourceFile(QString(":/GeometryShaderPoints.gsh")); 10 | gshLines->compileSourceFile(QString(":/GeometryShaderLines.gsh")); 11 | 12 | shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, QString(":/VertexShader.vsh")); 13 | shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, QString(":/FragmentShader.fsh")); 14 | setPrimitive(GL_LINE_STRIP); 15 | 16 | shaderProgram.link(); 17 | } 18 | 19 | void ShaderController::startWork() { 20 | shaderProgram.bind(); 21 | shaderProgram.enableAttributeArray("vertex"); 22 | } 23 | 24 | void ShaderController::endWork() { 25 | shaderProgram.disableAttributeArray("vertex"); 26 | shaderProgram.release(); 27 | } 28 | 29 | void ShaderController::setVertex() { 30 | shaderProgram.setAttributeArray("vertex", GL_FLOAT, 0, 3); 31 | } 32 | 33 | void ShaderController::setMatrix(const QMatrix4x4 &matrix) { 34 | shaderProgram.setUniformValue("matrix", matrix); 35 | } 36 | 37 | void ShaderController::setArcadeMode(bool enabled) { 38 | shaderProgram.setUniformValue("arcadeMode", enabled); 39 | } 40 | 41 | void ShaderController::setStartTailSize(float size) { 42 | shaderProgram.setUniformValue("startTailSize", size); 43 | } 44 | 45 | void ShaderController::setFinalTailSize(float size) { 46 | shaderProgram.setUniformValue("finalTailSize", size); 47 | } 48 | 49 | void ShaderController::setTailLength(size_t length) { 50 | shaderProgram.setUniformValue("tailLength", static_cast(length)); 51 | } 52 | 53 | void ShaderController::setStartVertexIndex(size_t index) { 54 | shaderProgram.setUniformValue("startIndex", static_cast(index)); 55 | } 56 | 57 | void ShaderController::setTailColoringMode(bool enabled) { 58 | shaderProgram.setUniformValue("tailColoringMode", enabled); 59 | } 60 | 61 | void ShaderController::setTrajectoryIndex(size_t index) { 62 | shaderProgram.setUniformValue("trajectoryIndex", static_cast(index)); 63 | } 64 | 65 | void ShaderController::setTrajectoriesNumber(size_t number) { 66 | shaderProgram.setUniformValue("trajectoriesNumber", static_cast(number)); 67 | } 68 | 69 | void ShaderController::setColors(const QVector &colors) { 70 | shaderProgram.setUniformValue("colorsNumber", colors.size()); 71 | shaderProgram.setUniformValueArray("colors", colors.constData(), colors.size()); 72 | } 73 | 74 | void ShaderController::setInterpolationDistance(float distance) { 75 | shaderProgram.setUniformValue("interpolationDist", distance); 76 | } 77 | 78 | void ShaderController::setPrimitive(GLenum primitive) { 79 | shaderProgram.release(); 80 | 81 | bool isLines = shaderProgram.shaders().contains(gshLines); 82 | bool isPoints = shaderProgram.shaders().contains(gshPoints); 83 | if (primitive == GL_POINTS && !isPoints) { 84 | if (isLines) { 85 | shaderProgram.removeShader(gshLines); 86 | } 87 | shaderProgram.addShader(gshPoints); 88 | } else if (primitive == GL_LINE_STRIP && !isLines) { 89 | if (isPoints) { 90 | shaderProgram.removeShader(gshPoints); 91 | } 92 | shaderProgram.addShader(gshLines); 93 | } 94 | 95 | shaderProgram.bind(); 96 | } 97 | 98 | } //namespace ShaderController 99 | -------------------------------------------------------------------------------- /src/VideoEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Camera.hpp" 6 | #include "VideoEncoder.hpp" 7 | 8 | namespace VideoEncoder { 9 | 10 | const AVPixelFormat INPUT_PIX_FORMAT = AV_PIX_FMT_RGB32; 11 | const AVPixelFormat OUTPUT_PIX_FORMAT = AV_PIX_FMT_YUV420P; 12 | 13 | VideoEncoder::VideoEncoder() : 14 | working{false}, 15 | built{false} {} 16 | 17 | bool VideoEncoder::isWorking() const { 18 | return working; 19 | } 20 | 21 | void VideoEncoder::startEncoding(int videoWidth, int videoHeight, const char *filename) { 22 | if (working) { 23 | endEncoding(); 24 | } 25 | working = true; 26 | 27 | width = videoWidth; 28 | height = videoHeight; 29 | 30 | formatContext = nullptr; 31 | stream = nullptr; 32 | yuvFrame = nullptr; 33 | rgbFrame = nullptr; 34 | codecContext = nullptr; 35 | convertFramesContext = nullptr; 36 | 37 | AVOutputFormat *outputFormat = nullptr; 38 | AVCodec *codec = nullptr; 39 | AVDictionary *formatOptions = nullptr; 40 | 41 | #if LIBAVCODEC_VERSION_MAJOR < 58 42 | av_register_all(); 43 | #endif 44 | 45 | av_log_set_level(AV_LOG_QUIET); 46 | 47 | formatContext = avformat_alloc_context(); 48 | if (formatContext == nullptr) { 49 | throw std::bad_alloc(); 50 | } 51 | 52 | outputFormat = av_guess_format(nullptr, filename, nullptr); 53 | if (outputFormat == nullptr) { 54 | throw std::logic_error("Could not determine a format of the video."); 55 | } 56 | 57 | formatContext->oformat = outputFormat; 58 | if (av_dict_set(&formatOptions, "brand", "mp42", 0) < 0) { 59 | throw std::logic_error("Could not set the video options."); 60 | } 61 | 62 | codec = avcodec_find_encoder_by_name("mpeg4"); 63 | if (codec == nullptr) { 64 | throw std::logic_error("Could not find the codec."); 65 | } 66 | 67 | stream = avformat_new_stream(formatContext, codec); 68 | if (stream == nullptr) { 69 | throw std::logic_error("Could not create a new video stream."); 70 | } 71 | stream->time_base = (AVRational){1, 35}; 72 | 73 | codecContext = avcodec_alloc_context3(codec); 74 | 75 | if (codec->sample_fmts == nullptr) { 76 | codecContext->sample_fmt = AV_SAMPLE_FMT_S16; 77 | } else { 78 | codecContext->sample_fmt = codec->sample_fmts[0]; 79 | } 80 | codecContext->width = width; 81 | codecContext->height = height; 82 | codecContext->time_base = stream->time_base; 83 | codecContext->pix_fmt = OUTPUT_PIX_FORMAT; 84 | codecContext->gop_size = 250; 85 | codecContext->level = 31; 86 | 87 | #if LIBAVCODEC_VERSION_MAJOR < 58 88 | codecContext->flags |= CODEC_FLAG_QSCALE; 89 | #else 90 | codecContext->flags |= AV_CODEC_FLAG_QSCALE; 91 | #endif 92 | 93 | codecContext->global_quality = 1; 94 | 95 | //set up extra options 96 | av_opt_set(codecContext->priv_data, "profile", "main", 0); 97 | av_opt_set(codecContext->priv_data, "preset", "slow", 0); 98 | av_opt_set(codecContext->priv_data, "b-pyramid", "0", 0); 99 | 100 | if (avcodec_open2(codecContext, codec, nullptr) < 0) { 101 | throw std::logic_error("Could not initialize a codec context."); 102 | } 103 | 104 | av_dump_format(formatContext, 0, filename, 1); 105 | if (avio_open(&formatContext->pb, filename, AVIO_FLAG_WRITE) < 0) { 106 | throw std::logic_error("Could not create or initialize an AVIO context."); 107 | } 108 | 109 | avcodec_parameters_from_context(stream->codecpar, codecContext); 110 | if (avformat_write_header(formatContext, &formatOptions) < 0) { 111 | throw std::logic_error("Could not write the stream header in the output file."); 112 | } 113 | 114 | rgbFrame = av_frame_alloc(); 115 | if (rgbFrame == nullptr) { 116 | throw std::bad_alloc(); 117 | } 118 | rgbFrame->width = width; 119 | rgbFrame->height = height; 120 | rgbFrame->format = INPUT_PIX_FORMAT; 121 | if (av_frame_get_buffer(rgbFrame, 1) < 0) { 122 | throw std::bad_alloc(); 123 | } 124 | 125 | yuvFrame = av_frame_alloc(); 126 | if (yuvFrame == nullptr) { 127 | throw std::bad_alloc(); 128 | } 129 | yuvFrame->width = width; 130 | yuvFrame->height = height; 131 | yuvFrame->format = OUTPUT_PIX_FORMAT; 132 | if (av_frame_get_buffer(yuvFrame, 1) < 0) { 133 | throw std::bad_alloc(); 134 | } 135 | 136 | convertFramesContext = sws_getContext(width, height, INPUT_PIX_FORMAT, width, height, OUTPUT_PIX_FORMAT, 137 | SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); 138 | 139 | frameNumber = 0; 140 | 141 | built = true; 142 | } 143 | 144 | void VideoEncoder::endEncoding() { 145 | if (!working) { 146 | return; 147 | } 148 | allStates.clear(); 149 | 150 | while (built && writeFrame(nullptr)); 151 | if (formatContext != nullptr) { 152 | if (built) { 153 | av_write_trailer(formatContext); 154 | } 155 | if (formatContext->pb != nullptr) { 156 | avio_close(formatContext->pb); 157 | } 158 | avformat_free_context(formatContext); 159 | } 160 | if (yuvFrame != nullptr) { 161 | av_frame_free(&yuvFrame); 162 | } 163 | if (rgbFrame != nullptr) { 164 | av_frame_free(&rgbFrame); 165 | } 166 | if (convertFramesContext != nullptr) { 167 | sws_freeContext(convertFramesContext); 168 | } 169 | 170 | working = false; 171 | built = false; 172 | } 173 | 174 | VideoEncoder::~VideoEncoder() { 175 | endEncoding(); 176 | } 177 | 178 | bool VideoEncoder::writeFrame(AVFrame *frame) { 179 | AVPacket packet; 180 | av_init_packet(&packet); 181 | if (frame != nullptr) { 182 | frame->pts = frameNumber++; 183 | } 184 | if (avcodec_send_frame(codecContext, frame) < 0) { 185 | return false; 186 | } 187 | if (avcodec_receive_packet(codecContext, &packet) < 0) { 188 | return false; 189 | } 190 | av_packet_rescale_ts(&packet, codecContext->time_base, stream->time_base); 191 | packet.stream_index = stream->index; 192 | if (av_interleaved_write_frame(formatContext, &packet) < 0) { 193 | return false; 194 | } 195 | return true; 196 | } 197 | 198 | void VideoEncoder::writeFrame(const QImage &image) { 199 | if (!working) { 200 | return; 201 | } 202 | const uint8_t *data[8] = { 203 | image.constBits(), 204 | rgbFrame->data[1], 205 | rgbFrame->data[2], 206 | rgbFrame->data[3], 207 | rgbFrame->data[4], 208 | rgbFrame->data[5], 209 | rgbFrame->data[6], 210 | rgbFrame->data[7] 211 | }; 212 | sws_scale(convertFramesContext, data, rgbFrame->linesize, 0, image.height(), 213 | yuvFrame->data, yuvFrame->linesize); 214 | 215 | writeFrame(yuvFrame); 216 | } 217 | 218 | void VideoEncoder::writeState(const FrameState &state) { 219 | if (!working) { 220 | return; 221 | } 222 | allStates << state; 223 | } 224 | 225 | void VideoEncoder::endEncoding(std::function drawFunc, 226 | std::function callback) { 227 | if (!working) { 228 | endEncoding(); 229 | } 230 | GLint prevViewport[4]; 231 | glGetIntegerv(GL_VIEWPORT, prevViewport); 232 | glViewport(0, 0, width, height); 233 | 234 | QOpenGLFramebufferObject buffer(width, height); 235 | buffer.bind(); 236 | 237 | Camera::Camera camera; 238 | camera.recalculatePerspective(width, height); 239 | 240 | for (size_t i = 0; i < static_cast(allStates.size()); i++) { 241 | const auto &[position, target, time] = allStates[i]; 242 | 243 | camera.setPosition(position); 244 | camera.setTarget(target); 245 | drawFunc(camera.getMatrix(), time); 246 | 247 | writeFrame(buffer.toImage()); 248 | 249 | callback((i + 1) * 100 / allStates.size()); 250 | } 251 | 252 | buffer.release(); 253 | glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); 254 | } 255 | 256 | } //namespace VideoEncoder 257 | 258 | -------------------------------------------------------------------------------- /src/Window.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "DynamicSystemParser/DynamicSystemParser.hpp" 5 | #include "DynamicSystems/DynamicSystem.hpp" 6 | #include "Window.hpp" 7 | #include "ui_form.h" 8 | #include "PointsViewQGLWidget.hpp" 9 | #include "WindowPreferences.hpp" 10 | 11 | Window::Window(QWidget *parent) : QWidget(parent), ui(new Ui::Window) { 12 | auto dynamicSystemsVector = DynamicSystems::getDefaultSystems(); 13 | dynamicSystemsVector.push_back(getCustomSystem({"1", "1", "1"})); 14 | for (auto &system : dynamicSystemsVector) { 15 | QString name = system.getAttractorName().data(); 16 | dynamicSystems.emplace(std::move(name), std::move(system)); 17 | } 18 | 19 | task = new CountPointsTask(*this); 20 | 21 | windowPreferences = nullptr; 22 | 23 | setFocusPolicy(Qt::StrongFocus); 24 | ui->setupUi(this); 25 | 26 | ui->pointsViewer->setPreferences(&prefs); 27 | 28 | sliderTimer = new QTimer(this); 29 | sliderTimer->setInterval(prefs.controller.sliderTimeInterval); 30 | connect(sliderTimer, SIGNAL(timeout()), this, SLOT(updateSlider())); 31 | 32 | for (auto &[name, system] : dynamicSystems) { 33 | ui->modelsComboBox->addItem(name); 34 | } 35 | } 36 | 37 | void collectAllConstants(QLayout *currentLayout, std::vector &constants) { 38 | for (size_t i = 0; i < static_cast(currentLayout->count()); i++) { 39 | QLayoutItem *item = currentLayout->itemAt(i); 40 | 41 | if (item->layout() != nullptr) { 42 | collectAllConstants(item->layout(), constants); 43 | } else { 44 | QWidget *widget = item->widget(); 45 | if (widget == nullptr || typeid(*widget) != typeid(QDoubleSpinBox)) { 46 | continue; 47 | } 48 | constants.push_back(dynamic_cast(widget)->value()); 49 | } 50 | } 51 | } 52 | 53 | Window::DynamicSystemWrapper Window::getCustomSystem(const std::array &expressions) { 54 | return DynamicSystemParser::getDynamicSystem("Custom system", expressions); 55 | } 56 | 57 | void Window::insertConstants(const std::vector>> &goodParams) { 58 | ui->constantsComboBox->clear(); 59 | for (auto&[name, params] : goodParams) { 60 | ui->constantsComboBox->addItem(name.c_str()); 61 | } 62 | } 63 | 64 | void Window::insertExpressions(std::array array, bool readOnly) { 65 | ui->firstExpr->setText(array[0].data()); 66 | ui->firstExpr->setReadOnly(readOnly); 67 | ui->secondExpr->setText(array[1].data()); 68 | ui->secondExpr->setReadOnly(readOnly); 69 | ui->thirdExpr->setText(array[2].data()); 70 | ui->thirdExpr->setReadOnly(readOnly); 71 | } 72 | 73 | void Window::afterCountPointsUIUpdate() { 74 | timeValue = 0; 75 | ui->progressSlider->setValue(timeValue); 76 | sliderTimer->start(); 77 | 78 | clearFocus(); 79 | 80 | ui->progressSlider->setMaximum(std::min(10000, prefs.model.pointsNumber)); 81 | } 82 | 83 | void Window::updateOpenGLWidget(QVector buffer) { 84 | ui->pointsViewer->addNewLocus(std::move(buffer)); 85 | } 86 | 87 | CountPointsTask::CountPointsTask(Window &wind_) : wind(wind_) { 88 | QObject::connect(this, &CountPointsTask::updater, &wind, &Window::updateOpenGLWidget, Qt::QueuedConnection); 89 | } 90 | 91 | void CountPointsTask::run() { 92 | if (wind.ui->modelsComboBox->currentText() == "Custom system") { 93 | wind.dynamicSystems.erase("Custom system"); 94 | 95 | const std::string exprX = wind.ui->firstExpr->text().toStdString(); 96 | const std::string exprY = wind.ui->secondExpr->text().toStdString(); 97 | const std::string exprZ = wind.ui->thirdExpr->text().toStdString(); 98 | 99 | wind.dynamicSystems.emplace("Custom system", wind.getCustomSystem({exprX, exprY, exprZ})); 100 | } 101 | 102 | Window::DynamicSystemWrapper &system = wind.dynamicSystems.at(wind.ui->modelsComboBox->currentText()); 103 | 104 | for (size_t i = 0; i < wind.prefs.visualization.locusNumber; i++) { 105 | QVector buffer; 106 | buffer.reserve(wind.prefs.model.pointsNumber); 107 | long double offset = wind.prefs.model.startPointDelta * i; 108 | 109 | std::vector constants; 110 | collectAllConstants(wind.ui->constantsHolderLayout, constants); 111 | 112 | auto pushBackVector = DynamicSystemWrapper_n::getPushBackAndNormalizeLambda(buffer, 113 | wind.prefs.model.divNormalization); 114 | 115 | system.compute(std::move(pushBackVector), 116 | Model::Point{wind.prefs.model.startPoint.x + offset, 117 | wind.prefs.model.startPoint.y + offset, 118 | wind.prefs.model.startPoint.z + offset}, 119 | wind.prefs.model.pointsNumber, 120 | wind.prefs.model.deltaTime, 121 | constants); 122 | 123 | emit updater(std::move(buffer)); 124 | } 125 | } 126 | 127 | void removeAllFromLayout(QLayout *layout) { 128 | while (layout->count()) { 129 | QLayoutItem *item = layout->takeAt(0); 130 | if (item->layout() != nullptr) { 131 | removeAllFromLayout(item->layout()); 132 | delete item->layout(); 133 | } else { 134 | delete item->widget(); 135 | delete item; 136 | } 137 | } 138 | } 139 | 140 | void Window::addNewConstant(const std::string_view &name, long double initValue) { 141 | constexpr static size_t maxColumnSize = 3; 142 | 143 | QGridLayout *lastLayout = nullptr; 144 | size_t rows = 0; 145 | 146 | if (ui->constantsHolderLayout->count() != 0) { 147 | auto item = ui->constantsHolderLayout->takeAt(ui->constantsHolderLayout->count() - 1)->layout(); 148 | lastLayout = dynamic_cast(item); 149 | 150 | if (lastLayout->rowCount() == maxColumnSize) { 151 | ui->constantsHolderLayout->addLayout(lastLayout); 152 | lastLayout = nullptr; 153 | } else { 154 | rows = lastLayout->rowCount(); 155 | } 156 | } 157 | if (lastLayout == nullptr) { 158 | lastLayout = new QGridLayout(); 159 | } 160 | ui->constantsHolderLayout->addLayout(lastLayout); 161 | 162 | lastLayout->setAlignment(Qt::AlignTop); 163 | 164 | QLabel *valueLabel = new QLabel(QString::fromStdString(std::string(name))); 165 | valueLabel->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored)); 166 | lastLayout->addWidget(valueLabel, rows, 0); 167 | 168 | QLabel *equalityLabel = new QLabel(" = "); 169 | equalityLabel->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored)); 170 | lastLayout->addWidget(equalityLabel, rows, 1); 171 | 172 | QDoubleSpinBox *valueBox = new QDoubleSpinBox(); 173 | valueBox->setRange(-10000, 10000); 174 | valueBox->setDecimals(6); 175 | valueBox->setSingleStep(0.01); 176 | valueBox->setValue(initValue); 177 | 178 | lastLayout->addWidget(valueBox, rows, 2); 179 | } 180 | 181 | void Window::slot_restart_button() { 182 | ui->pointsViewer->clearAll(); 183 | (*task)(); 184 | } 185 | 186 | void Window::slot_model_selection(QString currentModel) { 187 | DynamicSystemWrapper &system = dynamicSystems.at(currentModel); 188 | insertConstants(system.getInterestingConstants()); 189 | bool readOnly = ui->modelsComboBox->currentText() != "Custom system"; 190 | insertExpressions(system.getFormulae(), readOnly); 191 | } 192 | 193 | void Window::slot_constants_selection(QString currentConstants) { 194 | removeAllFromLayout(ui->constantsHolderLayout); 195 | 196 | DynamicSystemWrapper &system = dynamicSystems.at(ui->modelsComboBox->currentText()); 197 | auto goodParams = system.getInterestingConstants(); 198 | 199 | for (auto&[name, params] : goodParams) { 200 | if (name.data() == currentConstants) { 201 | for (size_t i = 0; i < params.size(); i++) { 202 | addNewConstant(system.getVariablesNames()[i], params[i]); 203 | } 204 | 205 | break; 206 | } 207 | } 208 | } 209 | 210 | void Window::slot_time_slider(int timeValue_) { 211 | timeValue = timeValue_; 212 | ui->pointsViewer->setCurrentTime((prefs.model.pointsNumber / ui->progressSlider->maximum()) * timeValue); 213 | } 214 | 215 | void Window::slot_pause_button() { 216 | pauseState ^= true; 217 | if (!pauseState) { 218 | ui->pauseButton->setText("Pause"); 219 | } else { 220 | ui->pauseButton->setText("Continue"); 221 | } 222 | } 223 | 224 | void Window::slot_open_preferences() { 225 | windowPreferences = new WindowPreferences(this, &prefs); 226 | windowPreferences->show(); 227 | } 228 | 229 | void Window::updateSlider() { 230 | if (!pauseState && timeValue <= prefs.model.pointsNumber) { 231 | ui->progressSlider->setValue(timeValue += prefs.controller.deltaTimePerStep); 232 | ui->pointsViewer->setCurrentTime((prefs.model.pointsNumber / ui->progressSlider->maximum()) * timeValue); 233 | } 234 | ui->pointsViewer->repaint(); 235 | } 236 | 237 | void Window::updateVideoRecordingState() { 238 | static bool enabled = false; 239 | 240 | enabled ^= true; 241 | if (enabled) { 242 | bool prevPauseState = pauseState; 243 | pauseState = true; 244 | 245 | QString videoName = QFileDialog::getSaveFileName(this, tr("Save Video"), "", 246 | tr("Video (*.avi)"), nullptr, 247 | QFileDialog::Option::DontUseNativeDialog); 248 | if (videoName.isEmpty()) { 249 | pauseState = prevPauseState; 250 | enabled = false; 251 | 252 | return; 253 | } 254 | 255 | if (!videoName.endsWith(".avi")) { 256 | videoName += ".avi"; 257 | } 258 | ui->pointsViewer->startVideoRecording(videoName.toStdString().c_str()); 259 | 260 | ui->videoRecordingButton->setText("End recording"); 261 | ui->videoRecordingProgress->setEnabled(true); 262 | 263 | pauseState = prevPauseState; 264 | } else { 265 | ui->videoRecordingButton->setEnabled(false); 266 | 267 | auto changeProgress = [&bar = ui->videoRecordingProgress](int progress) { 268 | bar->setValue(progress); 269 | }; 270 | 271 | ui->pointsViewer->endVideoRecording(changeProgress); 272 | 273 | ui->videoRecordingProgress->setValue(0); 274 | ui->videoRecordingProgress->setEnabled(false); 275 | 276 | ui->videoRecordingButton->setText("Start recording"); 277 | ui->videoRecordingButton->setEnabled(true); 278 | } 279 | } 280 | 281 | Window::~Window() { 282 | removeAllFromLayout(ui->constantsHolderLayout); 283 | 284 | delete windowPreferences; 285 | delete ui; 286 | } 287 | 288 | void Window::keyPressEvent(QKeyEvent *event) { 289 | if (event->key() == Qt::Key_Space) { 290 | slot_pause_button(); 291 | } else if (event->key() == Qt::Key_Enter) { 292 | slot_restart_button(); 293 | } else { 294 | ui->pointsViewer->keyPressEvent(event); 295 | } 296 | } 297 | 298 | void Window::keyReleaseEvent(QKeyEvent *event) { 299 | ui->pointsViewer->keyReleaseEvent(event); 300 | } 301 | -------------------------------------------------------------------------------- /src/WindowPreferences.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "WindowPreferences.hpp" 6 | #include "ui_formPreferences.h" 7 | 8 | WindowPreferences::WindowPreferences(QWidget *parent, Preferences::Preferences *prefs_) : 9 | QWidget(parent), ui(new Ui::WindowPreferences), prefs(prefs_) { 10 | 11 | ui->setupUi(this); 12 | setCurrentStateInUI(); 13 | } 14 | 15 | QColor getColorFromQVector4D(const QVector4D &color) { 16 | return QColor(color.x() * 255, color.y() * 255, color.z() * 255, color.w() * 255); 17 | } 18 | 19 | QVector4D getQVector4DfromColor(const QColor &color) { 20 | return QVector4D(color.red(), color.green(), color.blue(), color.alpha()) / 255; 21 | } 22 | 23 | void WindowPreferences::addNewColorButton(QColor initColor) { 24 | constexpr static size_t MAX_COLORS_NUMBER = 7; 25 | 26 | if (static_cast(ui->colorsHolderLayout->count()) >= MAX_COLORS_NUMBER) { 27 | return; 28 | } 29 | 30 | QPushButton *button = new QPushButton(ui->colorsHolderLayout->widget()); 31 | button->setAutoFillBackground(true); 32 | button->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum)); 33 | button->setPalette(initColor); 34 | 35 | connect(button, SIGNAL(clicked()), this, SLOT(pickButtonColor())); 36 | 37 | ui->colorsHolderLayout->addWidget(button); 38 | } 39 | 40 | void WindowPreferences::setCurrentStateInUI() { 41 | /* Model settings */ 42 | ui->trajNumberValue->setValue(prefs->visualization.locusNumber); 43 | ui->pointsNumberValue->setValue(prefs->model.pointsNumber); 44 | ui->startPointsDeltaValue->setValue(prefs->model.startPointDelta); 45 | ui->deltaTimeValue->setValue(prefs->model.deltaTime); 46 | ui->xCoordValue->setValue(prefs->model.startPoint.x); 47 | ui->yCoordValue->setValue(prefs->model.startPoint.y); 48 | ui->zCoordValue->setValue(prefs->model.startPoint.z); 49 | 50 | /* Camera settings */ 51 | ui->sensitivitySlider->setValue((prefs->camera.sensitivity - 0.0005) / (0.03 - 0.0005) * 100); 52 | ui->speedMoveSlider->setValue((prefs->camera.speed - 0.05) / (0.3 - 0.05) * 100); 53 | 54 | /* View settings */ 55 | ui->tailPointsNumberValue->setValue(prefs->visualization.tailPointsNumber); 56 | ui->timerSpeedValue->setValue(prefs->controller.deltaTimePerStep); 57 | ui->interpolationDegree->setValue((0.21 - prefs->visualization.interpolationDistance) / 0.002); 58 | 59 | for (const auto &color : prefs->visualization.colors) { 60 | addNewColorButton(getColorFromQVector4D(color)); 61 | } 62 | 63 | if (prefs->visualization.arcadeMode) { 64 | ui->arcadeModeCheckBox->setCheckState(Qt::CheckState::Checked); 65 | } else { 66 | ui->arcadeModeCheckBox->setCheckState(Qt::CheckState::Unchecked); 67 | } 68 | 69 | /* Camera settings */ 70 | ui->videoWidthValue->setValue(prefs->video.width); 71 | ui->videoHeightValue->setValue(prefs->video.height); 72 | } 73 | 74 | void WindowPreferences::setStateFromUI() { 75 | /* Model settings */ 76 | prefs->visualization.locusNumber = ui->trajNumberValue->value(); 77 | prefs->model.pointsNumber = ui->pointsNumberValue->value(); 78 | prefs->model.startPointDelta = ui->startPointsDeltaValue->value(); 79 | prefs->model.deltaTime = ui->deltaTimeValue->value(); 80 | prefs->model.startPoint.x = ui->xCoordValue->value(); 81 | prefs->model.startPoint.y = ui->yCoordValue->value(); 82 | prefs->model.startPoint.z = ui->zCoordValue->value(); 83 | 84 | /* Camera settings */ 85 | prefs->camera.speed = 0.05 + ui->speedMoveSlider->value() / 100.0 * (0.3 - 0.05); 86 | prefs->camera.sensitivity = 0.0005 + ui->sensitivitySlider->value() / 100.0 * (0.03 - 0.0005); 87 | 88 | /* View settings */ 89 | prefs->visualization.tailPointsNumber = ui->tailPointsNumberValue->value(); 90 | prefs->controller.deltaTimePerStep = ui->timerSpeedValue->value(); 91 | prefs->visualization.interpolationDistance = 0.21 - ui->interpolationDegree->value() * 0.002; 92 | 93 | prefs->visualization.colors.clear(); 94 | while (ui->colorsHolderLayout->count() > 1) { 95 | QPushButton *button = static_cast(ui->colorsHolderLayout->takeAt(1)->widget()); 96 | prefs->visualization.colors.push_back(getQVector4DfromColor(button->palette().button().color())); 97 | } 98 | 99 | if (ui->arcadeModeCheckBox->checkState() == Qt::CheckState::Checked) { 100 | prefs->enableArcadeMode(); 101 | } else { 102 | prefs->disableArcadeMode(); 103 | } 104 | 105 | /* Camera settings */ 106 | prefs->video.width = ui->videoWidthValue->value(); 107 | prefs->video.height = ui->videoHeightValue->value(); 108 | 109 | prefs->controller.preferencesChanged = true; 110 | } 111 | 112 | void WindowPreferences::slot_cancel_button() { 113 | setCurrentStateInUI(); 114 | this->hide(); 115 | } 116 | 117 | void WindowPreferences::slot_apply_button() { 118 | setStateFromUI(); 119 | this->hide(); 120 | } 121 | 122 | void WindowPreferences::slot_set_default_button() { 123 | *prefs = Preferences::defaultPreferences; 124 | setCurrentStateInUI(); 125 | this->hide(); 126 | } 127 | 128 | void WindowPreferences::addNewColor() { 129 | static std::mt19937 rnd(std::chrono::steady_clock::now().time_since_epoch().count()); 130 | 131 | int color = rnd(); 132 | addNewColorButton(QColor::fromRgb(color > 0 ? color : -color)); 133 | } 134 | 135 | void WindowPreferences::removeLastColor() { 136 | if (ui->colorsHolderLayout->count() <= 2) { 137 | return; 138 | } 139 | 140 | QLayoutItem *item = ui->colorsHolderLayout->takeAt(ui->colorsHolderLayout->count() - 1); 141 | delete item->widget(); 142 | delete item; 143 | } 144 | 145 | void WindowPreferences::pickButtonColor() { 146 | QPushButton *button = static_cast(sender()); 147 | QColor color = QColorDialog::getColor(Qt::white, button, "Choose a color", QColorDialog::DontUseNativeDialog); 148 | if (color != QColor::Invalid) { 149 | button->setPalette(color); 150 | } 151 | } 152 | 153 | WindowPreferences::~WindowPreferences() { 154 | delete ui; 155 | } 156 | -------------------------------------------------------------------------------- /src/form.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Window 4 | 5 | 6 | 7 | 0 8 | 0 9 | 854 10 | 651 11 | 12 | 13 | 14 | Dynamic systems 15 | 16 | 17 | 18 | 19 | 20 | false 21 | 22 | 23 | 0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 0 32 | 0 33 | 34 | 35 | 36 | 37 | 120 38 | 16777215 39 | 40 | 41 | 42 | Preferences 43 | 44 | 45 | 46 | 47 | 48 | 49 | Start recording 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | x'= 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 0 75 | 0 76 | 77 | 78 | 79 | 80 | 88 81 | 24 82 | 83 | 84 | 85 | 86 | 120 87 | 16777215 88 | 89 | 90 | 91 | Pause 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 370 100 | 370 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | Start modeling 109 | 110 | 111 | 112 | 113 | 114 | 115 | 10000 116 | 117 | 118 | 10 119 | 120 | 121 | 0 122 | 123 | 124 | Qt::Horizontal 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | y'= 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | z'= 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | QLayout::SetDefaultConstraint 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | PointsViewQGLWidget 171 | QWidget 172 |
PointsViewQGLWidget.hpp
173 | 1 174 |
175 |
176 | 177 | 178 | 179 | buildModelButton 180 | clicked() 181 | Window 182 | slot_restart_button() 183 | 184 | 185 | 414 186 | 639 187 | 188 | 189 | 414 190 | 504 191 | 192 | 193 | 194 | 195 | progressSlider 196 | valueChanged(int) 197 | Window 198 | slot_time_slider(int) 199 | 200 | 201 | 45 202 | 397 203 | 204 | 205 | 7 206 | 348 207 | 208 | 209 | 210 | 211 | modelsComboBox 212 | currentTextChanged(QString) 213 | Window 214 | slot_model_selection(QString) 215 | 216 | 217 | 842 218 | 443 219 | 220 | 221 | 507 222 | 380 223 | 224 | 225 | 226 | 227 | constantsComboBox 228 | currentTextChanged(QString) 229 | Window 230 | slot_constants_selection(QString) 231 | 232 | 233 | 471 234 | 459 235 | 236 | 237 | 508 238 | 422 239 | 240 | 241 | 242 | 243 | pauseButton 244 | clicked() 245 | Window 246 | slot_pause_button() 247 | 248 | 249 | 35 250 | 444 251 | 252 | 253 | 4 254 | 413 255 | 256 | 257 | 258 | 259 | preferencesButton 260 | clicked() 261 | Window 262 | slot_open_preferences() 263 | 264 | 265 | 211 266 | 444 267 | 268 | 269 | 3 270 | 377 271 | 272 | 273 | 274 | 275 | videoRecordingButton 276 | clicked() 277 | Window 278 | updateVideoRecordingState() 279 | 280 | 281 | 330 282 | 439 283 | 284 | 285 | 426 286 | 325 287 | 288 | 289 | 290 | 291 | 292 | slot_restart_button() 293 | slot_time_slider(int) 294 | slot_model_selection(QString) 295 | slot_constants_selection(QString) 296 | slot_pause_button() 297 | slot_open_preferences() 298 | updateVideoRecordingState() 299 | 300 |
301 | -------------------------------------------------------------------------------- /src/formPreferences.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | WindowPreferences 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 758 13 | 404 14 | 15 | 16 | 17 | Qt::StrongFocus 18 | 19 | 20 | Qt::DefaultContextMenu 21 | 22 | 23 | Настройки 24 | 25 | 26 | true 27 | 28 | 29 | 30 | 31 | 32 | 0 33 | 34 | 35 | 36 | 37 | Cancel 38 | 39 | 40 | 41 | 42 | 43 | 44 | Set default values 45 | 46 | 47 | 48 | 49 | 50 | 51 | Accept 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 0 61 | 62 | 63 | 64 | Modeling 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Delta time 73 | 74 | 75 | 76 | 77 | 78 | 79 | 10 80 | 81 | 82 | 1.000000000000000 83 | 84 | 85 | 0.001000000000000 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | Start point position 97 | 98 | 99 | 100 | 101 | 102 | 103 | 5 104 | 105 | 106 | -10000.000000000000000 107 | 108 | 109 | 10000.000000000000000 110 | 111 | 112 | 113 | 114 | 115 | 116 | 5 117 | 118 | 119 | -1000.000000000000000 120 | 121 | 122 | 1000.000000000000000 123 | 124 | 125 | 126 | 127 | 128 | 129 | 5 130 | 131 | 132 | -1000.000000000000000 133 | 134 | 135 | 1000.000000000000000 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | Start points number 147 | 148 | 149 | 150 | 151 | 152 | 153 | 100000 154 | 155 | 156 | 100 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | Trajectory size (in points) 168 | 169 | 170 | 171 | 172 | 173 | 174 | 200000 175 | 176 | 177 | 1000 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | Distance between start points 189 | 190 | 191 | 192 | 193 | 194 | 195 | 5 196 | 197 | 198 | 10.000000000000000 199 | 200 | 201 | 0.010000000000000 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | Camera 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 0 221 | 0 222 | 223 | 224 | 225 | Movement speed 226 | 227 | 228 | 229 | 230 | 231 | 232 | 1 233 | 234 | 235 | 100 236 | 237 | 238 | Qt::Horizontal 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 0 251 | 0 252 | 253 | 254 | 255 | Sensitivity 256 | 257 | 258 | 259 | 260 | 261 | 262 | 1 263 | 264 | 265 | 100 266 | 267 | 268 | Qt::Horizontal 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | Visualization 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | Visible tail size (in points) 287 | 288 | 289 | 290 | 291 | 292 | 293 | 10 294 | 295 | 296 | 1000 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | Time speed 308 | 309 | 310 | 311 | 312 | 313 | 314 | 1 315 | 316 | 317 | 20 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | Interpolation level (in %) 329 | 330 | 331 | 332 | 333 | 334 | 335 | 0 336 | 337 | 338 | 100 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | Qt::LeftToRight 348 | 349 | 350 | Arcade mode 351 | 352 | 353 | false 354 | 355 | 356 | 357 | 358 | 359 | 360 | QLayout::SetDefaultConstraint 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 0 369 | 0 370 | 371 | 372 | 373 | Colors: 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 0 386 | 0 387 | 388 | 389 | 390 | + 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 0 399 | 0 400 | 401 | 402 | 403 | - 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | Recording 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | Height 428 | 429 | 430 | 431 | 432 | 433 | 434 | 100 435 | 436 | 437 | 2160 438 | 439 | 440 | 100 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | Width 452 | 453 | 454 | 455 | 456 | 457 | 458 | 100 459 | 460 | 461 | 4096 462 | 463 | 464 | 100 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | prefsTabWidget 478 | trajNumberValue 479 | pointsNumberValue 480 | xCoordValue 481 | yCoordValue 482 | zCoordValue 483 | sensitivitySlider 484 | speedMoveSlider 485 | tailPointsNumberValue 486 | cancelButton 487 | setDefaultButton 488 | applyButton 489 | 490 | 491 | 492 | 493 | cancelButton 494 | clicked() 495 | WindowPreferences 496 | slot_cancel_button() 497 | 498 | 499 | 55 500 | 379 501 | 502 | 503 | 7 504 | 384 505 | 506 | 507 | 508 | 509 | applyButton 510 | clicked() 511 | WindowPreferences 512 | slot_apply_button() 513 | 514 | 515 | 745 516 | 391 517 | 518 | 519 | 390 520 | 397 521 | 522 | 523 | 524 | 525 | addColorButton 526 | clicked() 527 | WindowPreferences 528 | addNewColor() 529 | 530 | 531 | 67 532 | 282 533 | 534 | 535 | 378 536 | 201 537 | 538 | 539 | 540 | 541 | removeColorButton 542 | clicked() 543 | WindowPreferences 544 | removeLastColor() 545 | 546 | 547 | 67 548 | 320 549 | 550 | 551 | 378 552 | 201 553 | 554 | 555 | 556 | 557 | 558 | slot_cancel_button() 559 | slot_set_default_button() 560 | slot_apply_button() 561 | addNewColor() 562 | removeLastColor() 563 | 564 | 565 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Window.hpp" 4 | 5 | int main(int argc, char *argv[]) { 6 | QApplication::setAttribute(Qt::AA_UseDesktopOpenGL); 7 | QApplication app(argc, argv); 8 | 9 | Window w; 10 | 11 | w.resize(w.sizeHint()); 12 | 13 | w.showMaximized(); 14 | 15 | return app.exec(); 16 | } 17 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Ofast -pipe -Wall -Wextra") 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS") 8 | 9 | project(TestDynSys) 10 | 11 | find_package(GTest REQUIRED) 12 | find_package(Threads REQUIRED) 13 | 14 | include_directories( 15 | ../include/ 16 | ${GTEST_INCLUDE_DIRS} 17 | ) 18 | 19 | find_package(Qt5Widgets REQUIRED) 20 | 21 | add_executable(TestDynSys 22 | testAll.cpp 23 | testParser.cpp 24 | ../src/DynamicSystemParser/DynamicSystemParser.cpp 25 | ../src/Parser/Parser.cpp 26 | ../src/Parser/Lexer.cpp 27 | testSystems.cpp 28 | ) 29 | 30 | qt5_use_modules(TestDynSys Widgets OpenGL) 31 | 32 | target_link_libraries(TestDynSys 33 | ${QT_LIBRARIES} 34 | ${GTEST_LIBRARIES} 35 | Threads::Threads 36 | ) 37 | 38 | enable_testing() 39 | add_Test(TestDynSys "./TestDynSys") 40 | -------------------------------------------------------------------------------- /test/testAll.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | int main(int argc, char **argv) { 4 | ::testing::InitGoogleTest(&argc, argv); 5 | return RUN_ALL_TESTS(); 6 | } -------------------------------------------------------------------------------- /test/testParser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gtest/gtest.h" 4 | #include "Parser/Parser.hpp" 5 | #include "DynamicSystemParser/DynamicSystemParser.hpp" 6 | 7 | TEST(parser, simple_integer_arithmetic) { 8 | auto func = Parser::parseExpression("2+1", {nullptr, nullptr, nullptr}); 9 | EXPECT_EQ(func->calc(), 3); 10 | func = Parser::parseExpression("6 - 1", {nullptr, nullptr, nullptr}); 11 | EXPECT_EQ(func->calc(), 5); 12 | func = Parser::parseExpression("1 + 2 - 6 - 1", {nullptr, nullptr, nullptr}); 13 | EXPECT_EQ(func->calc(), -4); 14 | func = Parser::parseExpression("2 * 12", {nullptr, nullptr, nullptr}); 15 | EXPECT_EQ(func->calc(), 24); 16 | func = Parser::parseExpression("16/8", {nullptr, nullptr, nullptr}); 17 | EXPECT_EQ(func->calc(), 2); 18 | func = Parser::parseExpression("2+2*3", {nullptr, nullptr, nullptr}); 19 | EXPECT_EQ(func->calc(), 8); 20 | func = Parser::parseExpression("2/2*3", {nullptr, nullptr, nullptr}); 21 | EXPECT_EQ(func->calc(), 3); 22 | func = Parser::parseExpression("2*(1+2)/3", {nullptr, nullptr, nullptr}); 23 | EXPECT_EQ(func->calc(), 2); 24 | func = Parser::parseExpression("1 + 2 * ((3) + (2 * 3)) / 3", {nullptr, nullptr, nullptr}); 25 | EXPECT_EQ(func->calc(), 7); 26 | func = Parser::parseExpression("2^3", {nullptr, nullptr, nullptr}); 27 | EXPECT_EQ(func->calc(), 8); 28 | func = Parser::parseExpression("2^3^2 - (2^3)^2", {nullptr, nullptr, nullptr}); 29 | EXPECT_EQ(func->calc(), 448); 30 | } 31 | 32 | TEST(parser, simple_float_arithmetic) { 33 | auto func = Parser::parseExpression("1 / 2", {nullptr, nullptr, nullptr}); 34 | EXPECT_NEAR(func->calc(), 0.5, 0.0000001); 35 | func = Parser::parseExpression("1.12 + 2.34", {nullptr, nullptr, nullptr}); 36 | EXPECT_NEAR(func->calc(), 3.46, 0.0000001); 37 | func = Parser::parseExpression("1.567", {nullptr, nullptr, nullptr}); 38 | EXPECT_NEAR(func->calc(), 1.567, 0.0000001); 39 | func = Parser::parseExpression("1,567 + 1,1", {nullptr, nullptr, nullptr}); 40 | EXPECT_NEAR(func->calc(), 2.667, 0.0000001); 41 | func = Parser::parseExpression("1.12 + 2.3*1.1 - 1.2", {nullptr, nullptr, nullptr}); 42 | EXPECT_NEAR(func->calc(), (1.12 + 2.3 * 1.1 - 1.2), 0.0000001); 43 | func = Parser::parseExpression("1.12*(2 + 3.7) - 8.456", {nullptr, nullptr, nullptr}); 44 | EXPECT_NEAR(func->calc(), (1.12 * (2 + 3.7) - 8.456), 0.0000001); 45 | func = Parser::parseExpression("3.72*(2.1345 + 3.7)/1.32 - 1.2453*8.456", {nullptr, nullptr, nullptr}); 46 | EXPECT_NEAR(func->calc(), (3.72 * (2.1345 + 3.7) / 1.32 - 1.2453 * 8.456), 0.0000001); 47 | func = Parser::parseExpression("1.12^4 - 2.5^3.6", {nullptr, nullptr, nullptr}); 48 | EXPECT_NEAR(func->calc(), -25.502, 1e-3); 49 | } 50 | 51 | TEST(parser, simple_variables_arithmetic) { 52 | long double x = 1, y = 1, z = 1; 53 | auto func = Parser::parseExpression("(1 + x) / 2", {&x, &y, &z}); 54 | EXPECT_NEAR(func->calc(), 1, 0.0000001); 55 | x = -4; 56 | z = y = 20; 57 | EXPECT_NEAR(func->calc(), -1.5, 0.0000001); 58 | func = Parser::parseExpression("(y + x) / z", {&x, &y, &z}); 59 | EXPECT_NEAR(func->calc(), (y + x) / z, 0.0000001); 60 | x = 12.34; 61 | y = -765.1; 62 | EXPECT_NEAR(func->calc(), (y + x) / z, 0.0000001); 63 | z = 1.2; 64 | EXPECT_NEAR(func->calc(), (y + x) / z, 0.0000001); 65 | } 66 | 67 | TEST(parser, parse_errors) { 68 | long double x = 1, y = 1, z = 1; 69 | auto func = Parser::parseExpression("(1)", {&x, &y, &z}); 70 | func = Parser::parseExpression("1 / 0", {&x, &y, &z}); 71 | long double val = func->calc(); 72 | EXPECT_EQ(val, INFINITY); 73 | bool caught = false; 74 | try { 75 | func = Parser::parseExpression("(1++1)", {&x, &y, &z}); 76 | } catch (const Parser::ParserException &) { 77 | caught = true; 78 | } 79 | EXPECT_TRUE(caught); 80 | caught = false; 81 | try { 82 | func = Parser::parseExpression("()((1+)1)", {&x, &y, &z}); 83 | } catch (const Parser::ParserException &) { 84 | caught = true; 85 | } 86 | EXPECT_TRUE(caught); 87 | caught = false; 88 | try { 89 | func = Parser::parseExpression("2*(21 - 1?1)", {&x, &y, &z}); 90 | } catch (const Parser::ParserException &) { 91 | caught = true; 92 | } 93 | EXPECT_TRUE(caught); 94 | caught = false; 95 | try { 96 | func = Parser::parseExpression("2(21 - 1)", {&x, &y, &z}); 97 | } catch (const Parser::ParserException &) { 98 | caught = true; 99 | } 100 | EXPECT_TRUE(caught); 101 | caught = false; 102 | try { 103 | func = Parser::parseExpression("2z - x", {&x, &y, &z}); 104 | } catch (const Parser::ParserException &) { 105 | caught = true; 106 | } 107 | EXPECT_TRUE(caught); 108 | caught = false; 109 | try { 110 | func = Parser::parseExpression("sin(5", {&x, &y, &z}); 111 | } catch (const Parser::ParserException &) { 112 | caught = true; 113 | } 114 | EXPECT_TRUE(caught); 115 | caught = false; 116 | try { 117 | func = Parser::parseExpression("a + 3 - 2 * c", {&x, &y, &z}, {{"a", 3}}); 118 | } catch (const Parser::ParserException &) { 119 | caught = true; 120 | } 121 | EXPECT_TRUE(caught); 122 | } 123 | 124 | TEST(parser, lambda_test) { 125 | std::vector emptyVector; 126 | auto lambdaGetter = DynamicSystemParser::Impl::parseExpressions("x+y", "y - 1.1*z", "z - 1", {}); 127 | auto lambda = lambdaGetter(emptyVector); 128 | EXPECT_NEAR(lambda({1, 1, 1}).x, (Model::Point{2, -0.1, 0}).x, 1e-10); 129 | EXPECT_NEAR(lambda({1, 1, 1}).y, (Model::Point{2, -0.1, 0}).y, 1e-10); 130 | EXPECT_NEAR(lambda({1, 1, 1}).z, (Model::Point{2, -0.1, 0}).z, 1e-10); 131 | EXPECT_NEAR(lambda({-1, 0, 2}).x, (Model::Point{-1, -2.2, 1}).x, 1e-10); 132 | EXPECT_NEAR(lambda({-1, 0, 2}).y, (Model::Point{-1, -2.2, 1}).y, 1e-10); 133 | EXPECT_NEAR(lambda({-1, 0, 2}).z, (Model::Point{-1, -2.2, 1}).z, 1e-10); 134 | } 135 | 136 | TEST(parser, strange_format) { 137 | auto func = Parser::parseExpression("2\n +\n 1\n", {nullptr, nullptr, nullptr}); 138 | EXPECT_EQ(func->calc(), 3); 139 | func = Parser::parseExpression("6 - 1\n", {nullptr, nullptr, nullptr}); 140 | EXPECT_EQ(func->calc(), 5); 141 | func = Parser::parseExpression(" 1 + 2 - 6 - 1", {nullptr, nullptr, nullptr}); 142 | EXPECT_EQ(func->calc(), -4); 143 | func = Parser::parseExpression(" 2 * 12 ", {nullptr, nullptr, nullptr}); 144 | EXPECT_EQ(func->calc(), 24); 145 | } 146 | 147 | TEST(parser, use_variables) { 148 | auto func = Parser::parseExpression("2 + a * bedy - c", {nullptr, nullptr, nullptr}, 149 | {{"a", 0.8}, 150 | {"bedy", 10.5}, 151 | {"c", -1}}); 152 | EXPECT_NEAR(func->calc(), 11.4, 1e-10); 153 | func = Parser::parseExpression("(2 * abcd - 3) + (2 * abcd * abcd * (abc - 2)) / 3 - 1", 154 | {nullptr, nullptr, nullptr}, 155 | {{"abcd", 11.7}, 156 | {"abc", -3.15}}); 157 | EXPECT_NEAR(func->calc(), -450.589, 1e-10); 158 | func = Parser::parseExpression("3 * pi - e * 2 - (3 + e + pi) / (pi * pi - 2)", {nullptr, nullptr, nullptr}); 159 | EXPECT_NEAR(func->calc(), 2.862, 1e-3); 160 | } 161 | 162 | TEST(parser, function_calls) { 163 | auto func = Parser::parseExpression("sin(pi) + cos(2 * tan(pi)) - 3 * sin(2) / cos(sin(3))", 164 | {nullptr, nullptr, nullptr}); 165 | EXPECT_NEAR(func->calc(), -1.755, 1e-3); 166 | func = Parser::parseExpression( 167 | "ln(100) * log(13) / 2 * (atan(3 + sin(hello)) - atanh(0.3)) * acos(0.5) - hello * sqrt(abs(hello))", 168 | {nullptr, nullptr, nullptr}, 169 | {{"hello", -123}}); 170 | EXPECT_NEAR(func->calc(), 1366.768, 1e-3); 171 | EXPECT_NEAR(Parser::parseExpression("exp(2)", {nullptr, nullptr, nullptr})->calc(), 172 | Parser::parseExpression("e ^ 2 - sin(pi) * cos(112.2)", {nullptr, nullptr, nullptr})->calc(), 1e-10); 173 | } 174 | 175 | TEST(parser, strong_tests) { 176 | auto func = Parser::parseExpression("sin(priv^ 2 * abs(-priv * priv * e / pre) + sin(3 * pi / " 177 | "cos(tan(log(acos(pi * (1 / pre / pre)) + e^(priv - pre + prev - 0.5)^2)" 178 | "/ 2 + priv / priv * priv * (e - pi / 2 + asinh(acosh(10) - prev * (3 / 2.5)))" 179 | "- 100 / sqrt(e^3)^3) + pi^e^e) - 10^(e + e / 2 - 3)) - 100)", 180 | {nullptr, nullptr, nullptr}, 181 | {{"priv", 10}, 182 | {"pre", 2}, 183 | {"prev", 2.5}}); 184 | EXPECT_NEAR(func->calc(), 0.8358, 1e-4); 185 | func = Parser::parseExpression("ln(sin(asin(a / 5 * 2) + asin(0.3232) * cos(abs(aaa * 20) / e)" 186 | " + e^(a * 20 - aa - aaa * 3) - e^5 * e^(aa + aa)" 187 | "/ pi^10) ^abs(aaa * 6) / ln(sin(cos(sin(cos(3) + 2) + aa * 2) - (1 / aa))" 188 | "+ ln(100) + atanh(0.3))^3 + 100)", {nullptr, nullptr, nullptr}, 189 | {{"a", 0.5}, 190 | {"aa", 1.5}, 191 | {"aaa", -0.5}}); 192 | EXPECT_NEAR(func->calc(), 4.6044, 1e-4); 193 | } 194 | -------------------------------------------------------------------------------- /test/testSystems.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Model/Model.hpp" 3 | #include "DynamicSystems/DynamicSystem.hpp" 4 | 5 | auto getCounter(int &count) { 6 | return [&count](const Model::Point &) { 7 | ++count; 8 | }; 9 | } 10 | 11 | int countConvergesSystems(const Model::Point &startPoint, int requiredCount, long double tau) { 12 | int convergesCount = 0; 13 | 14 | auto vectorSystems = DynamicSystems::getDefaultSystems()))>(); 15 | 16 | for (auto &system : vectorSystems) { 17 | auto constantValues = system.getInterestingConstants(); 18 | for (auto&[name, params] : constantValues) { 19 | int count = 0; 20 | system.compute(getCounter(count), startPoint, requiredCount, tau, params); 21 | if (count == requiredCount) convergesCount++; 22 | } 23 | } 24 | return convergesCount; 25 | } 26 | 27 | TEST(model, allConstantRK4_1) { 28 | Model::Point startPoint = {0.2, 0.3, 0.1}; 29 | int requiredCount = 100'000; 30 | long double tau = 0.01; 31 | 32 | EXPECT_GE(countConvergesSystems(startPoint, requiredCount, tau), 34); 33 | } 34 | 35 | 36 | TEST(model, allConstantRK4_2) { 37 | Model::Point startPoint = {0.02, 0.03, 0.01}; 38 | int requiredCount = 500'000; 39 | long double tau = 0.01; 40 | 41 | EXPECT_GE(countConvergesSystems(startPoint, requiredCount, tau), 33); 42 | } 43 | 44 | TEST(model, allConstantRK4_3) { 45 | Model::Point startPoint = {0.02, 0.03, 0.01}; 46 | int requiredCount = 200'000; 47 | long double tau = 0.0001; 48 | 49 | EXPECT_GE(countConvergesSystems(startPoint, requiredCount, tau), 37); 50 | } 51 | 52 | TEST(model, allConstantRK4_4) { 53 | Model::Point startPoint = {0.02, 0.03, 0.01}; 54 | int requiredCount = 100'000; 55 | long double tau = 0.1; 56 | 57 | EXPECT_GE(countConvergesSystems(startPoint, requiredCount, tau), 21); 58 | } --------------------------------------------------------------------------------