0026171: Coding rules - eliminate -Wshorten-64-to-32 CLang warnings
[occt.git] / src / NCollection / NCollection_CellFilter.hxx
1 // Created on: 2007-05-26
2 // Created by: Andrey BETENEV
3 // Copyright (c) 2007-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #ifndef NCollection_CellFilter_HeaderFile
17 #define NCollection_CellFilter_HeaderFile
18
19 #include <Standard_Real.hxx>
20 #include <NCollection_LocalArray.hxx>
21 #include <NCollection_Array1.hxx>
22 #include <NCollection_List.hxx>
23 #include <NCollection_Map.hxx>
24 #include <NCollection_DataMap.hxx>
25 #include <NCollection_IncAllocator.hxx>
26 #include <NCollection_TypeDef.hxx>
27
28 //! Auxiliary enumeration serving as responce from method Inspect
29 enum NCollection_CellFilter_Action 
30 {
31   CellFilter_Keep  = 0, //!< Target is needed and should be kept
32   CellFilter_Purge = 1  //!< Target is not needed and can be removed from the current cell
33 };
34
35 /**
36  * A data structure for sorting geometric objects (called targets) in 
37  * n-dimensional space into cells, with associated algorithm for fast checking 
38  * of coincidence (overlapping, intersection, etc.) with other objects 
39  * (called here bullets).
40  *
41  * Description
42  * 
43  * The algorithm is based on hash map, thus it has linear time of initialization 
44  * (O(N) where N is number of cells covered by added targets) and constant-time 
45  * search for one bullet (more precisely, O(M) where M is number of cells covered 
46  * by the bullet).
47  *
48  * The idea behind the algorithm is to separate each coordinate of the space
49  * into equal-size cells. Note that this works well when cell size is 
50  * approximately equal to the characteristic size of the involved objects 
51  * (targets and bullets; including tolerance eventually used for coincidence 
52  * check). 
53  *
54  * Usage
55  *
56  * The target objects to be searched are added to the tool by methods Add(); 
57  * each target is classified as belonging to some cell(s). The data on cells 
58  * (list of targets found in each one) are stored in the hash map with key being 
59  * cumulative index of the cell by all coordinates.
60  * Thus the time needed to find targets in some cell is O(1) * O(number of 
61  * targets in the cell).
62  *
63  * As soon as all the targets are added, the algorithm is ready to check for 
64  * coincidence.
65  * To find the targets coincident with any given bullet, it checks all the 
66  * candidate targets in the cell(s) covered by the bullet object 
67  * (methods Inspect()).
68  *
69  * The methods Add() and Inspect() have two flavours each: one accepts
70  * single point identifying one cell, another accept two points specifying
71  * the range of cells. It should be noted that normally at least one of these
72  * methods is called as for range of cells: either due to objects having non-
73  * zero size, or in order to account for the tolerance when objects are points.
74  *
75  * The set of targets can be modified during the process: new targets can be
76  * added by Add(), existing targets can be removed by Remove().
77  *
78  * Implementation
79  *
80  * The algorithm is implemented as template class, thus it is capable to 
81  * work with objects of any type. The only argument of the template should be 
82  * the specific class providing all necessary features required by the 
83  * algorithm:
84  *
85  * - typedef "Target" defining type of target objects.
86  *   This type must have copy constructor
87  *
88  * - typedef "Point" defining type of geometrical points used 
89  *
90  * - enum Dimension whose value must be dimension of the point
91  *
92  * - method Coord() returning value of the i-th coordinate of the point:
93  *
94  *   static Standard_Real Coord (int i, const Point& thePnt);
95  *
96  *   Note that index i is from 0 to Dimension-1.
97  *
98  * - method IsEqual() used by Remove() to identify objects to be removed:
99  *
100  *   Standard_Boolean IsEqual (const Target& theT1, const Target& theT2);
101  *
102  * - method Inspect() performing necessary actions on the candidate target 
103  *   object (usially comparison with the currently checked bullet object):
104  *
105  *   NCollection_CellFilter_Action Inspect (const Target& theObject);
106  *
107  *   The returned value can be used to command CellFilter
108  *   to remove the inspected item from the current cell; this allows
109  *   to exclude the items that has been processed and are not needed any 
110  *   more in further search (for better performance).
111  *
112  *   Note that method Inspect() can be const and/or virtual.
113  */
114
115 template <class Inspector> class NCollection_CellFilter
116 {
117 public:
118   typedef TYPENAME Inspector::Target Target;
119   typedef TYPENAME Inspector::Point  Point;
120
121 public:
122
123   //! Constructor; initialized by dimension count and cell size.
124   //!
125   //! Note: the cell size must be ensured to be greater than
126   //! maximal coordinate of the involved points divided by INT_MAX,
127   //! in order to avoid integer overflow of cell index.
128   //! 
129   //! By default cell size is 0, which is invalid; thus if default
130   //! constructor is used, the tool must be initialized later with
131   //! appropriate cell size by call to Reset()
132   //! Constructor when dimension count is unknown at compilation time.
133   NCollection_CellFilter (const Standard_Integer theDim,
134                           const Standard_Real theCellSize = 0,
135                           const Handle(NCollection_IncAllocator)& theAlloc = 0)
136   : myCellSize(0, theDim - 1)
137   {
138     myDim = theDim;
139     Reset (theCellSize, theAlloc);
140   }
141
142   //! Constructor when dimenstion count is known at compilation time.
143   NCollection_CellFilter (const Standard_Real theCellSize = 0,
144                           const Handle(NCollection_IncAllocator)& theAlloc = 0)
145   : myCellSize(0, Inspector::Dimension - 1)
146   {
147     myDim = Inspector::Dimension;
148     Reset (theCellSize, theAlloc);
149   }
150
151   //! Clear the data structures, set new cell size and allocator
152   void Reset (Standard_Real theCellSize, 
153               const Handle(NCollection_IncAllocator)& theAlloc=0)
154   {
155     for (int i=0; i < myDim; i++)
156       myCellSize(i) = theCellSize;
157     resetAllocator ( theAlloc );
158   }
159
160   //! Clear the data structures and set new cell sizes and allocator
161   void Reset (NCollection_Array1<Standard_Real>& theCellSize, 
162               const Handle(NCollection_IncAllocator)& theAlloc=0)
163   {
164     myCellSize = theCellSize;
165     resetAllocator ( theAlloc );
166   }
167   
168   //! Adds a target object for further search at a point (into only one cell)
169   void Add (const Target& theTarget, const Point &thePnt)
170   {
171     Cell aCell (thePnt, myCellSize);
172     add (aCell, theTarget);
173   }
174
175   //! Adds a target object for further search in the range of cells 
176   //! defined by two points (the first point must have all coordinates equal or
177   //! less than the same coordinate of the second point)
178   void Add (const Target& theTarget, 
179             const Point &thePntMin, const Point &thePntMax)
180   {
181     // get cells range by minimal and maximal coordinates
182     Cell aCellMin (thePntMin, myCellSize);
183     Cell aCellMax (thePntMax, myCellSize);
184     Cell aCell = aCellMin;
185     // add object recursively into all cells in range
186     iterateAdd (myDim-1, aCell, aCellMin, aCellMax, theTarget);
187   }
188
189   //! Find a target object at a point and remove it from the structures.
190   //! For usage of this method "operator ==" should be defined for Target.
191   void Remove (const Target& theTarget, const Point &thePnt)
192   {
193     Cell aCell (thePnt, myCellSize);
194     remove (aCell, theTarget);
195   }
196
197   //! Find a target object in the range of cells defined by two points and
198   //! remove it from the structures
199   //! (the first point must have all coordinates equal or
200   //! less than the same coordinate of the second point).
201   //! For usage of this method "operator ==" should be defined for Target.
202   void Remove (const Target& theTarget, 
203                const Point &thePntMin, const Point &thePntMax)
204   {
205     // get cells range by minimal and maximal coordinates
206     Cell aCellMin (thePntMin, myCellSize);
207     Cell aCellMax (thePntMax, myCellSize);
208     Cell aCell = aCellMin;
209     // remove object recursively from all cells in range
210     iterateRemove (myDim-1, aCell, aCellMin, aCellMax, theTarget);
211   }
212
213   //! Inspect all targets in the cell corresponding to the given point
214   void Inspect (const Point& thePnt, Inspector &theInspector) 
215   {
216     Cell aCell (thePnt, myCellSize);
217     inspect (aCell, theInspector);
218   }
219
220   //! Inspect all targets in the cells range limited by two given points
221   //! (the first point must have all coordinates equal or
222   //! less than the same coordinate of the second point)
223   void Inspect (const Point& thePntMin, const Point& thePntMax, 
224                 Inspector &theInspector) 
225   {
226     // get cells range by minimal and maximal coordinates
227     Cell aCellMin (thePntMin, myCellSize);
228     Cell aCellMax (thePntMax, myCellSize);
229     Cell aCell = aCellMin;
230     // inspect object recursively into all cells in range
231     iterateInspect (myDim-1, aCell, 
232                     aCellMin, aCellMax, theInspector);
233   }
234
235 #if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x530)
236 public: // work-around against obsolete SUN WorkShop 5.3 compiler
237 #else
238 protected:
239 #endif
240  
241   /**
242    * Auxiliary class for storing points belonging to the cell as the list
243    */
244   struct ListNode
245   {
246     ListNode()
247     {
248       // Empty constructor is forbidden.
249       throw Standard_NoSuchObject("NCollection_CellFilter::ListNode()");
250     }
251
252     Target Object;
253     ListNode *Next;
254   };
255
256   //! Cell index type.
257   typedef Standard_Integer Cell_IndexType;
258
259   /**
260    * Auxiliary structure representing a cell in the space.
261    * Cells are stored in the map, each cell contains list of objects 
262    * that belong to that cell.
263    */
264   struct Cell
265   {
266   public:
267
268     //! Constructor; computes cell indices
269     Cell (const Point& thePnt, 
270           const NCollection_Array1<Standard_Real>& theCellSize)
271       : index(theCellSize.Size()),
272         Objects(0)
273     {
274       for (int i = 0; i < theCellSize.Size(); i++)
275       {
276           Standard_Real aVal = (Standard_Real)(Inspector::Coord(i, thePnt) / theCellSize(theCellSize.Lower() + i));
277           //If the value of index is greater than
278           //INT_MAX it is decreased correspondingly for the value of INT_MAX. If the value
279           //of index is less than INT_MIN it is increased correspondingly for the absolute
280           //value of INT_MIN.
281           index[i] = Cell_IndexType((aVal > INT_MAX - 1) ? fmod(aVal, (Standard_Real) INT_MAX)
282                                                          : (aVal < INT_MIN + 1) ? fmod(aVal, (Standard_Real) INT_MIN)
283                                                                                 : aVal);
284       }
285     }
286
287     //! Copy constructor: ensure that list is not deleted twice
288     Cell (const Cell& theOther)
289       : index(theOther.index.Size())
290     {
291       (*this) = theOther;
292     }
293
294     //! Assignment operator: ensure that list is not deleted twice
295     void operator = (const Cell& theOther)
296     {
297       Standard_Integer aDim = Standard_Integer(theOther.index.Size());
298       for(Standard_Integer anIdx = 0; anIdx < aDim; anIdx++)
299         index[anIdx] = theOther.index[anIdx];
300
301       Objects = theOther.Objects;
302       ((Cell&)theOther).Objects = 0;
303     }
304
305     //! Destructor; calls destructors for targets contained in the list
306     ~Cell ()
307     {
308       for ( ListNode* aNode = Objects; aNode; aNode = aNode->Next )
309         aNode->Object.~Target();
310       // note that list nodes need not to be freed, since IncAllocator is used
311       Objects = 0;
312     }
313
314     //! Compare cell with other one
315     Standard_Boolean IsEqual (const Cell& theOther) const
316     {
317       Standard_Integer aDim = Standard_Integer(theOther.index.Size());
318       for (int i=0; i < aDim; i++) 
319         if ( index[i] != theOther.index[i] ) return Standard_False;
320       return Standard_True;
321     }
322
323     //! Returns hash code for this cell, in the range [1, theUpperBound]
324     //! @param theUpperBound the upper bound of the range a computing hash code must be within
325     //! @return a computed hash code, in the range [1, theUpperBound]
326     Standard_Integer HashCode (const Standard_Integer theUpperBound) const
327     {
328       // number of bits per each dimension in the hash code
329       const std::size_t aDim       = index.Size();
330       const std::size_t aShiftBits = (BITS (Cell_IndexType) - 1) / aDim;
331       std::size_t       aCode      = 0;
332
333       for (std::size_t i = 0; i < aDim; ++i)
334       {
335         aCode = (aCode << aShiftBits) ^ std::size_t(index[i]);
336       }
337
338       return ::HashCode(aCode, theUpperBound);
339     }
340
341   public:
342     NCollection_LocalArray<Cell_IndexType, 10> index;
343     ListNode *Objects;
344   };
345   
346   //! Returns hash code for the given cell, in the range [1, theUpperBound]
347   //! @param theCell the cell object which hash code is to be computed
348   //! @param theUpperBound the upper bound of the range a computing hash code must be within
349   //! @return a computed hash code, in the range [1, theUpperBound]
350   friend Standard_Integer HashCode (const Cell& theCell, const Standard_Integer theUpperBound)
351   {
352     return theCell.HashCode (theUpperBound);
353   }
354
355   friend Standard_Boolean IsEqual (const Cell &aCell1, const Cell &aCell2)
356   { return aCell1.IsEqual(aCell2); }
357
358 protected:
359
360   //! Reset allocator to the new one
361   void resetAllocator (const Handle(NCollection_IncAllocator)& theAlloc)
362   {
363     if ( theAlloc.IsNull() )
364       myAllocator = new NCollection_IncAllocator;
365     else 
366       myAllocator = theAlloc;
367     myCells.Clear ( myAllocator );
368   }
369   
370   //! Add a new target object into the specified cell
371   void add (const Cell& theCell, const Target& theTarget)
372   {
373     // add a new cell or get reference to existing one
374     Cell& aMapCell = (Cell&)myCells.Added (theCell);
375
376     // create a new list node and add it to the beginning of the list
377     ListNode* aNode = (ListNode*)myAllocator->Allocate(sizeof(ListNode));
378     new (&aNode->Object) Target (theTarget);
379     aNode->Next = aMapCell.Objects;
380     aMapCell.Objects = aNode;
381   }
382
383   //! Internal addition function, performing iteration for adjacent cells
384   //! by one dimension; called recursively to cover all dimensions
385   void iterateAdd (int idim, Cell &theCell, 
386                    const Cell& theCellMin, const Cell& theCellMax, 
387                    const Target& theTarget)
388   {
389     const Cell_IndexType aStart = theCellMin.index[idim];
390     const Cell_IndexType anEnd  = theCellMax.index[idim];
391     for (Cell_IndexType i = aStart; i <= anEnd; ++i)
392     {
393       theCell.index[idim] = i;
394       if ( idim ) // recurse
395       {
396         iterateAdd (idim-1, theCell, theCellMin, theCellMax, theTarget);
397       }
398       else // add to this cell
399       {
400         add (theCell, theTarget);
401       }
402     }
403   }
404   
405   //! Remove the target object from the specified cell
406   void remove (const Cell& theCell, const Target& theTarget)
407   {
408     // check if any objects are recorded in that cell
409     if ( ! myCells.Contains (theCell) ) 
410       return;
411
412     // iterate by objects in the cell and check each
413     Cell& aMapCell = (Cell&)myCells.Added (theCell);
414     ListNode* aNode = aMapCell.Objects;
415     ListNode* aPrev = NULL;
416     while (aNode)
417     {
418       ListNode* aNext = aNode->Next;
419       if (Inspector::IsEqual (aNode->Object, theTarget))
420       {
421         aNode->Object.~Target();
422         (aPrev ? aPrev->Next : aMapCell.Objects) = aNext;
423         // note that aNode itself need not to be freed, since IncAllocator is used
424       }
425       else
426         aPrev = aNode;
427       aNode = aNext;
428     }
429   }
430
431   //! Internal removal function, performing iteration for adjacent cells
432   //! by one dimension; called recursively to cover all dimensions
433   void iterateRemove (int idim, Cell &theCell, 
434                       const Cell& theCellMin, const Cell& theCellMax, 
435                       const Target& theTarget)
436   {
437     const Cell_IndexType aStart = theCellMin.index[idim];
438     const Cell_IndexType anEnd  = theCellMax.index[idim];
439     for (Cell_IndexType i = aStart; i <= anEnd; ++i)
440     {
441       theCell.index[idim] = i;
442       if ( idim ) // recurse
443       {
444         iterateRemove (idim-1, theCell, theCellMin, theCellMax, theTarget);
445       }
446       else // remove from this cell
447       {
448         remove (theCell, theTarget);
449       }
450     }
451   }
452
453   //! Inspect the target objects in the specified cell.
454   void inspect (const Cell& theCell, Inspector& theInspector) 
455   {
456     // check if any objects are recorded in that cell
457     if ( ! myCells.Contains (theCell) ) 
458       return;
459
460     // iterate by objects in the cell and check each
461     Cell& aMapCell = (Cell&)myCells.Added (theCell);
462     ListNode* aNode = aMapCell.Objects;
463     ListNode* aPrev = NULL;
464     while(aNode) {
465       ListNode* aNext = aNode->Next;
466       NCollection_CellFilter_Action anAction = 
467         theInspector.Inspect (aNode->Object);
468       // delete items requested to be purged
469       if ( anAction == CellFilter_Purge ) {
470         aNode->Object.~Target();
471         (aPrev ? aPrev->Next : aMapCell.Objects) = aNext;
472         // note that aNode itself need not to be freed, since IncAllocator is used
473       }
474       else
475         aPrev = aNode;
476       aNode = aNext;      
477     }
478   }
479
480   //! Inspect the target objects in the specified range of the cells
481   void iterateInspect (int idim, Cell &theCell, 
482                        const Cell& theCellMin, const Cell& theCellMax, 
483                        Inspector& theInspector) 
484   {
485     const Cell_IndexType aStart = theCellMin.index[idim];
486     const Cell_IndexType anEnd  = theCellMax.index[idim];
487     for (Cell_IndexType i = aStart; i <= anEnd; ++i)
488     {
489       theCell.index[idim] = i;
490       if ( idim ) // recurse
491       {
492         iterateInspect (idim-1, theCell, theCellMin, theCellMax, theInspector);
493       }
494       else // inspect this cell
495       {
496         inspect (theCell, theInspector);
497       }
498     }
499   }
500
501 protected:
502   Standard_Integer myDim;
503   Handle(NCollection_BaseAllocator) myAllocator;
504   NCollection_Map<Cell>             myCells;
505   NCollection_Array1<Standard_Real> myCellSize;
506 };
507
508 /**
509  * Base class defining part of the Inspector interface 
510  * for CellFilter algorithm, working with gp_XYZ points in 3d space
511  */
512
513 class gp_XYZ;
514 struct NCollection_CellFilter_InspectorXYZ
515 {
516   //! Points dimension
517   enum { Dimension = 3 };
518
519   //! Points type
520   typedef gp_XYZ Point;
521
522   //! Access to coordinate
523   static Standard_Real Coord (int i, const Point &thePnt) { return thePnt.Coord(i+1); }
524   
525   //! Auxiliary method to shift point by each coordinate on given value;
526   //! useful for preparing a points range for Inspect with tolerance
527   Point Shift (const Point& thePnt, Standard_Real theTol) const 
528   { return Point (thePnt.X() + theTol, thePnt.Y() + theTol, thePnt.Z() + theTol); }
529 };
530
531 /**
532  * Base class defining part of the Inspector interface 
533  * for CellFilter algorithm, working with gp_XY points in 2d space
534  */
535
536 class gp_XY;
537 struct NCollection_CellFilter_InspectorXY
538 {
539   //! Points dimension
540   enum { Dimension = 2 };
541
542   //! Points type
543   typedef gp_XY Point;
544
545   //! Access to coordinate
546   static Standard_Real Coord (int i, const Point &thePnt) { return thePnt.Coord(i+1); }
547
548   //! Auxiliary method to shift point by each coordinate on given value;
549   //! useful for preparing a points range for Inspect with tolerance
550   Point Shift (const Point& thePnt, Standard_Real theTol) const 
551   { return Point (thePnt.X() + theTol, thePnt.Y() + theTol); }
552 };
553
554 #endif