Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-16 09:17:57

0001 // Created on: 2007-05-26
0002 // Created by: Andrey BETENEV
0003 // Copyright (c) 2007-2014 OPEN CASCADE SAS
0004 //
0005 // This file is part of Open CASCADE Technology software library.
0006 //
0007 // This library is free software; you can redistribute it and/or modify it under
0008 // the terms of the GNU Lesser General Public License version 2.1 as published
0009 // by the Free Software Foundation, with special exception defined in the file
0010 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0011 // distribution for complete text of the license and disclaimer of any warranty.
0012 //
0013 // Alternatively, this file may be used under the terms of Open CASCADE
0014 // commercial license or contractual agreement.
0015 
0016 #ifndef NCollection_CellFilter_HeaderFile
0017 #define NCollection_CellFilter_HeaderFile
0018 
0019 #include <NCollection_LocalArray.hxx>
0020 #include <NCollection_Array1.hxx>
0021 #include <Standard_HashUtils.hxx>
0022 #include <NCollection_Map.hxx>
0023 #include <NCollection_IncAllocator.hxx>
0024 
0025 //! Auxiliary enumeration serving as response from method Inspect
0026 enum NCollection_CellFilter_Action
0027 {
0028   CellFilter_Keep  = 0, //!< Target is needed and should be kept
0029   CellFilter_Purge = 1  //!< Target is not needed and can be removed from the current cell
0030 };
0031 
0032 /**
0033  * A data structure for sorting geometric objects (called targets) in
0034  * n-dimensional space into cells, with associated algorithm for fast checking
0035  * of coincidence (overlapping, intersection, etc.) with other objects
0036  * (called here bullets).
0037  *
0038  * Description
0039  *
0040  * The algorithm is based on hash map, thus it has linear time of initialization
0041  * (O(N) where N is number of cells covered by added targets) and constant-time
0042  * search for one bullet (more precisely, O(M) where M is number of cells covered
0043  * by the bullet).
0044  *
0045  * The idea behind the algorithm is to separate each coordinate of the space
0046  * into equal-size cells. Note that this works well when cell size is
0047  * approximately equal to the characteristic size of the involved objects
0048  * (targets and bullets; including tolerance eventually used for coincidence
0049  * check).
0050  *
0051  * Usage
0052  *
0053  * The target objects to be searched are added to the tool by methods Add();
0054  * each target is classified as belonging to some cell(s). The data on cells
0055  * (list of targets found in each one) are stored in the hash map with key being
0056  * cumulative index of the cell by all coordinates.
0057  * Thus the time needed to find targets in some cell is O(1) * O(number of
0058  * targets in the cell).
0059  *
0060  * As soon as all the targets are added, the algorithm is ready to check for
0061  * coincidence.
0062  * To find the targets coincident with any given bullet, it checks all the
0063  * candidate targets in the cell(s) covered by the bullet object
0064  * (methods Inspect()).
0065  *
0066  * The methods Add() and Inspect() have two flavours each: one accepts
0067  * single point identifying one cell, another accept two points specifying
0068  * the range of cells. It should be noted that normally at least one of these
0069  * methods is called as for range of cells: either due to objects having non-
0070  * zero size, or in order to account for the tolerance when objects are points.
0071  *
0072  * The set of targets can be modified during the process: new targets can be
0073  * added by Add(), existing targets can be removed by Remove().
0074  *
0075  * Implementation
0076  *
0077  * The algorithm is implemented as template class, thus it is capable to
0078  * work with objects of any type. The only argument of the template should be
0079  * the specific class providing all necessary features required by the
0080  * algorithm:
0081  *
0082  * - typedef "Target" defining type of target objects.
0083  *   This type must have copy constructor
0084  *
0085  * - typedef "Point" defining type of geometrical points used
0086  *
0087  * - static constexpr int Dimension whose value must be dimension of the point
0088  *
0089  * - method Coord() returning value of the i-th coordinate of the point:
0090  *
0091  *   static double Coord (int i, const Point& thePnt);
0092  *
0093  *   Note that index i is from 0 to Dimension-1.
0094  *
0095  * - method IsEqual() used by Remove() to identify objects to be removed:
0096  *
0097  *   bool IsEqual (const Target& theT1, const Target& theT2);
0098  *
0099  * - method Inspect() performing necessary actions on the candidate target
0100  *   object (usually comparison with the currently checked bullet object):
0101  *
0102  *   NCollection_CellFilter_Action Inspect (const Target& theObject);
0103  *
0104  *   The returned value can be used to command CellFilter
0105  *   to remove the inspected item from the current cell; this allows
0106  *   to exclude the items that has been processed and are not needed any
0107  *   more in further search (for better performance).
0108  *
0109  *   Note that method Inspect() can be const and/or virtual.
0110  */
0111 
0112 template <class Inspector>
0113 class NCollection_CellFilter
0114 {
0115 public:
0116   typedef typename Inspector::Target Target;
0117   typedef typename Inspector::Point  Point;
0118 
0119 public:
0120   //! Constructor; initialized by dimension count and cell size.
0121   //!
0122   //! Note: the cell size must be ensured to be greater than
0123   //! maximal coordinate of the involved points divided by INT_MAX,
0124   //! in order to avoid integer overflow of cell index.
0125   //!
0126   //! By default cell size is 0, which is invalid; thus if default
0127   //! constructor is used, the tool must be initialized later with
0128   //! appropriate cell size by call to Reset()
0129   //! Constructor when dimension count is unknown at compilation time.
0130   NCollection_CellFilter(const int                                    theDim,
0131                          const double                                 theCellSize = 0,
0132                          const occ::handle<NCollection_IncAllocator>& theAlloc    = nullptr)
0133       : myCellSize(0, theDim - 1)
0134   {
0135     myDim = theDim;
0136     Reset(theCellSize, theAlloc);
0137   }
0138 
0139   //! Constructor when dimension count is known at compilation time.
0140   NCollection_CellFilter(const double                                 theCellSize = 0,
0141                          const occ::handle<NCollection_IncAllocator>& theAlloc    = nullptr)
0142       : myCellSize(0, Inspector::Dimension - 1)
0143   {
0144     myDim = Inspector::Dimension;
0145     Reset(theCellSize, theAlloc);
0146   }
0147 
0148   //! Clear the data structures, set new cell size and allocator
0149   void Reset(double theCellSize, const occ::handle<NCollection_IncAllocator>& theAlloc = nullptr)
0150   {
0151     for (int i = 0; i < myDim; i++)
0152       myCellSize(i) = theCellSize;
0153     resetAllocator(theAlloc);
0154   }
0155 
0156   //! Clear the data structures and set new cell sizes and allocator
0157   void Reset(NCollection_Array1<double>&                  theCellSize,
0158              const occ::handle<NCollection_IncAllocator>& theAlloc = nullptr)
0159   {
0160     myCellSize = theCellSize;
0161     resetAllocator(theAlloc);
0162   }
0163 
0164   //! Adds a target object for further search at a point (into only one cell)
0165   void Add(const Target& theTarget, const Point& thePnt)
0166   {
0167     Cell aCell(thePnt, myCellSize);
0168     add(aCell.index, theTarget);
0169   }
0170 
0171   //! Adds a target object for further search in the range of cells
0172   //! defined by two points (the first point must have all coordinates equal or
0173   //! less than the same coordinate of the second point)
0174   void Add(const Target& theTarget, const Point& thePntMin, const Point& thePntMax)
0175   {
0176     // get cells range by minimal and maximal coordinates
0177     Cell aCellMin(thePntMin, myCellSize);
0178     Cell aCellMax(thePntMax, myCellSize);
0179     Cell aCell(aCellMin.index);
0180     // add object recursively into all cells in range
0181     iterateAdd(myDim - 1, aCell.index, aCellMin, aCellMax, theTarget);
0182   }
0183 
0184   //! Find a target object at a point and remove it from the structures.
0185   //! For usage of this method "operator ==" should be defined for Target.
0186   void Remove(const Target& theTarget, const Point& thePnt)
0187   {
0188     Cell aCell(thePnt, myCellSize);
0189     remove(aCell, theTarget);
0190   }
0191 
0192   //! Find a target object in the range of cells defined by two points and
0193   //! remove it from the structures
0194   //! (the first point must have all coordinates equal or
0195   //! less than the same coordinate of the second point).
0196   //! For usage of this method "operator ==" should be defined for Target.
0197   void Remove(const Target& theTarget, const Point& thePntMin, const Point& thePntMax)
0198   {
0199     // get cells range by minimal and maximal coordinates
0200     Cell aCellMin(thePntMin, myCellSize);
0201     Cell aCellMax(thePntMax, myCellSize);
0202     Cell aCell(aCellMin.index);
0203     // remove object recursively from all cells in range
0204     iterateRemove(myDim - 1, aCell, aCellMin, aCellMax, theTarget);
0205   }
0206 
0207   //! Inspect all targets in the cell corresponding to the given point
0208   void Inspect(const Point& thePnt, Inspector& theInspector)
0209   {
0210     Cell aCell(thePnt, myCellSize);
0211     inspect(aCell, theInspector);
0212   }
0213 
0214   //! Inspect all targets in the cells range limited by two given points
0215   //! (the first point must have all coordinates equal or
0216   //! less than the same coordinate of the second point)
0217   void Inspect(const Point& thePntMin, const Point& thePntMax, Inspector& theInspector)
0218   {
0219     // get cells range by minimal and maximal coordinates
0220     Cell aCellMin(thePntMin, myCellSize);
0221     Cell aCellMax(thePntMax, myCellSize);
0222     Cell aCell(aCellMin.index);
0223     // inspect object recursively into all cells in range
0224     iterateInspect(myDim - 1, aCell, aCellMin, aCellMax, theInspector);
0225   }
0226 
0227 protected:
0228   /**
0229    * Auxiliary class for storing points belonging to the cell as the list
0230    */
0231   struct ListNode
0232   {
0233     ListNode() = delete;
0234 
0235     Target    Object;
0236     ListNode* Next;
0237   };
0238 
0239   //! Cell index type.
0240   typedef int                                        Cell_IndexType;
0241   typedef NCollection_LocalArray<Cell_IndexType, 10> CellIndex;
0242 
0243   /**
0244    * Auxiliary structure representing a cell in the space.
0245    * Cells are stored in the map, each cell contains list of objects
0246    * that belong to that cell.
0247    */
0248   struct Cell
0249   {
0250   public:
0251     //! Constructor; computes cell indices
0252     Cell(const Point& thePnt, const NCollection_Array1<double>& theCellSize)
0253         : index(theCellSize.Length()),
0254           Objects(nullptr)
0255     {
0256       for (int i = 0; i < theCellSize.Length(); i++)
0257       {
0258         double aVal = (double)(Inspector::Coord(i, thePnt) / theCellSize(theCellSize.Lower() + i));
0259         // If the value of index is greater than
0260         // INT_MAX it is decreased correspondingly for the value of INT_MAX. If the value
0261         // of index is less than INT_MIN it is increased correspondingly for the absolute
0262         // value of INT_MIN.
0263         index[i] = Cell_IndexType((aVal > INT_MAX - 1)   ? fmod(aVal, (double)INT_MAX)
0264                                   : (aVal < INT_MIN + 1) ? fmod(aVal, (double)INT_MIN)
0265                                                          : aVal);
0266       }
0267     }
0268 
0269     //! Constructor from cell index; creates a lookup-only cell (no object list).
0270     Cell(const CellIndex& theIndex)
0271         : index(theIndex.Size()),
0272           Objects(nullptr)
0273     {
0274       std::memcpy(index, theIndex, theIndex.Size() * sizeof(Cell_IndexType));
0275     }
0276 
0277     //! Move constructor: transfers ownership of the object list
0278     Cell(Cell&& theOther) noexcept
0279         : index(std::move(theOther.index)),
0280           Objects(theOther.Objects)
0281     {
0282       theOther.Objects = nullptr;
0283     }
0284 
0285     Cell& operator=(Cell&& theOther) noexcept
0286     {
0287       index            = std::move(theOther.index);
0288       Objects          = theOther.Objects;
0289       theOther.Objects = nullptr;
0290       return *this;
0291     }
0292 
0293     //! Destructor; calls destructors for targets contained in the list
0294     ~Cell()
0295     {
0296       for (ListNode* aNode = Objects; aNode; aNode = aNode->Next)
0297         aNode->Object.~Target();
0298       // note that list nodes need not to be freed, since IncAllocator is used
0299       Objects = nullptr;
0300     }
0301 
0302     //! Compare cell with other one
0303     bool IsEqual(const Cell& theOther) const noexcept
0304     {
0305       const size_t aDim = index.Size();
0306       if (aDim != theOther.index.Size())
0307         return false;
0308       for (size_t i = 0; i < aDim; i++)
0309         if (index[i] != theOther.index[i])
0310           return false;
0311       return true;
0312     }
0313 
0314     bool operator==(const Cell& theOther) const noexcept { return IsEqual(theOther); }
0315 
0316   public:
0317     CellIndex index;
0318     ListNode* Objects;
0319   };
0320 
0321   struct CellHasher
0322   {
0323     size_t operator()(const Cell& theCell) const noexcept
0324     {
0325       const std::size_t aDim = theCell.index.Size();
0326       return opencascade::hashBytes(&theCell.index[0],
0327                                     static_cast<int>(aDim * sizeof(Cell_IndexType)));
0328     }
0329 
0330     bool operator()(const Cell& theCell1, const Cell& theCell2) const noexcept
0331     {
0332       return theCell1 == theCell2;
0333     }
0334   };
0335 
0336   typedef NCollection_Map<Cell, CellHasher> CellMap;
0337 
0338 protected:
0339   //! Reset allocator to the new one
0340   void resetAllocator(const occ::handle<NCollection_IncAllocator>& theAlloc)
0341   {
0342     if (theAlloc.IsNull())
0343       myAllocator = new NCollection_IncAllocator;
0344     else
0345       myAllocator = theAlloc;
0346     myCells.Clear(myAllocator);
0347   }
0348 
0349   //! Add a new target object into the specified cell
0350   void add(const CellIndex& theIndex, const Target& theTarget)
0351   {
0352     // add a new cell or get reference to existing one
0353     Cell& aMapCell = const_cast<Cell&>(myCells.TryEmplaced(theIndex));
0354 
0355     // create a new list node and add it to the beginning of the list
0356     ListNode* aNode = (ListNode*)myAllocator->Allocate(sizeof(ListNode));
0357     new (&aNode->Object) Target(theTarget);
0358     aNode->Next      = aMapCell.Objects;
0359     aMapCell.Objects = aNode;
0360   }
0361 
0362   //! Internal addition function, performing iteration for adjacent cells
0363   //! by one dimension; called recursively to cover all dimensions
0364   void iterateAdd(int           idim,
0365                   CellIndex&    theIndex,
0366                   const Cell&   theMinIndex,
0367                   const Cell&   theMaxIndex,
0368                   const Target& theTarget)
0369   {
0370     const Cell_IndexType aStart = theMinIndex.index[idim];
0371     const Cell_IndexType anEnd  = theMaxIndex.index[idim];
0372     for (Cell_IndexType i = aStart; i <= anEnd; ++i)
0373     {
0374       theIndex[idim] = i;
0375       if (idim) // recurse
0376       {
0377         iterateAdd(idim - 1, theIndex, theMinIndex, theMaxIndex, theTarget);
0378       }
0379       else // add to this cell
0380       {
0381         add(theIndex, theTarget);
0382       }
0383     }
0384   }
0385 
0386   //! Remove the target object from the specified cell
0387   void remove(const Cell& theCell, const Target& theTarget)
0388   {
0389     // Modifying the Objects field does not affect the hash, const_cast is safe
0390     auto aMapCellOpt = myCells.Contained(theCell);
0391     if (!aMapCellOpt)
0392       return;
0393 
0394     Cell& aMapCell = const_cast<Cell&>(aMapCellOpt->get());
0395 
0396     // iterate by objects in the cell and check each
0397     ListNode* aNode = aMapCell.Objects;
0398     ListNode* aPrev = nullptr;
0399     while (aNode)
0400     {
0401       ListNode* aNext = aNode->Next;
0402       if (Inspector::IsEqual(aNode->Object, theTarget))
0403       {
0404         aNode->Object.~Target();
0405         (aPrev ? aPrev->Next : aMapCell.Objects) = aNext;
0406         // note that aNode itself need not to be freed, since IncAllocator is used
0407       }
0408       else
0409         aPrev = aNode;
0410       aNode = aNext;
0411     }
0412 
0413     // cleanup empty cell to prevent dead cell accumulation
0414     if (!aMapCell.Objects)
0415       myCells.Remove(theCell);
0416   }
0417 
0418   //! Internal removal function, performing iteration for adjacent cells
0419   //! by one dimension; called recursively to cover all dimensions
0420   void iterateRemove(int           idim,
0421                      Cell&         theCell,
0422                      const Cell&   theCellMin,
0423                      const Cell&   theCellMax,
0424                      const Target& theTarget)
0425   {
0426     const Cell_IndexType aStart = theCellMin.index[idim];
0427     const Cell_IndexType anEnd  = theCellMax.index[idim];
0428     for (Cell_IndexType i = aStart; i <= anEnd; ++i)
0429     {
0430       theCell.index[idim] = i;
0431       if (idim) // recurse
0432       {
0433         iterateRemove(idim - 1, theCell, theCellMin, theCellMax, theTarget);
0434       }
0435       else // remove from this cell
0436       {
0437         remove(theCell, theTarget);
0438       }
0439     }
0440   }
0441 
0442   //! Inspect the target objects in the specified cell.
0443   void inspect(const Cell& theCell, Inspector& theInspector)
0444   {
0445     // Modifying the Objects field does not affect the hash, const_cast is safe
0446     auto aMapCellOpt = myCells.Contained(theCell);
0447     if (!aMapCellOpt)
0448       return;
0449 
0450     Cell& aMapCell = const_cast<Cell&>(aMapCellOpt->get());
0451 
0452     // iterate by objects in the cell and check each
0453     ListNode* aNode = aMapCell.Objects;
0454     ListNode* aPrev = nullptr;
0455     while (aNode)
0456     {
0457       ListNode*                     aNext    = aNode->Next;
0458       NCollection_CellFilter_Action anAction = theInspector.Inspect(aNode->Object);
0459       // delete items requested to be purged
0460       if (anAction == CellFilter_Purge)
0461       {
0462         aNode->Object.~Target();
0463         (aPrev ? aPrev->Next : aMapCell.Objects) = aNext;
0464         // note that aNode itself need not to be freed, since IncAllocator is used
0465       }
0466       else
0467         aPrev = aNode;
0468       aNode = aNext;
0469     }
0470 
0471     // cleanup empty cell to prevent dead cell accumulation
0472     if (!aMapCell.Objects)
0473       myCells.Remove(theCell);
0474   }
0475 
0476   //! Inspect the target objects in the specified range of the cells
0477   void iterateInspect(int         idim,
0478                       Cell&       theCell,
0479                       const Cell& theCellMin,
0480                       const Cell& theCellMax,
0481                       Inspector&  theInspector)
0482   {
0483     const Cell_IndexType aStart = theCellMin.index[idim];
0484     const Cell_IndexType anEnd  = theCellMax.index[idim];
0485     for (Cell_IndexType i = aStart; i <= anEnd; ++i)
0486     {
0487       theCell.index[idim] = i;
0488       if (idim) // recurse
0489       {
0490         iterateInspect(idim - 1, theCell, theCellMin, theCellMax, theInspector);
0491       }
0492       else // inspect this cell
0493       {
0494         inspect(theCell, theInspector);
0495       }
0496     }
0497   }
0498 
0499 protected:
0500   int                                    myDim;
0501   occ::handle<NCollection_BaseAllocator> myAllocator;
0502   CellMap                                myCells;
0503   NCollection_Array1<double>             myCellSize;
0504 };
0505 
0506 #endif