Krita Source Code Documentation
Loading...
Searching...
No Matches
KisSpatialContainer Class Reference

#include <KisSpatialContainer.h>

Classes

struct  SpatialNode
 

Public Member Functions

void addPoint (int index, QPointF position)
 
void clear ()
 
int count ()
 
void debugWriteOut ()
 
QRectF exactBounds ()
 
void findAllInRange (QVector< int > &indexes, QPointF center, qreal range)
 
QPointF getTopLeft ()
 
void initializeFor (int numPoints, QRectF startArea)
 
void initializeWith (const QVector< QPointF > &points)
 
void initializeWithGridPoints (QRectF gridRect, int pixelPrecision)
 
 KisSpatialContainer (const KisSpatialContainer &rhs)
 
 KisSpatialContainer (QRectF startArea, int maxPointsInDict=100)
 
 KisSpatialContainer (QRectF startArea, QVector< QPointF > &points)
 
void movePoint (int index, QPointF positionBefore, QPointF positionAfter)
 
void removePoint (int index, QPointF position)
 
QVector< QPointF > toVector ()
 
 ~KisSpatialContainer ()
 

Private Member Functions

void addPointRec (int index, QPointF position, SpatialNode *node)
 
void clearRec (SpatialNode *node)
 
SpatialNodecreateNodeForPoint (int index, QPointF position)
 
void debugWriteOutRec (SpatialNode *node, QString prefix)
 
void deepCopyData (SpatialNode *node, const SpatialNode *from)
 
void findAllInRangeRec (QVector< int > &indexes, QPointF center, qreal range, SpatialNode *node)
 
void gatherDataRec (QVector< QPointF > &vector, SpatialNode *node)
 
std::optional< qreal > getBoundaryOnAxis (bool positive, bool xAxis, SpatialNode *node)
 
QPointF getBoundaryPoint (bool left, bool top)
 
void initializeLevels (SpatialNode *node, int levelsLeft, QRectF area)
 
void initializeWithGridPointsRec (QRectF gridRect, int pixelPrecision, SpatialNode *node, int startRow, int startColumn, int columnCount)
 
void movePointRec (int index, QPointF positionBefore, QPointF positionAfter, SpatialNode *node)
 
void removePointRec (int index, QPointF position, SpatialNode *node)
 

Private Attributes

int m_count {0}
 
int m_maxPointsInDict = {100}
 
int m_nextNodeId = {0}
 
SpatialNodem_root {nullptr}
 

Friends

class KisSpatialContainerTest
 

Detailed Description

Definition at line 23 of file KisSpatialContainer.h.

Constructor & Destructor Documentation

◆ KisSpatialContainer() [1/3]

KisSpatialContainer::KisSpatialContainer ( QRectF startArea,
int maxPointsInDict = 100 )

◆ KisSpatialContainer() [2/3]

KisSpatialContainer::KisSpatialContainer ( QRectF startArea,
QVector< QPointF > & points )

Definition at line 92 of file KisSpatialContainer.cpp.

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}
void initializeWith(const QVector< QPointF > &points)

References initializeWith(), m_nextNodeId, m_root, KisSpatialContainer::SpatialNode::nodeId, KisSpatialContainer::SpatialNode::xPartition, and KisSpatialContainer::SpatialNode::yPartition.

◆ KisSpatialContainer() [3/3]

KisSpatialContainer::KisSpatialContainer ( const KisSpatialContainer & rhs)

Definition at line 102 of file KisSpatialContainer.cpp.

103{
104 m_root = rhs.m_root;
105 m_root = new SpatialNode();
107 m_count = rhs.m_count;
110}
void deepCopyData(SpatialNode *node, const SpatialNode *from)

References deepCopyData(), m_count, m_maxPointsInDict, m_nextNodeId, and m_root.

◆ ~KisSpatialContainer()

KisSpatialContainer::~KisSpatialContainer ( )

Definition at line 112 of file KisSpatialContainer.cpp.

113{
114 clear();
115 delete m_root;
116 m_root = nullptr;
117}

References clear(), and m_root.

Member Function Documentation

◆ addPoint()

void KisSpatialContainer::addPoint ( int index,
QPointF position )

Definition at line 151 of file KisSpatialContainer.cpp.

152{
153 addPointRec(index, position, m_root);
154}
void addPointRec(int index, QPointF position, SpatialNode *node)

References addPointRec(), and m_root.

◆ addPointRec()

void KisSpatialContainer::addPointRec ( int index,
QPointF position,
SpatialNode * node )
private

