//////////////////////////////////////////////////////////////////////////////// /** \file \brief \ru Дерево габаритных кубов. \en Bounding boxes tree. \~ */ //////////////////////////////////////////////////////////////////////////////// #ifndef __MB_CUBE_TREE_H #define __MB_CUBE_TREE_H #include #include #include #include #include #include #include //------------------------------------------------------------------------------ /** \brief \ru Базовый интерфейс для вычисления квадрата расстояния до объекта, которому принадлежит габаритный куб дерева по указанному индексу. \en The base interface for calculating of squared distance to the object from the tree's bounding box with specified index. \~ \ingroup Base_Items */ // --- template class MbTreeDistanceToElementBase { public: /// \ru Конструктор. \en Constructor. MbTreeDistanceToElementBase() {} /// \ru Деструктор. \en Destructor. virtual ~MbTreeDistanceToElementBase() {} /// \ru Вычислить квадрат расстояния от точки до объекта из габаритного куба. \en Find the squared distance from the point to tree object from bounding box. virtual double DistanceToElement2( const Point & pnt, size_t iElem ) const = 0; }; //------------------------------------------------------------------------------ /** \brief \ru Дерево объектов с габаритными кубами. \en Tree of objects with bounding boxes. \~ \details \ru Дерево объектов с габаритными кубами.\n Oбъекты дерева должны иметь функции "const Cube & Type::GetCube() const", "double Type::DistanceToCube( const Cube & cube, double eps )". \en Tree of objects with bounding boxes.\n Tree objects must have functions : "const Cube & Type::GetCube() const", "double Type::DistanceToCube( const Cube & cube, double eps )". \~ \ingroup Base_Items */ // --- template class MbCubeTree { public: typedef std::pair ItemIndex; typedef std::pair ItemItem; typedef std::vector ItemIndexVector; typedef std::pair ItemDistance; typedef std::pair IndexDistance; typedef MbCubeTree TypeTree; /// \ru Тип алгоритма вычисления расстояния от точки до габарита. \en Algorithm type that is used to measure distance between point and bounding box. enum DistanceMeasure { edm_MaxCoord = 0, ///< \ru Максимальное расстояние вдоль одной из координатных осей. \en Distance maximum along one of coordinate axes. edm_CubeCenter, ///< \ru Расстояние до центра габарита объекта. \en Distance to central point of bounding box. }; /// \ru Битовые флаги направлений ветвления. \en Bit flags of branching directions. enum BranchingDirections { ebd_WasDirX = 0x01, ///< \ru Была попытка ветвления по оси Ox. \en An attempt was made to branch along the Ox axis. ebd_WasDirY = 0x02, ///< \ru Была попытка ветвления по оси Oy. \en An attempt was made to branch along the Oy axis. ebd_WasDirZ = 0x04, ///< \ru Была попытка ветвления по оси Oz. \en An attempt was made to branch along the Oz axis. }; private: /// \ru Координатная ось деления на ветви. \en Selected coordinate axis for branching. enum DirectionAxis { eda_anyDir = 0, ///< \ru Произвольная координатная ось. \en Arbitrarily coordinate axis. eda_axisX = 1, ///< \ru Координатная ось X. \en Coordinate axis X. eda_axisY = 2, ///< \ru Координатная ось Y. \en Coordinate axis Y. eda_axisZ = 3, ///< \ru Координатная ось Z. \en Coordinate axis Z. eda_newDir = 4, ///< \ru Нужен выбор оси в зависимости от битового флага. \en Have to select axis by bit flags of branching. }; // Selected coordinate axis for branching // ^ // | maximum +-------------+ // | | upperBranch | // | | | // | upper +---|-------------|---+ // | | | | | // | midst --------midstBranch-------- or leafObjects for terminal branch content // | | | | | // | lower +---|-------------|---+ // | | | // | | lowerBranch | // | minimum +-------------+ // | private: double midst; ///< \ru Центральное значение по оси direction. \en Central value along "direction" axis. double minimum; ///< \ru Минимальное значение по оси direction. \en Minimal value along "direction" axis. double maximum; ///< \ru Максимальное значение по оси direction. \en Maximal value along "direction" axis. double lower; ///< \ru Размер центральной ветви в сторону увеличения от midst. \en Size of the central branch upward relative to the value of midst. double upper; ///< \ru Размер центральной ветви в сторону уменьшения от midst. \en Size of the central branch downward relative to the value of midst. DirectionAxis direction; ///< \ru Направление деления на ветви. \en Branching direction. DistanceMeasure dmType; ///< \ru Тип расчета расстояния от точки до габарита объекта. \en Distance measurement type between point and bounding box. Cube branchCube; ///< \ru Габарит ветви. \en Branch bounding box. ItemIndexVector leafObjects; ///< \ru Объекты c габаритами в конечной ветви (листе). \en Objects (with indices in initial array) in terminal branch of tree. TypeTree * midstBranch; ///< \ru Ветвь, элементы которой пересекают центральное значение. \en A branch, whose elements cross the central value. TypeTree * lowerBranch; ///< \ru Ветвь, элементы которой меньше центрального значения. \en A branch whose elements are smaller than the central value. TypeTree * upperBranch; ///< \ru Ветвь, элементы которой больше центрального значения. \en A branch whose elements are larger than the central value. static size_t treeDepth; ///< \ru Глубина дерева. \en Tree depth. static size_t leafMinCount; ///< \ru Минимальное количество элементов в конечной ветви. \en The minimum number of elements in the leaf branch. static const double minDoubleValue; ///< \ru Минимальное значение double. \en Min double value. static const double maxDoubleValue; ///< \ru Максимальное значение double. \en Max double value. static const double defaultEpsilon; ///< \ru Точность по умолчанию. \en Default accuracy. public: /** \brief \ru Конструктор дерева по объектам. \en Tree constructor by objects. \~ \details \ru Конструктор дерева по объектам. \n \en Tree constructor by objects. \n \~ \param[in] objects - \ru Объекты дерева. \en Tree objects. \~ \param[in] dm - \ru Тип вычисления расстояния от точки до габарита. \en Distance measure type between point and bounding box. \~ */ MbCubeTree( std::vector & objects, DistanceMeasure dm = edm_MaxCoord ); /** \brief \ru Конструктор дерева по объектам. \en Tree constructor by objects. \~ \details \ru Конструктор дерева по объектам. \n \en Tree constructor by objects. \n \~ \param[in] objects - \ru Объекты дерева. \en Tree objects. \~ \param[in] gabarit - \ru Общий габарит всех объектов дерева, должен быть не пустым. \en The total bounding box of all objects in the tree, it must not be empty. \~ \param[in] dm - \ru Тип вычисления расстояния от точки до габарита. \en Distance measure type between point and bounding box. \~ */ MbCubeTree( std::vector & objects, const Cube & gabarit, DistanceMeasure dm = edm_MaxCoord ); /** \brief \ru Конструктор пустого дерева. \en Constructor of empty tree. \~ \details \ru Конструктор пустого дерева. \n \en Constructor of empty tree. \n \~ \param[in] dm - \ru Тип вычисления расстояния от точки до габарита. \en Distance measure type between point and bounding box. \~ */ MbCubeTree( DistanceMeasure dm = edm_MaxCoord ); private: MbCubeTree( const MbCubeTree & ); // не реализовано / not implemented MbCubeTree & operator = ( const MbCubeTree & ); // не реализовано / not implemented public: /// \ru Деструктор. \en Destuctror. ~MbCubeTree(); public: /** \brief \ru Инициализация дерева по объектам. \en Initialize tree by objects. \~ \details \ru Инициализация дерева по объектам. \n \en Initialize tree by objects. \n \~ \param[in] objects - \ru Объекты дерева. \en Tree objects. \~ \param[in] gabarit - \ru Общий габарит всех объектов дерева, должен быть не пустым. \en The total bounding box of all objects in the tree, it must not be empty. \~ \param[in] dm - \ru Тип вычисления расстояния от точки до габарита. \en Distance measure type between point and bounding box. \~ */ bool InitTree( std::vector & objects, const Cube & gabarit, DistanceMeasure dm = edm_MaxCoord ); /** \brief \ru Копировать дерево. \en Copy the tree. \~ \details \ru Копировать дерево. \n \en Copy the tree. \n \~ \param[in] srcTree - \ru Исходное дерево. \en Initial tree. \~ \param[in] copiedObjects - \ru Копии объектов дерева. \en Copies of tree objects. \~ */ bool InitTree( const MbCubeTree & srcTree, const std::vector & srcObjects, const std::vector & dstObjects ); /// \ru Очистить дерево. \en Clear the tree. void Clear(); /// \ru Сдвинуть дерево. \en Move the tree. void Move( const Vector & ); public: // функции настроек и состояния дерева / settings and tree state functions /// \ru Получить тип алгоритма вычисления расстояния от точки до габарита. \en Get algorithm type that is used to measure distance between point and bounding box. DistanceMeasure GetDistanceMeasure() const { return dmType; } /// \ru Количество объектов в дереве (не хранит, считает обходом дерева). \en Number of objects in the tree (does not store, tree calculates it by traversing tree nodes). size_t Count() const; /// \ru Количество узлов в дереве. \en Number of branches in the tree. size_t NodeCount() const; /// \ru Готово ли дерево. \en Whether tree is ready. bool IsReady() const; /// \ru Получить глубину дерева (общая переменная). \en Get tree depth (shared variable). size_t GetTreeDepth() const { return treeDepth; }; /// \ru Установить глубину дерева (общая переменная). \en Set tree depth (shared variable). void SetTreeDepth( size_t k ) { treeDepth = (std::min)( k, (size_t)64 ); } /// \ru Сбросить глубину дерева (общая переменная). \en Reset tree depth (shared variable). void ResetTreeDepth() { treeDepth = 64; } /// \ru Получить минимальное количество элементов в конечной ветви (общая переменная). \en Get a minimum number of elements in the leaf branch (shared variable). size_t GetLeafMinElementsCount() const { return leafMinCount; } /// \ru Установить минимальное количество элементов в конечной ветви (общая переменная). \en Set a minimum number of elements in the leaf branch (shared variable). void SetLeafMinElementsCount( size_t k ) { leafMinCount = (std::max)( k, (size_t)1 ); } /// \ru Сбросить минимальное количество элементов в конечной ветви (общая переменная). \en Reset the minimum number of elements in the leaf branch (shared variable). void ResetLeafMinElementsCount() { leafMinCount = 1; } public: // функции работы с деревом объектов / public functions for work with objects tree /// \ru Выдать содержащие точку объекты дерева. \en Get tree objects containing a point. void GetContainsObjects ( const Point & pnt, double epsilon, std::vector & objects ) const; /// \ru Выдать содержащие точку объекты дерева. \en Get tree objects containing a point. void GetContainsObjects( const Point & pnt, double epsilon, std::vector & indices ) const; /// \ru Выдать пересекающиеся с габаритным кубом объекты дерева. \en Get objects of the tree intersecting with the bounding box. void GetIntersectObjects( const Cube & gabarit, double epsilon, std::vector & objects, bool skipOwnself = false ) const; /// \ru Выдать пересекающиеся с габаритным кубом объекты дерева. \en Get objects of the tree intersecting with the bounding box. void GetIntersectObjects( const Cube & gabarit, double epsilon, std::vector & indices, bool skipOwnself = false ) const; /// \ru Выдать пересекающиеся с отрезком луча кубом объекты дерева. \en Get objects of the tree intersecting with the ray segment. void GetIntersectObjects( const Point & rayPnt1, const Point & rayPnt2, double epsilon, std::vector & indices ) const; /// \ru Найти ближайший к кубу объект дерева. \en Find the nearest tree object to a bounding box. void FindNearestObject ( const Cube & gabarit, double & distance, const Type *& object, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Найти ближайший к кубу объект дерева в виде индекса в исходном массиве. \en Find the nearest tree object to a bounding box (as an index in initial array of objects). void FindNearestObject ( const Cube & gabarit, double & distance, size_t & index, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Найти ближайший к точке объект дерева. \en Find the nearest tree object to a point. void FindNearestObject ( const Point & pnt, double & distance, const Type *& object, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Найти ближайший к точке объект дерева в виде индекса в исходном массиве. \en Find the nearest tree object to a point (as an index in initial array of objects). void FindNearestObject ( const Point & pnt, double & distance, size_t & index, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Выдать объекты дерева, расположенные к кубу ближе заданного расстояния. \en Get tree objects that are closer to the cube than the specified distance. void GetNearestObjects ( const Cube & gabarit, const double distance, std::vector & itemDistances, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Выдать объекты дерева, расположенные к точке ближе заданного расстояния. \en Get tree objects that are closer to the point than the specified distance. void GetNearestObjects ( const Point & pnt, const double distance, std::vector & itemDistances, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Выдать индексы объектов дерева, расположенные к точке ближе заданного расстояния. \en Get tree objects indices that are closer to the point than the specified distance. void GetNearestObjects ( const Point & pnt, const double distance, std::vector & indexDistances, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Выдать индексы объектов дерева, расположенные к точке ближе заданного расстояния. \en Get tree objects indices that are closer to the point than the specified distance. void GetNearestObjects ( const Point & pnt, const double distance, std::vector & indices, double eps = MbCubeTree::defaultEpsilon ) const; /// \ru Выдать индекс объекта дерева, ближайшего к точке, и квадрат расстояния до него. \en Get tree objects index that are closest to the point, and squared distance to object. double FindNearestObject ( const Point & pnt, const MbTreeDistanceToElementBase & calc, size_t & index ) const; private: // функции инициализация и заполнение ветвей дерева / internal functions for initialization, filling branches /// \ru Копировать дерево. \en Copy the tree. bool InitTree( const MbCubeTree & srcTree, const std::vector & objPair ); /// \ru Инициализация дерева по объектам. \en Initialize tree by objects. bool InitTree ( std::vector & objects, const Cube & gabarit, DirectionAxis a, int tier, unsigned char flag ); /// \ru Заполнение ветвей дерева. \en Fill (set) tree branches. void SetBranches( std::vector & objects, const Cube & gabarit, DirectionAxis a, int tier, unsigned char & flag ); /// \ru Заполнение конечной ветви дерева. \en Fill terminal tree branch. void FillLeaf( std::vector & objects, const Cube & gabarit, DirectionAxis a ); /// \ru Установка битового флага flag в зависимости от направления direction. \en Set direction bit flag. void SetFlag( unsigned char & flag ); /// \ru Получить компонент точки в зависимости от направления direction. \en Get point component by branching direction. double GetPointComponent( const Point & pnt ) const; /// \ru Получить компонент точки в зависимости от направления direction. \en Get point component by branching direction. void GetCubeMinMaxComponents( const Cube & cube, double epsExt, double & wMin, double & wMax ) const; /// \ru Измерить расстояние по выбранной в мере. \en Measure distance using selected measure type. double GetDistance( const Type & object, const Point & pnt, bool signedDistance ) const; /// \ru Функция сравнения - меньше. \en Compare function "Less". static bool LessPairByFirstItem( const ItemItem & e1, const ItemItem & e2 ) { return (e1.first < e2.first) ? true : false; } }; //------------------------------------------------------------------------------- // \ru Глубина дерева. \en Tree depth. // --- template size_t MbCubeTree::treeDepth( 64 ); //------------------------------------------------------------------------------- // \ru Минимальное количество элементов в конечной ветви. \en The minimum number of elements in the leaf branch. // --- template size_t MbCubeTree::leafMinCount( 1 ); //------------------------------------------------------------------------------- // \ru Минимальное значение double. \en Min double value. // --- template const double MbCubeTree::minDoubleValue( (std::numeric_limits::lowest)() ); //------------------------------------------------------------------------------- // \ru Максимальное значение double. \en Max double value. // --- template const double MbCubeTree::maxDoubleValue( (std::numeric_limits::max)() ); //------------------------------------------------------------------------------- // \ru Точность по умолчанию. \en Default accuracy. // --- template const double MbCubeTree::defaultEpsilon( 1E-7 ); //------------------------------------------------------------------------------- // \ru Конструктор дерева по объектам. \en Tree constructor by objects. // --- template inline MbCubeTree::MbCubeTree( std::vector & objects, DistanceMeasure dm ) : midst ( 0.0 ) , minimum ( 0.0 ) , maximum ( 0.0 ) , lower ( 0.0 ) , upper ( 0.0 ) , direction ( eda_anyDir ) , dmType ( dm ) , leafObjects( ) , branchCube ( ) , midstBranch( nullptr ) , lowerBranch( nullptr ) , upperBranch( nullptr ) { size_t cnt = objects.size(); C3D_ASSERT( Cube::GetDimension() == Point::GetDimension() ); if ( cnt > 0 ) { // Инициализация дерева Cube gabarit; for ( size_t i = 0; i < cnt; ++i ) { if ( objects[i].first != nullptr ) gabarit |= objects[i].first->GetCube(); } branchCube = gabarit; InitTree( objects, gabarit, eda_anyDir, 0, 0 ); ResetLeafMinElementsCount(); ResetTreeDepth(); } } //------------------------------------------------------------------------------- // \ru Конструктор дерева по объектам. \en Tree constructor by objects. // --- template inline MbCubeTree::MbCubeTree( std::vector & objects, const Cube & gabarit, DistanceMeasure dm ) : midst ( 0.0 ) , minimum ( 0.0 ) , maximum ( 0.0 ) , lower ( 0.0 ) , upper ( 0.0 ) , direction ( eda_anyDir ) , dmType ( dm ) , leafObjects( ) , branchCube ( gabarit ) , midstBranch( nullptr ) , lowerBranch( nullptr ) , upperBranch( nullptr ) { C3D_ASSERT( Cube::GetDimension() == Point::GetDimension() ); InitTree( objects, gabarit, eda_anyDir, 0, 0 ); ResetLeafMinElementsCount(); ResetTreeDepth(); } //------------------------------------------------------------------------------- // \ru Конструктор дерева. \en Tree constructor. // --- template inline MbCubeTree::MbCubeTree( DistanceMeasure dm ) : midst ( 0.0 ) , minimum ( 0.0 ) , maximum ( 0.0 ) , lower ( 0.0 ) , upper ( 0.0 ) , direction ( eda_anyDir ) , dmType ( dm ) , leafObjects( ) , branchCube ( ) , midstBranch( nullptr ) , lowerBranch( nullptr ) , upperBranch( nullptr ) { } //------------------------------------------------------------------------------- // \ru Очистить дерево. \en Clear the tree. // --- template inline void MbCubeTree::Clear() { delete midstBranch; delete lowerBranch; delete upperBranch; midstBranch = lowerBranch = upperBranch = nullptr; midst = minimum = maximum = lower = upper = 0.0; direction = eda_anyDir; branchCube.SetEmpty(); leafObjects.clear(); } //------------------------------------------------------------------------------- // \ru Деструктор. \en Destructor. // --- template inline MbCubeTree::~MbCubeTree() { delete midstBranch; delete lowerBranch; delete upperBranch; } //------------------------------------------------------------------------------- // \ru Сдвинуть дерево. \en Move the tree. // --- template inline void MbCubeTree::Move( const Vector & to ) { if ( to.MaxFactor() > 0.0 ) { if ( midstBranch != nullptr ) midstBranch->Move( to ); if ( lowerBranch != nullptr ) lowerBranch->Move( to ); if ( upperBranch != nullptr ) upperBranch->Move( to ); Point pto; pto.Move( to ); double toValue = GetPointComponent( pto ); midst += toValue; minimum += toValue; maximum += toValue; lower += toValue; upper += toValue; branchCube.Move( to ); } } //------------------------------------------------------------------------------- // \ru Копировать дерево. \en Copy the tree. // --- template inline bool MbCubeTree::InitTree( const MbCubeTree & srcTree, const std::vector & itemPairs ) { bool res = true; midst = srcTree.midst; minimum = srcTree.minimum; maximum = srcTree.maximum; lower = srcTree.lower; upper = srcTree.upper; direction = srcTree.direction; dmType = srcTree.dmType; branchCube = srcTree.branchCube; size_t cnt = srcTree.leafObjects.size(); leafObjects.resize( cnt ); typename std::vector::const_iterator it; ItemItem itemPair( nullptr, nullptr ); for ( size_t k = 0; k < cnt && res; ++k ) { res = false; leafObjects[k].second = srcTree.leafObjects[k].second; itemPair.first = srcTree.leafObjects[k].first; it = std::lower_bound( itemPairs.begin(), itemPairs.end(), itemPair, MbCubeTree::LessPairByFirstItem ); if ( it != itemPairs.end() ) { if ( !MbCubeTree::LessPairByFirstItem( itemPair, *it ) ) { leafObjects[k].first = it->second; res = true; } } } if ( res && (srcTree.midstBranch != nullptr) ) { C3D_ASSERT( midstBranch == nullptr ); if ( midstBranch == nullptr ) midstBranch = new MbCubeTree( srcTree.midstBranch->dmType ); else midstBranch->Clear(); res &= midstBranch->InitTree( *srcTree.midstBranch, itemPairs ); } if ( res && (srcTree.lowerBranch != nullptr) ) { C3D_ASSERT( lowerBranch == nullptr ); if ( lowerBranch == nullptr ) lowerBranch = new MbCubeTree( srcTree.lowerBranch->dmType ); else lowerBranch->Clear(); res &= lowerBranch->InitTree( *srcTree.lowerBranch, itemPairs ); } if ( res && (srcTree.upperBranch != nullptr) ) { C3D_ASSERT( upperBranch == nullptr ); if ( upperBranch == nullptr ) upperBranch = new MbCubeTree( srcTree.upperBranch->dmType ); else upperBranch->Clear(); res &= upperBranch->InitTree( *srcTree.upperBranch, itemPairs ); } return res; } //------------------------------------------------------------------------------- // \ru Копировать дерево. \en Copy the tree. // --- template inline bool MbCubeTree::InitTree( const MbCubeTree & srcTree, const std::vector & srcItems, const std::vector & dstItems ) { bool res = false; Clear(); const size_t itemCnt = srcItems.size(); if ( (itemCnt > 0) && (itemCnt == dstItems.size()) && srcTree.IsReady() && (srcTree.Count() == itemCnt) ) { res = true; std::vector< std::pair > itemPairs; itemPairs.resize( itemCnt ); double eps = defaultEpsilon; size_t k; for ( k = 0; k < itemCnt; ++k ) { res = false; if ( srcItems[k].second == dstItems[k].second ) { const Type * srcItem = srcItems[k].first; const Type * dstItem = dstItems[k].first; if ( (srcItem != dstItem) && (srcItem != nullptr) && (dstItem != nullptr) ) { if ( srcItem->IsSame( *dstItem, eps ) ) { itemPairs[k].first = srcItem; itemPairs[k].second = dstItem; res = true; } } } } if ( res ) { res = true; if ( itemPairs.size() > 1 ) { std::sort( itemPairs.begin(), itemPairs.end() ); itemPairs.erase( std::unique( itemPairs.begin(), itemPairs.end() ), itemPairs.end() ); if ( itemPairs.size() != itemCnt ) res = false; } } if ( res ) { res = InitTree( srcTree, itemPairs ); } } return res; } //------------------------------------------------------------------------------- // \ru Инициализация дерева по объектам. \en Initialize tree by objects. // --- template inline bool MbCubeTree::InitTree( std::vector & objects, const Cube & gabarit, DistanceMeasure dm ) { bool res = false; Clear(); dmType = dm; size_t cnt = objects.size(); C3D_ASSERT( Cube::GetDimension() == Point::GetDimension() ); if ( cnt > 0 ) { // Инициализация дерева res = InitTree( objects, gabarit, eda_anyDir, 0, 0 ); ResetLeafMinElementsCount(); ResetTreeDepth(); } return res; } //------------------------------------------------------------------------------- // \ru Инициализация дерева по объектам. \en Initialize tree by objects. // --- template inline bool MbCubeTree::InitTree( std::vector & objects, const Cube & gabarit, DirectionAxis a, int tier, unsigned char flag ) { bool res = false; size_t iCount = objects.size(); if ( iCount > 0 ) { // Инициализация дерева / Initialize tree const double eps = defaultEpsilon; const size_t gabDim = gabarit.GetDimension(); // slow : memory reallocation //std::vector< std::pair > gabLimits; //std::vector gabLens; //gabLimits.reserve( gabDim ); //gabLens.reserve( gabDim ); double gabLens[3]; C3D_ASSERT( gabDim < 4 ); bool validLimits = true; { double gabMin, gabMax, gabLen; for ( size_t i = 0; i < gabDim; ++i ) { gabMin = gabarit.GetMin(i); gabMax = gabarit.GetMax(i); gabLen = gabMax - gabMin; // slow : memory reallocation //gabLimits.push_back( std::make_pair( gabMin, gabMax ) ); //gabLens.push_back( gabLen ); //gabLimits[i] = std::make_pair( gabMin, gabMax ); gabLens[i] = gabLen; validLimits &= (gabLen > -eps); } } if ( validLimits ) { res = true; branchCube = gabarit; if ( (tier < treeDepth) && (iCount > leafMinCount) ) { // если ось указана, то следующее разделение должно выполниться по другой оси switch ( a ) { case eda_axisX : { // разделение на ветви по по наиболее вытянутой оси Y или Z if ( gabDim == 3 ) { if ( gabLens[1] - eps > gabLens[2] ) SetBranches( objects, gabarit, eda_axisY, tier, flag ); else SetBranches( objects, gabarit, eda_axisZ, tier, flag ); } else if ( gabDim == 2 ) { SetBranches( objects, gabarit, eda_axisY, tier, flag ); } } break; case eda_axisY : { // разделение на ветви по по наиболее вытянутой оси Z или X if ( gabDim == 3 ) { if ( gabLens[2] - eps > gabLens[0] ) SetBranches( objects, gabarit, eda_axisZ, tier, flag ); else SetBranches( objects, gabarit, eda_axisX, tier, flag ); } else if ( gabDim == 2 ) { SetBranches( objects, gabarit, eda_axisX, tier, flag ); } } break; case eda_axisZ : { // разделение на ветви по по наиболее вытянутой оси X или Y if ( gabDim == 3 ) { if ( gabLens[0] - eps > gabLens[1] ) SetBranches( objects, gabarit, eda_axisX, tier, flag ); else SetBranches( objects, gabarit, eda_axisY, tier, flag ); } else { C3D_ASSERT_UNCONDITIONAL( false ); } } break; case eda_newDir : { // разделение на ветви по ещё не использованному направлению if ( gabDim == 3 ) { if ( flag & ebd_WasDirX ) { // попытка разделения по оси X не удалась if ( gabLens[1] + eps > gabLens[2] && !(flag & ebd_WasDirY) ) SetBranches( objects, gabarit, eda_axisY, tier, flag ); else { if ( !(flag & ebd_WasDirZ) ) SetBranches( objects, gabarit, eda_axisZ, tier, flag ); else { // исчерпаны все возможности разделения FillLeaf( objects, gabarit, eda_axisZ ); } } } else if ( flag & ebd_WasDirY ) { // попытка разделения по оси Y не удалась if ( gabLens[2] - eps > gabLens[0] && !(flag & ebd_WasDirZ) ) SetBranches( objects, gabarit, eda_axisZ, tier, flag ); else SetBranches( objects, gabarit, eda_axisX, tier, flag ); } else { // попытка разделения по оси Z не удалась if ( gabLens[0] + eps > gabLens[1] ) SetBranches( objects, gabarit, eda_axisX, tier, flag ); else SetBranches( objects, gabarit, eda_axisY, tier, flag ); } } else if ( gabDim == 2 ) { if ( flag & ebd_WasDirX ) { // попытка разделения по оси X не удалась if ( !(flag & ebd_WasDirY) ) SetBranches( objects, gabarit, eda_axisY, tier, flag ); else { // исчерпаны все возможности разделения FillLeaf( objects, gabarit, eda_axisY ); } } else if ( flag & ebd_WasDirY ) { // попытка разделения по оси Y не удалась if ( !(flag & ebd_WasDirX) ) SetBranches( objects, gabarit, eda_axisX, tier, flag ); else { // исчерпаны все возможности разделения FillLeaf( objects, gabarit, eda_axisX ); } } } else { C3D_ASSERT_UNCONDITIONAL( false ); } } break; default : { // разделение на ветви по наиболее вытянутой оси if ( gabDim == 3 ) { if ( (gabLens[0] + eps > gabLens[1]) && (gabLens[0] + eps > gabLens[2]) ) SetBranches( objects, gabarit, eda_axisX, tier, flag ); else if ( gabLens[1] + eps > gabLens[2] ) SetBranches( objects, gabarit, eda_axisY, tier, flag ); else SetBranches( objects, gabarit, eda_axisZ, tier, flag ); } else if ( gabDim == 2 ) { if ( gabLens[0] + eps > gabLens[1] ) SetBranches( objects, gabarit, eda_axisX, tier, flag ); else SetBranches( objects, gabarit, eda_axisY, tier, flag ); } } break; } } else { // один объект или достигнут максимальный уровень вложенности if ( (a == eda_anyDir) || (a == eda_newDir) ) { if ( gabDim == 3 ) { if ( (gabLens[0] + eps > gabLens[1]) && (gabLens[0] + eps > gabLens[2]) ) a = eda_axisX; else if ( gabLens[1] + eps > gabLens[2] ) a = eda_axisY; else a = eda_axisZ; } else if ( gabDim == 2 ) { if ( gabLens[0] + eps > gabLens[1] ) a = eda_axisX; else a = eda_axisY; } } FillLeaf( objects, gabarit, a ); } } } return res; } //------------------------------------------------------------------------------- // установка ветвей // --- template inline void MbCubeTree::SetBranches( std::vector & objects, const Cube & gabarit, DirectionAxis a, int tier, unsigned char & flag ) { direction = a; const size_t iCount = objects.size(); double eps = defaultEpsilon; GetCubeMinMaxComponents( gabarit, eps, minimum, maximum ); midst = 0.5 * (minimum + maximum); // центральное значение lower = midst; upper = midst; size_t lowerCount = 0; size_t upperCount = 0; size_t midstCount = 0; Cube lowerGabarit; Cube upperGabarit; Cube midstGabarit; for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = objects[i]; if ( obj.first != nullptr ) { const Cube & cube = obj.first->GetCube(); double pMin, pMax; GetCubeMinMaxComponents( cube, 0.0, pMin, pMax ); if ( pMax < midst - eps ) { lowerCount++; lowerGabarit |= cube; } else if ( pMin > midst + eps ) { upperCount++; upperGabarit |= cube; } else { lower = std_min( lower, pMin ); upper = std_max( upper, pMax ); midstCount++; midstGabarit |= cube; } } } std::vector lowerArray; std::vector upperArray; std::vector midstArray; lowerArray.reserve( lowerCount ); upperArray.reserve( upperCount ); midstArray.reserve( midstCount ); // KOMPAS-46058 //size_t iDelta = std_max( (size_t)1, iCount / 8 ); //lowerArray.reserve( iDelta ); //upperArray.reserve( iDelta ); //midstArray.reserve( iDelta ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = objects[i]; if ( obj.first != nullptr ) { const Cube & cube = obj.first->GetCube(); double pMin, pMax; GetCubeMinMaxComponents( cube, 0.0, pMin, pMax ); if ( pMax < midst - eps ) { // объект расположен ниже центрального значения lowerArray.emplace_back( obj ); } else if ( pMin > midst + eps ) { // объект расположен выше центрального значения upperArray.emplace_back( obj ); } else { // объект содержит центральное значение lower = std_min( lower, pMin ); upper = std_max( upper, pMax ); midstArray.emplace_back( obj ); } } } lower -= eps; // верхний размер центральной ветви, расширенный на погрешность upper += eps; // нижний размер центральной ветви, расширенный на погрешность lowerCount = lowerArray.size(); upperCount = upperArray.size(); midstCount = midstArray.size(); if ( (lowerCount > 0) || (upperCount > 0) ) { if ( lowerCount > 0 ) { // нижняя ветвь / lower branch C3D_ASSERT ( lowerBranch == nullptr ); if ( lowerBranch == nullptr ) lowerBranch = new MbCubeTree( dmType ); lowerBranch->InitTree( lowerArray, lowerGabarit, eda_anyDir, tier+1, 0 ); lowerArray.clear(); } if ( upperCount > 0 ) { // верхняя ветвь / upper branch C3D_ASSERT( upperBranch == nullptr ); if ( upperBranch == nullptr ) upperBranch = new MbCubeTree( dmType ); upperBranch->InitTree( upperArray, upperGabarit, eda_anyDir, tier+1, 0 ); upperArray.clear(); } if ( midstCount > 0 ) { // центральная ветвь / central branch C3D_ASSERT( midstBranch == nullptr ); if ( midstBranch == nullptr ) midstBranch = new MbCubeTree( dmType ); midstBranch->InitTree( midstArray, midstGabarit, direction, tier+1, 0 ); midstArray.clear(); } } else { // попытка разделить на ветви по-другому, ещё не использованному направлению bool freeDir = false; const size_t gabDim = gabarit.GetDimension(); if ( gabDim == 3 ) { freeDir = !((flag & ebd_WasDirX) && (flag & ebd_WasDirY) && (flag & ebd_WasDirZ)); } else if ( gabDim == 2 ) { freeDir = !((flag & ebd_WasDirX) && (flag & ebd_WasDirY)); } if ( midstCount > leafMinCount && freeDir ) { // установка битового флага flag в зависимости от направления direction SetFlag( flag ); InitTree( midstArray, midstGabarit, eda_newDir, tier, flag ); midstArray.clear(); } else { // разделение на ветви не произошло ни по одному направлению branchCube = gabarit; leafObjects = std::move( objects ); } } } //------------------------------------------------------------------------------- // установка битового флага flag в зависимости от направления direction // --- template inline void MbCubeTree::SetFlag( unsigned char & flag ) { if ( direction == eda_axisX ) flag |= ebd_WasDirX; else if ( direction == eda_axisY ) flag |= ebd_WasDirY; else if ( direction == eda_axisZ ) flag |= ebd_WasDirZ; } //------------------------------------------------------------------------------- // \ru Получить компонент точки в зависимости от направления direction. \en Get point component by branching direction. // --- template inline double MbCubeTree::GetPointComponent( const Point & pnt ) const { double w = minDoubleValue; switch ( direction ) { case eda_axisX : { w = pnt[0]; } break; case eda_axisY : { w = pnt[1]; } break; case eda_axisZ : { w = pnt[2]; } break; case eda_anyDir : { C3D_ASSERT_UNCONDITIONAL( false ); } break; case eda_newDir : { C3D_ASSERT_UNCONDITIONAL( false ); } break; default: break; } return w; } //------------------------------------------------------------------------------- // \ru Получить компонент точки в зависимости от направления direction. \en Get point component by branching direction. // --- template inline void MbCubeTree::GetCubeMinMaxComponents( const Cube & cube, double eps, double & wMin, double & wMax ) const { wMin = maxDoubleValue; wMax = minDoubleValue; switch ( direction ) { case eda_axisX: { wMin = cube.GetMin( 0 ) - eps; wMax = cube.GetMax( 0 ) + eps; } break; case eda_axisY: { wMin = cube.GetMin( 1 ) - eps; wMax = cube.GetMax( 1 ) + eps; } break; case eda_axisZ: { wMin = cube.GetMin( 2 ) - eps; wMax = cube.GetMax( 2 ) + eps; } break; case eda_anyDir: { C3D_ASSERT_UNCONDITIONAL( false ); } break; case eda_newDir: { C3D_ASSERT_UNCONDITIONAL( false ); } break; default: break; } } //------------------------------------------------------------------------------- // \ru Заполнение конечной ветви дерева (установка конечной ветви). \en Fill (set) terminal tree branch. // --- template inline void MbCubeTree::FillLeaf( std::vector & objects, const Cube & gabarit, DirectionAxis a ) { direction = a; const double eps = defaultEpsilon; GetCubeMinMaxComponents( gabarit, eps, minimum, maximum ); midst = 0.5 * (minimum + maximum); // центральное значение lower = minimum; upper = maximum; branchCube = gabarit; leafObjects = std::move( objects ); } //------------------------------------------------------------------------------- // \ru Готово ли дерево. \en Whether tree is ready. // --- template inline bool MbCubeTree::IsReady() const { return ( (midstBranch != nullptr) || (lowerBranch != nullptr) || (upperBranch != nullptr) || (leafObjects.size() > 0) ); } //------------------------------------------------------------------------------- // \ru Количество объектов в дереве. \en Number of objects in the tree. // --- template inline size_t MbCubeTree::Count() const { size_t cnt = leafObjects.size(); if ( midstBranch != nullptr ) cnt += midstBranch->Count(); if ( lowerBranch != nullptr ) cnt += lowerBranch->Count(); if ( upperBranch != nullptr ) cnt += upperBranch->Count(); return cnt; } //------------------------------------------------------------------------------- // \ru Количество узлов в дереве. \en Number of branches in the tree. // --- template inline size_t MbCubeTree::NodeCount() const { size_t count = 0; std::vector stack; stack.push_back( this ); while ( !stack.empty() ) { const TypeTree * ptr = stack.back(); stack.pop_back(); ++count; if ( ptr->lowerBranch != nullptr ) stack.push_back( ptr->lowerBranch ); if ( ptr->upperBranch != nullptr ) stack.push_back( ptr->upperBranch ); if ( ptr->midstBranch != nullptr ) stack.push_back( ptr->midstBranch ); } return count; } //------------------------------------------------------------------------------- // \ru Выдать содержащие точку объекты дерева. \en Get tree objects containing a point. // --- template inline void MbCubeTree::GetContainsObjects( const Point & pnt, double epsilon, std::vector & items ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double w = GetPointComponent( pnt ); if ( (w < (minimum - epsilon)) || ((maximum + epsilon) < w) ) return; // вне области / out of region if ( (w < (midst + epsilon)) && (lowerBranch != nullptr) ) { lowerBranch->GetContainsObjects( pnt, epsilon, items ); } if ( ((midst - epsilon) < w) && (upperBranch != nullptr) ) { upperBranch->GetContainsObjects( pnt, epsilon, items ); } if ( ((lower - epsilon) < w) && (w < (upper + epsilon)) && (midstBranch != nullptr) ) { midstBranch->GetContainsObjects( pnt, epsilon, items ); } } else { // содержимое конечной ветви // terminal branch content size_t iCount = leafObjects.size(); items.reserve( items.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { if ( obj.first->GetCube().Contains( pnt, epsilon ) ) items.push_back( obj.first ); } } } } //------------------------------------------------------------------------------- // \ru Выдать содержащие точку объекты дерева. \en Get tree objects containing a point. // --- template inline void MbCubeTree::GetContainsObjects( const Point & pnt, double epsilon, std::vector & indices ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double w = GetPointComponent( pnt ); if ( (w < (minimum - epsilon)) || ((maximum + epsilon) < w) ) return; // вне области / out of region if ( (w < (midst + epsilon)) && (lowerBranch != nullptr) ) { lowerBranch->GetContainsObjects( pnt, epsilon, indices ); } if ( ((midst - epsilon) < w) && (upperBranch != nullptr) ) { upperBranch->GetContainsObjects( pnt, epsilon, indices ); } if ( ((lower - epsilon) < w) && (w < (upper + epsilon)) && (midstBranch != nullptr) ) { midstBranch->GetContainsObjects( pnt, epsilon, indices ); } } else { // содержимое конечной ветви // terminal branch content size_t iCount = leafObjects.size(); indices.reserve( indices.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { if ( obj.first->GetCube().Contains( pnt, epsilon ) ) indices.push_back( obj.second ); } } } } //------------------------------------------------------------------------------- // \ru Выдать пересекающиеся с габаритным кубом объекты дерева. \en Get objects of the tree intersecting with the bounding box. // --- template inline void MbCubeTree::GetIntersectObjects( const Cube & gabarit, double epsilon, std::vector & items, bool skipOwnself ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double wMin, wMax; GetCubeMinMaxComponents( gabarit, 0.0, wMin, wMax ); if ( (wMax < minimum) || (maximum < wMin) ) return; // вне области / out of region if ( (wMin < midst) && (lowerBranch != nullptr) ) { lowerBranch->GetIntersectObjects( gabarit, epsilon, items, skipOwnself ); } if ( (midst < wMax) && (upperBranch != nullptr) ) { upperBranch->GetIntersectObjects( gabarit, epsilon, items, skipOwnself ); } if ( (lower < wMax) && (wMin < upper) && (midstBranch != nullptr) ) { midstBranch->GetIntersectObjects( gabarit, epsilon, items, skipOwnself ); } } else { // содержимое конечной ветви // terminal branch content size_t iCount = leafObjects.size(); items.reserve( items.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { if ( !skipOwnself || (&gabarit != &static_cast(obj.first->GetCube()) ) ) { // KOMPAS-20871 if ( obj.first->GetCube().Intersect( gabarit, epsilon ) ) items.push_back( obj.first ); } } } } } //------------------------------------------------------------------------------- // \ru Выдать пересекающиеся с габаритным кубом объекты дерева. \en Get objects of the tree intersecting with the bounding box. // --- template inline void MbCubeTree::GetIntersectObjects( const Cube & gabarit, double epsilon, std::vector & items, bool skipOwnself ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double wMin, wMax; GetCubeMinMaxComponents( gabarit, 0.0, wMin, wMax ); if ( (wMax < minimum) || (maximum < wMin) ) return; // вне области / out of region if ( (wMin < midst) && (lowerBranch != nullptr) ) { lowerBranch->GetIntersectObjects( gabarit, epsilon, items, skipOwnself ); } if ( (midst < wMax) && (upperBranch != nullptr) ) { upperBranch->GetIntersectObjects( gabarit, epsilon, items, skipOwnself ); } if ( (lower < wMax) && (wMin < upper) && (midstBranch != nullptr) ) { midstBranch->GetIntersectObjects( gabarit, epsilon, items, skipOwnself ); } } else { // содержимое конечной ветви // terminal branch content size_t iCount = leafObjects.size(); items.reserve( items.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { if ( !skipOwnself || (&gabarit != &static_cast(obj.first->GetCube()) ) ) { // KOMPAS-20871 if ( obj.first->GetCube().Intersect( gabarit, epsilon ) ) items.push_back( obj.second ); } } } } } //------------------------------------------------------------------------------- // \ru Выдать пересекающиеся с отрезком луча кубом объекты дерева. \en Get objects of the tree intersecting with the ray segment. // --- template inline void MbCubeTree::GetIntersectObjects( const Point & rayPnt1, const Point & rayPnt2, double eps, std::vector & itemIndices ) const { bool isBranch = (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr); bool isLeaf = !leafObjects.empty(); Point segmPnt1( rayPnt1 ), segmPnt2( rayPnt2 ); double segmLen = segmPnt1.DistanceToPoint( segmPnt2 ); double sameEps = std_min( eps, LENGTH_EPSILON ); sameEps = std_max( sameEps, EXTENT_EPSILON ); C3D_ASSERT( !branchCube.IsEmpty() ); // C3D_ASSERT( !rayPnt1.IsSame( rayPnt2, sameEps ) ); if ( isBranch || isLeaf ) { if ( !branchCube.IsEmpty() && !rayPnt1.IsSame( rayPnt2, sameEps ) ) { Vector rayVect( rayPnt1, rayPnt2 ); { double rayLen = rayVect.Length(); rayVect /= rayLen; } double minParam = maxDoubleValue; double maxParam = minDoubleValue; Point vertPnt; Vector vertVect; size_t vertCnt = branchCube.GetVerticesCount(); for ( size_t vertInd = 0; vertInd < vertCnt; ++vertInd ) { branchCube.GetVertex( vertInd, vertPnt ); vertVect.Init( rayPnt1, vertPnt ); double vt = (rayVect * vertVect); if ( vt < minParam ) minParam = vt; if ( vt > maxParam ) maxParam = vt; } segmPnt1.Set( rayPnt1, rayVect, minParam ); segmPnt2.Set( rayPnt1, rayVect, maxParam ); segmLen = segmPnt1.DistanceToPoint( segmPnt2 ); } } bool diffPnt = !segmPnt1.IsSame( segmPnt2, sameEps ); if ( isBranch ) { double w1 = minDoubleValue; double w2 = minDoubleValue; switch ( direction ) { case eda_axisX : { w1 = segmPnt1[0]; w2 = segmPnt2[0]; } break; case eda_axisY : { w1 = segmPnt1[1]; w2 = segmPnt2[1]; } break; case eda_axisZ : { w1 = segmPnt1[2]; w2 = segmPnt2[2]; } break; case eda_anyDir : { C3D_ASSERT_UNCONDITIONAL( false ); } break; case eda_newDir : { C3D_ASSERT_UNCONDITIONAL( false ); } break; default: break; } double wMin = std_min( w1, w2 ); double wMax = std_max( w1, w2 ); if ( (wMax < (minimum - eps)) || ((maximum + eps) < wMin) ) return; // вне области / out of region if ( (wMin < (midst + eps)) && (lowerBranch != nullptr) ) { if ( diffPnt ) { lowerBranch->GetIntersectObjects( segmPnt1, segmPnt2, eps, itemIndices ); }else{ lowerBranch->GetContainsObjects( segmPnt1, eps, itemIndices ); } } if ( (wMax > (midst - eps)) && (upperBranch != nullptr) ) { if ( diffPnt ) { upperBranch->GetIntersectObjects( segmPnt1, segmPnt2, eps, itemIndices ); } else { upperBranch->GetContainsObjects( segmPnt1, eps, itemIndices ); } } if ( (wMax > (lower - eps)) && (wMin < (upper + eps)) && (midstBranch != nullptr) ) { if ( diffPnt ) { midstBranch->GetIntersectObjects( segmPnt1, segmPnt2, eps, itemIndices ); } else { midstBranch->GetContainsObjects( segmPnt1, eps, itemIndices ); } } } else if ( isLeaf ) { // содержимое конечной ветви // terminal branch content if ( !diffPnt ) { GetContainsObjects( segmPnt1, eps, itemIndices ); return; } const Cube segmCube( segmPnt1, segmPnt2, true ); Point objPnt, segmPnt; segmPnt.Set( segmPnt1, 0.5, segmPnt2, 0.5 ); Vector vect1, vect2; size_t iCount = leafObjects.size(); itemIndices.reserve( itemIndices.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { bool add = false; const Cube & objCube = obj.first->GetCube(); if ( objCube.Contains( segmPnt, eps ) ) add = true; else if ( objCube.Intersect( segmCube, eps ) ) { add = true; if ( segmLen > eps ) { objCube.GetCenter( objPnt ); double objDist = 0.5 * objCube.GetDiagonal(); vect1.Init( segmPnt1, objPnt ); vect2.Init( segmPnt1, segmPnt2 ); vect2 /= segmLen; vect2 = (vect1 * vect2) * vect2; vect1 -= vect2; double d = ::sqrt( vect1 * vect1 ); if ( d > objDist + eps ) add = false; } } if ( add ) itemIndices.push_back( obj.second ); } } } } //------------------------------------------------------------------------------- // Измерить расстояние по выбранной в мере // --- template inline double MbCubeTree::GetDistance( const Type & object, const Point & pnt, bool signedDistance ) const { double d = maxDoubleValue; if ( dmType == edm_MaxCoord ) d = object.GetCube().DistanceToPoint( pnt ); else if ( dmType == edm_CubeCenter ) { Point pnt0; object.GetCube().GetCenter( pnt0 ); d = pnt0.DistanceToPoint( pnt ); } if ( !signedDistance ) d = ::fabs(d); return d; } //------------------------------------------------------------------------------- // \ru Найти ближайший к кубу объект дерева. \en Find the nearest tree object to a bounding box. // --- template inline void MbCubeTree::FindNearestObject( const Cube & gabarit, double & distance, const Type *& item, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double wMin, wMax; GetCubeMinMaxComponents( gabarit, 0.0, wMin, wMax ); if ( (wMax < (minimum-distance)) || ((maximum+distance) < wMin) ) return; // вне области / out of region if ( (wMin < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->FindNearestObject( gabarit, distance, item, eps ); } if ( ((midst-distance) < wMax) && (upperBranch != nullptr) ) { upperBranch->FindNearestObject( gabarit, distance, item, eps ); } if ( ((lower-distance) < wMax) && (wMin < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->FindNearestObject( gabarit, distance, item, eps ); } } else { // содержимое конечной ветви // terminal branch content for ( size_t i = 0, iCount = leafObjects.size(); i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = obj.first->GetCube().DistanceToCube( gabarit, eps ); if ( distance > d - eps ) { item = obj.first; distance = d; } } } } } //------------------------------------------------------------------------------- // \ru Найти ближайший к кубу объект дерева в виде индекса в исходном массиве. \en Find the nearest tree object to a bounding box (as an index in initial array of objects). // --- template inline void MbCubeTree::FindNearestObject( const Cube & gabarit, double & distance, size_t & index, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double wMin, wMax; GetCubeMinMaxComponents( gabarit, 0.0, wMin, wMax ); if ( (wMax < (minimum-distance)) || ((maximum+distance) < wMin) ) return; // вне области / out of region if ( (wMin < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->FindNearestObject( gabarit, distance, index, eps ); } if ( ((midst-distance) < wMax) && (upperBranch != nullptr) ) { upperBranch->FindNearestObject( gabarit, distance, index, eps ); } if ( ((lower-distance) < wMax) && (wMin < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->FindNearestObject( gabarit, distance, index, eps ); } } else { // содержимое конечной ветви // terminal branch content for ( size_t i = 0, iCount = leafObjects.size(); i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = obj.first->GetCube().DistanceToCube( gabarit, eps ); if ( distance > d - eps ) { index = obj.second; distance = d; } } } } } //------------------------------------------------------------------------------- // \ru Выдать объекты дерева, расположенные к кубу ближе заданного расстояния. \en Get tree objects that are closer to the cube than the specified distance. // --- template inline void MbCubeTree::GetNearestObjects( const Cube & gabarit, const double distance, std::vector & itemDistances, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double wMin, wMax; GetCubeMinMaxComponents( gabarit, 0.0, wMin, wMax ); if ( (wMax < (minimum-distance)) || ((maximum+distance) < wMin) ) return; // вне области / out of region if ( (wMin < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->GetNearestObjects( gabarit, distance, itemDistances, eps ); } if ( ((midst-distance) < wMax) && (upperBranch != nullptr) ) { upperBranch->GetNearestObjects( gabarit, distance, itemDistances, eps ); } if ( ((lower-distance) < wMax) && (wMin < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->GetNearestObjects( gabarit, distance, itemDistances, eps ); } } else { // содержимое конечной ветви // terminal branch content for ( size_t i = 0, iCount = leafObjects.size(); i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = obj.first->GetCube().DistanceToCube( gabarit, eps ); if ( distance > d - eps ) { IndexDistance itemDistance( obj.second, d ); itemDistances.push_back( itemDistance ); } } } } } //------------------------------------------------------------------------------- // \ru Найти ближайший к точке объект дерева. \en Find the nearest tree object to a point. // --- template inline void MbCubeTree::FindNearestObject( const Point & pnt, double & distance, const Type *& item, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double w = GetPointComponent( pnt ); if ( (w < (minimum-distance)) || ((maximum+distance) < w) ) return; // вне области / out of region if ( (w < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->FindNearestObject( pnt, distance, item, eps ); } if ( ((midst-distance) < w) && (upperBranch != nullptr) ) { upperBranch->FindNearestObject( pnt, distance, item, eps ); } if ( ((lower-distance) < w) && (w < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->FindNearestObject( pnt, distance, item, eps ); } } else { // содержимое конечной ветви // terminal branch content for ( size_t i = 0, iCount = leafObjects.size(); i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = GetDistance( *obj.first, pnt, false ); if ( distance > d - eps ) { item = obj.first; distance = d; } } } } } //------------------------------------------------------------------------------- // Выдать ближайший к точке объект дерева // --- template inline void MbCubeTree::FindNearestObject( const Point & pnt, double & distance, size_t & index, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double w = GetPointComponent( pnt ); if ( (w < (minimum-distance)) || ((maximum+distance) < w) ) return; // вне области / out of region if ( (w < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->FindNearestObject( pnt, distance, index, eps ); } if ( ((midst-distance) < w) && (upperBranch != nullptr) ) { upperBranch->FindNearestObject( pnt, distance, index, eps ); } if ( ((lower-distance) < w) && (w < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->FindNearestObject( pnt, distance, index, eps ); } } else { // содержимое конечной ветви // terminal branch content for ( size_t i = 0, iCount = leafObjects.size(); i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = GetDistance( *obj.first, pnt, false ); if ( distance > d - eps ) { index = obj.second; distance = d; } } } } } //------------------------------------------------------------------------------- // \ru Выдать объекты дерева, расположенные к точке ближе заданного расстояния. \en Get tree objects that are closer to the point than the specified distance. // --- template inline void MbCubeTree::GetNearestObjects( const Point & pnt, const double distance, std::vector & itemDistances, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double w = GetPointComponent( pnt ); if ( (w < (minimum-distance)) || ((maximum+distance) < w) ) return; // вне области / out of region if ( (w < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->GetNearestObjects( pnt, distance, itemDistances, eps ); } if ( ((midst-distance) < w) && (upperBranch != nullptr) ) { upperBranch->GetNearestObjects( pnt, distance, itemDistances, eps ); } if ( ((lower-distance) < w) && (w < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->GetNearestObjects( pnt, distance, itemDistances, eps ); } } else { // содержимое конечной ветви // terminal branch content size_t iCount = leafObjects.size(); itemDistances.reserve( itemDistances.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = GetDistance( *obj.first, pnt, true ); if ( distance > d - eps ) { ItemDistance itemDistance( obj.first, d ); itemDistances.push_back( itemDistance ); } } } } } //------------------------------------------------------------------------------- // \ru Выдать индексы объектов дерева, расположенные к точке ближе заданного расстояния. \en Get tree objects indices that are closer to the point than the specified distance. // --- template inline void MbCubeTree::GetNearestObjects( const Point & pnt, const double distance, std::vector & itemDistances, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double w = GetPointComponent( pnt ); if ( (w < (minimum-distance)) || ((maximum+distance) < w) ) return; // вне области / out of region if ( (w < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->GetNearestObjects( pnt, distance, itemDistances, eps ); } if ( ((midst-distance) < w) && (upperBranch != nullptr) ) { upperBranch->GetNearestObjects( pnt, distance, itemDistances, eps ); } if ( ((lower-distance) < w) && (w < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->GetNearestObjects( pnt, distance, itemDistances, eps ); } } else { // содержимое конечной ветви // terminal branch content size_t iCount = leafObjects.size(); itemDistances.reserve( itemDistances.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = GetDistance( *obj.first, pnt, true ); if ( distance > d - eps ) { IndexDistance itemDistance( obj.second, d ); itemDistances.push_back( itemDistance ); } } } } } //------------------------------------------------------------------------------- // \ru Выдать индексы объектов дерева, расположенные к точке ближе заданного расстояния. \en Get tree objects indices that are closer to the point than the specified distance. // --- template inline void MbCubeTree::GetNearestObjects( const Point & pnt, const double distance, std::vector & itemIndices, double eps ) const { if ( (lowerBranch != nullptr) || (upperBranch != nullptr) || (midstBranch != nullptr) ) { double w = GetPointComponent( pnt ); if ( (w < (minimum-distance)) || ((maximum+distance) < w) ) return; // вне области / out of region if ( (w < (midst+distance)) && (lowerBranch != nullptr) ) { lowerBranch->GetNearestObjects( pnt, distance, itemIndices, eps ); } if ( ((midst-distance) < w) && (upperBranch != nullptr) ) { upperBranch->GetNearestObjects( pnt, distance, itemIndices, eps ); } if ( ((lower-distance) < w) && (w < (upper+distance)) && (midstBranch != nullptr) ) { midstBranch->GetNearestObjects( pnt, distance, itemIndices, eps ); } } else { // содержимое конечной ветви // terminal branch content size_t iCount = leafObjects.size(); itemIndices.reserve( itemIndices.size() + iCount ); for ( size_t i = 0; i < iCount; ++i ) { const ItemIndex & obj = leafObjects[i]; if ( obj.first != nullptr ) { double d = GetDistance( *obj.first, pnt, true ); if ( distance > d - eps ) { itemIndices.push_back( obj.second ); } } } } } //------------------------------------------------------------------------------ /** \brief \ru Вспомогательная структура для элемента приоритетной очереди. \en Auxilliary structure for the priority queue element. \~ \ingroup Base_Items */ // --- template struct NodeElem { const Type * _ptr; ///< \ru Указатель на узел дерева. \en Pointer to the tree node. double _weight; ///< \ru Расстояние от куба узла до точки. \en Distance from the node bounding box to the point. /// \ru Конструктор. \en Constructor. NodeElem() : _ptr( nullptr ), _weight( MB_MAXDOUBLE ) {} /// \ru Конструктор. \en Constructor. NodeElem( const Type * ptr, double weigth ) : _ptr( ptr ), _weight( weigth ) {} /// \ru Конструктор копирования. \en Copy constructor. NodeElem( const NodeElem & that ) : _ptr( that._ptr ), _weight( that._weight ) {} /// \ru Деструктор. \en Destructor. ~NodeElem() {} /// \ru Оператор присваивания. \en An assignment operator. NodeElem & operator = ( const NodeElem & that ) { _ptr = that._ptr; _weight = that._weight; return *this; } /// \ru Оператор сравнения. \en Comparison operator. bool operator < ( const NodeElem & that ) const { return _weight > that._weight; } }; //------------------------------------------------------------------------------- // \ru Выдать индекс объекта дерева, ближайшего к точке. \en Get tree objects index that are closest to the point. // --- template double MbCubeTree::FindNearestObject( const Point & pnt, const MbTreeDistanceToElementBase & calc, size_t & index ) const { double dist2( MB_MAXDOUBLE ); index = SYS_MAX_T; std::priority_queue< NodeElem > queue; queue.push( NodeElem(this, 0.) ); while ( !queue.empty() ) { const NodeElem node = queue.top(); // Первым рассматриваем узлы, наиболее близкие к точке. queue.pop(); double d2 = node._ptr->branchCube.DistanceToPoint2( pnt ); if ( d2 > dist2 ) break; // Все оставшиеся узлы дальше уже найденного расстояния. if ( ( node._ptr->lowerBranch != nullptr ) || ( node._ptr->upperBranch != nullptr ) || ( node._ptr->midstBranch != nullptr ) ) { if ( node._ptr->lowerBranch != nullptr ) { d2 = node._ptr->lowerBranch->branchCube.DistanceToPoint2( pnt ); if ( d2 < dist2 ) queue.push( NodeElem( node._ptr->lowerBranch, d2 ) ); } if ( node._ptr->upperBranch != nullptr ) { d2 = node._ptr->upperBranch->branchCube.DistanceToPoint2( pnt ); if ( d2 < dist2 ) queue.push( NodeElem( node._ptr->upperBranch, d2 ) ); } if ( node._ptr->midstBranch != nullptr ) { d2 = node._ptr->midstBranch->branchCube.DistanceToPoint2( pnt ); if ( d2 < dist2 ) queue.push( NodeElem( node._ptr->midstBranch, d2 ) ); } } else { // содержимое конечной ветви // terminal branch content for ( size_t i = 0, iCount = node._ptr->leafObjects.size(); i < iCount; ++i ) { const ItemIndex & obj = node._ptr->leafObjects[i]; d2 = obj.first->DistanceToPoint2( pnt ); if ( d2 < dist2 ) { d2 = calc.DistanceToElement2( pnt, obj.second ); if ( d2 < dist2 ) { dist2 = d2; index = obj.second; } } } } } return dist2; } #endif