added energy overlay

main
Benjamin Kraft 1 year ago
parent a99effbeb6
commit 8d1c374b0f
  1. 0
      shaders/fragment_pendula.glsl
  2. 11
      shaders/fragment_quad.glsl
  3. 6
      shaders/shaders.qrc
  4. 9
      shaders/vertex_pendula.glsl
  5. 11
      shaders/vertex_quad.glsl
  6. 83
      src/GLWidget.cpp
  7. 12
      src/GLWidget.h
  8. 2
      src/MainWindow.cpp
  9. 177
      src/Overlay.cpp
  10. 29
      src/Overlay.h
  11. 23
      src/Pendulum.cpp
  12. 2
      src/Pendulum.h
  13. 43
      src/Simulation.cpp
  14. 15
      src/Simulation.h
  15. 4
      src/Vector.cpp
  16. 1
      src/Vector.h
  17. 1
      src/main.cpp

@ -0,0 +1,11 @@
#version 330 core
out vec4 FragColor;
in vec2 texCoord;
uniform sampler2D quadTexture;
void main() {
FragColor = texture(quadTexture, texCoord);
}

@ -1,6 +1,8 @@
<RCC> <RCC>
<qresource prefix="/shaders/"> <qresource prefix="/shaders/">
<file>vertex.glsl</file> <file>vertex_pendula.glsl</file>
<file>fragment.glsl</file> <file>fragment_pendula.glsl</file>
<file>vertex_quad.glsl</file>
<file>fragment_quad.glsl</file>
</qresource> </qresource>
</RCC> </RCC>