Definition at line 212 of file KisSpatialContainer.cpp.

213{
214 if (!node) return;
215 if (node->isLeaf) { // leaf
216 SpatialNodeData childData(index, position);
217
218 node->pointsData << childData;
219 KisAlgebra2D::accumulateBounds(position, &node->maximumArea);
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
230 QVector<SpatialNodeData>::iterator it = node->pointsData.begin();
231 QVector<SpatialNodeData>::iterator end = node->pointsData.end();
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}
SpatialNode * createNodeForPoint(int index, QPointF position)
void accumulateBounds(const Point &pt, Rect *bounds)

References KisAlgebra2D::accumulateBounds(), addPointRec(), KisSpatialContainer::SpatialNode::childIndexForPoint(), KisSpatialContainer::SpatialNode::children, createNodeForPoint(), KisSpatialContainer::SpatialNode::isLeaf, m_count, m_maxPointsInDict, KisSpatialContainer::SpatialNode::maximumArea, KisSpatialContainer::SpatialNode::pointsCount, KisSpatialContainer::SpatialNode::pointsData, KisSpatialContainer::SpatialNode::xPartition, and KisSpatialContainer::SpatialNode::yPartition.

◆ clear()

◆ clearRec()

void KisSpatialContainer::clearRec ( SpatialNode * node)
private

Definition at line 675 of file KisSpatialContainer.cpp.

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}
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128

References KisSpatialContainer::SpatialNode::children, clearRec(), KisSpatialContainer::SpatialNode::isLeaf, and KIS_SAFE_ASSERT_RECOVER_RETURN.

◆ count()

int KisSpatialContainer::count ( )

Definition at line 184 of file KisSpatialContainer.cpp.

185{
186 return m_count;
187}

References m_count.

◆ createNodeForPoint()

KisSpatialContainer::SpatialNode * KisSpatialContainer::createNodeForPoint ( int index,
QPointF position )
private

Definition at line 334 of file KisSpatialContainer.cpp.

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}
KisSpatialContainer::SpatialNode::SpatialNodeData SpatialNodeData

References m_nextNodeId, KisSpatialContainer::SpatialNode::maximumArea, KisSpatialContainer::SpatialNode::nodeId, KisSpatialContainer::SpatialNode::pointsCount, and KisSpatialContainer::SpatialNode::pointsData.

◆ debugWriteOut()

void KisSpatialContainer::debugWriteOut ( )

Definition at line 393 of file KisSpatialContainer.cpp.

394{
396}
void debugWriteOutRec(SpatialNode *node, QString prefix)

References debugWriteOutRec(), and m_root.

◆ debugWriteOutRec()

void KisSpatialContainer::debugWriteOutRec ( SpatialNode * node,
QString prefix )
private

Definition at line 398 of file KisSpatialContainer.cpp.

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}
#define ppVar(var)
Definition kis_debug.h:155

References KisSpatialContainer::SpatialNode::children, debugWriteOutRec(), KisSpatialContainer::SpatialNode::isLeaf, KisSpatialContainer::SpatialNode::nodeId, KisSpatialContainer::SpatialNode::pointsCount, KisSpatialContainer::SpatialNode::pointsData, ppVar, KisSpatialContainer::SpatialNode::xPartition, and KisSpatialContainer::SpatialNode::yPartition.

◆ deepCopyData()

void KisSpatialContainer::deepCopyData ( SpatialNode * node,
const SpatialNode * from )
private

Definition at line 692 of file KisSpatialContainer.cpp.

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}

References KisSpatialContainer::SpatialNode::children, deepCopyData(), KisSpatialContainer::SpatialNode::isLeaf, KisSpatialContainer::SpatialNode::maximumArea, KisSpatialContainer::SpatialNode::nodeId, KisSpatialContainer::SpatialNode::pointsCount, KisSpatialContainer::SpatialNode::pointsData, KisSpatialContainer::SpatialNode::xPartition, and KisSpatialContainer::SpatialNode::yPartition.

◆ exactBounds()

QRectF KisSpatialContainer::exactBounds ( )

Definition at line 194 of file KisSpatialContainer.cpp.

195{
196 QPointF topLeft = getTopLeft();
197 QPointF bottomRight = getBoundaryPoint(true, true);
198 return QRectF(topLeft, bottomRight);
199}
QPointF getBoundaryPoint(bool left, bool top)

References getBoundaryPoint(), and getTopLeft().

◆ findAllInRange()

void KisSpatialContainer::findAllInRange ( QVector< int > & indexes,
QPointF center,
qreal range )

