main
Benjamin Kraft 1 year ago
parent cf0d2ece41
commit bdaf7752e9
  1. 10
      src/GLWidget.cpp
  2. 7
      src/GLWidget.h
  3. 231
      src/MainWindow.cpp
  4. 40
      src/MainWindow.h
  5. 1
      src/Pendulum.cpp
  6. 3
      src/Pendulum.h
  7. 3
      src/Simulation.cpp
  8. 18
      src/Simulation.h
  9. 2
      src/Slider.cpp
  10. 37
      src/Slider.h
  11. 12
      src/main.cpp

@ -0,0 +1,10 @@
#include "GLWidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <iostream>
void GLWidget::paintEvent(QPaintEvent *e) {
auto p = new QPainter(this);
p->fillRect(e->rect(), Qt::black);
p->end();
}

@ -0,0 +1,7 @@
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions {
protected:
void paintEvent(QPaintEvent* e) override;
};

@ -0,0 +1,231 @@
#include "MainWindow.h"
#include <QHBoxLayout>
#include "GLWidget.h"
#include "Simulation.h"
#include <QPushButton>
#include <QLabel>
#include <QScrollArea>
#include <QCheckBox>
#include <QRadioButton>
#include <QButtonGroup>
#include <QSizePolicy>
#include <iostream>
#include <QDial>
#include "Slider.h"
#include <iostream>
MainWindow::MainWindow() {
simulation = new Simulation;
masses = std::vector<double>(MaxSegments);
lengths = std::vector<double>(MaxSegments);
resetMasses();
resetLengths();
buildUI();
}
void MainWindow::buildUI() {
setMinimumSize(800, 500);
auto uiLyt = new QVBoxLayout;
glWidget = new GLWidget;
auto mainLyt = new QHBoxLayout(this);
mainLyt->addLayout(uiLyt);
mainLyt->addWidget(glWidget, 1);
auto scrollArea = new QScrollArea;
scrollArea->setWidget(buildAddUI());
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(true);
auto l1 = new QLabel("Add Pendula");
auto l2 = new QLabel("Simulation");
auto f = l1->font();
f.setBold(true);
f.setPixelSize(15);
l1->setFont(f);
l2->setFont(f);
uiLyt->addWidget(l1);
uiLyt->addWidget(scrollArea);
uiLyt->addSpacing(30);
uiLyt->addWidget(l2);
uiLyt->addWidget(buildSimulationUI());
}
QWidget * MainWindow::buildAddUI() {
auto w = new QWidget;
auto lyt = new QVBoxLayout(w);
lyt->setAlignment(Qt::AlignTop);
auto segLabel = new QLabel();
auto segmentSlider = new Slider<>(segLabel, "Segments: %d", &segments);
segmentGrid = new QGridLayout();
segmentGrid->addWidget(new QLabel("Mass"), 0, 1);
segmentGrid->addWidget(new QLabel("Length"), 0, 3);
lyt->addWidget(segLabel);
lyt->addWidget(segmentSlider);
lyt->addLayout(segmentGrid);
connect(segmentSlider, &QSlider::valueChanged, [this, segmentSlider](int newV){
int oldSegments = segmentSlider->targetOld;
if (segments < oldSegments){
for (int row = segments; row < oldSegments; row++){
for (int i = 0; i < 5; i++){
auto item = segmentGrid->itemAtPosition(row + 1, i);
segmentGrid->removeItem(item);
delete item->widget();
}
}
}
if (segments > oldSegments){
for (int row = oldSegments; row < segments; row++){
char buff[100];
snprintf(buff, sizeof(buff), "(%02d)", row);
segmentGrid->addWidget(new QLabel(buff), row + 1, 0);
auto massLabel = new QLabel;
auto massSlider = new Slider<double>(massLabel, "%03.1fkg", &masses[row], [](int v){
return double(v) / 10;
}, [](double v) {
return int(v * 10);
});
massSlider->setFromTarget();
massSlider->setMinimum(1);
auto lengthLabel = new QLabel;
auto lengthSlider = new Slider<double>(lengthLabel, "% 4.1fm", &lengths[row], [](int v){
return double(v) / 10;
}, [](double v) {
return int(v * 10);
});
lengthSlider->setMaximum(250);
lengthSlider->setFromTarget();
lengthSlider->setMinimum(1);
segmentGrid->addWidget(massLabel, row + 1, 1);
segmentGrid->addWidget(massSlider, row + 1, 2);
segmentGrid->addWidget(lengthLabel, row + 1, 3);
segmentGrid->addWidget(lengthSlider, row + 1, 4);
}
}
});
segmentSlider->setValue(2);
segmentSlider->setMinimum(1);
segmentSlider->setMaximum(MaxSegments);
auto segmentButtonGrid = new QHBoxLayout;
lyt->addLayout(segmentButtonGrid);
auto resetMassesBtn = new QPushButton("Reset");
auto normalizeLengthsBtn = new QPushButton("Normalize");
auto resetLengthsBtn = new QPushButton("Reset");
connect(resetMassesBtn, &QPushButton::clicked, this, &MainWindow::resetMasses);
connect(normalizeLengthsBtn, &QPushButton::clicked, this, &MainWindow::normalizeLengths);
connect(resetLengthsBtn, &QPushButton::clicked, this, &MainWindow::resetLengths);
segmentButtonGrid->addWidget(resetMassesBtn);
segmentButtonGrid->addWidget(normalizeLengthsBtn);
segmentButtonGrid->addWidget(resetLengthsBtn);
lyt->addWidget(new QLabel("Starting Angle:"));
auto dial = new QDial;
dial->setMinimum(0);
dial->setMaximum(360);
dial->setMinimumSize(100, 100);
lyt->addWidget(dial);
auto colorLyt = new QHBoxLayout;
lyt->addLayout(colorLyt);
colorLyt->addWidget(new QPushButton("Select color"));
colorLyt->addWidget(new QLabel("Color this label"));
lyt->addWidget(new QCheckBox("Add multiple"));
lyt->addWidget(new QCheckBox("Use rainbow coloring"));
lyt->addWidget(new QLabel("Count:"));
// lyt->addWidget(new Slider(nullptr, [](int v) {return "";}));
lyt->addWidget(new QLabel("Property to change slightly:"));
auto group = new QButtonGroup;
auto b1 = new QRadioButton("Starting Angle");
auto b2 = new QRadioButton("Starting Mass");
auto b3 = new QRadioButton("Starting Length");
group->addButton(b1);
group->addButton(b2);
group->addButton(b3);
lyt->addWidget(b1);
lyt->addWidget(b2);
lyt->addWidget(b3);
b1->setChecked(true);
lyt->addWidget(new QLabel("Index:"));
// lyt->addWidget(new Slider(nullptr, [](int v) {return "";}));
lyt->addWidget(new QLabel("Change Amount:"));
// lyt->addWidget(new Slider(nullptr, [](int v) {return "";}));
auto btnLyt = new QHBoxLayout;
lyt->addLayout(btnLyt);
btnLyt->addWidget(new QPushButton("Add"));
btnLyt->addWidget(new QPushButton("Remove"));
return w;
}
QWidget * MainWindow::buildSimulationUI() {
auto w = new QWidget;
auto lyt = new QVBoxLayout(w);
lyt->addWidget(new QLabel("Gravity:"));
// lyt->addWidget(new Slider(nullptr, [](int v) {return "";}));
lyt->addWidget(new QLabel("Timescale:"));
// lyt->addWidget(new Slider(nullptr, [](int v) {return "";}));
lyt->addWidget(new QLabel("PBD Substeps:"));
// lyt->addWidget(new Slider(nullptr, [](int v) {return "";}));
auto btnLyt = new QHBoxLayout;
lyt->addLayout(btnLyt);
btnLyt->addWidget(new QPushButton("Reset"));
btnLyt->addWidget(new QPushButton("Resume/Pause"));
return w;
}
void MainWindow::resetMasses() {
for (int i = 0; i < MaxSegments; i++)
masses[i] = 1;
for (int i = 0; i < segments; i++){
dynamic_cast<Slider<double> *>(segmentGrid->itemAtPosition(i + 1, 2)->widget())->setFromTarget();
}
}
void MainWindow::resetLengths() {
for (int i = 0; i < MaxSegments; i++)
lengths[i] = 1;
for (int i = 0; i < segments; i++){
dynamic_cast<Slider<double> *>(segmentGrid->itemAtPosition(i + 1, 4)->widget())->setFromTarget();
}
}
void MainWindow::normalizeLengths() {
double sum = std::reduce(lengths.begin(), lengths.begin() + segments);
double factor = simulation->size / 2 / sum;
for (int i = 0; i < segments; i++){
lengths[i] *= factor;
}
for (int i = 0; i < segments; i++){
dynamic_cast<Slider<double> *>(segmentGrid->itemAtPosition(i + 1, 4)->widget())->setFromTarget();
}
}

