things
This commit is contained in:
parent
4aefe8b42d
commit
94837ed410
41
.clang-format
Normal file
41
.clang-format
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# linux kernel style formatting
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 8
|
||||||
|
UseTab: AlignWithSpaces
|
||||||
|
|
||||||
|
BreakBeforeBraces: Linux
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
ColumnLimit: 85
|
||||||
|
|
||||||
|
InsertBraces: true
|
||||||
|
SortIncludes: Never
|
||||||
|
BinPackParameters: false
|
||||||
|
BinPackArguments: false
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SeparateDefinitionBlocks: Always
|
||||||
|
AlignAfterOpenBracket: BlockIndent
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
|
||||||
|
AlignConsecutiveDeclarations:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: true
|
||||||
|
PadOperators: false
|
||||||
|
|
||||||
|
AlignConsecutiveMacros:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: true
|
||||||
|
|
||||||
|
AlignConsecutiveBitFields:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: true
|
||||||
|
|
||||||
|
AlignConsecutiveAssignments:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: true
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,8 @@
|
|||||||
microgui
|
microgui
|
||||||
|
ugui
|
||||||
*.o
|
*.o
|
||||||
test/test
|
test/test
|
||||||
**/compile_commands.json
|
**/compile_commands.json
|
||||||
**/.cache
|
**/.cache
|
||||||
test
|
test
|
||||||
|
raylib/*
|
||||||
|
108
ARCHITECTURE.md
Normal file
108
ARCHITECTURE.md
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
## High level overview
|
||||||
|
|
||||||
|
Under the hood every element has an id, this id allows the library to store state
|
||||||
|
between frames.
|
||||||
|
Elements are also cached such that when the ui tree is rebuilt at the beginning of
|
||||||
|
every frame the element data structure doesn't have to be rebuilt.
|
||||||
|
|
||||||
|
Elements are arranged in a tree, nodes are container elements that can contain other
|
||||||
|
elements, leafs are elements that cannot contain other elements.
|
||||||
|
|
||||||
|
Every element has a size and a position, containers also have to keep track of their
|
||||||
|
layout information and some other state.
|
||||||
|
|
||||||
|
Elements can push commands into the draw stack, which is a structure that contains
|
||||||
|
all the draw commands that the user application has to perform do display the ui
|
||||||
|
correctly, such commands include drawing lines, rectangles, sprites, text, etc.
|
||||||
|
|
||||||
|
|
||||||
|
```text
|
||||||
|
+-----------+
|
||||||
|
| ug_init() |
|
||||||
|
+-----+-----+
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
+---------v----------+
|
||||||
|
|ug_input_keyboard() |
|
||||||
|
|ug_input_mouse() <----+
|
||||||
|
|ug_input_clipboard()| |
|
||||||
|
| ... | |
|
||||||
|
+---------+----------+ |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
+-------v--------+ |
|
||||||
|
|ug_frame_begin()| |
|
||||||
|
+-------+--------+ |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
+---------v----------+ |
|
||||||
|
|ug_window_start() | |
|
||||||
|
+---->ug_container_start()| |
|
||||||
|
| |ug_div_start() | |
|
||||||
|
| | ... | |
|
||||||
|
| +---------+----------+ |
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
multiple +--------v---------+ |
|
||||||
|
times |ug_layout_row() | |
|
||||||
|
| |ug_layout_column()| |
|
||||||
|
| |ug_layout_float() | |
|
||||||
|
| | ... | |
|
||||||
|
| +--------+---------+ |
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
| +------v------+ |
|
||||||
|
| |ug_button() | |
|
||||||
|
| |ug_text_box()| |
|
||||||
|
| |ug_slider() | |
|
||||||
|
| | ... | |
|
||||||
|
| +------+------+ |
|
||||||
|
| | |
|
||||||
|
+--------------+ |
|
||||||
|
| |
|
||||||
|
+--------v---------+ |
|
||||||
|
|ug_window_end() | |
|
||||||
|
|ug_container_end()| |
|
||||||
|
|ug_div_end() | |
|
||||||
|
| ... | |
|
||||||
|
+--------+---------+ |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
+------v-------+ |
|
||||||
|
|ug_frame_end()| |
|
||||||
|
+------+-------+ |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
+------v-------+ |
|
||||||
|
|user draws the| |
|
||||||
|
| ui +-------+
|
||||||
|
+------+-------+
|
||||||
|
|
|
||||||
|
|quit
|
||||||
|
|
|
||||||
|
+------v-------+
|
||||||
|
| ug_destroy() |
|
||||||
|
+--------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
How elements determine if they have focus or not
|
||||||
|
|
||||||
|
```C
|
||||||
|
// in begin_{container} code
|
||||||
|
calculate focus
|
||||||
|
set has_focus property
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// in the element code
|
||||||
|
if(PARENT_HAS_FOCUS()) {
|
||||||
|
update stuff
|
||||||
|
} else {
|
||||||
|
fast path to return
|
||||||
|
}
|
||||||
|
```
|
14
Makefile
Normal file
14
Makefile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
CFLAGS = -Wall -Wextra -pedantic -std=c11 -g -Iraylib/src
|
||||||
|
CC = gcc
|
||||||
|
LDFLAGS = -Lraylib/src -lm
|
||||||
|
|
||||||
|
all: ugui
|
||||||
|
|
||||||
|
raylib/src/libraylib.a: raylib/src/Makefile
|
||||||
|
cd raylib/src; $(MAKE) PLATFORM=PLATFORM_DESKTOP
|
||||||
|
|
||||||
|
ugui: ugui.o vectree.o raylib/src/libraylib.a
|
||||||
|
|
||||||
|
ugui.o: ugui.c ugui.h
|
||||||
|
|
||||||
|
vectree.o: vectree.c ugui.h
|
219
cache.c
Normal file
219
cache.c
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
// LRU cache:
|
||||||
|
/*
|
||||||
|
* The cache uses a pool (array) containing all the elements and a hash table
|
||||||
|
* associating the position in the pool with the element id
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ugui.h"
|
||||||
|
|
||||||
|
// To have less collisions TABLE_SIZE has to be larger than the cache size
|
||||||
|
#define CACHE_SIZE 265
|
||||||
|
#define TABLE_SIZE (CACHE_SIZE * 1.5f)
|
||||||
|
#define CACHE_NCYCLES (CACHE_SIZE * 2 / 3)
|
||||||
|
#define CACHE_BSIZE (((CACHE_SIZE + 0x3f) & (~0x3f)) >> 6)
|
||||||
|
#define CACHE_BRESET(b) \
|
||||||
|
for (int i = 0; i < CACHE_BSIZE; b[i++] = 0) \
|
||||||
|
;
|
||||||
|
#define CACHE_BSET(b, x) b[(x) >> 6] |= (uint64_t)1 << ((x)&63)
|
||||||
|
#define CACHE_BTEST(b, x) (b[(x) >> 6] & ((uint64_t)1 << ((x)&63)))
|
||||||
|
|
||||||
|
/* Hash Table Implementation ----------------------------------------------------- */
|
||||||
|
|
||||||
|
#define HASH_MAXSIZE 4096
|
||||||
|
|
||||||
|
// hash table (id -> index)
|
||||||
|
typedef struct {
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t index;
|
||||||
|
} IdElem;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t items, size, exp;
|
||||||
|
IdElem bucket[];
|
||||||
|
} IdTable;
|
||||||
|
|
||||||
|
IdTable *table_create(uint32_t size)
|
||||||
|
{
|
||||||
|
if (!size || size > HASH_MAXSIZE) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* round to the greater power of two */
|
||||||
|
/* FIXME: check for intger overflow here */
|
||||||
|
uint32_t exp = 32 - __builtin_clz(size - 1);
|
||||||
|
size = 1 << (exp);
|
||||||
|
/* FIXME: check for intger overflow here */
|
||||||
|
IdTable *ht = malloc(sizeof(IdTable) + sizeof(IdElem) * size);
|
||||||
|
if (ht) {
|
||||||
|
ht->items = 0;
|
||||||
|
ht->size = size;
|
||||||
|
memset(ht->bucket, 0, sizeof(IdTable) * size);
|
||||||
|
}
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
|
||||||
|
void table_destroy(IdTable *ht)
|
||||||
|
{
|
||||||
|
if (ht) {
|
||||||
|
free(ht);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and return the element by pointer
|
||||||
|
IdElem *table_search(IdTable *ht, uint32_t id)
|
||||||
|
{
|
||||||
|
if (!ht) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this case id is the hash
|
||||||
|
uint32_t idx = id % ht->size;
|
||||||
|
|
||||||
|
for (uint32_t x = 0, i; x < ht->size; x++) {
|
||||||
|
i = (idx + x) % ht->size;
|
||||||
|
if (ht->bucket[i].id == 0 || ht->bucket[i].id == id) {
|
||||||
|
return &(ht->bucket[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this simply overrides the found item
|
||||||
|
IdElem *table_insert(IdTable *ht, IdElem entry)
|
||||||
|
{
|
||||||
|
IdElem *r = table_search(ht, entry.id);
|
||||||
|
if (r != NULL) {
|
||||||
|
if (r->id != 0) {
|
||||||
|
ht->items++;
|
||||||
|
}
|
||||||
|
*r = entry;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdElem *table_remove(IdTable *ht, uint32_t id)
|
||||||
|
{
|
||||||
|
if (!ht) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
IdElem *r = table_search(ht, id);
|
||||||
|
if (r) {
|
||||||
|
r->id = 0;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cache Implementation ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
// Every CACHE_CYCLES operations mark not-present the unused elements
|
||||||
|
#define CACHE_CYCLE(c) \
|
||||||
|
do { \
|
||||||
|
if (++(c->cycles) > CACHE_NCYCLES) { \
|
||||||
|
for (int i = 0; i < CACHE_BSIZE; i++) { \
|
||||||
|
c->present[i] &= c->used[i]; \
|
||||||
|
c->used[i] = 0; \
|
||||||
|
} \
|
||||||
|
c->cycles = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
IdTable *table;
|
||||||
|
UgElem *array;
|
||||||
|
uint64_t *present, *used;
|
||||||
|
int cycles;
|
||||||
|
} UgElemCache;
|
||||||
|
|
||||||
|
/* FIXME: check for allocation errors */
|
||||||
|
UgElemCache ug_cache_init(void)
|
||||||
|
{
|
||||||
|
IdTable *t = table_create(TABLE_SIZE);
|
||||||
|
UgElem *a = malloc(sizeof(UgElem) * CACHE_SIZE);
|
||||||
|
uint64_t *p = malloc(sizeof(uint64_t) * CACHE_BSIZE);
|
||||||
|
uint64_t *u = malloc(sizeof(uint64_t) * CACHE_BSIZE);
|
||||||
|
CACHE_BRESET(p);
|
||||||
|
CACHE_BRESET(u);
|
||||||
|
return (UgElemCache) {.table = t, .array = a, .present = p, .used = u, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ug_cache_free(UgElemCache *cache)
|
||||||
|
{
|
||||||
|
if (cache) {
|
||||||
|
table_destroy(cache->table);
|
||||||
|
free(cache->array);
|
||||||
|
free(cache->present);
|
||||||
|
free(cache->used);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const UgElem *ug_cache_search(UgElemCache *cache, uint32_t id)
|
||||||
|
{
|
||||||
|
if (!cache) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
IdElem *r;
|
||||||
|
r = table_search(cache->table, id);
|
||||||
|
/* MISS */
|
||||||
|
if (!r || id != r->id) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* MISS, the data is not valid (not present) */
|
||||||
|
if (!CACHE_BTEST(cache->present, r->index)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* HIT, set as recently used */
|
||||||
|
CACHE_BSET(cache->used, r->index);
|
||||||
|
return (&cache->array[r->index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look for a free spot in the present bitmap and return its index */
|
||||||
|
/* If there is no free space left then just return the first position */
|
||||||
|
int ug_cache_get_free_spot(UgElemCache *cache)
|
||||||
|
{
|
||||||
|
if (!cache) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int x = 0;
|
||||||
|
for (int b = 0; b < CACHE_BSIZE; b++) {
|
||||||
|
if (cache->present[b] == 0) {
|
||||||
|
x = 64;
|
||||||
|
} else {
|
||||||
|
x = __builtin_clzll(cache->present[b]);
|
||||||
|
}
|
||||||
|
x = 64 - x;
|
||||||
|
if (!CACHE_BTEST(cache->present, x + 64 * b)) {
|
||||||
|
return x + 64 * b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UgElem *ug_cache_insert_at(UgElemCache *cache, const UgElem *g, uint32_t index)
|
||||||
|
{
|
||||||
|
if (!cache) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
UgElem *spot = NULL;
|
||||||
|
|
||||||
|
/* Set used and present */
|
||||||
|
CACHE_BSET(cache->present, index);
|
||||||
|
CACHE_BSET(cache->used, index);
|
||||||
|
CACHE_CYCLE(cache);
|
||||||
|
spot = &(cache->array[index]);
|
||||||
|
*spot = *g;
|
||||||
|
IdElem e = {.id = g->id, .index = index};
|
||||||
|
if (!table_insert(cache->table, e)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return spot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert an element in the cache
|
||||||
|
const UgElem *ug_cache_insert(UgElemCache *cache, const UgElem *g, int32_t *index)
|
||||||
|
{
|
||||||
|
*index = ug_cache_get_free_spot(cache);
|
||||||
|
return ug_cache_insert_at(cache, g, *index);
|
||||||
|
}
|
||||||
|
|
5
compile_flags.txt
Normal file
5
compile_flags.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-Iraylib/src
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-pedantic
|
||||||
|
-std=c11
|
348
stuff/vectree.h
Normal file
348
stuff/vectree.h
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
#ifndef _VECTREE_H
|
||||||
|
#define _VECTREE_H
|
||||||
|
|
||||||
|
#ifdef VTREE_DTYPE
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int size, elements;
|
||||||
|
VTREE_DTYPE *vector;
|
||||||
|
int *refs;
|
||||||
|
} Vtree;
|
||||||
|
|
||||||
|
int vtree_init(Vtree *tree, unsigned int size);
|
||||||
|
int vtree_pack(Vtree *tree);
|
||||||
|
int vtree_resize(Vtree *tree, unsigned int newsize);
|
||||||
|
int vtree_add(Vtree *tree, VTREE_DTYPE elem, int parent);
|
||||||
|
int vtree_prune(Vtree *tree, int ref);
|
||||||
|
int vtree_subtree_size(Vtree *tree, int ref);
|
||||||
|
int vtree_children_it(Vtree *tree, int parent, int *cursor);
|
||||||
|
int vtree_level_order_it(Vtree *tree, int ref, int **queue_p, int *cursor);
|
||||||
|
int vtree_destroy(Vtree *tree);
|
||||||
|
|
||||||
|
#ifdef VTREE_IMPL
|
||||||
|
|
||||||
|
#define IS_VALID_REF(t, r) ((r) >= 0 && (r) < (t)->size)
|
||||||
|
#define REF_IS_PRESENT(t, r) ((t)->refs[r] >= 0)
|
||||||
|
|
||||||
|
int vtree_init(Vtree *tree, unsigned int size)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->vector = malloc(sizeof(VTREE_DTYPE) * size);
|
||||||
|
if (tree->vector == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->refs = malloc(sizeof(int) * size);
|
||||||
|
if (tree->refs == NULL) {
|
||||||
|
free(tree->vector);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set all refs to -1, meaning invalid (free) element
|
||||||
|
for (unsigned int i = 0; i < size; i++) {
|
||||||
|
tree->refs[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill vector with zeroes
|
||||||
|
memset(tree->vector, 0, size * sizeof(VTREE_DTYPE));
|
||||||
|
|
||||||
|
tree->size = size;
|
||||||
|
tree->elements = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vtree_destroy(Vtree *tree)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tree->vector);
|
||||||
|
free(tree->refs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vtree_pack(Vtree *tree)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add a PACKED flag to skip this
|
||||||
|
|
||||||
|
int free_spot = -1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == -1) {
|
||||||
|
free_spot = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find a item that can be packed
|
||||||
|
if (free_spot >= 0 && tree->refs[i] >= 0) {
|
||||||
|
int old_ref = i;
|
||||||
|
|
||||||
|
// move the item
|
||||||
|
tree->vector[free_spot] = tree->vector[i];
|
||||||
|
tree->refs[free_spot] = tree->refs[i];
|
||||||
|
|
||||||
|
tree->vector[i] = (VTREE_DTYPE){0};
|
||||||
|
tree->refs[i] = -1;
|
||||||
|
|
||||||
|
// and move all references
|
||||||
|
for (int x = 0; x < tree->size; x++) {
|
||||||
|
if (tree->refs[x] == old_ref) {
|
||||||
|
tree->refs[x] = free_spot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark the free spot as used
|
||||||
|
free_spot = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vtree_resize(Vtree *tree, unsigned int newsize)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return error when shrinking with too many elements
|
||||||
|
if ((int)newsize < tree->elements) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack the vector when shrinking to avoid data loss
|
||||||
|
if ((int)newsize < tree->size) {
|
||||||
|
//if (vtree_pack(tree) < 0) {
|
||||||
|
// return -1;
|
||||||
|
//}
|
||||||
|
// TODO: allow shrinking, since packing destroys all references
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VTREE_DTYPE *newvec = realloc(tree->vector, newsize * sizeof(VTREE_DTYPE));
|
||||||
|
if (newvec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *newrefs = realloc(tree->refs, newsize * sizeof(int));
|
||||||
|
if (newrefs == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->vector = newvec;
|
||||||
|
tree->refs = newrefs;
|
||||||
|
if ((int)newsize > tree->size) {
|
||||||
|
for (int i = tree->size; i < (int)newsize; i++) {
|
||||||
|
tree->vector[i] = (VTREE_DTYPE){0};
|
||||||
|
tree->refs[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->size = newsize;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add an element to the tree, return it's ref
|
||||||
|
int vtree_add(Vtree *tree, VTREE_DTYPE elem, int parent)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid parent
|
||||||
|
if (!IS_VALID_REF(tree, parent)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no space left
|
||||||
|
if (tree->elements >= tree->size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the parent exists
|
||||||
|
// if there are no elements in the tree the first add will set the root
|
||||||
|
if (!REF_IS_PRESENT(tree, parent) && tree->elements != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the first free spot
|
||||||
|
int free_spot = -1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == -1) {
|
||||||
|
free_spot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (free_spot < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally add the element
|
||||||
|
tree->vector[free_spot] = elem;
|
||||||
|
tree->refs[free_spot] = parent;
|
||||||
|
tree->elements++;
|
||||||
|
|
||||||
|
return free_spot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prune the tree starting from the ref
|
||||||
|
// returns the number of pruned elements
|
||||||
|
int vtree_prune(Vtree *tree, int ref)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_VALID_REF(tree, ref)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!REF_IS_PRESENT(tree, ref)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->vector[ref] = (VTREE_DTYPE){0};
|
||||||
|
tree->refs[ref] = -1;
|
||||||
|
|
||||||
|
int count = 1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == ref) {
|
||||||
|
count += vtree_prune(tree, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the size of the subtree starting from ref
|
||||||
|
int vtree_subtree_size(Vtree *tree, int ref)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_VALID_REF(tree, ref)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!REF_IS_PRESENT(tree, ref)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
// only root has the reference to itself
|
||||||
|
if (tree->refs[i] == ref && ref != i) {
|
||||||
|
count += vtree_subtree_size(tree, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through the first level children, use a cursor like strtok_r
|
||||||
|
int vtree_children_it(Vtree *tree, int parent, int *cursor)
|
||||||
|
{
|
||||||
|
if (tree == NULL || cursor == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the cursor is out of bounds then we are done for sure
|
||||||
|
if (!IS_VALID_REF(tree, *cursor)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// same for the parent, if it's invalid it can't have children
|
||||||
|
if (!IS_VALID_REF(tree, parent) || !REF_IS_PRESENT(tree, parent)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the first child, update the cursor and return the ref
|
||||||
|
for (int i = *cursor; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == parent) {
|
||||||
|
*cursor = i + 1;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no children are found return -1
|
||||||
|
*cursor = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterates trough every leaf of the subtree in the following manner
|
||||||
|
* node [x], x: visit order
|
||||||
|
* [0]
|
||||||
|
* / | \
|
||||||
|
* / [2] [3]
|
||||||
|
* [1] |
|
||||||
|
* / \ [6]
|
||||||
|
* [4] [5]
|
||||||
|
*/
|
||||||
|
int vtree_level_order_it(Vtree *tree, int ref, int **queue_p, int *cursor)
|
||||||
|
{
|
||||||
|
if (tree == NULL || queue_p == NULL || cursor == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *queue = *queue_p;
|
||||||
|
|
||||||
|
// TODO: this could also be done when adding or removing elements
|
||||||
|
// first call, create a ref array ordered like we desire
|
||||||
|
if (queue == NULL) {
|
||||||
|
*cursor = 0;
|
||||||
|
// create a queue of invalid refs, size is the worst case
|
||||||
|
queue = malloc(sizeof(int) * tree->size);
|
||||||
|
if (queue == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
queue[i] = -1;
|
||||||
|
}
|
||||||
|
*queue_p = queue;
|
||||||
|
|
||||||
|
// iterate through the queue appending found children
|
||||||
|
int pos = 0, off = 0;
|
||||||
|
do {
|
||||||
|
//printf ("ref=%d\n", ref);
|
||||||
|
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == ref) {
|
||||||
|
queue[pos++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;ref == queue[off] && off < tree->size; off++);
|
||||||
|
ref = queue[off];
|
||||||
|
|
||||||
|
} while (IS_VALID_REF(tree, ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
//PRINT_ARR(queue, tree->size);
|
||||||
|
//return -1;
|
||||||
|
|
||||||
|
// on successive calls just iterate through the queue until we find an
|
||||||
|
// invalid ref
|
||||||
|
int ret = queue[(*cursor)++];
|
||||||
|
if (!IS_VALID_REF(tree, ret)) {
|
||||||
|
free(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif // VTREE_IMPL
|
||||||
|
|
||||||
|
#endif // VTREE_DTYPE
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
61
ugui.c
Normal file
61
ugui.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "ugui.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
UgCtx ctx;
|
||||||
|
ug_init(&ctx);
|
||||||
|
|
||||||
|
InitWindow(800, 450, "Ugui Test");
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while (!WindowShouldClose()) {
|
||||||
|
ug_begin_frame(&ctx);
|
||||||
|
|
||||||
|
// drawing
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(BLACK);
|
||||||
|
DrawText(
|
||||||
|
"Congrats! You created your first window!",
|
||||||
|
190,
|
||||||
|
200,
|
||||||
|
20,
|
||||||
|
LIGHTGRAY
|
||||||
|
);
|
||||||
|
EndDrawing();
|
||||||
|
|
||||||
|
ug_end_frame(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWindow();
|
||||||
|
|
||||||
|
ug_destroy(&ctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ug_init(UgCtx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ug_tree_init(&ctx->tree, 10);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ug_destroy(UgCtx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ug_tree_destroy(&ctx->tree);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
63
ugui.h
Normal file
63
ugui.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef _UGUI_H
|
||||||
|
#define _UGUI_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t x, y, w, h;
|
||||||
|
} UgRect;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t x, y;
|
||||||
|
} UgPoint;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t r, g, b, a;
|
||||||
|
} UgColor;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ETYPE_NONE = 0,
|
||||||
|
ETYPE_BUTTON,
|
||||||
|
ETYPE_TEXT,
|
||||||
|
ETYPE_SCROLL,
|
||||||
|
ETYPE_SLIDER,
|
||||||
|
} UgElemType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t id;
|
||||||
|
UgRect rec;
|
||||||
|
union {
|
||||||
|
uint32_t type_int;
|
||||||
|
UgElemType type;
|
||||||
|
};
|
||||||
|
} UgElem;
|
||||||
|
|
||||||
|
// TODO: add a packed flag
|
||||||
|
// TODO: add a fill index to skip some searching for free spots
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int size, elements;
|
||||||
|
uint32_t *vector; // vector of element ids
|
||||||
|
int *refs, *ordered_refs;
|
||||||
|
} UgTree;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UgTree tree;
|
||||||
|
} UgCtx;
|
||||||
|
|
||||||
|
// tree implementation
|
||||||
|
int ug_tree_init(UgTree *tree, unsigned int size);
|
||||||
|
int ug_tree_pack(UgTree *tree);
|
||||||
|
int ug_tree_resize(UgTree *tree, unsigned int newsize);
|
||||||
|
int ug_tree_add(UgTree *tree, uint32_t elem, int parent);
|
||||||
|
int ug_tree_prune(UgTree *tree, int ref);
|
||||||
|
int ug_tree_subtree_size(UgTree *tree, int ref);
|
||||||
|
int ug_tree_children_it(UgTree *tree, int parent, int *cursor);
|
||||||
|
int ug_tree_level_order_it(UgTree *tree, int ref, int *cursor);
|
||||||
|
int ug_tree_destroy(UgTree *tree);
|
||||||
|
|
||||||
|
int ug_init(UgCtx *ctx);
|
||||||
|
int ug_destroy(UgCtx *ctx);
|
||||||
|
|
||||||
|
#endif // _UGUI_H
|
||||||
|
|
335
vectree.c
Normal file
335
vectree.c
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ugui.h"
|
||||||
|
|
||||||
|
#define IS_VALID_REF(t, r) ((r) >= 0 && (r) < (t)->size)
|
||||||
|
#define REF_IS_PRESENT(t, r) ((t)->refs[r] >= 0)
|
||||||
|
|
||||||
|
int ug_tree_init(UgTree *tree, unsigned int size)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->vector = malloc(sizeof(UgElem) * size);
|
||||||
|
if (tree->vector == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->refs = malloc(sizeof(int) * size);
|
||||||
|
if (tree->refs == NULL) {
|
||||||
|
free(tree->vector);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ordered refs are used in the iterators
|
||||||
|
tree->ordered_refs = malloc(sizeof(int) * size);
|
||||||
|
if (tree->ordered_refs == NULL) {
|
||||||
|
free(tree->vector);
|
||||||
|
free(tree->refs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set all refs to -1, meaning invalid (free) element
|
||||||
|
for (unsigned int i = 0; i < size; i++) {
|
||||||
|
tree->refs[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill vector with zeroes
|
||||||
|
memset(tree->vector, 0, size * sizeof(UgElem));
|
||||||
|
|
||||||
|
tree->size = size;
|
||||||
|
tree->elements = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ug_tree_destroy(UgTree *tree)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tree->vector);
|
||||||
|
free(tree->refs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ug_tree_pack(UgTree *tree)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add a PACKED flag to skip this
|
||||||
|
|
||||||
|
int free_spot = -1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == -1) {
|
||||||
|
free_spot = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find a item that can be packed
|
||||||
|
if (free_spot >= 0 && tree->refs[i] >= 0) {
|
||||||
|
int old_ref = i;
|
||||||
|
|
||||||
|
// move the item
|
||||||
|
tree->vector[free_spot] = tree->vector[i];
|
||||||
|
tree->refs[free_spot] = tree->refs[i];
|
||||||
|
|
||||||
|
tree->vector[i] = 0;
|
||||||
|
tree->refs[i] = -1;
|
||||||
|
|
||||||
|
// and move all references
|
||||||
|
for (int x = 0; x < tree->size; x++) {
|
||||||
|
if (tree->refs[x] == old_ref) {
|
||||||
|
tree->refs[x] = free_spot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark the free spot as used
|
||||||
|
free_spot = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ug_tree_resize(UgTree *tree, unsigned int newsize)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return error when shrinking with too many elements
|
||||||
|
if ((int)newsize < tree->elements) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack the vector when shrinking to avoid data loss
|
||||||
|
if ((int)newsize < tree->size) {
|
||||||
|
// if (ug_tree_pack(tree) < 0) {
|
||||||
|
// return -1;
|
||||||
|
// }
|
||||||
|
// TODO: allow shrinking, since packing destroys all references
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t *newvec = realloc(tree->vector, newsize * sizeof(uint32_t));
|
||||||
|
if (newvec == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *newrefs = realloc(tree->refs, newsize * sizeof(int));
|
||||||
|
if (newrefs == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *neworrefs = realloc(tree->ordered_refs, newsize * sizeof(int));
|
||||||
|
if (neworrefs == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->vector = newvec;
|
||||||
|
tree->refs = newrefs;
|
||||||
|
tree->ordered_refs = neworrefs;
|
||||||
|
if ((int)newsize > tree->size) {
|
||||||
|
for (int i = tree->size; i < (int)newsize; i++) {
|
||||||
|
tree->vector[i] = 0;
|
||||||
|
tree->refs[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->size = newsize;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add an element to the tree, return it's ref
|
||||||
|
int ug_tree_add(UgTree *tree, uint32_t elem, int parent)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid parent
|
||||||
|
if (!IS_VALID_REF(tree, parent)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no space left
|
||||||
|
if (tree->elements >= tree->size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the parent exists
|
||||||
|
// if there are no elements in the tree the first add will set the root
|
||||||
|
if (!REF_IS_PRESENT(tree, parent) && tree->elements != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the first free spot
|
||||||
|
int free_spot = -1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == -1) {
|
||||||
|
free_spot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (free_spot < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally add the element
|
||||||
|
tree->vector[free_spot] = elem;
|
||||||
|
tree->refs[free_spot] = parent;
|
||||||
|
tree->elements++;
|
||||||
|
|
||||||
|
return free_spot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prune the tree starting from the ref
|
||||||
|
// returns the number of pruned elements
|
||||||
|
int ug_tree_prune(UgTree *tree, int ref)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_VALID_REF(tree, ref)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!REF_IS_PRESENT(tree, ref)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->vector[ref] = 0;
|
||||||
|
tree->refs[ref] = -1;
|
||||||
|
|
||||||
|
int count = 1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == ref) {
|
||||||
|
count += ug_tree_prune(tree, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the size of the subtree starting from ref
|
||||||
|
int ug_tree_subtree_size(UgTree *tree, int ref)
|
||||||
|
{
|
||||||
|
if (tree == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_VALID_REF(tree, ref)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!REF_IS_PRESENT(tree, ref)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 1;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
// only root has the reference to itself
|
||||||
|
if (tree->refs[i] == ref && ref != i) {
|
||||||
|
count += ug_tree_subtree_size(tree, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through the first level children, use a cursor like strtok_r
|
||||||
|
int ug_tree_children_it(UgTree *tree, int parent, int *cursor)
|
||||||
|
{
|
||||||
|
if (tree == NULL || cursor == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the cursor is out of bounds then we are done for sure
|
||||||
|
if (!IS_VALID_REF(tree, *cursor)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// same for the parent, if it's invalid it can't have children
|
||||||
|
if (!IS_VALID_REF(tree, parent) || !REF_IS_PRESENT(tree, parent)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the first child, update the cursor and return the ref
|
||||||
|
for (int i = *cursor; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == parent) {
|
||||||
|
*cursor = i + 1;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no children are found return -1
|
||||||
|
*cursor = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterates trough every leaf of the subtree in the following manner
|
||||||
|
* node [x], x: visit order
|
||||||
|
* [0]
|
||||||
|
* / | \
|
||||||
|
* / [2] [3]
|
||||||
|
* [1] |
|
||||||
|
* / \ [6]
|
||||||
|
* [4] [5]
|
||||||
|
*/
|
||||||
|
int ug_tree_level_order_it(UgTree *tree, int ref, int *cursor)
|
||||||
|
{
|
||||||
|
if (tree == NULL || cursor == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *queue = tree->ordered_refs;
|
||||||
|
|
||||||
|
// TODO: this could also be done when adding or removing elements
|
||||||
|
// first call, create a ref array ordered like we desire
|
||||||
|
if (queue == NULL) {
|
||||||
|
*cursor = 0;
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
queue[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through the queue appending found children
|
||||||
|
int pos = 0, off = 0;
|
||||||
|
do {
|
||||||
|
// printf ("ref=%d\n", ref);
|
||||||
|
|
||||||
|
for (int i = 0; i < tree->size; i++) {
|
||||||
|
if (tree->refs[i] == ref) {
|
||||||
|
queue[pos++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; ref == queue[off] && off < tree->size; off++)
|
||||||
|
;
|
||||||
|
ref = queue[off];
|
||||||
|
|
||||||
|
} while (IS_VALID_REF(tree, ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRINT_ARR(queue, tree->size);
|
||||||
|
// return -1;
|
||||||
|
|
||||||
|
// on successive calls just iterate through the queue until we find an
|
||||||
|
// invalid ref, if the user set the cursor to -1 it means it has found what
|
||||||
|
// he needed, so free
|
||||||
|
if (*cursor < 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (IS_VALID_REF(tree, *cursor)) {
|
||||||
|
return queue[(*cursor)++];
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user