-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdrawarea.cpp
More file actions
146 lines (118 loc) · 5.93 KB
/
Copy pathdrawarea.cpp
File metadata and controls
146 lines (118 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "drawarea.h"
#include "Vec2.h"
#include "collider.h"
#include "particle.h"
#include "object.h"
#include <QPainter>
#include <QMouseEvent>
QColor generateRandomColor() {
int red = std::rand() % 256;
int green = std::rand() % 256;
int blue = std::rand() % 256;
return QColor(red, green, blue);
}
DrawArea::DrawArea(int _width, int _height, QWidget *parent)
: width(_width), height(_height), QOpenGLWidget{parent}
{
std::srand(std::time(nullptr));
this->setFixedSize(this->width, this->height);
// Add two first objects
float radius = 15;
float mass = 1.0;
Particle circle1(this->viewToWorld({{ 360, 40 }}), {{ 0.0, 0.0 }}, radius, mass);
Particle circle2(this->viewToWorld({{ 360, 70 }}), {{ 0.0, 0.0 }}, radius, mass);
Particle circle3(this->viewToWorld({{ 390, 40 }}), {{ 0.0, 0.0 }}, radius, mass);
Particle circle4(this->viewToWorld({{ 390, 70 }}), {{ 0.0, 0.0 }}, radius, mass);
Object object(generateRandomColor(), circle1, circle2, circle3, circle4);
this->context.objects.push_back(std::move(object));
Particle circle5(this->viewToWorld({{ 165, 65 }}), {{ 0.0, 0.0 }}, radius, mass);
Particle circle6(this->viewToWorld({{ 160, 35 }}), {{ 0.0, 0.0 }}, radius, mass);
Particle circle7(this->viewToWorld({{ 190, 35 }}), {{ 0.0, 0.0 }}, radius, mass);
Object object2(generateRandomColor(), circle5, circle6, circle7);
this->context.objects.push_back(std::move(object2));
// Add a few colliders, for testing purpose
this->context.addCollider(std::make_unique<PlanCollider>(
worldToView(Vec2{{30.0, 400.0}}), normalize(Vec2{{0.0, 1.0}})));
this->context.addCollider(std::make_unique<PlanCollider>(
worldToView(Vec2{{600.0, 150.0}}), normalize(Vec2{{-1.0, 1.0}})));
this->context.addCollider(std::make_unique<PlanCollider>(
worldToView(Vec2{{50.0, 150.0}}), normalize(Vec2{{2.5, 1.0}})));
this->context.addCollider(std::make_unique<SphereCollider>(
worldToView(Vec2{{440.0, 170.0}}), 50.0f));
this->context.addCollider(std::make_unique<SphereCollider>(
worldToView(Vec2{{25.0, 200.0}}), 100.0f));
}
void DrawArea::paintEvent(QPaintEvent *event) {}
void DrawArea::mouseDoubleClickEvent(QMouseEvent *event) {
float x = event->position().x();
float y = event->position().y();
float radius = 15;
float mass = 1.0;
Vec2 view_pos {{ x-radius-1, y-radius }}; // -radius correction for drawing right under the cursor tip
Vec2 vel {{ 0.0, 0.0 }};
Particle circle(this->viewToWorld(view_pos), vel, radius, mass);
// As it stands, a double click will create a new object, made of a single particle
// In the future, it would be nice to let the user create an object made of multiple particles
Object object(generateRandomColor(), circle);
this->context.objects.push_back(std::move(object));
QPainter p(this); // to be refactored
this->renderContext(&p, nullptr);
}
void DrawArea::mousePressEvent(QMouseEvent* event){
if (!(this->context.click.has_value())) { //if the optional vec2 doesnt have a value it means that it is the first click
this->context.click=Vec2{static_cast<float>(event->pos().x()), static_cast<float>(event->pos().y())};
}
else {
Vec2 first_click= this->context.click.value();
Vec2 second_click=Vec2{static_cast<float>(event->pos().x()), static_cast<float>(event->pos().y())};
first_click= worldToView(first_click); // converting the positions to the view
second_click= worldToView(second_click);
this->context.click= second_click - first_click; //directing vector in the optional vector
if (this->context.addPlanCollidersOn){ //the button is on
Vec2 normal= perpendicular(this->context.click.value()); // transforming it in a normal vector to use the addcolliders function
std::unique_ptr<PlanCollider> new_collider= std::make_unique<PlanCollider> (first_click, normal);
this->context.addCollider(std::move(new_collider));
}
else if (this->context.addSphereColliderOn){
float norm= squaredLength(this->context.click.value()); // getting the radius which is the norm of the directing vector
std::unique_ptr<SphereCollider> new_collider= std::make_unique<SphereCollider> (first_click,sqrt(norm));
this->context.addCollider(std::move(new_collider));
}
this->context.click.reset(); // reseting the optional vector when already two clicks
}
}
void DrawArea::animate() {
this->context.updatePhysicalSystem(10);
QPainter p(this); // to be refactored
this->renderContext(&p, nullptr);
}
void DrawArea::renderContext(QPainter *painter, QPaintEvent *event) {
painter->fillRect(this->rect(), QColor(215, 214, 213)); // clear canva by painting the entire background
painter->setPen(QPen(QColor(134, 132, 130)));
painter->setBrush(QBrush(QColor(134, 132, 130)));
renderColliders(painter);
renderObjects(painter);
this->update();
}
void DrawArea::renderColliders(QPainter *painter) {
for (const auto& collider: context.colliders) {
collider->render(painter, [this](const Vec2& pos) { return worldToView(pos); }); // passing a function pointer to worldToView is probably not the best solution. These utilities could be defined somewhere else.
}
}
void DrawArea::renderObjects(QPainter *painter) {
for (Object& object: context.objects){
object.render(painter, [this](const Vec2& pos) { return worldToView(pos); }); // same comment as with renderColliders
}
}
/**
* @brief Switch from the physical system coordinates to the Qt coordinates.
*/
Vec2 DrawArea::worldToView(Vec2 world_pos) {
return Vec2{world_pos[0], this->height - world_pos[1]};
}
/**
* @brief Switch from the Qt coordinates to the physical system coordinates.
*/
Vec2 DrawArea::viewToWorld(Vec2 view_pos) {
return Vec2{view_pos[0], this->height - view_pos[1]};
}