Implemented a QuadTree and a test

This commit is contained in:
VegOwOtenks 2025-02-28 17:50:36 +01:00
parent a569dd05a6
commit 87f592ea8b
4 changed files with 519 additions and 1 deletions

View file

@ -1 +1,428 @@
#include "QuadTree.h"
#include "../errorcodes.h"
#include <math.h>
#include <stdbool.h>
#include <time.h>
enum QuadTreePosition {
POSITION_TOP_LEFT,
POSITION_TOP_RIGHT,
POSITION_BOTTOM_LEFT,
POSITION_BOTTOM_RIGHT,
};
int QuadTree_Create(QuadTree* tree, scalar width, scalar height, allocator_t* allocator)
{
tree->root = NULL;
tree->width = width;
tree->height = height;
tree->allocator = allocator;
return EXIT_SUCCESS;
}
static enum QuadTreePosition Dimension_LeafPosition(QuadTreeLeaf* leaf, QuadTreeDimension dimension)
{
scalar middle_x = (dimension.right - dimension.left) / 2 + dimension.left;
scalar middle_y = (dimension.bottom - dimension.top) / 2 + dimension.top;
if (middle_x < leaf->x) {
if (middle_y < leaf->y) {
return POSITION_BOTTOM_RIGHT;
} else {
return POSITION_TOP_RIGHT;
}
} else {
if (middle_y < leaf->y) {
return POSITION_BOTTOM_LEFT;
} else {
return POSITION_TOP_LEFT;
}
}
}
static enum QuadTreeNodeType QuadSubTree_GetType(QuadSubTree* tree, enum QuadTreePosition position)
{
switch (position) {
case POSITION_TOP_LEFT:
return tree->top_left_type;
case POSITION_TOP_RIGHT:
return tree->top_right_type;
case POSITION_BOTTOM_LEFT:
return tree->bottom_left_type;
case POSITION_BOTTOM_RIGHT:
return tree->bottom_right_type;
default:
return *(int*) NULL;
}
}
static void QuadSubTree_SetType(QuadSubTree* tree, enum QuadTreePosition position, enum QuadTreeNodeType type)
{
switch (position) {
case POSITION_TOP_LEFT:
tree->top_left_type = type;
break;
case POSITION_TOP_RIGHT:
tree->top_right_type = type;
break;
case POSITION_BOTTOM_LEFT:
tree->bottom_left_type = type;
break;
case POSITION_BOTTOM_RIGHT:
tree->bottom_right_type = type;
break;
}
}
static void QuadSubTree_SetTree(QuadSubTree* tree, enum QuadTreePosition position, QuadSubTree* branch)
{
QuadSubTree_SetType(tree, position, QUADTREENODETYPE_TREE);
switch (position) {
case POSITION_TOP_LEFT:
tree->top_left.tree = branch;
break;
case POSITION_TOP_RIGHT:
tree->top_right.tree = branch;
break;
case POSITION_BOTTOM_LEFT:
tree->bottom_left.tree = branch;
break;
case POSITION_BOTTOM_RIGHT:
tree->bottom_right.tree = branch;
break;
}
}
static QuadSubTree** QuadSubTree_GetTree(QuadSubTree* tree, enum QuadTreePosition position)
{
switch (position) {
case POSITION_TOP_LEFT:
return &tree->top_left.tree;
case POSITION_TOP_RIGHT:
return &tree->top_right.tree;
case POSITION_BOTTOM_LEFT:
return &tree->bottom_left.tree;
case POSITION_BOTTOM_RIGHT:
return &tree->bottom_right.tree;
default:
return NULL;
}
}
static void QuadSubTree_SetLeaf(QuadSubTree* tree, enum QuadTreePosition position, QuadTreeLeaf* leaf)
{
QuadSubTree_SetType(tree, position, QUADTREENODETYPE_LEAF);
switch (position) {
case POSITION_TOP_LEFT:
tree->top_left.leaf = leaf;
break;
case POSITION_TOP_RIGHT:
tree->top_right.leaf = leaf;
break;
case POSITION_BOTTOM_LEFT:
tree->bottom_left.leaf = leaf;
break;
case POSITION_BOTTOM_RIGHT:
tree->bottom_right.leaf = leaf;
break;
}
}
static QuadTreeLeaf* QuadSubTree_GetLeaf(QuadSubTree* tree, enum QuadTreePosition position)
{
QuadSubTree_SetType(tree, position, QUADTREENODETYPE_LEAF);
switch (position) {
case POSITION_TOP_LEFT:
return tree->top_left.leaf;
case POSITION_TOP_RIGHT:
return tree->top_right.leaf;
case POSITION_BOTTOM_LEFT:
return tree->bottom_left.leaf;
case POSITION_BOTTOM_RIGHT:
return tree->bottom_right.leaf;
default:
return NULL;
}
}
static QuadTreeDimension Dimension_Adjust(QuadTreeDimension dimension, enum QuadTreePosition position)
{
scalar middle_x = (dimension.right - dimension.left) / 2 + dimension.left;
scalar middle_y = (dimension.bottom - dimension.top) / 2 + dimension.top;
switch (position) {
case POSITION_TOP_LEFT:
dimension.bottom = middle_y;
dimension.right = middle_x;
break;
case POSITION_TOP_RIGHT:
dimension.left = middle_x;
dimension.bottom = middle_y;
break;
case POSITION_BOTTOM_LEFT:
dimension.right = middle_x;
dimension.top = middle_y;
break;
case POSITION_BOTTOM_RIGHT:
dimension.left = middle_x;
dimension.top = middle_y;
break;
}
return dimension;
}
void QuadSubTree_Create(QuadSubTree* branch)
{
branch->bottom_right_type = QUADTREENODETYPE_EMPTY;
branch->top_right_type = QUADTREENODETYPE_EMPTY;
branch->bottom_left_type = QUADTREENODETYPE_EMPTY;
branch->top_left_type = QUADTREENODETYPE_EMPTY;
}
bool QuadTreeLeaf_Equal(QuadTreeLeaf* self, QuadTreeLeaf* other)
{
return self->y == other->y && self->x == other->x;
}
static int QuadSubTree_Insert(QuadSubTree** root, QuadTreeLeaf* leaf, QuadTreeDimension dimension, allocator_t* allocator)
{
if (*root == NULL) {
QuadSubTree* branch = Allocator_Allocate(allocator, sizeof(QuadSubTree));
if (branch == NULL) return ENOMEM;
QuadSubTree_Create(branch);
enum QuadTreePosition selected_position = Dimension_LeafPosition(leaf, dimension);
QuadSubTree_SetLeaf(branch, selected_position, leaf);
*root = branch;
} else {
enum QuadTreePosition branch_position = Dimension_LeafPosition(leaf, dimension);
enum QuadTreeNodeType branch_type = QuadSubTree_GetType(*root, branch_position);
QuadTreeDimension branch_dimension = Dimension_Adjust(dimension, branch_position);
switch (branch_type) {
case QUADTREENODETYPE_LEAF:
{
QuadTreeLeaf* preserved_leaf = QuadSubTree_GetLeaf(*root, branch_position);
if (QuadTreeLeaf_Equal(preserved_leaf, leaf)) return EEXIST;
QuadSubTree_SetTree(*root, branch_position, NULL);
QuadSubTree** branch_tree = QuadSubTree_GetTree(*root, branch_position);
int insert_code = QuadSubTree_Insert(branch_tree, leaf, branch_dimension, allocator);
if (insert_code) return insert_code;
return QuadSubTree_Insert(branch_tree, preserved_leaf, branch_dimension, allocator);
}
case QUADTREENODETYPE_TREE:
{
QuadSubTree** branch = QuadSubTree_GetTree(*root, branch_position);
return QuadSubTree_Insert(branch, leaf, branch_dimension, allocator);
}
case QUADTREENODETYPE_EMPTY:
QuadSubTree_SetLeaf(*root, branch_position, leaf);
break;
}
}
return EXIT_SUCCESS;
}
int QuadTree_Insert(QuadTree* tree, QuadTreeLeaf* leaf)
{
QuadTreeDimension root_dimension = { 0, 0, tree->width, tree->height };
return QuadSubTree_Insert(&tree->root, leaf, root_dimension, tree->allocator);
}
static int QuadSubTree_ForEachTree(QuadSubTree* tree, QuadTree_TreeCallback action, void* context, QuadTreeDimension dimension)
{
// TODO: Fix me
if (tree->bottom_left_type == QUADTREENODETYPE_TREE) {
QuadTreeDimension d = Dimension_Adjust(dimension, POSITION_BOTTOM_LEFT);
int r = QuadSubTree_ForEachTree(tree->bottom_left.tree, action, context, d);
if (r) return r;
}
if (tree->bottom_right_type == QUADTREENODETYPE_TREE) {
QuadTreeDimension d = Dimension_Adjust(dimension, POSITION_BOTTOM_RIGHT);
int r = QuadSubTree_ForEachTree(tree->bottom_right.tree, action, context, d);
if (r) return r;
}
if (tree->top_left_type == QUADTREENODETYPE_TREE) {
QuadTreeDimension d = Dimension_Adjust(dimension, POSITION_TOP_LEFT);
int r = QuadSubTree_ForEachTree(tree->top_left.tree, action, context, d);
if (r) return r;
}
if (tree->top_right_type == QUADTREENODETYPE_TREE) {
QuadTreeDimension d = Dimension_Adjust(dimension, POSITION_TOP_RIGHT);
int r = QuadSubTree_ForEachTree(tree->top_right.tree, action, context, d);
if (r) return r;
}
int action_result = action(context, tree, dimension);
if (action_result) return action_result;
return EXIT_SUCCESS;
}
int QuadTree_ForEachTree(QuadTree* tree, QuadTree_TreeCallback action, void* context)
{
QuadTreeDimension root_dimension = { 0, 0, tree->width, tree->height };
return QuadSubTree_ForEachTree(tree->root, action, context, root_dimension);
}
int QuadSubTree_ForEachLeaf(QuadSubTree* tree, QuadTree_LeafCallback action, void* context)
{
enum QuadTreePosition positions[] = {
POSITION_TOP_LEFT,
POSITION_TOP_RIGHT,
POSITION_BOTTOM_LEFT,
POSITION_BOTTOM_RIGHT,
};
for (size_t i = 0; i < sizeof(positions) / sizeof(positions[0]); i++) {
enum QuadTreeNodeType type = QuadSubTree_GetType(tree, positions[i]);
switch (type) {
case QUADTREENODETYPE_TREE:
{
QuadSubTree** subtree = QuadSubTree_GetTree(tree, positions[i]);
int result = QuadSubTree_ForEachLeaf(*subtree, action, context);
if (result != EXIT_SUCCESS) return result;
break;
}
case QUADTREENODETYPE_LEAF:
{
QuadTreeLeaf* leaf = QuadSubTree_GetLeaf(tree, positions[i]);
int result = action(context, leaf);
if (result != EXIT_SUCCESS) return result;
break;
}
case QUADTREENODETYPE_EMPTY:
break;
}
}
return EXIT_SUCCESS;
}
int QuadTree_ForEachLeaf(QuadTree* tree, QuadTree_LeafCallback action, void* context)
{
return QuadSubTree_ForEachLeaf(tree->root, action, context);
}
bool Dimension_IsInRadiusOfLeaf(QuadTreeDimension dimension, QuadTreeLeaf point, scalar radius)
{
// is inside x radius?
if (true
&& (point.x + radius) > dimension.left
&& (point.x - radius) < dimension.right
) {
// is inside y radius?
if (true
&& (point.y + radius) > dimension.top
&& (point.y - radius) < dimension.bottom
) {
return true;
}
}
return false;
}
int QuadSubTree_ForEachLeafInRadius(QuadSubTree* tree, QuadTree_LeafCallback action, void* context, QuadTreeDimension dimension, QuadTreeLeaf point, scalar radius)
{
enum QuadTreePosition positions[] = {
POSITION_TOP_LEFT,
POSITION_TOP_RIGHT,
POSITION_BOTTOM_LEFT,
POSITION_BOTTOM_RIGHT,
};
for (size_t i = 0; i < sizeof(positions) / sizeof(positions[0]); i++) {
enum QuadTreePosition position = positions[i];
enum QuadTreeNodeType type = QuadSubTree_GetType(tree, position);
switch (type) {
case QUADTREENODETYPE_TREE:
{
QuadTreeDimension branch_dimension = Dimension_Adjust(dimension, position);
QuadSubTree** branch = QuadSubTree_GetTree(tree, position);
if (Dimension_IsInRadiusOfLeaf(branch_dimension, point, radius)) {
int result = QuadSubTree_ForEachLeafInRadius(*branch, action, context, branch_dimension, point, radius);
if (result != EXIT_SUCCESS) return result;
}
break;
}
case QUADTREENODETYPE_LEAF:
{
QuadTreeLeaf* leaf = QuadSubTree_GetLeaf(tree, position);
if (QuadTreeLeaf_Distance(&point, leaf) <= radius) {
int result = action(context, leaf);
if (result != EXIT_SUCCESS) return result;
}
break;
}
case QUADTREENODETYPE_EMPTY:
break;
}
}
return EXIT_SUCCESS;
}
scalar QuadTreeLeaf_Distance(QuadTreeLeaf* self, QuadTreeLeaf* other)
{
return sqrt(pow(self->x - other->x, 2) + pow(self->y - other->y, 2));
}
int QuadTree_ForEachLeafInRadius(QuadTree* tree, QuadTree_LeafCallback action, void* context, QuadTreeLeaf point, scalar radius)
{
QuadTreeDimension root_dimension = { 0, 0, tree->width, tree->height };
return QuadSubTree_ForEachLeafInRadius(tree->root, action, context, root_dimension, point, radius);
}
int QuadSubTree_Destroy(void* allocator, QuadSubTree* tree, QuadTreeDimension dimension)
{
return Allocator_Free(allocator, tree, sizeof(*tree));
}
void QuadTree_Destroy(QuadTree* tree)
{
QuadTree_ForEachTree(tree, QuadSubTree_Destroy, tree->allocator);
}

