LCOV - code coverage report
Current view: top level - src/engraving/dom - bsp.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 63.4 % 194 123
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include <cmath>
      24              : 
      25              : #include "bsp.h"
      26              : #include "engravingitem.h"
      27              : 
      28              : using namespace mu;
      29              : 
      30              : namespace mu::engraving {
      31              : //---------------------------------------------------------
      32              : //   InsertItemBspTreeVisitor
      33              : //---------------------------------------------------------
      34              : 
      35              : class InsertItemBspTreeVisitor : public BspTreeVisitor
      36              : {
      37            0 :     OBJECT_ALLOCATOR(engraving, InsertItemBspTreeVisitor)
      38              : public:
      39              :     EngravingItem* item;
      40              : 
      41         1936 :     inline void visit(std::vector<EngravingItem*>& items) override { items.push_back(item); }
      42              : };
      43              : 
      44              : //---------------------------------------------------------
      45              : //   RemoveItemBspTreeVisitor
      46              : //---------------------------------------------------------
      47              : 
      48              : class RemoveItemBspTreeVisitor : public BspTreeVisitor
      49              : {
      50            0 :     OBJECT_ALLOCATOR(engraving, RemoveItemBspTreeVisitor)
      51              : public:
      52              :     EngravingItem* item;
      53              : 
      54            0 :     inline void visit(std::vector<EngravingItem*>& items) override { muse::remove(items, item); }
      55              : };
      56              : 
      57              : //---------------------------------------------------------
      58              : //   FindItemBspTreeVisitor
      59              : //---------------------------------------------------------
      60              : 
      61              : class FindItemBspTreeVisitor : public BspTreeVisitor
      62              : {
      63            0 :     OBJECT_ALLOCATOR(engraving, FindItemBspTreeVisitor)
      64              : public:
      65              :     std::vector<EngravingItem*> foundItems;
      66              : 
      67          576 :     void visit(std::vector<EngravingItem*>& items) override
      68              :     {
      69         2480 :         for (auto item : items) {
      70         1904 :             if (!item->itemDiscovered) {
      71          390 :                 item->itemDiscovered = true;
      72          390 :                 foundItems.push_back(item);
      73              :             }
      74              :         }
      75          576 :     }
      76              : };
      77              : 
      78              : //---------------------------------------------------------
      79              : //   BspTree
      80              : //---------------------------------------------------------
      81              : 
      82         3555 : BspTree::BspTree()
      83         3555 :     : m_leafCnt(0)
      84              : {
      85         3555 :     m_depth = 0;
      86         3555 : }
      87              : 
      88              : //---------------------------------------------------------
      89              : //   intmaxlog
      90              : //---------------------------------------------------------
      91              : 
      92            7 : static inline int intmaxlog(int n)
      93              : {
      94            7 :     return n > 0 ? std::max(int(::ceil(::log(double(n)) / ::log(double(2)))), 5) : 0;
      95              : }
      96              : 
      97              : //---------------------------------------------------------
      98              : //   initialize
      99              : //---------------------------------------------------------
     100              : 
     101            7 : void BspTree::initialize(const RectF& rec, int n)
     102              : {
     103            7 :     m_depth      = intmaxlog(n);
     104            7 :     this->m_rect = rec;
     105            7 :     m_leafCnt    = 0;
     106              : 
     107            7 :     m_nodes.resize((1 << (m_depth + 1)) - 1);
     108            7 :     m_leaves.assign(1LL << m_depth, std::vector<EngravingItem*>());
     109            7 :     initialize(rec, m_depth, 0);
     110            7 : }
     111              : 
     112              : //---------------------------------------------------------
     113              : //   clear
     114              : //---------------------------------------------------------
     115              : 
     116            1 : void BspTree::clear()
     117              : {
     118            1 :     m_leafCnt = 0;
     119            1 :     m_nodes.clear();
     120            1 :     m_leaves.clear();
     121            1 : }
     122              : 
     123              : //---------------------------------------------------------
     124              : //   insert
     125              : //---------------------------------------------------------
     126              : 
     127          413 : void BspTree::insert(EngravingItem* element)
     128              : {
     129          413 :     InsertItemBspTreeVisitor insertVisitor;
     130          413 :     insertVisitor.item = element;
     131          413 :     climbTree(&insertVisitor, element->pageBoundingRect());
     132          413 : }
     133              : 
     134              : //---------------------------------------------------------
     135              : //   remove
     136              : //---------------------------------------------------------
     137              : 
     138            0 : void BspTree::remove(EngravingItem* element)
     139              : {
     140            0 :     RemoveItemBspTreeVisitor removeVisitor;
     141            0 :     removeVisitor.item = element;
     142            0 :     climbTree(&removeVisitor, element->pageBoundingRect());
     143            0 : }
     144              : 
     145              : //---------------------------------------------------------
     146              : //   items
     147              : //---------------------------------------------------------
     148              : 
     149            5 : std::vector<EngravingItem*> BspTree::items(const RectF& rec)
     150              : {
     151            5 :     FindItemBspTreeVisitor findVisitor;
     152            5 :     climbTree(&findVisitor, rec);
     153            5 :     std::vector<EngravingItem*> l;
     154          395 :     for (EngravingItem* e : findVisitor.foundItems) {
     155          390 :         e->itemDiscovered = false;
     156          390 :         if (e->pageBoundingRect().intersects(rec)) {
     157          380 :             l.push_back(e);
     158              :         }
     159              :     }
     160            5 :     return l;
     161            5 : }
     162              : 
     163              : //---------------------------------------------------------
     164              : //   items
     165              : //---------------------------------------------------------
     166              : 
     167            0 : std::vector<EngravingItem*> BspTree::items(const PointF& pos)
     168              : {
     169            0 :     FindItemBspTreeVisitor findVisitor;
     170            0 :     climbTree(&findVisitor, pos);
     171              : 
     172            0 :     std::vector<EngravingItem*> l;
     173            0 :     for (EngravingItem* e : findVisitor.foundItems) {
     174            0 :         e->itemDiscovered = false;
     175            0 :         if (e->contains(pos)) {
     176            0 :             l.push_back(e);
     177              :         }
     178              :     }
     179            0 :     return l;
     180            0 : }
     181              : 
     182              : //---------------------------------------------------------
     183              : //   nearestNeighbor (public)
     184              : //---------------------------------------------------------
     185              : 
     186           44 : EngravingItem* BspTree::nearestNeighbor(const PointF& pos)
     187              : {
     188           44 :     EngravingItem* nn = nullptr;
     189           44 :     double bestDistance = std::numeric_limits<double>::max();
     190           44 :     nearestNeighbor(pos, &nn, bestDistance);
     191           44 :     return nn;
     192              : }
     193              : 
     194              : //---------------------------------------------------------
     195              : //   nearestNeighbor (private)
     196              : //---------------------------------------------------------
     197              : 
     198         4121 : void BspTree::nearestNeighbor(const PointF& pos, EngravingItem** bestItem, double& bestDistance, int nodeIndex)
     199              : {
     200         4121 :     if (m_nodes.empty()) {
     201            0 :         return;
     202              :     }
     203              : 
     204         4121 :     Node* node = &m_nodes[nodeIndex];
     205              : 
     206              :     // Base case: go through the items in the leaf node (if any), and update bestItem/bestDistance accordingly
     207         4121 :     if (node->type == Node::Type::LEAF) {
     208         1929 :         for (auto item : m_leaves[node->leafIndex]) {
     209           78 :             PointF itemPos = item->pageBoundingRect().center();
     210           78 :             double currDistance = std::sqrt(std::pow(pos.x() - itemPos.x(), 2) + std::pow(pos.y() - itemPos.y(), 2));
     211           78 :             if (currDistance < bestDistance) {
     212           59 :                 *bestItem = item;
     213           59 :                 bestDistance = currDistance;
     214              :             }
     215              :         }
     216         1851 :         return;
     217              :     }
     218              : 
     219              :     // Find which child contains pos and which is the "sibling"
     220         2270 :     int containerIdx = firstChildIndex(nodeIndex);
     221         2270 :     int siblingIdx =  containerIdx + 1;
     222         2270 :     if (node->type == Node::Type::VERTICAL) {
     223         1438 :         if (pos.x() >= node->offset) {
     224          500 :             ++containerIdx;
     225          500 :             --siblingIdx;
     226              :         }
     227          832 :     } else if (pos.y() >= node->offset) {
     228          330 :         ++containerIdx;
     229          330 :         --siblingIdx;
     230              :     }
     231              : 
     232              :     // Recursion on container node
     233         2270 :     nearestNeighbor(pos, bestItem, bestDistance, containerIdx);
     234              : 
     235              :     // If the distance to the "offset" is shorter than the best distance we've found so far, then it's possible that the nearest
     236              :     // neighbour is in the sibling node (so we should search there too).
     237              :     double distanceToOffset;
     238         2270 :     if (node->type == Node::Type::HORIZONTAL) {
     239          832 :         distanceToOffset = std::abs(pos.y() - node->offset);
     240              :     } else {
     241         1438 :         distanceToOffset = std::abs(pos.x() - node->offset);
     242              :     }
     243              : 
     244         2270 :     if (distanceToOffset < bestDistance) {
     245              :         // Recursion on sibling node
     246         1807 :         nearestNeighbor(pos, bestItem, bestDistance, siblingIdx);
     247              :     }
     248              : }
     249              : 
     250              : #ifndef NDEBUG
     251              : //---------------------------------------------------------
     252              : //   debug
     253              : //---------------------------------------------------------
     254              : 
     255            0 : String BspTree::debug(int index) const
     256              : {
     257            0 :     const Node* node = &m_nodes.at(index);
     258              : 
     259            0 :     String tmp;
     260            0 :     if (node->type == Node::Type::LEAF) {
     261            0 :         RectF rec = rectForIndex(index);
     262            0 :         if (!m_leaves[node->leafIndex].empty()) {
     263            0 :             tmp += String(u"[%1, %2, %3, %4] contains %5 items\n")
     264            0 :                    .arg(rec.left()).arg(rec.top())
     265            0 :                    .arg(rec.width()).arg(rec.height())
     266            0 :                    .arg(m_leaves[node->leafIndex].size());
     267              :         }
     268              :     } else {
     269            0 :         if (node->type == Node::Type::HORIZONTAL) {
     270            0 :             tmp += debug(firstChildIndex(index));
     271            0 :             tmp += debug(firstChildIndex(index) + 1);
     272              :         } else {
     273            0 :             tmp += debug(firstChildIndex(index));
     274            0 :             tmp += debug(firstChildIndex(index) + 1);
     275              :         }
     276              :     }
     277            0 :     return tmp;
     278            0 : }
     279              : 
     280              : #endif
     281              : 
     282              : //---------------------------------------------------------
     283              : //   initialize
     284              : //---------------------------------------------------------
     285              : 
     286         2169 : void BspTree::initialize(const RectF& rec, int dep, int index)
     287              : {
     288         2169 :     Node* node = &m_nodes[index];
     289         2169 :     if (index == 0) {
     290            7 :         node->type = Node::Type::HORIZONTAL;
     291            7 :         node->offset = rec.center().y();
     292              :     }
     293              : 
     294         2169 :     if (dep) {
     295              :         Node::Type type;
     296         1081 :         RectF rect1, rect2;
     297              :         double offset1, offset2;
     298              : 
     299         1081 :         if (node->type == Node::Type::HORIZONTAL) {
     300          531 :             type = Node::Type::VERTICAL;
     301          531 :             rect1.setRect(rec.left(), rec.top(), rec.width(), rec.height() * .5);
     302          531 :             rect2.setRect(rect1.left(), rect1.bottom(), rect1.width(), rec.height() - rect1.height());
     303          531 :             offset1 = rect1.center().x();
     304          531 :             offset2 = rect2.center().x();
     305              :         } else {
     306          550 :             type = Node::Type::HORIZONTAL;
     307          550 :             rect1.setRect(rec.left(), rec.top(), rec.width() * .5, rec.height());
     308          550 :             rect2.setRect(rect1.right(), rect1.top(), rec.width() - rect1.width(), rect1.height());
     309          550 :             offset1 = rect1.center().y();
     310          550 :             offset2 = rect2.center().y();
     311              :         }
     312              : 
     313         1081 :         int childIndex = firstChildIndex(index);
     314              : 
     315         1081 :         Node* child   = &m_nodes[childIndex];
     316         1081 :         child->offset = offset1;
     317         1081 :         child->type   = type;
     318              : 
     319         1081 :         child = &m_nodes[childIndex + 1];
     320         1081 :         child->offset = offset2;
     321         1081 :         child->type   = type;
     322              : 
     323         1081 :         initialize(rect1, dep - 1, childIndex);
     324         1081 :         initialize(rect2, dep - 1, childIndex + 1);
     325              :     } else {
     326         1088 :         node->type      = Node::Type::LEAF;
     327         1088 :         node->leafIndex = m_leafCnt++;
     328              :     }
     329         2169 : }
     330              : 
     331              : //---------------------------------------------------------
     332              : //   climbTree
     333              : //---------------------------------------------------------
     334              : 
     335            0 : void BspTree::climbTree(BspTreeVisitor* visitor, const PointF& pos, int index)
     336              : {
     337            0 :     if (m_nodes.empty()) {
     338            0 :         return;
     339              :     }
     340              : 
     341            0 :     Node* node = &m_nodes[index];
     342            0 :     int childIndex = firstChildIndex(index);
     343              : 
     344            0 :     switch (node->type) {
     345            0 :     case Node::Type::LEAF:
     346            0 :         visitor->visit(m_leaves[node->leafIndex]);
     347            0 :         break;
     348            0 :     case Node::Type::VERTICAL:
     349            0 :         if (pos.x() < node->offset) {
     350            0 :             climbTree(visitor, pos, childIndex);
     351              :         } else {
     352            0 :             climbTree(visitor, pos, childIndex + 1);
     353              :         }
     354            0 :         break;
     355            0 :     case Node::Type::HORIZONTAL:
     356            0 :         if (pos.y() < node->offset) {
     357            0 :             climbTree(visitor, pos, childIndex);
     358              :         } else {
     359            0 :             climbTree(visitor, pos, childIndex + 1);
     360              :         }
     361            0 :         break;
     362              :     }
     363              : }
     364              : 
     365              : //---------------------------------------------------------
     366              : //   climbTree
     367              : //---------------------------------------------------------
     368              : 
     369         7853 : void BspTree::climbTree(BspTreeVisitor* visitor, const RectF& rec, int index)
     370              : {
     371         7853 :     if (m_nodes.empty()) {
     372            0 :         return;
     373              :     }
     374              : 
     375         7853 :     Node* node = &m_nodes[index];
     376         7853 :     int childIndex = firstChildIndex(index);
     377              : 
     378         7853 :     switch (node->type) {
     379         2512 :     case Node::Type::LEAF:
     380         2512 :         visitor->visit(m_leaves[node->leafIndex]);
     381         2512 :         break;
     382         2285 :     case Node::Type::VERTICAL:
     383         2285 :         if (rec.left() < node->offset) {
     384         1572 :             climbTree(visitor, rec, childIndex);
     385         1572 :             if (rec.right() >= node->offset) {
     386          681 :                 climbTree(visitor, rec, childIndex + 1);
     387              :             }
     388              :         } else {
     389          713 :             climbTree(visitor, rec, childIndex + 1);
     390              :         }
     391         2285 :         break;
     392         3056 :     case Node::Type::HORIZONTAL:
     393         3056 :         if (rec.top() < node->offset) {
     394         2251 :             climbTree(visitor, rec, childIndex);
     395         2251 :             if (rec.bottom() >= node->offset) {
     396         1413 :                 climbTree(visitor, rec, childIndex + 1);
     397              :             }
     398              :         } else {
     399          805 :             climbTree(visitor, rec, childIndex + 1);
     400              :         }
     401              :     }
     402              : }
     403              : 
     404              : //---------------------------------------------------------
     405              : //   rectForIndex
     406              : //---------------------------------------------------------
     407              : 
     408            0 : RectF BspTree::rectForIndex(int index) const
     409              : {
     410            0 :     if (index <= 0) {
     411            0 :         return m_rect;
     412              :     }
     413              : 
     414            0 :     int parentIdx = parentIndex(index);
     415            0 :     RectF rec   = rectForIndex(parentIdx);
     416            0 :     const Node* parent = &m_nodes.at(parentIdx);
     417              : 
     418            0 :     if (parent->type == Node::Type::HORIZONTAL) {
     419            0 :         if (index & 1) {
     420            0 :             rec.setRight(parent->offset);
     421              :         } else {
     422            0 :             rec.setLeft(parent->offset);
     423              :         }
     424              :     } else {
     425            0 :         if (index & 1) {
     426            0 :             rec.setBottom(parent->offset);
     427              :         } else {
     428            0 :             rec.setTop(parent->offset);
     429              :         }
     430              :     }
     431            0 :     return rec;
     432              : }
     433              : }
        

Generated by: LCOV version 2.0-1