213 lines
6.1 KiB
C
213 lines
6.1 KiB
C
#include "shape.h"
|
|
#include "camera.h"
|
|
#include "debug.h"
|
|
#include "render.h"
|
|
#include "list.h"
|
|
#include <SDL2/SDL_render.h>
|
|
#include <stdlib.h>
|
|
|
|
struct Shape {
|
|
List points;
|
|
|
|
Vector mean;
|
|
int is_convex;
|
|
Vector min, max;
|
|
};
|
|
|
|
static
|
|
Vector* _shape_get_furthest_in_direction(Shape* self, Vector direction) {
|
|
// ensure direction is normalized
|
|
direction = vnormalizedf(direction);
|
|
float furthest_dot = vdotf(direction, vsubf(*list_at_as(Vector, &self->points, 0), self->mean));
|
|
Vector* furthest = list_at(&self->points, 0);
|
|
|
|
float dot;
|
|
list_foreach(Vector*, point, &self->points) {
|
|
dot = vdotf(direction, vsubf(*point, self->mean));
|
|
if(dot > furthest_dot) {
|
|
furthest = point;
|
|
}
|
|
}
|
|
|
|
return furthest;
|
|
}
|
|
|
|
// go through each point,
|
|
// in order to be convex, none of the points in the shape can be "inset".
|
|
// This means that if one of the points is not the furthest in it's own direction
|
|
// measured from the median.
|
|
static
|
|
int _shape_calculate_is_convex(Shape* self) {
|
|
// point relative to mean
|
|
Vector relative;
|
|
list_foreach(Vector*, point, &self->points) {
|
|
relative = vnormalizedf(vsubf(*point, self->mean));
|
|
if(point != _shape_get_furthest_in_direction(self, relative)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static
|
|
Vector _shape_calculate_mean(Shape* self) {
|
|
Vector avg = ZeroVector;
|
|
size_t count = 0;
|
|
list_foreach(Vector*, point, &self->points) {
|
|
++count;
|
|
avg = vaddf(avg, vmulff(*point, 1.f/count));
|
|
}
|
|
|
|
return avg;
|
|
}
|
|
|
|
Shape* shape_new(const Vector* points, size_t points_len) {
|
|
// allocate required space for shape and points array
|
|
Shape* self = malloc(sizeof(Shape));
|
|
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate enough space for a shape object.");
|
|
|
|
self->points = list_from_type(Vector);
|
|
list_reserve(&self->points, points_len);
|
|
|
|
if(points != NULL) {
|
|
memcpy(list_iterator_begin_as(Vector, &self->points), points, points_len * sizeof(Vector));
|
|
self->points.len = points_len;
|
|
|
|
// derive metadata
|
|
self->mean = _shape_calculate_mean(self);
|
|
self->is_convex = _shape_calculate_is_convex(self);
|
|
} else {
|
|
self->is_convex = 1;
|
|
self->mean = ZeroVector;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
Shape* shape_new_square(Vector size) {
|
|
return shape_new((Vector[4]){
|
|
MakeVector(-size.x, -size.y),
|
|
MakeVector(size.x, -size.y),
|
|
MakeVector(size.x, size.y),
|
|
MakeVector(-size.x, size.y)
|
|
}, 4);
|
|
}
|
|
|
|
Shape* shape_clone(const Shape* source) {
|
|
Shape* self = malloc(sizeof(Shape));
|
|
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for shape object.");
|
|
|
|
self->points = list_copy(&source->points);
|
|
|
|
// copy data from source
|
|
self->mean = source->mean;
|
|
self->is_convex = source->is_convex;
|
|
|
|
return self;
|
|
}
|
|
|
|
void shape_destroy(Shape* self) {
|
|
list_empty(&self->points);
|
|
free(self);
|
|
}
|
|
|
|
Vector* shape_get_start(Shape* self) {
|
|
return list_iterator_begin(&self->points);
|
|
}
|
|
|
|
Vector* shape_get_end(Shape* self) {
|
|
return list_iterator_end(&self->points);
|
|
}
|
|
|
|
size_t shape_get_points_count(const Shape* self) {
|
|
return self->points.len;
|
|
}
|
|
|
|
Vector shape_get_point(Shape* self, size_t at) {
|
|
ASSERT_RETURN(at < self->points.len, self->mean, "Point index %zu out of bounds for shape", at);
|
|
return *list_at_as(Vector, &self->points, at);
|
|
}
|
|
|
|
Vector shape_get_point_transformed(Shape* self, size_t at, Transform transform) {
|
|
return transform_point(&transform, shape_get_point(self, at));
|
|
}
|
|
|
|
void shape_set_point(Shape* self, size_t at, Vector point) {
|
|
ASSERT_RETURN(at < self->points.len,, "Point index %zu out of bounds for shape", at);
|
|
*list_at_as(Vector, &self->points, at) = point;
|
|
}
|
|
|
|
void shape_add_point(Shape* self, Vector point) {
|
|
list_add(&self->points, &point);
|
|
}
|
|
|
|
void shape_insert_point(Shape* self, size_t at, Vector point) {
|
|
ASSERT_RETURN(at < self->points.len + 1,, "Point index %zu out of bounds for shape", at);
|
|
list_insert(&self->points, &point, at);
|
|
}
|
|
|
|
List* shape_get_points(Shape* self) {
|
|
return &self->points;
|
|
}
|
|
|
|
Vector shape_remove_point(Shape* self, size_t at) {
|
|
Vector point = *list_at_as(Vector, &self->points, at);
|
|
list_erase(&self->points, at);
|
|
return point;
|
|
}
|
|
|
|
Vector shape_get_median_point(Shape* self) {
|
|
return self->mean;
|
|
}
|
|
|
|
int shape_is_convex(Shape* self) {
|
|
return self->is_convex;
|
|
}
|
|
|
|
void shape_draw(Shape* self, Transform transform) {
|
|
Vector lhs, rhs, normal;
|
|
for(size_t i = 0; i < self->points.len; ++i) {
|
|
lhs = shape_get_point_transformed(self, i, transform);
|
|
rhs = shape_get_point_transformed(self, (i + 1) % self->points.len, transform);
|
|
normal = vnormalizedf(vperpendicularf(vsubf(rhs, lhs)));
|
|
lhs = camera_world_to_pixel_point(&g_camera, lhs);
|
|
rhs = camera_world_to_pixel_point(&g_camera, rhs);
|
|
normal = transform_direction(&g_camera.transform, normal);
|
|
|
|
SDL_SetRenderDrawColor(g_renderer, 255, 255, 255, 255);
|
|
SDL_RenderDrawLineF(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
|
|
|
|
lhs = vlerpf(lhs, rhs, 0.5f);
|
|
rhs = vaddf(lhs, vmulff(normal, 10.f));
|
|
SDL_SetRenderDrawColor(g_renderer, 0, 0, 255, 255);
|
|
SDL_RenderDrawLineF(g_renderer, lhs.x, lhs.y, rhs.x, rhs.y);
|
|
}
|
|
}
|
|
|
|
Vector shape_get_min_extent(Shape* self, Transform* transform) {
|
|
if(self->points.len == 0)
|
|
return ZeroVector;
|
|
Vector min = *list_at_as(Vector, &self->points, 0);
|
|
Vector point;
|
|
for(size_t i = 0; i < shape_get_points_count(self); ++i) {
|
|
point = shape_get_point_transformed(self, i, *transform);
|
|
min.x = fminf(min.x, point.x);
|
|
min.y = fminf(min.y, point.y);
|
|
}
|
|
return min;
|
|
}
|
|
|
|
Vector shape_get_max_extent(Shape* self, Transform* transform) {
|
|
if(self->points.len == 0)
|
|
return ZeroVector;
|
|
Vector max = *list_at_as(Vector, &self->points, 0);
|
|
Vector point;
|
|
for(size_t i = 0; i < shape_get_points_count(self); ++i) {
|
|
point = shape_get_point_transformed(self, i, *transform);
|
|
max.x = fmaxf(max.x, point.x);
|
|
max.y = fmaxf(max.y, point.y);
|
|
}
|
|
return max;
|
|
}
|