File indexing completed on 2024-05-12 15:54:50

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 }