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