Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
K
kicad-source-mirror
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
kicad-source-mirror
Commits
3701e131
Commit
3701e131
authored
Dec 10, 2013
by
Maciej Suminski
Committed by
Wayne Stambaugh
Dec 10, 2013
Browse files
Options
Browse Files
Download
Plain Diff
Commit merge request lp:197858 (fixes lp:1249736).
parents
dbd72122
d7fc8db0
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
44 additions
and
1791 deletions
+44
-1791
view.cpp
common/view/view.cpp
+22
-8
rtree.h
include/geometry/rtree.h
+21
-23
rtree.h
include/rtree.h
+0
-1758
view_rtree.h
include/view/view_rtree.h
+1
-2
No files found.
common/view/view.cpp
View file @
3701e131
...
...
@@ -153,10 +153,12 @@ struct queryVisitor
{
}
void
operator
()(
VIEW_ITEM
*
aItem
)
bool
operator
()(
VIEW_ITEM
*
aItem
)
{
if
(
aItem
->
ViewIsVisible
()
)
m_cont
.
push_back
(
VIEW
::
LAYER_ITEM_PAIR
(
aItem
,
m_layer
)
);
return
true
;
}
Container
&
m_cont
;
...
...
@@ -387,7 +389,7 @@ struct VIEW::updateItemsColor
{
}
void
operator
()(
VIEW_ITEM
*
aItem
)
bool
operator
()(
VIEW_ITEM
*
aItem
)
{
// Obtain the color that should be used for coloring the item
const
COLOR4D
color
=
painter
->
GetSettings
()
->
GetColor
(
aItem
,
layer
);
...
...
@@ -395,6 +397,8 @@ struct VIEW::updateItemsColor
if
(
group
>=
0
)
gal
->
ChangeGroupColor
(
group
,
color
);
return
true
;
}
int
layer
;
...
...
@@ -447,12 +451,14 @@ struct VIEW::changeItemsDepth
{
}
void
operator
()(
VIEW_ITEM
*
aItem
)
bool
operator
()(
VIEW_ITEM
*
aItem
)
{
int
group
=
aItem
->
getGroup
(
layer
);
if
(
group
>=
0
)
gal
->
ChangeGroupDepth
(
group
,
depth
);
return
true
;
}
int
layer
,
depth
;
...
...
@@ -571,15 +577,17 @@ struct VIEW::drawItem
{
}
void
operator
()(
VIEW_ITEM
*
aItem
)
bool
operator
()(
VIEW_ITEM
*
aItem
)
{
// Conditions that have te be fulfilled for an item to be drawn
bool
drawCondition
=
aItem
->
ViewIsVisible
()
&&
aItem
->
ViewGetLOD
(
currentLayer
->
id
)
<
view
->
m_scale
;
if
(
!
drawCondition
)
return
;
return
true
;
view
->
draw
(
aItem
,
currentLayer
->
id
);
return
true
;
}
const
VIEW_LAYER
*
currentLayer
;
...
...
@@ -676,9 +684,11 @@ bool VIEW::IsDirty() const
struct
VIEW
::
unlinkItem
{
void
operator
()(
VIEW_ITEM
*
aItem
)
bool
operator
()(
VIEW_ITEM
*
aItem
)
{
aItem
->
m_view
=
NULL
;
return
true
;
}
};
...
...
@@ -690,7 +700,7 @@ struct VIEW::recacheItem
{
}
void
operator
()(
VIEW_ITEM
*
aItem
)
bool
operator
()(
VIEW_ITEM
*
aItem
)
{
// Remove previously cached group
int
prevGroup
=
aItem
->
getGroup
(
layer
);
...
...
@@ -712,6 +722,8 @@ struct VIEW::recacheItem
{
aItem
->
setGroup
(
layer
,
-
1
);
}
return
true
;
}
VIEW
*
view
;
...
...
@@ -792,12 +804,14 @@ struct VIEW::clearLayerCache
{
}
void
operator
()(
VIEW_ITEM
*
aItem
)
bool
operator
()(
VIEW_ITEM
*
aItem
)
{
if
(
aItem
->
storesGroups
()
)
{
aItem
->
deleteGroups
();
}
return
true
;
}
VIEW
*
view
;
...
...
include/geometry/rtree.h
View file @
3701e131
...
...
@@ -163,7 +163,7 @@ public:
/// Calculate Statistics
Statistics
CalcStats
(
);
Statistics
CalcStats
();
/// Remove all entries from tree
void
RemoveAll
();
...
...
@@ -1286,7 +1286,7 @@ int RTREE_QUAL::PickBranch( Rect* a_rect, Node* a_node )
ELEMTYPEREAL
increase
;
ELEMTYPEREAL
bestIncr
=
(
ELEMTYPEREAL
)
-
1
;
ELEMTYPEREAL
area
;
ELEMTYPEREAL
bestArea
;
ELEMTYPEREAL
bestArea
=
0
;
int
best
=
0
;
Rect
tempRect
;
...
...
@@ -1622,7 +1622,7 @@ void RTREE_QUAL::PickSeeds( PartitionVars* a_parVars )
&
a_parVars
->
m_branchBuf
[
indexB
].
m_rect
);
waste
=
CalcRectVolume
(
&
oneRect
)
-
area
[
indexA
]
-
area
[
indexB
];
if
(
waste
>
worst
)
if
(
waste
>
=
worst
)
{
worst
=
waste
;
seed0
=
indexA
;
...
...
@@ -1856,8 +1856,6 @@ bool RTREE_QUAL::Search( Node* a_node, Rect* a_rect, int& a_foundCount, bool a_r
}
//calculate the minimum distance between a point and a rectangle as defined by Manolopoulos et al.
//it uses the square distance to avoid the use of ELEMTYPEREAL values, which are slower.
RTREE_TEMPLATE
...
...
include/rtree.h
deleted
100644 → 0
View file @
dbd72122
/*
* R-Tree index
*
* from http://www.superliminal.com/
*
* LICENSE : Entirely free for all uses. Enjoy!
*
* AUTORS
* - 1983 Original algorithm and test code by Antonin Guttman and Michael Stonebraker, UC Berkely
* - 1994 ANCI C ported from original test code by Melinda Green - melinda@superliminal.com
* - 1995 Sphere volume fix for degeneracy problem submitted by Paul Brook
* - 2004 Templated C++ port by Greg Douglas
* - 2008 Portability issues fixed by Maxence Laurent
*/
#ifndef RTREE_H
#define RTREE_H
// NOTE This file compiles under MSVC 6 SP5 and MSVC .Net 2003 it may not work on other compilers without modification.
// NOTE These next few lines may be win32 specific, you may need to modify them to compile on other platform
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <stdlib.h>
#define ASSERT assert // RTree uses ASSERT( condition )
#ifndef Min
#define Min std::min
#endif // Min
#ifndef Max
#define Max std::max
#endif // Max
//
// RTree.h
//
#define RTREE_TEMPLATE template <class DATATYPE, class ELEMTYPE, int NUMDIMS, \
class ELEMTYPEREAL, int TMAXNODES, int TMINNODES>
#define RTREE_SEARCH_TEMPLATE template <class DATATYPE, class ELEMTYPE, int NUMDIMS, \
class ELEMTYPEREAL, int TMAXNODES, int TMINNODES, class VISITOR>
#define RTREE_QUAL RTree<DATATYPE, ELEMTYPE, NUMDIMS, ELEMTYPEREAL, TMAXNODES, \
TMINNODES>
#define RTREE_SEARCH_QUAL RTree<DATATYPE, ELEMTYPE, NUMDIMS, ELEMTYPEREAL, TMAXNODES, \
TMINNODES, VISITOR>
#define RTREE_DONT_USE_MEMPOOLS // This version does not contain a fixed memory allocator, fill in lines with EXAMPLE to implement one.
#define RTREE_USE_SPHERICAL_VOLUME // Better split classification, may be slower on some systems
// Fwd decl
class
RTFileStream
;
// File I/O helper class, look below for implementation and notes.
/// \class RTree
/// Implementation of RTree, a multidimensional bounding rectangle tree.
/// Example usage: For a 3-dimensional tree use RTree<Object*, float, 3> myTree;
///
/// This modified, templated C++ version by Greg Douglas at Auran (http://www.auran.com)
///
/// DATATYPE Referenced data, should be int, void*, obj* etc. no larger than sizeof<void*> and simple type
/// ELEMTYPE Type of element such as int or float
/// NUMDIMS Number of dimensions such as 2 or 3
/// ELEMTYPEREAL Type of element that allows fractional and large values such as float or double, for use in volume calcs
///
/// NOTES: Inserting and removing data requires the knowledge of its constant Minimal Bounding Rectangle.
/// This version uses new/delete for nodes, I recommend using a fixed size allocator for efficiency.
/// Instead of using a callback function for returned results, I recommend and efficient pre-sized, grow-only memory
/// array similar to MFC CArray or STL Vector for returning search query result.
///
template
<
class
DATATYPE
,
class
ELEMTYPE
,
int
NUMDIMS
,
class
ELEMTYPEREAL
=
ELEMTYPE
,
int
TMAXNODES
=
8
,
int
TMINNODES
=
TMAXNODES
/
2
>
class
RTree
{
protected
:
struct
Node
;
// Fwd decl. Used by other internal structs and iterator
public
:
// These constant must be declared after Branch and before Node struct
// Stuck up here for MSVC 6 compiler. NSVC .NET 2003 is much happier.
enum
{
MAXNODES
=
TMAXNODES
,
///< Max elements in node
MINNODES
=
TMINNODES
,
///< Min elements in node
};
public
:
RTree
();
virtual
~
RTree
();
/// Insert entry
/// \param a_min Min of bounding rect
/// \param a_max Max of bounding rect
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
void
Insert
(
const
ELEMTYPE
a_min
[
NUMDIMS
],
const
ELEMTYPE
a_max
[
NUMDIMS
],
const
DATATYPE
&
a_dataId
);
/// Remove entry
/// \param a_min Min of bounding rect
/// \param a_max Max of bounding rect
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
void
Remove
(
const
ELEMTYPE
a_min
[
NUMDIMS
],
const
ELEMTYPE
a_max
[
NUMDIMS
],
const
DATATYPE
&
a_dataId
);
/// Find all within search rectangle
/// \param a_min Min of search bounding rect
/// \param a_max Max of search bounding rect
/// \param a_searchResult Search result array. Caller should set grow size. Function will reset, not append to array.
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
/// \param a_context User context to pass as parameter to a_resultCallback
/// \return Returns the number of entries found
int
Search
(
const
ELEMTYPE
a_min
[
NUMDIMS
],
const
ELEMTYPE
a_max
[
NUMDIMS
],
bool
a_resultCallback
(
DATATYPE
a_data
,
void
*
a_context
),
void
*
a_context
);
template
<
class
VISITOR
>
int
Search
(
const
ELEMTYPE
a_min
[
NUMDIMS
],
const
ELEMTYPE
a_max
[
NUMDIMS
],
VISITOR
&
a_visitor
)
{
#ifdef _DEBUG
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
ASSERT
(
a_min
[
index
]
<=
a_max
[
index
]
);
}
#endif // _DEBUG
Rect
rect
;
for
(
int
axis
=
0
;
axis
<
NUMDIMS
;
++
axis
)
{
rect
.
m_min
[
axis
]
=
a_min
[
axis
];
rect
.
m_max
[
axis
]
=
a_max
[
axis
];
}
// NOTE: May want to return search result another way, perhaps returning the number of found elements here.
Search
(
m_root
,
&
rect
,
a_visitor
);
return
0
;
}
/// Remove all entries from tree
void
RemoveAll
();
/// Count the data elements in this container. This is slow as no internal counter is maintained.
int
Count
();
/// Load tree contents from file
bool
Load
(
const
char
*
a_fileName
);
/// Load tree contents from stream
bool
Load
(
RTFileStream
&
a_stream
);
/// Save tree contents to file
bool
Save
(
const
char
*
a_fileName
);
/// Save tree contents to stream
bool
Save
(
RTFileStream
&
a_stream
);
/// Iterator is not remove safe.
class
Iterator
{
private
:
enum
{
MAX_STACK
=
32
};
// Max stack size. Allows almost n^32 where n is number of branches in node
struct
StackElement
{
Node
*
m_node
;
int
m_branchIndex
;
};
public
:
Iterator
()
{
Init
();
}
~
Iterator
()
{
}
/// Is iterator invalid
bool
IsNull
()
{
return
m_tos
<=
0
;
}
/// Is iterator pointing to valid data
bool
IsNotNull
()
{
return
m_tos
>
0
;
}
/// Access the current data element. Caller must be sure iterator is not NULL first.
DATATYPE
&
operator
*
()
{
ASSERT
(
IsNotNull
()
);
StackElement
&
curTos
=
m_stack
[
m_tos
-
1
];
return
curTos
.
m_node
->
m_branch
[
curTos
.
m_branchIndex
].
m_data
;
}
/// Access the current data element. Caller must be sure iterator is not NULL first.
const
DATATYPE
&
operator
*
()
const
{
ASSERT
(
IsNotNull
()
);
StackElement
&
curTos
=
m_stack
[
m_tos
-
1
];
return
curTos
.
m_node
->
m_branch
[
curTos
.
m_branchIndex
].
m_data
;
}
/// Find the next data element
bool
operator
++
()
{
return
FindNextData
();
}
/// Get the bounds for this node
void
GetBounds
(
ELEMTYPE
a_min
[
NUMDIMS
],
ELEMTYPE
a_max
[
NUMDIMS
]
)
{
ASSERT
(
IsNotNull
()
);
StackElement
&
curTos
=
m_stack
[
m_tos
-
1
];
Branch
&
curBranch
=
curTos
.
m_node
->
m_branch
[
curTos
.
m_branchIndex
];
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
a_min
[
index
]
=
curBranch
.
m_rect
.
m_min
[
index
];
a_max
[
index
]
=
curBranch
.
m_rect
.
m_max
[
index
];
}
}
private
:
/// Reset iterator
void
Init
()
{
m_tos
=
0
;
}
/// Find the next data element in the tree (For internal use only)
bool
FindNextData
()
{
for
(
;
;
)
{
if
(
m_tos
<=
0
)
{
return
false
;
}
StackElement
curTos
=
Pop
();
// Copy stack top cause it may change as we use it
if
(
curTos
.
m_node
->
IsLeaf
()
)
{
// Keep walking through data while we can
if
(
curTos
.
m_branchIndex
+
1
<
curTos
.
m_node
->
m_count
)
{
// There is more data, just point to the next one
Push
(
curTos
.
m_node
,
curTos
.
m_branchIndex
+
1
);
return
true
;
}
// No more data, so it will fall back to previous level
}
else
{
if
(
curTos
.
m_branchIndex
+
1
<
curTos
.
m_node
->
m_count
)
{
// Push sibling on for future tree walk
// This is the 'fall back' node when we finish with the current level
Push
(
curTos
.
m_node
,
curTos
.
m_branchIndex
+
1
);
}
// Since cur node is not a leaf, push first of next level to get deeper into the tree
Node
*
nextLevelnode
=
curTos
.
m_node
->
m_branch
[
curTos
.
m_branchIndex
].
m_child
;
Push
(
nextLevelnode
,
0
);
// If we pushed on a new leaf, exit as the data is ready at TOS
if
(
nextLevelnode
->
IsLeaf
()
)
{
return
true
;
}
}
}
}
/// Push node and branch onto iteration stack (For internal use only)
void
Push
(
Node
*
a_node
,
int
a_branchIndex
)
{
m_stack
[
m_tos
].
m_node
=
a_node
;
m_stack
[
m_tos
].
m_branchIndex
=
a_branchIndex
;
++
m_tos
;
ASSERT
(
m_tos
<=
MAX_STACK
);
}
/// Pop element off iteration stack (For internal use only)
StackElement
&
Pop
()
{
ASSERT
(
m_tos
>
0
);
--
m_tos
;
return
m_stack
[
m_tos
];
}
StackElement
m_stack
[
MAX_STACK
];
///< Stack as we are doing iteration instead of recursion
int
m_tos
;
///< Top Of Stack index
friend
class
RTree
;
// Allow hiding of non-public functions while allowing manipulation by logical owner
};
/// Get 'first' for iteration
void
GetFirst
(
Iterator
&
a_it
)
{
a_it
.
Init
();
Node
*
first
=
m_root
;
while
(
first
)
{
if
(
first
->
IsInternalNode
()
&&
first
->
m_count
>
1
)
{
a_it
.
Push
(
first
,
1
);
// Descend sibling branch later
}
else
if
(
first
->
IsLeaf
()
)
{
if
(
first
->
m_count
)
{
a_it
.
Push
(
first
,
0
);
}
break
;
}
first
=
first
->
m_branch
[
0
].
m_child
;
}
}
/// Get Next for iteration
void
GetNext
(
Iterator
&
a_it
)
{
++
a_it
;
}
/// Is iterator NULL, or at end?
bool
IsNull
(
Iterator
&
a_it
)
{
return
a_it
.
IsNull
();
}
/// Get object at iterator position
DATATYPE
&
GetAt
(
Iterator
&
a_it
)
{
return
*
a_it
;
}
protected
:
/// Minimal bounding rectangle (n-dimensional)
struct
Rect
{
ELEMTYPE
m_min
[
NUMDIMS
];
///< Min dimensions of bounding box
ELEMTYPE
m_max
[
NUMDIMS
];
///< Max dimensions of bounding box
};
/// May be data or may be another subtree
/// The parents level determines this.
/// If the parents level is 0, then this is data
struct
Branch
{
Rect
m_rect
;
///< Bounds
union
{
Node
*
m_child
;
///< Child node
DATATYPE
m_data
;
///< Data Id or Ptr
};
};
/// Node for each branch level
struct
Node
{
bool
IsInternalNode
()
{
return
m_level
>
0
;
}
// Not a leaf, but a internal node
bool
IsLeaf
()
{
return
m_level
==
0
;
}
// A leaf, contains data
int
m_count
;
///< Count
int
m_level
;
///< Leaf is zero, others positive
Branch
m_branch
[
MAXNODES
];
///< Branch
};
/// A link list of nodes for reinsertion after a delete operation
struct
ListNode
{
ListNode
*
m_next
;
///< Next in list
Node
*
m_node
;
///< Node
};
/// Variables for finding a split partition
struct
PartitionVars
{
int
m_partition
[
MAXNODES
+
1
];
int
m_total
;
int
m_minFill
;
int
m_taken
[
MAXNODES
+
1
];
int
m_count
[
2
];
Rect
m_cover
[
2
];
ELEMTYPEREAL
m_area
[
2
];
Branch
m_branchBuf
[
MAXNODES
+
1
];
int
m_branchCount
;
Rect
m_coverSplit
;
ELEMTYPEREAL
m_coverSplitArea
;
};
Node
*
AllocNode
();
void
FreeNode
(
Node
*
a_node
);
void
InitNode
(
Node
*
a_node
);
void
InitRect
(
Rect
*
a_rect
);
bool
InsertRectRec
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
*
a_node
,
Node
**
a_newNode
,
int
a_level
);
bool
InsertRect
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
**
a_root
,
int
a_level
);
Rect
NodeCover
(
Node
*
a_node
);
bool
AddBranch
(
Branch
*
a_branch
,
Node
*
a_node
,
Node
**
a_newNode
);
void
DisconnectBranch
(
Node
*
a_node
,
int
a_index
);
int
PickBranch
(
Rect
*
a_rect
,
Node
*
a_node
);
Rect
CombineRect
(
Rect
*
a_rectA
,
Rect
*
a_rectB
);
void
SplitNode
(
Node
*
a_node
,
Branch
*
a_branch
,
Node
**
a_newNode
);
ELEMTYPEREAL
RectSphericalVolume
(
Rect
*
a_rect
);
ELEMTYPEREAL
RectVolume
(
Rect
*
a_rect
);
ELEMTYPEREAL
CalcRectVolume
(
Rect
*
a_rect
);
void
GetBranches
(
Node
*
a_node
,
Branch
*
a_branch
,
PartitionVars
*
a_parVars
);
void
ChoosePartition
(
PartitionVars
*
a_parVars
,
int
a_minFill
);
void
LoadNodes
(
Node
*
a_nodeA
,
Node
*
a_nodeB
,
PartitionVars
*
a_parVars
);
void
InitParVars
(
PartitionVars
*
a_parVars
,
int
a_maxRects
,
int
a_minFill
);
void
PickSeeds
(
PartitionVars
*
a_parVars
);
void
Classify
(
int
a_index
,
int
a_group
,
PartitionVars
*
a_parVars
);
bool
RemoveRect
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
**
a_root
);
bool
RemoveRectRec
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
*
a_node
,
ListNode
**
a_listNode
);
ListNode
*
AllocListNode
();
void
FreeListNode
(
ListNode
*
a_listNode
);
bool
Overlap
(
Rect
*
a_rectA
,
Rect
*
a_rectB
);
void
ReInsert
(
Node
*
a_node
,
ListNode
**
a_listNode
);
bool
Search
(
Node
*
a_node
,
Rect
*
a_rect
,
int
&
a_foundCount
,
bool
a_resultCallback
(
DATATYPE
a_data
,
void
*
a_context
),
void
*
a_context
);
template
<
class
VISITOR
>
bool
Search
(
Node
*
a_node
,
Rect
*
a_rect
,
VISITOR
&
a_visitor
)
{
ASSERT
(
a_node
);
ASSERT
(
a_node
->
m_level
>=
0
);
ASSERT
(
a_rect
);
if
(
a_node
->
IsInternalNode
()
)
// This is an internal node in the tree
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
if
(
Overlap
(
a_rect
,
&
a_node
->
m_branch
[
index
].
m_rect
)
)
{
if
(
!
Search
(
a_node
->
m_branch
[
index
].
m_child
,
a_rect
,
a_visitor
)
)
{
return
false
;
// Don't continue searching
}
}
}
}
else
// This is a leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
if
(
Overlap
(
a_rect
,
&
a_node
->
m_branch
[
index
].
m_rect
)
)
{
DATATYPE
&
id
=
a_node
->
m_branch
[
index
].
m_data
;
a_visitor
(
id
);
}
}
}
return
true
;
// Continue searching
}
void
RemoveAllRec
(
Node
*
a_node
);
void
Reset
();
void
CountRec
(
Node
*
a_node
,
int
&
a_count
);
bool
SaveRec
(
Node
*
a_node
,
RTFileStream
&
a_stream
);
bool
LoadRec
(
Node
*
a_node
,
RTFileStream
&
a_stream
);
Node
*
m_root
;
///< Root of tree
ELEMTYPEREAL
m_unitSphereVolume
;
///< Unit sphere constant for required number of dimensions
};
// Because there is not stream support, this is a quick and dirty file I/O helper.
// Users will likely replace its usage with a Stream implementation from their favorite API.
class
RTFileStream
{
FILE
*
m_file
;
public
:
RTFileStream
()
{
m_file
=
NULL
;
}
~
RTFileStream
()
{
Close
();
}
bool
OpenRead
(
const
char
*
a_fileName
)
{
m_file
=
fopen
(
a_fileName
,
"rb"
);
if
(
!
m_file
)
{
return
false
;
}
return
true
;
}
bool
OpenWrite
(
const
char
*
a_fileName
)
{
m_file
=
fopen
(
a_fileName
,
"wb"
);
if
(
!
m_file
)
{
return
false
;
}
return
true
;
}
void
Close
()
{
if
(
m_file
)
{
fclose
(
m_file
);
m_file
=
NULL
;
}
}
template
<
typename
TYPE
>
size_t
Write
(
const
TYPE
&
a_value
)
{
ASSERT
(
m_file
);
return
fwrite
(
(
void
*
)
&
a_value
,
sizeof
(
a_value
),
1
,
m_file
);
}
template
<
typename
TYPE
>
size_t
WriteArray
(
const
TYPE
*
a_array
,
int
a_count
)
{
ASSERT
(
m_file
);
return
fwrite
(
(
void
*
)
a_array
,
sizeof
(
TYPE
)
*
a_count
,
1
,
m_file
);
}
template
<
typename
TYPE
>
size_t
Read
(
TYPE
&
a_value
)
{
ASSERT
(
m_file
);
return
fread
(
(
void
*
)
&
a_value
,
sizeof
(
a_value
),
1
,
m_file
);
}
template
<
typename
TYPE
>
size_t
ReadArray
(
TYPE
*
a_array
,
int
a_count
)
{
ASSERT
(
m_file
);
return
fread
(
(
void
*
)
a_array
,
sizeof
(
TYPE
)
*
a_count
,
1
,
m_file
);
}
};
RTREE_TEMPLATE
RTREE_QUAL
::
RTree
()
{
ASSERT
(
MAXNODES
>
MINNODES
);
ASSERT
(
MINNODES
>
0
);
// We only support machine word size simple data type eg. integer index or object pointer.
// Since we are storing as union with non data branch
ASSERT
(
sizeof
(
DATATYPE
)
==
sizeof
(
void
*
)
||
sizeof
(
DATATYPE
)
==
sizeof
(
int
)
);
// Precomputed volumes of the unit spheres for the first few dimensions
const
float
UNIT_SPHERE_VOLUMES
[]
=
{
0
.
000000
f
,
2
.
000000
f
,
3
.
141593
f
,
// Dimension 0,1,2
4
.
188790
f
,
4
.
934802
f
,
5
.
263789
f
,
// Dimension 3,4,5
5
.
167713
f
,
4
.
724766
f
,
4
.
05
8712
f
,
// Dimension 6,7,8
3
.
298509
f
,
2
.
550164
f
,
1
.
884104
f
,
// Dimension 9,10,11
1
.
335263
f
,
0
.
910629
f
,
0
.
599265
f
,
// Dimension 12,13,14
0
.
381443
f
,
0
.
235331
f
,
0
.
140981
f
,
// Dimension 15,16,17
0
.
082146
f
,
0
.
046622
f
,
0
.
025
807
f
,
// Dimension 18,19,20
};
m_root
=
AllocNode
();
m_root
->
m_level
=
0
;
m_unitSphereVolume
=
(
ELEMTYPEREAL
)
UNIT_SPHERE_VOLUMES
[
NUMDIMS
];
}
RTREE_TEMPLATE
RTREE_QUAL
::~
RTree
()
{
Reset
();
// Free, or reset node memory
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
Insert
(
const
ELEMTYPE
a_min
[
NUMDIMS
],
const
ELEMTYPE
a_max
[
NUMDIMS
],
const
DATATYPE
&
a_dataId
)
{
#ifdef _DEBUG
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
ASSERT
(
a_min
[
index
]
<=
a_max
[
index
]
);
}
#endif // _DEBUG
Rect
rect
;
for
(
int
axis
=
0
;
axis
<
NUMDIMS
;
++
axis
)
{
rect
.
m_min
[
axis
]
=
a_min
[
axis
];
rect
.
m_max
[
axis
]
=
a_max
[
axis
];
}
InsertRect
(
&
rect
,
a_dataId
,
&
m_root
,
0
);
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
Remove
(
const
ELEMTYPE
a_min
[
NUMDIMS
],
const
ELEMTYPE
a_max
[
NUMDIMS
],
const
DATATYPE
&
a_dataId
)
{
#ifdef _DEBUG
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
ASSERT
(
a_min
[
index
]
<=
a_max
[
index
]
);
}
#endif // _DEBUG
Rect
rect
;
for
(
int
axis
=
0
;
axis
<
NUMDIMS
;
++
axis
)
{
rect
.
m_min
[
axis
]
=
a_min
[
axis
];
rect
.
m_max
[
axis
]
=
a_max
[
axis
];
}
RemoveRect
(
&
rect
,
a_dataId
,
&
m_root
);
}
RTREE_TEMPLATE
int
RTREE_QUAL
::
Search
(
const
ELEMTYPE
a_min
[
NUMDIMS
],
const
ELEMTYPE
a_max
[
NUMDIMS
],
bool
a_resultCallback
(
DATATYPE
a_data
,
void
*
a_context
),
void
*
a_context
)
{
#ifdef _DEBUG
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
ASSERT
(
a_min
[
index
]
<=
a_max
[
index
]
);
}
#endif // _DEBUG
Rect
rect
;
for
(
int
axis
=
0
;
axis
<
NUMDIMS
;
++
axis
)
{
rect
.
m_min
[
axis
]
=
a_min
[
axis
];
rect
.
m_max
[
axis
]
=
a_max
[
axis
];
}
// NOTE: May want to return search result another way, perhaps returning the number of found elements here.
int
foundCount
=
0
;
Search
(
m_root
,
&
rect
,
foundCount
,
a_resultCallback
,
a_context
);
return
foundCount
;
}
RTREE_TEMPLATE
int
RTREE_QUAL
::
Count
()
{
int
count
=
0
;
CountRec
(
m_root
,
count
);
return
count
;
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
CountRec
(
Node
*
a_node
,
int
&
a_count
)
{
if
(
a_node
->
IsInternalNode
()
)
// not a leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
CountRec
(
a_node
->
m_branch
[
index
].
m_child
,
a_count
);
}
}
else
// A leaf node
{
a_count
+=
a_node
->
m_count
;
}
}
RTREE_TEMPLATE
bool
RTREE_QUAL
::
Load
(
const
char
*
a_fileName
)
{
RemoveAll
();
// Clear existing tree
RTFileStream
stream
;
if
(
!
stream
.
OpenRead
(
a_fileName
)
)
{
return
false
;
}
bool
result
=
Load
(
stream
);
stream
.
Close
();
return
result
;
};
RTREE_TEMPLATE
bool
RTREE_QUAL
::
Load
(
RTFileStream
&
a_stream
)
{
// Write some kind of header
int
_dataFileId
=
(
'R'
<<
0
)
|
(
'T'
<<
8
)
|
(
'R'
<<
16
)
|
(
'E'
<<
24
);
int
_dataSize
=
sizeof
(
DATATYPE
);
int
_dataNumDims
=
NUMDIMS
;
int
_dataElemSize
=
sizeof
(
ELEMTYPE
);
int
_dataElemRealSize
=
sizeof
(
ELEMTYPEREAL
);
int
_dataMaxNodes
=
TMAXNODES
;
int
_dataMinNodes
=
TMINNODES
;
int
dataFileId
=
0
;
int
dataSize
=
0
;
int
dataNumDims
=
0
;
int
dataElemSize
=
0
;
int
dataElemRealSize
=
0
;
int
dataMaxNodes
=
0
;
int
dataMinNodes
=
0
;
a_stream
.
Read
(
dataFileId
);
a_stream
.
Read
(
dataSize
);
a_stream
.
Read
(
dataNumDims
);
a_stream
.
Read
(
dataElemSize
);
a_stream
.
Read
(
dataElemRealSize
);
a_stream
.
Read
(
dataMaxNodes
);
a_stream
.
Read
(
dataMinNodes
);
bool
result
=
false
;
// Test if header was valid and compatible
if
(
(
dataFileId
==
_dataFileId
)
&&
(
dataSize
==
_dataSize
)
&&
(
dataNumDims
==
_dataNumDims
)
&&
(
dataElemSize
==
_dataElemSize
)
&&
(
dataElemRealSize
==
_dataElemRealSize
)
&&
(
dataMaxNodes
==
_dataMaxNodes
)
&&
(
dataMinNodes
==
_dataMinNodes
)
)
{
// Recursively load tree
result
=
LoadRec
(
m_root
,
a_stream
);
}
return
result
;
}
RTREE_TEMPLATE
bool
RTREE_QUAL
::
LoadRec
(
Node
*
a_node
,
RTFileStream
&
a_stream
)
{
a_stream
.
Read
(
a_node
->
m_level
);
a_stream
.
Read
(
a_node
->
m_count
);
if
(
a_node
->
IsInternalNode
()
)
// not a leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
Branch
*
curBranch
=
&
a_node
->
m_branch
[
index
];
a_stream
.
ReadArray
(
curBranch
->
m_rect
.
m_min
,
NUMDIMS
);
a_stream
.
ReadArray
(
curBranch
->
m_rect
.
m_max
,
NUMDIMS
);
curBranch
->
m_child
=
AllocNode
();
LoadRec
(
curBranch
->
m_child
,
a_stream
);
}
}
else
// A leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
Branch
*
curBranch
=
&
a_node
->
m_branch
[
index
];
a_stream
.
ReadArray
(
curBranch
->
m_rect
.
m_min
,
NUMDIMS
);
a_stream
.
ReadArray
(
curBranch
->
m_rect
.
m_max
,
NUMDIMS
);
a_stream
.
Read
(
curBranch
->
m_data
);
}
}
return
true
;
// Should do more error checking on I/O operations
}
RTREE_TEMPLATE
bool
RTREE_QUAL
::
Save
(
const
char
*
a_fileName
)
{
RTFileStream
stream
;
if
(
!
stream
.
OpenWrite
(
a_fileName
)
)
{
return
false
;
}
bool
result
=
Save
(
stream
);
stream
.
Close
();
return
result
;
}
RTREE_TEMPLATE
bool
RTREE_QUAL
::
Save
(
RTFileStream
&
a_stream
)
{
// Write some kind of header
int
dataFileId
=
(
'R'
<<
0
)
|
(
'T'
<<
8
)
|
(
'R'
<<
16
)
|
(
'E'
<<
24
);
int
dataSize
=
sizeof
(
DATATYPE
);
int
dataNumDims
=
NUMDIMS
;
int
dataElemSize
=
sizeof
(
ELEMTYPE
);
int
dataElemRealSize
=
sizeof
(
ELEMTYPEREAL
);
int
dataMaxNodes
=
TMAXNODES
;
int
dataMinNodes
=
TMINNODES
;
a_stream
.
Write
(
dataFileId
);
a_stream
.
Write
(
dataSize
);
a_stream
.
Write
(
dataNumDims
);
a_stream
.
Write
(
dataElemSize
);
a_stream
.
Write
(
dataElemRealSize
);
a_stream
.
Write
(
dataMaxNodes
);
a_stream
.
Write
(
dataMinNodes
);
// Recursively save tree
bool
result
=
SaveRec
(
m_root
,
a_stream
);
return
result
;
}
RTREE_TEMPLATE
bool
RTREE_QUAL
::
SaveRec
(
Node
*
a_node
,
RTFileStream
&
a_stream
)
{
a_stream
.
Write
(
a_node
->
m_level
);
a_stream
.
Write
(
a_node
->
m_count
);
if
(
a_node
->
IsInternalNode
()
)
// not a leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
Branch
*
curBranch
=
&
a_node
->
m_branch
[
index
];
a_stream
.
WriteArray
(
curBranch
->
m_rect
.
m_min
,
NUMDIMS
);
a_stream
.
WriteArray
(
curBranch
->
m_rect
.
m_max
,
NUMDIMS
);
SaveRec
(
curBranch
->
m_child
,
a_stream
);
}
}
else
// A leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
Branch
*
curBranch
=
&
a_node
->
m_branch
[
index
];
a_stream
.
WriteArray
(
curBranch
->
m_rect
.
m_min
,
NUMDIMS
);
a_stream
.
WriteArray
(
curBranch
->
m_rect
.
m_max
,
NUMDIMS
);
a_stream
.
Write
(
curBranch
->
m_data
);
}
}
return
true
;
// Should do more error checking on I/O operations
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
RemoveAll
()
{
// Delete all existing nodes
Reset
();
m_root
=
AllocNode
();
m_root
->
m_level
=
0
;
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
Reset
()
{
#ifdef RTREE_DONT_USE_MEMPOOLS
// Delete all existing nodes
RemoveAllRec
(
m_root
);
#else // RTREE_DONT_USE_MEMPOOLS
// Just reset memory pools. We are not using complex types
// EXAMPLE
#endif // RTREE_DONT_USE_MEMPOOLS
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
RemoveAllRec
(
Node
*
a_node
)
{
ASSERT
(
a_node
);
ASSERT
(
a_node
->
m_level
>=
0
);
if
(
a_node
->
IsInternalNode
()
)
// This is an internal node in the tree
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
RemoveAllRec
(
a_node
->
m_branch
[
index
].
m_child
);
}
}
FreeNode
(
a_node
);
}
RTREE_TEMPLATE
typename
RTREE_QUAL
::
Node
*
RTREE_QUAL
::
AllocNode
()
{
Node
*
newNode
;
#ifdef RTREE_DONT_USE_MEMPOOLS
newNode
=
new
Node
;
#else // RTREE_DONT_USE_MEMPOOLS
// EXAMPLE
#endif // RTREE_DONT_USE_MEMPOOLS
InitNode
(
newNode
);
return
newNode
;
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
FreeNode
(
Node
*
a_node
)
{
ASSERT
(
a_node
);
#ifdef RTREE_DONT_USE_MEMPOOLS
delete
a_node
;
#else // RTREE_DONT_USE_MEMPOOLS
// EXAMPLE
#endif // RTREE_DONT_USE_MEMPOOLS
}
// Allocate space for a node in the list used in DeletRect to
// store Nodes that are too empty.
RTREE_TEMPLATE
typename
RTREE_QUAL
::
ListNode
*
RTREE_QUAL
::
AllocListNode
()
{
#ifdef RTREE_DONT_USE_MEMPOOLS
return
new
ListNode
;
#else // RTREE_DONT_USE_MEMPOOLS
// EXAMPLE
#endif // RTREE_DONT_USE_MEMPOOLS
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
FreeListNode
(
ListNode
*
a_listNode
)
{
#ifdef RTREE_DONT_USE_MEMPOOLS
delete
a_listNode
;
#else // RTREE_DONT_USE_MEMPOOLS
// EXAMPLE
#endif // RTREE_DONT_USE_MEMPOOLS
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
InitNode
(
Node
*
a_node
)
{
a_node
->
m_count
=
0
;
a_node
->
m_level
=
-
1
;
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
InitRect
(
Rect
*
a_rect
)
{
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
a_rect
->
m_min
[
index
]
=
(
ELEMTYPE
)
0
;
a_rect
->
m_max
[
index
]
=
(
ELEMTYPE
)
0
;
}
}
// Inserts a new data rectangle into the index structure.
// Recursively descends tree, propagates splits back up.
// Returns 0 if node was not split. Old node updated.
// If node was split, returns 1 and sets the pointer pointed to by
// new_node to point to the new node. Old node updated to become one of two.
// The level argument specifies the number of steps up from the leaf
// level to insert; e.g. a data rectangle goes in at level = 0.
RTREE_TEMPLATE
bool
RTREE_QUAL
::
InsertRectRec
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
*
a_node
,
Node
**
a_newNode
,
int
a_level
)
{
ASSERT
(
a_rect
&&
a_node
&&
a_newNode
);
ASSERT
(
a_level
>=
0
&&
a_level
<=
a_node
->
m_level
);
int
index
;
Branch
branch
;
Node
*
otherNode
;
// Still above level for insertion, go down tree recursively
if
(
a_node
->
m_level
>
a_level
)
{
index
=
PickBranch
(
a_rect
,
a_node
);
if
(
!
InsertRectRec
(
a_rect
,
a_id
,
a_node
->
m_branch
[
index
].
m_child
,
&
otherNode
,
a_level
)
)
{
// Child was not split
a_node
->
m_branch
[
index
].
m_rect
=
CombineRect
(
a_rect
,
&
(
a_node
->
m_branch
[
index
].
m_rect
)
);
return
false
;
}
else
// Child was split
{
a_node
->
m_branch
[
index
].
m_rect
=
NodeCover
(
a_node
->
m_branch
[
index
].
m_child
);
branch
.
m_child
=
otherNode
;
branch
.
m_rect
=
NodeCover
(
otherNode
);
return
AddBranch
(
&
branch
,
a_node
,
a_newNode
);
}
}
else
if
(
a_node
->
m_level
==
a_level
)
// Have reached level for insertion. Add rect, split if necessary
{
branch
.
m_rect
=
*
a_rect
;
branch
.
m_child
=
(
Node
*
)
a_id
;
// Child field of leaves contains id of data record
return
AddBranch
(
&
branch
,
a_node
,
a_newNode
);
}
else
{
// Should never occur
ASSERT
(
0
);
return
false
;
}
}
// Insert a data rectangle into an index structure.
// InsertRect provides for splitting the root;
// returns 1 if root was split, 0 if it was not.
// The level argument specifies the number of steps up from the leaf
// level to insert; e.g. a data rectangle goes in at level = 0.
// InsertRect2 does the recursion.
//
RTREE_TEMPLATE
bool
RTREE_QUAL
::
InsertRect
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
**
a_root
,
int
a_level
)
{
ASSERT
(
a_rect
&&
a_root
);
ASSERT
(
a_level
>=
0
&&
a_level
<=
(
*
a_root
)
->
m_level
);
#ifdef _DEBUG
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
ASSERT
(
a_rect
->
m_min
[
index
]
<=
a_rect
->
m_max
[
index
]
);
}
#endif // _DEBUG
Node
*
newRoot
;
Node
*
newNode
;
Branch
branch
;
if
(
InsertRectRec
(
a_rect
,
a_id
,
*
a_root
,
&
newNode
,
a_level
)
)
// Root split
{
newRoot
=
AllocNode
();
// Grow tree taller and new root
newRoot
->
m_level
=
(
*
a_root
)
->
m_level
+
1
;
branch
.
m_rect
=
NodeCover
(
*
a_root
);
branch
.
m_child
=
*
a_root
;
AddBranch
(
&
branch
,
newRoot
,
NULL
);
branch
.
m_rect
=
NodeCover
(
newNode
);
branch
.
m_child
=
newNode
;
AddBranch
(
&
branch
,
newRoot
,
NULL
);
*
a_root
=
newRoot
;
return
true
;
}
return
false
;
}
// Find the smallest rectangle that includes all rectangles in branches of a node.
RTREE_TEMPLATE
typename
RTREE_QUAL
::
Rect
RTREE_QUAL
::
NodeCover
(
Node
*
a_node
)
{
ASSERT
(
a_node
);
int
firstTime
=
true
;
Rect
rect
;
InitRect
(
&
rect
);
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
if
(
firstTime
)
{
rect
=
a_node
->
m_branch
[
index
].
m_rect
;
firstTime
=
false
;
}
else
{
rect
=
CombineRect
(
&
rect
,
&
(
a_node
->
m_branch
[
index
].
m_rect
)
);
}
}
return
rect
;
}
// Add a branch to a node. Split the node if necessary.
// Returns 0 if node not split. Old node updated.
// Returns 1 if node split, sets *new_node to address of new node.
// Old node updated, becomes one of two.
RTREE_TEMPLATE
bool
RTREE_QUAL
::
AddBranch
(
Branch
*
a_branch
,
Node
*
a_node
,
Node
**
a_newNode
)
{
ASSERT
(
a_branch
);
ASSERT
(
a_node
);
if
(
a_node
->
m_count
<
MAXNODES
)
// Split won't be necessary
{
a_node
->
m_branch
[
a_node
->
m_count
]
=
*
a_branch
;
++
a_node
->
m_count
;
return
false
;
}
else
{
ASSERT
(
a_newNode
);
SplitNode
(
a_node
,
a_branch
,
a_newNode
);
return
true
;
}
}
// Disconnect a dependent node.
// Caller must return (or stop using iteration index) after this as count has changed
RTREE_TEMPLATE
void
RTREE_QUAL
::
DisconnectBranch
(
Node
*
a_node
,
int
a_index
)
{
ASSERT
(
a_node
&&
(
a_index
>=
0
)
&&
(
a_index
<
MAXNODES
)
);
ASSERT
(
a_node
->
m_count
>
0
);
// Remove element by swapping with the last element to prevent gaps in array
a_node
->
m_branch
[
a_index
]
=
a_node
->
m_branch
[
a_node
->
m_count
-
1
];
--
a_node
->
m_count
;
}
// Pick a branch. Pick the one that will need the smallest increase
// in area to accomodate the new rectangle. This will result in the
// least total area for the covering rectangles in the current node.
// In case of a tie, pick the one which was smaller before, to get
// the best resolution when searching.
RTREE_TEMPLATE
int
RTREE_QUAL
::
PickBranch
(
Rect
*
a_rect
,
Node
*
a_node
)
{
ASSERT
(
a_rect
&&
a_node
);
bool
firstTime
=
true
;
ELEMTYPEREAL
increase
;
ELEMTYPEREAL
bestIncr
=
(
ELEMTYPEREAL
)
-
1
;
ELEMTYPEREAL
area
;
ELEMTYPEREAL
bestArea
;
int
best
=
0
;
// avoid compil complaint about uninitialized
Rect
tempRect
;
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
Rect
*
curRect
=
&
a_node
->
m_branch
[
index
].
m_rect
;
area
=
CalcRectVolume
(
curRect
);
tempRect
=
CombineRect
(
a_rect
,
curRect
);
increase
=
CalcRectVolume
(
&
tempRect
)
-
area
;
if
(
(
increase
<
bestIncr
)
||
firstTime
)
{
best
=
index
;
bestArea
=
area
;
bestIncr
=
increase
;
firstTime
=
false
;
}
else
if
(
(
increase
==
bestIncr
)
&&
(
area
<
bestArea
)
)
{
best
=
index
;
bestArea
=
area
;
bestIncr
=
increase
;
}
}
return
best
;
}
// Combine two rectangles into larger one containing both
RTREE_TEMPLATE
typename
RTREE_QUAL
::
Rect
RTREE_QUAL
::
CombineRect
(
Rect
*
a_rectA
,
Rect
*
a_rectB
)
{
ASSERT
(
a_rectA
&&
a_rectB
);
Rect
newRect
;
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
newRect
.
m_min
[
index
]
=
Min
(
a_rectA
->
m_min
[
index
],
a_rectB
->
m_min
[
index
]
);
newRect
.
m_max
[
index
]
=
Max
(
a_rectA
->
m_max
[
index
],
a_rectB
->
m_max
[
index
]
);
}
return
newRect
;
}
// Split a node.
// Divides the nodes branches and the extra one between two nodes.
// Old node is one of the new ones, and one really new one is created.
// Tries more than one method for choosing a partition, uses best result.
RTREE_TEMPLATE
void
RTREE_QUAL
::
SplitNode
(
Node
*
a_node
,
Branch
*
a_branch
,
Node
**
a_newNode
)
{
ASSERT
(
a_node
);
ASSERT
(
a_branch
);
// Could just use local here, but member or external is faster since it is reused
PartitionVars
localVars
;
PartitionVars
*
parVars
=
&
localVars
;
int
level
;
// Load all the branches into a buffer, initialize old node
level
=
a_node
->
m_level
;
GetBranches
(
a_node
,
a_branch
,
parVars
);
// Find partition
ChoosePartition
(
parVars
,
MINNODES
);
// Put branches from buffer into 2 nodes according to chosen partition
*
a_newNode
=
AllocNode
();
(
*
a_newNode
)
->
m_level
=
a_node
->
m_level
=
level
;
LoadNodes
(
a_node
,
*
a_newNode
,
parVars
);
ASSERT
(
(
a_node
->
m_count
+
(
*
a_newNode
)
->
m_count
)
==
parVars
->
m_total
);
}
// Calculate the n-dimensional volume of a rectangle
RTREE_TEMPLATE
ELEMTYPEREAL
RTREE_QUAL
::
RectVolume
(
Rect
*
a_rect
)
{
ASSERT
(
a_rect
);
ELEMTYPEREAL
volume
=
(
ELEMTYPEREAL
)
1
;
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
volume
*=
a_rect
->
m_max
[
index
]
-
a_rect
->
m_min
[
index
];
}
ASSERT
(
volume
>=
(
ELEMTYPEREAL
)
0
);
return
volume
;
}
// The exact volume of the bounding sphere for the given Rect
RTREE_TEMPLATE
ELEMTYPEREAL
RTREE_QUAL
::
RectSphericalVolume
(
Rect
*
a_rect
)
{
ASSERT
(
a_rect
);
ELEMTYPEREAL
sumOfSquares
=
(
ELEMTYPEREAL
)
0
;
ELEMTYPEREAL
radius
;
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
ELEMTYPEREAL
halfExtent
=
(
(
ELEMTYPEREAL
)
a_rect
->
m_max
[
index
]
-
(
ELEMTYPEREAL
)
a_rect
->
m_min
[
index
]
)
*
0
.
5
f
;
sumOfSquares
+=
halfExtent
*
halfExtent
;
}
radius
=
(
ELEMTYPEREAL
)
sqrt
(
sumOfSquares
);
// Pow maybe slow, so test for common dims like 2,3 and just use x*x, x*x*x.
if
(
NUMDIMS
==
3
)
{
return
radius
*
radius
*
radius
*
m_unitSphereVolume
;
}
else
if
(
NUMDIMS
==
2
)
{
return
radius
*
radius
*
m_unitSphereVolume
;
}
else
{
return
(
ELEMTYPEREAL
)
(
pow
(
radius
,
NUMDIMS
)
*
m_unitSphereVolume
);
}
}
// Use one of the methods to calculate retangle volume
RTREE_TEMPLATE
ELEMTYPEREAL
RTREE_QUAL
::
CalcRectVolume
(
Rect
*
a_rect
)
{
#ifdef RTREE_USE_SPHERICAL_VOLUME
return
RectSphericalVolume
(
a_rect
);
// Slower but helps certain merge cases
#else // RTREE_USE_SPHERICAL_VOLUME
return
RectVolume
(
a_rect
);
// Faster but can cause poor merges
#endif // RTREE_USE_SPHERICAL_VOLUME
}
// Load branch buffer with branches from full node plus the extra branch.
RTREE_TEMPLATE
void
RTREE_QUAL
::
GetBranches
(
Node
*
a_node
,
Branch
*
a_branch
,
PartitionVars
*
a_parVars
)
{
ASSERT
(
a_node
);
ASSERT
(
a_branch
);
ASSERT
(
a_node
->
m_count
==
MAXNODES
);
// Load the branch buffer
for
(
int
index
=
0
;
index
<
MAXNODES
;
++
index
)
{
a_parVars
->
m_branchBuf
[
index
]
=
a_node
->
m_branch
[
index
];
}
a_parVars
->
m_branchBuf
[
MAXNODES
]
=
*
a_branch
;
a_parVars
->
m_branchCount
=
MAXNODES
+
1
;
// Calculate rect containing all in the set
a_parVars
->
m_coverSplit
=
a_parVars
->
m_branchBuf
[
0
].
m_rect
;
for
(
int
index
=
1
;
index
<
MAXNODES
+
1
;
++
index
)
{
a_parVars
->
m_coverSplit
=
CombineRect
(
&
a_parVars
->
m_coverSplit
,
&
a_parVars
->
m_branchBuf
[
index
].
m_rect
);
}
a_parVars
->
m_coverSplitArea
=
CalcRectVolume
(
&
a_parVars
->
m_coverSplit
);
InitNode
(
a_node
);
}
// Method #0 for choosing a partition:
// As the seeds for the two groups, pick the two rects that would waste the
// most area if covered by a single rectangle, i.e. evidently the worst pair
// to have in the same group.
// Of the remaining, one at a time is chosen to be put in one of the two groups.
// The one chosen is the one with the greatest difference in area expansion
// depending on which group - the rect most strongly attracted to one group
// and repelled from the other.
// If one group gets too full (more would force other group to violate min
// fill requirement) then other group gets the rest.
// These last are the ones that can go in either group most easily.
RTREE_TEMPLATE
void
RTREE_QUAL
::
ChoosePartition
(
PartitionVars
*
a_parVars
,
int
a_minFill
)
{
ASSERT
(
a_parVars
);
ELEMTYPEREAL
biggestDiff
;
int
group
,
chosen
=
0
,
betterGroup
=
0
;
InitParVars
(
a_parVars
,
a_parVars
->
m_branchCount
,
a_minFill
);
PickSeeds
(
a_parVars
);
while
(
(
(
a_parVars
->
m_count
[
0
]
+
a_parVars
->
m_count
[
1
])
<
a_parVars
->
m_total
)
&&
(
a_parVars
->
m_count
[
0
]
<
(
a_parVars
->
m_total
-
a_parVars
->
m_minFill
)
)
&&
(
a_parVars
->
m_count
[
1
]
<
(
a_parVars
->
m_total
-
a_parVars
->
m_minFill
)
)
)
{
biggestDiff
=
(
ELEMTYPEREAL
)
-
1
;
for
(
int
index
=
0
;
index
<
a_parVars
->
m_total
;
++
index
)
{
if
(
!
a_parVars
->
m_taken
[
index
]
)
{
Rect
*
curRect
=
&
a_parVars
->
m_branchBuf
[
index
].
m_rect
;
Rect
rect0
=
CombineRect
(
curRect
,
&
a_parVars
->
m_cover
[
0
]
);
Rect
rect1
=
CombineRect
(
curRect
,
&
a_parVars
->
m_cover
[
1
]
);
ELEMTYPEREAL
growth0
=
CalcRectVolume
(
&
rect0
)
-
a_parVars
->
m_area
[
0
];
ELEMTYPEREAL
growth1
=
CalcRectVolume
(
&
rect1
)
-
a_parVars
->
m_area
[
1
];
ELEMTYPEREAL
diff
=
growth1
-
growth0
;
if
(
diff
>=
0
)
{
group
=
0
;
}
else
{
group
=
1
;
diff
=
-
diff
;
}
if
(
diff
>
biggestDiff
)
{
biggestDiff
=
diff
;
chosen
=
index
;
betterGroup
=
group
;
}
else
if
(
(
diff
==
biggestDiff
)
&&
(
a_parVars
->
m_count
[
group
]
<
a_parVars
->
m_count
[
betterGroup
])
)
{
chosen
=
index
;
betterGroup
=
group
;
}
}
}
Classify
(
chosen
,
betterGroup
,
a_parVars
);
}
// If one group too full, put remaining rects in the other
if
(
(
a_parVars
->
m_count
[
0
]
+
a_parVars
->
m_count
[
1
])
<
a_parVars
->
m_total
)
{
if
(
a_parVars
->
m_count
[
0
]
>=
a_parVars
->
m_total
-
a_parVars
->
m_minFill
)
{
group
=
1
;
}
else
{
group
=
0
;
}
for
(
int
index
=
0
;
index
<
a_parVars
->
m_total
;
++
index
)
{
if
(
!
a_parVars
->
m_taken
[
index
]
)
{
Classify
(
index
,
group
,
a_parVars
);
}
}
}
ASSERT
(
(
a_parVars
->
m_count
[
0
]
+
a_parVars
->
m_count
[
1
])
==
a_parVars
->
m_total
);
ASSERT
(
(
a_parVars
->
m_count
[
0
]
>=
a_parVars
->
m_minFill
)
&&
(
a_parVars
->
m_count
[
1
]
>=
a_parVars
->
m_minFill
)
);
}
// Copy branches from the buffer into two nodes according to the partition.
RTREE_TEMPLATE
void
RTREE_QUAL
::
LoadNodes
(
Node
*
a_nodeA
,
Node
*
a_nodeB
,
PartitionVars
*
a_parVars
)
{
ASSERT
(
a_nodeA
);
ASSERT
(
a_nodeB
);
ASSERT
(
a_parVars
);
for
(
int
index
=
0
;
index
<
a_parVars
->
m_total
;
++
index
)
{
ASSERT
(
a_parVars
->
m_partition
[
index
]
==
0
||
a_parVars
->
m_partition
[
index
]
==
1
);
if
(
a_parVars
->
m_partition
[
index
]
==
0
)
{
AddBranch
(
&
a_parVars
->
m_branchBuf
[
index
],
a_nodeA
,
NULL
);
}
else
if
(
a_parVars
->
m_partition
[
index
]
==
1
)
{
AddBranch
(
&
a_parVars
->
m_branchBuf
[
index
],
a_nodeB
,
NULL
);
}
}
}
// Initialize a PartitionVars structure.
RTREE_TEMPLATE
void
RTREE_QUAL
::
InitParVars
(
PartitionVars
*
a_parVars
,
int
a_maxRects
,
int
a_minFill
)
{
ASSERT
(
a_parVars
);
a_parVars
->
m_count
[
0
]
=
a_parVars
->
m_count
[
1
]
=
0
;
a_parVars
->
m_area
[
0
]
=
a_parVars
->
m_area
[
1
]
=
(
ELEMTYPEREAL
)
0
;
a_parVars
->
m_total
=
a_maxRects
;
a_parVars
->
m_minFill
=
a_minFill
;
for
(
int
index
=
0
;
index
<
a_maxRects
;
++
index
)
{
a_parVars
->
m_taken
[
index
]
=
false
;
a_parVars
->
m_partition
[
index
]
=
-
1
;
}
}
RTREE_TEMPLATE
void
RTREE_QUAL
::
PickSeeds
(
PartitionVars
*
a_parVars
)
{
int
seed0
=
0
,
seed1
=
0
;
// avoid compil complaint about uninitialized
ELEMTYPEREAL
worst
,
waste
;
ELEMTYPEREAL
area
[
MAXNODES
+
1
];
for
(
int
index
=
0
;
index
<
a_parVars
->
m_total
;
++
index
)
{
area
[
index
]
=
CalcRectVolume
(
&
a_parVars
->
m_branchBuf
[
index
].
m_rect
);
}
worst
=
-
a_parVars
->
m_coverSplitArea
-
1
;
for
(
int
indexA
=
0
;
indexA
<
a_parVars
->
m_total
-
1
;
++
indexA
)
{
for
(
int
indexB
=
indexA
+
1
;
indexB
<
a_parVars
->
m_total
;
++
indexB
)
{
Rect
oneRect
=
CombineRect
(
&
a_parVars
->
m_branchBuf
[
indexA
].
m_rect
,
&
a_parVars
->
m_branchBuf
[
indexB
].
m_rect
);
waste
=
CalcRectVolume
(
&
oneRect
)
-
area
[
indexA
]
-
area
[
indexB
];
if
(
waste
>
worst
)
{
worst
=
waste
;
seed0
=
indexA
;
seed1
=
indexB
;
}
}
}
Classify
(
seed0
,
0
,
a_parVars
);
Classify
(
seed1
,
1
,
a_parVars
);
}
// Put a branch in one of the groups.
RTREE_TEMPLATE
void
RTREE_QUAL
::
Classify
(
int
a_index
,
int
a_group
,
PartitionVars
*
a_parVars
)
{
ASSERT
(
a_parVars
);
ASSERT
(
!
a_parVars
->
m_taken
[
a_index
]
);
a_parVars
->
m_partition
[
a_index
]
=
a_group
;
a_parVars
->
m_taken
[
a_index
]
=
true
;
if
(
a_parVars
->
m_count
[
a_group
]
==
0
)
{
a_parVars
->
m_cover
[
a_group
]
=
a_parVars
->
m_branchBuf
[
a_index
].
m_rect
;
}
else
{
a_parVars
->
m_cover
[
a_group
]
=
CombineRect
(
&
a_parVars
->
m_branchBuf
[
a_index
].
m_rect
,
&
a_parVars
->
m_cover
[
a_group
]
);
}
a_parVars
->
m_area
[
a_group
]
=
CalcRectVolume
(
&
a_parVars
->
m_cover
[
a_group
]
);
++
a_parVars
->
m_count
[
a_group
];
}
// Delete a data rectangle from an index structure.
// Pass in a pointer to a Rect, the tid of the record, ptr to ptr to root node.
// Returns 1 if record not found, 0 if success.
// RemoveRect provides for eliminating the root.
RTREE_TEMPLATE
bool
RTREE_QUAL
::
RemoveRect
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
**
a_root
)
{
ASSERT
(
a_rect
&&
a_root
);
ASSERT
(
*
a_root
);
Node
*
tempNode
;
ListNode
*
reInsertList
=
NULL
;
if
(
!
RemoveRectRec
(
a_rect
,
a_id
,
*
a_root
,
&
reInsertList
)
)
{
// Found and deleted a data item
// Reinsert any branches from eliminated nodes
while
(
reInsertList
)
{
tempNode
=
reInsertList
->
m_node
;
for
(
int
index
=
0
;
index
<
tempNode
->
m_count
;
++
index
)
{
InsertRect
(
&
(
tempNode
->
m_branch
[
index
].
m_rect
),
tempNode
->
m_branch
[
index
].
m_data
,
a_root
,
tempNode
->
m_level
);
}
ListNode
*
remLNode
=
reInsertList
;
reInsertList
=
reInsertList
->
m_next
;
FreeNode
(
remLNode
->
m_node
);
FreeListNode
(
remLNode
);
}
// Check for redundant root (not leaf, 1 child) and eliminate
if
(
(
*
a_root
)
->
m_count
==
1
&&
(
*
a_root
)
->
IsInternalNode
()
)
{
tempNode
=
(
*
a_root
)
->
m_branch
[
0
].
m_child
;
ASSERT
(
tempNode
);
FreeNode
(
*
a_root
);
*
a_root
=
tempNode
;
}
return
false
;
}
else
{
return
true
;
}
}
// Delete a rectangle from non-root part of an index structure.
// Called by RemoveRect. Descends tree recursively,
// merges branches on the way back up.
// Returns 1 if record not found, 0 if success.
RTREE_TEMPLATE
bool
RTREE_QUAL
::
RemoveRectRec
(
Rect
*
a_rect
,
const
DATATYPE
&
a_id
,
Node
*
a_node
,
ListNode
**
a_listNode
)
{
ASSERT
(
a_rect
&&
a_node
&&
a_listNode
);
ASSERT
(
a_node
->
m_level
>=
0
);
if
(
a_node
->
IsInternalNode
()
)
// not a leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
if
(
Overlap
(
a_rect
,
&
(
a_node
->
m_branch
[
index
].
m_rect
)
)
)
{
if
(
!
RemoveRectRec
(
a_rect
,
a_id
,
a_node
->
m_branch
[
index
].
m_child
,
a_listNode
)
)
{
if
(
a_node
->
m_branch
[
index
].
m_child
->
m_count
>=
MINNODES
)
{
// child removed, just resize parent rect
a_node
->
m_branch
[
index
].
m_rect
=
NodeCover
(
a_node
->
m_branch
[
index
].
m_child
);
}
else
{
// child removed, not enough entries in node, eliminate node
ReInsert
(
a_node
->
m_branch
[
index
].
m_child
,
a_listNode
);
DisconnectBranch
(
a_node
,
index
);
// Must return after this call as count has changed
}
return
false
;
}
}
}
return
true
;
}
else
// A leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
if
(
a_node
->
m_branch
[
index
].
m_child
==
(
Node
*
)
a_id
)
{
DisconnectBranch
(
a_node
,
index
);
// Must return after this call as count has changed
return
false
;
}
}
return
true
;
}
}
// Decide whether two rectangles overlap.
RTREE_TEMPLATE
bool
RTREE_QUAL
::
Overlap
(
Rect
*
a_rectA
,
Rect
*
a_rectB
)
{
ASSERT
(
a_rectA
&&
a_rectB
);
for
(
int
index
=
0
;
index
<
NUMDIMS
;
++
index
)
{
if
(
a_rectA
->
m_min
[
index
]
>
a_rectB
->
m_max
[
index
]
||
a_rectB
->
m_min
[
index
]
>
a_rectA
->
m_max
[
index
]
)
{
return
false
;
}
}
return
true
;
}
// Add a node to the reinsertion list. All its branches will later
// be reinserted into the index structure.
RTREE_TEMPLATE
void
RTREE_QUAL
::
ReInsert
(
Node
*
a_node
,
ListNode
**
a_listNode
)
{
ListNode
*
newListNode
;
newListNode
=
AllocListNode
();
newListNode
->
m_node
=
a_node
;
newListNode
->
m_next
=
*
a_listNode
;
*
a_listNode
=
newListNode
;
}
// Search in an index tree or subtree for all data retangles that overlap the argument rectangle.
RTREE_TEMPLATE
bool
RTREE_QUAL
::
Search
(
Node
*
a_node
,
Rect
*
a_rect
,
int
&
a_foundCount
,
bool
a_resultCallback
(
DATATYPE
a_data
,
void
*
a_context
),
void
*
a_context
)
{
ASSERT
(
a_node
);
ASSERT
(
a_node
->
m_level
>=
0
);
ASSERT
(
a_rect
);
if
(
a_node
->
IsInternalNode
()
)
// This is an internal node in the tree
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
if
(
Overlap
(
a_rect
,
&
a_node
->
m_branch
[
index
].
m_rect
)
)
{
if
(
!
Search
(
a_node
->
m_branch
[
index
].
m_child
,
a_rect
,
a_foundCount
,
a_resultCallback
,
a_context
)
)
{
return
false
;
// Don't continue searching
}
}
}
}
else
// This is a leaf node
{
for
(
int
index
=
0
;
index
<
a_node
->
m_count
;
++
index
)
{
if
(
Overlap
(
a_rect
,
&
a_node
->
m_branch
[
index
].
m_rect
)
)
{
DATATYPE
&
id
=
a_node
->
m_branch
[
index
].
m_data
;
// NOTE: There are different ways to return results. Here's where to modify
if
(
&
a_resultCallback
)
{
++
a_foundCount
;
if
(
!
a_resultCallback
(
id
,
a_context
)
)
{
return
false
;
// Don't continue searching
}
}
}
}
}
return
true
;
// Continue searching
}
#undef RTREE_TEMPLATE
#undef RTREE_QUAL
#undef RTREE_SEARCH_TEMPLATE
#undef RTREE_SEARCH_QUAL
#endif // RTREE_H
include/view/view_rtree.h
View file @
3701e131
...
...
@@ -27,7 +27,7 @@
#include <math/box2.h>
#include <rtree.h>
#include <
geometry/
rtree.h>
namespace
KIGFX
{
...
...
@@ -76,7 +76,6 @@ public:
* Executes a function object aVisitor for each item whose bounding box intersects
* with aBounds.
*/
template
<
class
Visitor
>
void
Query
(
const
BOX2I
&
aBounds
,
Visitor
&
aVisitor
)
// const
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment