You've already forked flecs_tests
145 lines
4.8 KiB
C
145 lines
4.8 KiB
C
#ifndef INTERACT_H
|
|
#define INTERACT_H
|
|
|
|
#include "api.h"
|
|
#include "types.h"
|
|
|
|
static void selected_aabb(userdata_t *ud, float *min_x, float *min_y,
|
|
float *max_x, float *max_y)
|
|
{
|
|
bool first = true;
|
|
for (int i = 0; i < ud->shapes.count; i++) {
|
|
shape_t *s = (shape_t*) vec_get(&ud->shapes, i);
|
|
if (!s->selected) continue;
|
|
float sc = cosf(s->rotation), ss = sinf(s->rotation);
|
|
for (uint32_t v = 0; v < s->num_verts; v++) {
|
|
float lx = s->verts[v].x * s->sx;
|
|
float ly = s->verts[v].y * s->sy;
|
|
float wx = s->cx + lx * sc - ly * ss;
|
|
float wy = s->cy + lx * ss + ly * sc;
|
|
if (first) { *min_x = *max_x = wx; *min_y = *max_y = wy; first = false; }
|
|
else {
|
|
if (wx < *min_x) *min_x = wx;
|
|
if (wx > *max_x) *max_x = wx;
|
|
if (wy < *min_y) *min_y = wy;
|
|
if (wy > *max_y) *max_y = wy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void update_shape_states(userdata_t *ud)
|
|
{
|
|
for (int i = 0; i < ud->shapes.count; i++) {
|
|
shape_t *s = (shape_t*) vec_get(&ud->shapes, i);
|
|
shape_set_state(s, s->hovered, s->selected);
|
|
}
|
|
}
|
|
|
|
static void select_group_recursive(userdata_t *ud, int gid)
|
|
{
|
|
for (int i = 0; i < ud->shapes.count; i++) {
|
|
shape_t *s = (shape_t*) vec_get(&ud->shapes, i);
|
|
if (is_shape_in_group_hierarchy(s->group_id, gid, &ud->groups)) {
|
|
s->selected = true;
|
|
ud->interact.selected_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void deselect_and_select_group_recursive(userdata_t *ud, int gid)
|
|
{
|
|
for (int i = 0; i < ud->shapes.count; i++)
|
|
((shape_t*) vec_get(&ud->shapes, i))->selected = false;
|
|
ud->interact.selected_count = 0;
|
|
select_group_recursive(ud, gid);
|
|
}
|
|
|
|
static void toggle_group_recursive(userdata_t *ud, int gid)
|
|
{
|
|
int total = 0, sel = 0;
|
|
for (int i = 0; i < ud->shapes.count; i++) {
|
|
shape_t *s = (shape_t*) vec_get(&ud->shapes, i);
|
|
if (is_shape_in_group_hierarchy(s->group_id, gid, &ud->groups)) {
|
|
total++;
|
|
if (s->selected) sel++;
|
|
}
|
|
}
|
|
bool all_sel = (sel == total && total > 0);
|
|
for (int i = 0; i < ud->shapes.count; i++) {
|
|
shape_t *s = (shape_t*) vec_get(&ud->shapes, i);
|
|
if (is_shape_in_group_hierarchy(s->group_id, gid, &ud->groups)) {
|
|
s->selected = !all_sel;
|
|
ud->interact.selected_count += s->selected ? 1 : -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rebuild_groups_from_shapes(vector_t *groups, vector_t *shapes)
|
|
{
|
|
// Save existing parent relationships so nested groups survive rebuild
|
|
int old_count = groups->count;
|
|
int *saved = NULL;
|
|
if (old_count > 0) {
|
|
saved = (int*) ALLOC((size_t)old_count * 2 * sizeof(int));
|
|
for (int i = 0; i < old_count; i++) {
|
|
group_t *g = (group_t*) vec_get(groups, i);
|
|
saved[i * 2] = g->id;
|
|
saved[i * 2 + 1] = g->parent_id;
|
|
}
|
|
}
|
|
|
|
groups->count = 0;
|
|
for (int i = 0; i < shapes->count; i++) {
|
|
shape_t *s = (shape_t*) vec_get(shapes, i);
|
|
if (s->group_id == 0) continue;
|
|
bool found = false;
|
|
for (int g = 0; g < groups->count; g++) {
|
|
if (((group_t*) vec_get(groups, g))->id == s->group_id) { found = true; break; }
|
|
}
|
|
if (!found) {
|
|
group_t grp = { .id = s->group_id, .parent_id = 0 };
|
|
*((group_t*) vec_push(groups)) = grp;
|
|
}
|
|
}
|
|
|
|
// Restore parent relationships for groups that still exist
|
|
for (int i = 0; i < old_count; i++) {
|
|
int gid = saved[i * 2];
|
|
int pid = saved[i * 2 + 1];
|
|
if (pid == 0) continue;
|
|
group_t *g = find_group(groups, gid);
|
|
if (g) {
|
|
// Only restore if parent group still exists
|
|
if (find_group(groups, pid))
|
|
g->parent_id = pid;
|
|
}
|
|
}
|
|
|
|
if (saved) FREE(saved);
|
|
}
|
|
|
|
static int hit_test_resize_handles(userdata_t *ud, float wx, float wy, float tol)
|
|
{
|
|
if (ud->interact.selected_count <= 0) return -1;
|
|
float omin[2], omax[2];
|
|
if (ud->interact.aabb_cached) {
|
|
omin[0] = ud->interact.cached_aabb[0]; omin[1] = ud->interact.cached_aabb[1];
|
|
omax[0] = ud->interact.cached_aabb[2]; omax[1] = ud->interact.cached_aabb[3];
|
|
} else {
|
|
selected_aabb(ud, &omin[0], &omin[1], &omax[0], &omax[1]);
|
|
}
|
|
float hs = CORNER_SIZE_PX / ud->camera.zoom * 0.5f + tol;
|
|
float mid_x = (omin[0] + omax[0]) * 0.5f;
|
|
float mid_y = (omin[1] + omax[1]) * 0.5f;
|
|
float hx[8] = {omin[0], mid_x, omax[0], omax[0], omax[0], mid_x, omin[0], omin[0]};
|
|
float hy[8] = {omin[1], omin[1], omin[1], mid_y, omax[1], omax[1], omax[1], mid_y};
|
|
for (int h = 0; h < 8; h++) {
|
|
if (fabsf(wx - hx[h]) <= hs && fabsf(wy - hy[h]) <= hs)
|
|
return h;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#endif
|