2023-03-14 21:46:36 +01:00
|
|
|
#include "polygons.h"
|
|
|
|
|
2023-05-17 13:38:46 +02:00
|
|
|
#include "balls.h" // just for the restitution_coefficient_get() function,
|
|
|
|
// prolly should be placed in another header file
|
2023-03-25 10:57:32 +01:00
|
|
|
#include "cairo.h"
|
2023-04-02 21:53:21 +02:00
|
|
|
#include "collisions.h"
|
2023-03-25 10:57:32 +01:00
|
|
|
#include "game.h"
|
2023-05-17 13:33:07 +02:00
|
|
|
#include "gravity.h"
|
2023-03-20 15:33:35 +01:00
|
|
|
#include "matrix.h"
|
2023-03-14 23:10:31 +01:00
|
|
|
#include "polygon_generator.h"
|
|
|
|
|
2023-05-17 13:22:34 +02:00
|
|
|
#include <algorithm>
|
2023-03-14 21:46:36 +01:00
|
|
|
#include <iostream>
|
2023-04-02 21:53:21 +02:00
|
|
|
#include <utility>
|
2023-03-14 21:46:36 +01:00
|
|
|
|
|
|
|
polygon* polygons = nullptr;
|
2023-05-17 15:38:05 +02:00
|
|
|
uint n_polygons = 11;
|
2023-03-14 21:46:36 +01:00
|
|
|
|
|
|
|
void polygons_init_state() {
|
|
|
|
polygons = new polygon[n_polygons];
|
2023-04-10 14:40:16 +02:00
|
|
|
int wall_thickness = 50;
|
|
|
|
// north wall
|
|
|
|
polygons[0] = poly_generate::rectangle(width, wall_thickness, INFINITY)
|
|
|
|
.set_center({width / 2., -wall_thickness / 2.});
|
|
|
|
// south wall
|
|
|
|
polygons[1] = poly_generate::rectangle(width, wall_thickness, INFINITY)
|
|
|
|
.set_center({width / 2., height + wall_thickness / 2.});
|
|
|
|
// west wall
|
|
|
|
polygons[2] = poly_generate::rectangle(wall_thickness, height, INFINITY)
|
|
|
|
.set_center({-wall_thickness / 2., height / 2.});
|
|
|
|
// east wall
|
|
|
|
polygons[3] = poly_generate::rectangle(wall_thickness, height, INFINITY)
|
|
|
|
.set_center({width + wall_thickness / 2., height / 2.});
|
2023-05-17 15:38:05 +02:00
|
|
|
polygons[4] = poly_generate::rectangle(50, height / 2., INFINITY)
|
|
|
|
.set_center({25 + width * 1. / 2, height / 2.})
|
|
|
|
.set_angle(0);
|
|
|
|
|
|
|
|
#define PROBLEMATIC_COUPLES 0
|
2023-05-17 13:22:34 +02:00
|
|
|
#if PROBLEMATIC_COUPLES == 0
|
2023-05-17 15:38:05 +02:00
|
|
|
polygons[5] = poly_generate::regular(100, 3)
|
2023-05-17 13:22:34 +02:00
|
|
|
.set_center({100, 400})
|
|
|
|
.set_angle(0)
|
2023-04-25 12:05:39 +02:00
|
|
|
.set_speed({200, -10});
|
|
|
|
|
2023-05-17 15:38:05 +02:00
|
|
|
polygons[6] = poly_generate::square(100)
|
2023-05-17 13:22:34 +02:00
|
|
|
.set_center({600, 400})
|
|
|
|
.set_angle(45)
|
|
|
|
.set_speed({-200, -10});
|
|
|
|
|
2023-05-17 15:38:05 +02:00
|
|
|
polygons[7] = poly_generate::general(
|
2023-05-06 14:06:45 +02:00
|
|
|
{{0, 0}, {100, 0}, {100, 100}, {50, 150}, {0, 100}}, 3)
|
2023-05-17 15:38:05 +02:00
|
|
|
.set_center({200, 600});
|
|
|
|
|
|
|
|
polygons[8] = poly_generate::rectangle(100, 150).set_center({600, 200});
|
|
|
|
polygons[9] = poly_generate::regular(50, 5).set_center({150, 150});
|
|
|
|
polygons[10] =
|
|
|
|
poly_generate::general({{0, 0}, {50, 80}, {0, 160}, {-50, 80}})
|
|
|
|
.set_center({700, 700})
|
|
|
|
.set_speed({0, -100});
|
2023-05-17 13:22:34 +02:00
|
|
|
#elif PROBLEMATIC_COUPLES == 1
|
|
|
|
// for this problematic couple to work, remove the gravity
|
|
|
|
n_polygons = 7;
|
|
|
|
polygons[4] = poly_generate::regular(100, 3)
|
|
|
|
.set_center({103.91, 684.587})
|
|
|
|
.set_angle(280.108)
|
|
|
|
.set_speed({190.114, 131.983})
|
|
|
|
.set_angular_speed(32.6448);
|
|
|
|
|
|
|
|
polygons[5] = poly_generate::square(100)
|
|
|
|
.set_center({614.338, 514.889})
|
|
|
|
.set_angle(-54.3526)
|
|
|
|
.set_speed({-70.0347, -62.4788})
|
|
|
|
.set_angular_speed(-39.3846);
|
2023-05-06 14:06:45 +02:00
|
|
|
|
2023-05-17 13:22:34 +02:00
|
|
|
polygons[6] = poly_generate::general(
|
|
|
|
{{0, 0}, {100, 0}, {100, 100}, {50, 150}, {0, 100}}, 3)
|
|
|
|
.set_center({261.425, 556.613})
|
|
|
|
.set_angle(-122.59)
|
|
|
|
.set_speed({-46.9522, 48.5392})
|
|
|
|
.set_angular_speed(-35.5983);
|
2023-05-06 14:06:45 +02:00
|
|
|
|
|
|
|
#endif
|
2023-03-20 15:33:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static double to_rad(double angle_in_deg) {
|
2023-04-02 21:53:21 +02:00
|
|
|
static double PI_180 = M_PI / 180.;
|
2023-03-20 15:33:35 +01:00
|
|
|
return angle_in_deg * PI_180;
|
|
|
|
}
|
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
static double to_deg(double angle_in_rad) {
|
|
|
|
static double PI_180 = 180. / M_PI;
|
|
|
|
return angle_in_rad * PI_180;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_point_inside_rect(rect rect, vec2d point) {
|
|
|
|
vec2d tl = rect.first, br = rect.second;
|
|
|
|
return point.x > tl.x && point.x < br.x && point.y > tl.y && point.y < br.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool bounding_rects_collide(rect cur_bound, rect other_bound) {
|
|
|
|
vec2d other_tl = other_bound.first, other_br = other_bound.second;
|
|
|
|
return is_point_inside_rect(cur_bound, other_tl) ||
|
|
|
|
is_point_inside_rect(cur_bound, {other_tl.x, other_br.y}) ||
|
|
|
|
is_point_inside_rect(cur_bound, {other_br.x, other_tl.y}) ||
|
|
|
|
is_point_inside_rect(cur_bound, other_br);
|
|
|
|
}
|
|
|
|
|
|
|
|
static double impulse_parameter(vec2d v_ab1,
|
|
|
|
vec2d n,
|
|
|
|
double m_a,
|
|
|
|
double m_b,
|
|
|
|
vec2d r_ap,
|
|
|
|
vec2d r_bp,
|
|
|
|
double I_a,
|
|
|
|
double I_b,
|
|
|
|
double e) {
|
|
|
|
double nominator = -(1 + e) * vec2d::dot(v_ab1, n);
|
2023-04-10 14:40:16 +02:00
|
|
|
|
|
|
|
double r_ap_cross_n = vec2d::cross(r_ap, n);
|
|
|
|
double r_bp_cross_n = vec2d::cross(r_bp, n);
|
|
|
|
double denominator = 1 / m_a + 1 / m_b + r_ap_cross_n * r_ap_cross_n / I_a +
|
|
|
|
r_bp_cross_n * r_bp_cross_n / I_b;
|
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
return nominator / denominator;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_collision(collision& c, polygon* a, polygon* b) {
|
|
|
|
// see https://www.myphysicslab.com/engine2D/collision-en.html for the
|
|
|
|
// formulas
|
|
|
|
double omega_a1 = to_rad(a->angular_speed);
|
|
|
|
double omega_b1 = to_rad(b->angular_speed);
|
|
|
|
|
|
|
|
vec2d r_ap = c.impact_point - a->centroid();
|
|
|
|
vec2d v_ap1 = a->speed + vec2d::cross(omega_a1, r_ap);
|
|
|
|
|
|
|
|
vec2d r_bp = c.impact_point - b->centroid();
|
|
|
|
vec2d v_bp1 = b->speed + vec2d::cross(omega_b1, r_bp);
|
|
|
|
|
|
|
|
vec2d v_ab1 = v_ap1 - v_bp1;
|
|
|
|
|
2023-05-17 13:22:34 +02:00
|
|
|
// avoid the polygons getting stuck if, on the frame after the impact,
|
|
|
|
// the polygon a is still inside of b
|
|
|
|
if (a->collided_with.find(b) != a->collided_with.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
a->collided_with.insert(b);
|
|
|
|
b->collided_with.insert(a);
|
|
|
|
|
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
double I_a = a->inertia, I_b = b->inertia;
|
|
|
|
|
2023-05-17 13:38:46 +02:00
|
|
|
double j = impulse_parameter(v_ab1,
|
|
|
|
c.n,
|
|
|
|
a->mass,
|
|
|
|
b->mass,
|
|
|
|
r_ap,
|
|
|
|
r_bp,
|
|
|
|
I_a,
|
|
|
|
I_b,
|
|
|
|
restitution_coefficient_get());
|
2023-04-02 21:53:21 +02:00
|
|
|
|
|
|
|
vec2d v_a2 = a->speed + j * c.n / a->mass;
|
|
|
|
vec2d v_b2 = b->speed - j * c.n / b->mass;
|
|
|
|
|
|
|
|
double omega_a2 = omega_a1 + vec2d::cross(r_ap, j * c.n) / I_a;
|
|
|
|
double omega_b2 = omega_b1 - vec2d::cross(r_bp, j * c.n) / I_b;
|
|
|
|
|
|
|
|
a->speed = v_a2;
|
|
|
|
a->angular_speed = to_deg(omega_a2);
|
|
|
|
|
|
|
|
b->speed = v_b2;
|
|
|
|
b->angular_speed = to_deg(omega_b2);
|
|
|
|
}
|
|
|
|
|
2023-04-25 12:05:39 +02:00
|
|
|
collision col; // tbd
|
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
static void check_collisions(polygon* current_p) {
|
|
|
|
rect cur_bound = current_p->get_bounding_box();
|
|
|
|
|
2023-05-17 13:22:34 +02:00
|
|
|
bool collided_with_something = false;
|
2023-04-02 21:53:21 +02:00
|
|
|
for (polygon* other_p = polygons; other_p != polygons + n_polygons;
|
|
|
|
++other_p) {
|
|
|
|
if (other_p == current_p) // polygons don't collide with themselves
|
|
|
|
continue;
|
2023-05-17 15:38:05 +02:00
|
|
|
if (vec2d::norm(current_p->speed) == 0 &&
|
|
|
|
vec2d::norm(other_p->speed) == 0)
|
|
|
|
// if both are not moving, no need to go through the whole
|
|
|
|
// collision detection thing
|
|
|
|
continue;
|
2023-04-02 21:53:21 +02:00
|
|
|
rect other_bound = other_p->get_bounding_box();
|
|
|
|
if (bounding_rects_collide(cur_bound, other_bound) ||
|
|
|
|
bounding_rects_collide(other_bound, cur_bound)) {
|
|
|
|
collision c = collides(*current_p, *other_p);
|
|
|
|
if (c.collides) {
|
2023-05-17 13:22:34 +02:00
|
|
|
collided_with_something = true;
|
2023-04-25 12:05:39 +02:00
|
|
|
col = c;
|
2023-04-02 21:53:21 +02:00
|
|
|
handle_collision(c, current_p, other_p);
|
2023-05-17 13:22:34 +02:00
|
|
|
} else if (current_p->collided_with.find(other_p) !=
|
|
|
|
current_p->collided_with.end()) {
|
|
|
|
current_p->collided_with.erase(other_p);
|
|
|
|
other_p->collided_with.erase(current_p);
|
2023-04-02 21:53:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-17 13:22:34 +02:00
|
|
|
if (!collided_with_something && current_p->collided_with.size() > 0)
|
|
|
|
current_p->collided_with.clear();
|
2023-04-02 21:53:21 +02:00
|
|
|
}
|
|
|
|
|
2023-03-20 15:33:35 +01:00
|
|
|
void polygons_update_state() {
|
|
|
|
for (polygon* p = polygons; p != polygons + n_polygons; ++p) {
|
2023-04-02 21:53:21 +02:00
|
|
|
if (p->mass == INFINITY) // immovable objects don't need to be updated
|
|
|
|
continue;
|
2023-04-10 14:40:16 +02:00
|
|
|
// check_border_collision(p);
|
2023-04-02 21:53:21 +02:00
|
|
|
check_collisions(p);
|
2023-05-17 13:22:34 +02:00
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
p->rotate(delta * p->angular_speed);
|
2023-05-17 13:22:34 +02:00
|
|
|
p->angle = std::fmod(p->angle, 360);
|
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
p->translate(delta * p->speed);
|
2023-05-17 13:33:07 +02:00
|
|
|
|
|
|
|
vec2d g = gravity_vector(p);
|
|
|
|
p->translate(.5 * delta * delta * g);
|
|
|
|
p->speed += delta * g;
|
2023-05-17 13:22:34 +02:00
|
|
|
// std::cout << *p << std::endl;
|
2023-03-20 15:33:35 +01:00
|
|
|
}
|
2023-03-14 21:46:36 +01:00
|
|
|
}
|
|
|
|
|
2023-03-25 10:57:32 +01:00
|
|
|
void polygon::update_global_points() {
|
2023-03-20 15:33:35 +01:00
|
|
|
double cos_theta = std::cos(to_rad(this->angle));
|
|
|
|
double sin_theta = std::sin(to_rad(this->angle));
|
|
|
|
|
|
|
|
matrix rotation = matrix{{cos_theta, sin_theta}, {-sin_theta, cos_theta}};
|
|
|
|
|
2023-03-25 10:57:32 +01:00
|
|
|
for (uint i = 0; i < this->points.size(); ++i)
|
|
|
|
this->global_points[i] = rotation * this->points[i] + this->center;
|
|
|
|
}
|
|
|
|
|
|
|
|
void polygon::draw_bounding_rect(cairo_t* cr) const {
|
|
|
|
cairo_set_source_rgb(cr, .7, .7, .7);
|
|
|
|
double dashes[] = {5, 10};
|
|
|
|
cairo_set_dash(cr, dashes, 2, 0);
|
|
|
|
|
|
|
|
auto bb = this->get_bounding_box();
|
2023-04-02 21:53:21 +02:00
|
|
|
vec2d tl = bb.first, br = bb.second;
|
2023-03-25 10:57:32 +01:00
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
cairo_line_to(cr, tl.x, tl.y);
|
|
|
|
cairo_line_to(cr, tl.x, br.y);
|
|
|
|
cairo_line_to(cr, br.x, br.y);
|
|
|
|
cairo_line_to(cr, br.x, tl.y);
|
|
|
|
cairo_line_to(cr, tl.x, tl.y);
|
2023-03-25 10:57:32 +01:00
|
|
|
cairo_stroke(cr);
|
|
|
|
cairo_set_dash(cr, 0, 0, 0); // disable dashes
|
|
|
|
}
|
|
|
|
|
2023-04-25 12:05:39 +02:00
|
|
|
static void draw_circle(cairo_t* cr, vec2d p, double radius) {
|
|
|
|
cairo_translate(cr, p.x, p.y);
|
|
|
|
cairo_arc(cr, 0, 0, radius, 0, 2 * M_PI);
|
|
|
|
cairo_fill(cr);
|
|
|
|
cairo_translate(cr, -p.x, -p.y);
|
|
|
|
}
|
|
|
|
|
2023-03-25 10:57:32 +01:00
|
|
|
void polygon::draw(cairo_t* cr) const {
|
2023-04-10 14:40:16 +02:00
|
|
|
// this->draw_bounding_rect(cr);
|
2023-03-25 10:57:32 +01:00
|
|
|
|
|
|
|
cairo_set_source_rgb(cr, 1, 1, 1);
|
|
|
|
|
|
|
|
for (auto& point : this->global_points)
|
|
|
|
cairo_line_to(cr, point.x, point.y);
|
|
|
|
|
|
|
|
cairo_line_to(cr, this->global_points[0].x, this->global_points[0].y);
|
2023-03-14 21:46:36 +01:00
|
|
|
cairo_stroke(cr);
|
2023-04-02 21:53:21 +02:00
|
|
|
|
2023-04-25 12:05:39 +02:00
|
|
|
|
2023-04-02 21:53:21 +02:00
|
|
|
// draw centroid
|
|
|
|
vec2d centroid = this->centroid();
|
2023-04-25 12:05:39 +02:00
|
|
|
draw_circle(cr, centroid, 1);
|
2023-05-17 13:22:34 +02:00
|
|
|
|
2023-05-17 15:40:55 +02:00
|
|
|
// draw speed
|
2023-05-17 13:22:34 +02:00
|
|
|
(10 * delta * this->speed).draw(cr, centroid);
|
2023-03-14 21:46:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void polygons_draw(cairo_t* cr) {
|
2023-05-17 15:40:55 +02:00
|
|
|
draw_circle(cr, col.impact_point, 3); // tbd
|
|
|
|
col.n.draw(cr, col.impact_point); // tbd
|
|
|
|
|
2023-03-14 21:46:36 +01:00
|
|
|
for (const polygon* p = polygons; p != polygons + n_polygons; ++p)
|
|
|
|
p->draw(cr);
|
|
|
|
}
|