Added gravitation

This commit is contained in:
Robert 2020-09-26 17:18:32 +02:00
parent 61afefa6b5
commit 64419997dd
12 changed files with 294 additions and 51 deletions

View file

@ -7,7 +7,7 @@ cmake_minimum_required (VERSION 3.8)
add_executable (GraviSim
"main.cpp"
"MainWindow.cpp"
"Screen.hpp" "Screen.cpp" "Planet.hpp" "Planet.cpp" "PlanetConfig.cpp" )
"Screen.hpp" "Screen.cpp" "Planet.hpp" "Planet.cpp" "PlanetConfig.cpp" "PlanetManager.hpp" "PlanetManager.cpp")
target_link_libraries(GraviSim
Qt5::Widgets

View file

@ -33,6 +33,7 @@ void MainWindow::OpenPlanetDialog(Planet* planet)
ui.config->Enable();
ui.config->SetTitle(planet->name);
ui.config->SetRadius(planet->radius);
ui.config->SetMass(planet->mass);
ui.config->SetColor(planet->GetColor());
ui.config->SetX(planet->position.rx());
ui.config->SetY(planet->position.ry());
@ -48,10 +49,17 @@ void MainWindow::ClosePlanetDialog()
ui.config->Disable();
ui.config->SetColor(QColor(0, 0, 0, 0));
ui.config->SetRadius(0.f);
ui.config->SetMass(0.f);
ui.config->SetX(0.f);
ui.config->SetY(0.f);
}
void MainWindow::UpdatePlanetPositionInDialog(const QPointF& position)
{
ui.config->SetX(position.x());
ui.config->SetY(position.y());
}
void MainWindow::OnNameChanged(const QString& name)
{
if (activePlanet != nullptr)
@ -64,6 +72,12 @@ void MainWindow::OnRadiusChanged(double radius)
activePlanet->radius = radius;
}
void MainWindow::OnMassChanged(double mass)
{
if (activePlanet != nullptr)
activePlanet->mass = mass;
}
void MainWindow::OnColourChanged(const QColor& color)
{
if (activePlanet != nullptr)
@ -92,4 +106,5 @@ void MainWindow::OnToggle()
{
isSimulating = !isSimulating;
ui.config->SetButtonLabel((isSimulating) ? "Stop Simulation" : "Start Simulation");
ui.screen->simulate = isSimulating;
}

View file

@ -17,9 +17,12 @@ public:
void OpenPlanetDialog(Planet* planet);
void ClosePlanetDialog();
void UpdatePlanetPositionInDialog(const QPointF& position);
public slots:
void OnNameChanged(const QString& name);
void OnRadiusChanged(double radius);
void OnMassChanged(double mass);
void OnColourChanged(const QColor& color);
void OnXChanged(double x);
void OnYChanged(double y);

View file

@ -1,13 +1,18 @@
#include "Planet.hpp"
Planet::Planet(float xPos, float yPos, float radius, QColor color) :
position{ xPos, yPos }, radius(radius)
#define LENGTH(v) (sqrtf(v.x()*v.x() + v.y()*v.y()))
QPen Planet::arrowColor = QPen(Qt::white);
QLine Planet::arrow = QLine();
Planet::Planet(uint32_t id, float xPos, float yPos, float radius, QColor color) :
position{ xPos, yPos }, radius(radius), id(id)
{
Initialize(color);
}
Planet::Planet(QPointF pos, float radius, QColor color) :
position(pos), radius(radius)
Planet::Planet(uint32_t id, QPointF pos, float radius, QColor color) :
position(pos), radius(radius), id(id)
{
Initialize(color);
}
@ -31,11 +36,33 @@ void Planet::SetColor(const QColor& color)
selectionColor = QColor::fromHsv((h + 180) % 360, s, v, a);
}
void Planet::Draw(QPainter* painter)
void Planet::Update()
{
painter->setBrush(circle);
painter->setPen(outline);
painter->drawEllipse(position, radius, radius);
velocity += acceleration * (1.f / 60.f);
position += velocity * (1.f / 60.f);
}
void Planet::Draw(QPainter & painter)
{
painter.setBrush(circle);
painter.setPen(outline);
painter.drawEllipse(position, radius, radius);
// Draw velocity arrow
if (!velocity.isNull())
{
painter.save();
qreal angle = QPointF::dotProduct(velocity, QPointF(1.0f, 0.0f)) / LENGTH(velocity);
painter.rotate(angle);
arrow.setP1(position.toPoint());
arrow.setP2((position + velocity).toPoint());
painter.setPen(arrowColor);
painter.drawLine(arrow);
painter.restore();
}
}
bool Planet::IsInside(QPointF point) const
@ -48,4 +75,6 @@ void Planet::Initialize(QColor color)
{
name = "Unnamed";
SetColor(color);
arrowColor.setWidthF(2.0f);
acceleration = QPointF(0.f, 0.f);
}

View file

