File indexing completed on 2025-04-27 04:04:22
0001 /* 0002 * SPDX-FileCopyrightText: (C) 2022 Silas Henrique <silash35@gmail.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "kdtree.h" 0008 0009 // Helper function that returns the squared distance between two points 0010 double squaredDistance(QPointF &point1, QPointF &point2) 0011 { 0012 double dx = fabs(point1.x() - point2.x()); 0013 double dy = fabs(point1.y() - point2.y()); 0014 0015 return (dx * dx + dy * dy); 0016 } 0017 0018 // Helper function that returns the node closest to the Pivot 0019 KdNode *calculateClosest(KdNode *pivot, KdNode *p1, KdNode *p2) 0020 { 0021 if (p1 == nullptr) { 0022 return p2; 0023 } 0024 if (p2 == nullptr) { 0025 return p1; 0026 } 0027 0028 double distanceToP1 = squaredDistance(pivot->point, p1->point); 0029 double distanceToP2 = squaredDistance(pivot->point, p2->point); 0030 0031 if (distanceToP1 < distanceToP2) { 0032 return p1; 0033 } else { 0034 return p2; 0035 } 0036 } 0037 0038 // KdNode 0039 0040 KdNode::KdNode(QPointF p, QVariantMap d) 0041 : point{p} 0042 , data{d} 0043 { 0044 } 0045 0046 void KdNode::insert(KdNode *newNode, unsigned int axis) 0047 { 0048 // Toggle axis at each depth 0049 unsigned int newAxis = axis == 0 ? 1 : 0; 0050 0051 double newPointCurrentAxis = axis == 0 ? newNode->point.x() : newNode->point.y(); 0052 double thisPointCurrentAxis = axis == 0 ? this->point.x() : this->point.y(); 0053 0054 if (newPointCurrentAxis < thisPointCurrentAxis) { 0055 if (this->left == nullptr) { 0056 this->left.reset(newNode); 0057 } else { 0058 this->left->insert(newNode, newAxis); 0059 } 0060 } else { 0061 if (this->right == nullptr) { 0062 this->right.reset(newNode); 0063 } else { 0064 this->right->insert(newNode, newAxis); 0065 } 0066 } 0067 } 0068 0069 KdNode *KdNode::findNearest(KdNode *pivot, unsigned int axis) 0070 { 0071 // Toggle axis at each depth 0072 unsigned int newAxis = axis == 0 ? 1 : 0; 0073 0074 KdNode *best = this; 0075 KdNode *nextNode = nullptr; 0076 KdNode *oppositeNode = nullptr; 0077 0078 double pivotPointCurrentAxis = axis == 0 ? pivot->point.x() : pivot->point.y(); 0079 double thisPointCurrentAxis = axis == 0 ? this->point.x() : this->point.y(); 0080 0081 if (pivotPointCurrentAxis < thisPointCurrentAxis) { 0082 nextNode = this->left.get(); 0083 oppositeNode = this->right.get(); 0084 } else { 0085 nextNode = this->right.get(); 0086 oppositeNode = this->left.get(); 0087 } 0088 0089 if (nextNode != nullptr) { 0090 best = calculateClosest(pivot, nextNode->findNearest(pivot, newAxis), this); 0091 } 0092 0093 if (oppositeNode != nullptr && squaredDistance(pivot->point, best->point) > fabs(pivotPointCurrentAxis - thisPointCurrentAxis)) { 0094 best = calculateClosest(pivot, oppositeNode->findNearest(pivot, newAxis), best); 0095 } 0096 0097 return best; 0098 } 0099 0100 // KdTree 0101 0102 void KdTree::clear() 0103 { 0104 this->root.reset(); 0105 } 0106 0107 void KdTree::insert(double x, double y, QVariantMap &data) 0108 { 0109 QPointF newPoint = QPointF(x, y); 0110 KdNode *newNode = new KdNode(newPoint, data); 0111 if (this->root == nullptr) { 0112 this->root.reset(newNode); 0113 } else { 0114 this->root->insert(newNode, 0); 0115 } 0116 } 0117 0118 KdNode *KdTree::findNearest(double x, double y) 0119 { 0120 if (this->root == nullptr) { 0121 return nullptr; 0122 } else { 0123 QPointF pivotPoint = QPointF(x, y); 0124 KdNode pivotNode = KdNode(pivotPoint, QVariantMap()); 0125 return this->root->findNearest(&pivotNode, 0); 0126 } 0127 } 0128 0129 bool KdTree::isEmpty() const 0130 { 0131 return this->root == nullptr; 0132 }