Definition at line 179 of file KisSpatialContainer.cpp.

180{
181 findAllInRangeRec(indexes, center, range, m_root);
182}
void findAllInRangeRec(QVector< int > &indexes, QPointF center, qreal range, SpatialNode *node)

References findAllInRangeRec(), and m_root.

◆ findAllInRangeRec()

void KisSpatialContainer::findAllInRangeRec ( QVector< int > & indexes,
QPointF center,
qreal range,
SpatialNode * node )
private

Definition at line 354 of file KisSpatialContainer.cpp.

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}
bool isInRange(const QPointF &center, qreal range, const SpatialNodeData &data)

References KisSpatialContainer::SpatialNode::childIndexForPoint(), KisSpatialContainer::SpatialNode::children, findAllInRangeRec(), isInRange(), KisSpatialContainer::SpatialNode::isLeaf, KisSpatialContainer::SpatialNode::pointsCount, and KisSpatialContainer::SpatialNode::pointsData.

◆ gatherDataRec()

void KisSpatialContainer::gatherDataRec ( QVector< QPointF > & vector,
SpatialNode * node )
private

Definition at line 378 of file KisSpatialContainer.cpp.

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}
void gatherDataRec(QVector< QPointF > &vector, SpatialNode *node)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126

References KisSpatialContainer::SpatialNode::children, gatherDataRec(), KisSpatialContainer::SpatialNode::isLeaf, KIS_SAFE_ASSERT_RECOVER, and KisSpatialContainer::SpatialNode::pointsData.

◆ getBoundaryOnAxis()

std::optional< qreal > KisSpatialContainer::getBoundaryOnAxis ( bool positive,
bool xAxis,
SpatialNode * node )
private

Definition at line 422 of file KisSpatialContainer.cpp.

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}
std::optional< qreal > getBoundaryOnAxis(bool positive, bool xAxis, SpatialNode *node)

References KisSpatialContainer::SpatialNode::childIndexForPoint(), KisSpatialContainer::SpatialNode::children, getBoundaryOnAxis(), KisSpatialContainer::SpatialNode::isLeaf, KisSpatialContainer::SpatialNode::pointsData, KisSpatialContainer::SpatialNode::SpatialNodeData::position, KisSpatialContainer::SpatialNode::xPartition, and KisSpatialContainer::SpatialNode::yPartition.

◆ getBoundaryPoint()

QPointF KisSpatialContainer::getBoundaryPoint ( bool left,
bool top )
private

Definition at line 505 of file KisSpatialContainer.cpp.

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}

References getBoundaryOnAxis(), and m_root.

◆ getTopLeft()

QPointF KisSpatialContainer::getTopLeft ( )

Definition at line 189 of file KisSpatialContainer.cpp.

190{
191 return getBoundaryPoint(false, false);
192}

References getBoundaryPoint().

◆ initializeFor()

void KisSpatialContainer::initializeFor ( int numPoints,
QRectF startArea )

Definition at line 119 of file KisSpatialContainer.cpp.

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}
void initializeLevels(SpatialNode *node, int levelsLeft, QRectF area)

References clear(), initializeLevels(), m_maxPointsInDict, and m_root.

◆ initializeLevels()

void KisSpatialContainer::initializeLevels ( SpatialNode * node,
int levelsLeft,
QRectF area )
private

Definition at line 521 of file KisSpatialContainer.cpp.

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}

References KisSpatialContainer::SpatialNode::childIndexForPoint(), KisSpatialContainer::SpatialNode::children, initializeLevels(), KisSpatialContainer::SpatialNode::isLeaf, KIS_SAFE_ASSERT_RECOVER_RETURN, m_nextNodeId, KisSpatialContainer::SpatialNode::xPartition, and KisSpatialContainer::SpatialNode::yPartition.

◆ initializeWith()

void KisSpatialContainer::initializeWith ( const QVector< QPointF > & points)

Definition at line 135 of file KisSpatialContainer.cpp.

136{
137 clear();
138
139 for (int i = 0; i < points.length(); i++) {
140 addPoint(i, points[i]);
141 }
142}
void addPoint(int index, QPointF position)

References addPoint(), and clear().

◆ initializeWithGridPoints()

void KisSpatialContainer::initializeWithGridPoints ( QRectF gridRect,
int pixelPrecision )

Definition at line 144 of file KisSpatialContainer.cpp.

