Krita Source Code Documentation
Loading...
Searching...
No Matches
KisSpatialContainer.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2025 Agata Cacko <cacko.azh@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QTransform>
10#include <QPainterPath>
11#include <kis_debug.h>
12#include <kis_algebra_2d.h>
13
14#include <boost/accumulators/accumulators.hpp>
15#include <boost/accumulators/statistics/stats.hpp>
16#include <boost/accumulators/statistics/min.hpp>
17#include <boost/accumulators/statistics/max.hpp>
18
19#include <array>
20#include <QVector2D>
21#include <QVector3D>
22
23#include <QtMath>
24
25#include <config-gsl.h>
26
27#ifdef HAVE_GSL
28#include <gsl/gsl_multimin.h>
29#endif /*HAVE_GSL*/
30
31#include <Eigen/Eigenvalues>
32
33#define SANITY_CHECKS
34
36
37
39
40public:
42 int index;
43 QPointF position;
44
46 SpatialNodeData(int _index, QPointF _position)
47 : index(_index)
48 , position(_position)
49 {}
50 };
51
52public:
53
55 ~SpatialNode() {nodeId = -100;}
56
57 int childIndexForPoint(QPointF position);
58
59public:
60
62 QVector<SpatialNode*> children = {}; // four children // the instance is owned by the parent
63 qreal xPartition {0.0};
64 qreal yPartition {0.0};
65
66 int pointsCount {0};
67 bool isLeaf {true};
68
69 // this area contains all the points
70 // but it isn't necessarily the smallest possible QRectF that contains them
72
73 int nodeId {-1};
74
75};
76
77
78
80
81
82
83KisSpatialContainer::KisSpatialContainer(QRectF startArea, int maxPointsInDict)
84 : m_maxPointsInDict(maxPointsInDict)
85{
87 m_root->xPartition = startArea.x() + startArea.width()/2.0;
88 m_root->yPartition = startArea.y() + startArea.height()/2.0;
90}
91
93{
95 m_root->xPartition = startArea.x() + startArea.width()/2.0;
96 m_root->yPartition = startArea.y() + startArea.height()/2.0;
98
99 initializeWith(points);
100}
101
111
113{
114 clear();
115 delete m_root;
116 m_root = nullptr;
117}
118
119void KisSpatialContainer::initializeFor(int numPoints, QRectF startArea)
120{
121 clear();
122
123 int levels = 1;
124 int leaves = (int)qCeil(numPoints/(double)m_maxPointsInDict);
125 int nodes = leaves;
126 while (nodes > 0) {
127 nodes >>= 2;
128 levels++;
129 }
130
131 initializeLevels(m_root, levels, startArea);
132
133}
134
136{
137 clear();
138
139 for (int i = 0; i < points.length(); i++) {
140 addPoint(i, points[i]);
141 }
142}
143
144void KisSpatialContainer::initializeWithGridPoints(QRectF gridRect, int pixelPrecision)
145{
146 clear();
147 int columnsNum = GridIterationTools::calcGridSize(gridRect.toRect(), pixelPrecision).width();
148 initializeWithGridPointsRec(gridRect, pixelPrecision, m_root, 0, 0, columnsNum);
149}
150
151void KisSpatialContainer::addPoint(int index, QPointF position)
152{
153 addPointRec(index, position, m_root);
154}
155
156void KisSpatialContainer::removePoint(int index, QPointF position)
157{
158 removePointRec(index, position, m_root);
159}
160
161void KisSpatialContainer::movePoint(int index, QPointF positionBefore, QPointF positionAfter)
162{
163 movePointRec(index, positionBefore, positionAfter, m_root);
164}
165
167{
168 if (m_count == 0) {
169 return QVector<QPointF>();
170 }
171
172 QVector<QPointF> response;
173 response.resize(m_count);
174 gatherDataRec(response, m_root);
175 return response;
176
177}
178
179void KisSpatialContainer::findAllInRange(QVector<int> &indexes, QPointF center, qreal range)
180{
181 findAllInRangeRec(indexes, center, range, m_root);
182}
183
185{
186 return m_count;
187}
188
190{
191 return getBoundaryPoint(false, false);
192}
193
195{
196 QPointF topLeft = getTopLeft();
197 QPointF bottomRight = getBoundaryPoint(true, true);
198 return QRectF(topLeft, bottomRight);
199}
200
202{
204
205 // now clean up root
206 m_root->isLeaf = true;
207 m_root->children = {};
208 m_root->pointsCount = 0;
209 m_root->pointsData = {};
210}
211
212void KisSpatialContainer::addPointRec(int index, QPointF position, SpatialNode *node)
213{
214 if (!node) return;
215 if (node->isLeaf) { // leaf
216 SpatialNodeData childData(index, position);
217
218 node->pointsData << childData;
220 m_count++;
221
222 if (node->pointsCount == m_maxPointsInDict) {
223 // gotta make it into a parent node
224 node->xPartition = node->maximumArea.x() + node->maximumArea.width()/2.0;
225 node->yPartition = node->maximumArea.y() + node->maximumArea.height()/2.0;
226 node->children = QVector<SpatialNode*> {nullptr, nullptr, nullptr, nullptr};
227 node->isLeaf = false;
228
229
232
233 for (; it != end; ++it) {
234 int childIndex = node->childIndexForPoint(it->position);
235 if (!node->children[childIndex]) {
236 node->children[childIndex] = createNodeForPoint(it->index, it->position);
237 } else {
238 addPointRec(it->index, it->position, node->children[childIndex]);
239 }
240 }
241 node->pointsData.clear();
242 node->pointsCount = 0;
243
244 } else {
245 node->pointsCount++;
246 }
247 } else { // inner node
248 int childIndex = node->childIndexForPoint(position);
249 if (!node->children[childIndex]) { // no node for points there yet
250 SpatialNode* newChild = createNodeForPoint(index, position);
251 node->children[childIndex] = newChild;
252 } else {
253 addPointRec(index, position, node->children[childIndex]);
254 }
255 }
256}
257
258void KisSpatialContainer::removePointRec(int index, QPointF position, SpatialNode *node)
259{
260 if (!node) return;
261 if (node->isLeaf) {
262
263 auto sameIndex = [index] (const SpatialNodeData& item) {
264 return item.index == index;
265 };
266 // quickest way: move the last item into the place where the found item was; keeping the order isn't necessary
267 QVector<SpatialNodeData>::iterator foundItem = std::find_if(node->pointsData.begin(), node->pointsData.end(), sameIndex);
268 if (foundItem != node->pointsData.end()) {
270 *foundItem = *lastItem;
271 node->pointsData.removeLast();
272 node->pointsCount--;
273 }
274
275 } else {
276 int childIndex = node->childIndexForPoint(position);
277 SpatialNode* child = node->children[childIndex];
278 if (child) {
279 removePointRec(index, position, child);
280 // this below isn't really necessary but keeps the tree tidy by removing no longer needed nodes
281 if (child->isLeaf) {
282 if (child->pointsCount == 0) {
283 // child = empty leaf
284 delete node->children[childIndex];
285 node->children[childIndex] = nullptr;
286 child = nullptr;
287 }
288 } else {
289 bool foundExistingGrandChild = false;
290 for (int i = 0; i < child->children.length(); i++) {
291 if (child->children[i]) {
292 foundExistingGrandChild = true;
293 }
294 }
295 if (!foundExistingGrandChild) {
296 // child = empty parent node
297 delete node->children[childIndex];
298 node->children[childIndex] = nullptr;
299 child = nullptr;
300 }
301 }
302 }
303 }
304}
305
306void KisSpatialContainer::movePointRec(int index, QPointF positionBefore, QPointF positionAfter, SpatialNode *node)
307{
308 if (!node) {
309 return;
310 }
311 if (node->isLeaf) {
312 for (int i = 0; i < node->pointsCount; i++) {
313 if(node->pointsData[i].index == index) {
314 node->pointsData[i].position = positionAfter;
315 }
316 }
317 KisAlgebra2D::accumulateBounds(positionAfter, &node->maximumArea);
318 } else {
319 int childBefore = node->childIndexForPoint(positionBefore);
320 int childAfter = node->childIndexForPoint(positionAfter);
321 if (childBefore == childAfter) {
322 movePointRec(index, positionBefore, positionAfter, node->children[childBefore]);
323 } else {
324 removePointRec(index, positionBefore, node->children[childBefore]);
325 if (!node->children[childAfter]) {
326 node->children[childAfter] = createNodeForPoint(index, positionAfter);
327 } else {
328 addPointRec(index, positionAfter, node->children[childAfter]);
329 }
330 }
331 }
332}
333
335{
336 SpatialNode* newChild = new SpatialNode();
337 newChild->pointsCount = 1;
338 newChild->pointsData = QVector<SpatialNodeData> {SpatialNodeData(index, position)};
339 newChild->maximumArea = QRectF(position, position);
340 newChild->nodeId = m_nextNodeId++;
341 return newChild;
342}
343
344
345bool isInRange(const QPointF &center, qreal range, const SpatialNodeData& data) {
346 if(qAbs(data.position.x() - center.x()) <= range && qAbs(data.position.y() - center.y()) <= range) {
347 if(KisAlgebra2D::norm(data.position - center) <= range) {
348 return true;
349 }
350 }
351 return false;
352}
353
354void KisSpatialContainer::findAllInRangeRec(QVector<int> &indexes, QPointF center, qreal range, SpatialNode *node)
355{
356 if (!node) return;
357 if (node->isLeaf) {
358 for (int i = 0; i < node->pointsCount; i++) {
359 if (isInRange(center, range, node->pointsData[i])) {
360 indexes.append(node->pointsData[i].index);
361 }
362 }
363 } else {
364 QList<int> childrenToVisit;
365 childrenToVisit << node->childIndexForPoint(center + QPointF(range, range));
366 childrenToVisit << node->childIndexForPoint(center + QPointF(range, -range));
367 childrenToVisit << node->childIndexForPoint(center + QPointF(-range, range));
368 childrenToVisit << node->childIndexForPoint(center + QPointF(-range, -range));
369
370 for (int i = 0; i < node->children.length(); i++) {
371 if (childrenToVisit.contains(i)) { // that eliminates all the leaves outside of the area
372 findAllInRangeRec(indexes, center, range, node->children[i]);
373 }
374 }
375 }
376}
377
379{
380 if (!node) return;
381 if (node->isLeaf) {
382 for (int i = 0; i < node->pointsData.size(); i++) {
383 KIS_SAFE_ASSERT_RECOVER(node->pointsData[i].index < vector.length()) { continue; };
384 vector[node->pointsData[i].index] = node->pointsData[i].position;
385 }
386 } else {
387 for (int i = 0; i < node->children.length(); i++) {
388 gatherDataRec(vector, node->children[i]);
389 }
390 }
391}
392
397
399{
400 if (!node) {
401 return;
402 }
403 if (node->isLeaf) {
404 qCritical() << prefix << "* (" << node->nodeId << ") is Leaf?" << node->isLeaf << ppVar(node->pointsCount) << ppVar(node->pointsData.length());
405 for (int i = 0; i < node->pointsCount; i++) {
406 qCritical() << prefix << " - " << node->pointsData[i].index << " | " << node->pointsData[i].position;
407 }
408 } else {
409 qCritical() << prefix << "* (" << node->nodeId << ") is Leaf?" << node->isLeaf << ppVar(node->xPartition) << ppVar(node->yPartition);
410
411 qCritical() << prefix << "0: ";
412 debugWriteOutRec(node->children[0], prefix + "0.");
413 qCritical() << prefix << "1: ";
414 debugWriteOutRec(node->children[1], prefix + "1.");
415 qCritical() << prefix << "2: ";
416 debugWriteOutRec(node->children[2], prefix + "2.");
417 qCritical() << prefix << "3: ";
418 debugWriteOutRec(node->children[3], prefix + "3.");
419 }
420}
421
422std::optional<qreal> KisSpatialContainer::getBoundaryOnAxis(bool positive, bool xAxis, SpatialNode *node)
423{
424 if (!node) {
425 return std::nullopt;
426 }
427
428 if (node->isLeaf) {
429 if (node->pointsData.length() == 0) {
430 return std::nullopt;
431 }
432
433 auto getNum = [xAxis] (const SpatialNodeData& a, const SpatialNodeData& b) {
434 if (xAxis) {
435 return a.position.x() < b.position.x();
436 } else {
437 return a.position.y() < b.position.y();
438 }
439 };
440 if (positive) {
441 QVector<SpatialNodeData>::iterator data = std::max_element(node->pointsData.begin(), node->pointsData.end(), getNum);
442 if (data != node->pointsData.end()) {
443 if (xAxis) {
444 return data->position.x();
445 } else {
446 return data->position.y();
447 }
448 }
449 } else {
450 QVector<SpatialNodeData>::iterator data = std::min_element(node->pointsData.begin(), node->pointsData.end(), getNum);
451
452 if (data != node->pointsData.end()) {
453 if (xAxis) {
454 return data->position.x();
455 } else {
456 return data->position.y();
457 }
458 }
459 }
460 return std::nullopt;
461
462 } else {
463 int diff = positive? 1 : -1;
464 int childIndex1, childIndex2;
465 if (xAxis) {
466 childIndex1 = node->childIndexForPoint(QPointF(node->xPartition + diff, node->yPartition + 1));
467 childIndex2 = node->childIndexForPoint(QPointF(node->xPartition + diff, node->yPartition - 1));
468 } else {
469 childIndex1 = node->childIndexForPoint(QPointF(node->xPartition + 1, node->yPartition + diff));
470 childIndex2 = node->childIndexForPoint(QPointF(node->xPartition - 1, node->yPartition + diff));
471 }
472 std::optional<qreal> response1 = getBoundaryOnAxis(positive, xAxis, node->children[childIndex1]);
473 std::optional<qreal> response2 = getBoundaryOnAxis(positive, xAxis, node->children[childIndex2]);
474
475 if (!response1.has_value() && !response2.has_value()) {
476 // gotta check the other two
477 diff = -diff;
478 if (xAxis) {
479 childIndex1 = node->childIndexForPoint(QPointF(node->xPartition + diff, node->yPartition + 1));
480 childIndex2 = node->childIndexForPoint(QPointF(node->xPartition + diff, node->yPartition - 1));
481 } else {
482 childIndex1 = node->childIndexForPoint(QPointF(node->xPartition + 1, node->yPartition + diff));
483 childIndex2 = node->childIndexForPoint(QPointF(node->xPartition - 1, node->yPartition + diff));
484 }
485 response1 = getBoundaryOnAxis(positive, xAxis, node->children[childIndex1]);
486 response2 = getBoundaryOnAxis(positive, xAxis, node->children[childIndex2]);
487 }
488
489
490 if (!response1.has_value()) {
491 return response2;
492 }
493 if (!response2.has_value()) {
494 return response1;
495 }
496
497 if (positive) {
498 return qMax(response1.value(), response2.value());
499 } else {
500 return qMin(response1.value(), response2.value());
501 }
502 }
503}
504
505QPointF KisSpatialContainer::getBoundaryPoint(bool left, bool top)
506{
507 std::optional<qreal> x = getBoundaryOnAxis(left, true, m_root);
508 std::optional<qreal> y = getBoundaryOnAxis(top, false, m_root);
509
510 QPointF response = QPointF();
511 if (x.has_value()) {
512 response.setX(x.value());
513 }
514 if (y.has_value()) {
515 response.setY(y.value());
516 }
517
518 return response;
519}
520
521void KisSpatialContainer::initializeLevels(SpatialNode *node, int levelsLeft, QRectF area)
522{
524
525 if (levelsLeft > 1) {
526
527 node->xPartition = area.x() + area.width();
528 node->yPartition = area.y() + area.height();
529 QPointF center = QPointF(node->xPartition, node->yPartition);
530 node->isLeaf = false;
531 node->children = {nullptr, nullptr, nullptr, nullptr};
532 for (int i = 0; i < node->children.length(); i++) {
533 node->children[i] = new SpatialNode();
534 node->children[i]->nodeId = m_nextNodeId++;
535 }
536
537 int topLeftChild = node->childIndexForPoint(center + QPointF(-1, -1));
538 initializeLevels(node->children[topLeftChild], levelsLeft - 1, QRectF(area.topLeft(), center));
539
540 int topRightChild = node->childIndexForPoint(center + QPointF(1, -1));
541 initializeLevels(node->children[topRightChild], levelsLeft - 1, QRectF(QPointF(node->xPartition, area.top()), QPointF(area.right(), node->yPartition)));
542
543 int bottomLeftChild = node->childIndexForPoint(center + QPointF(-1, 1));
544 initializeLevels(node->children[bottomLeftChild], levelsLeft - 1, QRectF(QPointF(area.left(), node->yPartition), QPointF(node->xPartition, area.bottom())));
545
546 int bottomRightChild = node->childIndexForPoint(center + QPointF(1, 1));
547 initializeLevels(node->children[bottomRightChild], levelsLeft - 1, QRectF(QPointF(node->xPartition, node->yPartition), QPointF(area.right(), area.bottom())));
548
549 } else {
550 node->isLeaf = true;
551 node->children = {};
552 }
553
554
555}
556
557void KisSpatialContainer::initializeWithGridPointsRec(QRectF gridRect, int pixelPrecision, SpatialNode *node, int startRow, int startColumn, int columnCount)
558{
559 node->maximumArea = gridRect;
560
561 QSize size = GridIterationTools::calcGridSize(gridRect.toRect(), pixelPrecision);
562 int width = size.width();
563 int height = size.height();
564
565 int marginX = int(gridRect.left())%pixelPrecision;
566 marginX = marginX == 0 ? 0 : pixelPrecision - marginX;
567 int marginY = int(gridRect.top())%pixelPrecision;
568 marginY = marginY == 0 ? 0 : pixelPrecision - marginY;
569
570 // ceiling will always be on the corner or outside
571 // floor will always be on the corner or inside
572 QPoint topLeftDividableCeiling = QPoint((qCeil(gridRect.left()/(qreal)pixelPrecision))*pixelPrecision, (qCeil(gridRect.top()/(qreal)pixelPrecision))*pixelPrecision);
573 QPoint topLeftDividableFloor = QPoint((qFloor(gridRect.left()/(qreal)pixelPrecision))*pixelPrecision, (qFloor(gridRect.top()/(qreal)pixelPrecision))*pixelPrecision);
574
575
576 if (width * height <= m_maxPointsInDict) {
577 node->isLeaf = true;
578 node->pointsData.reserve(width * height);
579
580 for (int i = 0; i < width; i++) {
581 for (int j = 0; j < height; j++) {
582
583 int pointIndex = (startRow + j)*columnCount + (startColumn + i);
584
585 QPointF nextPoint = QPointF(topLeftDividableFloor.x() + i * pixelPrecision, topLeftDividableFloor.y() + j * pixelPrecision);
586 if (i == 0) {
587 nextPoint.setX(gridRect.x());
588 }
589 if (j == 0) {
590 nextPoint.setY(gridRect.y());
591 }
592
593 // must be -1, because it's actually last included pixels and gridRect here is QRectF, not QRect
594 if (nextPoint.x() > gridRect.right() - 1) {
595 nextPoint.setX(gridRect.right() - 1);
596 }
597 if (nextPoint.y() > gridRect.bottom() - 1) {
598 nextPoint.setY(gridRect.bottom() - 1);
599 }
600
601 node->pointsData << SpatialNodeData(pointIndex, nextPoint);
602 }
603 }
604 node->pointsCount = node->pointsData.count();
605 m_count += node->pointsCount;
606 node->maximumArea = gridRect;
607
608 } else {
609
610 node->isLeaf = false;
611
612 QPoint bottomRightDividable = QPoint((qFloor(gridRect.right()/(qreal)pixelPrecision))*pixelPrecision, (qFloor(gridRect.bottom()/(qreal)pixelPrecision))*pixelPrecision);
613 QPoint topLeftDividable = topLeftDividableCeiling;
614
615 QPoint sizeDividable = QPoint(bottomRightDividable - topLeftDividable);
616 int xLeftDividable = (sizeDividable.x()) / pixelPrecision / 2;
617 int yTopDividable = (sizeDividable.y()) / pixelPrecision / 2;
618
619 qreal xLeftCenter = topLeftDividable.x() + xLeftDividable*pixelPrecision;
620 qreal yTopCenter = topLeftDividable.y() + yTopDividable*pixelPrecision;
621
622 qreal xRightCenter = topLeftDividable.x() + (xLeftDividable + 1)*pixelPrecision;
623 qreal yBottomCenter = topLeftDividable.y() + (yTopDividable + 1)*pixelPrecision;
624
625
626 int xLeft = width/2;
627 int yTop = height/2;
628
629 if (bottomRightDividable.x() <= topLeftDividable.x()) {
630 // shouldn't really happen for Liquify, but let's have sane values
631 xLeftCenter = gridRect.left() + xLeft*pixelPrecision;
632 xRightCenter = gridRect.left() + (xLeft + 1)*pixelPrecision;
633 }
634
635 if (bottomRightDividable.y() <= topLeftDividable.y()) {
636 yTopCenter = gridRect.top() + yTop*pixelPrecision;
637 yBottomCenter = gridRect.top() + (yTop + 1)*pixelPrecision;
638 }
639
640
641 node->xPartition = (xLeftCenter + xRightCenter)/2;
642 node->yPartition = (yTopCenter + yBottomCenter)/2;
643
644 node->children = {nullptr, nullptr, nullptr, nullptr};
645 for (int i = 0; i < node->children.count(); i++) {
646 node->children[i] = new SpatialNode();
647 node->children[i]->nodeId = m_nextNodeId++;
648 }
649
650 QPointF center = QPointF(node->xPartition, node->yPartition);
651
652 int topLeftChild = node->childIndexForPoint(center + QPointF(-1, -1));
653
654 initializeWithGridPointsRec(QRectF(gridRect.topLeft(), QPointF(xLeftCenter + 1, yTopCenter + 1)), pixelPrecision, node->children[topLeftChild], startRow, startColumn, columnCount);
655
656 int rightStartColumn = startColumn + xLeftDividable + 1 + (topLeftDividableCeiling.x() > gridRect.left() ? 1 : 0);
657 int bottomStartRow = startRow + yTopDividable + 1 + (topLeftDividableCeiling.y() > gridRect.top() ? 1 : 0);
658
659 int topRightChild = node->childIndexForPoint(center + QPointF(1, -1));
660 initializeWithGridPointsRec(QRectF(QPointF(xRightCenter, gridRect.top()), QPointF(gridRect.right(), yTopCenter + 1)), pixelPrecision,
661 node->children[topRightChild], startRow, rightStartColumn, columnCount);
662
663 int bottomLeftChild = node->childIndexForPoint(center + QPointF(-1, 1));
664 initializeWithGridPointsRec(QRectF(QPointF(gridRect.left(), yBottomCenter), QPointF(xLeftCenter + 1, gridRect.bottom())), pixelPrecision,
665 node->children[bottomLeftChild], bottomStartRow, startColumn, columnCount);
666
667 int bottomRightChild = node->childIndexForPoint(center + QPointF(1, 1));
668 initializeWithGridPointsRec(QRectF(QPointF(xRightCenter, yBottomCenter), QPointF(gridRect.right(), gridRect.bottom())), pixelPrecision,
669 node->children[bottomRightChild], bottomStartRow, rightStartColumn, columnCount);
670
671 }
672
673}
674
676{
677 if (!node) return;
678 bool hadChildren = false;
679 for (int i = 0; i < node->children.length(); i++) {
680 if (node->children[i]) {
681 hadChildren = true;
682
683 clearRec(node->children[i]);
684
685 delete node->children[i];
686 node->children[i] = nullptr;
687 }
688 }
689 KIS_SAFE_ASSERT_RECOVER_RETURN(!node->isLeaf || !hadChildren);
690}
691
693{
694 node->isLeaf = from->isLeaf;
695 node->maximumArea = from->maximumArea;
696 node->nodeId = from->nodeId;
697 node->pointsCount = from->pointsCount;
698 node->pointsData = from->pointsData;
699 node->xPartition = from->xPartition;
700 node->yPartition = from->yPartition;
701
702 if (!from->isLeaf) {
703 node->children = {nullptr, nullptr, nullptr, nullptr};
704 for (int i = 0; i < node->children.size(); i++) {
705 if (from->children[i]) {
706 node->children[i] = new SpatialNode();
707 deepCopyData(node->children[i], from->children[i]);
708 }
709 }
710 }
711}
712
714{
715 if (position.x() > xPartition) {
716 if (position.y() > yPartition) {
717 return 0;
718 }
719 return 1;
720 } else {
721 if (position.y() > yPartition) {
722 return 2;
723 }
724 return 3;
725 }
726}
727
KisSpatialContainer::SpatialNode::SpatialNodeData SpatialNodeData
bool isInRange(const QPointF &center, qreal range, const SpatialNodeData &data)
void initializeFor(int numPoints, QRectF startArea)
void debugWriteOutRec(SpatialNode *node, QString prefix)
void findAllInRangeRec(QVector< int > &indexes, QPointF center, qreal range, SpatialNode *node)
void addPoint(int index, QPointF position)
void initializeWith(const QVector< QPointF > &points)
QVector< QPointF > toVector()
void initializeWithGridPointsRec(QRectF gridRect, int pixelPrecision, SpatialNode *node, int startRow, int startColumn, int columnCount)
void removePoint(int index, QPointF position)
void clearRec(SpatialNode *node)
void movePointRec(int index, QPointF positionBefore, QPointF positionAfter, SpatialNode *node)
QPointF getBoundaryPoint(bool left, bool top)
void removePointRec(int index, QPointF position, SpatialNode *node)
SpatialNode * createNodeForPoint(int index, QPointF position)
std::optional< qreal > getBoundaryOnAxis(bool positive, bool xAxis, SpatialNode *node)
void addPointRec(int index, QPointF position, SpatialNode *node)
void gatherDataRec(QVector< QPointF > &vector, SpatialNode *node)
void findAllInRange(QVector< int > &indexes, QPointF center, qreal range)
void initializeWithGridPoints(QRectF gridRect, int pixelPrecision)
void movePoint(int index, QPointF positionBefore, QPointF positionAfter)
void deepCopyData(SpatialNode *node, const SpatialNode *from)
void initializeLevels(SpatialNode *node, int levelsLeft, QRectF area)
KisSpatialContainer(QRectF startArea, int maxPointsInDict=100)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define ppVar(var)
Definition kis_debug.h:155
QSize calcGridSize(const QRect &srcBounds, const int pixelPrecision)
void accumulateBounds(const Point &pt, Rect *bounds)
qreal norm(const T &a)
QVector< SpatialNodeData > pointsData