Files
flecs_tests/src/util.h
Peaceultime 7e3da1c424 refactor: eliminate globals, add pen tool + edit mode, frustum culling
Remove all file-scope mutable state so the codebase compiles cleanly
as a single translation unit. State moved into structs owned by
userdata_t:

- group_index_ctx_t replaces g_group_by_id/g_group_by_id_cap
- shape_pool_ctx_t replaces g_shape_pool_dirty/g_shape_data_dirty
  and g_shape_groups/g_shape_groups_count
- panel_log_ctx_t wired through all subsystems explicitly
- pipeline_ctx_t owns render pipeline handles
- overlay_upload_state_t (per-buffer flags) replaces single bool

New features piggybacking on the refactor:

- Pen tool: click-to-place anchors, Catmull-Rom preview, finalize
  into Bezier shapes with control points
- Edit mode: anchor/handle hit testing, dragging, pre-drag undo
  snapshots, dedicated GPU buffers for edit overlays
- Frustum culling: spatial-grid-based viewport cull in draw_shapes
  with linear-scan fallback for oversized viewports
- Log dedup: FNV-1a 64-bit hash to skip duplicate messages
- Buffer shrink: halve draw buffers after 60 frames of low usage
- Shape geometry hashing for instanced-draw vertex-buffer grouping
- Group member_indices arrays with O(n) rebuild
- Log ring expanded 64→256 entries, added log_filter

Debug build: added --profiling-funcs and -sASSERTIONS flags.
2026-05-03 00:38:45 +02:00

151 lines
4.1 KiB
C

#ifndef UTIL_H
#define UTIL_H
#include <stdint.h>
#include <string.h>
typedef struct vector_t {
uint8_t *data;
int count;
int capacity;
int stride;
} vector_t;
/**
* Zero-initialize a vector with the given element stride.
*
* @param v vector to initialize
* @param stride byte size of each element
*/
static void vec_init(vector_t *v, int stride) {
memset(v, 0, sizeof(*v));
v->stride = stride;
}
/**
* Grow the vector's backing array to at least min_capacity elements.
* Doubles capacity (starting at 8) or uses min_capacity, whichever is larger.
*
* @param v vector to grow
* @param min_capacity minimum element count required
*/
static void vec_grow(vector_t *v, int min_capacity) {
int new_cap = v->capacity ? v->capacity * 2 : 8;
if (new_cap < min_capacity) new_cap = min_capacity;
uint8_t *new_data = (uint8_t*) ALLOC(new_cap * v->stride);
if (!new_data) {
EM_ASM({ console.error("vec_grow: ALLOC failed for %d elements of %d bytes", $0, $1); },
new_cap, v->stride);
return;
}
if (v->data) {
memcpy(new_data, v->data, v->count * v->stride);
FREE(v->data);
}
v->data = new_data;
v->capacity = new_cap;
}
/**
* Append an uninitialized element to the end of the vector. Grows if needed.
*
* @param v vector to push into
* @return pointer to the new (uninitialized) element
*/
static void *vec_push(vector_t *v) {
if (v->count >= v->capacity) vec_grow(v, v->count + 1);
return v->data + (v->count++) * v->stride;
}
/**
* Remove the last element from the vector (decrements count, no free).
*
* @param v vector to pop from
*/
static void vec_pop(vector_t *v) {
if (v->count > 0) v->count--;
}
static void vec_remove_ordered(vector_t *v, int index) {
if (index < 0 || index >= v->count) return;
if (index < v->count - 1) {
memmove(v->data + index * v->stride,
v->data + (index + 1) * v->stride,
(v->count - index - 1) * v->stride);
}
v->count--;
}
// Remove `count` elements at given indices in a single compaction pass.
// Indices must be sorted in ascending order and must be valid.
static void vec_remove_ordered_bulk(vector_t *v, const int *indices, int count) {
if (count <= 0) return;
int write = indices[0];
for (int k = 0; k < count; k++) {
int gap_start = indices[k];
int gap_end = (k + 1 < count) ? indices[k + 1] : v->count;
int keep = gap_end - gap_start - 1;
if (keep > 0) {
memmove(v->data + write * v->stride,
v->data + (gap_start + 1) * v->stride,
(size_t)keep * (size_t)v->stride);
write += keep;
}
}
v->count -= count;
}
static void *vec_insert(vector_t *v, int index) {
if (index < 0 || index > v->count) return NULL;
if (v->count >= v->capacity) vec_grow(v, v->count + 1);
if (index < v->count) {
memmove(v->data + (index + 1) * v->stride,
v->data + index * v->stride,
(v->count - index) * v->stride);
}
v->count++;
return v->data + index * v->stride;
}
/**
* Remove the element at index by swapping in the last element (O(1)).
* Order is not preserved.
*
* @param v vector to remove from
* @param index index of the element to remove
*/
static void vec_remove(vector_t *v, int index) {
if (index < 0 || index >= v->count) return;
if (index < v->count - 1) {
memcpy(v->data + index * v->stride,
v->data + (v->count - 1) * v->stride,
v->stride);
}
v->count--;
}
/**
* Return a pointer to the element at index (no bounds check).
*
* @param v vector to access
* @param index element index
* @return pointer to the element
*/
static void *vec_get(vector_t *v, int index) {
return v->data + index * v->stride;
}
/**
* Free the backing array and reset the vector to empty.
*
* @param v vector to free
*/
static void vec_free(vector_t *v) {
if (v->data) FREE(v->data);
v->data = NULL;
v->count = 0;
v->capacity = 0;
}
#endif