@ -7,8 +7,8 @@
class Planet
{
public:
Planet(float xPos, float yPos, float radius, QColor color);
Planet(QPointF pos, float radius, QColor color);
Planet(uint32_t id, float xPos, float yPos, float radius, QColor color);
Planet(uint32_t id, QPointF pos, float radius, QColor color);
void Resize(float newRadius) { radius = newRadius; }
void Select(bool select);
@ -16,7 +16,8 @@ public:
void SetColor(const QColor& color);
const QColor& GetColor() { return circle.color(); }
void Draw(QPainter* painter);
void Update();
void Draw(QPainter & painter);
bool IsInside(QPointF point) const;
@ -24,9 +25,14 @@ private:
void Initialize(QColor color);
public:
const uint32_t id;
QPointF position;
QPointF velocity;
QPointF acceleration;
QString name;
float radius;
float mass;
private:
@ -34,4 +40,7 @@ private:
QBrush circle;
QPen outline;
static QPen arrowColor;
static QLine arrow;
};

View file

@ -18,6 +18,9 @@ PlanetConfig::PlanetConfig(QWidget* parent) :
connect(ui.radius, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
instance, [instance](double d) { instance->OnRadiusChanged(d); });
connect(ui.mass, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
instance, [instance](double d) { instance->OnMassChanged(d); });
connect(ui.colour, SIGNAL(returnPressed()),
this, SLOT(ColourTBSlot()));
@ -38,6 +41,7 @@ PlanetConfig::PlanetConfig(QWidget* parent) :
void PlanetConfig::Disable()
{
ui.radius->setDisabled(true);
ui.mass->setDisabled(true);
ui.colour->setDisabled(true);
ui.xPos->setDisabled(true);
ui.yPos->setDisabled(true);
@ -47,6 +51,7 @@ void PlanetConfig::Disable()
void PlanetConfig::Enable()
{
ui.radius->setDisabled(false);
ui.mass->setDisabled(false);
ui.colour->setDisabled(false);
ui.xPos->setDisabled(false);
ui.yPos->setDisabled(false);
@ -78,6 +83,11 @@ void PlanetConfig::SetY(double y)
ui.yPos->setValue(y);
}
void PlanetConfig::SetMass(double mass)
{
ui.mass->setValue(mass);
}
void PlanetConfig::SetButtonLabel(const QString& label)
{
ui.toggle->setText(label);

View file

@ -19,6 +19,7 @@ public:
void SetColor(const QColor& color);
void SetX(double x);
void SetY(double y);
void SetMass(double mass);
void SetButtonLabel(const QString& label);

99
src/PlanetManager.cpp Normal file
View file

@ -0,0 +1,99 @@
#include "PlanetManager.hpp"
std::map<uint32_t, Planet*> PlanetManager::planets;
uint32_t PlanetManager::planetCounter = 0;
Planet* PlanetManager::AddPlanet(const QPointF& position, float radius, const QColor& color, const QString& name)
{
Planet* planet = new Planet(planetCounter, position, radius, color);
planet->mass = rand() % 100;
planets.insert(
std::make_pair(
planetCounter++,
planet
)
);
return planet;
}
void PlanetManager::RemovePlanet(uint32_t id)
{
auto it = planets.find(id);
if (it == planets.end())
return;
delete it->second;
planets.erase(it);
}
Planet* PlanetManager::GetPlanet(uint32_t id)
{
auto it = planets.find(id);
if (it == planets.end())
return nullptr;
return it->second;
}
Planet* PlanetManager::GetLastPlanet()
{
return planets.rbegin()->second;
}
void PlanetManager::RemoveLastPlanet()
{
auto it = planets.cbegin();
delete it->second;
planets.erase(it);
}
Planet* PlanetManager::IsInPlanet(const QPointF& position)
{
for (auto it : planets)
if (it.second->IsInside(position))
return it.second;
return nullptr;
}
void PlanetManager::Update()
{
for (auto planet : planets)
planet.second->Update();
// Collision detection && Gravitation
for (auto planet : planets)
{
planet.second->acceleration = QPointF(0.f, 0.f);
for (auto otherPlanet : planets)
{
if (planet == otherPlanet)
continue;
QPointF distance = planet.second->position - otherPlanet.second->position;
float radiusSum = (planet.second->radius + otherPlanet.second->radius);
float dist = distance.x() * distance.x() + distance.y() * distance.y();
if (dist < radiusSum * radiusSum)
{
// Collision
Planet* lighter = ((planet.second->mass > otherPlanet.second->mass) ? otherPlanet.second : planet.second);
lighter->position += (radiusSum - sqrt(dist) + 1) * distance / sqrt(dist);
}
else
{
// Gravitation
planet.second->acceleration -= 1000000 * (planet.second->mass * otherPlanet.second->mass) / pow(dist, 3.f / 2.f) * distance / sqrt(dist);
}
}
planet.second->acceleration /= planet.second->mass;
}
}
void PlanetManager::Render(QPainter& painter)
{
for (auto it : planets)
it.second->Draw(painter);
}

30
src/PlanetManager.hpp Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include <map>
#include <QPointF>
#include "Planet.hpp"
class PlanetManager
{
public:
PlanetManager() = delete;
static Planet* AddPlanet(const QPointF& position, float radius, const QColor& color,
const QString& name = "Unnamed");
static void RemovePlanet(uint32_t id);
static Planet* GetPlanet(uint32_t id);
static Planet* GetLastPlanet();
static void RemoveLastPlanet();
static Planet* IsInPlanet(const QPointF& position);
static void Update();
static void Render(QPainter& painter);
private:
static std::map<uint32_t, Planet*> planets;
static uint32_t planetCounter;
};

View file

@ -9,6 +9,7 @@
#include <QtMath>
#include "MainWindow.hpp"
#include "PlanetManager.hpp"
#define BIND_LMB(f) (lmbAction = std::bind(&Screen::f, this, std::placeholders::_1))
@ -32,32 +33,30 @@ Screen::Screen(QWidget* parent) :
void Screen::mouseMoveEvent(QMouseEvent* event)
{
if (mouseDown && hovered == nullptr)
if (mouseDown && selected == nullptr && hovered == nullptr)
{
Planet* planet = planets.back();
Planet* planet = PlanetManager::GetLastPlanet();
QPointF distance = event->localPos() - mouseClickPos;
planet->Resize(qSqrt(distance.x()*distance.x() + distance.y() *distance.y()));
}
else if (mouseDown && selected != nullptr)
{
selected->velocity = event->localPos() - selected->position;
}
else
{
hovered = nullptr;
for (unsigned i = planets.size(); i-- > 0; )
{
if (planets[i]->IsInside(event->localPos()))
{
setCursor(Qt::PointingHandCursor);
BIND_LMB(lmb_SelectPlanet);
hovered = planets[i];
break;
}
}
hovered = PlanetManager::IsInPlanet(event->localPos());
if (hovered == nullptr)
{
setCursor(Qt::ArrowCursor);
if(selected == nullptr)
BIND_LMB(lmb_MakePlanet);
}
else
{
BIND_LMB(lmb_SelectPlanet);
setCursor(Qt::PointingHandCursor);
}
}
}
@ -72,7 +71,7 @@ void Screen::mousePressEvent(QMouseEvent* event)
{
if (mouseDown)
{
planets.pop_back();
PlanetManager::RemoveLastPlanet();
mouseDown = false;
}
}
@ -86,17 +85,9 @@ void Screen::mouseReleaseEvent(QMouseEvent* event)
void Screen::DeletePlanet(Planet* planet)
{
for (std::vector<Planet*>::iterator it = planets.begin(); it != planets.end(); it++)
{
if ((*it) == planet)
{
planets.erase(it);
delete planet;
hovered = nullptr;
selected = nullptr;
break;
}
}
PlanetManager::RemovePlanet(planet->id);
hovered = nullptr;
selected = nullptr;
}
void Screen::Render()
@ -106,9 +97,12 @@ void Screen::Render()
void Screen::lmb_MakePlanet(QMouseEvent* event)
{
planets.push_back(new Planet(event->localPos(), 0,
QColor(rand() % 256, rand() % 256, rand() % 256)
));
Planet* newPlanet = PlanetManager::AddPlanet(event->localPos(), 0,
QColor(rand() % 200, rand() % 200, rand() % 200)
);
newPlanet->velocity = QPointF(rand() % 400 - 200, rand() % 400 - 200);
mouseClickPos = event->localPos();
mouseDown = true;
}
@ -117,12 +111,19 @@ void Screen::lmb_SelectPlanet(QMouseEvent* event)
{
if (hovered != nullptr)
{
if (selected != nullptr)
selected->Select(false);
if (selected != hovered)
{
if (selected != nullptr)
selected->Select(false);
hovered->Select(true);
selected = hovered;
MainWindow::Instance()->OpenPlanetDialog(selected);
hovered->Select(true);
selected = hovered;
MainWindow::Instance()->OpenPlanetDialog(selected);
}
else
{
mouseDown = true;
}
}
else
{
@ -136,12 +137,20 @@ void Screen::lmb_SelectPlanet(QMouseEvent* event)
void Screen::paintEvent(QPaintEvent* event)
{
if (simulate)
{
PlanetManager::Update();
if (selected != nullptr)
{
MainWindow::Instance()->UpdatePlanetPositionInDialog(selected->position);
}
}
QPainter painter;
painter.begin(this);
painter.fillRect(rect(), background);
for (Planet* planet : planets)
planet->Draw(&painter);
PlanetManager::Render(painter);
painter.end();
}

View file

@ -5,8 +5,8 @@
#include <QOpenGLWidget>
#include "Planet.hpp"
class Planet;
class MainWindow;
class Screen : public QOpenGLWidget
@ -30,6 +30,9 @@ private:
protected:
void paintEvent(QPaintEvent* event) override;
public:
bool simulate = false;
private:
bool mouseDown = false;
@ -40,7 +43,6 @@ private:
QBrush background;
QTimer* renderTimer;
std::vector<Planet*> planets;
Planet* selected = nullptr;
Planet* hovered = nullptr;
};

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>308</width>
<height>380</height>
<height>454</height>
</rect>
</property>
<property name="windowTitle">
@ -82,6 +82,42 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<family>Courier New</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Mass</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="mass">
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::AdaptiveDecimalStepType</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>