/* See license.txt in the root of this project. */ # include # include # include # include "auxkdtree2d.h" # include "auxmemory.h" /*tex This started out as a small library embedded in lmtkdtreelib but at some point I decided to make two specialized ones instead. This is one of the things an llm can do when you feed it a proper file. This kind of kdtree code is after all rather well known. In the process I removed some iterative variants and passing a \quote {management} structure that kept track of the used dimensions. We also avoid modulo operations on the dimension. */ # define epsilon 1e-9 static inline double kd2_get(kd2_point p, int dim) { return (dim == 0) ? p->x : p->y; } static inline int kd2_equal(kd2_point_data *a, kd2_point_data *b) { return (fabs(a->x - b->x) < epsilon) && (fabs(a->y - b->y) < epsilon); } static inline int kd2_exact(kd2_point_data *a, kd2_point_data *b) { return (a->x == b->x) && (a->y == b->y); } static inline double kd2_squared_distance(kd2_point_data *a, kd2_point_data *b) { double dx = a->x - b->x; double dy = a->y - b->y; return (dx * dx) + (dy * dy); } static kd2_node kd2_create(kd2_point p) { kd2_node temp = (kd2_node) lmt_memory_malloc(sizeof(kd2_node_data)); if (temp) { temp->left = NULL; temp->right = NULL; temp->point = *p; } return temp; } static kd2_node kd2_min_of_three(kd2_node middle, kd2_node left, kd2_node right, int target_dim) { kd2_node min = middle; if (left && kd2_get(&(left->point), target_dim) < kd2_get(&(min->point), target_dim)) { min = left; } if (right && kd2_get(&(right->point), target_dim) < kd2_get(&(min->point), target_dim)) { min = right; } return min; } static kd2_node kd2_min_entry(kd2_node root, int target_dim, int current_dim) { if (! root) { return NULL; } else { int next_dim = (current_dim == 0) ? 1 : 0; if (current_dim == target_dim) { if (root->left) { return kd2_min_entry(root->left, target_dim, next_dim); } else { return root; } } else { return kd2_min_of_three( root, kd2_min_entry(root->left, target_dim, next_dim), kd2_min_entry(root->right, target_dim, next_dim), target_dim ); } } } static kd2_node kd2_min(kd2_node root, int target_dim) { return kd2_min_entry(root, target_dim, 0); } static kd2_node kd2_insert_entry(kd2_node root, kd2_point p, int current_dim) { if (! root) { return kd2_create(p); } else { int next_dim = (current_dim == 0) ? 1 : 0; if (kd2_get(p, current_dim) < kd2_get(&(root->point), current_dim)) { root->left = kd2_insert_entry(root->left, p, next_dim); } else { root->right = kd2_insert_entry(root->right, p, next_dim); } return root; } } kd2_node kd2_insert(kd2_node root, kd2_point p) { return kd2_insert_entry(root, p, 0); } static kd2_node kd2_delete_entry(kd2_node root, kd2_point p, int current_dim) { if (! root) { return NULL; } else { int next_dim = (current_dim == 0) ? 1 : 0; if (kd2_equal(&(root->point), p)) { if (! root->left && ! root->right) { lmt_memory_free(root); return NULL; } else if (root->right) { kd2_node min = kd2_min(root->right, current_dim); root->point = min->point; root->right = kd2_delete_entry(root->right, &(min->point), next_dim); } else if (root->left) { kd2_node min = kd2_min(root->left, current_dim); root->point = min->point; root->right = kd2_delete_entry(root->left, &(min->point), next_dim); root->left = NULL; } } else if (kd2_get(p, current_dim) < kd2_get(&(root->point), current_dim)) { root->left = kd2_delete_entry(root->left, p, next_dim); } else { root->right = kd2_delete_entry(root->right, p, next_dim); } return root; } } kd2_node kd2_delete(kd2_node root, kd2_point p) { return kd2_delete_entry(root, p, 0); } static void kd2_nearest_entry(kd2_node root, kd2_point p, int current_dim, kd2_node *nearest, double *distance) { if (root) { kd2_node first = NULL; kd2_node second = NULL; double d = kd2_squared_distance(&(root->point), p); if (! *nearest || d < *distance) { *distance = d; *nearest = root; } // if (kd2_get(p, current_dim) < kd2_get(&(root->point), current_dim)) { double axis_dist = kd2_get(p, current_dim) - kd2_get(&(root->point), current_dim); if (axis_dist < 0) { first = root->left; second = root->right; } else { first = root->right; second = root->left; } int next_dim = (current_dim == 0) ? 1 : 0; kd2_nearest_entry(first, p, next_dim, nearest, distance); // double axis_dist = kd2_get(p, current_dim) - kd2_get(&(root->point), current_dim); axis_dist = axis_dist * axis_dist; if (axis_dist < *distance) { kd2_nearest_entry(second, p, next_dim, nearest, distance); } } } kd2_node kd2_nearest(kd2_node root, kd2_point p) { kd2_node nearest = NULL; double distance = INFINITY; kd2_nearest_entry(root, p, 0, &nearest, &distance); return nearest; } void kd2_flush(kd2_node root) { if (root) { kd2_flush(root->left); kd2_flush(root->right); lmt_memory_free(root); } } static kd2_node kd2_found_entry(kd2_node root, kd2_point p, int current_dim) { if (! root) { return NULL; } else { int next_dim = (current_dim == 0) ? 1 : 0; if (kd2_equal(&(root->point), p)) { return root; } else if (kd2_get(p, current_dim) < kd2_get(&(root->point), current_dim)) { return kd2_found_entry(root->left, p, next_dim); } else { return kd2_found_entry(root->right, p, next_dim); } } } kd2_node kd2_found(kd2_node root, kd2_point p) { return kd2_found_entry(root, p, 0); } static kd2_node kd2_nearestindex_entry(kd2_node root, kd2_point p, unsigned depth, kd2_node nearest, double * distance) { if (root) { kd2_node first = NULL; kd2_node second = NULL; double dx = p->x - root->point.x; double dy = p->y - root->point.y; double d = dx * dx + dy * dy; double delta = root->point.axis == 1 ? dx : dy; if (d < *distance) { *distance = d; nearest = root; } if (delta <= 0) { first = root->left; second = root->right; } else { first = root->right; second = root->left; } nearest = kd2_nearestindex_entry(first, p, depth + 1, nearest, distance); if (delta * delta < *distance) { nearest = kd2_nearestindex_entry(second, p, depth + 1, nearest, distance); } } return nearest; } kd2_node kd2_nearestindex(kd2_node root, kd2_point p) { double distance = INFINITY; return kd2_nearestindex_entry(root, p, 0, root, &distance); }