@ -0,0 +1,40 @@
#include <QWidget>
#include <vector>
class Simulation;
class QGridLayout;
class GLWidget;
enum Property {Angle, Mass, Length};
class MainWindow : public QWidget {
public:
explicit MainWindow();
private:
void buildUI();
QWidget * buildAddUI();
QWidget * buildSimulationUI();
Simulation * simulation;
GLWidget * glWidget;
static const int MaxSegments = 50;
std::vector<double> masses, lengths;
int segments = 0;
double startingAngle = 90;
QColor color = Qt::white;
bool multiple = false;
bool rainbow = false;
int count = 100;
Property changeProperty = Angle;
int changeIndex = 0;
double changeAmount = 0.001;
QGridLayout * segmentGrid;
public slots:
void resetMasses();
void resetLengths();
void normalizeLengths();
};

@ -0,0 +1 @@
#include "Pendulum.h"

@ -0,0 +1,3 @@
class Pendulum {
};

@ -0,0 +1,3 @@
#include "Simulation.h"
Simulation::Simulation() = default;

@ -0,0 +1,18 @@
#include <vector>
#include <cstdint>
class Pendulum;
class Simulation {
public:
explicit Simulation();
double size = 50;
private:
std::vector<Pendulum *> pendula;
double gravity;
double timescale;
uint64_t substeps;
};

