From b6cbbc980d23a5ebd7ff9de432808721103b2154 Mon Sep 17 00:00:00 2001 From: Armin Co <armin.co@hs-bochum.de> Date: Sun, 8 Dec 2019 21:47:44 +0100 Subject: [PATCH] Fixed threading issues. New rendering and thread sage gtk without crashes. --- README.md | 7 +- src/ArrayCanvas.cpp | 69 --------------- src/ArrayCanvas.hpp | 70 --------------- src/CMakeLists.txt | 6 +- src/Common.hpp | 18 ++++ src/Configuration.hpp | 60 ------------- src/Particle.cpp | 197 ++++++++++++++++++++++++++++++------------ src/Particle.hpp | 47 ++++++++-- src/ParticleImage.cpp | 67 +++++++++++--- src/ParticleImage.hpp | 18 ++-- src/QDrawingArea.cpp | 40 +++++++++ src/QDrawingArea.hpp | 29 +++++++ src/QWindow.cpp | 160 ++++++++++++++++++++++++++++++++++ src/QWindow.hpp | 59 +++++++++++++ src/QuantumMath.cpp | 46 ---------- src/QuantumMath.hpp | 52 ----------- src/main.cpp | 111 ++---------------------- 17 files changed, 570 insertions(+), 486 deletions(-) delete mode 100644 src/ArrayCanvas.cpp delete mode 100644 src/ArrayCanvas.hpp create mode 100644 src/Common.hpp delete mode 100644 src/Configuration.hpp create mode 100644 src/QDrawingArea.cpp create mode 100644 src/QDrawingArea.hpp create mode 100644 src/QWindow.cpp create mode 100644 src/QWindow.hpp delete mode 100644 src/QuantumMath.cpp delete mode 100644 src/QuantumMath.hpp diff --git a/README.md b/README.md index bc574f2..0f91e46 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,9 @@ cmake ../ make ./run -```` \ No newline at end of file +```` + +## List of errors + - cairo-surface.c:930: cairo_surface_reference: Assertion `CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)' failed + - (qpong.app:4590): GLib-CRITICAL **: 22:41:50.315: Source ID 120 was not found when attempting to remove it + - (qpong.app:27121): GdkPixbuf-CRITICAL **: 22:17:54.606: gdk_pixbuf_get_width: assertion 'GDK_IS_PIXBUF (pixbuf)' failed** Gdk:ERROR:../../../../gdk/gdkcairo.c:197:gdk_cairo_surface_paint_pixbuf: assertion failed: (cairo_image_surface_get_width (surface) == gdk_pixbuf_get_width (pixbuf)) Bail out! Gdk:ERROR:../../../../gdk/gdkcairo.c:197:gdk_cairo_surface_paint_pixbuf: assertion failed: cairo_image_surface_get_width (surface) == gdk_pixbuf_get_width (pixbuf)) \ No newline at end of file diff --git a/src/ArrayCanvas.cpp b/src/ArrayCanvas.cpp deleted file mode 100644 index 519894f..0000000 --- a/src/ArrayCanvas.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/// -/// @file ArrayCanvas.cpp -/// @author Armin Co -/// -/// @brief Implementation of the ArrayCanvas class. -/// - -#include "ArrayCanvas.hpp" - - -bool ArrayCanvas::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) -{ - // Clear background. - cr->save(); - cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); - cr->paint(); - - // Print arrays. - for (unsigned y = 0; y < k_drawingAreaHeight; y++) - { - for (unsigned x = 0; x < k_drawingAreaWidth; x++) - { - QColor c; - 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(xi, yi, imageScaleFactor, imageScaleFactor); - cr->fill(); - - // Print momentum - c = m_bufMomentumRepresentation->getValue(x, y); - cr->set_source_rgba(c.r, c.g, c.b, c.a); - 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 deleted file mode 100644 index 65a9c94..0000000 --- a/src/ArrayCanvas.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/// -/// @file ArrayCanvas.hpp -/// @author Armin Co -/// -/// @brief Class that draws two ParticleImages to a canvas. -/// - -#ifndef QPONG_DRAWING_AREA_HPP -#define QPONG_DRAWING_AREA_HPP - -#include <array> - -#include <gtkmm/drawingarea.h> -#include <glibmm.h> - -#include "ParticleImage.hpp" - - -/// @brief Class ArrayCanvas. Draws two ParticleImages when triggered. -/// -class ArrayCanvas : public Gtk::DrawingArea -{ - -public: - - /// @brief Class ArrayCanvas drawing area for two ParticleImages. - /// - 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() - { - m_dispatcher.emit(); - } - - -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_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. - /// - void queueDraw() - { - queue_draw(); - } -}; - -#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ed79de7..c23e90e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,9 @@ ## library for the app add_library(qpong - QuantumMath.cpp Particle.cpp ParticleImage.cpp - ArrayCanvas.cpp + QWindow.cpp + QDrawingArea.cpp ) ## create the application @@ -11,7 +11,7 @@ add_executable(qpong.app main.cpp ) -# we do need some threads +## we do need some threads find_package(Threads) ## get all GTKMM dependencies and configuration diff --git a/src/Common.hpp b/src/Common.hpp new file mode 100644 index 0000000..f1f947a --- /dev/null +++ b/src/Common.hpp @@ -0,0 +1,18 @@ +/// +/// @file Common.hpp +/// @author Armin Co +/// + +#ifndef QPONG_COMMON_HPP +#define QPONG_COMMON_HPP + +namespace QPong +{ + +constexpr int arrayWidth = 512; +constexpr int arrayHeight = 512; + +constexpr unsigned arraySize = arrayWidth * arrayHeight; + +} +#endif \ No newline at end of file diff --git a/src/Configuration.hpp b/src/Configuration.hpp deleted file mode 100644 index c44ef02..0000000 --- a/src/Configuration.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/// -/// @file Configuration.hpp -/// @author Armin Co -/// -/// @brief A collection of constants which configure the physical environment. -/// - -#ifndef QPONG_CONFIGURATION_HPP -#define QPONG_CONFIGURATION_HPP - -/// @brief Width of the drawing area in pixels. -/// -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.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.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 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 momentum on initialisation. -/// -constexpr double lambda = 80.0; - -/// @brief Scale the max value at initialisation of the momentum -/// -constexpr double A0 = 1.0; - -#endif \ No newline at end of file diff --git a/src/Particle.cpp b/src/Particle.cpp index 37670bb..e01b4c1 100644 --- a/src/Particle.cpp +++ b/src/Particle.cpp @@ -10,42 +10,125 @@ #include <gtk/gtk.h> +#include "Common.hpp" #include "Particle.hpp" -#include "Configuration.hpp" -const Complex Ci(0.0, 1.0); +/// @brief Planck constant. +/// +constexpr double hbar = 0.0001; + +const std::complex<double> Ci(0.0, 1.0); + +/// @brief Calc square of a number. +/// +template <typename T> +const T sqr(const T n) +{ + return n * n; +} + +const double xAt(int i) +{ + constexpr double dx = 2.0 / QPong::arrayWidth; + return i * dx - 1.0; +} + +const double yAt(int iy) +{ + constexpr double dy = 2.0 / QPong::arrayHeight; + return iy * dy - 1.0; +} + +const double pxAt(int ix) +{ + if (ix > QPong::arrayWidth / 2) + ix -= QPong::arrayWidth; + return ix * M_PI * hbar; +} + +const double pyAt(int iy) +{ + if (iy > QPong::arrayHeight / 2) + iy -= QPong::arrayHeight; + return iy * M_PI * hbar; +} + +const double obstaclePotential(double x, double y, double x0, double y0) +{ + y -= y0; + + constexpr double vx = 100000.0; + double E = 0.0; + constexpr double height = 0.31; + + if (y >= height) + { + double d = sqr(x-x0) + sqr(y-height); + E = 1.0 / (sqrt(d) * vx); + } + else if (y <= (-height)) + { + double d = sqr(x-x0) + sqr((-y)-height); + E = 1.0 / (sqrt(d) * vx); + } + else if ( x >= x0 && y < height && y>-height) + { + double d = x-x0; + E = 1.0 / (d * vx); + } + else if (x < x0 && y < height && y > -height) + { + double d = x0-x; + E = 1.0 / (d * vx); + } + + return E; +} void Particle::initMomentum() { std::cout << "Initialising particle" << std::endl; -#pragma omp parallel for - for (unsigned iy = 0; iy < ny; iy++) + for (unsigned iy = 0; iy < QPong::arrayHeight; iy++) { double y = yAt(iy); - for (unsigned ix = 0; ix < nx; ix++) + for (unsigned ix = 0; ix < QPong::arrayWidth; ix++) { double x = xAt(ix); - unsigned index = nx * iy + ix; - m_psi[index] = A0 * exp(Ci / hbar * (x * k_px0 + y * k_py0) - lambda * (square(x - k_x0) + square(y - k_y0))); + unsigned index = QPong::arrayWidth * iy + ix; + + constexpr double k_px0 = 0.025; + constexpr double k_py0 = 0.009; + constexpr double k_x0 = 0.0; + constexpr double k_y0 = -0.0; + constexpr double lambda = 70.0; + constexpr double A0 = 2.5; + m_psi[index] = A0 * exp(Ci / hbar * (x * k_px0 + y * k_py0) - lambda * (sqr(x - k_x0) + sqr(y - k_y0))); } } } -Particle::Particle(ArrayCanvas &ac, ParticleImage *momentum, ParticleImage *position, ParticleImage *obstacles) : m_arrayCanvas{ac},m_bufMomentumRepresentation{momentum}, m_bufPositionRepresentation{position}, m_obstacleImage{obstacles} +Particle::Particle(ParticleImage *momentum, ParticleImage *position, ParticleImage *obstacles) + : m_bufMomentumRepresentation{momentum} + , m_bufPositionRepresentation{position} + , m_obstacleImage{obstacles} + , m_ready{false} { - std::cout << "Creating particle" << std::endl; + m_players.insert(std::make_pair(Players::One, Player {-0.7, 0.0})); + m_players.insert(std::make_pair(Players::Two, Player { 0.7, 0.0})); + fftw_init_threads(); - constexpr int k_numThreads = 4; + constexpr int k_numThreads = 8; fftw_plan_with_nthreads(k_numThreads); - m_psi = static_cast<Complex *>(fftw_malloc(sizeof(fftw_complex) * n)); + m_psi = static_cast<std::complex<double> *>(fftw_malloc(sizeof(fftw_complex) * QPong::arraySize)); - 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); + m_planForward = fftw_plan_dft_2d(QPong::arrayHeight, QPong::arrayWidth, (fftw_complex *)m_psi, (fftw_complex *)m_psi, FFTW_FORWARD, FFTW_MEASURE); + m_planBackward = fftw_plan_dft_2d(QPong::arrayHeight, QPong::arrayWidth, (fftw_complex *)m_psi, (fftw_complex *)m_psi, FFTW_BACKWARD, FFTW_MEASURE); initMomentum(); + m_ready = true; } Particle::~Particle() @@ -55,49 +138,48 @@ Particle::~Particle() fftw_destroy_plan(m_planBackward); } +bool Particle::notReady() +{ + return !m_ready; +} + void Particle::propagate() { -#pragma omp parallel for - for (unsigned iy = 0; iy < ny; iy++) + constexpr double dt = 0.1; + for (unsigned iy = 0; iy < QPong::arrayHeight; iy++) { double y = yAt(iy); #pragma omp parallel for - - for (unsigned long ix = 0; ix < nx; ix++) + for (unsigned long ix = 0; ix < QPong::arrayWidth; ix++) { - 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]); + double E = obstaclePotential(x, y, m_players[Players::One].x, m_players[Players::One].y); + double E2 = obstaclePotential(x, y, m_players[Players::Two].x, m_players[Players::Two].y); + double E3 = sqr(y) * sqr(y) * sqr(y) * sqr(y) * 0.00012; + int index = QPong::arrayWidth * iy + ix; + std::complex<double> tmp = m_psi[index] * exp(-Ci * dt / hbar * (E + E2 + E3)); + m_psi[index] = tmp; + m_bufPositionRepresentation->updateBuffer(index, tmp, (E+E2+E3)*800); } } - fftw_execute (m_planForward); // transform into momentum repr. -#pragma omp parallel for - for (unsigned iy = 0; iy < ny; iy++) + for (unsigned iy = 0; iy < QPong::arrayHeight; iy++) { double py = pyAt(iy); #pragma omp parallel for - for (unsigned long ix = 0; ix < nx; ix++) + for (unsigned long ix = 0; ix < QPong::arrayWidth; ix++) { double px = pxAt(ix); - double E = (square(px) + square(py)) / (2.0 * m); - constexpr double N = 1.0 / (nx * ny); - int index = nx * iy + ix; + constexpr double m = 1.0; + double E = (sqr(px) + sqr(py)) / (2.0 * m); + constexpr double N = 1.0 / (QPong::arrayWidth * QPong::arrayHeight); + int index = QPong::arrayWidth * iy + ix; m_psi[index] *= exp(-Ci * dt / hbar * E) * N; } } updateMomentumImage(); //momentum fftw_execute(m_planBackward); // transform into position repr. - // updatePositionImage(); //position } @@ -107,47 +189,48 @@ void Particle::updateMomentumImage() unsigned py = 0; unsigned long momentum_index = 0; unsigned long array_index = 0; -#pragma omp parallel for - - for (unsigned y = 0; y < ny; y++) + for (unsigned y = 0; y < QPong::arrayHeight; y++) { - for (unsigned x = 0; x < nx; x++) + for (unsigned x = 0; x < QPong::arrayWidth; x++) { - if (y >= ny / 2) + if (y >= QPong::arrayHeight / 2) { - py = y - ny / 2; + py = y - QPong::arrayHeight / 2; } else { - py = y + ny / 2; + py = y + QPong::arrayHeight / 2; } - if (x >= nx / 2) + + if (x >= QPong::arrayWidth / 2) { - px = x - nx / 2; + px = x - QPong::arrayWidth / 2; } else { - px = x + nx / 2; + px = x + QPong::arrayWidth / 2; } - momentum_index = py * nx + px; - array_index = y * nx + x; - m_bufMomentumRepresentation->updateBuffer(array_index, (m_psi[momentum_index] * 142.42)); + constexpr double scale = 164.0; + array_index = y * QPong::arrayWidth + x; + momentum_index = py * QPong::arrayWidth + px; + m_bufMomentumRepresentation->updateBuffer(array_index, (m_psi[momentum_index] * scale), 0.0); } } } -void Particle::updatePositionImage() +void Particle::updateObstacle(Players player, Move direction) { -#pragma omp parallel for + constexpr double stepSize = 0.05; - for (unsigned y = 0; y < ny; y++) + switch (direction) { - for (unsigned x = 0; x < nx; x++) - { - unsigned index = y * nx + x; - m_bufPositionRepresentation->updateBuffer(index, m_psi[index]); - } + case Move::Up: + m_players[player].y -= stepSize; + break; + case Move::Down: + m_players[player].y += stepSize; + break; } -} +} \ No newline at end of file diff --git a/src/Particle.hpp b/src/Particle.hpp index a970d9a..327b4b5 100644 --- a/src/Particle.hpp +++ b/src/Particle.hpp @@ -8,33 +8,65 @@ #ifndef QPONG_PARTICLE_HPP #define QPONG_PARTICLE_HPP +#include <complex> +#include <map> + #include <math.h> #include <fftw3.h> -#include "ArrayCanvas.hpp" -#include "QuantumMath.hpp" #include "ParticleImage.hpp" -class ArrayCanvas; // Forward declaration +enum class Players +{ + One, + Two +}; + +enum class Move +{ + Up, + Down +}; + +struct Player +{ + double x; + double y; +}; class Particle { public: Particle() = delete; - Particle(ArrayCanvas &ac, ParticleImage *momentum, ParticleImage *position, ParticleImage *obstacles); + Particle(ParticleImage *momentum, ParticleImage *position, ParticleImage *obstacles); ~Particle(); + + /// @brief Execute one step of the simulation. + /// void propagate(); /// @brief Init the array with initial momentum. - /// Representation is in position representation. + /// Representation is in position representation. /// void initMomentum(); + + /// @brief Update obstacle position + /// @param player + /// @param direction + /// + void updateObstacle(Players player, Move direction); + + /// @brief Check if everything is initialised. + /// + bool notReady(); + private: - Complex *m_psi; - ArrayCanvas &m_arrayCanvas; + std::complex<double> *m_psi; fftw_plan m_planForward; fftw_plan m_planBackward; + bool m_ready; + std::map<Players, Player> m_players; ParticleImage *m_bufPositionRepresentation; ParticleImage *m_bufMomentumRepresentation; @@ -43,7 +75,6 @@ private: /// @brief Update the array at the ArrayCanvas /// void updateMomentumImage(); - void updatePositionImage(); }; #endif \ No newline at end of file diff --git a/src/ParticleImage.cpp b/src/ParticleImage.cpp index e2945ca..7fac7fa 100644 --- a/src/ParticleImage.cpp +++ b/src/ParticleImage.cpp @@ -3,28 +3,75 @@ /// @author Armin Co /// + #include "ParticleImage.hpp" +#include "Common.hpp" constexpr unsigned maxBufferCount = 2; -void ParticleImage::updateBuffer(unsigned index, Complex v) +ParticleImage::ParticleImage() { + m_obsbuf = new double[QPong::arraySize]; + m_pixbuf = new guint8[QPong::arraySize * 4]; + m_complexBuffer = new std::complex<double>[QPong::arraySize]; +} - m_buffer[index] = v; +void ParticleImage::updateBuffer(unsigned index, std::complex<double> v, double obs) +{ + m_obsbuf[index] = obs; + m_complexBuffer[index] = v; } -const QColor ParticleImage::getValue(const unsigned x, const unsigned y) +Glib::RefPtr<Gdk::Pixbuf> ParticleImage::getPixbuf() +{ + for (unsigned i = 0; i < QPong::arraySize * 4;) + { + QColor c = getValue(i/4); + m_pixbuf[i++] = static_cast<guint8>(c.r * 255); + m_pixbuf[i++] = static_cast<guint8>(c.g * 255); + m_pixbuf[i++] = static_cast<guint8>(c.b * 255); + m_pixbuf[i++] = static_cast<guint8>( 255); + } + + Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create_from_data(m_pixbuf, Gdk::Colorspace::COLORSPACE_RGB, true, 8, QPong::arrayWidth, QPong::arrayHeight, QPong::arrayWidth * 4); + pb = pb.get()->scale_simple(1024, 1024, Gdk::InterpType::INTERP_NEAREST); + return pb; +} + + +const QColor ParticleImage::getValue(unsigned index) { - unsigned long index = y * nx + x; - constexpr double scale_factor = 0.9; - Complex val = m_buffer[index]; + std::complex<double> val = m_complexBuffer[index]; QColor c; - val *= scale_factor; double r = real(val); double i = imag(val); - c.r = square(r); - c.g = square(r) + square(i); - c.b = square(i); + double obs = m_obsbuf[index]; + c.r = obs + r*r; + c.g = r*r + i*i; + // c.b = 0; + c.b = i*i; + + if (c.r > 1.0) + { + c.r = 1.0; + } + + if (c.g > 1.0) + { + c.g = 1.0; + } + + if (c.b > 1.0) + { + c.b = 1.0; + } return c; +} + +const QColor ParticleImage::getValue(const unsigned x, const unsigned y) +{ + unsigned index = y * QPong::arrayWidth + x; + + return getValue(index); } \ No newline at end of file diff --git a/src/ParticleImage.hpp b/src/ParticleImage.hpp index 240b19b..daec1dd 100644 --- a/src/ParticleImage.hpp +++ b/src/ParticleImage.hpp @@ -8,11 +8,13 @@ #ifndef QPONG_PARTICLE_IMAGE_HPP #define QPONG_PARTICLE_IMAGE_HPP +#include <complex> #include <array> -#include <cairomm/context.h> +#include <mutex> -#include "QuantumMath.hpp" +#include <gtkmm.h> +#include "Common.hpp" /// @brief Convinient conatiner to store a RGBA value. /// @@ -31,17 +33,23 @@ struct QColor class ParticleImage { public: - + ParticleImage(); + /// @return Get Color value from particle at position (x,y). /// const QColor getValue(const unsigned x, const unsigned y); + const QColor getValue(unsigned index); + + Glib::RefPtr<Gdk::Pixbuf> getPixbuf(); - void updateBuffer(unsigned index, Complex v); + void updateBuffer(unsigned index, std::complex<double> v, double obs); private: /// @brief Buffer for array with complex values from simulation. /// - std::array<Complex, n> m_buffer; + double *m_obsbuf; + std::complex<double> *m_complexBuffer; + guint8 *m_pixbuf; }; #endif \ No newline at end of file diff --git a/src/QDrawingArea.cpp b/src/QDrawingArea.cpp new file mode 100644 index 0000000..6a35845 --- /dev/null +++ b/src/QDrawingArea.cpp @@ -0,0 +1,40 @@ +/// +/// @file QDrawingArea.cpp +/// @author Armin Co +/// + +#include <thread> + +#include "QDrawingArea.hpp" + +QDrawingArea::QDrawingArea() +{ + m_dispatcher.connect(sigc::mem_fun(*this, &QDrawingArea::queue_draw)); +} + +void QDrawingArea::triggerRedraw() +{ + m_dispatcher.emit(); +} + + +void QDrawingArea::setPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf) +{ + // if ((pixbuf != nullptr) && (m_image != nullptr)) + m_image = pixbuf; +} + +bool QDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) +{ + cr->save(); + if (!m_image) + { + return true; + } + + Gdk::Cairo::set_source_pixbuf(cr, m_image, 0, 0); + cr->paint(); + cr->restore(); + return true; +} + diff --git a/src/QDrawingArea.hpp b/src/QDrawingArea.hpp new file mode 100644 index 0000000..d269dca --- /dev/null +++ b/src/QDrawingArea.hpp @@ -0,0 +1,29 @@ +/// +/// @file QDrawingArea.hpp +/// @author Armin Co +/// + +#ifndef QPONG_DRAWING_AREA_HPP +#define QPONG_DRAWING_AREA_HPP + +#include <gtkmm.h> +#include <gdkmm.h> + +class QDrawingArea : public Gtk::DrawingArea +{ +public: + QDrawingArea(); + virtual ~QDrawingArea(){}; + + void triggerRedraw(); + void setPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf); + +protected: + bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) override; + +private: + Glib::RefPtr<Gdk::Pixbuf> m_image; + Glib::Dispatcher m_dispatcher; +}; + +#endif \ No newline at end of file diff --git a/src/QWindow.cpp b/src/QWindow.cpp new file mode 100644 index 0000000..3036873 --- /dev/null +++ b/src/QWindow.cpp @@ -0,0 +1,160 @@ +#include <iostream> +#include <chrono> + +#include "QWindow.hpp" + +bool propagating = true; +int propRate = 0; +int renderRate = 0; + +/// @brief This functino has to run in its own thread and propagates the particle as fast as possible. +/// +void simulation(Particle *p) +{ + do + { + auto start = std::chrono::system_clock::now(); + p->propagate(); + propRate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start).count(); + } + while (propagating); +} + +/// @brief This function has to run in its own thread an will redraw the canvas every ~25ms. +/// +void render(QWindow *w) +{ + do + { + using namespace std::chrono_literals; + auto start = std::chrono::system_clock::now(); + std::this_thread::sleep_for(20ms); + w->emitImageDraw(); + renderRate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start).count(); + } while (true); +} + +QWindow::QWindow() + : m_button{"Initialise Particle"} + , m_label("Hi, you are playing QPong and this is a label!") +{ + set_border_width(10); + + m_momentum = new ParticleImage; + m_position = new ParticleImage; + m_particle = new Particle(m_momentum, m_position, m_obstacle); + + add(m_box); + 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_fpsBox.pack_start(m_renderRate, Gtk::PACK_EXPAND_WIDGET); + m_fpsBox.pack_start(m_proppergatingRate, Gtk::PACK_EXPAND_WIDGET); + m_vBox.pack_start(m_fpsBox, Gtk::PACK_SHRINK); + + + m_hBox.pack_start(m_positionArea); + m_hBox.pack_start(m_momentumArea); + m_vBox.pack_start(m_hBox); + + m_label.set_margin_top(5); + m_label.set_margin_bottom(5); + m_proppergatingRate.set_text("0"); + m_renderRate.set_text("0"); + m_renderRate.set_margin_top(5); + m_renderRate.set_margin_bottom(5); + m_proppergatingRate.set_margin_top(5); + m_proppergatingRate.set_margin_bottom(5); + + m_button.signal_clicked().connect(sigc::mem_fun(*this, &QWindow::onButtonPress)); + + this->signal_key_press_event().connect( sigc::mem_fun( *this, &QWindow::onKeyPress)); + + m_guiDispatcher.connect(sigc::mem_fun(*this, &QWindow::updateGui)); + m_areaDispatcher.connect(sigc::mem_fun(*this, &QWindow::updateImage)); + + while (m_particle->notReady()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + m_propagatingThread = std::thread(simulation, m_particle); + m_propagatingThread.detach(); + + std::thread rendering(render, this); + rendering.detach(); + + m_box.show_all(); +} + +unsigned guiUpdates = 0; +void QWindow::updateGui() +{ + guiUpdates++; + if (guiUpdates % 10 == 0) + { + std::stringstream ss; + ss << "image update rate: "; + ss << 1000 / renderRate; + m_renderRate.set_text(ss.str()); + + ss = std::stringstream(); + ss << "simulation rate: "; + ss << 1000 / propRate; + m_proppergatingRate.set_text(ss.str()); + } +} + + +bool QWindow::onKeyPress(GdkEventKey* event) +{ + constexpr int q = 113; + if (event->keyval == q) + { + m_particle->updateObstacle(Players::One, Move::Up); + } + constexpr int a = 97; + if (event->keyval == a) + { + m_particle->updateObstacle(Players::One, Move::Down); + } + constexpr int ue = 252; + if (event->keyval == ue) + { + m_particle->updateObstacle(Players::Two, Move::Up); + } + constexpr int oe = 246; + if (event->keyval == oe) + { + m_particle->updateObstacle(Players::Two, Move::Down); + } + return true; +} + +void QWindow::emitImageDraw() +{ + m_areaDispatcher.emit(); + m_guiDispatcher.emit(); +} + +void QWindow::updateImage() +{ + m_positionArea.setPixbuf(m_position->getPixbuf()); + m_momentumArea.setPixbuf(m_momentum->getPixbuf()); + m_positionArea.triggerRedraw(); + m_momentumArea.triggerRedraw(); +} + +void QWindow::onButtonPress() +{ + propagating = false; + std::this_thread::sleep_for((std::chrono::seconds)1); + m_particle->initMomentum(); + + m_propagatingThread = std::thread(simulation, m_particle); + + propagating = true; + m_propagatingThread.detach(); + +} \ No newline at end of file diff --git a/src/QWindow.hpp b/src/QWindow.hpp new file mode 100644 index 0000000..f8b8cdd --- /dev/null +++ b/src/QWindow.hpp @@ -0,0 +1,59 @@ +/// +/// @file QWindow.hpp +/// @author Armin Co +/// + +#ifndef Q_WINDOW_HPP +#define Q_WINDOW_HPP + +#include <thread> + +#include <gtkmm.h> + +#include "Particle.hpp" +#include "QDrawingArea.hpp" + +/// @brief Definition of the QPong window +/// +class QWindow : public Gtk::Window +{ +public: + QWindow(); + virtual ~QWindow(){} + + void updateImage(); + void updateGui(); + void emitImageDraw(); + +protected: + /// @brief signal handlers + /// + void onButtonPress(); + bool onKeyPress(GdkEventKey* event); + +private: + Gtk::Box m_box; + Gtk::VBox m_vBox; + Gtk::HBox m_hBox; + Gtk::Box m_fpsBox; + + Gtk::Label m_label; + Gtk::Button m_button; + + Gtk::Label m_renderRate; + Gtk::Label m_proppergatingRate; + + Glib::Dispatcher m_guiDispatcher; + Glib::Dispatcher m_areaDispatcher; + + QDrawingArea m_positionArea; + QDrawingArea m_momentumArea; + + Particle *m_particle; + std::thread m_propagatingThread; + ParticleImage *m_momentum; + ParticleImage *m_position; + ParticleImage *m_obstacle; +}; + +#endif \ No newline at end of file diff --git a/src/QuantumMath.cpp b/src/QuantumMath.cpp deleted file mode 100644 index 92161b1..0000000 --- a/src/QuantumMath.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/// -/// @file QuantumMath.cpp -/// @author Armin Co -/// -/// @brief Implementation of the QuantumMath functions. -/// - -#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; -} - -const double yAt(int iy) -{ - return (iy - (ny / 2)) * dy; -} - -const double pxAt(int ix) -{ - if (ix > nx / 2) - ix -= nx; - return ix * 2.0 * M_PI * hbar / (nx * dx); -} - -const double pyAt(int iy) -{ - if (iy > ny / 2) - iy -= ny; - return iy * 2.0 * M_PI * hbar / (ny * dy); -} diff --git a/src/QuantumMath.hpp b/src/QuantumMath.hpp deleted file mode 100644 index 258ab6c..0000000 --- a/src/QuantumMath.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/// -/// @file QunatumMath.hpp -/// @author Armin Co -/// -/// @brief Simple functions for math and other calculations. - -#ifndef QPONG_Q_MATH_HPP -#define QPONG_Q_MATH_HPP - -#include <complex> - -#include "Configuration.hpp" - -using Complex = std::complex<double>; - - -/// -/// @brief Calc square of a number. -/// -template <typename T> -const T square(const T n) -{ - return n * n; -} - -// Points in x direction. -const int nx = k_drawingAreaWidth; - -// Points in y direction. -const int ny = k_drawingAreaHeight; - -// Number of all points/pixels. -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. -/// -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 momentum representation. -/// -const double pxAt(int ix); - -const double pyAt(int iy); -#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d2a55e4..b6c669f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,122 +4,23 @@ /// /// @brief Application /// + #include <iostream> -#include <chrono> -#include <thread> #include <gtkmm.h> -#include "ArrayCanvas.hpp" -#include "Particle.hpp" - -#include "Configuration.hpp" - -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) -{ - do - { - p->propagate(); - } - while (propagating); -} - -/// @brief This function has to run in its own thread an will redraw the canvas every ~25ms. -/// -void render(ArrayCanvas *ac) -{ - do - { - using namespace std::chrono_literals; - std::this_thread::sleep_for(5ms); - ac->triggerRedraw(); - } while (true); -} - -class QWindow : public Gtk::Window -{ -public: - QWindow() : m_button{"Initialise Particle"} - { - 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_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(){} - - -protected: - // signal handlers: - void on_button_clicked() - { - propagating = false; - std::this_thread::sleep_for((std::chrono::seconds)1); - m_particle->initMomentum(); - m_propagatingThread = std::thread(simulation, m_particle); - propagating = true; - m_propagatingThread.detach(); - } - - // Member widgets: - Gtk::Box m_box; - Gtk::VBox m_vBox; - Gtk::Label m_label; - Gtk::Button m_button; - - ArrayCanvas *m_ac; - - // qpong - Particle *m_particle; - std::thread m_propagatingThread; - ParticleImage *m_momentum; - ParticleImage *m_position; - ParticleImage *m_obstacles; -}; +#include "QWindow.hpp" /// @brief MAIN /// int main(int argc, char* argv[]) { - std::cout << "Starting QPong" << std::endl; + std::cout << "QPong -- Starting" << std::endl; auto app = Gtk::Application::create(argc, argv, "de.armin-co.qpong"); - - std::cout << "Creating window" << std::endl; + QWindow window; - window.set_default_size(k_drawingAreaWidth*imageScaleFactor*2+130, k_drawingAreaWidth*imageScaleFactor+100); + window.set_default_size(1920, 1200); + std::cout << "QPong -- Window created!" << std::endl; return app->run(window); } -- GitLab