View file

@ -1,8 +1,66 @@
#ifndef UTILITIEC_QUADTREE_H
#define UTILITIEC_QUADTREE_H
#include <stddef.h>
#include "../allocator-interface/allocator-interface.h"
typedef double scalar;
enum QuadTreeNodeType {
QUADTREENODETYPE_TREE,
QUADTREENODETYPE_LEAF,
QUADTREENODETYPE_EMPTY,
};
typedef struct QuadSubTree_s QuadSubTree;
typedef struct QuadTreeLeaf {
scalar x;
scalar y;
} QuadTreeLeaf;
union QuadTreeNode {
QuadSubTree* tree;
QuadTreeLeaf* leaf;
};
struct QuadSubTree_s {
enum QuadTreeNodeType top_left_type :2;
enum QuadTreeNodeType top_right_type :2;
enum QuadTreeNodeType bottom_left_type :2;
enum QuadTreeNodeType bottom_right_type :2;
union QuadTreeNode top_left;
union QuadTreeNode top_right;
union QuadTreeNode bottom_left;
union QuadTreeNode bottom_right;
};
typedef struct QuadTree_s {
QuadSubTree* root;
allocator_t* allocator;
scalar width;
scalar height;
} QuadTree;
int QuadTree_Create(QuadTree* tree, scalar width, scalar height, allocator_t* allocator);
int QuadTree_Insert(QuadTree* tree, QuadTreeLeaf* leaf);
typedef struct QuadTreeDimension_s {
scalar top;
scalar left;
scalar right;
scalar bottom;
} QuadTreeDimension;
typedef int (*QuadTree_TreeCallback) (void* context, QuadSubTree* tree, QuadTreeDimension dimension);
int QuadTree_ForEachTree(QuadTree* tree, QuadTree_TreeCallback action, void* context);
typedef int (*QuadTree_LeafCallback) (void* context, QuadTreeLeaf* leaf);
int QuadTree_ForEachLeaf(QuadTree* tree, QuadTree_LeafCallback action, void* context);
int QuadTree_ForEachLeafInRadius(QuadTree* tree, QuadTree_LeafCallback action, void* context, QuadTreeLeaf point, scalar radius);
void QuadTree_Destroy(QuadTree* tree);
scalar QuadTreeLeaf_Distance(QuadTreeLeaf* self, QuadTreeLeaf* other);
#endif

View file

@ -52,3 +52,11 @@ target_link_libraries(Subprocess-test
pointers
allocator-interface
)
add_executable(QuadTree-test QuadTree.test.c)
target_link_libraries(QuadTree-test
QuadTree
rand
pointers
allocator-interface
)

25
tests/QuadTree.test.c Normal file
View file

@ -0,0 +1,25 @@
#include "../src/rand/xoshiro256.h"
#include <stdio.h>
#include "../src/QuadTree/QuadTree.h"
static QuadTree tree;
typedef struct Particle_s {
QuadTreeLeaf position;
} Particle;
static Particle particles[100];
int main()
{
QuadTree_Create(&tree, 10000, 10000, NULL);
Xoshiro256State rand_state = { 0, 10, 0, 0 };
for (size_t i = 0; i < sizeof(particles) / sizeof(particles[0]); i++) {
particles[i].position.x = xoshiro256_next(&rand_state) % 10000;
particles[i].position.y = xoshiro256_next(&rand_state) % 10000;
printf("%f %f\n", particles[i].position.y, particles[i].position.x);
QuadTree_Insert(&tree, &particles[i].position);
}
}