@ -0,0 +1,2 @@
#include "Slider.h"
#include <QLabel>

@ -0,0 +1,37 @@
#include <QSlider>
#include <functional>
#include <QLabel>
template<typename T = int>
class Slider : public QSlider {
public:
explicit Slider(QLabel* label, const char *format, T * target,
const std::function<T(int)> &convertToT = [](int v){ return T(v); },
const std::function<int(T)> &convertFromT = [](T v){ return int(v); }) : QSlider(Qt::Horizontal) {
this->convertToT = convertToT;
this->convertFromT = convertFromT;
this->target = target;
connect(this, &QSlider::valueChanged, this, [=, this](int newValue){
T convertedValue = convertToT(newValue);
char buff[100];
snprintf(buff, sizeof(buff), format, convertedValue);
label->setText(buff);
targetOld = *target;
*target = convertedValue;
});
}
void setFromTarget(){
setValue(convertFromT(*target));
}
T targetOld;
private:
T * target;
std::function<T(int)> convertToT;
std::function<int(T)> convertFromT;
};

@ -1,10 +1,16 @@
#include <iostream>
#include <Eigen/Core>
#include <QApplication> #include <QApplication>
#include <QWidget> #include <QSurfaceFormat>
#include "MainWindow.h"
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
QApplication app(argc, argv); QApplication app(argc, argv);
QSurfaceFormat fmt;
fmt.setSamples(4);
QSurfaceFormat::setDefaultFormat(fmt);
MainWindow w;
w.show();
return QApplication::exec(); return QApplication::exec();
} }

Loading…
Cancel
Save