145{
146 clear();
147 int columnsNum = GridIterationTools::calcGridSize(gridRect.toRect(), pixelPrecision).width();
148 initializeWithGridPointsRec(gridRect, pixelPrecision, m_root, 0, 0, columnsNum);
149}
void initializeWithGridPointsRec(QRectF gridRect, int pixelPrecision, SpatialNode *node, int startRow, int startColumn, int columnCount)
QSize calcGridSize(const QRect &srcBounds, const int pixelPrecision)

References GridIterationTools::calcGridSize(), clear(), initializeWithGridPointsRec(), and m_root.

◆ initializeWithGridPointsRec()

void KisSpatialContainer::initializeWithGridPointsRec ( QRectF gridRect,
int pixelPrecision,
SpatialNode * node,
int startRow,
int startColumn,
int columnCount )
private

Definition at line 557 of file KisSpatialContainer.cpp.

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}
int size(const Forest< T > &forest)
Definition KisForest.h:1232

References GridIterationTools::calcGridSize(), KisSpatialContainer::SpatialNode::childIndexForPoint(), KisSpatialContainer::SpatialNode::children, initializeWithGridPointsRec(), KisSpatialContainer::SpatialNode::isLeaf, m_count, m_maxPointsInDict, m_nextNodeId, KisSpatialContainer::SpatialNode::maximumArea, KisSpatialContainer::SpatialNode::pointsCount, KisSpatialContainer::SpatialNode::pointsData, KisSpatialContainer::SpatialNode::xPartition, and KisSpatialContainer::SpatialNode::yPartition.

◆ movePoint()

void KisSpatialContainer::movePoint ( int index,
QPointF positionBefore,
QPointF positionAfter )

Definition at line 161 of file KisSpatialContainer.cpp.

162{
163 movePointRec(index, positionBefore, positionAfter, m_root);
164}
void movePointRec(int index, QPointF positionBefore, QPointF positionAfter, SpatialNode *node)

References m_root, and movePointRec().

◆ movePointRec()

void KisSpatialContainer::movePointRec ( int index,
QPointF positionBefore,
QPointF positionAfter,
SpatialNode * node )
private

Definition at line 306 of file KisSpatialContainer.cpp.

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}
void removePointRec(int index, QPointF position, SpatialNode *node)

References KisAlgebra2D::accumulateBounds(), addPointRec(), KisSpatialContainer::SpatialNode::childIndexForPoint(), KisSpatialContainer::SpatialNode::children, createNodeForPoint(), KisSpatialContainer::SpatialNode::isLeaf, KisSpatialContainer::SpatialNode::maximumArea, movePointRec(), KisSpatialContainer::SpatialNode::pointsCount, KisSpatialContainer::SpatialNode::pointsData, and removePointRec().

◆ removePoint()

void KisSpatialContainer::removePoint ( int index,
QPointF position )

Definition at line 156 of file KisSpatialContainer.cpp.

157{
158 removePointRec(index, position, m_root);
159}

References m_root, and removePointRec().

◆ removePointRec()

void KisSpatialContainer::removePointRec ( int index,
QPointF position,
SpatialNode * node )
private

Definition at line 258 of file KisSpatialContainer.cpp.

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()) {
269 QVector<SpatialNodeData>::reverse_iterator lastItem = node->pointsData.rbegin();
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}

References KisSpatialContainer::SpatialNode::childIndexForPoint(), KisSpatialContainer::SpatialNode::children, KisSpatialContainer::SpatialNode::isLeaf, KisSpatialContainer::SpatialNode::pointsCount, KisSpatialContainer::SpatialNode::pointsData, and removePointRec().

◆ toVector()

QVector< QPointF > KisSpatialContainer::toVector ( )

Definition at line 166 of file KisSpatialContainer.cpp.

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}

References gatherDataRec(), m_count, and m_root.

Friends And Related Symbol Documentation

◆ KisSpatialContainerTest

friend class KisSpatialContainerTest
friend

Definition at line 101 of file KisSpatialContainer.h.

Member Data Documentation

◆ m_count

int KisSpatialContainer::m_count {0}
private

Definition at line 96 of file KisSpatialContainer.h.

96{0};

◆ m_maxPointsInDict

int KisSpatialContainer::m_maxPointsInDict = {100}
private

Definition at line 97 of file KisSpatialContainer.h.

97{100};

◆ m_nextNodeId

int KisSpatialContainer::m_nextNodeId = {0}
private

Definition at line 98 of file KisSpatialContainer.h.

98{0}; // just for debug

◆ m_root

SpatialNode* KisSpatialContainer::m_root {nullptr}
private

Definition at line 99 of file KisSpatialContainer.h.

99{nullptr};

The documentation for this class was generated from the following files: