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
dcb5d8f2
Commit
dcb5d8f2
authored
Sep 18, 2013
by
Maciej Suminski
Browse files
Options
Browse Files
Download
Plain Diff
Merged Tom's branch
parents
3f6fa861
75b59d8f
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
5359 additions
and
285 deletions
+5359
-285
graphics_abstraction_layer.cpp
common/gal/graphics_abstraction_layer.cpp
+2
-2
opengl_gal.cpp
common/gal/opengl/opengl_gal.cpp
+1
-1
shape_line_chain.cpp
common/geometry/shape_line_chain.cpp
+1
-1
profile.h
common/profile.h
+4
-3
view.cpp
common/view/view.cpp
+8
-0
wx_view_controls.cpp
common/view/wx_view_controls.cpp
+13
-0
rtree.h
include/geometry/rtree.h
+1908
-0
seg.h
include/geometry/seg.h
+7
-12
shape.h
include/geometry/shape.h
+4
-1
shape_index.h
include/geometry/shape_index.h
+319
-256
shape_index_list.h
include/geometry/shape_index_list.h
+290
-0
view.h
include/view/view.h
+2
-0
view_controls.h
include/view/view_controls.h
+27
-3
view_group.h
include/view/view_group.h
+1
-1
wxPcbStruct.h
include/wxPcbStruct.h
+5
-0
pcb_painter.cpp
pcbnew/pcb_painter.cpp
+2
-4
CMakeLists.txt
polygon/CMakeLists.txt
+7
-1
shapes.cc
polygon/poly2tri/common/shapes.cc
+367
-0
shapes.h
polygon/poly2tri/common/shapes.h
+325
-0
utils.h
polygon/poly2tri/common/utils.h
+123
-0
poly2tri.h
polygon/poly2tri/poly2tri.h
+39
-0
advancing_front.cc
polygon/poly2tri/sweep/advancing_front.cc
+109
-0
advancing_front.h
polygon/poly2tri/sweep/advancing_front.h
+118
-0
cdt.cc
polygon/poly2tri/sweep/cdt.cc
+72
-0
cdt.h
polygon/poly2tri/sweep/cdt.h
+105
-0
sweep.cc
polygon/poly2tri/sweep/sweep.cc
+813
-0
sweep.h
polygon/poly2tri/sweep/sweep.h
+285
-0
sweep_context.cc
polygon/poly2tri/sweep/sweep_context.cc
+216
-0
sweep_context.h
polygon/poly2tri/sweep/sweep_context.h
+186
-0
No files found.
common/gal/graphics_abstraction_layer.cpp
View file @
dcb5d8f2
...
...
@@ -54,8 +54,8 @@ GAL::GAL() :
// Initialize the cursor shape
SetCursorColor
(
COLOR4D
(
1.0
,
1.0
,
1.0
,
1.0
)
);
SetCursorSize
(
15
);
SetCursorEnabled
(
tru
e
);
SetCursorSize
(
80
);
SetCursorEnabled
(
fals
e
);
strokeFont
.
LoadNewStrokeFont
(
newstroke_font
,
newstroke_font_bufsize
);
}
...
...
common/gal/opengl/opengl_gal.cpp
View file @
dcb5d8f2
...
...
@@ -82,7 +82,7 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
SetSize
(
aParent
->
GetSize
()
);
screenSize
=
VECTOR2D
(
aParent
->
GetSize
()
);
initCursor
(
2
0
);
initCursor
(
8
0
);
// Grid color settings are different in Cairo and OpenGL
SetGridColor
(
COLOR4D
(
0.8
,
0.8
,
0.8
,
0.1
)
);
...
...
common/geometry/shape_line_chain.cpp
View file @
dcb5d8f2
...
...
@@ -439,7 +439,7 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
const
VECTOR2I
SHAPE_LINE_CHAIN
::
NearestPoint
(
const
VECTOR2I
&
aP
)
const
{
int
min_d
=
INT_MAX
;
int
nearest
;
int
nearest
=
0
;
for
(
int
i
=
0
;
i
<
SegmentCount
()
;
i
++
)
{
int
d
=
CSegment
(
i
).
Distance
(
aP
);
...
...
common/profile.h
View file @
dcb5d8f2
...
...
@@ -27,8 +27,8 @@
* @brief Simple profiling functions for measuring code execution time.
*/
#ifndef __PROFILE_H
#define __PROFILE_H
#ifndef __
T
PROFILE_H
#define __
T
PROFILE_H
#include <sys/time.h>
#include <stdint.h>
...
...
@@ -96,7 +96,6 @@ static inline uint64_t get_tics()
gettimeofday
(
&
tv
,
NULL
);
return
(
uint64_t
)
tv
.
tv_sec
*
1000000ULL
+
(
uint64_t
)
tv
.
tv_usec
;
#endif
}
...
...
@@ -144,3 +143,5 @@ static inline void prof_end( prof_counter* cnt )
else
cnt
->
value
=
get_tics
()
-
cnt
->
value
;
}
#endif
common/view/view.cpp
View file @
dcb5d8f2
...
...
@@ -452,6 +452,14 @@ void VIEW::ChangeLayerDepth( int aLayer, int aDepth )
m_layers
[
aLayer
].
items
->
Query
(
r
,
visitor
);
}
int
VIEW
::
GetTopLayer
(
)
const
{
if
(
m_topLayers
.
size
()
==
0
)
return
0
;
return
*
m_topLayers
.
begin
();
}
void
VIEW
::
SetTopLayer
(
int
aLayer
,
bool
aEnabled
)
{
...
...
common/view/wx_view_controls.cpp
View file @
dcb5d8f2
...
...
@@ -60,11 +60,24 @@ WX_VIEW_CONTROLS::WX_VIEW_CONTROLS( VIEW* aView, wxWindow* aParentPanel ) :
}
void
VIEW_CONTROLS
::
ShowCursor
(
bool
aEnabled
)
{
m_view
->
GetGAL
()
->
SetCursorEnabled
(
aEnabled
);
}
void
WX_VIEW_CONTROLS
::
onMotion
(
wxMouseEvent
&
aEvent
)
{
m_mousePosition
.
x
=
aEvent
.
GetX
();
m_mousePosition
.
y
=
aEvent
.
GetY
();
if
(
m_forceCursorPosition
)
m_cursorPosition
=
m_view
->
ToScreen
(
m_forcedPosition
);
else
if
(
m_snappingEnabled
)
m_cursorPosition
=
m_view
->
GetGAL
()
->
GetGridPoint
(
m_mousePosition
);
else
m_cursorPosition
=
m_mousePosition
;
bool
isAutoPanning
=
false
;
if
(
m_autoPanEnabled
)
...
...
include/geometry/rtree.h
0 → 100644
View file @
dcb5d8f2
//TITLE
//
// R-TREES: A DYNAMIC INDEX STRUCTURE FOR SPATIAL SEARCHING
//
//DESCRIPTION
//
// A C++ templated version of the RTree algorithm.
// For more information please read the comments in RTree.h
//
//AUTHORS
//
// * 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
// * 2013 CERN (www.cern.ch)
//
//LICENSE:
//
// Entirely free for all uses. Enjoy!
#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
};
struct
Statistics
{
int
maxDepth
;
int
avgDepth
;
int
maxNodeLoad
;
int
avgNodeLoad
;
int
totalItems
;
};
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.
int
cnt
;
Search
(
m_root
,
&
rect
,
a_visitor
,
cnt
);
return
cnt
;
}
/// Calculate Statistics
Statistics
CalcStats
(
);
/// 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
);
/// Find the nearest neighbor of a specific point.
/// It uses the MINDIST method, simplifying the one from "R-Trees: Theory and Applications" by Yannis Manolopoulos et al.
/// The bounding rectangle is used to calculate the distance to the DATATYPE.
/// \param a_point point to start the search
/// \return Returns the DATATYPE located closest to a_point, 0 if the tree is empty.
DATATYPE
NearestNeighbor
(
const
ELEMTYPE
a_point
[
NUMDIMS
]
);
/// Find the nearest neighbor of a specific point.
/// It uses the MINDIST method, simplifying the one from "R-Trees: Theory and Applications" by Yannis Manolopoulos et al.
/// It receives a callback function to calculate the distance to a DATATYPE object, instead of using the bounding rectangle.
/// \param a_point point to start the search
/// \param a_squareDistanceCallback function that performs the square distance calculation for the selected DATATYPE.
/// \param a_squareDistance Pointer in which the square distance to the nearest neighbour will be returned.
/// \return Returns the DATATYPE located closest to a_point, 0 if the tree is empty.
DATATYPE
NearestNeighbor
(
const
ELEMTYPE
a_point
[
NUMDIMS
],
ELEMTYPE
a_squareDistanceCallback
(
const
ELEMTYPE
a_point
[
NUMDIMS
],
DATATYPE
a_data
),
ELEMTYPE
*
a_squareDistance
);
/// 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
;
};
/// Data structure used for Nearest Neighbor search implementation
struct
NNNode
{
Branch
m_branch
;
ELEMTYPE
minDist
;
bool
isLeaf
;
};
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
);
ELEMTYPE
MinDist
(
const
ELEMTYPE
a_point
[
NUMDIMS
],
Rect
*
a_rect
);
void
InsertNNListSorted
(
std
::
vector
<
NNNode
*>*
nodeList
,
NNNode
*
newNode
);
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
,
int
&
a_foundCount
)
{
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
,
a_foundCount
)
)
{
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
;
if
(
!
a_visitor
(
id
)
)
return
false
;
a_foundCount
++
;
}
}
}
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
DATATYPE
RTREE_QUAL
::
NearestNeighbor
(
const
ELEMTYPE
a_point
[
NUMDIMS
]
)
{
return
this
->
NearestNeighbor
(
a_point
,
0
,
0
);
}
RTREE_TEMPLATE
DATATYPE
RTREE_QUAL
::
NearestNeighbor
(
const
ELEMTYPE
a_point
[
NUMDIMS
],
ELEMTYPE
a_squareDistanceCallback
(
const
ELEMTYPE
a_point
[
NUMDIMS
],
DATATYPE
a_data
),
ELEMTYPE
*
a_squareDistance
)
{
typedef
typename
std
::
vector
<
NNNode
*>::
iterator
iterator
;
std
::
vector
<
NNNode
*>
nodeList
;
Node
*
node
=
m_root
;
NNNode
*
closestNode
=
0
;
while
(
!
closestNode
||
!
closestNode
->
isLeaf
)
{
//check every node on this level
for
(
int
index
=
0
;
index
<
node
->
m_count
;
++
index
)
{
NNNode
*
newNode
=
new
NNNode
;
newNode
->
isLeaf
=
node
->
IsLeaf
();
newNode
->
m_branch
=
node
->
m_branch
[
index
];
if
(
newNode
->
isLeaf
&&
a_squareDistanceCallback
)
newNode
->
minDist
=
a_squareDistanceCallback
(
a_point
,
newNode
->
m_branch
.
m_data
);
else
newNode
->
minDist
=
this
->
MinDist
(
a_point
,
&
(
node
->
m_branch
[
index
].
m_rect
)
);
//TODO: a custom list could be more efficient than a vector
this
->
InsertNNListSorted
(
&
nodeList
,
newNode
);
}
if
(
nodeList
.
size
()
==
0
)
{
return
0
;
}
closestNode
=
nodeList
.
back
();
node
=
closestNode
->
m_branch
.
m_child
;
nodeList
.
pop_back
();
free
(
closestNode
);
}
// free memory used for remaining NNNodes in nodeList
for
(
iterator
iter
=
nodeList
.
begin
();
iter
!=
nodeList
.
end
();
++
iter
)
{
NNNode
*
node
=
*
iter
;
free
(
node
);
}
*
a_squareDistance
=
closestNode
->
minDist
;
return
closestNode
->
m_branch
.
m_data
;
}
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
;
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
,
betterGroup
;
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
,
seed1
;
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
}
//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
ELEMTYPE
RTREE_QUAL
::
MinDist
(
const
ELEMTYPE
a_point
[
NUMDIMS
],
Rect
*
a_rect
)
{
ELEMTYPE
*
q
,
*
s
,
*
t
;
q
=
(
ELEMTYPE
*
)
a_point
;
s
=
a_rect
->
m_min
;
t
=
a_rect
->
m_max
;
int
minDist
=
0
;
for
(
int
index
=
0
;
index
<
NUMDIMS
;
index
++
)
{
int
r
=
q
[
index
];
if
(
q
[
index
]
<
s
[
index
]
)
{
r
=
s
[
index
];
}
else
if
(
q
[
index
]
>
t
[
index
]
)
{
r
=
t
[
index
];
}
int
addend
=
q
[
index
]
-
r
;
minDist
+=
addend
*
addend
;
}
return
minDist
;
}
//insert a NNNode in a list sorted by its minDist (desc.)
RTREE_TEMPLATE
void
RTREE_QUAL
::
InsertNNListSorted
(
std
::
vector
<
NNNode
*>*
nodeList
,
NNNode
*
newNode
)
{
typedef
typename
std
::
vector
<
NNNode
*>::
iterator
iterator
;
iterator
iter
=
nodeList
->
begin
();
while
(
iter
!=
nodeList
->
end
()
&&
(
*
iter
)
->
minDist
>
newNode
->
minDist
)
{
++
iter
;
}
nodeList
->
insert
(
iter
,
newNode
);
}
#undef RTREE_TEMPLATE
#undef RTREE_QUAL
#undef RTREE_SEARCH_TEMPLATE
#undef RTREE_SEARCH_QUAL
#endif // RTREE_H
include/geometry/seg.h
View file @
dcb5d8f2
...
...
@@ -267,6 +267,12 @@ class SEG {
{
return
(
a
-
b
).
EuclideanNorm
();
}
ecoord
SquaredLength
()
const
{
return
(
a
-
b
).
SquaredEuclideanNorm
();
}
/**
* Function Index()
...
...
@@ -301,18 +307,7 @@ inline VECTOR2I SEG::LineProject( const VECTOR2I& aP ) const
{
// fixme: numerical errors for large integers
assert
(
false
);
/*const VECTOR2I d = aB - aA;
ecoord det = d.Dot(d);
ecoord dxdy = (ecoord) d.x * d.y;
ecoord qx =
( (extended_type) aA.x * d.y * d.y + (extended_type) d.x * d.x * x - dxdy *
(aA.y - y) ) / det;
extended_type qy =
( (extended_type) aA.y * d.x * d.x + (extended_type) d.y * d.y * y - dxdy *
(aA.x - x) ) / det;
return VECTOR2<T> ( (T) qx, (T) qy );*/
return
VECTOR2I
(
0
,
0
);
}
...
...
include/geometry/shape.h
View file @
dcb5d8f2
...
...
@@ -77,7 +77,10 @@ class SHAPE {
* Returns a dynamically allocated copy of the shape
* @retval copy of the shape
*/
virtual
SHAPE
*
Clone
()
const
{
assert
(
false
);
};
virtual
SHAPE
*
Clone
()
const
{
assert
(
false
);
return
NULL
;
};
/**
* Function Collide()
...
...
include/geometry/shape_index.h
View file @
dcb5d8f2
...
...
@@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Jacobo Aragunde Pérez
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or
...
...
@@ -25,266 +26,328 @@
#ifndef __SHAPE_INDEX_H
#define __SHAPE_INDEX_H
#include <boost/unordered_map.hpp>
#include <vector>
#include <geometry/shape.h>
#include <geometry/rtree.h>
template
<
class
T
>
const
SHAPE
*
defaultShapeFunctor
(
const
T
aItem
)
/**
* shapeFunctor template function
*
* It is used by SHAPE_INDEX to get a SHAPE* from another type.
* By default relies on T::GetShape() method, should be specialized if the T object
* doesn't allow that method.
* @param object generic T object
* @return a SHAPE* object equivalent to object.
*/
template
<
class
T
>
static
const
SHAPE
*
shapeFunctor
(
T
aItem
)
{
return
aItem
->
GetShape
();
}
/**
* shapeFunctor template function: specialization for T = SHAPE*
*/
template
<>
const
SHAPE
*
shapeFunctor
(
SHAPE
*
aItem
)
{
return
aItem
;
}
/**
* boundingBox template method
*
* It is used by SHAPE_INDEX to get the bounding box of a generic T object.
* By default relies on T::BBox() method, should be specialized if the T object
* doesn't allow that method.
* @param object generic T object
* @return a BOX2I object containing the bounding box of the T object.
*/
template
<
class
T
>
BOX2I
boundingBox
(
T
object
)
{
return
shapeFunctor
(
object
)
->
BBox
();
}
/**
* acceptVisitor template method
*
* It is used by SHAPE_INDEX to implement Accept().
* By default relies on V::operation() redefinition, should be specialized if V class
* doesn't have its () operation defined to accept T objects.
* @param object generic T object
* @param visitor V visitor object
*/
template
<
class
T
,
class
V
>
void
acceptVisitor
(
T
object
,
V
visitor
)
{
visitor
(
object
);
}
/**
* collide template method
*
* It is used by SHAPE_INDEX to implement Query().
* By default relies on T::Collide(U) method, should be specialized if the T object
* doesn't allow that method.
* @param object generic T object
* @param anotherObject generic U object
* @param minDistance minimum collision distance
* @return if object and anotherObject collide
*/
template
<
class
T
,
class
U
>
bool
collide
(
T
object
,
U
anotherObject
,
int
minDistance
)
{
return
aItem
->
GetShape
();
return
shapeFunctor
(
object
)
->
Collide
(
anotherObject
,
minDistance
);
}
template
<
class
T
,
class
V
>
bool
queryCallback
(
T
shape
,
void
*
context
)
{
V
*
visitor
=
(
V
*
)
context
;
acceptVisitor
<
T
,
V
>
(
shape
,
*
visitor
);
return
true
;
}
template
<
class
T
,
const
SHAPE
*
(
ShapeFunctor
)(
const
T
)
=
defaultShapeFunctor
<
T
>
>
class
SHAPE_INDEX_LIST
{
struct
ShapeEntry
{
ShapeEntry
(
T
aParent
)
{
shape
=
ShapeFunctor
(
aParent
);
bbox
=
shape
->
BBox
(
0
);
parent
=
aParent
;
}
~
ShapeEntry
()
{
}
T
parent
;
const
SHAPE
*
shape
;
BOX2I
bbox
;
};
typedef
std
::
vector
<
ShapeEntry
>
ShapeVec
;
typedef
typename
std
::
vector
<
ShapeEntry
>::
iterator
ShapeVecIter
;
public
:
// "Normal" iterator interface, for STL algorithms.
class
iterator
{
public
:
iterator
()
{};
iterator
(
ShapeVecIter
aCurrent
)
:
m_current
(
aCurrent
)
{};
iterator
(
const
iterator
&
b
)
:
m_current
(
b
.
m_current
)
{};
T
operator
*
()
const
{
return
(
*
m_current
).
parent
;
}
void
operator
++
()
{
++
m_current
;
}
iterator
&
operator
++
(
int
dummy
)
{
++
m_current
;
return
*
this
;
}
bool
operator
==
(
const
iterator
&
rhs
)
const
{
return
m_current
==
rhs
.
m_current
;
}
bool
operator
!=
(
const
iterator
&
rhs
)
const
{
return
m_current
!=
rhs
.
m_current
;
}
const
iterator
&
operator
=
(
const
iterator
&
rhs
)
{
m_current
=
rhs
.
m_current
;
return
*
this
;
}
private
:
ShapeVecIter
m_current
;
};
// "Query" iterator, for iterating over a set of spatially matching shapes.
class
query_iterator
{
public
:
query_iterator
()
{
}
query_iterator
(
ShapeVecIter
aCurrent
,
ShapeVecIter
aEnd
,
SHAPE
*
aShape
,
int
aMinDistance
,
bool
aExact
)
:
m_end
(
aEnd
),
m_current
(
aCurrent
),
m_shape
(
aShape
),
m_minDistance
(
aMinDistance
),
m_exact
(
aExact
)
{
if
(
aShape
)
{
m_refBBox
=
aShape
->
BBox
();
next
();
}
}
query_iterator
(
const
query_iterator
&
b
)
:
m_end
(
b
.
m_end
),
m_current
(
b
.
m_current
),
m_shape
(
b
.
m_shape
),
m_minDistance
(
b
.
m_minDistance
),
m_exact
(
b
.
m_exact
),
m_refBBox
(
b
.
m_refBBox
)
{
}
T
operator
*
()
const
{
return
(
*
m_current
).
parent
;
}
query_iterator
&
operator
++
()
{
++
m_current
;
next
();
return
*
this
;
}
query_iterator
&
operator
++
(
int
dummy
)
{
++
m_current
;
next
();
return
*
this
;
}
bool
operator
==
(
const
query_iterator
&
rhs
)
const
{
return
m_current
==
rhs
.
m_current
;
}
bool
operator
!=
(
const
query_iterator
&
rhs
)
const
{
return
m_current
!=
rhs
.
m_current
;
}
const
query_iterator
&
operator
=
(
const
query_iterator
&
rhs
)
{
m_end
=
rhs
.
m_end
;
m_current
=
rhs
.
m_current
;
m_shape
=
rhs
.
m_shape
;
m_minDistance
=
rhs
.
m_minDistance
;
m_exact
=
rhs
.
m_exact
;
m_refBBox
=
rhs
.
m_refBBox
;
return
*
this
;
}
private
:
void
next
()
{
while
(
m_current
!=
m_end
)
{
if
(
m_refBBox
.
Distance
(
m_current
->
bbox
)
<=
m_minDistance
)
{
if
(
!
m_exact
||
m_current
->
shape
->
Collide
(
m_shape
,
m_minDistance
))
return
;
}
++
m_current
;
}
}
ShapeVecIter
m_end
;
ShapeVecIter
m_current
;
BOX2I
m_refBBox
;
bool
m_exact
;
SHAPE
*
m_shape
;
int
m_minDistance
;
};
void
Add
(
T
aItem
)
{
ShapeEntry
s
(
aItem
);
m_shapes
.
push_back
(
s
);
}
void
Remove
(
const
T
aItem
)
{
ShapeVecIter
i
;
for
(
i
=
m_shapes
.
begin
();
i
!=
m_shapes
.
end
();
++
i
)
{
if
(
i
->
parent
==
aItem
)
break
;
}
if
(
i
==
m_shapes
.
end
())
return
;
m_shapes
.
erase
(
i
);
}
int
Size
()
const
{
return
m_shapes
.
size
();
}
template
<
class
Visitor
>
int
Query
(
const
SHAPE
*
aShape
,
int
aMinDistance
,
Visitor
&
v
,
bool
aExact
=
true
)
//const
{
ShapeVecIter
i
;
int
n
=
0
;
VECTOR2I
::
extended_type
minDistSq
=
(
VECTOR2I
::
extended_type
)
aMinDistance
*
aMinDistance
;
BOX2I
refBBox
=
aShape
->
BBox
();
for
(
i
=
m_shapes
.
begin
();
i
!=
m_shapes
.
end
();
++
i
)
{
if
(
refBBox
.
SquaredDistance
(
i
->
bbox
)
<=
minDistSq
)
{
if
(
!
aExact
||
i
->
shape
->
Collide
(
aShape
,
aMinDistance
))
{
n
++
;
if
(
!
v
(
i
->
parent
))
return
n
;
}
}
}
return
n
;
}
void
Clear
()
{
m_shapes
.
clear
();
}
query_iterator
qbegin
(
SHAPE
*
aShape
,
int
aMinDistance
,
bool
aExact
)
{
return
query_iterator
(
m_shapes
.
begin
(),
m_shapes
.
end
(),
aShape
,
aMinDistance
,
aExact
);
}
const
query_iterator
qend
()
{
return
query_iterator
(
m_shapes
.
end
(),
m_shapes
.
end
(),
NULL
,
0
,
false
);
}
iterator
begin
()
{
return
iterator
(
m_shapes
.
begin
()
);
}
iterator
end
()
{
return
iterator
(
m_shapes
.
end
()
);
}
private
:
ShapeVec
m_shapes
;
template
<
class
T
=
SHAPE
*>
class
SHAPE_INDEX
{
public
:
SHAPE_INDEX
();
~
SHAPE_INDEX
();
/**
* Function Add()
*
* Adds a SHAPE to the index.
* @param shape the new SHAPE
*/
void
Add
(
T
shape
);
/**
* Function Remove()
*
* Removes a SHAPE to the index.
* @param shape the new SHAPE
*/
void
Remove
(
T
shape
);
/**
* Function RemoveAll()
*
* Removes all the contents of the index.
*/
void
RemoveAll
();
/**
* Function Accept()
*
* Accepts a visitor for every SHAPE object contained in this INDEX.
* @param visitor Visitor object to be run
*/
template
<
class
V
>
void
Accept
(
V
visitor
)
{
SHAPE_INDEX
::
Iterator
iter
=
this
->
Begin
();
while
(
!
iter
.
IsNull
())
{
T
shape
=
*
iter
;
acceptVisitor
(
shape
,
visitor
);
iter
++
;
}
}
/**
* Function Reindex()
*
* Rebuilds the index. This should be used if the geometry of the objects
* contained by the index has changed.
*/
void
Reindex
();
/**
* Function Query()
*
* Runs a callback on every SHAPE object contained in the bounding box of (shape).
* @param shape shape to search against
* @param minDistance distance threshold
* @param visitor object to be invoked on every object contained in the search area.
*/
template
<
class
V
>
int
Query
(
const
SHAPE
*
shape
,
int
minDistance
,
V
&
visitor
,
bool
aExact
)
{
BOX2I
box
=
shape
->
BBox
();
box
.
Inflate
(
minDistance
);
int
min
[
2
]
=
{
box
.
GetX
(),
box
.
GetY
()};
int
max
[
2
]
=
{
box
.
GetRight
(),
box
.
GetBottom
()};
return
this
->
m_tree
->
Search
(
min
,
max
,
visitor
);
}
class
Iterator
{
private
:
typedef
typename
RTree
<
T
,
int
,
2
,
float
>::
Iterator
RTreeIterator
;
RTreeIterator
iterator
;
/**
* Function Init()
*
* Setup the internal tree iterator.
* @param tree pointer to a RTREE object
*/
void
Init
(
RTree
<
T
,
int
,
2
,
float
>*
tree
)
{
tree
->
GetFirst
(
iterator
);
}
public
:
/**
* Iterator constructor
*
* Creates an iterator for the index object
* @param index SHAPE_INDEX object to iterate
*/
Iterator
(
SHAPE_INDEX
*
index
)
{
Init
(
index
->
m_tree
);
}
/**
* Operator * (prefix)
*
* Returns the next data element.
*/
T
operator
*
()
{
return
*
iterator
;
}
/**
* Operator ++ (prefix)
*
* Shifts the iterator to the next element.
*/
bool
operator
++
()
{
return
++
iterator
;
}
/**
* Operator ++ (postfix)
*
* Shifts the iterator to the next element.
*/
bool
operator
++
(
int
)
{
return
++
iterator
;
}
/**
* Function IsNull()
*
* Checks if the iterator has reached the end.
* @return true if it is in an invalid position (data finished)
*/
bool
IsNull
()
{
return
iterator
.
IsNull
();
}
/**
* Function IsNotNull()
*
* Checks if the iterator has not reached the end.
* @return true if it is in an valid position (data not finished)
*/
bool
IsNotNull
()
{
return
iterator
.
IsNotNull
();
}
/**
* Function Next()
*
* Returns the current element of the iterator and moves to the next
* position.
* @return SHAPE object pointed by the iterator before moving to the
* next position.
*/
T
Next
()
{
T
object
=
*
iterator
;
++
iterator
;
return
object
;
}
};
/**
* Function Begin()
*
* Creates an iterator for the current index object
* @return iterator
*/
Iterator
Begin
();
private
:
RTree
<
T
,
int
,
2
,
float
>*
m_tree
;
};
/*
* Class members implementation
*/
template
<
class
T
>
SHAPE_INDEX
<
T
>::
SHAPE_INDEX
()
{
this
->
m_tree
=
new
RTree
<
T
,
int
,
2
,
float
>
();
}
template
<
class
T
>
SHAPE_INDEX
<
T
>::~
SHAPE_INDEX
()
{
delete
this
->
m_tree
;
}
template
<
class
T
>
void
SHAPE_INDEX
<
T
>::
Add
(
T
shape
)
{
BOX2I
box
=
boundingBox
(
shape
);
int
min
[
2
]
=
{
box
.
GetX
(),
box
.
GetY
()};
int
max
[
2
]
=
{
box
.
GetRight
(),
box
.
GetBottom
()};
this
->
m_tree
->
Insert
(
min
,
max
,
shape
);
}
template
<
class
T
>
void
SHAPE_INDEX
<
T
>::
Remove
(
T
shape
)
{
BOX2I
box
=
boundingBox
(
shape
);
int
min
[
2
]
=
{
box
.
GetX
(),
box
.
GetY
()};
int
max
[
2
]
=
{
box
.
GetRight
(),
box
.
GetBottom
()};
this
->
m_tree
->
Remove
(
min
,
max
,
shape
);
}
template
<
class
T
>
void
SHAPE_INDEX
<
T
>::
RemoveAll
()
{
this
->
m_tree
->
RemoveAll
();
}
template
<
class
T
>
void
SHAPE_INDEX
<
T
>::
Reindex
()
{
RTree
<
T
,
int
,
2
,
float
>*
newTree
;
newTree
=
new
RTree
<
T
,
int
,
2
,
float
>
();
SHAPE_INDEX
::
Iterator
iter
=
this
->
Begin
();
while
(
!
iter
.
IsNull
())
{
T
shape
=
*
iter
;
BOX2I
box
=
boundingBox
(
shape
);
int
min
[
2
]
=
{
box
.
GetX
(),
box
.
GetY
()};
int
max
[
2
]
=
{
box
.
GetRight
(),
box
.
GetBottom
()};
newTree
->
Insert
(
min
,
max
,
shape
);
iter
++
;
}
delete
this
->
m_tree
;
this
->
m_tree
=
newTree
;
}
template
<
class
T
>
typename
SHAPE_INDEX
<
T
>::
Iterator
SHAPE_INDEX
<
T
>::
Begin
()
{
return
Iterator
(
this
);
}
#endif
include/geometry/shape_index_list.h
0 → 100644
View file @
dcb5d8f2
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __SHAPE_INDEX_LIST_H
#define __SHAPE_INDEX_LIST_H
#include <boost/unordered_map.hpp>
template
<
class
T
>
const
SHAPE
*
defaultShapeFunctor
(
const
T
aItem
)
{
return
aItem
->
GetShape
();
}
template
<
class
T
,
const
SHAPE
*
(
ShapeFunctor
)(
const
T
)
=
defaultShapeFunctor
<
T
>
>
class
SHAPE_INDEX_LIST
{
struct
ShapeEntry
{
ShapeEntry
(
T
aParent
)
{
shape
=
ShapeFunctor
(
aParent
);
bbox
=
shape
->
BBox
(
0
);
parent
=
aParent
;
}
~
ShapeEntry
()
{
}
T
parent
;
const
SHAPE
*
shape
;
BOX2I
bbox
;
};
typedef
std
::
vector
<
ShapeEntry
>
ShapeVec
;
typedef
typename
std
::
vector
<
ShapeEntry
>::
iterator
ShapeVecIter
;
public
:
// "Normal" iterator interface, for STL algorithms.
class
iterator
{
public
:
iterator
()
{};
iterator
(
ShapeVecIter
aCurrent
)
:
m_current
(
aCurrent
)
{};
iterator
(
const
iterator
&
b
)
:
m_current
(
b
.
m_current
)
{};
T
operator
*
()
const
{
return
(
*
m_current
).
parent
;
}
void
operator
++
()
{
++
m_current
;
}
iterator
&
operator
++
(
int
dummy
)
{
++
m_current
;
return
*
this
;
}
bool
operator
==
(
const
iterator
&
rhs
)
const
{
return
m_current
==
rhs
.
m_current
;
}
bool
operator
!=
(
const
iterator
&
rhs
)
const
{
return
m_current
!=
rhs
.
m_current
;
}
const
iterator
&
operator
=
(
const
iterator
&
rhs
)
{
m_current
=
rhs
.
m_current
;
return
*
this
;
}
private
:
ShapeVecIter
m_current
;
};
// "Query" iterator, for iterating over a set of spatially matching shapes.
class
query_iterator
{
public
:
query_iterator
()
{
}
query_iterator
(
ShapeVecIter
aCurrent
,
ShapeVecIter
aEnd
,
SHAPE
*
aShape
,
int
aMinDistance
,
bool
aExact
)
:
m_end
(
aEnd
),
m_current
(
aCurrent
),
m_shape
(
aShape
),
m_minDistance
(
aMinDistance
),
m_exact
(
aExact
)
{
if
(
aShape
)
{
m_refBBox
=
aShape
->
BBox
();
next
();
}
}
query_iterator
(
const
query_iterator
&
b
)
:
m_end
(
b
.
m_end
),
m_current
(
b
.
m_current
),
m_shape
(
b
.
m_shape
),
m_minDistance
(
b
.
m_minDistance
),
m_exact
(
b
.
m_exact
),
m_refBBox
(
b
.
m_refBBox
)
{
}
T
operator
*
()
const
{
return
(
*
m_current
).
parent
;
}
query_iterator
&
operator
++
()
{
++
m_current
;
next
();
return
*
this
;
}
query_iterator
&
operator
++
(
int
dummy
)
{
++
m_current
;
next
();
return
*
this
;
}
bool
operator
==
(
const
query_iterator
&
rhs
)
const
{
return
m_current
==
rhs
.
m_current
;
}
bool
operator
!=
(
const
query_iterator
&
rhs
)
const
{
return
m_current
!=
rhs
.
m_current
;
}
const
query_iterator
&
operator
=
(
const
query_iterator
&
rhs
)
{
m_end
=
rhs
.
m_end
;
m_current
=
rhs
.
m_current
;
m_shape
=
rhs
.
m_shape
;
m_minDistance
=
rhs
.
m_minDistance
;
m_exact
=
rhs
.
m_exact
;
m_refBBox
=
rhs
.
m_refBBox
;
return
*
this
;
}
private
:
void
next
()
{
while
(
m_current
!=
m_end
)
{
if
(
m_refBBox
.
Distance
(
m_current
->
bbox
)
<=
m_minDistance
)
{
if
(
!
m_exact
||
m_current
->
shape
->
Collide
(
m_shape
,
m_minDistance
))
return
;
}
++
m_current
;
}
}
ShapeVecIter
m_end
;
ShapeVecIter
m_current
;
BOX2I
m_refBBox
;
bool
m_exact
;
SHAPE
*
m_shape
;
int
m_minDistance
;
};
void
Add
(
T
aItem
)
{
ShapeEntry
s
(
aItem
);
m_shapes
.
push_back
(
s
);
}
void
Remove
(
const
T
aItem
)
{
ShapeVecIter
i
;
for
(
i
=
m_shapes
.
begin
();
i
!=
m_shapes
.
end
();
++
i
)
{
if
(
i
->
parent
==
aItem
)
break
;
}
if
(
i
==
m_shapes
.
end
())
return
;
m_shapes
.
erase
(
i
);
}
int
Size
()
const
{
return
m_shapes
.
size
();
}
template
<
class
Visitor
>
int
Query
(
const
SHAPE
*
aShape
,
int
aMinDistance
,
Visitor
&
v
,
bool
aExact
=
true
)
//const
{
ShapeVecIter
i
;
int
n
=
0
;
VECTOR2I
::
extended_type
minDistSq
=
(
VECTOR2I
::
extended_type
)
aMinDistance
*
aMinDistance
;
BOX2I
refBBox
=
aShape
->
BBox
();
for
(
i
=
m_shapes
.
begin
();
i
!=
m_shapes
.
end
();
++
i
)
{
if
(
refBBox
.
SquaredDistance
(
i
->
bbox
)
<=
minDistSq
)
{
if
(
!
aExact
||
i
->
shape
->
Collide
(
aShape
,
aMinDistance
))
{
n
++
;
if
(
!
v
(
i
->
parent
))
return
n
;
}
}
}
return
n
;
}
void
Clear
()
{
m_shapes
.
clear
();
}
query_iterator
qbegin
(
SHAPE
*
aShape
,
int
aMinDistance
,
bool
aExact
)
{
return
query_iterator
(
m_shapes
.
begin
(),
m_shapes
.
end
(),
aShape
,
aMinDistance
,
aExact
);
}
const
query_iterator
qend
()
{
return
query_iterator
(
m_shapes
.
end
(),
m_shapes
.
end
(),
NULL
,
0
,
false
);
}
iterator
begin
()
{
return
iterator
(
m_shapes
.
begin
()
);
}
iterator
end
()
{
return
iterator
(
m_shapes
.
end
()
);
}
private
:
ShapeVec
m_shapes
;
};
#endif
include/view/view.h
View file @
dcb5d8f2
...
...
@@ -362,6 +362,8 @@ public:
*/
void
EnableTopLayer
(
bool
aEnable
);
int
GetTopLayer
()
const
;
/**
* Function ClearTopLayers()
* Removes all layers from the on-the-top set (they are no longer displayed over the rest of
...
...
include/view/view_controls.h
View file @
dcb5d8f2
...
...
@@ -46,9 +46,9 @@ class VIEW;
class
VIEW_CONTROLS
{
public
:
VIEW_CONTROLS
(
VIEW
*
aView
)
:
m_view
(
aView
),
m_
snappingEnabled
(
false
),
m_
grabMouse
(
false
),
m_autoPanEnabled
(
false
),
m_autoPanMargin
(
0
.
1
),
m_autoPanSpeed
(
0
.
15
)
{};
VIEW_CONTROLS
(
VIEW
*
aView
)
:
m_view
(
aView
),
m_
forceCursorPosition
(
false
),
m_
snappingEnabled
(
false
),
m_grabMouse
(
false
),
m_autoPanEnabled
(
false
),
m_autoPan
Margin
(
0
.
1
),
m_autoPan
Speed
(
0
.
15
)
{};
virtual
~
VIEW_CONTROLS
()
{};
/**
...
...
@@ -121,6 +121,21 @@ public:
*/
virtual
const
VECTOR2D
GetCursorPosition
()
const
=
0
;
/**
* Function ForceCursorPosition()
* Places the cursor immediately at a given point. Mouse movement is ignored.
* @param aEnabled enable forced cursor position
* @param aPosition the position
*/
virtual
void
ForceCursorPosition
(
bool
aEnabled
,
const
VECTOR2D
&
aPosition
=
VECTOR2D
(
0
,
0
)
)
{
m_forcedPosition
=
aPosition
;
m_forceCursorPosition
=
aEnabled
;
}
virtual
void
ShowCursor
(
bool
aEnabled
);
protected
:
/// Pointer to controlled VIEW.
VIEW
*
m_view
;
...
...
@@ -128,6 +143,15 @@ protected:
/// Current mouse position
VECTOR2D
m_mousePosition
;
/// Current cursor position
VECTOR2D
m_cursorPosition
;
/// Forced cursor position
VECTOR2D
m_forcedPosition
;
/// Is the forced cursor position enabled
bool
m_forceCursorPosition
;
/// Should the cursor snap to grid or move freely
bool
m_snappingEnabled
;
...
...
include/view/view_group.h
View file @
dcb5d8f2
...
...
@@ -142,7 +142,7 @@ public:
*
* @return Pointer to the VIEW instance.
*/
KiGfx
::
VIEW
*
GetView
()
const
KiGfx
::
VIEW
*
GetView
()
const
{
return
m_view
;
}
...
...
include/wxPcbStruct.h
View file @
dcb5d8f2
...
...
@@ -1690,6 +1690,11 @@ public:
*/
void
UpdateTitle
();
void
SetTopLayer
(
LAYER_NUM
aLayer
)
{
setTopLayer
(
aLayer
);
}
DECLARE_EVENT_TABLE
()
};
...
...
pcbnew/pcb_painter.cpp
View file @
dcb5d8f2
...
...
@@ -206,8 +206,6 @@ PCB_PAINTER::PCB_PAINTER( GAL* aGal ) :
bool
PCB_PAINTER
::
Draw
(
const
VIEW_ITEM
*
aItem
,
int
aLayer
)
{
const
BOARD_ITEM
*
item
=
static_cast
<
const
BOARD_ITEM
*>
(
aItem
);
// the "cast" applied in here clarifies which overloaded draw() is called
switch
(
item
->
Type
()
)
{
...
...
@@ -284,9 +282,9 @@ void PCB_PAINTER::draw( const TRACK* aTrack, int aLayer )
return
;
NETINFO_ITEM
*
net
=
(
(
BOARD
*
)
aTrack
->
GetParent
()
)
->
FindNet
(
netNumber
);
if
(
net
==
NULL
)
if
(
!
net
)
return
;
std
::
string
netName
=
std
::
string
(
net
->
GetShortNetname
().
mb_str
()
);
VECTOR2D
textPosition
=
start
+
line
/
2.0
;
// center of the track
double
textOrientation
=
-
atan
(
line
.
y
/
line
.
x
);
...
...
polygon/CMakeLists.txt
View file @
dcb5d8f2
...
...
@@ -9,6 +9,12 @@ set(POLYGON_SRCS
PolyLine.cpp
polygon_test_point_inside.cpp
clipper.cpp
)
poly2tri/common/shapes.cc
poly2tri/sweep/sweep.cc
poly2tri/sweep/cdt.cc
poly2tri/sweep/advancing_front.cc
poly2tri/sweep/sweep_context.cc
)
add_library
(
polygon STATIC
${
POLYGON_SRCS
}
)
polygon/poly2tri/common/shapes.cc
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "shapes.h"
#include <iostream>
namespace
p2t
{
Triangle
::
Triangle
(
Point
&
a
,
Point
&
b
,
Point
&
c
)
{
points_
[
0
]
=
&
a
;
points_
[
1
]
=
&
b
;
points_
[
2
]
=
&
c
;
neighbors_
[
0
]
=
NULL
;
neighbors_
[
1
]
=
NULL
;
neighbors_
[
2
]
=
NULL
;
constrained_edge
[
0
]
=
constrained_edge
[
1
]
=
constrained_edge
[
2
]
=
false
;
delaunay_edge
[
0
]
=
delaunay_edge
[
1
]
=
delaunay_edge
[
2
]
=
false
;
interior_
=
false
;
}
// Update neighbor pointers
void
Triangle
::
MarkNeighbor
(
Point
*
p1
,
Point
*
p2
,
Triangle
*
t
)
{
if
((
p1
==
points_
[
2
]
&&
p2
==
points_
[
1
])
||
(
p1
==
points_
[
1
]
&&
p2
==
points_
[
2
]))
neighbors_
[
0
]
=
t
;
else
if
((
p1
==
points_
[
0
]
&&
p2
==
points_
[
2
])
||
(
p1
==
points_
[
2
]
&&
p2
==
points_
[
0
]))
neighbors_
[
1
]
=
t
;
else
if
((
p1
==
points_
[
0
]
&&
p2
==
points_
[
1
])
||
(
p1
==
points_
[
1
]
&&
p2
==
points_
[
0
]))
neighbors_
[
2
]
=
t
;
else
assert
(
0
);
}
// Exhaustive search to update neighbor pointers
void
Triangle
::
MarkNeighbor
(
Triangle
&
t
)
{
if
(
t
.
Contains
(
points_
[
1
],
points_
[
2
]))
{
neighbors_
[
0
]
=
&
t
;
t
.
MarkNeighbor
(
points_
[
1
],
points_
[
2
],
this
);
}
else
if
(
t
.
Contains
(
points_
[
0
],
points_
[
2
]))
{
neighbors_
[
1
]
=
&
t
;
t
.
MarkNeighbor
(
points_
[
0
],
points_
[
2
],
this
);
}
else
if
(
t
.
Contains
(
points_
[
0
],
points_
[
1
]))
{
neighbors_
[
2
]
=
&
t
;
t
.
MarkNeighbor
(
points_
[
0
],
points_
[
1
],
this
);
}
}
/**
* Clears all references to all other triangles and points
*/
void
Triangle
::
Clear
()
{
Triangle
*
t
;
for
(
int
i
=
0
;
i
<
3
;
i
++
)
{
t
=
neighbors_
[
i
];
if
(
t
!=
NULL
)
{
t
->
ClearNeighbor
(
this
);
}
}
ClearNeighbors
();
points_
[
0
]
=
points_
[
1
]
=
points_
[
2
]
=
NULL
;
}
void
Triangle
::
ClearNeighbor
(
Triangle
*
triangle
)
{
if
(
neighbors_
[
0
]
==
triangle
)
{
neighbors_
[
0
]
=
NULL
;
}
else
if
(
neighbors_
[
1
]
==
triangle
)
{
neighbors_
[
1
]
=
NULL
;
}
else
{
neighbors_
[
2
]
=
NULL
;
}
}
void
Triangle
::
ClearNeighbors
()
{
neighbors_
[
0
]
=
NULL
;
neighbors_
[
1
]
=
NULL
;
neighbors_
[
2
]
=
NULL
;
}
void
Triangle
::
ClearDelunayEdges
()
{
delaunay_edge
[
0
]
=
delaunay_edge
[
1
]
=
delaunay_edge
[
2
]
=
false
;
}
Point
*
Triangle
::
OppositePoint
(
Triangle
&
t
,
Point
&
p
)
{
Point
*
cw
=
t
.
PointCW
(
p
);
double
x
=
cw
->
x
;
double
y
=
cw
->
y
;
x
=
p
.
x
;
y
=
p
.
y
;
return
PointCW
(
*
cw
);
}
// Legalized triangle by rotating clockwise around point(0)
void
Triangle
::
Legalize
(
Point
&
point
)
{
points_
[
1
]
=
points_
[
0
];
points_
[
0
]
=
points_
[
2
];
points_
[
2
]
=
&
point
;
}
// Legalize triagnle by rotating clockwise around oPoint
void
Triangle
::
Legalize
(
Point
&
opoint
,
Point
&
npoint
)
{
if
(
&
opoint
==
points_
[
0
])
{
points_
[
1
]
=
points_
[
0
];
points_
[
0
]
=
points_
[
2
];
points_
[
2
]
=
&
npoint
;
}
else
if
(
&
opoint
==
points_
[
1
])
{
points_
[
2
]
=
points_
[
1
];
points_
[
1
]
=
points_
[
0
];
points_
[
0
]
=
&
npoint
;
}
else
if
(
&
opoint
==
points_
[
2
])
{
points_
[
0
]
=
points_
[
2
];
points_
[
2
]
=
points_
[
1
];
points_
[
1
]
=
&
npoint
;
}
else
{
assert
(
0
);
}
}
int
Triangle
::
Index
(
const
Point
*
p
)
{
if
(
p
==
points_
[
0
])
{
return
0
;
}
else
if
(
p
==
points_
[
1
])
{
return
1
;
}
else
if
(
p
==
points_
[
2
])
{
return
2
;
}
assert
(
0
);
}
int
Triangle
::
EdgeIndex
(
const
Point
*
p1
,
const
Point
*
p2
)
{
if
(
points_
[
0
]
==
p1
)
{
if
(
points_
[
1
]
==
p2
)
{
return
2
;
}
else
if
(
points_
[
2
]
==
p2
)
{
return
1
;
}
}
else
if
(
points_
[
1
]
==
p1
)
{
if
(
points_
[
2
]
==
p2
)
{
return
0
;
}
else
if
(
points_
[
0
]
==
p2
)
{
return
2
;
}
}
else
if
(
points_
[
2
]
==
p1
)
{
if
(
points_
[
0
]
==
p2
)
{
return
1
;
}
else
if
(
points_
[
1
]
==
p2
)
{
return
0
;
}
}
return
-
1
;
}
void
Triangle
::
MarkConstrainedEdge
(
const
int
index
)
{
constrained_edge
[
index
]
=
true
;
}
void
Triangle
::
MarkConstrainedEdge
(
Edge
&
edge
)
{
MarkConstrainedEdge
(
edge
.
p
,
edge
.
q
);
}
// Mark edge as constrained
void
Triangle
::
MarkConstrainedEdge
(
Point
*
p
,
Point
*
q
)
{
if
((
q
==
points_
[
0
]
&&
p
==
points_
[
1
])
||
(
q
==
points_
[
1
]
&&
p
==
points_
[
0
]))
{
constrained_edge
[
2
]
=
true
;
}
else
if
((
q
==
points_
[
0
]
&&
p
==
points_
[
2
])
||
(
q
==
points_
[
2
]
&&
p
==
points_
[
0
]))
{
constrained_edge
[
1
]
=
true
;
}
else
if
((
q
==
points_
[
1
]
&&
p
==
points_
[
2
])
||
(
q
==
points_
[
2
]
&&
p
==
points_
[
1
]))
{
constrained_edge
[
0
]
=
true
;
}
}
// The point counter-clockwise to given point
Point
*
Triangle
::
PointCW
(
Point
&
point
)
{
if
(
&
point
==
points_
[
0
])
{
return
points_
[
2
];
}
else
if
(
&
point
==
points_
[
1
])
{
return
points_
[
0
];
}
else
if
(
&
point
==
points_
[
2
])
{
return
points_
[
1
];
}
assert
(
0
);
}
// The point counter-clockwise to given point
Point
*
Triangle
::
PointCCW
(
Point
&
point
)
{
if
(
&
point
==
points_
[
0
])
{
return
points_
[
1
];
}
else
if
(
&
point
==
points_
[
1
])
{
return
points_
[
2
];
}
else
if
(
&
point
==
points_
[
2
])
{
return
points_
[
0
];
}
assert
(
0
);
}
// The neighbor clockwise to given point
Triangle
*
Triangle
::
NeighborCW
(
Point
&
point
)
{
if
(
&
point
==
points_
[
0
])
{
return
neighbors_
[
1
];
}
else
if
(
&
point
==
points_
[
1
])
{
return
neighbors_
[
2
];
}
return
neighbors_
[
0
];
}
// The neighbor counter-clockwise to given point
Triangle
*
Triangle
::
NeighborCCW
(
Point
&
point
)
{
if
(
&
point
==
points_
[
0
])
{
return
neighbors_
[
2
];
}
else
if
(
&
point
==
points_
[
1
])
{
return
neighbors_
[
0
];
}
return
neighbors_
[
1
];
}
bool
Triangle
::
GetConstrainedEdgeCCW
(
Point
&
p
)
{
if
(
&
p
==
points_
[
0
])
{
return
constrained_edge
[
2
];
}
else
if
(
&
p
==
points_
[
1
])
{
return
constrained_edge
[
0
];
}
return
constrained_edge
[
1
];
}
bool
Triangle
::
GetConstrainedEdgeCW
(
Point
&
p
)
{
if
(
&
p
==
points_
[
0
])
{
return
constrained_edge
[
1
];
}
else
if
(
&
p
==
points_
[
1
])
{
return
constrained_edge
[
2
];
}
return
constrained_edge
[
0
];
}
void
Triangle
::
SetConstrainedEdgeCCW
(
Point
&
p
,
bool
ce
)
{
if
(
&
p
==
points_
[
0
])
{
constrained_edge
[
2
]
=
ce
;
}
else
if
(
&
p
==
points_
[
1
])
{
constrained_edge
[
0
]
=
ce
;
}
else
{
constrained_edge
[
1
]
=
ce
;
}
}
void
Triangle
::
SetConstrainedEdgeCW
(
Point
&
p
,
bool
ce
)
{
if
(
&
p
==
points_
[
0
])
{
constrained_edge
[
1
]
=
ce
;
}
else
if
(
&
p
==
points_
[
1
])
{
constrained_edge
[
2
]
=
ce
;
}
else
{
constrained_edge
[
0
]
=
ce
;
}
}
bool
Triangle
::
GetDelunayEdgeCCW
(
Point
&
p
)
{
if
(
&
p
==
points_
[
0
])
{
return
delaunay_edge
[
2
];
}
else
if
(
&
p
==
points_
[
1
])
{
return
delaunay_edge
[
0
];
}
return
delaunay_edge
[
1
];
}
bool
Triangle
::
GetDelunayEdgeCW
(
Point
&
p
)
{
if
(
&
p
==
points_
[
0
])
{
return
delaunay_edge
[
1
];
}
else
if
(
&
p
==
points_
[
1
])
{
return
delaunay_edge
[
2
];
}
return
delaunay_edge
[
0
];
}
void
Triangle
::
SetDelunayEdgeCCW
(
Point
&
p
,
bool
e
)
{
if
(
&
p
==
points_
[
0
])
{
delaunay_edge
[
2
]
=
e
;
}
else
if
(
&
p
==
points_
[
1
])
{
delaunay_edge
[
0
]
=
e
;
}
else
{
delaunay_edge
[
1
]
=
e
;
}
}
void
Triangle
::
SetDelunayEdgeCW
(
Point
&
p
,
bool
e
)
{
if
(
&
p
==
points_
[
0
])
{
delaunay_edge
[
1
]
=
e
;
}
else
if
(
&
p
==
points_
[
1
])
{
delaunay_edge
[
2
]
=
e
;
}
else
{
delaunay_edge
[
0
]
=
e
;
}
}
// The neighbor across to given point
Triangle
&
Triangle
::
NeighborAcross
(
Point
&
opoint
)
{
if
(
&
opoint
==
points_
[
0
])
{
return
*
neighbors_
[
0
];
}
else
if
(
&
opoint
==
points_
[
1
])
{
return
*
neighbors_
[
1
];
}
return
*
neighbors_
[
2
];
}
void
Triangle
::
DebugPrint
()
{
using
namespace
std
;
cout
<<
points_
[
0
]
->
x
<<
","
<<
points_
[
0
]
->
y
<<
" "
;
cout
<<
points_
[
1
]
->
x
<<
","
<<
points_
[
1
]
->
y
<<
" "
;
cout
<<
points_
[
2
]
->
x
<<
","
<<
points_
[
2
]
->
y
<<
endl
;
}
}
polygon/poly2tri/common/shapes.h
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Include guard
#ifndef SHAPES_H
#define SHAPES_H
#include <vector>
#include <cstddef>
#include <assert.h>
#include <cmath>
namespace
p2t
{
struct
Edge
;
struct
Point
{
double
x
,
y
;
/// Default constructor does nothing (for performance).
Point
()
{
x
=
0
.
0
;
y
=
0
.
0
;
}
/// The edges this point constitutes an upper ending point
std
::
vector
<
Edge
*>
edge_list
;
/// Construct using coordinates.
Point
(
double
x
,
double
y
)
:
x
(
x
),
y
(
y
)
{}
/// Set this point to all zeros.
void
set_zero
()
{
x
=
0
.
0
;
y
=
0
.
0
;
}
/// Set this point to some specified coordinates.
void
set
(
double
x_
,
double
y_
)
{
x
=
x_
;
y
=
y_
;
}
/// Negate this point.
Point
operator
-
()
const
{
Point
v
;
v
.
set
(
-
x
,
-
y
);
return
v
;
}
/// Add a point to this point.
void
operator
+=
(
const
Point
&
v
)
{
x
+=
v
.
x
;
y
+=
v
.
y
;
}
/// Subtract a point from this point.
void
operator
-=
(
const
Point
&
v
)
{
x
-=
v
.
x
;
y
-=
v
.
y
;
}
/// Multiply this point by a scalar.
void
operator
*=
(
double
a
)
{
x
*=
a
;
y
*=
a
;
}
/// Get the length of this point (the norm).
double
Length
()
const
{
return
sqrt
(
x
*
x
+
y
*
y
);
}
/// Convert this point into a unit point. Returns the Length.
double
Normalize
()
{
double
len
=
Length
();
x
/=
len
;
y
/=
len
;
return
len
;
}
};
// Represents a simple polygon's edge
struct
Edge
{
Point
*
p
,
*
q
;
/// Constructor
Edge
(
Point
&
p1
,
Point
&
p2
)
:
p
(
&
p1
),
q
(
&
p2
)
{
if
(
p1
.
y
>
p2
.
y
)
{
q
=
&
p1
;
p
=
&
p2
;
}
else
if
(
p1
.
y
==
p2
.
y
)
{
if
(
p1
.
x
>
p2
.
x
)
{
q
=
&
p1
;
p
=
&
p2
;
}
else
if
(
p1
.
x
==
p2
.
x
)
{
// Repeat points
assert
(
false
);
}
}
q
->
edge_list
.
push_back
(
this
);
}
};
// Triangle-based data structures are know to have better performance than quad-edge structures
// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
// "Triangulations in CGAL"
class
Triangle
{
public
:
/// Constructor
Triangle
(
Point
&
a
,
Point
&
b
,
Point
&
c
);
/// Flags to determine if an edge is a Constrained edge
bool
constrained_edge
[
3
];
/// Flags to determine if an edge is a Delauney edge
bool
delaunay_edge
[
3
];
Point
*
GetPoint
(
const
int
&
index
);
Point
*
PointCW
(
Point
&
point
);
Point
*
PointCCW
(
Point
&
point
);
Point
*
OppositePoint
(
Triangle
&
t
,
Point
&
p
);
Triangle
*
GetNeighbor
(
const
int
&
index
);
void
MarkNeighbor
(
Point
*
p1
,
Point
*
p2
,
Triangle
*
t
);
void
MarkNeighbor
(
Triangle
&
t
);
void
MarkConstrainedEdge
(
const
int
index
);
void
MarkConstrainedEdge
(
Edge
&
edge
);
void
MarkConstrainedEdge
(
Point
*
p
,
Point
*
q
);
int
Index
(
const
Point
*
p
);
int
EdgeIndex
(
const
Point
*
p1
,
const
Point
*
p2
);
Triangle
*
NeighborCW
(
Point
&
point
);
Triangle
*
NeighborCCW
(
Point
&
point
);
bool
GetConstrainedEdgeCCW
(
Point
&
p
);
bool
GetConstrainedEdgeCW
(
Point
&
p
);
void
SetConstrainedEdgeCCW
(
Point
&
p
,
bool
ce
);
void
SetConstrainedEdgeCW
(
Point
&
p
,
bool
ce
);
bool
GetDelunayEdgeCCW
(
Point
&
p
);
bool
GetDelunayEdgeCW
(
Point
&
p
);
void
SetDelunayEdgeCCW
(
Point
&
p
,
bool
e
);
void
SetDelunayEdgeCW
(
Point
&
p
,
bool
e
);
bool
Contains
(
Point
*
p
);
bool
Contains
(
const
Edge
&
e
);
bool
Contains
(
Point
*
p
,
Point
*
q
);
void
Legalize
(
Point
&
point
);
void
Legalize
(
Point
&
opoint
,
Point
&
npoint
);
/**
* Clears all references to all other triangles and points
*/
void
Clear
();
void
ClearNeighbor
(
Triangle
*
triangle
);
void
ClearNeighbors
();
void
ClearDelunayEdges
();
inline
bool
IsInterior
();
inline
void
IsInterior
(
bool
b
);
Triangle
&
NeighborAcross
(
Point
&
opoint
);
void
DebugPrint
();
private
:
/// Triangle points
Point
*
points_
[
3
];
/// Neighbor list
Triangle
*
neighbors_
[
3
];
/// Has this triangle been marked as an interior triangle?
bool
interior_
;
};
inline
bool
cmp
(
const
Point
*
a
,
const
Point
*
b
)
{
if
(
a
->
y
<
b
->
y
)
{
return
true
;
}
else
if
(
a
->
y
==
b
->
y
)
{
// Make sure q is point with greater x value
if
(
a
->
x
<
b
->
x
)
{
return
true
;
}
}
return
false
;
}
/// Add two points_ component-wise.
inline
Point
operator
+
(
const
Point
&
a
,
const
Point
&
b
)
{
return
Point
(
a
.
x
+
b
.
x
,
a
.
y
+
b
.
y
);
}
/// Subtract two points_ component-wise.
inline
Point
operator
-
(
const
Point
&
a
,
const
Point
&
b
)
{
return
Point
(
a
.
x
-
b
.
x
,
a
.
y
-
b
.
y
);
}
/// Multiply point by scalar
inline
Point
operator
*
(
double
s
,
const
Point
&
a
)
{
return
Point
(
s
*
a
.
x
,
s
*
a
.
y
);
}
inline
bool
operator
==
(
const
Point
&
a
,
const
Point
&
b
)
{
return
a
.
x
==
b
.
x
&&
a
.
y
==
b
.
y
;
}
inline
bool
operator
!=
(
const
Point
&
a
,
const
Point
&
b
)
{
return
!
(
a
.
x
==
b
.
x
)
&&
!
(
a
.
y
==
b
.
y
);
}
/// Peform the dot product on two vectors.
inline
double
Dot
(
const
Point
&
a
,
const
Point
&
b
)
{
return
a
.
x
*
b
.
x
+
a
.
y
*
b
.
y
;
}
/// Perform the cross product on two vectors. In 2D this produces a scalar.
inline
double
Cross
(
const
Point
&
a
,
const
Point
&
b
)
{
return
a
.
x
*
b
.
y
-
a
.
y
*
b
.
x
;
}
/// Perform the cross product on a point and a scalar. In 2D this produces
/// a point.
inline
Point
Cross
(
const
Point
&
a
,
double
s
)
{
return
Point
(
s
*
a
.
y
,
-
s
*
a
.
x
);
}
/// Perform the cross product on a scalar and a point. In 2D this produces
/// a point.
inline
Point
Cross
(
const
double
s
,
const
Point
&
a
)
{
return
Point
(
-
s
*
a
.
y
,
s
*
a
.
x
);
}
inline
Point
*
Triangle
::
GetPoint
(
const
int
&
index
)
{
return
points_
[
index
];
}
inline
Triangle
*
Triangle
::
GetNeighbor
(
const
int
&
index
)
{
return
neighbors_
[
index
];
}
inline
bool
Triangle
::
Contains
(
Point
*
p
)
{
return
p
==
points_
[
0
]
||
p
==
points_
[
1
]
||
p
==
points_
[
2
];
}
inline
bool
Triangle
::
Contains
(
const
Edge
&
e
)
{
return
Contains
(
e
.
p
)
&&
Contains
(
e
.
q
);
}
inline
bool
Triangle
::
Contains
(
Point
*
p
,
Point
*
q
)
{
return
Contains
(
p
)
&&
Contains
(
q
);
}
inline
bool
Triangle
::
IsInterior
()
{
return
interior_
;
}
inline
void
Triangle
::
IsInterior
(
bool
b
)
{
interior_
=
b
;
}
}
#endif
polygon/poly2tri/common/utils.h
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UTILS_H
#define UTILS_H
// Otherwise #defines like M_PI are undeclared under Visual Studio
#define _USE_MATH_DEFINES
#include <exception>
#include <math.h>
namespace
p2t
{
const
double
PI_3div4
=
3
*
M_PI
/
4
;
const
double
PI_div2
=
1
.
57079632679489661923
;
const
double
EPSILON
=
1e-12
;
enum
Orientation
{
CW
,
CCW
,
COLLINEAR
};
/**
* Forumla to calculate signed area<br>
* Positive if CCW<br>
* Negative if CW<br>
* 0 if collinear<br>
* <pre>
* A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
* = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
* </pre>
*/
Orientation
Orient2d
(
Point
&
pa
,
Point
&
pb
,
Point
&
pc
)
{
double
detleft
=
(
pa
.
x
-
pc
.
x
)
*
(
pb
.
y
-
pc
.
y
);
double
detright
=
(
pa
.
y
-
pc
.
y
)
*
(
pb
.
x
-
pc
.
x
);
double
val
=
detleft
-
detright
;
if
(
val
>
-
EPSILON
&&
val
<
EPSILON
)
{
return
COLLINEAR
;
}
else
if
(
val
>
0
)
{
return
CCW
;
}
return
CW
;
}
/*
bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
{
double pdx = pd.x;
double pdy = pd.y;
double adx = pa.x - pdx;
double ady = pa.y - pdy;
double bdx = pb.x - pdx;
double bdy = pb.y - pdy;
double adxbdy = adx * bdy;
double bdxady = bdx * ady;
double oabd = adxbdy - bdxady;
if (oabd <= EPSILON) {
return false;
}
double cdx = pc.x - pdx;
double cdy = pc.y - pdy;
double cdxady = cdx * ady;
double adxcdy = adx * cdy;
double ocad = cdxady - adxcdy;
if (ocad <= EPSILON) {
return false;
}
return true;
}
*/
bool
InScanArea
(
Point
&
pa
,
Point
&
pb
,
Point
&
pc
,
Point
&
pd
)
{
double
oadb
=
(
pa
.
x
-
pb
.
x
)
*
(
pd
.
y
-
pb
.
y
)
-
(
pd
.
x
-
pb
.
x
)
*
(
pa
.
y
-
pb
.
y
);
if
(
oadb
>=
-
EPSILON
)
{
return
false
;
}
double
oadc
=
(
pa
.
x
-
pc
.
x
)
*
(
pd
.
y
-
pc
.
y
)
-
(
pd
.
x
-
pc
.
x
)
*
(
pa
.
y
-
pc
.
y
);
if
(
oadc
<=
EPSILON
)
{
return
false
;
}
return
true
;
}
}
#endif
polygon/poly2tri/poly2tri.h
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef POLY2TRI_H
#define POLY2TRI_H
#include "common/shapes.h"
#include "sweep/cdt.h"
#endif
polygon/poly2tri/sweep/advancing_front.cc
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "advancing_front.h"
namespace
p2t
{
AdvancingFront
::
AdvancingFront
(
Node
&
head
,
Node
&
tail
)
{
head_
=
&
head
;
tail_
=
&
tail
;
search_node_
=
&
head
;
}
Node
*
AdvancingFront
::
LocateNode
(
const
double
&
x
)
{
Node
*
node
=
search_node_
;
if
(
x
<
node
->
value
)
{
while
((
node
=
node
->
prev
)
!=
NULL
)
{
if
(
x
>=
node
->
value
)
{
search_node_
=
node
;
return
node
;
}
}
}
else
{
while
((
node
=
node
->
next
)
!=
NULL
)
{
if
(
x
<
node
->
value
)
{
search_node_
=
node
->
prev
;
return
node
->
prev
;
}
}
}
return
NULL
;
}
Node
*
AdvancingFront
::
FindSearchNode
(
const
double
&
x
)
{
(
void
)
x
;
// suppress compiler warnings "unused parameter 'x'"
// TODO: implement BST index
return
search_node_
;
}
Node
*
AdvancingFront
::
LocatePoint
(
const
Point
*
point
)
{
const
double
px
=
point
->
x
;
Node
*
node
=
FindSearchNode
(
px
);
const
double
nx
=
node
->
point
->
x
;
if
(
px
==
nx
)
{
if
(
point
!=
node
->
point
)
{
// We might have two nodes with same x value for a short time
if
(
point
==
node
->
prev
->
point
)
{
node
=
node
->
prev
;
}
else
if
(
point
==
node
->
next
->
point
)
{
node
=
node
->
next
;
}
else
{
assert
(
0
);
}
}
}
else
if
(
px
<
nx
)
{
while
((
node
=
node
->
prev
)
!=
NULL
)
{
if
(
point
==
node
->
point
)
{
break
;
}
}
}
else
{
while
((
node
=
node
->
next
)
!=
NULL
)
{
if
(
point
==
node
->
point
)
break
;
}
}
if
(
node
)
search_node_
=
node
;
return
node
;
}
AdvancingFront
::~
AdvancingFront
()
{
}
}
polygon/poly2tri/sweep/advancing_front.h
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ADVANCED_FRONT_H
#define ADVANCED_FRONT_H
#include "../common/shapes.h"
namespace
p2t
{
struct
Node
;
// Advancing front node
struct
Node
{
Point
*
point
;
Triangle
*
triangle
;
Node
*
next
;
Node
*
prev
;
double
value
;
Node
(
Point
&
p
)
:
point
(
&
p
),
triangle
(
NULL
),
next
(
NULL
),
prev
(
NULL
),
value
(
p
.
x
)
{
}
Node
(
Point
&
p
,
Triangle
&
t
)
:
point
(
&
p
),
triangle
(
&
t
),
next
(
NULL
),
prev
(
NULL
),
value
(
p
.
x
)
{
}
};
// Advancing front
class
AdvancingFront
{
public
:
AdvancingFront
(
Node
&
head
,
Node
&
tail
);
// Destructor
~
AdvancingFront
();
Node
*
head
();
void
set_head
(
Node
*
node
);
Node
*
tail
();
void
set_tail
(
Node
*
node
);
Node
*
search
();
void
set_search
(
Node
*
node
);
/// Locate insertion point along advancing front
Node
*
LocateNode
(
const
double
&
x
);
Node
*
LocatePoint
(
const
Point
*
point
);
private
:
Node
*
head_
,
*
tail_
,
*
search_node_
;
Node
*
FindSearchNode
(
const
double
&
x
);
};
inline
Node
*
AdvancingFront
::
head
()
{
return
head_
;
}
inline
void
AdvancingFront
::
set_head
(
Node
*
node
)
{
head_
=
node
;
}
inline
Node
*
AdvancingFront
::
tail
()
{
return
tail_
;
}
inline
void
AdvancingFront
::
set_tail
(
Node
*
node
)
{
tail_
=
node
;
}
inline
Node
*
AdvancingFront
::
search
()
{
return
search_node_
;
}
inline
void
AdvancingFront
::
set_search
(
Node
*
node
)
{
search_node_
=
node
;
}
}
#endif
polygon/poly2tri/sweep/cdt.cc
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cdt.h"
namespace
p2t
{
CDT
::
CDT
(
std
::
vector
<
Point
*>
polyline
)
{
sweep_context_
=
new
SweepContext
(
polyline
);
sweep_
=
new
Sweep
;
}
void
CDT
::
AddHole
(
std
::
vector
<
Point
*>
polyline
)
{
sweep_context_
->
AddHole
(
polyline
);
}
void
CDT
::
AddPoint
(
Point
*
point
)
{
sweep_context_
->
AddPoint
(
point
);
}
void
CDT
::
Triangulate
()
{
sweep_
->
Triangulate
(
*
sweep_context_
);
}
std
::
vector
<
p2t
::
Triangle
*>
CDT
::
GetTriangles
()
{
return
sweep_context_
->
GetTriangles
();
}
std
::
list
<
p2t
::
Triangle
*>
CDT
::
GetMap
()
{
return
sweep_context_
->
GetMap
();
}
CDT
::~
CDT
()
{
delete
sweep_context_
;
delete
sweep_
;
}
}
polygon/poly2tri/sweep/cdt.h
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CDT_H
#define CDT_H
#include "advancing_front.h"
#include "sweep_context.h"
#include "sweep.h"
/**
*
* @author Mason Green <mason.green@gmail.com>
*
*/
namespace
p2t
{
class
CDT
{
public
:
/**
* Constructor - add polyline with non repeating points
*
* @param polyline
*/
CDT
(
std
::
vector
<
Point
*>
polyline
);
/**
* Destructor - clean up memory
*/
~
CDT
();
/**
* Add a hole
*
* @param polyline
*/
void
AddHole
(
std
::
vector
<
Point
*>
polyline
);
/**
* Add a steiner point
*
* @param point
*/
void
AddPoint
(
Point
*
point
);
/**
* Triangulate - do this AFTER you've added the polyline, holes, and Steiner points
*/
void
Triangulate
();
/**
* Get CDT triangles
*/
std
::
vector
<
Triangle
*>
GetTriangles
();
/**
* Get triangle map
*/
std
::
list
<
Triangle
*>
GetMap
();
private
:
/**
* Internals
*/
SweepContext
*
sweep_context_
;
Sweep
*
sweep_
;
};
}
#endif
polygon/poly2tri/sweep/sweep.cc
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdexcept>
#include "sweep.h"
#include "sweep_context.h"
#include "advancing_front.h"
#include "../common/utils.h"
namespace
p2t
{
// Triangulate simple polygon with holes
void
Sweep
::
Triangulate
(
SweepContext
&
tcx
)
{
tcx
.
InitTriangulation
();
tcx
.
CreateAdvancingFront
(
nodes_
);
// Sweep points; build mesh
SweepPoints
(
tcx
);
// Clean up
FinalizationPolygon
(
tcx
);
}
void
Sweep
::
SweepPoints
(
SweepContext
&
tcx
)
{
for
(
int
i
=
1
;
i
<
tcx
.
point_count
();
i
++
)
{
Point
&
point
=
*
tcx
.
GetPoint
(
i
);
Node
*
node
=
&
PointEvent
(
tcx
,
point
);
for
(
unsigned
int
i
=
0
;
i
<
point
.
edge_list
.
size
();
i
++
)
{
EdgeEvent
(
tcx
,
point
.
edge_list
[
i
],
node
);
}
}
}
void
Sweep
::
FinalizationPolygon
(
SweepContext
&
tcx
)
{
// Get an Internal triangle to start with
Triangle
*
t
=
tcx
.
front
()
->
head
()
->
next
->
triangle
;
Point
*
p
=
tcx
.
front
()
->
head
()
->
next
->
point
;
while
(
!
t
->
GetConstrainedEdgeCW
(
*
p
))
{
t
=
t
->
NeighborCCW
(
*
p
);
}
// Collect interior triangles constrained by edges
tcx
.
MeshClean
(
*
t
);
}
Node
&
Sweep
::
PointEvent
(
SweepContext
&
tcx
,
Point
&
point
)
{
Node
&
node
=
tcx
.
LocateNode
(
point
);
Node
&
new_node
=
NewFrontTriangle
(
tcx
,
point
,
node
);
// Only need to check +epsilon since point never have smaller
// x value than node due to how we fetch nodes from the front
if
(
point
.
x
<=
node
.
point
->
x
+
EPSILON
)
{
Fill
(
tcx
,
node
);
}
//tcx.AddNode(new_node);
FillAdvancingFront
(
tcx
,
new_node
);
return
new_node
;
}
void
Sweep
::
EdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
)
{
tcx
.
edge_event
.
constrained_edge
=
edge
;
tcx
.
edge_event
.
right
=
(
edge
->
p
->
x
>
edge
->
q
->
x
);
if
(
IsEdgeSideOfTriangle
(
*
node
->
triangle
,
*
edge
->
p
,
*
edge
->
q
))
{
return
;
}
// For now we will do all needed filling
// TODO: integrate with flip process might give some better performance
// but for now this avoid the issue with cases that needs both flips and fills
FillEdgeEvent
(
tcx
,
edge
,
node
);
EdgeEvent
(
tcx
,
*
edge
->
p
,
*
edge
->
q
,
node
->
triangle
,
*
edge
->
q
);
}
void
Sweep
::
EdgeEvent
(
SweepContext
&
tcx
,
Point
&
ep
,
Point
&
eq
,
Triangle
*
triangle
,
Point
&
point
)
{
if
(
IsEdgeSideOfTriangle
(
*
triangle
,
ep
,
eq
))
{
return
;
}
Point
*
p1
=
triangle
->
PointCCW
(
point
);
Orientation
o1
=
Orient2d
(
eq
,
*
p1
,
ep
);
if
(
o1
==
COLLINEAR
)
{
if
(
triangle
->
Contains
(
&
eq
,
p1
))
{
triangle
->
MarkConstrainedEdge
(
&
eq
,
p1
);
// We are modifying the constraint maybe it would be better to
// not change the given constraint and just keep a variable for the new constraint
tcx
.
edge_event
.
constrained_edge
->
q
=
p1
;
triangle
=
&
triangle
->
NeighborAcross
(
point
);
EdgeEvent
(
tcx
,
ep
,
*
p1
,
triangle
,
*
p1
);
}
else
{
std
::
runtime_error
(
"EdgeEvent - collinear points not supported"
);
assert
(
0
);
}
return
;
}
Point
*
p2
=
triangle
->
PointCW
(
point
);
Orientation
o2
=
Orient2d
(
eq
,
*
p2
,
ep
);
if
(
o2
==
COLLINEAR
)
{
if
(
triangle
->
Contains
(
&
eq
,
p2
))
{
triangle
->
MarkConstrainedEdge
(
&
eq
,
p2
);
// We are modifying the constraint maybe it would be better to
// not change the given constraint and just keep a variable for the new constraint
tcx
.
edge_event
.
constrained_edge
->
q
=
p2
;
triangle
=
&
triangle
->
NeighborAcross
(
point
);
EdgeEvent
(
tcx
,
ep
,
*
p2
,
triangle
,
*
p2
);
}
else
{
std
::
runtime_error
(
"EdgeEvent - collinear points not supported"
);
assert
(
0
);
}
return
;
}
if
(
o1
==
o2
)
{
// Need to decide if we are rotating CW or CCW to get to a triangle
// that will cross edge
if
(
o1
==
CW
)
{
triangle
=
triangle
->
NeighborCCW
(
point
);
}
else
{
triangle
=
triangle
->
NeighborCW
(
point
);
}
EdgeEvent
(
tcx
,
ep
,
eq
,
triangle
,
point
);
}
else
{
// This triangle crosses constraint so lets flippin start!
FlipEdgeEvent
(
tcx
,
ep
,
eq
,
triangle
,
point
);
}
}
bool
Sweep
::
IsEdgeSideOfTriangle
(
Triangle
&
triangle
,
Point
&
ep
,
Point
&
eq
)
{
int
index
=
triangle
.
EdgeIndex
(
&
ep
,
&
eq
);
if
(
index
!=
-
1
)
{
triangle
.
MarkConstrainedEdge
(
index
);
Triangle
*
t
=
triangle
.
GetNeighbor
(
index
);
if
(
t
)
{
t
->
MarkConstrainedEdge
(
&
ep
,
&
eq
);
}
return
true
;
}
return
false
;
}
Node
&
Sweep
::
NewFrontTriangle
(
SweepContext
&
tcx
,
Point
&
point
,
Node
&
node
)
{
Triangle
*
triangle
=
new
Triangle
(
point
,
*
node
.
point
,
*
node
.
next
->
point
);
triangle
->
MarkNeighbor
(
*
node
.
triangle
);
tcx
.
AddToMap
(
triangle
);
Node
*
new_node
=
new
Node
(
point
);
nodes_
.
push_back
(
new_node
);
new_node
->
next
=
node
.
next
;
new_node
->
prev
=
&
node
;
node
.
next
->
prev
=
new_node
;
node
.
next
=
new_node
;
if
(
!
Legalize
(
tcx
,
*
triangle
))
{
tcx
.
MapTriangleToNodes
(
*
triangle
);
}
return
*
new_node
;
}
void
Sweep
::
Fill
(
SweepContext
&
tcx
,
Node
&
node
)
{
Triangle
*
triangle
=
new
Triangle
(
*
node
.
prev
->
point
,
*
node
.
point
,
*
node
.
next
->
point
);
// TODO: should copy the constrained_edge value from neighbor triangles
// for now constrained_edge values are copied during the legalize
triangle
->
MarkNeighbor
(
*
node
.
prev
->
triangle
);
triangle
->
MarkNeighbor
(
*
node
.
triangle
);
tcx
.
AddToMap
(
triangle
);
// Update the advancing front
node
.
prev
->
next
=
node
.
next
;
node
.
next
->
prev
=
node
.
prev
;
// If it was legalized the triangle has already been mapped
if
(
!
Legalize
(
tcx
,
*
triangle
))
{
tcx
.
MapTriangleToNodes
(
*
triangle
);
}
}
void
Sweep
::
FillAdvancingFront
(
SweepContext
&
tcx
,
Node
&
n
)
{
// Fill right holes
Node
*
node
=
n
.
next
;
while
(
node
->
next
)
{
// if HoleAngle exceeds 90 degrees then break.
if
(
LargeHole_DontFill
(
node
))
break
;
Fill
(
tcx
,
*
node
);
node
=
node
->
next
;
}
// Fill left holes
node
=
n
.
prev
;
while
(
node
->
prev
)
{
// if HoleAngle exceeds 90 degrees then break.
if
(
LargeHole_DontFill
(
node
))
break
;
Fill
(
tcx
,
*
node
);
node
=
node
->
prev
;
}
// Fill right basins
if
(
n
.
next
&&
n
.
next
->
next
)
{
double
angle
=
BasinAngle
(
n
);
if
(
angle
<
PI_3div4
)
{
FillBasin
(
tcx
,
n
);
}
}
}
// True if HoleAngle exceeds 90 degrees.
bool
Sweep
::
LargeHole_DontFill
(
Node
*
node
)
{
Node
*
nextNode
=
node
->
next
;
Node
*
prevNode
=
node
->
prev
;
if
(
!
AngleExceeds90Degrees
(
node
->
point
,
nextNode
->
point
,
prevNode
->
point
))
return
false
;
// Check additional points on front.
Node
*
next2Node
=
nextNode
->
next
;
// "..Plus.." because only want angles on same side as point being added.
if
((
next2Node
!=
NULL
)
&&
!
AngleExceedsPlus90DegreesOrIsNegative
(
node
->
point
,
next2Node
->
point
,
prevNode
->
point
))
return
false
;
Node
*
prev2Node
=
prevNode
->
prev
;
// "..Plus.." because only want angles on same side as point being added.
if
((
prev2Node
!=
NULL
)
&&
!
AngleExceedsPlus90DegreesOrIsNegative
(
node
->
point
,
nextNode
->
point
,
prev2Node
->
point
))
return
false
;
return
true
;
}
bool
Sweep
::
AngleExceeds90Degrees
(
Point
*
origin
,
Point
*
pa
,
Point
*
pb
)
{
double
angle
=
Angle
(
*
origin
,
*
pa
,
*
pb
);
bool
exceeds90Degrees
=
((
angle
>
PI_div2
)
||
(
angle
<
-
PI_div2
));
return
exceeds90Degrees
;
}
bool
Sweep
::
AngleExceedsPlus90DegreesOrIsNegative
(
Point
*
origin
,
Point
*
pa
,
Point
*
pb
)
{
double
angle
=
Angle
(
*
origin
,
*
pa
,
*
pb
);
bool
exceedsPlus90DegreesOrIsNegative
=
(
angle
>
PI_div2
)
||
(
angle
<
0
);
return
exceedsPlus90DegreesOrIsNegative
;
}
double
Sweep
::
Angle
(
Point
&
origin
,
Point
&
pa
,
Point
&
pb
)
{
/* Complex plane
* ab = cosA +i*sinA
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
* atan2(y,x) computes the principal value of the argument function
* applied to the complex number x+iy
* Where x = ax*bx + ay*by
* y = ax*by - ay*bx
*/
double
px
=
origin
.
x
;
double
py
=
origin
.
y
;
double
ax
=
pa
.
x
-
px
;
double
ay
=
pa
.
y
-
py
;
double
bx
=
pb
.
x
-
px
;
double
by
=
pb
.
y
-
py
;
double
x
=
ax
*
by
-
ay
*
bx
;
double
y
=
ax
*
bx
+
ay
*
by
;
double
angle
=
atan2
(
x
,
y
);
return
angle
;
}
double
Sweep
::
BasinAngle
(
Node
&
node
)
{
double
ax
=
node
.
point
->
x
-
node
.
next
->
next
->
point
->
x
;
double
ay
=
node
.
point
->
y
-
node
.
next
->
next
->
point
->
y
;
return
atan2
(
ay
,
ax
);
}
double
Sweep
::
HoleAngle
(
Node
&
node
)
{
/* Complex plane
* ab = cosA +i*sinA
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
* atan2(y,x) computes the principal value of the argument function
* applied to the complex number x+iy
* Where x = ax*bx + ay*by
* y = ax*by - ay*bx
*/
double
ax
=
node
.
next
->
point
->
x
-
node
.
point
->
x
;
double
ay
=
node
.
next
->
point
->
y
-
node
.
point
->
y
;
double
bx
=
node
.
prev
->
point
->
x
-
node
.
point
->
x
;
double
by
=
node
.
prev
->
point
->
y
-
node
.
point
->
y
;
return
atan2
(
ax
*
by
-
ay
*
bx
,
ax
*
bx
+
ay
*
by
);
}
bool
Sweep
::
Legalize
(
SweepContext
&
tcx
,
Triangle
&
t
)
{
// To legalize a triangle we start by finding if any of the three edges
// violate the Delaunay condition
for
(
int
i
=
0
;
i
<
3
;
i
++
)
{
if
(
t
.
delaunay_edge
[
i
])
continue
;
Triangle
*
ot
=
t
.
GetNeighbor
(
i
);
if
(
ot
)
{
Point
*
p
=
t
.
GetPoint
(
i
);
Point
*
op
=
ot
->
OppositePoint
(
t
,
*
p
);
int
oi
=
ot
->
Index
(
op
);
// If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
// then we should not try to legalize
if
(
ot
->
constrained_edge
[
oi
]
||
ot
->
delaunay_edge
[
oi
])
{
t
.
constrained_edge
[
i
]
=
ot
->
constrained_edge
[
oi
];
continue
;
}
bool
inside
=
Incircle
(
*
p
,
*
t
.
PointCCW
(
*
p
),
*
t
.
PointCW
(
*
p
),
*
op
);
if
(
inside
)
{
// Lets mark this shared edge as Delaunay
t
.
delaunay_edge
[
i
]
=
true
;
ot
->
delaunay_edge
[
oi
]
=
true
;
// Lets rotate shared edge one vertex CW to legalize it
RotateTrianglePair
(
t
,
*
p
,
*
ot
,
*
op
);
// We now got one valid Delaunay Edge shared by two triangles
// This gives us 4 new edges to check for Delaunay
// Make sure that triangle to node mapping is done only one time for a specific triangle
bool
not_legalized
=
!
Legalize
(
tcx
,
t
);
if
(
not_legalized
)
{
tcx
.
MapTriangleToNodes
(
t
);
}
not_legalized
=
!
Legalize
(
tcx
,
*
ot
);
if
(
not_legalized
)
tcx
.
MapTriangleToNodes
(
*
ot
);
// Reset the Delaunay edges, since they only are valid Delaunay edges
// until we add a new triangle or point.
// XXX: need to think about this. Can these edges be tried after we
// return to previous recursive level?
t
.
delaunay_edge
[
i
]
=
false
;
ot
->
delaunay_edge
[
oi
]
=
false
;
// If triangle have been legalized no need to check the other edges since
// the recursive legalization will handles those so we can end here.
return
true
;
}
}
}
return
false
;
}
bool
Sweep
::
Incircle
(
Point
&
pa
,
Point
&
pb
,
Point
&
pc
,
Point
&
pd
)
{
double
adx
=
pa
.
x
-
pd
.
x
;
double
ady
=
pa
.
y
-
pd
.
y
;
double
bdx
=
pb
.
x
-
pd
.
x
;
double
bdy
=
pb
.
y
-
pd
.
y
;
double
adxbdy
=
adx
*
bdy
;
double
bdxady
=
bdx
*
ady
;
double
oabd
=
adxbdy
-
bdxady
;
if
(
oabd
<=
0
)
return
false
;
double
cdx
=
pc
.
x
-
pd
.
x
;
double
cdy
=
pc
.
y
-
pd
.
y
;
double
cdxady
=
cdx
*
ady
;
double
adxcdy
=
adx
*
cdy
;
double
ocad
=
cdxady
-
adxcdy
;
if
(
ocad
<=
0
)
return
false
;
double
bdxcdy
=
bdx
*
cdy
;
double
cdxbdy
=
cdx
*
bdy
;
double
alift
=
adx
*
adx
+
ady
*
ady
;
double
blift
=
bdx
*
bdx
+
bdy
*
bdy
;
double
clift
=
cdx
*
cdx
+
cdy
*
cdy
;
double
det
=
alift
*
(
bdxcdy
-
cdxbdy
)
+
blift
*
ocad
+
clift
*
oabd
;
return
det
>
0
;
}
void
Sweep
::
RotateTrianglePair
(
Triangle
&
t
,
Point
&
p
,
Triangle
&
ot
,
Point
&
op
)
{
Triangle
*
n1
,
*
n2
,
*
n3
,
*
n4
;
n1
=
t
.
NeighborCCW
(
p
);
n2
=
t
.
NeighborCW
(
p
);
n3
=
ot
.
NeighborCCW
(
op
);
n4
=
ot
.
NeighborCW
(
op
);
bool
ce1
,
ce2
,
ce3
,
ce4
;
ce1
=
t
.
GetConstrainedEdgeCCW
(
p
);
ce2
=
t
.
GetConstrainedEdgeCW
(
p
);
ce3
=
ot
.
GetConstrainedEdgeCCW
(
op
);
ce4
=
ot
.
GetConstrainedEdgeCW
(
op
);
bool
de1
,
de2
,
de3
,
de4
;
de1
=
t
.
GetDelunayEdgeCCW
(
p
);
de2
=
t
.
GetDelunayEdgeCW
(
p
);
de3
=
ot
.
GetDelunayEdgeCCW
(
op
);
de4
=
ot
.
GetDelunayEdgeCW
(
op
);
t
.
Legalize
(
p
,
op
);
ot
.
Legalize
(
op
,
p
);
// Remap delaunay_edge
ot
.
SetDelunayEdgeCCW
(
p
,
de1
);
t
.
SetDelunayEdgeCW
(
p
,
de2
);
t
.
SetDelunayEdgeCCW
(
op
,
de3
);
ot
.
SetDelunayEdgeCW
(
op
,
de4
);
// Remap constrained_edge
ot
.
SetConstrainedEdgeCCW
(
p
,
ce1
);
t
.
SetConstrainedEdgeCW
(
p
,
ce2
);
t
.
SetConstrainedEdgeCCW
(
op
,
ce3
);
ot
.
SetConstrainedEdgeCW
(
op
,
ce4
);
// Remap neighbors
// XXX: might optimize the markNeighbor by keeping track of
// what side should be assigned to what neighbor after the
// rotation. Now mark neighbor does lots of testing to find
// the right side.
t
.
ClearNeighbors
();
ot
.
ClearNeighbors
();
if
(
n1
)
ot
.
MarkNeighbor
(
*
n1
);
if
(
n2
)
t
.
MarkNeighbor
(
*
n2
);
if
(
n3
)
t
.
MarkNeighbor
(
*
n3
);
if
(
n4
)
ot
.
MarkNeighbor
(
*
n4
);
t
.
MarkNeighbor
(
ot
);
}
void
Sweep
::
FillBasin
(
SweepContext
&
tcx
,
Node
&
node
)
{
if
(
Orient2d
(
*
node
.
point
,
*
node
.
next
->
point
,
*
node
.
next
->
next
->
point
)
==
CCW
)
{
tcx
.
basin
.
left_node
=
node
.
next
->
next
;
}
else
{
tcx
.
basin
.
left_node
=
node
.
next
;
}
// Find the bottom and right node
tcx
.
basin
.
bottom_node
=
tcx
.
basin
.
left_node
;
while
(
tcx
.
basin
.
bottom_node
->
next
&&
tcx
.
basin
.
bottom_node
->
point
->
y
>=
tcx
.
basin
.
bottom_node
->
next
->
point
->
y
)
{
tcx
.
basin
.
bottom_node
=
tcx
.
basin
.
bottom_node
->
next
;
}
if
(
tcx
.
basin
.
bottom_node
==
tcx
.
basin
.
left_node
)
{
// No valid basin
return
;
}
tcx
.
basin
.
right_node
=
tcx
.
basin
.
bottom_node
;
while
(
tcx
.
basin
.
right_node
->
next
&&
tcx
.
basin
.
right_node
->
point
->
y
<
tcx
.
basin
.
right_node
->
next
->
point
->
y
)
{
tcx
.
basin
.
right_node
=
tcx
.
basin
.
right_node
->
next
;
}
if
(
tcx
.
basin
.
right_node
==
tcx
.
basin
.
bottom_node
)
{
// No valid basins
return
;
}
tcx
.
basin
.
width
=
tcx
.
basin
.
right_node
->
point
->
x
-
tcx
.
basin
.
left_node
->
point
->
x
;
tcx
.
basin
.
left_highest
=
tcx
.
basin
.
left_node
->
point
->
y
>
tcx
.
basin
.
right_node
->
point
->
y
;
FillBasinReq
(
tcx
,
tcx
.
basin
.
bottom_node
);
}
void
Sweep
::
FillBasinReq
(
SweepContext
&
tcx
,
Node
*
node
)
{
// if shallow stop filling
if
(
IsShallow
(
tcx
,
*
node
))
{
return
;
}
Fill
(
tcx
,
*
node
);
if
(
node
->
prev
==
tcx
.
basin
.
left_node
&&
node
->
next
==
tcx
.
basin
.
right_node
)
{
return
;
}
else
if
(
node
->
prev
==
tcx
.
basin
.
left_node
)
{
Orientation
o
=
Orient2d
(
*
node
->
point
,
*
node
->
next
->
point
,
*
node
->
next
->
next
->
point
);
if
(
o
==
CW
)
{
return
;
}
node
=
node
->
next
;
}
else
if
(
node
->
next
==
tcx
.
basin
.
right_node
)
{
Orientation
o
=
Orient2d
(
*
node
->
point
,
*
node
->
prev
->
point
,
*
node
->
prev
->
prev
->
point
);
if
(
o
==
CCW
)
{
return
;
}
node
=
node
->
prev
;
}
else
{
// Continue with the neighbor node with lowest Y value
if
(
node
->
prev
->
point
->
y
<
node
->
next
->
point
->
y
)
{
node
=
node
->
prev
;
}
else
{
node
=
node
->
next
;
}
}
FillBasinReq
(
tcx
,
node
);
}
bool
Sweep
::
IsShallow
(
SweepContext
&
tcx
,
Node
&
node
)
{
double
height
;
if
(
tcx
.
basin
.
left_highest
)
{
height
=
tcx
.
basin
.
left_node
->
point
->
y
-
node
.
point
->
y
;
}
else
{
height
=
tcx
.
basin
.
right_node
->
point
->
y
-
node
.
point
->
y
;
}
// if shallow stop filling
if
(
tcx
.
basin
.
width
>
height
)
{
return
true
;
}
return
false
;
}
void
Sweep
::
FillEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
)
{
if
(
tcx
.
edge_event
.
right
)
{
FillRightAboveEdgeEvent
(
tcx
,
edge
,
node
);
}
else
{
FillLeftAboveEdgeEvent
(
tcx
,
edge
,
node
);
}
}
void
Sweep
::
FillRightAboveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
)
{
while
(
node
->
next
->
point
->
x
<
edge
->
p
->
x
)
{
// Check if next node is below the edge
if
(
Orient2d
(
*
edge
->
q
,
*
node
->
next
->
point
,
*
edge
->
p
)
==
CCW
)
{
FillRightBelowEdgeEvent
(
tcx
,
edge
,
*
node
);
}
else
{
node
=
node
->
next
;
}
}
}
void
Sweep
::
FillRightBelowEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
)
{
if
(
node
.
point
->
x
<
edge
->
p
->
x
)
{
if
(
Orient2d
(
*
node
.
point
,
*
node
.
next
->
point
,
*
node
.
next
->
next
->
point
)
==
CCW
)
{
// Concave
FillRightConcaveEdgeEvent
(
tcx
,
edge
,
node
);
}
else
{
// Convex
FillRightConvexEdgeEvent
(
tcx
,
edge
,
node
);
// Retry this one
FillRightBelowEdgeEvent
(
tcx
,
edge
,
node
);
}
}
}
void
Sweep
::
FillRightConcaveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
)
{
Fill
(
tcx
,
*
node
.
next
);
if
(
node
.
next
->
point
!=
edge
->
p
)
{
// Next above or below edge?
if
(
Orient2d
(
*
edge
->
q
,
*
node
.
next
->
point
,
*
edge
->
p
)
==
CCW
)
{
// Below
if
(
Orient2d
(
*
node
.
point
,
*
node
.
next
->
point
,
*
node
.
next
->
next
->
point
)
==
CCW
)
{
// Next is concave
FillRightConcaveEdgeEvent
(
tcx
,
edge
,
node
);
}
else
{
// Next is convex
}
}
}
}
void
Sweep
::
FillRightConvexEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
)
{
// Next concave or convex?
if
(
Orient2d
(
*
node
.
next
->
point
,
*
node
.
next
->
next
->
point
,
*
node
.
next
->
next
->
next
->
point
)
==
CCW
)
{
// Concave
FillRightConcaveEdgeEvent
(
tcx
,
edge
,
*
node
.
next
);
}
else
{
// Convex
// Next above or below edge?
if
(
Orient2d
(
*
edge
->
q
,
*
node
.
next
->
next
->
point
,
*
edge
->
p
)
==
CCW
)
{
// Below
FillRightConvexEdgeEvent
(
tcx
,
edge
,
*
node
.
next
);
}
else
{
// Above
}
}
}
void
Sweep
::
FillLeftAboveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
)
{
while
(
node
->
prev
->
point
->
x
>
edge
->
p
->
x
)
{
// Check if next node is below the edge
if
(
Orient2d
(
*
edge
->
q
,
*
node
->
prev
->
point
,
*
edge
->
p
)
==
CW
)
{
FillLeftBelowEdgeEvent
(
tcx
,
edge
,
*
node
);
}
else
{
node
=
node
->
prev
;
}
}
}
void
Sweep
::
FillLeftBelowEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
)
{
if
(
node
.
point
->
x
>
edge
->
p
->
x
)
{
if
(
Orient2d
(
*
node
.
point
,
*
node
.
prev
->
point
,
*
node
.
prev
->
prev
->
point
)
==
CW
)
{
// Concave
FillLeftConcaveEdgeEvent
(
tcx
,
edge
,
node
);
}
else
{
// Convex
FillLeftConvexEdgeEvent
(
tcx
,
edge
,
node
);
// Retry this one
FillLeftBelowEdgeEvent
(
tcx
,
edge
,
node
);
}
}
}
void
Sweep
::
FillLeftConvexEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
)
{
// Next concave or convex?
if
(
Orient2d
(
*
node
.
prev
->
point
,
*
node
.
prev
->
prev
->
point
,
*
node
.
prev
->
prev
->
prev
->
point
)
==
CW
)
{
// Concave
FillLeftConcaveEdgeEvent
(
tcx
,
edge
,
*
node
.
prev
);
}
else
{
// Convex
// Next above or below edge?
if
(
Orient2d
(
*
edge
->
q
,
*
node
.
prev
->
prev
->
point
,
*
edge
->
p
)
==
CW
)
{
// Below
FillLeftConvexEdgeEvent
(
tcx
,
edge
,
*
node
.
prev
);
}
else
{
// Above
}
}
}
void
Sweep
::
FillLeftConcaveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
)
{
Fill
(
tcx
,
*
node
.
prev
);
if
(
node
.
prev
->
point
!=
edge
->
p
)
{
// Next above or below edge?
if
(
Orient2d
(
*
edge
->
q
,
*
node
.
prev
->
point
,
*
edge
->
p
)
==
CW
)
{
// Below
if
(
Orient2d
(
*
node
.
point
,
*
node
.
prev
->
point
,
*
node
.
prev
->
prev
->
point
)
==
CW
)
{
// Next is concave
FillLeftConcaveEdgeEvent
(
tcx
,
edge
,
node
);
}
else
{
// Next is convex
}
}
}
}
void
Sweep
::
FlipEdgeEvent
(
SweepContext
&
tcx
,
Point
&
ep
,
Point
&
eq
,
Triangle
*
t
,
Point
&
p
)
{
Triangle
&
ot
=
t
->
NeighborAcross
(
p
);
Point
&
op
=
*
ot
.
OppositePoint
(
*
t
,
p
);
if
(
&
ot
==
NULL
)
{
// If we want to integrate the fillEdgeEvent do it here
// With current implementation we should never get here
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
assert
(
0
);
}
if
(
InScanArea
(
p
,
*
t
->
PointCCW
(
p
),
*
t
->
PointCW
(
p
),
op
))
{
// Lets rotate shared edge one vertex CW
RotateTrianglePair
(
*
t
,
p
,
ot
,
op
);
tcx
.
MapTriangleToNodes
(
*
t
);
tcx
.
MapTriangleToNodes
(
ot
);
if
(
p
==
eq
&&
op
==
ep
)
{
if
(
eq
==
*
tcx
.
edge_event
.
constrained_edge
->
q
&&
ep
==
*
tcx
.
edge_event
.
constrained_edge
->
p
)
{
t
->
MarkConstrainedEdge
(
&
ep
,
&
eq
);
ot
.
MarkConstrainedEdge
(
&
ep
,
&
eq
);
Legalize
(
tcx
,
*
t
);
Legalize
(
tcx
,
ot
);
}
else
{
// XXX: I think one of the triangles should be legalized here?
}
}
else
{
Orientation
o
=
Orient2d
(
eq
,
op
,
ep
);
t
=
&
NextFlipTriangle
(
tcx
,
(
int
)
o
,
*
t
,
ot
,
p
,
op
);
FlipEdgeEvent
(
tcx
,
ep
,
eq
,
t
,
p
);
}
}
else
{
Point
&
newP
=
NextFlipPoint
(
ep
,
eq
,
ot
,
op
);
FlipScanEdgeEvent
(
tcx
,
ep
,
eq
,
*
t
,
ot
,
newP
);
EdgeEvent
(
tcx
,
ep
,
eq
,
t
,
p
);
}
}
Triangle
&
Sweep
::
NextFlipTriangle
(
SweepContext
&
tcx
,
int
o
,
Triangle
&
t
,
Triangle
&
ot
,
Point
&
p
,
Point
&
op
)
{
if
(
o
==
CCW
)
{
// ot is not crossing edge after flip
int
edge_index
=
ot
.
EdgeIndex
(
&
p
,
&
op
);
ot
.
delaunay_edge
[
edge_index
]
=
true
;
Legalize
(
tcx
,
ot
);
ot
.
ClearDelunayEdges
();
return
t
;
}
// t is not crossing edge after flip
int
edge_index
=
t
.
EdgeIndex
(
&
p
,
&
op
);
t
.
delaunay_edge
[
edge_index
]
=
true
;
Legalize
(
tcx
,
t
);
t
.
ClearDelunayEdges
();
return
ot
;
}
Point
&
Sweep
::
NextFlipPoint
(
Point
&
ep
,
Point
&
eq
,
Triangle
&
ot
,
Point
&
op
)
{
Orientation
o2d
=
Orient2d
(
eq
,
op
,
ep
);
if
(
o2d
==
CW
)
{
// Right
return
*
ot
.
PointCCW
(
op
);
}
else
if
(
o2d
==
CCW
)
{
// Left
return
*
ot
.
PointCW
(
op
);
}
else
{
//throw new RuntimeException("[Unsupported] Opposing point on constrained edge");
assert
(
0
);
}
}
void
Sweep
::
FlipScanEdgeEvent
(
SweepContext
&
tcx
,
Point
&
ep
,
Point
&
eq
,
Triangle
&
flip_triangle
,
Triangle
&
t
,
Point
&
p
)
{
Triangle
&
ot
=
t
.
NeighborAcross
(
p
);
Point
&
op
=
*
ot
.
OppositePoint
(
t
,
p
);
if
(
&
t
.
NeighborAcross
(
p
)
==
NULL
)
{
// If we want to integrate the fillEdgeEvent do it here
// With current implementation we should never get here
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
assert
(
0
);
}
if
(
InScanArea
(
eq
,
*
flip_triangle
.
PointCCW
(
eq
),
*
flip_triangle
.
PointCW
(
eq
),
op
))
{
// flip with new edge op->eq
FlipEdgeEvent
(
tcx
,
eq
,
op
,
&
ot
,
op
);
// TODO: Actually I just figured out that it should be possible to
// improve this by getting the next ot and op before the the above
// flip and continue the flipScanEdgeEvent here
// set new ot and op here and loop back to inScanArea test
// also need to set a new flip_triangle first
// Turns out at first glance that this is somewhat complicated
// so it will have to wait.
}
else
{
Point
&
newP
=
NextFlipPoint
(
ep
,
eq
,
ot
,
op
);
FlipScanEdgeEvent
(
tcx
,
ep
,
eq
,
flip_triangle
,
ot
,
newP
);
}
}
Sweep
::~
Sweep
()
{
// Clean up memory
for
(
int
i
=
0
;
i
<
nodes_
.
size
();
i
++
)
{
delete
nodes_
[
i
];
}
}
}
polygon/poly2tri/sweep/sweep.h
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and
* Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
* International Journal of Geographical Information Science
*
* "FlipScan" Constrained Edge Algorithm invented by Thomas hln, thahlen@gmail.com
*/
#ifndef SWEEP_H
#define SWEEP_H
#include <vector>
namespace
p2t
{
class
SweepContext
;
struct
Node
;
struct
Point
;
struct
Edge
;
class
Triangle
;
class
Sweep
{
public
:
/**
* Triangulate
*
* @param tcx
*/
void
Triangulate
(
SweepContext
&
tcx
);
/**
* Destructor - clean up memory
*/
~
Sweep
();
private
:
/**
* Start sweeping the Y-sorted point set from bottom to top
*
* @param tcx
*/
void
SweepPoints
(
SweepContext
&
tcx
);
/**
* Find closes node to the left of the new point and
* create a new triangle. If needed new holes and basins
* will be filled to.
*
* @param tcx
* @param point
* @return
*/
Node
&
PointEvent
(
SweepContext
&
tcx
,
Point
&
point
);
/**
*
*
* @param tcx
* @param edge
* @param node
*/
void
EdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
);
void
EdgeEvent
(
SweepContext
&
tcx
,
Point
&
ep
,
Point
&
eq
,
Triangle
*
triangle
,
Point
&
point
);
/**
* Creates a new front triangle and legalize it
*
* @param tcx
* @param point
* @param node
* @return
*/
Node
&
NewFrontTriangle
(
SweepContext
&
tcx
,
Point
&
point
,
Node
&
node
);
/**
* Adds a triangle to the advancing front to fill a hole.
* @param tcx
* @param node - middle node, that is the bottom of the hole
*/
void
Fill
(
SweepContext
&
tcx
,
Node
&
node
);
/**
* Returns true if triangle was legalized
*/
bool
Legalize
(
SweepContext
&
tcx
,
Triangle
&
t
);
/**
* <b>Requirement</b>:<br>
* 1. a,b and c form a triangle.<br>
* 2. a and d is know to be on opposite side of bc<br>
* <pre>
* a
* +
* / \
* / \
* b/ \c
* +-------+
* / d \
* / \
* </pre>
* <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
* a,b and c<br>
* d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
* This preknowledge gives us a way to optimize the incircle test
* @param a - triangle point, opposite d
* @param b - triangle point
* @param c - triangle point
* @param d - point opposite a
* @return true if d is inside circle, false if on circle edge
*/
bool
Incircle
(
Point
&
pa
,
Point
&
pb
,
Point
&
pc
,
Point
&
pd
);
/**
* Rotates a triangle pair one vertex CW
*<pre>
* n2 n2
* P +-----+ P +-----+
* | t /| |\ t |
* | / | | \ |
* n1| / |n3 n1| \ |n3
* | / | after CW | \ |
* |/ oT | | oT \|
* +-----+ oP +-----+
* n4 n4
* </pre>
*/
void
RotateTrianglePair
(
Triangle
&
t
,
Point
&
p
,
Triangle
&
ot
,
Point
&
op
);
/**
* Fills holes in the Advancing Front
*
*
* @param tcx
* @param n
*/
void
FillAdvancingFront
(
SweepContext
&
tcx
,
Node
&
n
);
// Decision-making about when to Fill hole.
// Contributed by ToolmakerSteve2
bool
LargeHole_DontFill
(
Node
*
node
);
bool
AngleExceeds90Degrees
(
Point
*
origin
,
Point
*
pa
,
Point
*
pb
);
bool
AngleExceedsPlus90DegreesOrIsNegative
(
Point
*
origin
,
Point
*
pa
,
Point
*
pb
);
double
Angle
(
Point
&
origin
,
Point
&
pa
,
Point
&
pb
);
/**
*
* @param node - middle node
* @return the angle between 3 front nodes
*/
double
HoleAngle
(
Node
&
node
);
/**
* The basin angle is decided against the horizontal line [1,0]
*/
double
BasinAngle
(
Node
&
node
);
/**
* Fills a basin that has formed on the Advancing Front to the right
* of given node.<br>
* First we decide a left,bottom and right node that forms the
* boundaries of the basin. Then we do a reqursive fill.
*
* @param tcx
* @param node - starting node, this or next node will be left node
*/
void
FillBasin
(
SweepContext
&
tcx
,
Node
&
node
);
/**
* Recursive algorithm to fill a Basin with triangles
*
* @param tcx
* @param node - bottom_node
* @param cnt - counter used to alternate on even and odd numbers
*/
void
FillBasinReq
(
SweepContext
&
tcx
,
Node
*
node
);
bool
IsShallow
(
SweepContext
&
tcx
,
Node
&
node
);
bool
IsEdgeSideOfTriangle
(
Triangle
&
triangle
,
Point
&
ep
,
Point
&
eq
);
void
FillEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
);
void
FillRightAboveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
);
void
FillRightBelowEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
);
void
FillRightConcaveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
);
void
FillRightConvexEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
);
void
FillLeftAboveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
*
node
);
void
FillLeftBelowEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
);
void
FillLeftConcaveEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
);
void
FillLeftConvexEdgeEvent
(
SweepContext
&
tcx
,
Edge
*
edge
,
Node
&
node
);
void
FlipEdgeEvent
(
SweepContext
&
tcx
,
Point
&
ep
,
Point
&
eq
,
Triangle
*
t
,
Point
&
p
);
/**
* After a flip we have two triangles and know that only one will still be
* intersecting the edge. So decide which to contiune with and legalize the other
*
* @param tcx
* @param o - should be the result of an orient2d( eq, op, ep )
* @param t - triangle 1
* @param ot - triangle 2
* @param p - a point shared by both triangles
* @param op - another point shared by both triangles
* @return returns the triangle still intersecting the edge
*/
Triangle
&
NextFlipTriangle
(
SweepContext
&
tcx
,
int
o
,
Triangle
&
t
,
Triangle
&
ot
,
Point
&
p
,
Point
&
op
);
/**
* When we need to traverse from one triangle to the next we need
* the point in current triangle that is the opposite point to the next
* triangle.
*
* @param ep
* @param eq
* @param ot
* @param op
* @return
*/
Point
&
NextFlipPoint
(
Point
&
ep
,
Point
&
eq
,
Triangle
&
ot
,
Point
&
op
);
/**
* Scan part of the FlipScan algorithm<br>
* When a triangle pair isn't flippable we will scan for the next
* point that is inside the flip triangle scan area. When found
* we generate a new flipEdgeEvent
*
* @param tcx
* @param ep - last point on the edge we are traversing
* @param eq - first point on the edge we are traversing
* @param flipTriangle - the current triangle sharing the point eq with edge
* @param t
* @param p
*/
void
FlipScanEdgeEvent
(
SweepContext
&
tcx
,
Point
&
ep
,
Point
&
eq
,
Triangle
&
flip_triangle
,
Triangle
&
t
,
Point
&
p
);
void
FinalizationPolygon
(
SweepContext
&
tcx
);
std
::
vector
<
Node
*>
nodes_
;
};
}
#endif
polygon/poly2tri/sweep/sweep_context.cc
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sweep_context.h"
#include <algorithm>
#include "advancing_front.h"
namespace
p2t
{
SweepContext
::
SweepContext
(
std
::
vector
<
Point
*>
polyline
)
:
front_
(
0
),
head_
(
0
),
tail_
(
0
),
af_head_
(
0
),
af_middle_
(
0
),
af_tail_
(
0
)
{
basin
=
Basin
();
edge_event
=
EdgeEvent
();
points_
=
polyline
;
InitEdges
(
points_
);
}
void
SweepContext
::
AddHole
(
std
::
vector
<
Point
*>
polyline
)
{
InitEdges
(
polyline
);
for
(
unsigned
int
i
=
0
;
i
<
polyline
.
size
();
i
++
)
{
points_
.
push_back
(
polyline
[
i
]);
}
}
void
SweepContext
::
AddPoint
(
Point
*
point
)
{
points_
.
push_back
(
point
);
}
std
::
vector
<
Triangle
*>
SweepContext
::
GetTriangles
()
{
return
triangles_
;
}
std
::
list
<
Triangle
*>
SweepContext
::
GetMap
()
{
return
map_
;
}
void
SweepContext
::
InitTriangulation
()
{
double
xmax
(
points_
[
0
]
->
x
),
xmin
(
points_
[
0
]
->
x
);
double
ymax
(
points_
[
0
]
->
y
),
ymin
(
points_
[
0
]
->
y
);
// Calculate bounds.
for
(
unsigned
int
i
=
0
;
i
<
points_
.
size
();
i
++
)
{
Point
&
p
=
*
points_
[
i
];
if
(
p
.
x
>
xmax
)
xmax
=
p
.
x
;
if
(
p
.
x
<
xmin
)
xmin
=
p
.
x
;
if
(
p
.
y
>
ymax
)
ymax
=
p
.
y
;
if
(
p
.
y
<
ymin
)
ymin
=
p
.
y
;
}
double
dx
=
kAlpha
*
(
xmax
-
xmin
);
double
dy
=
kAlpha
*
(
ymax
-
ymin
);
head_
=
new
Point
(
xmax
+
dx
,
ymin
-
dy
);
tail_
=
new
Point
(
xmin
-
dx
,
ymin
-
dy
);
// Sort points along y-axis
std
::
sort
(
points_
.
begin
(),
points_
.
end
(),
cmp
);
}
void
SweepContext
::
InitEdges
(
std
::
vector
<
Point
*>
polyline
)
{
int
num_points
=
polyline
.
size
();
for
(
int
i
=
0
;
i
<
num_points
;
i
++
)
{
int
j
=
i
<
num_points
-
1
?
i
+
1
:
0
;
edge_list
.
push_back
(
new
Edge
(
*
polyline
[
i
],
*
polyline
[
j
]));
}
}
Point
*
SweepContext
::
GetPoint
(
const
int
&
index
)
{
return
points_
[
index
];
}
void
SweepContext
::
AddToMap
(
Triangle
*
triangle
)
{
map_
.
push_back
(
triangle
);
}
Node
&
SweepContext
::
LocateNode
(
Point
&
point
)
{
// TODO implement search tree
return
*
front_
->
LocateNode
(
point
.
x
);
}
void
SweepContext
::
CreateAdvancingFront
(
std
::
vector
<
Node
*>
nodes
)
{
(
void
)
nodes
;
// Initial triangle
Triangle
*
triangle
=
new
Triangle
(
*
points_
[
0
],
*
tail_
,
*
head_
);
map_
.
push_back
(
triangle
);
af_head_
=
new
Node
(
*
triangle
->
GetPoint
(
1
),
*
triangle
);
af_middle_
=
new
Node
(
*
triangle
->
GetPoint
(
0
),
*
triangle
);
af_tail_
=
new
Node
(
*
triangle
->
GetPoint
(
2
));
front_
=
new
AdvancingFront
(
*
af_head_
,
*
af_tail_
);
// TODO: More intuitive if head is middles next and not previous?
// so swap head and tail
af_head_
->
next
=
af_middle_
;
af_middle_
->
next
=
af_tail_
;
af_middle_
->
prev
=
af_head_
;
af_tail_
->
prev
=
af_middle_
;
}
void
SweepContext
::
RemoveNode
(
Node
*
node
)
{
delete
node
;
}
void
SweepContext
::
MapTriangleToNodes
(
Triangle
&
t
)
{
for
(
int
i
=
0
;
i
<
3
;
i
++
)
{
if
(
!
t
.
GetNeighbor
(
i
))
{
Node
*
n
=
front_
->
LocatePoint
(
t
.
PointCW
(
*
t
.
GetPoint
(
i
)));
if
(
n
)
n
->
triangle
=
&
t
;
}
}
}
void
SweepContext
::
RemoveFromMap
(
Triangle
*
triangle
)
{
map_
.
remove
(
triangle
);
}
void
SweepContext
::
MeshClean
(
Triangle
&
triangle
)
{
std
::
vector
<
Triangle
*>
triangles
;
triangles
.
push_back
(
&
triangle
);
while
(
!
triangles
.
empty
()){
Triangle
*
t
=
triangles
.
back
();
triangles
.
pop_back
();
if
(
t
!=
NULL
&&
!
t
->
IsInterior
())
{
t
->
IsInterior
(
true
);
triangles_
.
push_back
(
t
);
for
(
int
i
=
0
;
i
<
3
;
i
++
)
{
if
(
!
t
->
constrained_edge
[
i
])
triangles
.
push_back
(
t
->
GetNeighbor
(
i
));
}
}
}
}
SweepContext
::~
SweepContext
()
{
// Clean up memory
delete
head_
;
delete
tail_
;
delete
front_
;
delete
af_head_
;
delete
af_middle_
;
delete
af_tail_
;
typedef
std
::
list
<
Triangle
*>
type_list
;
for
(
type_list
::
iterator
iter
=
map_
.
begin
();
iter
!=
map_
.
end
();
++
iter
)
{
Triangle
*
ptr
=
*
iter
;
delete
ptr
;
}
for
(
unsigned
int
i
=
0
;
i
<
edge_list
.
size
();
i
++
)
{
delete
edge_list
[
i
];
}
}
}
polygon/poly2tri/sweep/sweep_context.h
0 → 100644
View file @
dcb5d8f2
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SWEEP_CONTEXT_H
#define SWEEP_CONTEXT_H
#include <list>
#include <vector>
#include <cstddef>
namespace
p2t
{
// Inital triangle factor, seed triangle will extend 30% of
// PointSet width to both left and right.
const
double
kAlpha
=
0
.
3
;
struct
Point
;
class
Triangle
;
struct
Node
;
struct
Edge
;
class
AdvancingFront
;
class
SweepContext
{
public
:
/// Constructor
SweepContext
(
std
::
vector
<
Point
*>
polyline
);
/// Destructor
~
SweepContext
();
void
set_head
(
Point
*
p1
);
Point
*
head
();
void
set_tail
(
Point
*
p1
);
Point
*
tail
();
int
point_count
();
Node
&
LocateNode
(
Point
&
point
);
void
RemoveNode
(
Node
*
node
);
void
CreateAdvancingFront
(
std
::
vector
<
Node
*>
nodes
);
/// Try to map a node to all sides of this triangle that don't have a neighbor
void
MapTriangleToNodes
(
Triangle
&
t
);
void
AddToMap
(
Triangle
*
triangle
);
Point
*
GetPoint
(
const
int
&
index
);
Point
*
GetPoints
();
void
RemoveFromMap
(
Triangle
*
triangle
);
void
AddHole
(
std
::
vector
<
Point
*>
polyline
);
void
AddPoint
(
Point
*
point
);
AdvancingFront
*
front
();
void
MeshClean
(
Triangle
&
triangle
);
std
::
vector
<
Triangle
*>
GetTriangles
();
std
::
list
<
Triangle
*>
GetMap
();
std
::
vector
<
Edge
*>
edge_list
;
struct
Basin
{
Node
*
left_node
;
Node
*
bottom_node
;
Node
*
right_node
;
double
width
;
bool
left_highest
;
Basin
()
:
left_node
(
NULL
),
bottom_node
(
NULL
),
right_node
(
NULL
),
width
(
0
.
0
),
left_highest
(
false
)
{
}
void
Clear
()
{
left_node
=
NULL
;
bottom_node
=
NULL
;
right_node
=
NULL
;
width
=
0
.
0
;
left_highest
=
false
;
}
};
struct
EdgeEvent
{
Edge
*
constrained_edge
;
bool
right
;
EdgeEvent
()
:
constrained_edge
(
NULL
),
right
(
false
)
{
}
};
Basin
basin
;
EdgeEvent
edge_event
;
private
:
friend
class
Sweep
;
std
::
vector
<
Triangle
*>
triangles_
;
std
::
list
<
Triangle
*>
map_
;
std
::
vector
<
Point
*>
points_
;
// Advancing front
AdvancingFront
*
front_
;
// head point used with advancing front
Point
*
head_
;
// tail point used with advancing front
Point
*
tail_
;
Node
*
af_head_
,
*
af_middle_
,
*
af_tail_
;
void
InitTriangulation
();
void
InitEdges
(
std
::
vector
<
Point
*>
polyline
);
};
inline
AdvancingFront
*
SweepContext
::
front
()
{
return
front_
;
}
inline
int
SweepContext
::
point_count
()
{
return
points_
.
size
();
}
inline
void
SweepContext
::
set_head
(
Point
*
p1
)
{
head_
=
p1
;
}
inline
Point
*
SweepContext
::
head
()
{
return
head_
;
}
inline
void
SweepContext
::
set_tail
(
Point
*
p1
)
{
tail_
=
p1
;
}
inline
Point
*
SweepContext
::
tail
()
{
return
tail_
;
}
}
#endif
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