@ -7,8 +7,8 @@ layout (location = 2) in float vMassRadius;
uniform mat3 VP; uniform mat3 VP;
uniform bool drawPoints; uniform bool drawPoints;
uniform float screenSizePixels; uniform int simulationSizePixels;
uniform float screenSizeMeters; uniform float simulationSizeMeters;
uniform float depthOffset; uniform float depthOffset;
out vec3 color; out vec3 color;
@ -16,8 +16,11 @@ out vec3 color;
void main() { void main() {
gl_Position = vec4(VP * vPos, 1.0); gl_Position = vec4(VP * vPos, 1.0);
if (drawPoints){ if (drawPoints){
// Points always on top of neighbouring lines
gl_Position.z -= depthOffset * 0.5; gl_Position.z -= depthOffset * 0.5;
gl_PointSize = vMassRadius / screenSizeMeters * screenSizePixels * 2;
// PointSize is diameter in Pixels
gl_PointSize = vMassRadius / simulationSizeMeters * simulationSizePixels * 2;
} else { } else {
color = vColor; color = vColor;
} }

@ -0,0 +1,11 @@
#version 330 core
layout (location = 0) in vec2 vPos;
layout (location = 1) in vec2 vTex;
out vec2 texCoord;
void main() {
gl_Position = vec4(vPos, 0, 1);
texCoord = vTex;
}

@ -6,24 +6,25 @@
#include "Simulation.h" #include "Simulation.h"
#include <QApplication> #include <QApplication>
#include <QDialog> #include <QDialog>
#include "FPS.h" #include "Overlay.h"
#include <QOpenGLShaderProgram> #include <QOpenGLShaderProgram>
#include "FPS.h"
GLWidget::GLWidget(Simulation * simulation) : simulation(simulation) { GLWidget::GLWidget(Simulation * simulation) : simulation(simulation) {
startTimer(1000 / 144); startTimer(1000 / 60);
fps = new FPS; overlay = new Overlay(simulation);
fps->setUpdateInterval(500);
connect(simulation, &Simulation::layoutChanged, this, &GLWidget::uploadStaticDataToGPU); connect(simulation, &Simulation::layoutChanged, this, &GLWidget::uploadStaticDataToGPU);
connect(simulation, &Simulation::layoutChanged, overlay, &Overlay::fetchEnergyLimit);
connect(simulation, &Simulation::positionChanged, this, &GLWidget::changePosition); connect(simulation, &Simulation::positionChanged, this, &GLWidget::changePosition);
} }
void GLWidget::initializeGL() { void GLWidget::initializeGL() {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
program = new QOpenGLShaderProgram; pendulaProgram = new QOpenGLShaderProgram;
program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex.glsl"); pendulaProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex_pendula.glsl");
program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment.glsl"); pendulaProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment_pendula.glsl");
program->link(); pendulaProgram->link();
glGenVertexArrays(1, &VAO); glGenVertexArrays(1, &VAO);
glGenBuffers(1, &positionVBO); glGenBuffers(1, &positionVBO);
@ -35,57 +36,53 @@ void GLWidget::initializeGL() {
glEnable(GL_PROGRAM_POINT_SIZE); glEnable(GL_PROGRAM_POINT_SIZE);
glEnable(GL_PRIMITIVE_RESTART); glEnable(GL_PRIMITIVE_RESTART);
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glEnable(GL_DEPTH_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS); glDepthFunc(GL_LESS);
glClearDepth(1); glClearDepth(1);
glClearColor(.15, .15, .15, 1); glClearColor(.15, .15, .15, 1);
std::vector<Pendulum*> empty; uploadStaticDataToGPU();
uploadStaticDataToGPU(&empty);
overlay->init();
} }
void GLWidget::paintGL() { void GLWidget::paintGL() {
// Native OpenGL glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program->bind(); // Content
program->setUniformValue("VP", VP); {
glViewport(0, 0, width(), height());
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
pendulaProgram->bind();
pendulaProgram->setUniformValue("VP", VP);
glBindVertexArray(VAO); glBindVertexArray(VAO);
// Lines // Lines
{ {
program->setUniformValue("drawPoints", false); pendulaProgram->setUniformValue("drawPoints", false);
glDrawElements(GL_LINE_STRIP, indexCount, GL_UNSIGNED_INT, nullptr); glDrawElements(GL_LINE_STRIP, indexCount, GL_UNSIGNED_INT, nullptr);
} }
// Mass Circles // Mass Circles
if (showMasses) { if (showMasses) {
program->setUniformValue("drawPoints", true); pendulaProgram->setUniformValue("drawPoints", true);
program->setUniformValue("depthOffset", depthOffset); pendulaProgram->setUniformValue("depthOffset", depthOffset);
program->setUniformValue("screenSizePixels", screenSizePixels * 0.9f); pendulaProgram->setUniformValue("simulationSizePixels", simulationSizePixels);
program->setUniformValue("screenSizeMeters", float(simulation->sizeMeters)); pendulaProgram->setUniformValue("simulationSizeMeters", float(simulation->sizeMeters));
glDrawElements(GL_POINTS, indexCount, GL_UNSIGNED_INT, nullptr); glDrawElements(GL_POINTS, indexCount, GL_UNSIGNED_INT, nullptr);
} }
glBindVertexArray(0); glBindVertexArray(0);
} }
QPixmap img(size()); // Overlay
auto p = new QPainter(&img);
// FPS
{ {
p->setPen(Qt::white); glDisable(GL_DEPTH_TEST);
auto font = p->font(); glEnable(GL_BLEND);
font.setPixelSize(20); overlay->draw();
p->setFont(font);
fps->newFrame();
QString fpsString = "FPS: " + QString::fromStdString(std::to_string(fps->current));
p->drawText(0, 0, 100, 100, Qt::AlignTop | Qt::AlignLeft, fpsString);
} }
p->end();
} }
void GLWidget::timerEvent(QTimerEvent *e) { void GLWidget::timerEvent(QTimerEvent *e) {
@ -101,7 +98,8 @@ bool GLWidget::AnyDialogOpen() {
return false; return false;
} }
void GLWidget::uploadStaticDataToGPU(const std::vector<Pendulum *> *pendula) { void GLWidget::uploadStaticDataToGPU() {
auto pendula = &simulation->pendula;
int pointCount = std::transform_reduce(pendula->begin(), pendula->end(), 0, [](int prev, int curr){ int pointCount = std::transform_reduce(pendula->begin(), pendula->end(), 0, [](int prev, int curr){
return prev + curr + 1; return prev + curr + 1;
@ -185,11 +183,11 @@ void GLWidget::uploadStaticDataToGPU(const std::vector<Pendulum *> *pendula) {
simulation->pendulaMutex.unlock(); simulation->pendulaMutex.unlock();
} }
void GLWidget::changePosition(const std::vector<Pendulum *> *pendula) { void GLWidget::changePosition() {
glBindBuffer(GL_ARRAY_BUFFER, positionVBO); glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
auto positions = (GLfloat*) glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(positionCount * sizeof(float)), GL_MAP_WRITE_BIT); auto positions = (GLfloat*) glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(positionCount * sizeof(float)), GL_MAP_WRITE_BIT);
size_t index = 0; size_t index = 0;
for (const auto p : *pendula){ for (const auto p : simulation->pendula){
index += 3; index += 3;
for (auto x : p->X){ for (auto x : p->X){
positions[index++] = float(x.x); positions[index++] = float(x.x);
@ -202,16 +200,15 @@ void GLWidget::changePosition(const std::vector<Pendulum *> *pendula) {
} }
void GLWidget::resizeGL(int w, int h) { void GLWidget::resizeGL(int w, int h) {
screenSizePixels = std::min(float(w), float(h)); simulationSizePixels = std::min(w, h) - 100;
float scale = screenSizePixels / float(simulation->sizeMeters) * 2 * 0.9f; float scale = float(simulationSizePixels) / float(simulation->sizeMeters) * 2;
VP.setToIdentity(); VP.setToIdentity();
VP(0, 0) = 1 / float(w); VP(0, 0) = scale / float(w);
VP(1, 1) = -1 / float(h); VP(1, 1) = scale / float(h);
VP *= scale;
VP(2, 2) = 1;
glViewport(0, 0, w, h); int s = simulationSizePixels;
overlay->resize(QSize(w, h), QRect((w - s) / 2, (h - s) / 2, s, s));
} }
void GLWidget::showMassesChanged(int state) { void GLWidget::showMassesChanged(int state) {

@ -6,8 +6,8 @@
class Pendulum; class Pendulum;
class Simulation; class Simulation;
class FPS;
class QOpenGLShaderProgram; class QOpenGLShaderProgram;
class Overlay;
class GLWidget : public QOpenGLWidget, protected QOpenGLExtraFunctions { class GLWidget : public QOpenGLWidget, protected QOpenGLExtraFunctions {
public: public:
@ -20,10 +20,10 @@ protected:
public slots: public slots:
void showMassesChanged(int state); void showMassesChanged(int state);
private slots: private slots:
void uploadStaticDataToGPU(const std::vector<Pendulum *> *pendula); void uploadStaticDataToGPU();
void changePosition(const std::vector<Pendulum *> *pendula); void changePosition();
private: private:
QOpenGLShaderProgram * program; QOpenGLShaderProgram * pendulaProgram;
GLuint VAO; GLuint VAO;
GLuint positionVBO; GLuint positionVBO;
GLuint colorVBO; GLuint colorVBO;
@ -32,11 +32,11 @@ private:
GLsizei indexCount = 0; GLsizei indexCount = 0;
GLsizei positionCount = 0; GLsizei positionCount = 0;
QMatrix3x3 VP; QMatrix3x3 VP;
float screenSizePixels; int simulationSizePixels;
float depthOffset; float depthOffset;
bool showMasses; bool showMasses;
Simulation * simulation; Simulation * simulation;
FPS * fps; Overlay * overlay;
static bool AnyDialogOpen(); static bool AnyDialogOpen();
}; };

@ -30,6 +30,8 @@ MainWindow::MainWindow() {
resetLengths(); resetLengths();
buildUI(); buildUI();
normalizeLengths();
} }
void MainWindow::buildUI() { void MainWindow::buildUI() {

@ -0,0 +1,177 @@
#include "Overlay.h"
#include "FPS.h"
#include <QPainter>
#include <QOpenGLShaderProgram>
#include "Simulation.h"
#include <iostream>
#include <QOpenGLTexture>
#include <sstream>
#include <iomanip>
Overlay::Overlay(Simulation * simulation) : simulation(simulation) {
fps = new FPS;
fps->setUpdateInterval(500);
}
void Overlay::init() {
initializeOpenGLFunctions();
program = new QOpenGLShaderProgram;
program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex_quad.glsl");
program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment_quad.glsl");
program->link();
float quadVertices[] = {
-1, 1, 0, 1, // top left
-1, -1, 0, 0, // bottom left
1, -1, 1, 0, // bottom right
1, 1, 1, 1 // top right
};
GLuint indices[] = {
0, 1, 2, 2, 3, 0
};
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
GLuint VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
GLuint EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (GLvoid*) (2 * sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
void Overlay::draw() {
int w = viewportSize.width();
int h = (viewportSize.height() - simulationRect.height()) / 2;
// Top HUD with Energy Plot
{
glViewport(0, viewportSize.height() - h, w, h);
QImage hud(w, h, QImage::Format_RGBA8888);
hud.fill(Qt::transparent);
QPainter p(&hud);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::white);
auto font = p.font();
font.setPixelSize(15);
p.setFont(font);
auto m = p.fontMetrics();
double pot = simulation->potentialEnergy;
double kin = simulation->kineticEnergy;
double total = pot + kin;
QColor empty(50, 50, 50);
QColor full(80, 80, 80);
// Total energy bar
{
double lossPercentage = (1 - total / energyLimit) * 100;
std::stringstream text;
text << std::fixed << std::setprecision(0) << "Total Energy: " << total << " J";
text << " --- ";
text << "Loss: " << std::setprecision(1) << lossPercentage << " %";
QString totalText = QString::fromStdString(text.str());
int x = int(total / energyLimit * w);
p.fillRect(0, 0, w, h / 2, empty);
p.fillRect(0, 0, x, h / 2, full);
p.drawLine(x, 0, x, h / 2);
p.drawText(0, 0, w, h / 2, Qt::AlignCenter | Qt::AlignVCenter, totalText);
}
// Split energy bar
{
int potX = total == 0 ? int(0.5 * w) : int(pot / total * w);
p.fillRect(0, h / 2, w, h / 2, empty);
p.fillRect(0, h / 2, potX, h / 2, full);
std::stringstream text;
text << std::fixed << std::setprecision(0) << "Potential: " << pot << " J";
QString potText = QString::fromStdString(text.str());
text = std::stringstream();
text << std::fixed << std::setprecision(0) << "Kinetic: " << kin << " J";
QString kinText = QString::fromStdString(text.str());
int textWidth1 = m.horizontalAdvance(potText);
int textWidth2 = m.horizontalAdvance(kinText);
int x1 = (w - textWidth1) / 4;
int x2 = (w - textWidth2) / 4 * 3;
p.drawLine(potX, h / 2, potX, h);
p.drawText(x1, h / 2, textWidth1, h / 2, Qt::AlignCenter | Qt::AlignVCenter, potText);
p.drawText(x2, h / 2, textWidth2, h / 2, Qt::AlignCenter | Qt::AlignVCenter, kinText);
}
p.drawLine(0, 0, w, 0);
p.drawLine(0, h / 2, w, h / 2);
p.drawLine(0, h, w, h);
drawTexture(&hud);
}
// Bottom HUD with FPS and UPS
{
glViewport(0, 0, w, h);
QImage hud(w, h, QImage::Format_RGBA8888);
// Background
hud.fill(Qt::transparent);
QPainter p(&hud);
p.setRenderHints(QPainter::Antialiasing);
// FPS
{
p.setPen(Qt::white);
auto font = p.font();
font.setPixelSize(15);
p.setFont(font);
fps->newFrame();
std::stringstream text;
text << "FPS: " << fps->current << "\n";
text << "UPS: " << simulation->ups->current;
p.drawText(0, 0, 100, h, Qt::AlignBottom | Qt::AlignLeft, QString::fromStdString(text.str()));
}
drawTexture(&hud);
}
}
void Overlay::resize(QSize newViewportSize, QRect newSimulationRect) {
viewportSize = newViewportSize;
simulationRect = newSimulationRect;
}
void Overlay::drawTexture(QImage *image) {
glBindVertexArray(VAO);
program->bind();
QOpenGLTexture texture(image->mirrored());
texture.bind();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
}
void Overlay::fetchEnergyLimit() {
energyLimit = simulation->kineticEnergy + simulation->potentialEnergy;
}

@ -0,0 +1,29 @@
#include <QRect>
#include <QOpenGLExtraFunctions>
class QOpenGLWidget;
class FPS;
class QOpenGLShaderProgram;
class QOpenGLTexture;
class Simulation;
class Overlay : public QObject, protected QOpenGLExtraFunctions {
Q_OBJECT
FPS * fps;
QSize viewportSize;
QRect simulationRect;
QOpenGLShaderProgram * program;
GLuint VAO;
void drawTexture(QImage *image);
Simulation * simulation;
double energyLimit = 0;
public slots:
void fetchEnergyLimit();
public:
explicit Overlay(Simulation *);
void init();
void draw();
void resize(QSize newViewportSize, QRect newSimulationRect);
};

@ -7,9 +7,9 @@ Pendulum::Pendulum(const std::vector<double> &M,
const std::vector<double> &L, const std::vector<double> &L,
QColor color, double startAngle) : M(M), L(L), color(color){ QColor color, double startAngle) : M(M), L(L), color(color){
startAngle *= 3.141 / 180; startAngle *= M_PI / 180;
Vector direction(-sin(startAngle), cos(startAngle)); Vector direction(-sin(startAngle), -cos(startAngle));
Vector currentPosition(0, 0); Vector currentPosition(0, 0);
for (int i = 0; i < M.size(); i++){ for (int i = 0; i < M.size(); i++){
@ -31,7 +31,7 @@ void Pendulum::update(double h, double g) {
for (int i = 0; i < X.size(); i++){ for (int i = 0; i < X.size(); i++){
// explicit integration with gravity as external force // explicit integration with gravity as external force
V[i] = V[i] + Vector(0, g * h); V[i] = V[i] + Vector(0, -g * h);
Vector p2 = X[i] + V[i] * h; Vector p2 = X[i] + V[i] * h;
// solve distance constraint // solve distance constraint
@ -64,3 +64,20 @@ void Pendulum::update(double h, double g) {
} }
double Pendulum::potentialEnergy(double gravity) const {
double total = 0;
double zeroLevel = 0;
for (int i = 0; i < X.size(); i++){
zeroLevel -= L[i];
total += M[i] * gravity * (X[i].y - zeroLevel);
}
return total;
}
double Pendulum::kineticEnergy() const {
double total = 0;
for (int i = 0; i < V.size(); i++)
total += M[i] * V[i].squaredLength();
return total / 2;
}

@ -12,6 +12,8 @@ public:
const std::vector<double> &L, const std::vector<double> &L,
QColor color, double startAngle); QColor color, double startAngle);
void update(double, double); void update(double, double);
double potentialEnergy(double gravity) const;
double kineticEnergy() const;
private: private:
friend class GLWidget; friend class GLWidget;
std::vector<Vector> X, V; std::vector<Vector> X, V;

@ -4,36 +4,47 @@
#include <QThread> #include <QThread>
#include <omp.h> #include <omp.h>
#include <iostream> #include <iostream>
#include "FPS.h"
Simulation::Simulation() { Simulation::Simulation() {
ups = new FPS;
ups->setUpdateInterval(100);
timer = new QTimer(this); timer = new QTimer(this);
QTimer::connect(timer, &QTimer::timeout, this, &Simulation::update); QTimer::connect(timer, &QTimer::timeout, this, &Simulation::update);
timer->setInterval(updateInterval); timer->setInterval(updateInterval);
timer->start(); timer->start();
lastUpdate = system_clock::now();
}; };
void Simulation::update() { void Simulation::update() {
auto thisUpdate = system_clock::now();
auto ms = (int)duration_cast<milliseconds>(thisUpdate - lastUpdate).count();
lastUpdate = thisUpdate;
if (!isPlaying) if (!isPlaying)
return; return;
double h = (timescale * updateInterval) / 1000; ups->newFrame();
h /= substeps; double h = (timescale * updateInterval) / 1000 / substeps;
pendulaMutex.lock(); pendulaMutex.lock();
double newPotentialEnergy = 0;
double newKineticEnergy = 0;
#pragma omp parallel for #pragma omp parallel for
for (int i = 0; i < pendula.size(); i++){ // NOLINT(*-loop-convert) // not ranged based for msvc for (int i = 0; i < pendula.size(); i++){ // NOLINT(*-loop-convert) // not ranged based for msvc
for (int k = 0; k < substeps; k++) for (int k = 0; k < substeps; k++)
pendula[i]->update(h, gravity); pendula[i]->update(h, gravity);
double localPotentialEnergy = pendula[i]->potentialEnergy(gravity);
double localKineticEnergy = pendula[i]->kineticEnergy();
#pragma omp atomic
newPotentialEnergy += localPotentialEnergy;
#pragma omp atomic
newKineticEnergy += localKineticEnergy;
} }
emit positionChanged(&pendula); potentialEnergy = newPotentialEnergy;
kineticEnergy = newKineticEnergy;
emit positionChanged();
} }
void Simulation::clearPendula() { void Simulation::clearPendula() {
@ -47,7 +58,9 @@ void Simulation::clearPendula() {
pendula.clear(); pendula.clear();
pendula.shrink_to_fit(); pendula.shrink_to_fit();
emit layoutChanged(&pendula); updateEnergy();
emit layoutChanged();
} }
void Simulation::addPendula(const std::vector<Pendulum *> &add) { void Simulation::addPendula(const std::vector<Pendulum *> &add) {
@ -56,6 +69,16 @@ void Simulation::addPendula(const std::vector<Pendulum *> &add) {
for (auto p : add) for (auto p : add)
pendula.push_back(p); pendula.push_back(p);
emit layoutChanged(&pendula); updateEnergy();
emit layoutChanged();
}
void Simulation::updateEnergy() {
potentialEnergy = kineticEnergy = 0;
for (auto p : pendula){
potentialEnergy += p->potentialEnergy(gravity);
kineticEnergy += p->kineticEnergy();
}
} }

@ -8,6 +8,7 @@
using namespace std::chrono; using namespace std::chrono;
class QTimer; class QTimer;
class FPS;
class Simulation : public QObject { class Simulation : public QObject {
Q_OBJECT Q_OBJECT
@ -20,20 +21,24 @@ public:
double timescale {}; double timescale {};
int substeps {}; int substeps {};
double potentialEnergy = 0, kineticEnergy = 0;
bool isPlaying = false; bool isPlaying = false;
std::vector<Pendulum *> pendula;
std::mutex pendulaMutex; std::mutex pendulaMutex;
FPS * ups;
signals: signals:
void layoutChanged(const std::vector<Pendulum *> *newPendula); void layoutChanged();
void positionChanged(const std::vector<Pendulum *> *changedPendula); void positionChanged();
public slots: public slots:
void clearPendula(); void clearPendula();
void addPendula(const std::vector<Pendulum *> &add); void addPendula(const std::vector<Pendulum *> &add);
private slots: private slots:
void update(); void update();
private: private:
void updateEnergy();
QTimer * timer; QTimer * timer;
int updateInterval = 17; int updateInterval = 16;
std::vector<Pendulum *> pendula;
time_point<system_clock> lastUpdate;
}; };

@ -29,3 +29,7 @@ Vector operator/(Vector v, double divisor){
return {v.x / divisor, v.y / divisor}; return {v.x / divisor, v.y / divisor};
} }
double Vector::squaredLength() const {
return x * x + y * y;
}

@ -3,6 +3,7 @@ struct Vector {
Vector(double x, double y); Vector(double x, double y);
double length() const; double length() const;
double squaredLength() const;
void normalize(); void normalize();
friend Vector operator +(Vector lhs, Vector rhs); friend Vector operator +(Vector lhs, Vector rhs);

@ -11,6 +11,7 @@ int main(int argc, char* argv[]) {
QSurfaceFormat fmt; QSurfaceFormat fmt;
fmt.setSamples(4); fmt.setSamples(4);
fmt.setDepthBufferSize(32); fmt.setDepthBufferSize(32);
fmt.setSwapInterval(1);
fmt.setVersion(3, 3); fmt.setVersion(3, 3);
fmt.setProfile(QSurfaceFormat::CoreProfile); fmt.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(fmt); QSurfaceFormat::setDefaultFormat(fmt);

Loading…
Cancel
Save