diff --git a/CMakeLists.txt b/CMakeLists.txt index 712ee1883ff933236db11fb0281956f7aaad5cb9..10469e98e2ec2a704448f582e189692e4ec9649d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,19 @@ -# CMake file for the Q-Pong game. +## CMake file for the Q-Pong game. +## +project(QuantumPong) cmake_minimum_required(VERSION 3.2) -project(run VERSION 1.0 LANGUAGES CXX) +## Use modern C++! set(CMAKE_CXX_STANDARD 17) +set(CMAKE_BUILD_TYPE Release) -find_package(PkgConfig REQUIRED) - -pkg_check_modules(GTKMM gtkmm-3.0) -include_directories(${GTKMM_INCLUDE_DIRS}) -include_directories(src) -add_definitions(${GTKMM_INCLUDE_DIRS}) +find_package(PkgConfig REQUIRED) -add_executable(run src/main.cpp src/ParticleImage.cpp src/ArrayCanvas.cpp src/Particle.cpp src/QuantumMath.cpp ) +message("Build type is ${CMAKE_BUILD_TYPE}") -find_package (Threads) +add_subdirectory( + src +) -target_link_libraries (run ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(run ${GTKMM_LIBRARIES}) -target_link_libraries(run m) -target_link_libraries(run fftw3 fftw3_omp) diff --git a/src/ArrayCanvas.cpp b/src/ArrayCanvas.cpp index d09405e88778ee6679157295d80acd47bc446254..519894feeb9dd1cec32a48d29ce65496850a9cdc 100644 --- a/src/ArrayCanvas.cpp +++ b/src/ArrayCanvas.cpp @@ -12,7 +12,7 @@ bool ArrayCanvas::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) { // Clear background. cr->save(); - cr->set_source_rgba(0.0, 0.0, 0.0, 0.0); + cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); cr->paint(); // Print arrays. @@ -20,22 +20,50 @@ bool ArrayCanvas::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) { for (unsigned x = 0; x < k_drawingAreaWidth; x++) { - - // Print local QColor c; - c = m_bufLocalRepresentation->getValue(x, y); + double xi = x * imageScaleFactor; + double yi = y * imageScaleFactor; + + // Print obstacles + // c = m_obstacles->getValue(x, y); + // cr->set_source_rgba(c.r, c.g, c.b, c.a); + // cr->rectangle(xi, yi, imageScaleFactor, imageScaleFactor); + // cr->fill(); + + // Print position + c = m_bufPositionRepresentation->getValue(x, y); cr->set_source_rgba(c.r, c.g, c.b, c.a); - cr->rectangle(x*imageScaleFactor, y*imageScaleFactor, imageScaleFactor, imageScaleFactor); + cr->rectangle(xi, yi, imageScaleFactor, imageScaleFactor); cr->fill(); - // Print impulse - c = m_bufImpulseRepresentation->getValue(x, y); + // Print momentum + c = m_bufMomentumRepresentation->getValue(x, y); cr->set_source_rgba(c.r, c.g, c.b, c.a); - cr->rectangle((x * imageScaleFactor+ k_drawingAreaWidth*imageScaleFactor), y * imageScaleFactor, imageScaleFactor, imageScaleFactor); + cr->rectangle((xi+ k_drawingAreaWidth*imageScaleFactor), yi, imageScaleFactor, imageScaleFactor); cr->fill(); - } } + + constexpr int crossSize = 10; + + QColor c = {1.0, 0.9, 0.9, 0.2}; + cr->set_source_rgba(c.r, c.g, c.b, c.a); + + // draw horizontal half of cross + cr->move_to((nx* imageScaleFactor + nx* imageScaleFactor/2 - crossSize* imageScaleFactor/2), (nx* imageScaleFactor/2)); + cr->line_to(nx* imageScaleFactor + nx* imageScaleFactor/2 + crossSize* imageScaleFactor/2, (ny* imageScaleFactor/2)); + cr->stroke(); + + // draw certical half of cross + cr->move_to((nx* imageScaleFactor + nx* imageScaleFactor/2), (nx* imageScaleFactor/2 - crossSize* imageScaleFactor/2)); + cr->line_to((nx* imageScaleFactor + nx* imageScaleFactor/2), (nx* imageScaleFactor/2 + crossSize* imageScaleFactor/2)); + cr->stroke(); + + // draw seperator between position and momentum image + cr->move_to(nx*imageScaleFactor, 0); + cr->line_to(nx*imageScaleFactor, ny * imageScaleFactor); + cr->stroke(); + cr->restore(); return true; } \ No newline at end of file diff --git a/src/ArrayCanvas.hpp b/src/ArrayCanvas.hpp index 1fb5b34e112473f0f638a6550e6f8af0e4f7c14c..65a9c940f3be8319410c401ae9e843786da814b8 100644 --- a/src/ArrayCanvas.hpp +++ b/src/ArrayCanvas.hpp @@ -16,7 +16,6 @@ #include "ParticleImage.hpp" -/// /// @brief Class ArrayCanvas. Draws two ParticleImages when triggered. /// class ArrayCanvas : public Gtk::DrawingArea @@ -24,16 +23,14 @@ class ArrayCanvas : public Gtk::DrawingArea public: - /// /// @brief Class ArrayCanvas drawing area for two ParticleImages. /// - ArrayCanvas(ParticleImage *momentum, ParticleImage *position) : m_bufImpulseRepresentation{momentum}, m_bufLocalRepresentation{position} + ArrayCanvas(ParticleImage *momentum, ParticleImage *position, ParticleImage *obstacles) : m_bufMomentumRepresentation{momentum}, m_bufPositionRepresentation{position}, m_obstacles{obstacles} { // Connect dispatcher m_dispatcher.connect(sigc::mem_fun(*this, &ArrayCanvas::queueDraw)); } - /// /// @brief Trigger the redraw of this Canvas for example from another thread. /// void triggerRedraw() @@ -43,25 +40,23 @@ public: protected: - /// + /// @brief Define the behaviour when on_draw is called. /// bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override; private: - - /// + /// @brief Bridge between threads to trigger the redraw of the canvas. /// Glib::Dispatcher m_dispatcher; - /// /// @brief ParitcleImage copys from the simulation, which will be drawn to the screen. /// - ParticleImage *m_bufLocalRepresentation; - ParticleImage *m_bufImpulseRepresentation; + ParticleImage *m_bufPositionRepresentation; + ParticleImage *m_bufMomentumRepresentation; + ParticleImage *m_obstacles; - /// /// @brief Wraps function to trigger redraw. /// This function cannot be called from another thread. /// Therefore the call of this function will be triggered by the dispatcher. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8dc5c99d410e300cb263fafdb07ea18e3c5007c0 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,31 @@ +## library for the app +add_library(qpong + QuantumMath.cpp + Particle.cpp + ParticleImage.cpp + ArrayCanvas.cpp +) + +## create the application +add_executable(qpong.app + main.cpp +) + +# we do need some threads +find_package(Threads) + +## get all GTKMM dependencies and configuration +pkg_check_modules(GTKMM gtkmm-3.0) + +include_directories(${GTKMM_INCLUDE_DIRS}) +link_directories(${GTKMM_LIBRARY_DIRS}) + +## link all necessary libs to the target +target_link_libraries(qpong.app + qpong + ${CMAKE_THREAD_LIBS_INIT} + ${GTKMM_LIBRARIES} + fftw3 + fftw3_omp + m # math +) diff --git a/src/Configuration.hpp b/src/Configuration.hpp index 87983004fe4cefeb2cd24f0541ba9694721d655b..c44ef0282a781d91c84321269597ce97d3c3052c 100644 --- a/src/Configuration.hpp +++ b/src/Configuration.hpp @@ -8,64 +8,52 @@ #ifndef QPONG_CONFIGURATION_HPP #define QPONG_CONFIGURATION_HPP -/// /// @brief Width of the drawing area in pixels. /// -constexpr unsigned long k_drawingAreaWidth = 300; // crashes at 418 -constexpr unsigned imageScaleFactor = 4; +constexpr unsigned long k_drawingAreaWidth = 256; // crashes at 418 +constexpr unsigned imageScaleFactor = 3; -/// /// @brief Height of the drawing area in pixels. /// constexpr unsigned long k_drawingAreaHeight = k_drawingAreaWidth; -/// /// @brief Size of one time step. /// -constexpr double dt = 0.1; +constexpr double dt = 0.15; -/// /// @brief Start position on the x-Axis. /// constexpr double k_x0 = 0.0; -/// /// @brief Start position on the y-Axis. /// -constexpr double k_y0 = 0.0; +constexpr double k_y0 = -0.5; -/// /// @brief Acceleration of gravity. /// constexpr double g = 9.81; -/// /// @brief Mass of the particle. /// constexpr double m = 1.0; -/// /// @brief Planck constant. /// constexpr double hbar = 0.0001; -/// -/// @brief Initial impulse in x direction. +/// @brief Initial momentum in x direction. /// constexpr double k_px0 = 0.02; -/// /// @brief Initial impuls in y direction. /// constexpr double k_py0 = 0.009; -/// -/// @brief This constants scales the size of the impulse on initialisation. +/// @brief This constants scales the size of the momentum on initialisation. /// constexpr double lambda = 80.0; -/// -/// @brief Scale the max value at initialisation of the impulse +/// @brief Scale the max value at initialisation of the momentum /// constexpr double A0 = 1.0; diff --git a/src/Particle.cpp b/src/Particle.cpp index 6a14bd3312cf6a767a3fb822d1063ec72b2d1755..b89ac67e5ceeeb83f04913ea7c2d151826bb6b2c 100644 --- a/src/Particle.cpp +++ b/src/Particle.cpp @@ -15,7 +15,7 @@ const Complex Ci(0.0, 1.0); -void Particle::initImpulse() +void Particle::initMomentum() { std::cout << "Initialising particle" << std::endl; @@ -32,7 +32,7 @@ void Particle::initImpulse() } } -Particle::Particle(ArrayCanvas &ac, ParticleImage *momentum, ParticleImage *position) : m_arrayCanvas{ac},m_bufImpulseRepresentation{momentum}, m_bufLocalRepresentation{position} +Particle::Particle(ArrayCanvas &ac, ParticleImage *momentum, ParticleImage *position, ParticleImage *obstacles) : m_arrayCanvas{ac},m_bufMomentumRepresentation{momentum}, m_bufPositionRepresentation{position}, m_obstacleImage{obstacles} { std::cout << "Creating particle" << std::endl; fftw_init_threads(); @@ -40,12 +40,12 @@ Particle::Particle(ArrayCanvas &ac, ParticleImage *momentum, ParticleImage *posi constexpr int k_numThreads = 4; fftw_plan_with_nthreads(k_numThreads); - m_psi = (Complex *)fftw_malloc(sizeof(fftw_complex) * n); + m_psi = static_cast<Complex *>(fftw_malloc(sizeof(fftw_complex) * n)); - m_planForward = fftw_plan_dft_1d(n, (fftw_complex *)m_psi, (fftw_complex *)m_psi, FFTW_FORWARD, FFTW_MEASURE); - m_planBackward = fftw_plan_dft_1d(n, (fftw_complex *)m_psi, (fftw_complex *)m_psi, FFTW_BACKWARD, FFTW_MEASURE); + m_planForward = fftw_plan_dft_2d(ny, nx, (fftw_complex *)m_psi, (fftw_complex *)m_psi, FFTW_FORWARD, FFTW_MEASURE); + m_planBackward = fftw_plan_dft_2d(ny, nx, (fftw_complex *)m_psi, (fftw_complex *)m_psi, FFTW_BACKWARD, FFTW_MEASURE); - initImpulse(); + initMomentum(); } Particle::~Particle() @@ -61,22 +61,31 @@ void Particle::propagate() for (unsigned long iy = 0; iy < ny; iy++) { double y = yAt(iy); +#pragma omp parallel for + for (unsigned long ix = 0; ix < nx; ix++) { - constexpr double l = 0.0003; //lambda + constexpr double l = 0.0007; //lambda double x = xAt(ix); + // double E = obstaclePotential(x, y); double E = (square(x) * square(x) + square(y/2)) * l; + // double E = (square(x) + square(y)) * l; int index = nx * iy + ix; m_psi[index] *= exp(-Ci * dt / hbar * E); + // Complex tmp = m_psi[index] * exp(-Ci * dt / hbar * E); + // m_psi[index] = tmp; + // m_bufPositionRepresentation->updateBuffer(index, tmp + E*600*Ci); + m_bufPositionRepresentation->updateBuffer(index, m_psi[index]); } } - fftw_execute (m_planForward); // transform into impulse repr. + fftw_execute (m_planForward); // transform into momentum repr. #pragma omp parallel for for (unsigned long iy = 0; iy < ny; iy++) { double py = pyAt(iy); +#pragma omp parallel for for (unsigned long ix = 0; ix < nx; ix++) { double px = pxAt(ix); @@ -86,17 +95,17 @@ void Particle::propagate() m_psi[index] *= exp(-Ci * dt / hbar * E) * N; } } - updateImpulseImage(); //momentum - fftw_execute(m_planBackward); // transform into local repr. - updateLocalImage(); //position + updateMomentumImage(); //momentum + fftw_execute(m_planBackward); // transform into position repr. + // updatePositionImage(); //position } -void Particle::updateImpulseImage() +void Particle::updateMomentumImage() { unsigned px = 0; unsigned py = 0; - unsigned long impulse_index = 0; + unsigned long momentum_index = 0; unsigned long array_index = 0; #pragma omp parallel for @@ -105,31 +114,31 @@ void Particle::updateImpulseImage() for (unsigned x = 0; x < nx; x++) { - // if (y >= ny / 2) - // { - // px = y - ny / 2; - // } - // else - // { - // px = y + ny / 2; - // } - // if (x >= nx / 2) - // { - // py = x - nx / 2; - // } - // else - // { - // py = x + nx / 2; - // } - impulse_index = x * nx + y; + if (y >= ny / 2) + { + py = y - ny / 2; + } + else + { + py = y + ny / 2; + } + if (x >= nx / 2) + { + px = x - nx / 2; + } + else + { + px = x + nx / 2; + } + + momentum_index = py * nx + px; array_index = y * nx + x; - // impulse_index = array_index; - m_bufImpulseRepresentation->updateBuffer(array_index, (m_psi[impulse_index] * 142.42)); + m_bufMomentumRepresentation->updateBuffer(array_index, (m_psi[momentum_index] * 142.42)); } } } -void Particle::updateLocalImage() +void Particle::updatePositionImage() { #pragma omp parallel for @@ -138,7 +147,7 @@ void Particle::updateLocalImage() for (unsigned x = 0; x < nx; x++) { unsigned index = y * nx + x; - m_bufLocalRepresentation->updateBuffer(index, m_psi[index]); + m_bufPositionRepresentation->updateBuffer(index, m_psi[index]); } } } diff --git a/src/Particle.hpp b/src/Particle.hpp index fba55f26f705c2c0f7ea8a07e26296cb4b858917..a970d9a84f8bceabb81f2ebce4411bc8b67e6758 100644 --- a/src/Particle.hpp +++ b/src/Particle.hpp @@ -21,15 +21,14 @@ class Particle { public: Particle() = delete; - Particle(ArrayCanvas &ac, ParticleImage *momentum, ParticleImage *position); + Particle(ArrayCanvas &ac, ParticleImage *momentum, ParticleImage *position, ParticleImage *obstacles); ~Particle(); void propagate(); - /// - /// @brief Init the array with initial impulse. - /// Representation is in local representation. + /// @brief Init the array with initial momentum. + /// Representation is in position representation. /// - void initImpulse(); + void initMomentum(); private: Complex *m_psi; ArrayCanvas &m_arrayCanvas; @@ -37,14 +36,14 @@ private: fftw_plan m_planBackward; - ParticleImage *m_bufLocalRepresentation; - ParticleImage *m_bufImpulseRepresentation; + ParticleImage *m_bufPositionRepresentation; + ParticleImage *m_bufMomentumRepresentation; + ParticleImage *m_obstacleImage; - /// /// @brief Update the array at the ArrayCanvas /// - void updateImpulseImage(); - void updateLocalImage(); + void updateMomentumImage(); + void updatePositionImage(); }; #endif \ No newline at end of file diff --git a/src/ParticleImage.cpp b/src/ParticleImage.cpp index 3ca8ba42fe9b5c3a1b7cad60a0dc82383fd40e0a..e2945ca40974b25f35e5e25edc3edefcd1b43ade 100644 --- a/src/ParticleImage.cpp +++ b/src/ParticleImage.cpp @@ -9,19 +9,22 @@ constexpr unsigned maxBufferCount = 2; void ParticleImage::updateBuffer(unsigned index, Complex v) { + m_buffer[index] = v; } const QColor ParticleImage::getValue(const unsigned x, const unsigned y) { unsigned long index = y * nx + x; - constexpr double scale_factor = 1.0; - Complex val = m_buffer[index] * scale_factor; + constexpr double scale_factor = 0.9; + Complex val = m_buffer[index]; + QColor c; + val *= scale_factor; double r = real(val); double i = imag(val); - QColor c; c.r = square(r); c.g = square(r) + square(i); c.b = square(i); + return c; } \ No newline at end of file diff --git a/src/ParticleImage.hpp b/src/ParticleImage.hpp index aecaa3af5d9e50165bcb5ffbbccc7b3dad3359b2..240b19b8027ff9af57e9bb396f913259516309c0 100644 --- a/src/ParticleImage.hpp +++ b/src/ParticleImage.hpp @@ -14,7 +14,6 @@ #include "QuantumMath.hpp" -/// /// @brief Convinient conatiner to store a RGBA value. /// struct QColor @@ -22,11 +21,10 @@ struct QColor float r = 0; float g = 0; float b = 0; - float a = 0.95; + float a = 1.0; }; -/// /// @brief Wraps an one dimensional array. /// Values can be accessed as in 2D. /// @@ -34,7 +32,6 @@ class ParticleImage { public: - /// /// @return Get Color value from particle at position (x,y). /// const QColor getValue(const unsigned x, const unsigned y); @@ -42,7 +39,6 @@ public: void updateBuffer(unsigned index, Complex v); private: - /// /// @brief Buffer for array with complex values from simulation. /// std::array<Complex, n> m_buffer; diff --git a/src/QuantumMath.cpp b/src/QuantumMath.cpp index 8650cf3ff3e63db6ef683937da015b5de9e4ec9c..92161b16f0207a03201d61b77819debd217773b3 100644 --- a/src/QuantumMath.cpp +++ b/src/QuantumMath.cpp @@ -7,6 +7,20 @@ #include "QuantumMath.hpp" +const double obstaclePotential(double x, double y) +{ + double xPos = 0; + double yPos = 0; + + constexpr double maxV = 1; + constexpr double l = 0.0007; //lambda + + double E = (square(x) * square(x) + square(y/2)) * l; + + + return E; +} + const double xAt(int i) { return (i - (nx / 2)) * dx; diff --git a/src/QuantumMath.hpp b/src/QuantumMath.hpp index 6ae50088c86c20d266ee266433db7ebe9f3531b2..258ab6cf2dd4d4df33efbe0169bb099eab84f853 100644 --- a/src/QuantumMath.hpp +++ b/src/QuantumMath.hpp @@ -35,14 +35,16 @@ constexpr int n = nx * ny; const double dx = 2.0 / nx; const double dy = 2.0 / ny; +/// @brief Potential of the obstachle that is controlled by the player. /// -/// @brief Get index to access array in local representation. +const double obstaclePotential(double x, double y); + +/// @brief Get index to access array in position representation. /// const double xAt(int i); const double yAt(int iy); -/// -/// @brief Get index to access array in impulse representation. +/// @brief Get index to access array in momentum representation. /// const double pxAt(int ix); diff --git a/src/main.cpp b/src/main.cpp index 5a86d3bb318a1085013e0ee25d90004c7c4d8fa5..d2a55e40afea2918ec119b70a4ad84c8475bac00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,27 +8,19 @@ #include <chrono> #include <thread> -#include <gtkmm/application.h> -#include <gtkmm/button.h> -#include <gtkmm/window.h> -#include <gtkmm/box.h> +#include <gtkmm.h> #include "ArrayCanvas.hpp" #include "Particle.hpp" #include "Configuration.hpp" -ParticleImage *momentum; -ParticleImage *position; bool propagating = true; -/// /// @brief This functino has to run in its own thread and propagates the particle as fast as possible. /// void simulation(Particle *p) { - //Particle *p = new Particle(ac, momentum, position); - do { p->propagate(); @@ -36,40 +28,56 @@ void simulation(Particle *p) while (propagating); } -/// /// @brief This function has to run in its own thread an will redraw the canvas every ~25ms. /// -void render(ArrayCanvas &ac) +void render(ArrayCanvas *ac) { do { using namespace std::chrono_literals; - std::this_thread::sleep_for(25ms); - ac.triggerRedraw(); + std::this_thread::sleep_for(5ms); + ac->triggerRedraw(); } while (true); - } class QWindow : public Gtk::Window { public: - QWindow(ArrayCanvas &ac) : m_button{"Initialise Particle"}, m_ac{ac} + QWindow() : m_button{"Initialise Particle"} { - m_particle = new Particle(m_ac, momentum, position); + m_momentum = new ParticleImage; + m_position = new ParticleImage; + m_obstacles = new ParticleImage; + + m_ac = new ArrayCanvas(m_momentum, m_position, m_obstacles); + + m_particle = new Particle(*m_ac, m_momentum, m_position, m_obstacles); set_border_width(10); + m_button.signal_clicked().connect(sigc::mem_fun(*this, &QWindow::on_button_clicked)); add(m_box); - m_box.pack_start(m_button, Gtk::PACK_SHRINK); - - m_box.pack_start(m_ac); - ac.show(); + m_box.pack_start(m_vBox); + m_vBox.pack_start(m_button, Gtk::PACK_SHRINK); + m_vBox.pack_start(m_label, Gtk::PACK_SHRINK); + m_vBox.pack_start(*m_ac); + m_label.set_margin_top(5); + m_label.set_margin_bottom(5); + m_ac->show(); m_button.show(); m_box.show(); + m_vBox.show(); + m_label.show(); + + m_label.set_text("Hi, you are playing QPong and this is a label!"); m_propagatingThread = std::thread(simulation, m_particle); m_propagatingThread.detach(); + + std::cout << "Starting to draw images" << std::endl; + std::thread rendering(render,m_ac); + rendering.detach(); } virtual ~QWindow(){} @@ -80,7 +88,7 @@ protected: { propagating = false; std::this_thread::sleep_for((std::chrono::seconds)1); - m_particle->initImpulse(); + m_particle->initMomentum(); m_propagatingThread = std::thread(simulation, m_particle); propagating = true; m_propagatingThread.detach(); @@ -88,40 +96,30 @@ protected: // Member widgets: Gtk::Box m_box; + Gtk::VBox m_vBox; + Gtk::Label m_label; Gtk::Button m_button; - ArrayCanvas &m_ac; + ArrayCanvas *m_ac; // qpong Particle *m_particle; std::thread m_propagatingThread; + ParticleImage *m_momentum; + ParticleImage *m_position; + ParticleImage *m_obstacles; }; -/// /// @brief MAIN /// int main(int argc, char* argv[]) { std::cout << "Starting QPong" << std::endl; - // App and window auto app = Gtk::Application::create(argc, argv, "de.armin-co.qpong"); - - // canvas - momentum = new ParticleImage; - position = new ParticleImage; - - std::cout << "Creating canvas" << std::endl; - ArrayCanvas ac{momentum, position}; std::cout << "Creating window" << std::endl; - QWindow window(ac); - window.set_default_size(k_drawingAreaWidth*imageScaleFactor*2+100, k_drawingAreaWidth*imageScaleFactor); - - std::cout << "Starting to draw images" << std::endl; - std::thread rendering(render, std::ref(ac)); - rendering.detach(); + QWindow window; + window.set_default_size(k_drawingAreaWidth*imageScaleFactor*2+130, k_drawingAreaWidth*imageScaleFactor+100); - // show and run - ac.show(); return app->run(window); -} \ No newline at end of file +}