Skip to content
Snippets Groups Projects
Commit 4e67ad15 authored by Armin Co's avatar Armin Co
Browse files

removed src dir

parent a58c26a9
No related branches found
No related tags found
No related merge requests found
## CMake file for the Q-Pong game.
##
project(QuantumPong)
cmake_minimum_required(VERSION 3.2)
......@@ -12,16 +11,27 @@ option(release_type "Set the type of the releas (Debug/Release)." Release)
## Use modern C++!
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE ${release_type})
set(CMAKE_BUILD_TYPE Release)
find_package(PkgConfig REQUIRED)
message(" - Build type is set to ${CMAKE_BUILD_TYPE}")
## Add folders with app and tests
##
add_subdirectory(src)
# @todo Check versions of glib etc. librarys.
# On Ubuntu 19.10 there are warnings which are not present in Ubuntu 18.04
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
# Add qpong lib
add_subdirectory(qpong)
include_directories(qpong)
# Add dir with gtk qpong app
add_subdirectory(gtk_qpong_app)
# Add some unit tests
# not many yet ^^
if (test)
add_subdirectory(libs/googletest)
add_subdirectory(tests)
......
# library for the app should be GTK independent
add_library(qpong
Particle.cpp
ImageBuffer.cpp
QPlayer.cpp
Profiler.cpp
)
# @todo Check versions of glib etc. librarys.
# On Ubuntu 19.10 there are warnings which are not present in Ubuntu 18.04
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
# create the application
add_executable(qpong.app
main.cpp
QWindow.cpp
QDrawingArea.cpp
ParticleImage.cpp
)
# we do need some threads
find_package(Threads)
# get all GTKMM dependencies and configuration
pkg_check_modules(GTKMM gtkmm-3.0)
link_directories(${GTKMM_LIBRARY_DIRS})
include_directories(${GTKMM_INCLUDE_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
)
///
/// @file Common.hpp
/// @author Armin Co
///
#ifndef QPONG_COMMON_HPP
#define QPONG_COMMON_HPP
namespace QPong
{
constexpr int arrayWidth = 384;
constexpr int arrayHeight = 384;
constexpr int numberOfThreads = 8;
constexpr unsigned arraySize = arrayWidth * arrayHeight;
static_assert(arraySize % numberOfThreads == 0, "Number of threads and array size doesn't match!");
}
/// @brief Calc square of a number.
///
template <typename T>
const T sqr(const T n)
{
return n * n;
}
#endif
\ No newline at end of file
///
/// @file ImageBuffer.cpp
///
#include "Common.hpp"
#include "ImageBuffer.hpp"
ImageBuffer::ImageBuffer()
{
m_potbuf = new double[QPong::arraySize];
m_complexBuffer = new std::complex<double>[QPong::arraySize];
}
void ImageBuffer::updateAt(unsigned index, std::complex<double> v, double obs)
{
m_potbuf[index] = obs;
m_complexBuffer[index] = v;
}
const QColor ImageBuffer::getValue(unsigned index)
{
std::complex<double> val = m_complexBuffer[index];
double r = real(val);
double i = imag(val);
double obs = m_potbuf[index];
QColor c {
static_cast<float>(obs + r*r), //< red
static_cast<float>(r*r + i*i + obs/2), //< green
static_cast<float>(i*i) //< blue
};
constexpr double max = 0.83;
if (c.r > max)
{
c.r = max;
}
if (c.g > max)
{
c.g = max;
}
if (c.b > max)
{
c.b = max;
}
return c;
}
///
/// @file ImageBuffer.hpp
/// @author Armin Co
/// @brief An imagebuffer object stores an image as an array of color (RGB) values.
///
#ifndef QPONG_IMAGE_BUFFER_HPP
#define QPONG_OMAGE_BUFFER_HPP
#include <complex>
/// @brief Convinient conatiner to store a RGB value.
/// {r, g, b}
struct QColor
{
float r = 0;
float g = 0;
float b = 0;
};
/// @brief Class ImageBuffer
///
class ImageBuffer
{
public:
/// @brief Constructor for ImageBuffer.
///
ImageBuffer();
/// @brief Update the buffer at the given position.
/// @param index Index of the value that should be changed.
/// @param v The value as a complex number which will be interpreted as a color.
/// @param pot Potential at position.
///
void updateAt(unsigned index, std::complex<double> v, double pot);
/// @return Get color value from particle at array index.
///
const QColor getValue(unsigned index);
private:
double *m_potbuf;
std::complex<double> *m_complexBuffer;
};
#endif
///
/// @file Particle.cpp
/// @author Armin Co
///
/// @brief Particle class implemantation and main().
///
#include <iostream>
#include <thread>
#include <vector>
#include <gtk/gtk.h>
#include "Common.hpp"
#include "Particle.hpp"
#include "Profiler.hpp"
/// @brief Planck constant.
///
constexpr double hbar = 0.0002;
const std::complex<double> Ci(0.0, 1.0);
/// @brief Takes the row index of an array and turns it
/// into a position.
///
const double xAt(int x)
{
return 2.0 * x / QPong::arrayWidth - 1.0;
}
/// @brief Takes the line index of an array and turns it
/// into a position.
///
const double yAt(int y)
{
return 2.0 * y / QPong::arrayHeight - 1.0;
}
void Particle::initMomentum()
{
double sumAtInit = 0.0;
for (unsigned iy = 0; iy < QPong::arrayHeight; iy++)
{
double y = yAt(iy);
for (unsigned ix = 0; ix < QPong::arrayWidth; ix++)
{
double x = xAt(ix);
unsigned index = QPong::arrayWidth * iy + ix;
constexpr double k_px0 = 0.063;
constexpr double k_py0 = 0.042;
constexpr double k_x0 = 0.0;
constexpr double k_y0 = -0.0;
constexpr double lambda = 60.0;
constexpr double A0 = 0.5;
m_psi[index] = A0 * exp(Ci / hbar * (x * k_px0 + y * k_py0) - lambda * (sqr(x - k_x0) + sqr(y - k_y0)));
sumAtInit += sqr(real(m_psi[index])) + sqr(imag(m_psi[index]));
}
}
for (unsigned iy = 0; iy < QPong::arrayHeight; iy++)
{
double y = yAt(iy);
for (unsigned ix = 0; ix < QPong::arrayWidth; ix++)
{
double x = xAt(ix);
unsigned index = QPong::arrayWidth * iy + ix;
m_psi[index] *= 1.0 / sqrt(sumAtInit);
}
}
}
Particle::Particle(ImageBuffer *momentum, ImageBuffer *position, std::map<Player, std::shared_ptr<QPlayer>> players)
: m_bufMomentumRepresentation{momentum}
, m_bufPositionRepresentation{position}
, m_ready{false}
, m_players {players}
{
fftw_init_threads();
fftw_plan_with_nthreads(QPong::numberOfThreads);
m_psi = static_cast<std::complex<double> *>(fftw_malloc(sizeof(fftw_complex) * QPong::arraySize));
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()
{
fftw_free((fftw_complex *)m_psi);
fftw_destroy_plan(m_planForward);
fftw_destroy_plan(m_planBackward);
}
bool Particle::notReady()
{
return !m_ready;
}
double Particle::removeParticle(int index, double cx)
{
double e = cos(cx) * cos(cx);
double normPre = sqr(std::real(m_psi[index])) + sqr(std::real(m_psi[index]));
m_psi[index] *= e;
double dif = normPre - (sqr(std::real(m_psi[index])) + sqr(std::real(m_psi[index])));
return dif;
}
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 batPotential(double x, double y, Position pos)
{
y -= pos.y;
double E = 0.0;
constexpr double scale = 0.000015;
constexpr double height = 0.31;
constexpr double cutoff = 0.0015;
if (y >= height)
{
double d = sqr(x-pos.x) + sqr(y-height) + cutoff;
E = 1.0 / d * scale;
}
else if (y <= (-height))
{
double d = sqr(x-pos.x) + sqr((-y)-height) + cutoff;
E = 1.0 / d * scale;
}
else
{
double d = sqr(pos.x-x) + cutoff;
E = 1.0 / d * scale;
}
return E;
}
constexpr double dt = 0.15;
void Particle::manipulateParticleInPosition(int indexFrom, int indexTo)
{
auto pOneX = m_players[Player::One]->getPosition().x;
auto pTwoX = m_players[Player::Two]->getPosition().x;
float sumScorePlayerOne = 0.0;
float sumScorePlayerTwo = 0.0;
for (int index = indexFrom; index < indexTo; index++)
{
double x = xAt(index % QPong::arrayWidth);
double y = yAt(index / QPong::arrayWidth);
if (x < pOneX) //< expects p1.x < 0
{
double cx = -pOneX + x;
cx = cx * (M_PI / 2) / (1.0 + pOneX);
double dif = removeParticle(index, cx);
sumScorePlayerTwo += dif;
}
if (x > pTwoX) //< expects p2.x > 0
{
double cx = x - pTwoX;
cx = cx * (M_PI / 2) / (1.0 - pTwoX);
double dif = removeParticle(index, cx);
sumScorePlayerOne += dif;
}
double playerOneBat = batPotential(x, y, m_players[Player::One]->getPosition());
double playerTwoBat = batPotential(x, y, m_players[Player::Two]->getPosition());
double horizontalBorders = sqr(y) * sqr(y) * sqr(y) * sqr(y) * 0.01;
std::complex<double> tmp = m_psi[index] * exp(-Ci * dt / hbar * (playerOneBat + playerTwoBat + horizontalBorders));
m_psi[index] = tmp;
m_bufPositionRepresentation->updateAt(index, tmp * 125.0, (playerOneBat + playerTwoBat + horizontalBorders) * 150);
}
m_players[Player::One]->addScore(sumScorePlayerOne);
m_players[Player::Two]->addScore(sumScorePlayerTwo);
}
double flattenMomentum(double x, double y)
{
double distanceToCenter = sqr(x) + sqr(y);
constexpr double offset = 0.015;
if (distanceToCenter < offset)
{
return 1.0;
}
else
{
return 1.0 / (1.0 + (distanceToCenter * 2.0));
}
}
void Particle::moveStep(int indexFrom, int indexTo)
{
for (int i = indexFrom; i < indexTo; i++)
{
double py = pyAt(i / QPong::arrayWidth);
double px = pxAt(i % QPong::arrayWidth);
constexpr double m = 1.0;
constexpr double N = 1.0 / QPong::arraySize;
double E = (sqr(px) + sqr(py)) / (2.0 * m);
m_psi[i] *= exp(-Ci * dt / hbar * E) * N * flattenMomentum(px, py);
}
}
void Particle::propagate()
{
for (auto &player : m_players)
{
player.second->update(dt);
}
std::vector<std::thread> positionThreads;
for (int i = 0; i < QPong::numberOfThreads; i++)
{
int from = i * (QPong::arraySize/QPong::numberOfThreads);
int to = (i+1) * (QPong::arraySize/QPong::numberOfThreads);
positionThreads.emplace_back(&Particle::manipulateParticleInPosition, this, i * (QPong::arraySize/QPong::numberOfThreads), (i+1) * (QPong::arraySize/QPong::numberOfThreads));
}
for (auto &thread : positionThreads)
{
thread.join();
}
fftw_execute (m_planForward); // transform into momentum repr.
std::vector<std::thread> momentumThreads;
for (int i = 0; i < QPong::numberOfThreads; i++)
{
momentumThreads.emplace_back(&Particle::moveStep, this, i * (QPong::arraySize / QPong::numberOfThreads), (i + 1) * (QPong::arraySize / QPong::numberOfThreads));
}
for (auto &thread : momentumThreads)
{
thread.join();
}
/// @todo Pull update Momentum image into moveStep()
updateMomentumImage(); //momentum
fftw_execute(m_planBackward); // transform into position repr.
}
void Particle::updateMomentumImage()
{
for (int index = 0; index < QPong::arraySize; index++)
{
auto y = index / QPong::arrayWidth;
unsigned py = 0;
if (y >= QPong::arrayHeight / 2)
{
py = y - QPong::arrayHeight / 2;
}
else
{
py = y + QPong::arrayHeight / 2;
}
auto x = index % QPong::arrayWidth;
unsigned px = 0;
if (x >= QPong::arrayWidth / 2)
{
px = x - QPong::arrayWidth / 2;
}
else
{
px = x + QPong::arrayWidth / 2;
}
constexpr double scale = 55580.0;;
auto array_index = y * QPong::arrayWidth + x;
auto momentum_index = py * QPong::arrayWidth + px;
m_bufMomentumRepresentation->updateAt(array_index, (m_psi[momentum_index] * scale), 0.0);
}
}
///
/// @file Particle.hpp
/// @author Armin Co
///
/// @brief Particle class header.
///
#ifndef QPONG_PARTICLE_HPP
#define QPONG_PARTICLE_HPP
#include <complex>
#include <memory>
#include <map>
#include <math.h>
#include <fftw3.h>
#include "ParticleImage.hpp"
#include "QPlayer.hpp"
class Particle
{
public:
/// @brief Contructor
///
Particle(ImageBuffer *momentum, ImageBuffer *position, std::map<Player, std::shared_ptr<QPlayer>> players);
/// @brief Destructor
///
~Particle();
/// @brief Execute one step of the simulation.
///
void propagate();
/// @brief Init the array with initial momentum.
/// Representation is in position representation.
///
void initMomentum();
/// @brief Check if everything is initialised.
///
bool notReady();
private:
std::complex<double> *m_psi;
fftw_plan m_planForward;
fftw_plan m_planBackward;
bool m_ready;
std::map<Player, std::shared_ptr<QPlayer>> m_players;
ImageBuffer *m_bufPositionRepresentation;
ImageBuffer *m_bufMomentumRepresentation;
/// @brief Update the array at the ArrayCanvas
///
void updateMomentumImage();
/// @brief Manipulate particle in position representation
/// For bats and borders potentials are used.
/// Removing the particle behind the bat with a cos2
///
void manipulateParticleInPosition(int from, int to);
/// @brief Move the particle one step forward in momentum representation
///
void moveStep(int indexFrom, int indexTo);
/// @brief Remove parts of the particle that are behind the bat.
///
double removeParticle(int index, double cx);
Particle() = delete;
};
#endif
\ No newline at end of file
///
/// @file ParticleImage.cpp
/// @author Armin Co
///
#include "ParticleImage.hpp"
#include "Common.hpp"
#include "Profiler.hpp"
constexpr int bytesPerPixel = 3;
constexpr int bitsPerSample = 8;
ParticleImage::ParticleImage()
: ImageBuffer()
{
m_pixbuf = new uChar[QPong::arraySize * bytesPerPixel];
}
Glib::RefPtr<Gdk::Pixbuf> ParticleImage::getPixbuf()
{
for (unsigned i = 0; i < QPong::arraySize * bytesPerPixel;)
{
QColor c = getValue(i/bytesPerPixel);
m_pixbuf[i++] = static_cast<uChar>(c.r * 255);
m_pixbuf[i++] = static_cast<uChar>(c.g * 255);
m_pixbuf[i++] = static_cast<uChar>(c.b * 255);
}
Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create_from_data(m_pixbuf, Gdk::Colorspace::COLORSPACE_RGB, false, bitsPerSample, QPong::arrayWidth, QPong::arrayHeight, QPong::arrayWidth * bytesPerPixel);
pb = pb->scale_simple(768, 768, Gdk::InterpType::INTERP_NEAREST);
return pb;
}
\ No newline at end of file
///
/// @file ParticleImage.hpp
/// @author Armin Co
///
/// @brief Class to wrap one dimensional array.
///
#ifndef QPONG_PARTICLE_IMAGE_HPP
#define QPONG_PARTICLE_IMAGE_HPP
#include <gtkmm.h>
#include "ImageBuffer.hpp"
/// @brief Wraps an one dimensional array.
/// Values can be accessed as in 2D.
///
class ParticleImage : public ImageBuffer
{
public:
ParticleImage();
Glib::RefPtr<Gdk::Pixbuf> getPixbuf();
private:
using uChar = unsigned char;
uChar* m_pixbuf;
};
#endif
\ No newline at end of file
///
/// @file Profiler.cpp
/// @author Armin Co
///
/// @brief
#include "Profiler.hpp"
Profiler::Profiler(const char* name)
: m_name {name}
, m_stopped {false}
, m_verbose {Verbose::False}
{
m_startTimepoint = std::chrono::high_resolution_clock::now();
}
Profiler::~Profiler()
{
if (!m_stopped)
{
stop();
}
}
long Profiler::stop()
{
auto endTimepoint = std::chrono::high_resolution_clock::now();
m_stopped = true;
long mus = std::chrono::duration_cast<std::chrono::nanoseconds>(endTimepoint - m_startTimepoint).count();
if (m_verbose == Verbose::True)
{
std::cout << m_name << ": " << mus / 1000.0 / 1000.0<< "ms" << std::endl;
}
return mus;
}
\ No newline at end of file
///
/// @file Profiler.hpp
/// @author Armin Co
///
/// @brief Class to do some basic timing and profiling.
///
#ifndef QPONG_PROFILER_HPP
#define QPONG_PROFILER_HPP
#include <chrono>
#include <iostream>
enum class Verbose
{
True,
False
};
class Profiler
{
public:
Profiler(const char* name);
~Profiler();
long stop();
private:
const char* m_name;
Verbose m_verbose;
std::chrono::time_point<std::chrono::system_clock> m_startTimepoint;
bool m_stopped;
};
#endif
\ No newline at end of file
///
/// @file QDrawingArea.cpp
/// @author Armin Co
///
#include "QDrawingArea.hpp"
QDrawingArea::QDrawingArea()
{
m_dispatcher.connect(sigc::mem_fun(*this, &QDrawingArea::queue_draw));
}
void QDrawingArea::redraw()
{
m_dispatcher.emit();
}
void QDrawingArea::setPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
{
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;
}
///
/// @file QDrawingArea.hpp
/// @author Armin Co
///
/// @brief QDrawingArea is a class which can show a Gdk::Pixbuf.
///
#ifndef QPONG_DRAWING_AREA_HPP
#define QPONG_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <gdkmm.h>
/// @brief QDrawingArea is derived from Gtk::DrawingArea.
/// An object of this class stores a reference to Gdk::Pixbuf which can be shown and updated.
///
class QDrawingArea : public Gtk::DrawingArea
{
public:
QDrawingArea();
virtual ~QDrawingArea(){};
/// @brief Request a redraw of the drawing area.
///
void redraw();
/// @brief Set a new pixbuf reference.
///
void setPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf);
protected:
/// @brief Signal handler which is called when a redraw of the drawing area is requested.
///
bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) override;
private:
Glib::RefPtr<Gdk::Pixbuf> m_image; ///< Reference a pixbuf.
Glib::Dispatcher m_dispatcher; ///< Dispatcher is used to trigger thread safe redraws.
};
#endif
\ No newline at end of file
///
/// @file QPlayer.cpp
/// @author Armin Co
///
#include "QPlayer.hpp"
#include <mutex>
constexpr double speedStepSize = 0.0005;
QPlayer::QPlayer(Player pl, Position pos)
: m_id {pl}
, m_x {pos.x}
, m_y {pos.y}
{
}
void QPlayer::update(double dt)
{
constexpr double negativeAccelScale = 1.5;
m_y += (m_vy * dt * 4);
if (m_vy > 0.0)
{
m_vy -= speedStepSize * negativeAccelScale;
}
if (m_vy < 0.0)
{
m_vy += speedStepSize * negativeAccelScale;
}
if (m_vy < speedStepSize*negativeAccelScale && m_vy > -speedStepSize * negativeAccelScale)
{
m_vy = 0.0;
}
constexpr double maxY = 1.3;
if (m_y > maxY)
{
m_y = maxY;
}
if (m_y < -maxY)
{
m_y = -maxY;
}
}
void QPlayer::move(Direction direction)
{
constexpr double addSpped = 3;
switch (direction)
{
case Direction::Up:
m_vy -= speedStepSize * addSpped;
break;
case Direction::Down:
m_vy += speedStepSize * addSpped;
break;
}
constexpr double maxSpeedScale = 45;
if (m_vy > speedStepSize * maxSpeedScale)
{
m_vy = speedStepSize * maxSpeedScale;
}
if (m_vy < -speedStepSize * maxSpeedScale)
{
m_vy = -speedStepSize * maxSpeedScale;
}
}
float QPlayer::getScore()
{
return m_score * 100;
}
std::mutex mtx;
void QPlayer::addScore(float score)
{
std::lock_guard<std::mutex> lockScore(mtx);
m_score += score;
}
Position QPlayer::getPosition()
{
return {m_x, m_y};
}
void QPlayer::reset()
{
m_score = 0;
}
\ No newline at end of file
///
/// @file Qlayer.hpp
/// @author Armin Co
///
/// @brief Player class.
///
#ifndef QPONG_QPLAYER_HPP
#define QPONG_QPLAYER_HPP
/// @brief Available players
///
enum class Player
{
One,
Two
};
/// @brief The bat can be moved up and down.
///
enum class Direction
{
Up,
Down
};
struct Position
{
double x;
double y;
};
class QPlayer
{
public:
QPlayer(Player pl, Position pos);
void update(double dt);
void move(Direction d);
float getScore();
void addScore(float score);
void reset();
Position getPosition();
private:
Player m_id;
double m_x;
double m_y;
double m_vx = 0;
double m_vy = 0;
double m_score = 0;
};
#endif
\ No newline at end of file
#include <iostream>
#include <iomanip>
#include <chrono>
#include "QWindow.hpp"
#include "Profiler.hpp"
bool propagating = true;
int propRate = 0;
int renderRate = 0;
bool qPressed = false;
bool aPressed = false;
bool upPressed = false;
bool downPressed = false;
/// @brief This functino has to run in its own thread and propagates the particle as fast as possible.
///
void QWindow::simulation()
{
do
{
auto start = std::chrono::system_clock::now();
m_particle->propagate();
propRate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start).count();
if (qPressed)
m_players[Player::One]->move(Direction::Up);
if (aPressed)
m_players[Player::One]->move(Direction::Down);
if (upPressed)
m_players[Player::Two]->move(Direction::Up);
if (downPressed)
m_players[Player::Two]->move(Direction::Down);
// Limit the speed of the simulation.
auto dt = std::chrono::system_clock::now() - start;
auto dtMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dt).count();
constexpr int propTime = 6;
if (dtMillis < propTime)
{
std::this_thread::sleep_for(std::chrono::milliseconds(propTime - dtMillis));
}
}
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->updateView();
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!")
, m_scorePlayerOne("0")
, m_scorePlayerTwo("0")
{
set_border_width(5);
constexpr double playerOneStartX = -0.7;
constexpr double playerTwoStartX = 0.7;
m_players.insert(std::make_pair<Player, std::shared_ptr<QPlayer>>(Player::One, std::make_shared<QPlayer>(Player::One, Position{-0.7, 0})));
m_players.insert(std::make_pair<Player, std::shared_ptr<QPlayer>>(Player::Two, std::make_shared<QPlayer>(Player::Two, Position{ 0.7, 0.4})));
m_momentum = new ParticleImage;
m_position = new ParticleImage;
m_particle = new Particle(m_momentum, m_position, m_players);
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_scoreBox.pack_start(m_scorePlayerOne, Gtk::PACK_EXPAND_WIDGET);
m_scoreBox.pack_start(m_scorePlayerTwo, Gtk::PACK_EXPAND_WIDGET);
m_vBox.pack_start(m_scoreBox, Gtk::PACK_SHRINK);
m_hBox.pack_start(m_positionArea, Gtk::PACK_EXPAND_WIDGET);
m_hBox.pack_start(m_momentumArea, Gtk::PACK_EXPAND_WIDGET);
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));
this->signal_key_release_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(&QWindow::simulation, this);
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());
ss = std::stringstream();
ss << "Score Player 1: ";
auto score = m_players[Player::One]->getScore();
ss << std::setprecision(2) << std::setw(7) << score << "%";
if (score > 50.0)
{
ss << std::endl << "GEWONNEN!";
ss << std::endl << "Zum Neustarten Partikel neu initialisieren";
}
m_scorePlayerOne.set_text(ss.str());
ss = std::stringstream();
ss << "Score Player 2: ";
score = m_players[Player::Two]->getScore();
ss << std::setprecision(2) << std::setw(7) << score << "%";
if (score > 50.0)
{
ss << std::endl << "GEWONNEN!";
ss << std::endl << "Zum Neustarten Partikel neu initialisieren";
}
m_scorePlayerTwo.set_text(ss.str());
}
}
bool QWindow::onKeyPress(GdkEventKey* event)
{
constexpr int q = 113;
constexpr int a = 97;
constexpr int ue = 252;
constexpr int oe = 246;
if (event->type == GdkEventType::GDK_KEY_PRESS)
{
if (event->keyval == q)
qPressed = true;
if (event->keyval == a)
aPressed = true;
if (event->keyval == ue)
upPressed = true;
if (event->keyval == oe)
downPressed = true;
}
else if (event->type == GdkEventType::GDK_KEY_RELEASE)
{
if (event->keyval == q)
qPressed = false;
if (event->keyval == a)
aPressed = false;
if (event->keyval == ue)
upPressed = false;
if (event->keyval == oe)
downPressed = false;
}
return true;
}
void QWindow::updateView()
{
m_areaDispatcher.emit();
m_guiDispatcher.emit();
}
void QWindow::updateImage()
{
m_positionArea.setPixbuf(m_position->getPixbuf());
m_momentumArea.setPixbuf(m_momentum->getPixbuf());
m_positionArea.redraw();
m_momentumArea.redraw();
}
void QWindow::onButtonPress()
{
propagating = false;
std::this_thread::sleep_for((std::chrono::seconds)1);
m_particle->initMomentum();
for (auto player : m_players)
{
player.second->reset();
}
m_propagatingThread = std::thread(&QWindow::simulation, this);
propagating = true;
m_propagatingThread.detach();
}
\ No newline at end of file
///
/// @file QWindow.hpp
/// @author Armin Co
///
#ifndef Q_WINDOW_HPP
#define Q_WINDOW_HPP
#include <thread>
#include <map>
#include <memory>
#include <gtkmm.h>
#include "Particle.hpp"
#include "QDrawingArea.hpp"
/// @brief Definition of the QPong window
///
class QWindow : public Gtk::Window
{
public:
/// @brief Constructor of the window
///
QWindow();
/// @brief Virtual destructor of the window.
///
virtual ~QWindow(){}
/// @brief Sends a signal to GTK to update drawing areas.
void updateView();
protected:
/// @brief signal handlers
///
void onButtonPress();
bool onKeyPress(GdkEventKey* event);
private:
/// @brief Updates the QDrawingArea for position and momentum.
///
void updateImage();
/// @brief Update all labels from the gui.
///
void updateGui();
/// @brief Loop throug iteratins of the simulation
///
void simulation();
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;
Gtk::Box m_scoreBox;
Gtk::Label m_scorePlayerOne;
Gtk::Label m_scorePlayerTwo;
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;
std::map<Player, std::shared_ptr<QPlayer>> m_players;
};
#endif
\ No newline at end of file
///
/// @file main.cpp
/// @author Armin Co
///
/// @brief The QPong game.
///
#include <iostream>
#include <gtkmm.h>
#include "QWindow.hpp"
/// @brief MAIN
///
int main(int argc, char* argv[])
{
std::cout << "QPong -- starting" << std::endl;
auto app = Gtk::Application::create(argc, argv, "de.armin-co.qpong");
QWindow window;
window.set_default_size(1920, 1080);
return app->run(window);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment