Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
wangsen
paddle_dbnet
Commits
26219d5f
Unverified
Commit
26219d5f
authored
Jul 17, 2020
by
shaohua.zhang
Committed by
GitHub
Jul 17, 2020
Browse files
Merge pull request #1 from PaddlePaddle/develop
update-2020-7-17
parents
0e8a3417
311c5997
Changes
270
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
5850 additions
and
0 deletions
+5850
-0
ppocr/postprocess/lanms/.gitignore
ppocr/postprocess/lanms/.gitignore
+1
-0
ppocr/postprocess/lanms/.ycm_extra_conf.py
ppocr/postprocess/lanms/.ycm_extra_conf.py
+140
-0
ppocr/postprocess/lanms/Makefile
ppocr/postprocess/lanms/Makefile
+13
-0
ppocr/postprocess/lanms/__init__.py
ppocr/postprocess/lanms/__init__.py
+20
-0
ppocr/postprocess/lanms/__main__.py
ppocr/postprocess/lanms/__main__.py
+10
-0
ppocr/postprocess/lanms/adaptor.cpp
ppocr/postprocess/lanms/adaptor.cpp
+61
-0
ppocr/postprocess/lanms/include/clipper/clipper.cpp
ppocr/postprocess/lanms/include/clipper/clipper.cpp
+4622
-0
ppocr/postprocess/lanms/include/clipper/clipper.hpp
ppocr/postprocess/lanms/include/clipper/clipper.hpp
+404
-0
ppocr/postprocess/lanms/include/pybind11/attr.h
ppocr/postprocess/lanms/include/pybind11/attr.h
+471
-0
ppocr/postprocess/lanms/include/pybind11/buffer_info.h
ppocr/postprocess/lanms/include/pybind11/buffer_info.h
+108
-0
No files found.
Too many changes to show.
To preserve performance only
270 of 270+
files are displayed.
Plain diff
Email patch
ppocr/postprocess/lanms/.gitignore
0 → 100644
View file @
26219d5f
adaptor.so
ppocr/postprocess/lanms/.ycm_extra_conf.py
0 → 100644
View file @
26219d5f
#!/usr/bin/env python
#
# Copyright (C) 2014 Google Inc.
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe 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 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe 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 YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import
os
import
sys
import
glob
import
ycm_core
# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
sys
.
path
.
append
(
os
.
path
.
dirname
(
__file__
))
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
))
from
plumbum.cmd
import
python_config
flags
=
[
'-Wall'
,
'-Wextra'
,
'-Wnon-virtual-dtor'
,
'-Winvalid-pch'
,
'-Wno-unused-local-typedefs'
,
'-std=c++11'
,
'-x'
,
'c++'
,
'-Iinclude'
,
]
+
python_config
(
'--cflags'
).
split
()
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags.
compilation_database_folder
=
''
if
os
.
path
.
exists
(
compilation_database_folder
):
database
=
ycm_core
.
CompilationDatabase
(
compilation_database_folder
)
else
:
database
=
None
SOURCE_EXTENSIONS
=
[
'.cpp'
,
'.cxx'
,
'.cc'
,
'.c'
,
'.m'
,
'.mm'
]
def
DirectoryOfThisScript
():
return
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
)
)
def
MakeRelativePathsInFlagsAbsolute
(
flags
,
working_directory
):
if
not
working_directory
:
return
list
(
flags
)
new_flags
=
[]
make_next_absolute
=
False
path_flags
=
[
'-isystem'
,
'-I'
,
'-iquote'
,
'--sysroot='
]
for
flag
in
flags
:
new_flag
=
flag
if
make_next_absolute
:
make_next_absolute
=
False
if
not
flag
.
startswith
(
'/'
):
new_flag
=
os
.
path
.
join
(
working_directory
,
flag
)
for
path_flag
in
path_flags
:
if
flag
==
path_flag
:
make_next_absolute
=
True
break
if
flag
.
startswith
(
path_flag
):
path
=
flag
[
len
(
path_flag
):
]
new_flag
=
path_flag
+
os
.
path
.
join
(
working_directory
,
path
)
break
if
new_flag
:
new_flags
.
append
(
new_flag
)
return
new_flags
def
IsHeaderFile
(
filename
):
extension
=
os
.
path
.
splitext
(
filename
)[
1
]
return
extension
in
[
'.h'
,
'.hxx'
,
'.hpp'
,
'.hh'
]
def
GetCompilationInfoForFile
(
filename
):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
if
IsHeaderFile
(
filename
):
basename
=
os
.
path
.
splitext
(
filename
)[
0
]
for
extension
in
SOURCE_EXTENSIONS
:
replacement_file
=
basename
+
extension
if
os
.
path
.
exists
(
replacement_file
):
compilation_info
=
database
.
GetCompilationInfoForFile
(
replacement_file
)
if
compilation_info
.
compiler_flags_
:
return
compilation_info
return
None
return
database
.
GetCompilationInfoForFile
(
filename
)
# This is the entry point; this function is called by ycmd to produce flags for
# a file.
def
FlagsForFile
(
filename
,
**
kwargs
):
if
database
:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info
=
GetCompilationInfoForFile
(
filename
)
if
not
compilation_info
:
return
None
final_flags
=
MakeRelativePathsInFlagsAbsolute
(
compilation_info
.
compiler_flags_
,
compilation_info
.
compiler_working_dir_
)
else
:
relative_to
=
DirectoryOfThisScript
()
final_flags
=
MakeRelativePathsInFlagsAbsolute
(
flags
,
relative_to
)
return
{
'flags'
:
final_flags
,
'do_cache'
:
True
}
ppocr/postprocess/lanms/Makefile
0 → 100644
View file @
26219d5f
CXXFLAGS
=
-I
include
-std
=
c++11
-O3
$(
shell
python3-config
--cflags
)
LDFLAGS
=
$(
shell
python3-config
--ldflags
)
DEPS
=
lanms.h
$(
shell
find include
-xtype
f
)
CXX_SOURCES
=
adaptor.cpp include/clipper/clipper.cpp
LIB_SO
=
adaptor.so
$(LIB_SO)
:
$(CXX_SOURCES) $(DEPS)
$(CXX)
-o
$@
$(CXXFLAGS)
$(LDFLAGS)
$(CXX_SOURCES)
--shared
-fPIC
clean
:
rm
-rf
$(LIB_SO)
ppocr/postprocess/lanms/__init__.py
0 → 100644
View file @
26219d5f
import
subprocess
import
os
import
numpy
as
np
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
))
if
subprocess
.
call
([
'make'
,
'-C'
,
BASE_DIR
])
!=
0
:
# return value
raise
RuntimeError
(
'Cannot compile lanms: {}'
.
format
(
BASE_DIR
))
def
merge_quadrangle_n9
(
polys
,
thres
=
0.3
,
precision
=
10000
):
from
.adaptor
import
merge_quadrangle_n9
as
nms_impl
if
len
(
polys
)
==
0
:
return
np
.
array
([],
dtype
=
'float32'
)
p
=
polys
.
copy
()
p
[:,:
8
]
*=
precision
ret
=
np
.
array
(
nms_impl
(
p
,
thres
),
dtype
=
'float32'
)
ret
[:,:
8
]
/=
precision
return
ret
ppocr/postprocess/lanms/__main__.py
0 → 100644
View file @
26219d5f
import
numpy
as
np
from
.
import
merge_quadrangle_n9
if
__name__
==
'__main__'
:
# unit square with confidence 1
q
=
np
.
array
([
0
,
0
,
0
,
1
,
1
,
1
,
1
,
0
,
1
],
dtype
=
'float32'
)
print
(
merge_quadrangle_n9
(
np
.
array
([
q
,
q
+
0.1
,
q
+
2
])))
ppocr/postprocess/lanms/adaptor.cpp
0 → 100644
View file @
26219d5f
#include "pybind11/pybind11.h"
#include "pybind11/numpy.h"
#include "pybind11/stl.h"
#include "pybind11/stl_bind.h"
#include "lanms.h"
namespace
py
=
pybind11
;
namespace
lanms_adaptor
{
std
::
vector
<
std
::
vector
<
float
>>
polys2floats
(
const
std
::
vector
<
lanms
::
Polygon
>
&
polys
)
{
std
::
vector
<
std
::
vector
<
float
>>
ret
;
for
(
size_t
i
=
0
;
i
<
polys
.
size
();
i
++
)
{
auto
&
p
=
polys
[
i
];
auto
&
poly
=
p
.
poly
;
ret
.
emplace_back
(
std
::
vector
<
float
>
{
float
(
poly
[
0
].
X
),
float
(
poly
[
0
].
Y
),
float
(
poly
[
1
].
X
),
float
(
poly
[
1
].
Y
),
float
(
poly
[
2
].
X
),
float
(
poly
[
2
].
Y
),
float
(
poly
[
3
].
X
),
float
(
poly
[
3
].
Y
),
float
(
p
.
score
),
});
}
return
ret
;
}
/**
*
* \param quad_n9 an n-by-9 numpy array, where first 8 numbers denote the
* quadrangle, and the last one is the score
* \param iou_threshold two quadrangles with iou score above this threshold
* will be merged
*
* \return an n-by-9 numpy array, the merged quadrangles
*/
std
::
vector
<
std
::
vector
<
float
>>
merge_quadrangle_n9
(
py
::
array_t
<
float
,
py
::
array
::
c_style
|
py
::
array
::
forcecast
>
quad_n9
,
float
iou_threshold
)
{
auto
pbuf
=
quad_n9
.
request
();
if
(
pbuf
.
ndim
!=
2
||
pbuf
.
shape
[
1
]
!=
9
)
throw
std
::
runtime_error
(
"quadrangles must have a shape of (n, 9)"
);
auto
n
=
pbuf
.
shape
[
0
];
auto
ptr
=
static_cast
<
float
*>
(
pbuf
.
ptr
);
return
polys2floats
(
lanms
::
merge_quadrangle_n9
(
ptr
,
n
,
iou_threshold
));
}
}
PYBIND11_PLUGIN
(
adaptor
)
{
py
::
module
m
(
"adaptor"
,
"NMS"
);
m
.
def
(
"merge_quadrangle_n9"
,
&
lanms_adaptor
::
merge_quadrangle_n9
,
"merge quadrangels"
);
return
m
.
ptr
();
}
ppocr/postprocess/lanms/include/clipper/clipper.cpp
0 → 100644
View file @
26219d5f
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.4.0 *
* Date : 2 July 2015 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2015 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
/*******************************************************************************
* *
* This is a translation of the Delphi Clipper library and the naming style *
* used has retained a Delphi flavour. *
* *
*******************************************************************************/
#include "clipper.hpp"
#include <cmath>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <cstring>
#include <cstdlib>
#include <ostream>
#include <functional>
namespace
ClipperLib
{
static
double
const
pi
=
3.141592653589793238
;
static
double
const
two_pi
=
pi
*
2
;
static
double
const
def_arc_tolerance
=
0.25
;
enum
Direction
{
dRightToLeft
,
dLeftToRight
};
static
int
const
Unassigned
=
-
1
;
//edge not currently 'owning' a solution
static
int
const
Skip
=
-
2
;
//edge that would otherwise close a path
#define HORIZONTAL (-1.0E+40)
#define TOLERANCE (1.0e-20)
#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
struct
TEdge
{
IntPoint
Bot
;
IntPoint
Curr
;
//current (updated for every new scanbeam)
IntPoint
Top
;
double
Dx
;
PolyType
PolyTyp
;
EdgeSide
Side
;
//side only refers to current side of solution poly
int
WindDelta
;
//1 or -1 depending on winding direction
int
WindCnt
;
int
WindCnt2
;
//winding count of the opposite polytype
int
OutIdx
;
TEdge
*
Next
;
TEdge
*
Prev
;
TEdge
*
NextInLML
;
TEdge
*
NextInAEL
;
TEdge
*
PrevInAEL
;
TEdge
*
NextInSEL
;
TEdge
*
PrevInSEL
;
};
struct
IntersectNode
{
TEdge
*
Edge1
;
TEdge
*
Edge2
;
IntPoint
Pt
;
};
struct
LocalMinimum
{
cInt
Y
;
TEdge
*
LeftBound
;
TEdge
*
RightBound
;
};
struct
OutPt
;
//OutRec: contains a path in the clipping solution. Edges in the AEL will
//carry a pointer to an OutRec when they are part of the clipping solution.
struct
OutRec
{
int
Idx
;
bool
IsHole
;
bool
IsOpen
;
OutRec
*
FirstLeft
;
//see comments in clipper.pas
PolyNode
*
PolyNd
;
OutPt
*
Pts
;
OutPt
*
BottomPt
;
};
struct
OutPt
{
int
Idx
;
IntPoint
Pt
;
OutPt
*
Next
;
OutPt
*
Prev
;
};
struct
Join
{
OutPt
*
OutPt1
;
OutPt
*
OutPt2
;
IntPoint
OffPt
;
};
struct
LocMinSorter
{
inline
bool
operator
()(
const
LocalMinimum
&
locMin1
,
const
LocalMinimum
&
locMin2
)
{
return
locMin2
.
Y
<
locMin1
.
Y
;
}
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
inline
cInt
Round
(
double
val
)
{
if
((
val
<
0
))
return
static_cast
<
cInt
>
(
val
-
0.5
);
else
return
static_cast
<
cInt
>
(
val
+
0.5
);
}
//------------------------------------------------------------------------------
inline
cInt
Abs
(
cInt
val
)
{
return
val
<
0
?
-
val
:
val
;
}
//------------------------------------------------------------------------------
// PolyTree methods ...
//------------------------------------------------------------------------------
void
PolyTree
::
Clear
()
{
for
(
PolyNodes
::
size_type
i
=
0
;
i
<
AllNodes
.
size
();
++
i
)
delete
AllNodes
[
i
];
AllNodes
.
resize
(
0
);
Childs
.
resize
(
0
);
}
//------------------------------------------------------------------------------
PolyNode
*
PolyTree
::
GetFirst
()
const
{
if
(
!
Childs
.
empty
())
return
Childs
[
0
];
else
return
0
;
}
//------------------------------------------------------------------------------
int
PolyTree
::
Total
()
const
{
int
result
=
(
int
)
AllNodes
.
size
();
//with negative offsets, ignore the hidden outer polygon ...
if
(
result
>
0
&&
Childs
[
0
]
!=
AllNodes
[
0
])
result
--
;
return
result
;
}
//------------------------------------------------------------------------------
// PolyNode methods ...
//------------------------------------------------------------------------------
PolyNode
::
PolyNode
()
:
Childs
(),
Parent
(
0
),
Index
(
0
),
m_IsOpen
(
false
)
{
}
//------------------------------------------------------------------------------
int
PolyNode
::
ChildCount
()
const
{
return
(
int
)
Childs
.
size
();
}
//------------------------------------------------------------------------------
void
PolyNode
::
AddChild
(
PolyNode
&
child
)
{
unsigned
cnt
=
(
unsigned
)
Childs
.
size
();
Childs
.
push_back
(
&
child
);
child
.
Parent
=
this
;
child
.
Index
=
cnt
;
}
//------------------------------------------------------------------------------
PolyNode
*
PolyNode
::
GetNext
()
const
{
if
(
!
Childs
.
empty
())
return
Childs
[
0
];
else
return
GetNextSiblingUp
();
}
//------------------------------------------------------------------------------
PolyNode
*
PolyNode
::
GetNextSiblingUp
()
const
{
if
(
!
Parent
)
//protects against PolyTree.GetNextSiblingUp()
return
0
;
else
if
(
Index
==
Parent
->
Childs
.
size
()
-
1
)
return
Parent
->
GetNextSiblingUp
();
else
return
Parent
->
Childs
[
Index
+
1
];
}
//------------------------------------------------------------------------------
bool
PolyNode
::
IsHole
()
const
{
bool
result
=
true
;
PolyNode
*
node
=
Parent
;
while
(
node
)
{
result
=
!
result
;
node
=
node
->
Parent
;
}
return
result
;
}
//------------------------------------------------------------------------------
bool
PolyNode
::
IsOpen
()
const
{
return
m_IsOpen
;
}
//------------------------------------------------------------------------------
#ifndef use_int32
//------------------------------------------------------------------------------
// Int128 class (enables safe math on signed 64bit integers)
// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1
// Int128 val2((long64)9223372036854775807);
// Int128 val3 = val1 * val2;
// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37)
//------------------------------------------------------------------------------
class
Int128
{
public:
ulong64
lo
;
long64
hi
;
Int128
(
long64
_lo
=
0
)
{
lo
=
(
ulong64
)
_lo
;
if
(
_lo
<
0
)
hi
=
-
1
;
else
hi
=
0
;
}
Int128
(
const
Int128
&
val
)
:
lo
(
val
.
lo
),
hi
(
val
.
hi
){}
Int128
(
const
long64
&
_hi
,
const
ulong64
&
_lo
)
:
lo
(
_lo
),
hi
(
_hi
){}
Int128
&
operator
=
(
const
long64
&
val
)
{
lo
=
(
ulong64
)
val
;
if
(
val
<
0
)
hi
=
-
1
;
else
hi
=
0
;
return
*
this
;
}
bool
operator
==
(
const
Int128
&
val
)
const
{
return
(
hi
==
val
.
hi
&&
lo
==
val
.
lo
);}
bool
operator
!=
(
const
Int128
&
val
)
const
{
return
!
(
*
this
==
val
);}
bool
operator
>
(
const
Int128
&
val
)
const
{
if
(
hi
!=
val
.
hi
)
return
hi
>
val
.
hi
;
else
return
lo
>
val
.
lo
;
}
bool
operator
<
(
const
Int128
&
val
)
const
{
if
(
hi
!=
val
.
hi
)
return
hi
<
val
.
hi
;
else
return
lo
<
val
.
lo
;
}
bool
operator
>=
(
const
Int128
&
val
)
const
{
return
!
(
*
this
<
val
);}
bool
operator
<=
(
const
Int128
&
val
)
const
{
return
!
(
*
this
>
val
);}
Int128
&
operator
+=
(
const
Int128
&
rhs
)
{
hi
+=
rhs
.
hi
;
lo
+=
rhs
.
lo
;
if
(
lo
<
rhs
.
lo
)
hi
++
;
return
*
this
;
}
Int128
operator
+
(
const
Int128
&
rhs
)
const
{
Int128
result
(
*
this
);
result
+=
rhs
;
return
result
;
}
Int128
&
operator
-=
(
const
Int128
&
rhs
)
{
*
this
+=
-
rhs
;
return
*
this
;
}
Int128
operator
-
(
const
Int128
&
rhs
)
const
{
Int128
result
(
*
this
);
result
-=
rhs
;
return
result
;
}
Int128
operator
-
()
const
//unary negation
{
if
(
lo
==
0
)
return
Int128
(
-
hi
,
0
);
else
return
Int128
(
~
hi
,
~
lo
+
1
);
}
operator
double
()
const
{
const
double
shift64
=
18446744073709551616.0
;
//2^64
if
(
hi
<
0
)
{
if
(
lo
==
0
)
return
(
double
)
hi
*
shift64
;
else
return
-
(
double
)(
~
lo
+
~
hi
*
shift64
);
}
else
return
(
double
)(
lo
+
hi
*
shift64
);
}
};
//------------------------------------------------------------------------------
Int128
Int128Mul
(
long64
lhs
,
long64
rhs
)
{
bool
negate
=
(
lhs
<
0
)
!=
(
rhs
<
0
);
if
(
lhs
<
0
)
lhs
=
-
lhs
;
ulong64
int1Hi
=
ulong64
(
lhs
)
>>
32
;
ulong64
int1Lo
=
ulong64
(
lhs
&
0xFFFFFFFF
);
if
(
rhs
<
0
)
rhs
=
-
rhs
;
ulong64
int2Hi
=
ulong64
(
rhs
)
>>
32
;
ulong64
int2Lo
=
ulong64
(
rhs
&
0xFFFFFFFF
);
//nb: see comments in clipper.pas
ulong64
a
=
int1Hi
*
int2Hi
;
ulong64
b
=
int1Lo
*
int2Lo
;
ulong64
c
=
int1Hi
*
int2Lo
+
int1Lo
*
int2Hi
;
Int128
tmp
;
tmp
.
hi
=
long64
(
a
+
(
c
>>
32
));
tmp
.
lo
=
long64
(
c
<<
32
);
tmp
.
lo
+=
long64
(
b
);
if
(
tmp
.
lo
<
b
)
tmp
.
hi
++
;
if
(
negate
)
tmp
=
-
tmp
;
return
tmp
;
};
#endif
//------------------------------------------------------------------------------
// Miscellaneous global functions
//------------------------------------------------------------------------------
bool
Orientation
(
const
Path
&
poly
)
{
return
Area
(
poly
)
>=
0
;
}
//------------------------------------------------------------------------------
double
Area
(
const
Path
&
poly
)
{
int
size
=
(
int
)
poly
.
size
();
if
(
size
<
3
)
return
0
;
double
a
=
0
;
for
(
int
i
=
0
,
j
=
size
-
1
;
i
<
size
;
++
i
)
{
a
+=
((
double
)
poly
[
j
].
X
+
poly
[
i
].
X
)
*
((
double
)
poly
[
j
].
Y
-
poly
[
i
].
Y
);
j
=
i
;
}
return
-
a
*
0.5
;
}
//------------------------------------------------------------------------------
double
Area
(
const
OutPt
*
op
)
{
const
OutPt
*
startOp
=
op
;
if
(
!
op
)
return
0
;
double
a
=
0
;
do
{
a
+=
(
double
)(
op
->
Prev
->
Pt
.
X
+
op
->
Pt
.
X
)
*
(
double
)(
op
->
Prev
->
Pt
.
Y
-
op
->
Pt
.
Y
);
op
=
op
->
Next
;
}
while
(
op
!=
startOp
);
return
a
*
0.5
;
}
//------------------------------------------------------------------------------
double
Area
(
const
OutRec
&
outRec
)
{
return
Area
(
outRec
.
Pts
);
}
//------------------------------------------------------------------------------
bool
PointIsVertex
(
const
IntPoint
&
Pt
,
OutPt
*
pp
)
{
OutPt
*
pp2
=
pp
;
do
{
if
(
pp2
->
Pt
==
Pt
)
return
true
;
pp2
=
pp2
->
Next
;
}
while
(
pp2
!=
pp
);
return
false
;
}
//------------------------------------------------------------------------------
//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
int
PointInPolygon
(
const
IntPoint
&
pt
,
const
Path
&
path
)
{
//returns 0 if false, +1 if true, -1 if pt ON polygon boundary
int
result
=
0
;
size_t
cnt
=
path
.
size
();
if
(
cnt
<
3
)
return
0
;
IntPoint
ip
=
path
[
0
];
for
(
size_t
i
=
1
;
i
<=
cnt
;
++
i
)
{
IntPoint
ipNext
=
(
i
==
cnt
?
path
[
0
]
:
path
[
i
]);
if
(
ipNext
.
Y
==
pt
.
Y
)
{
if
((
ipNext
.
X
==
pt
.
X
)
||
(
ip
.
Y
==
pt
.
Y
&&
((
ipNext
.
X
>
pt
.
X
)
==
(
ip
.
X
<
pt
.
X
))))
return
-
1
;
}
if
((
ip
.
Y
<
pt
.
Y
)
!=
(
ipNext
.
Y
<
pt
.
Y
))
{
if
(
ip
.
X
>=
pt
.
X
)
{
if
(
ipNext
.
X
>
pt
.
X
)
result
=
1
-
result
;
else
{
double
d
=
(
double
)(
ip
.
X
-
pt
.
X
)
*
(
ipNext
.
Y
-
pt
.
Y
)
-
(
double
)(
ipNext
.
X
-
pt
.
X
)
*
(
ip
.
Y
-
pt
.
Y
);
if
(
!
d
)
return
-
1
;
if
((
d
>
0
)
==
(
ipNext
.
Y
>
ip
.
Y
))
result
=
1
-
result
;
}
}
else
{
if
(
ipNext
.
X
>
pt
.
X
)
{
double
d
=
(
double
)(
ip
.
X
-
pt
.
X
)
*
(
ipNext
.
Y
-
pt
.
Y
)
-
(
double
)(
ipNext
.
X
-
pt
.
X
)
*
(
ip
.
Y
-
pt
.
Y
);
if
(
!
d
)
return
-
1
;
if
((
d
>
0
)
==
(
ipNext
.
Y
>
ip
.
Y
))
result
=
1
-
result
;
}
}
}
ip
=
ipNext
;
}
return
result
;
}
//------------------------------------------------------------------------------
int
PointInPolygon
(
const
IntPoint
&
pt
,
OutPt
*
op
)
{
//returns 0 if false, +1 if true, -1 if pt ON polygon boundary
int
result
=
0
;
OutPt
*
startOp
=
op
;
for
(;;)
{
if
(
op
->
Next
->
Pt
.
Y
==
pt
.
Y
)
{
if
((
op
->
Next
->
Pt
.
X
==
pt
.
X
)
||
(
op
->
Pt
.
Y
==
pt
.
Y
&&
((
op
->
Next
->
Pt
.
X
>
pt
.
X
)
==
(
op
->
Pt
.
X
<
pt
.
X
))))
return
-
1
;
}
if
((
op
->
Pt
.
Y
<
pt
.
Y
)
!=
(
op
->
Next
->
Pt
.
Y
<
pt
.
Y
))
{
if
(
op
->
Pt
.
X
>=
pt
.
X
)
{
if
(
op
->
Next
->
Pt
.
X
>
pt
.
X
)
result
=
1
-
result
;
else
{
double
d
=
(
double
)(
op
->
Pt
.
X
-
pt
.
X
)
*
(
op
->
Next
->
Pt
.
Y
-
pt
.
Y
)
-
(
double
)(
op
->
Next
->
Pt
.
X
-
pt
.
X
)
*
(
op
->
Pt
.
Y
-
pt
.
Y
);
if
(
!
d
)
return
-
1
;
if
((
d
>
0
)
==
(
op
->
Next
->
Pt
.
Y
>
op
->
Pt
.
Y
))
result
=
1
-
result
;
}
}
else
{
if
(
op
->
Next
->
Pt
.
X
>
pt
.
X
)
{
double
d
=
(
double
)(
op
->
Pt
.
X
-
pt
.
X
)
*
(
op
->
Next
->
Pt
.
Y
-
pt
.
Y
)
-
(
double
)(
op
->
Next
->
Pt
.
X
-
pt
.
X
)
*
(
op
->
Pt
.
Y
-
pt
.
Y
);
if
(
!
d
)
return
-
1
;
if
((
d
>
0
)
==
(
op
->
Next
->
Pt
.
Y
>
op
->
Pt
.
Y
))
result
=
1
-
result
;
}
}
}
op
=
op
->
Next
;
if
(
startOp
==
op
)
break
;
}
return
result
;
}
//------------------------------------------------------------------------------
bool
Poly2ContainsPoly1
(
OutPt
*
OutPt1
,
OutPt
*
OutPt2
)
{
OutPt
*
op
=
OutPt1
;
do
{
//nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
int
res
=
PointInPolygon
(
op
->
Pt
,
OutPt2
);
if
(
res
>=
0
)
return
res
>
0
;
op
=
op
->
Next
;
}
while
(
op
!=
OutPt1
);
return
true
;
}
//----------------------------------------------------------------------
bool
SlopesEqual
(
const
TEdge
&
e1
,
const
TEdge
&
e2
,
bool
UseFullInt64Range
)
{
#ifndef use_int32
if
(
UseFullInt64Range
)
return
Int128Mul
(
e1
.
Top
.
Y
-
e1
.
Bot
.
Y
,
e2
.
Top
.
X
-
e2
.
Bot
.
X
)
==
Int128Mul
(
e1
.
Top
.
X
-
e1
.
Bot
.
X
,
e2
.
Top
.
Y
-
e2
.
Bot
.
Y
);
else
#endif
return
(
e1
.
Top
.
Y
-
e1
.
Bot
.
Y
)
*
(
e2
.
Top
.
X
-
e2
.
Bot
.
X
)
==
(
e1
.
Top
.
X
-
e1
.
Bot
.
X
)
*
(
e2
.
Top
.
Y
-
e2
.
Bot
.
Y
);
}
//------------------------------------------------------------------------------
bool
SlopesEqual
(
const
IntPoint
pt1
,
const
IntPoint
pt2
,
const
IntPoint
pt3
,
bool
UseFullInt64Range
)
{
#ifndef use_int32
if
(
UseFullInt64Range
)
return
Int128Mul
(
pt1
.
Y
-
pt2
.
Y
,
pt2
.
X
-
pt3
.
X
)
==
Int128Mul
(
pt1
.
X
-
pt2
.
X
,
pt2
.
Y
-
pt3
.
Y
);
else
#endif
return
(
pt1
.
Y
-
pt2
.
Y
)
*
(
pt2
.
X
-
pt3
.
X
)
==
(
pt1
.
X
-
pt2
.
X
)
*
(
pt2
.
Y
-
pt3
.
Y
);
}
//------------------------------------------------------------------------------
bool
SlopesEqual
(
const
IntPoint
pt1
,
const
IntPoint
pt2
,
const
IntPoint
pt3
,
const
IntPoint
pt4
,
bool
UseFullInt64Range
)
{
#ifndef use_int32
if
(
UseFullInt64Range
)
return
Int128Mul
(
pt1
.
Y
-
pt2
.
Y
,
pt3
.
X
-
pt4
.
X
)
==
Int128Mul
(
pt1
.
X
-
pt2
.
X
,
pt3
.
Y
-
pt4
.
Y
);
else
#endif
return
(
pt1
.
Y
-
pt2
.
Y
)
*
(
pt3
.
X
-
pt4
.
X
)
==
(
pt1
.
X
-
pt2
.
X
)
*
(
pt3
.
Y
-
pt4
.
Y
);
}
//------------------------------------------------------------------------------
inline
bool
IsHorizontal
(
TEdge
&
e
)
{
return
e
.
Dx
==
HORIZONTAL
;
}
//------------------------------------------------------------------------------
inline
double
GetDx
(
const
IntPoint
pt1
,
const
IntPoint
pt2
)
{
return
(
pt1
.
Y
==
pt2
.
Y
)
?
HORIZONTAL
:
(
double
)(
pt2
.
X
-
pt1
.
X
)
/
(
pt2
.
Y
-
pt1
.
Y
);
}
//---------------------------------------------------------------------------
inline
void
SetDx
(
TEdge
&
e
)
{
cInt
dy
=
(
e
.
Top
.
Y
-
e
.
Bot
.
Y
);
if
(
dy
==
0
)
e
.
Dx
=
HORIZONTAL
;
else
e
.
Dx
=
(
double
)(
e
.
Top
.
X
-
e
.
Bot
.
X
)
/
dy
;
}
//---------------------------------------------------------------------------
inline
void
SwapSides
(
TEdge
&
Edge1
,
TEdge
&
Edge2
)
{
EdgeSide
Side
=
Edge1
.
Side
;
Edge1
.
Side
=
Edge2
.
Side
;
Edge2
.
Side
=
Side
;
}
//------------------------------------------------------------------------------
inline
void
SwapPolyIndexes
(
TEdge
&
Edge1
,
TEdge
&
Edge2
)
{
int
OutIdx
=
Edge1
.
OutIdx
;
Edge1
.
OutIdx
=
Edge2
.
OutIdx
;
Edge2
.
OutIdx
=
OutIdx
;
}
//------------------------------------------------------------------------------
inline
cInt
TopX
(
TEdge
&
edge
,
const
cInt
currentY
)
{
return
(
currentY
==
edge
.
Top
.
Y
)
?
edge
.
Top
.
X
:
edge
.
Bot
.
X
+
Round
(
edge
.
Dx
*
(
currentY
-
edge
.
Bot
.
Y
));
}
//------------------------------------------------------------------------------
void
IntersectPoint
(
TEdge
&
Edge1
,
TEdge
&
Edge2
,
IntPoint
&
ip
)
{
#ifdef use_xyz
ip
.
Z
=
0
;
#endif
double
b1
,
b2
;
if
(
Edge1
.
Dx
==
Edge2
.
Dx
)
{
ip
.
Y
=
Edge1
.
Curr
.
Y
;
ip
.
X
=
TopX
(
Edge1
,
ip
.
Y
);
return
;
}
else
if
(
Edge1
.
Dx
==
0
)
{
ip
.
X
=
Edge1
.
Bot
.
X
;
if
(
IsHorizontal
(
Edge2
))
ip
.
Y
=
Edge2
.
Bot
.
Y
;
else
{
b2
=
Edge2
.
Bot
.
Y
-
(
Edge2
.
Bot
.
X
/
Edge2
.
Dx
);
ip
.
Y
=
Round
(
ip
.
X
/
Edge2
.
Dx
+
b2
);
}
}
else
if
(
Edge2
.
Dx
==
0
)
{
ip
.
X
=
Edge2
.
Bot
.
X
;
if
(
IsHorizontal
(
Edge1
))
ip
.
Y
=
Edge1
.
Bot
.
Y
;
else
{
b1
=
Edge1
.
Bot
.
Y
-
(
Edge1
.
Bot
.
X
/
Edge1
.
Dx
);
ip
.
Y
=
Round
(
ip
.
X
/
Edge1
.
Dx
+
b1
);
}
}
else
{
b1
=
Edge1
.
Bot
.
X
-
Edge1
.
Bot
.
Y
*
Edge1
.
Dx
;
b2
=
Edge2
.
Bot
.
X
-
Edge2
.
Bot
.
Y
*
Edge2
.
Dx
;
double
q
=
(
b2
-
b1
)
/
(
Edge1
.
Dx
-
Edge2
.
Dx
);
ip
.
Y
=
Round
(
q
);
if
(
std
::
fabs
(
Edge1
.
Dx
)
<
std
::
fabs
(
Edge2
.
Dx
))
ip
.
X
=
Round
(
Edge1
.
Dx
*
q
+
b1
);
else
ip
.
X
=
Round
(
Edge2
.
Dx
*
q
+
b2
);
}
if
(
ip
.
Y
<
Edge1
.
Top
.
Y
||
ip
.
Y
<
Edge2
.
Top
.
Y
)
{
if
(
Edge1
.
Top
.
Y
>
Edge2
.
Top
.
Y
)
ip
.
Y
=
Edge1
.
Top
.
Y
;
else
ip
.
Y
=
Edge2
.
Top
.
Y
;
if
(
std
::
fabs
(
Edge1
.
Dx
)
<
std
::
fabs
(
Edge2
.
Dx
))
ip
.
X
=
TopX
(
Edge1
,
ip
.
Y
);
else
ip
.
X
=
TopX
(
Edge2
,
ip
.
Y
);
}
//finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
if
(
ip
.
Y
>
Edge1
.
Curr
.
Y
)
{
ip
.
Y
=
Edge1
.
Curr
.
Y
;
//use the more vertical edge to derive X ...
if
(
std
::
fabs
(
Edge1
.
Dx
)
>
std
::
fabs
(
Edge2
.
Dx
))
ip
.
X
=
TopX
(
Edge2
,
ip
.
Y
);
else
ip
.
X
=
TopX
(
Edge1
,
ip
.
Y
);
}
}
//------------------------------------------------------------------------------
void
ReversePolyPtLinks
(
OutPt
*
pp
)
{
if
(
!
pp
)
return
;
OutPt
*
pp1
,
*
pp2
;
pp1
=
pp
;
do
{
pp2
=
pp1
->
Next
;
pp1
->
Next
=
pp1
->
Prev
;
pp1
->
Prev
=
pp2
;
pp1
=
pp2
;
}
while
(
pp1
!=
pp
);
}
//------------------------------------------------------------------------------
void
DisposeOutPts
(
OutPt
*&
pp
)
{
if
(
pp
==
0
)
return
;
pp
->
Prev
->
Next
=
0
;
while
(
pp
)
{
OutPt
*
tmpPp
=
pp
;
pp
=
pp
->
Next
;
delete
tmpPp
;
}
}
//------------------------------------------------------------------------------
inline
void
InitEdge
(
TEdge
*
e
,
TEdge
*
eNext
,
TEdge
*
ePrev
,
const
IntPoint
&
Pt
)
{
std
::
memset
(
e
,
0
,
sizeof
(
TEdge
));
e
->
Next
=
eNext
;
e
->
Prev
=
ePrev
;
e
->
Curr
=
Pt
;
e
->
OutIdx
=
Unassigned
;
}
//------------------------------------------------------------------------------
void
InitEdge2
(
TEdge
&
e
,
PolyType
Pt
)
{
if
(
e
.
Curr
.
Y
>=
e
.
Next
->
Curr
.
Y
)
{
e
.
Bot
=
e
.
Curr
;
e
.
Top
=
e
.
Next
->
Curr
;
}
else
{
e
.
Top
=
e
.
Curr
;
e
.
Bot
=
e
.
Next
->
Curr
;
}
SetDx
(
e
);
e
.
PolyTyp
=
Pt
;
}
//------------------------------------------------------------------------------
TEdge
*
RemoveEdge
(
TEdge
*
e
)
{
//removes e from double_linked_list (but without removing from memory)
e
->
Prev
->
Next
=
e
->
Next
;
e
->
Next
->
Prev
=
e
->
Prev
;
TEdge
*
result
=
e
->
Next
;
e
->
Prev
=
0
;
//flag as removed (see ClipperBase.Clear)
return
result
;
}
//------------------------------------------------------------------------------
inline
void
ReverseHorizontal
(
TEdge
&
e
)
{
//swap horizontal edges' Top and Bottom x's so they follow the natural
//progression of the bounds - ie so their xbots will align with the
//adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
std
::
swap
(
e
.
Top
.
X
,
e
.
Bot
.
X
);
#ifdef use_xyz
std
::
swap
(
e
.
Top
.
Z
,
e
.
Bot
.
Z
);
#endif
}
//------------------------------------------------------------------------------
void
SwapPoints
(
IntPoint
&
pt1
,
IntPoint
&
pt2
)
{
IntPoint
tmp
=
pt1
;
pt1
=
pt2
;
pt2
=
tmp
;
}
//------------------------------------------------------------------------------
bool
GetOverlapSegment
(
IntPoint
pt1a
,
IntPoint
pt1b
,
IntPoint
pt2a
,
IntPoint
pt2b
,
IntPoint
&
pt1
,
IntPoint
&
pt2
)
{
//precondition: segments are Collinear.
if
(
Abs
(
pt1a
.
X
-
pt1b
.
X
)
>
Abs
(
pt1a
.
Y
-
pt1b
.
Y
))
{
if
(
pt1a
.
X
>
pt1b
.
X
)
SwapPoints
(
pt1a
,
pt1b
);
if
(
pt2a
.
X
>
pt2b
.
X
)
SwapPoints
(
pt2a
,
pt2b
);
if
(
pt1a
.
X
>
pt2a
.
X
)
pt1
=
pt1a
;
else
pt1
=
pt2a
;
if
(
pt1b
.
X
<
pt2b
.
X
)
pt2
=
pt1b
;
else
pt2
=
pt2b
;
return
pt1
.
X
<
pt2
.
X
;
}
else
{
if
(
pt1a
.
Y
<
pt1b
.
Y
)
SwapPoints
(
pt1a
,
pt1b
);
if
(
pt2a
.
Y
<
pt2b
.
Y
)
SwapPoints
(
pt2a
,
pt2b
);
if
(
pt1a
.
Y
<
pt2a
.
Y
)
pt1
=
pt1a
;
else
pt1
=
pt2a
;
if
(
pt1b
.
Y
>
pt2b
.
Y
)
pt2
=
pt1b
;
else
pt2
=
pt2b
;
return
pt1
.
Y
>
pt2
.
Y
;
}
}
//------------------------------------------------------------------------------
bool
FirstIsBottomPt
(
const
OutPt
*
btmPt1
,
const
OutPt
*
btmPt2
)
{
OutPt
*
p
=
btmPt1
->
Prev
;
while
((
p
->
Pt
==
btmPt1
->
Pt
)
&&
(
p
!=
btmPt1
))
p
=
p
->
Prev
;
double
dx1p
=
std
::
fabs
(
GetDx
(
btmPt1
->
Pt
,
p
->
Pt
));
p
=
btmPt1
->
Next
;
while
((
p
->
Pt
==
btmPt1
->
Pt
)
&&
(
p
!=
btmPt1
))
p
=
p
->
Next
;
double
dx1n
=
std
::
fabs
(
GetDx
(
btmPt1
->
Pt
,
p
->
Pt
));
p
=
btmPt2
->
Prev
;
while
((
p
->
Pt
==
btmPt2
->
Pt
)
&&
(
p
!=
btmPt2
))
p
=
p
->
Prev
;
double
dx2p
=
std
::
fabs
(
GetDx
(
btmPt2
->
Pt
,
p
->
Pt
));
p
=
btmPt2
->
Next
;
while
((
p
->
Pt
==
btmPt2
->
Pt
)
&&
(
p
!=
btmPt2
))
p
=
p
->
Next
;
double
dx2n
=
std
::
fabs
(
GetDx
(
btmPt2
->
Pt
,
p
->
Pt
));
if
(
std
::
max
(
dx1p
,
dx1n
)
==
std
::
max
(
dx2p
,
dx2n
)
&&
std
::
min
(
dx1p
,
dx1n
)
==
std
::
min
(
dx2p
,
dx2n
))
return
Area
(
btmPt1
)
>
0
;
//if otherwise identical use orientation
else
return
(
dx1p
>=
dx2p
&&
dx1p
>=
dx2n
)
||
(
dx1n
>=
dx2p
&&
dx1n
>=
dx2n
);
}
//------------------------------------------------------------------------------
OutPt
*
GetBottomPt
(
OutPt
*
pp
)
{
OutPt
*
dups
=
0
;
OutPt
*
p
=
pp
->
Next
;
while
(
p
!=
pp
)
{
if
(
p
->
Pt
.
Y
>
pp
->
Pt
.
Y
)
{
pp
=
p
;
dups
=
0
;
}
else
if
(
p
->
Pt
.
Y
==
pp
->
Pt
.
Y
&&
p
->
Pt
.
X
<=
pp
->
Pt
.
X
)
{
if
(
p
->
Pt
.
X
<
pp
->
Pt
.
X
)
{
dups
=
0
;
pp
=
p
;
}
else
{
if
(
p
->
Next
!=
pp
&&
p
->
Prev
!=
pp
)
dups
=
p
;
}
}
p
=
p
->
Next
;
}
if
(
dups
)
{
//there appears to be at least 2 vertices at BottomPt so ...
while
(
dups
!=
p
)
{
if
(
!
FirstIsBottomPt
(
p
,
dups
))
pp
=
dups
;
dups
=
dups
->
Next
;
while
(
dups
->
Pt
!=
pp
->
Pt
)
dups
=
dups
->
Next
;
}
}
return
pp
;
}
//------------------------------------------------------------------------------
bool
Pt2IsBetweenPt1AndPt3
(
const
IntPoint
pt1
,
const
IntPoint
pt2
,
const
IntPoint
pt3
)
{
if
((
pt1
==
pt3
)
||
(
pt1
==
pt2
)
||
(
pt3
==
pt2
))
return
false
;
else
if
(
pt1
.
X
!=
pt3
.
X
)
return
(
pt2
.
X
>
pt1
.
X
)
==
(
pt2
.
X
<
pt3
.
X
);
else
return
(
pt2
.
Y
>
pt1
.
Y
)
==
(
pt2
.
Y
<
pt3
.
Y
);
}
//------------------------------------------------------------------------------
bool
HorzSegmentsOverlap
(
cInt
seg1a
,
cInt
seg1b
,
cInt
seg2a
,
cInt
seg2b
)
{
if
(
seg1a
>
seg1b
)
std
::
swap
(
seg1a
,
seg1b
);
if
(
seg2a
>
seg2b
)
std
::
swap
(
seg2a
,
seg2b
);
return
(
seg1a
<
seg2b
)
&&
(
seg2a
<
seg1b
);
}
//------------------------------------------------------------------------------
// ClipperBase class methods ...
//------------------------------------------------------------------------------
ClipperBase
::
ClipperBase
()
//constructor
{
m_CurrentLM
=
m_MinimaList
.
begin
();
//begin() == end() here
m_UseFullRange
=
false
;
}
//------------------------------------------------------------------------------
ClipperBase
::~
ClipperBase
()
//destructor
{
Clear
();
}
//------------------------------------------------------------------------------
void
RangeTest
(
const
IntPoint
&
Pt
,
bool
&
useFullRange
)
{
if
(
useFullRange
)
{
if
(
Pt
.
X
>
hiRange
||
Pt
.
Y
>
hiRange
||
-
Pt
.
X
>
hiRange
||
-
Pt
.
Y
>
hiRange
)
throw
clipperException
(
"Coordinate outside allowed range"
);
}
else
if
(
Pt
.
X
>
loRange
||
Pt
.
Y
>
loRange
||
-
Pt
.
X
>
loRange
||
-
Pt
.
Y
>
loRange
)
{
useFullRange
=
true
;
RangeTest
(
Pt
,
useFullRange
);
}
}
//------------------------------------------------------------------------------
TEdge
*
FindNextLocMin
(
TEdge
*
E
)
{
for
(;;)
{
while
(
E
->
Bot
!=
E
->
Prev
->
Bot
||
E
->
Curr
==
E
->
Top
)
E
=
E
->
Next
;
if
(
!
IsHorizontal
(
*
E
)
&&
!
IsHorizontal
(
*
E
->
Prev
))
break
;
while
(
IsHorizontal
(
*
E
->
Prev
))
E
=
E
->
Prev
;
TEdge
*
E2
=
E
;
while
(
IsHorizontal
(
*
E
))
E
=
E
->
Next
;
if
(
E
->
Top
.
Y
==
E
->
Prev
->
Bot
.
Y
)
continue
;
//ie just an intermediate horz.
if
(
E2
->
Prev
->
Bot
.
X
<
E
->
Bot
.
X
)
E
=
E2
;
break
;
}
return
E
;
}
//------------------------------------------------------------------------------
TEdge
*
ClipperBase
::
ProcessBound
(
TEdge
*
E
,
bool
NextIsForward
)
{
TEdge
*
Result
=
E
;
TEdge
*
Horz
=
0
;
if
(
E
->
OutIdx
==
Skip
)
{
//if edges still remain in the current bound beyond the skip edge then
//create another LocMin and call ProcessBound once more
if
(
NextIsForward
)
{
while
(
E
->
Top
.
Y
==
E
->
Next
->
Bot
.
Y
)
E
=
E
->
Next
;
//don't include top horizontals when parsing a bound a second time,
//they will be contained in the opposite bound ...
while
(
E
!=
Result
&&
IsHorizontal
(
*
E
))
E
=
E
->
Prev
;
}
else
{
while
(
E
->
Top
.
Y
==
E
->
Prev
->
Bot
.
Y
)
E
=
E
->
Prev
;
while
(
E
!=
Result
&&
IsHorizontal
(
*
E
))
E
=
E
->
Next
;
}
if
(
E
==
Result
)
{
if
(
NextIsForward
)
Result
=
E
->
Next
;
else
Result
=
E
->
Prev
;
}
else
{
//there are more edges in the bound beyond result starting with E
if
(
NextIsForward
)
E
=
Result
->
Next
;
else
E
=
Result
->
Prev
;
MinimaList
::
value_type
locMin
;
locMin
.
Y
=
E
->
Bot
.
Y
;
locMin
.
LeftBound
=
0
;
locMin
.
RightBound
=
E
;
E
->
WindDelta
=
0
;
Result
=
ProcessBound
(
E
,
NextIsForward
);
m_MinimaList
.
push_back
(
locMin
);
}
return
Result
;
}
TEdge
*
EStart
;
if
(
IsHorizontal
(
*
E
))
{
//We need to be careful with open paths because this may not be a
//true local minima (ie E may be following a skip edge).
//Also, consecutive horz. edges may start heading left before going right.
if
(
NextIsForward
)
EStart
=
E
->
Prev
;
else
EStart
=
E
->
Next
;
if
(
IsHorizontal
(
*
EStart
))
//ie an adjoining horizontal skip edge
{
if
(
EStart
->
Bot
.
X
!=
E
->
Bot
.
X
&&
EStart
->
Top
.
X
!=
E
->
Bot
.
X
)
ReverseHorizontal
(
*
E
);
}
else
if
(
EStart
->
Bot
.
X
!=
E
->
Bot
.
X
)
ReverseHorizontal
(
*
E
);
}
EStart
=
E
;
if
(
NextIsForward
)
{
while
(
Result
->
Top
.
Y
==
Result
->
Next
->
Bot
.
Y
&&
Result
->
Next
->
OutIdx
!=
Skip
)
Result
=
Result
->
Next
;
if
(
IsHorizontal
(
*
Result
)
&&
Result
->
Next
->
OutIdx
!=
Skip
)
{
//nb: at the top of a bound, horizontals are added to the bound
//only when the preceding edge attaches to the horizontal's left vertex
//unless a Skip edge is encountered when that becomes the top divide
Horz
=
Result
;
while
(
IsHorizontal
(
*
Horz
->
Prev
))
Horz
=
Horz
->
Prev
;
if
(
Horz
->
Prev
->
Top
.
X
>
Result
->
Next
->
Top
.
X
)
Result
=
Horz
->
Prev
;
}
while
(
E
!=
Result
)
{
E
->
NextInLML
=
E
->
Next
;
if
(
IsHorizontal
(
*
E
)
&&
E
!=
EStart
&&
E
->
Bot
.
X
!=
E
->
Prev
->
Top
.
X
)
ReverseHorizontal
(
*
E
);
E
=
E
->
Next
;
}
if
(
IsHorizontal
(
*
E
)
&&
E
!=
EStart
&&
E
->
Bot
.
X
!=
E
->
Prev
->
Top
.
X
)
ReverseHorizontal
(
*
E
);
Result
=
Result
->
Next
;
//move to the edge just beyond current bound
}
else
{
while
(
Result
->
Top
.
Y
==
Result
->
Prev
->
Bot
.
Y
&&
Result
->
Prev
->
OutIdx
!=
Skip
)
Result
=
Result
->
Prev
;
if
(
IsHorizontal
(
*
Result
)
&&
Result
->
Prev
->
OutIdx
!=
Skip
)
{
Horz
=
Result
;
while
(
IsHorizontal
(
*
Horz
->
Next
))
Horz
=
Horz
->
Next
;
if
(
Horz
->
Next
->
Top
.
X
==
Result
->
Prev
->
Top
.
X
||
Horz
->
Next
->
Top
.
X
>
Result
->
Prev
->
Top
.
X
)
Result
=
Horz
->
Next
;
}
while
(
E
!=
Result
)
{
E
->
NextInLML
=
E
->
Prev
;
if
(
IsHorizontal
(
*
E
)
&&
E
!=
EStart
&&
E
->
Bot
.
X
!=
E
->
Next
->
Top
.
X
)
ReverseHorizontal
(
*
E
);
E
=
E
->
Prev
;
}
if
(
IsHorizontal
(
*
E
)
&&
E
!=
EStart
&&
E
->
Bot
.
X
!=
E
->
Next
->
Top
.
X
)
ReverseHorizontal
(
*
E
);
Result
=
Result
->
Prev
;
//move to the edge just beyond current bound
}
return
Result
;
}
//------------------------------------------------------------------------------
bool
ClipperBase
::
AddPath
(
const
Path
&
pg
,
PolyType
PolyTyp
,
bool
Closed
)
{
#ifdef use_lines
if
(
!
Closed
&&
PolyTyp
==
ptClip
)
throw
clipperException
(
"AddPath: Open paths must be subject."
);
#else
if
(
!
Closed
)
throw
clipperException
(
"AddPath: Open paths have been disabled."
);
#endif
int
highI
=
(
int
)
pg
.
size
()
-
1
;
if
(
Closed
)
while
(
highI
>
0
&&
(
pg
[
highI
]
==
pg
[
0
]))
--
highI
;
while
(
highI
>
0
&&
(
pg
[
highI
]
==
pg
[
highI
-
1
]))
--
highI
;
if
((
Closed
&&
highI
<
2
)
||
(
!
Closed
&&
highI
<
1
))
return
false
;
//create a new edge array ...
TEdge
*
edges
=
new
TEdge
[
highI
+
1
];
bool
IsFlat
=
true
;
//1. Basic (first) edge initialization ...
try
{
edges
[
1
].
Curr
=
pg
[
1
];
RangeTest
(
pg
[
0
],
m_UseFullRange
);
RangeTest
(
pg
[
highI
],
m_UseFullRange
);
InitEdge
(
&
edges
[
0
],
&
edges
[
1
],
&
edges
[
highI
],
pg
[
0
]);
InitEdge
(
&
edges
[
highI
],
&
edges
[
0
],
&
edges
[
highI
-
1
],
pg
[
highI
]);
for
(
int
i
=
highI
-
1
;
i
>=
1
;
--
i
)
{
RangeTest
(
pg
[
i
],
m_UseFullRange
);
InitEdge
(
&
edges
[
i
],
&
edges
[
i
+
1
],
&
edges
[
i
-
1
],
pg
[
i
]);
}
}
catch
(...)
{
delete
[]
edges
;
throw
;
//range test fails
}
TEdge
*
eStart
=
&
edges
[
0
];
//2. Remove duplicate vertices, and (when closed) collinear edges ...
TEdge
*
E
=
eStart
,
*
eLoopStop
=
eStart
;
for
(;;)
{
//nb: allows matching start and end points when not Closed ...
if
(
E
->
Curr
==
E
->
Next
->
Curr
&&
(
Closed
||
E
->
Next
!=
eStart
))
{
if
(
E
==
E
->
Next
)
break
;
if
(
E
==
eStart
)
eStart
=
E
->
Next
;
E
=
RemoveEdge
(
E
);
eLoopStop
=
E
;
continue
;
}
if
(
E
->
Prev
==
E
->
Next
)
break
;
//only two vertices
else
if
(
Closed
&&
SlopesEqual
(
E
->
Prev
->
Curr
,
E
->
Curr
,
E
->
Next
->
Curr
,
m_UseFullRange
)
&&
(
!
m_PreserveCollinear
||
!
Pt2IsBetweenPt1AndPt3
(
E
->
Prev
->
Curr
,
E
->
Curr
,
E
->
Next
->
Curr
)))
{
//Collinear edges are allowed for open paths but in closed paths
//the default is to merge adjacent collinear edges into a single edge.
//However, if the PreserveCollinear property is enabled, only overlapping
//collinear edges (ie spikes) will be removed from closed paths.
if
(
E
==
eStart
)
eStart
=
E
->
Next
;
E
=
RemoveEdge
(
E
);
E
=
E
->
Prev
;
eLoopStop
=
E
;
continue
;
}
E
=
E
->
Next
;
if
((
E
==
eLoopStop
)
||
(
!
Closed
&&
E
->
Next
==
eStart
))
break
;
}
if
((
!
Closed
&&
(
E
==
E
->
Next
))
||
(
Closed
&&
(
E
->
Prev
==
E
->
Next
)))
{
delete
[]
edges
;
return
false
;
}
if
(
!
Closed
)
{
m_HasOpenPaths
=
true
;
eStart
->
Prev
->
OutIdx
=
Skip
;
}
//3. Do second stage of edge initialization ...
E
=
eStart
;
do
{
InitEdge2
(
*
E
,
PolyTyp
);
E
=
E
->
Next
;
if
(
IsFlat
&&
E
->
Curr
.
Y
!=
eStart
->
Curr
.
Y
)
IsFlat
=
false
;
}
while
(
E
!=
eStart
);
//4. Finally, add edge bounds to LocalMinima list ...
//Totally flat paths must be handled differently when adding them
//to LocalMinima list to avoid endless loops etc ...
if
(
IsFlat
)
{
if
(
Closed
)
{
delete
[]
edges
;
return
false
;
}
E
->
Prev
->
OutIdx
=
Skip
;
MinimaList
::
value_type
locMin
;
locMin
.
Y
=
E
->
Bot
.
Y
;
locMin
.
LeftBound
=
0
;
locMin
.
RightBound
=
E
;
locMin
.
RightBound
->
Side
=
esRight
;
locMin
.
RightBound
->
WindDelta
=
0
;
for
(;;)
{
if
(
E
->
Bot
.
X
!=
E
->
Prev
->
Top
.
X
)
ReverseHorizontal
(
*
E
);
if
(
E
->
Next
->
OutIdx
==
Skip
)
break
;
E
->
NextInLML
=
E
->
Next
;
E
=
E
->
Next
;
}
m_MinimaList
.
push_back
(
locMin
);
m_edges
.
push_back
(
edges
);
return
true
;
}
m_edges
.
push_back
(
edges
);
bool
leftBoundIsForward
;
TEdge
*
EMin
=
0
;
//workaround to avoid an endless loop in the while loop below when
//open paths have matching start and end points ...
if
(
E
->
Prev
->
Bot
==
E
->
Prev
->
Top
)
E
=
E
->
Next
;
for
(;;)
{
E
=
FindNextLocMin
(
E
);
if
(
E
==
EMin
)
break
;
else
if
(
!
EMin
)
EMin
=
E
;
//E and E.Prev now share a local minima (left aligned if horizontal).
//Compare their slopes to find which starts which bound ...
MinimaList
::
value_type
locMin
;
locMin
.
Y
=
E
->
Bot
.
Y
;
if
(
E
->
Dx
<
E
->
Prev
->
Dx
)
{
locMin
.
LeftBound
=
E
->
Prev
;
locMin
.
RightBound
=
E
;
leftBoundIsForward
=
false
;
//Q.nextInLML = Q.prev
}
else
{
locMin
.
LeftBound
=
E
;
locMin
.
RightBound
=
E
->
Prev
;
leftBoundIsForward
=
true
;
//Q.nextInLML = Q.next
}
if
(
!
Closed
)
locMin
.
LeftBound
->
WindDelta
=
0
;
else
if
(
locMin
.
LeftBound
->
Next
==
locMin
.
RightBound
)
locMin
.
LeftBound
->
WindDelta
=
-
1
;
else
locMin
.
LeftBound
->
WindDelta
=
1
;
locMin
.
RightBound
->
WindDelta
=
-
locMin
.
LeftBound
->
WindDelta
;
E
=
ProcessBound
(
locMin
.
LeftBound
,
leftBoundIsForward
);
if
(
E
->
OutIdx
==
Skip
)
E
=
ProcessBound
(
E
,
leftBoundIsForward
);
TEdge
*
E2
=
ProcessBound
(
locMin
.
RightBound
,
!
leftBoundIsForward
);
if
(
E2
->
OutIdx
==
Skip
)
E2
=
ProcessBound
(
E2
,
!
leftBoundIsForward
);
if
(
locMin
.
LeftBound
->
OutIdx
==
Skip
)
locMin
.
LeftBound
=
0
;
else
if
(
locMin
.
RightBound
->
OutIdx
==
Skip
)
locMin
.
RightBound
=
0
;
m_MinimaList
.
push_back
(
locMin
);
if
(
!
leftBoundIsForward
)
E
=
E2
;
}
return
true
;
}
//------------------------------------------------------------------------------
bool
ClipperBase
::
AddPaths
(
const
Paths
&
ppg
,
PolyType
PolyTyp
,
bool
Closed
)
{
bool
result
=
false
;
for
(
Paths
::
size_type
i
=
0
;
i
<
ppg
.
size
();
++
i
)
if
(
AddPath
(
ppg
[
i
],
PolyTyp
,
Closed
))
result
=
true
;
return
result
;
}
//------------------------------------------------------------------------------
void
ClipperBase
::
Clear
()
{
DisposeLocalMinimaList
();
for
(
EdgeList
::
size_type
i
=
0
;
i
<
m_edges
.
size
();
++
i
)
{
TEdge
*
edges
=
m_edges
[
i
];
delete
[]
edges
;
}
m_edges
.
clear
();
m_UseFullRange
=
false
;
m_HasOpenPaths
=
false
;
}
//------------------------------------------------------------------------------
void
ClipperBase
::
Reset
()
{
m_CurrentLM
=
m_MinimaList
.
begin
();
if
(
m_CurrentLM
==
m_MinimaList
.
end
())
return
;
//ie nothing to process
std
::
sort
(
m_MinimaList
.
begin
(),
m_MinimaList
.
end
(),
LocMinSorter
());
m_Scanbeam
=
ScanbeamList
();
//clears/resets priority_queue
//reset all edges ...
for
(
MinimaList
::
iterator
lm
=
m_MinimaList
.
begin
();
lm
!=
m_MinimaList
.
end
();
++
lm
)
{
InsertScanbeam
(
lm
->
Y
);
TEdge
*
e
=
lm
->
LeftBound
;
if
(
e
)
{
e
->
Curr
=
e
->
Bot
;
e
->
Side
=
esLeft
;
e
->
OutIdx
=
Unassigned
;
}
e
=
lm
->
RightBound
;
if
(
e
)
{
e
->
Curr
=
e
->
Bot
;
e
->
Side
=
esRight
;
e
->
OutIdx
=
Unassigned
;
}
}
m_ActiveEdges
=
0
;
m_CurrentLM
=
m_MinimaList
.
begin
();
}
//------------------------------------------------------------------------------
void
ClipperBase
::
DisposeLocalMinimaList
()
{
m_MinimaList
.
clear
();
m_CurrentLM
=
m_MinimaList
.
begin
();
}
//------------------------------------------------------------------------------
bool
ClipperBase
::
PopLocalMinima
(
cInt
Y
,
const
LocalMinimum
*&
locMin
)
{
if
(
m_CurrentLM
==
m_MinimaList
.
end
()
||
(
*
m_CurrentLM
).
Y
!=
Y
)
return
false
;
locMin
=
&
(
*
m_CurrentLM
);
++
m_CurrentLM
;
return
true
;
}
//------------------------------------------------------------------------------
IntRect
ClipperBase
::
GetBounds
()
{
IntRect
result
;
MinimaList
::
iterator
lm
=
m_MinimaList
.
begin
();
if
(
lm
==
m_MinimaList
.
end
())
{
result
.
left
=
result
.
top
=
result
.
right
=
result
.
bottom
=
0
;
return
result
;
}
result
.
left
=
lm
->
LeftBound
->
Bot
.
X
;
result
.
top
=
lm
->
LeftBound
->
Bot
.
Y
;
result
.
right
=
lm
->
LeftBound
->
Bot
.
X
;
result
.
bottom
=
lm
->
LeftBound
->
Bot
.
Y
;
while
(
lm
!=
m_MinimaList
.
end
())
{
//todo - needs fixing for open paths
result
.
bottom
=
std
::
max
(
result
.
bottom
,
lm
->
LeftBound
->
Bot
.
Y
);
TEdge
*
e
=
lm
->
LeftBound
;
for
(;;)
{
TEdge
*
bottomE
=
e
;
while
(
e
->
NextInLML
)
{
if
(
e
->
Bot
.
X
<
result
.
left
)
result
.
left
=
e
->
Bot
.
X
;
if
(
e
->
Bot
.
X
>
result
.
right
)
result
.
right
=
e
->
Bot
.
X
;
e
=
e
->
NextInLML
;
}
result
.
left
=
std
::
min
(
result
.
left
,
e
->
Bot
.
X
);
result
.
right
=
std
::
max
(
result
.
right
,
e
->
Bot
.
X
);
result
.
left
=
std
::
min
(
result
.
left
,
e
->
Top
.
X
);
result
.
right
=
std
::
max
(
result
.
right
,
e
->
Top
.
X
);
result
.
top
=
std
::
min
(
result
.
top
,
e
->
Top
.
Y
);
if
(
bottomE
==
lm
->
LeftBound
)
e
=
lm
->
RightBound
;
else
break
;
}
++
lm
;
}
return
result
;
}
//------------------------------------------------------------------------------
void
ClipperBase
::
InsertScanbeam
(
const
cInt
Y
)
{
m_Scanbeam
.
push
(
Y
);
}
//------------------------------------------------------------------------------
bool
ClipperBase
::
PopScanbeam
(
cInt
&
Y
)
{
if
(
m_Scanbeam
.
empty
())
return
false
;
Y
=
m_Scanbeam
.
top
();
m_Scanbeam
.
pop
();
while
(
!
m_Scanbeam
.
empty
()
&&
Y
==
m_Scanbeam
.
top
())
{
m_Scanbeam
.
pop
();
}
// Pop duplicates.
return
true
;
}
//------------------------------------------------------------------------------
void
ClipperBase
::
DisposeAllOutRecs
(){
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
++
i
)
DisposeOutRec
(
i
);
m_PolyOuts
.
clear
();
}
//------------------------------------------------------------------------------
void
ClipperBase
::
DisposeOutRec
(
PolyOutList
::
size_type
index
)
{
OutRec
*
outRec
=
m_PolyOuts
[
index
];
if
(
outRec
->
Pts
)
DisposeOutPts
(
outRec
->
Pts
);
delete
outRec
;
m_PolyOuts
[
index
]
=
0
;
}
//------------------------------------------------------------------------------
void
ClipperBase
::
DeleteFromAEL
(
TEdge
*
e
)
{
TEdge
*
AelPrev
=
e
->
PrevInAEL
;
TEdge
*
AelNext
=
e
->
NextInAEL
;
if
(
!
AelPrev
&&
!
AelNext
&&
(
e
!=
m_ActiveEdges
))
return
;
//already deleted
if
(
AelPrev
)
AelPrev
->
NextInAEL
=
AelNext
;
else
m_ActiveEdges
=
AelNext
;
if
(
AelNext
)
AelNext
->
PrevInAEL
=
AelPrev
;
e
->
NextInAEL
=
0
;
e
->
PrevInAEL
=
0
;
}
//------------------------------------------------------------------------------
OutRec
*
ClipperBase
::
CreateOutRec
()
{
OutRec
*
result
=
new
OutRec
;
result
->
IsHole
=
false
;
result
->
IsOpen
=
false
;
result
->
FirstLeft
=
0
;
result
->
Pts
=
0
;
result
->
BottomPt
=
0
;
result
->
PolyNd
=
0
;
m_PolyOuts
.
push_back
(
result
);
result
->
Idx
=
(
int
)
m_PolyOuts
.
size
()
-
1
;
return
result
;
}
//------------------------------------------------------------------------------
void
ClipperBase
::
SwapPositionsInAEL
(
TEdge
*
Edge1
,
TEdge
*
Edge2
)
{
//check that one or other edge hasn't already been removed from AEL ...
if
(
Edge1
->
NextInAEL
==
Edge1
->
PrevInAEL
||
Edge2
->
NextInAEL
==
Edge2
->
PrevInAEL
)
return
;
if
(
Edge1
->
NextInAEL
==
Edge2
)
{
TEdge
*
Next
=
Edge2
->
NextInAEL
;
if
(
Next
)
Next
->
PrevInAEL
=
Edge1
;
TEdge
*
Prev
=
Edge1
->
PrevInAEL
;
if
(
Prev
)
Prev
->
NextInAEL
=
Edge2
;
Edge2
->
PrevInAEL
=
Prev
;
Edge2
->
NextInAEL
=
Edge1
;
Edge1
->
PrevInAEL
=
Edge2
;
Edge1
->
NextInAEL
=
Next
;
}
else
if
(
Edge2
->
NextInAEL
==
Edge1
)
{
TEdge
*
Next
=
Edge1
->
NextInAEL
;
if
(
Next
)
Next
->
PrevInAEL
=
Edge2
;
TEdge
*
Prev
=
Edge2
->
PrevInAEL
;
if
(
Prev
)
Prev
->
NextInAEL
=
Edge1
;
Edge1
->
PrevInAEL
=
Prev
;
Edge1
->
NextInAEL
=
Edge2
;
Edge2
->
PrevInAEL
=
Edge1
;
Edge2
->
NextInAEL
=
Next
;
}
else
{
TEdge
*
Next
=
Edge1
->
NextInAEL
;
TEdge
*
Prev
=
Edge1
->
PrevInAEL
;
Edge1
->
NextInAEL
=
Edge2
->
NextInAEL
;
if
(
Edge1
->
NextInAEL
)
Edge1
->
NextInAEL
->
PrevInAEL
=
Edge1
;
Edge1
->
PrevInAEL
=
Edge2
->
PrevInAEL
;
if
(
Edge1
->
PrevInAEL
)
Edge1
->
PrevInAEL
->
NextInAEL
=
Edge1
;
Edge2
->
NextInAEL
=
Next
;
if
(
Edge2
->
NextInAEL
)
Edge2
->
NextInAEL
->
PrevInAEL
=
Edge2
;
Edge2
->
PrevInAEL
=
Prev
;
if
(
Edge2
->
PrevInAEL
)
Edge2
->
PrevInAEL
->
NextInAEL
=
Edge2
;
}
if
(
!
Edge1
->
PrevInAEL
)
m_ActiveEdges
=
Edge1
;
else
if
(
!
Edge2
->
PrevInAEL
)
m_ActiveEdges
=
Edge2
;
}
//------------------------------------------------------------------------------
void
ClipperBase
::
UpdateEdgeIntoAEL
(
TEdge
*&
e
)
{
if
(
!
e
->
NextInLML
)
throw
clipperException
(
"UpdateEdgeIntoAEL: invalid call"
);
e
->
NextInLML
->
OutIdx
=
e
->
OutIdx
;
TEdge
*
AelPrev
=
e
->
PrevInAEL
;
TEdge
*
AelNext
=
e
->
NextInAEL
;
if
(
AelPrev
)
AelPrev
->
NextInAEL
=
e
->
NextInLML
;
else
m_ActiveEdges
=
e
->
NextInLML
;
if
(
AelNext
)
AelNext
->
PrevInAEL
=
e
->
NextInLML
;
e
->
NextInLML
->
Side
=
e
->
Side
;
e
->
NextInLML
->
WindDelta
=
e
->
WindDelta
;
e
->
NextInLML
->
WindCnt
=
e
->
WindCnt
;
e
->
NextInLML
->
WindCnt2
=
e
->
WindCnt2
;
e
=
e
->
NextInLML
;
e
->
Curr
=
e
->
Bot
;
e
->
PrevInAEL
=
AelPrev
;
e
->
NextInAEL
=
AelNext
;
if
(
!
IsHorizontal
(
*
e
))
InsertScanbeam
(
e
->
Top
.
Y
);
}
//------------------------------------------------------------------------------
bool
ClipperBase
::
LocalMinimaPending
()
{
return
(
m_CurrentLM
!=
m_MinimaList
.
end
());
}
//------------------------------------------------------------------------------
// TClipper methods ...
//------------------------------------------------------------------------------
Clipper
::
Clipper
(
int
initOptions
)
:
ClipperBase
()
//constructor
{
m_ExecuteLocked
=
false
;
m_UseFullRange
=
false
;
m_ReverseOutput
=
((
initOptions
&
ioReverseSolution
)
!=
0
);
m_StrictSimple
=
((
initOptions
&
ioStrictlySimple
)
!=
0
);
m_PreserveCollinear
=
((
initOptions
&
ioPreserveCollinear
)
!=
0
);
m_HasOpenPaths
=
false
;
#ifdef use_xyz
m_ZFill
=
0
;
#endif
}
//------------------------------------------------------------------------------
#ifdef use_xyz
void
Clipper
::
ZFillFunction
(
ZFillCallback
zFillFunc
)
{
m_ZFill
=
zFillFunc
;
}
//------------------------------------------------------------------------------
#endif
bool
Clipper
::
Execute
(
ClipType
clipType
,
Paths
&
solution
,
PolyFillType
fillType
)
{
return
Execute
(
clipType
,
solution
,
fillType
,
fillType
);
}
//------------------------------------------------------------------------------
bool
Clipper
::
Execute
(
ClipType
clipType
,
PolyTree
&
polytree
,
PolyFillType
fillType
)
{
return
Execute
(
clipType
,
polytree
,
fillType
,
fillType
);
}
//------------------------------------------------------------------------------
bool
Clipper
::
Execute
(
ClipType
clipType
,
Paths
&
solution
,
PolyFillType
subjFillType
,
PolyFillType
clipFillType
)
{
if
(
m_ExecuteLocked
)
return
false
;
if
(
m_HasOpenPaths
)
throw
clipperException
(
"Error: PolyTree struct is needed for open path clipping."
);
m_ExecuteLocked
=
true
;
solution
.
resize
(
0
);
m_SubjFillType
=
subjFillType
;
m_ClipFillType
=
clipFillType
;
m_ClipType
=
clipType
;
m_UsingPolyTree
=
false
;
bool
succeeded
=
ExecuteInternal
();
if
(
succeeded
)
BuildResult
(
solution
);
DisposeAllOutRecs
();
m_ExecuteLocked
=
false
;
return
succeeded
;
}
//------------------------------------------------------------------------------
bool
Clipper
::
Execute
(
ClipType
clipType
,
PolyTree
&
polytree
,
PolyFillType
subjFillType
,
PolyFillType
clipFillType
)
{
if
(
m_ExecuteLocked
)
return
false
;
m_ExecuteLocked
=
true
;
m_SubjFillType
=
subjFillType
;
m_ClipFillType
=
clipFillType
;
m_ClipType
=
clipType
;
m_UsingPolyTree
=
true
;
bool
succeeded
=
ExecuteInternal
();
if
(
succeeded
)
BuildResult2
(
polytree
);
DisposeAllOutRecs
();
m_ExecuteLocked
=
false
;
return
succeeded
;
}
//------------------------------------------------------------------------------
void
Clipper
::
FixHoleLinkage
(
OutRec
&
outrec
)
{
//skip OutRecs that (a) contain outermost polygons or
//(b) already have the correct owner/child linkage ...
if
(
!
outrec
.
FirstLeft
||
(
outrec
.
IsHole
!=
outrec
.
FirstLeft
->
IsHole
&&
outrec
.
FirstLeft
->
Pts
))
return
;
OutRec
*
orfl
=
outrec
.
FirstLeft
;
while
(
orfl
&&
((
orfl
->
IsHole
==
outrec
.
IsHole
)
||
!
orfl
->
Pts
))
orfl
=
orfl
->
FirstLeft
;
outrec
.
FirstLeft
=
orfl
;
}
//------------------------------------------------------------------------------
bool
Clipper
::
ExecuteInternal
()
{
bool
succeeded
=
true
;
try
{
Reset
();
m_Maxima
=
MaximaList
();
m_SortedEdges
=
0
;
succeeded
=
true
;
cInt
botY
,
topY
;
if
(
!
PopScanbeam
(
botY
))
return
false
;
InsertLocalMinimaIntoAEL
(
botY
);
while
(
PopScanbeam
(
topY
)
||
LocalMinimaPending
())
{
ProcessHorizontals
();
ClearGhostJoins
();
if
(
!
ProcessIntersections
(
topY
))
{
succeeded
=
false
;
break
;
}
ProcessEdgesAtTopOfScanbeam
(
topY
);
botY
=
topY
;
InsertLocalMinimaIntoAEL
(
botY
);
}
}
catch
(...)
{
succeeded
=
false
;
}
if
(
succeeded
)
{
//fix orientations ...
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
++
i
)
{
OutRec
*
outRec
=
m_PolyOuts
[
i
];
if
(
!
outRec
->
Pts
||
outRec
->
IsOpen
)
continue
;
if
((
outRec
->
IsHole
^
m_ReverseOutput
)
==
(
Area
(
*
outRec
)
>
0
))
ReversePolyPtLinks
(
outRec
->
Pts
);
}
if
(
!
m_Joins
.
empty
())
JoinCommonEdges
();
//unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
++
i
)
{
OutRec
*
outRec
=
m_PolyOuts
[
i
];
if
(
!
outRec
->
Pts
)
continue
;
if
(
outRec
->
IsOpen
)
FixupOutPolyline
(
*
outRec
);
else
FixupOutPolygon
(
*
outRec
);
}
if
(
m_StrictSimple
)
DoSimplePolygons
();
}
ClearJoins
();
ClearGhostJoins
();
return
succeeded
;
}
//------------------------------------------------------------------------------
void
Clipper
::
SetWindingCount
(
TEdge
&
edge
)
{
TEdge
*
e
=
edge
.
PrevInAEL
;
//find the edge of the same polytype that immediately preceeds 'edge' in AEL
while
(
e
&&
((
e
->
PolyTyp
!=
edge
.
PolyTyp
)
||
(
e
->
WindDelta
==
0
)))
e
=
e
->
PrevInAEL
;
if
(
!
e
)
{
if
(
edge
.
WindDelta
==
0
)
{
PolyFillType
pft
=
(
edge
.
PolyTyp
==
ptSubject
?
m_SubjFillType
:
m_ClipFillType
);
edge
.
WindCnt
=
(
pft
==
pftNegative
?
-
1
:
1
);
}
else
edge
.
WindCnt
=
edge
.
WindDelta
;
edge
.
WindCnt2
=
0
;
e
=
m_ActiveEdges
;
//ie get ready to calc WindCnt2
}
else
if
(
edge
.
WindDelta
==
0
&&
m_ClipType
!=
ctUnion
)
{
edge
.
WindCnt
=
1
;
edge
.
WindCnt2
=
e
->
WindCnt2
;
e
=
e
->
NextInAEL
;
//ie get ready to calc WindCnt2
}
else
if
(
IsEvenOddFillType
(
edge
))
{
//EvenOdd filling ...
if
(
edge
.
WindDelta
==
0
)
{
//are we inside a subj polygon ...
bool
Inside
=
true
;
TEdge
*
e2
=
e
->
PrevInAEL
;
while
(
e2
)
{
if
(
e2
->
PolyTyp
==
e
->
PolyTyp
&&
e2
->
WindDelta
!=
0
)
Inside
=
!
Inside
;
e2
=
e2
->
PrevInAEL
;
}
edge
.
WindCnt
=
(
Inside
?
0
:
1
);
}
else
{
edge
.
WindCnt
=
edge
.
WindDelta
;
}
edge
.
WindCnt2
=
e
->
WindCnt2
;
e
=
e
->
NextInAEL
;
//ie get ready to calc WindCnt2
}
else
{
//nonZero, Positive or Negative filling ...
if
(
e
->
WindCnt
*
e
->
WindDelta
<
0
)
{
//prev edge is 'decreasing' WindCount (WC) toward zero
//so we're outside the previous polygon ...
if
(
Abs
(
e
->
WindCnt
)
>
1
)
{
//outside prev poly but still inside another.
//when reversing direction of prev poly use the same WC
if
(
e
->
WindDelta
*
edge
.
WindDelta
<
0
)
edge
.
WindCnt
=
e
->
WindCnt
;
//otherwise continue to 'decrease' WC ...
else
edge
.
WindCnt
=
e
->
WindCnt
+
edge
.
WindDelta
;
}
else
//now outside all polys of same polytype so set own WC ...
edge
.
WindCnt
=
(
edge
.
WindDelta
==
0
?
1
:
edge
.
WindDelta
);
}
else
{
//prev edge is 'increasing' WindCount (WC) away from zero
//so we're inside the previous polygon ...
if
(
edge
.
WindDelta
==
0
)
edge
.
WindCnt
=
(
e
->
WindCnt
<
0
?
e
->
WindCnt
-
1
:
e
->
WindCnt
+
1
);
//if wind direction is reversing prev then use same WC
else
if
(
e
->
WindDelta
*
edge
.
WindDelta
<
0
)
edge
.
WindCnt
=
e
->
WindCnt
;
//otherwise add to WC ...
else
edge
.
WindCnt
=
e
->
WindCnt
+
edge
.
WindDelta
;
}
edge
.
WindCnt2
=
e
->
WindCnt2
;
e
=
e
->
NextInAEL
;
//ie get ready to calc WindCnt2
}
//update WindCnt2 ...
if
(
IsEvenOddAltFillType
(
edge
))
{
//EvenOdd filling ...
while
(
e
!=
&
edge
)
{
if
(
e
->
WindDelta
!=
0
)
edge
.
WindCnt2
=
(
edge
.
WindCnt2
==
0
?
1
:
0
);
e
=
e
->
NextInAEL
;
}
}
else
{
//nonZero, Positive or Negative filling ...
while
(
e
!=
&
edge
)
{
edge
.
WindCnt2
+=
e
->
WindDelta
;
e
=
e
->
NextInAEL
;
}
}
}
//------------------------------------------------------------------------------
bool
Clipper
::
IsEvenOddFillType
(
const
TEdge
&
edge
)
const
{
if
(
edge
.
PolyTyp
==
ptSubject
)
return
m_SubjFillType
==
pftEvenOdd
;
else
return
m_ClipFillType
==
pftEvenOdd
;
}
//------------------------------------------------------------------------------
bool
Clipper
::
IsEvenOddAltFillType
(
const
TEdge
&
edge
)
const
{
if
(
edge
.
PolyTyp
==
ptSubject
)
return
m_ClipFillType
==
pftEvenOdd
;
else
return
m_SubjFillType
==
pftEvenOdd
;
}
//------------------------------------------------------------------------------
bool
Clipper
::
IsContributing
(
const
TEdge
&
edge
)
const
{
PolyFillType
pft
,
pft2
;
if
(
edge
.
PolyTyp
==
ptSubject
)
{
pft
=
m_SubjFillType
;
pft2
=
m_ClipFillType
;
}
else
{
pft
=
m_ClipFillType
;
pft2
=
m_SubjFillType
;
}
switch
(
pft
)
{
case
pftEvenOdd
:
//return false if a subj line has been flagged as inside a subj polygon
if
(
edge
.
WindDelta
==
0
&&
edge
.
WindCnt
!=
1
)
return
false
;
break
;
case
pftNonZero
:
if
(
Abs
(
edge
.
WindCnt
)
!=
1
)
return
false
;
break
;
case
pftPositive
:
if
(
edge
.
WindCnt
!=
1
)
return
false
;
break
;
default:
//pftNegative
if
(
edge
.
WindCnt
!=
-
1
)
return
false
;
}
switch
(
m_ClipType
)
{
case
ctIntersection
:
switch
(
pft2
)
{
case
pftEvenOdd
:
case
pftNonZero
:
return
(
edge
.
WindCnt2
!=
0
);
case
pftPositive
:
return
(
edge
.
WindCnt2
>
0
);
default:
return
(
edge
.
WindCnt2
<
0
);
}
break
;
case
ctUnion
:
switch
(
pft2
)
{
case
pftEvenOdd
:
case
pftNonZero
:
return
(
edge
.
WindCnt2
==
0
);
case
pftPositive
:
return
(
edge
.
WindCnt2
<=
0
);
default:
return
(
edge
.
WindCnt2
>=
0
);
}
break
;
case
ctDifference
:
if
(
edge
.
PolyTyp
==
ptSubject
)
switch
(
pft2
)
{
case
pftEvenOdd
:
case
pftNonZero
:
return
(
edge
.
WindCnt2
==
0
);
case
pftPositive
:
return
(
edge
.
WindCnt2
<=
0
);
default:
return
(
edge
.
WindCnt2
>=
0
);
}
else
switch
(
pft2
)
{
case
pftEvenOdd
:
case
pftNonZero
:
return
(
edge
.
WindCnt2
!=
0
);
case
pftPositive
:
return
(
edge
.
WindCnt2
>
0
);
default:
return
(
edge
.
WindCnt2
<
0
);
}
break
;
case
ctXor
:
if
(
edge
.
WindDelta
==
0
)
//XOr always contributing unless open
switch
(
pft2
)
{
case
pftEvenOdd
:
case
pftNonZero
:
return
(
edge
.
WindCnt2
==
0
);
case
pftPositive
:
return
(
edge
.
WindCnt2
<=
0
);
default:
return
(
edge
.
WindCnt2
>=
0
);
}
else
return
true
;
break
;
default:
return
true
;
}
}
//------------------------------------------------------------------------------
OutPt
*
Clipper
::
AddLocalMinPoly
(
TEdge
*
e1
,
TEdge
*
e2
,
const
IntPoint
&
Pt
)
{
OutPt
*
result
;
TEdge
*
e
,
*
prevE
;
if
(
IsHorizontal
(
*
e2
)
||
(
e1
->
Dx
>
e2
->
Dx
))
{
result
=
AddOutPt
(
e1
,
Pt
);
e2
->
OutIdx
=
e1
->
OutIdx
;
e1
->
Side
=
esLeft
;
e2
->
Side
=
esRight
;
e
=
e1
;
if
(
e
->
PrevInAEL
==
e2
)
prevE
=
e2
->
PrevInAEL
;
else
prevE
=
e
->
PrevInAEL
;
}
else
{
result
=
AddOutPt
(
e2
,
Pt
);
e1
->
OutIdx
=
e2
->
OutIdx
;
e1
->
Side
=
esRight
;
e2
->
Side
=
esLeft
;
e
=
e2
;
if
(
e
->
PrevInAEL
==
e1
)
prevE
=
e1
->
PrevInAEL
;
else
prevE
=
e
->
PrevInAEL
;
}
if
(
prevE
&&
prevE
->
OutIdx
>=
0
)
{
cInt
xPrev
=
TopX
(
*
prevE
,
Pt
.
Y
);
cInt
xE
=
TopX
(
*
e
,
Pt
.
Y
);
if
(
xPrev
==
xE
&&
(
e
->
WindDelta
!=
0
)
&&
(
prevE
->
WindDelta
!=
0
)
&&
SlopesEqual
(
IntPoint
(
xPrev
,
Pt
.
Y
),
prevE
->
Top
,
IntPoint
(
xE
,
Pt
.
Y
),
e
->
Top
,
m_UseFullRange
))
{
OutPt
*
outPt
=
AddOutPt
(
prevE
,
Pt
);
AddJoin
(
result
,
outPt
,
e
->
Top
);
}
}
return
result
;
}
//------------------------------------------------------------------------------
void
Clipper
::
AddLocalMaxPoly
(
TEdge
*
e1
,
TEdge
*
e2
,
const
IntPoint
&
Pt
)
{
AddOutPt
(
e1
,
Pt
);
if
(
e2
->
WindDelta
==
0
)
AddOutPt
(
e2
,
Pt
);
if
(
e1
->
OutIdx
==
e2
->
OutIdx
)
{
e1
->
OutIdx
=
Unassigned
;
e2
->
OutIdx
=
Unassigned
;
}
else
if
(
e1
->
OutIdx
<
e2
->
OutIdx
)
AppendPolygon
(
e1
,
e2
);
else
AppendPolygon
(
e2
,
e1
);
}
//------------------------------------------------------------------------------
void
Clipper
::
AddEdgeToSEL
(
TEdge
*
edge
)
{
//SEL pointers in PEdge are reused to build a list of horizontal edges.
//However, we don't need to worry about order with horizontal edge processing.
if
(
!
m_SortedEdges
)
{
m_SortedEdges
=
edge
;
edge
->
PrevInSEL
=
0
;
edge
->
NextInSEL
=
0
;
}
else
{
edge
->
NextInSEL
=
m_SortedEdges
;
edge
->
PrevInSEL
=
0
;
m_SortedEdges
->
PrevInSEL
=
edge
;
m_SortedEdges
=
edge
;
}
}
//------------------------------------------------------------------------------
bool
Clipper
::
PopEdgeFromSEL
(
TEdge
*&
edge
)
{
if
(
!
m_SortedEdges
)
return
false
;
edge
=
m_SortedEdges
;
DeleteFromSEL
(
m_SortedEdges
);
return
true
;
}
//------------------------------------------------------------------------------
void
Clipper
::
CopyAELToSEL
()
{
TEdge
*
e
=
m_ActiveEdges
;
m_SortedEdges
=
e
;
while
(
e
)
{
e
->
PrevInSEL
=
e
->
PrevInAEL
;
e
->
NextInSEL
=
e
->
NextInAEL
;
e
=
e
->
NextInAEL
;
}
}
//------------------------------------------------------------------------------
void
Clipper
::
AddJoin
(
OutPt
*
op1
,
OutPt
*
op2
,
const
IntPoint
OffPt
)
{
Join
*
j
=
new
Join
;
j
->
OutPt1
=
op1
;
j
->
OutPt2
=
op2
;
j
->
OffPt
=
OffPt
;
m_Joins
.
push_back
(
j
);
}
//------------------------------------------------------------------------------
void
Clipper
::
ClearJoins
()
{
for
(
JoinList
::
size_type
i
=
0
;
i
<
m_Joins
.
size
();
i
++
)
delete
m_Joins
[
i
];
m_Joins
.
resize
(
0
);
}
//------------------------------------------------------------------------------
void
Clipper
::
ClearGhostJoins
()
{
for
(
JoinList
::
size_type
i
=
0
;
i
<
m_GhostJoins
.
size
();
i
++
)
delete
m_GhostJoins
[
i
];
m_GhostJoins
.
resize
(
0
);
}
//------------------------------------------------------------------------------
void
Clipper
::
AddGhostJoin
(
OutPt
*
op
,
const
IntPoint
OffPt
)
{
Join
*
j
=
new
Join
;
j
->
OutPt1
=
op
;
j
->
OutPt2
=
0
;
j
->
OffPt
=
OffPt
;
m_GhostJoins
.
push_back
(
j
);
}
//------------------------------------------------------------------------------
void
Clipper
::
InsertLocalMinimaIntoAEL
(
const
cInt
botY
)
{
const
LocalMinimum
*
lm
;
while
(
PopLocalMinima
(
botY
,
lm
))
{
TEdge
*
lb
=
lm
->
LeftBound
;
TEdge
*
rb
=
lm
->
RightBound
;
OutPt
*
Op1
=
0
;
if
(
!
lb
)
{
//nb: don't insert LB into either AEL or SEL
InsertEdgeIntoAEL
(
rb
,
0
);
SetWindingCount
(
*
rb
);
if
(
IsContributing
(
*
rb
))
Op1
=
AddOutPt
(
rb
,
rb
->
Bot
);
}
else
if
(
!
rb
)
{
InsertEdgeIntoAEL
(
lb
,
0
);
SetWindingCount
(
*
lb
);
if
(
IsContributing
(
*
lb
))
Op1
=
AddOutPt
(
lb
,
lb
->
Bot
);
InsertScanbeam
(
lb
->
Top
.
Y
);
}
else
{
InsertEdgeIntoAEL
(
lb
,
0
);
InsertEdgeIntoAEL
(
rb
,
lb
);
SetWindingCount
(
*
lb
);
rb
->
WindCnt
=
lb
->
WindCnt
;
rb
->
WindCnt2
=
lb
->
WindCnt2
;
if
(
IsContributing
(
*
lb
))
Op1
=
AddLocalMinPoly
(
lb
,
rb
,
lb
->
Bot
);
InsertScanbeam
(
lb
->
Top
.
Y
);
}
if
(
rb
)
{
if
(
IsHorizontal
(
*
rb
))
{
AddEdgeToSEL
(
rb
);
if
(
rb
->
NextInLML
)
InsertScanbeam
(
rb
->
NextInLML
->
Top
.
Y
);
}
else
InsertScanbeam
(
rb
->
Top
.
Y
);
}
if
(
!
lb
||
!
rb
)
continue
;
//if any output polygons share an edge, they'll need joining later ...
if
(
Op1
&&
IsHorizontal
(
*
rb
)
&&
m_GhostJoins
.
size
()
>
0
&&
(
rb
->
WindDelta
!=
0
))
{
for
(
JoinList
::
size_type
i
=
0
;
i
<
m_GhostJoins
.
size
();
++
i
)
{
Join
*
jr
=
m_GhostJoins
[
i
];
//if the horizontal Rb and a 'ghost' horizontal overlap, then convert
//the 'ghost' join to a real join ready for later ...
if
(
HorzSegmentsOverlap
(
jr
->
OutPt1
->
Pt
.
X
,
jr
->
OffPt
.
X
,
rb
->
Bot
.
X
,
rb
->
Top
.
X
))
AddJoin
(
jr
->
OutPt1
,
Op1
,
jr
->
OffPt
);
}
}
if
(
lb
->
OutIdx
>=
0
&&
lb
->
PrevInAEL
&&
lb
->
PrevInAEL
->
Curr
.
X
==
lb
->
Bot
.
X
&&
lb
->
PrevInAEL
->
OutIdx
>=
0
&&
SlopesEqual
(
lb
->
PrevInAEL
->
Bot
,
lb
->
PrevInAEL
->
Top
,
lb
->
Curr
,
lb
->
Top
,
m_UseFullRange
)
&&
(
lb
->
WindDelta
!=
0
)
&&
(
lb
->
PrevInAEL
->
WindDelta
!=
0
))
{
OutPt
*
Op2
=
AddOutPt
(
lb
->
PrevInAEL
,
lb
->
Bot
);
AddJoin
(
Op1
,
Op2
,
lb
->
Top
);
}
if
(
lb
->
NextInAEL
!=
rb
)
{
if
(
rb
->
OutIdx
>=
0
&&
rb
->
PrevInAEL
->
OutIdx
>=
0
&&
SlopesEqual
(
rb
->
PrevInAEL
->
Curr
,
rb
->
PrevInAEL
->
Top
,
rb
->
Curr
,
rb
->
Top
,
m_UseFullRange
)
&&
(
rb
->
WindDelta
!=
0
)
&&
(
rb
->
PrevInAEL
->
WindDelta
!=
0
))
{
OutPt
*
Op2
=
AddOutPt
(
rb
->
PrevInAEL
,
rb
->
Bot
);
AddJoin
(
Op1
,
Op2
,
rb
->
Top
);
}
TEdge
*
e
=
lb
->
NextInAEL
;
if
(
e
)
{
while
(
e
!=
rb
)
{
//nb: For calculating winding counts etc, IntersectEdges() assumes
//that param1 will be to the Right of param2 ABOVE the intersection ...
IntersectEdges
(
rb
,
e
,
lb
->
Curr
);
//order important here
e
=
e
->
NextInAEL
;
}
}
}
}
}
//------------------------------------------------------------------------------
void
Clipper
::
DeleteFromSEL
(
TEdge
*
e
)
{
TEdge
*
SelPrev
=
e
->
PrevInSEL
;
TEdge
*
SelNext
=
e
->
NextInSEL
;
if
(
!
SelPrev
&&
!
SelNext
&&
(
e
!=
m_SortedEdges
)
)
return
;
//already deleted
if
(
SelPrev
)
SelPrev
->
NextInSEL
=
SelNext
;
else
m_SortedEdges
=
SelNext
;
if
(
SelNext
)
SelNext
->
PrevInSEL
=
SelPrev
;
e
->
NextInSEL
=
0
;
e
->
PrevInSEL
=
0
;
}
//------------------------------------------------------------------------------
#ifdef use_xyz
void
Clipper
::
SetZ
(
IntPoint
&
pt
,
TEdge
&
e1
,
TEdge
&
e2
)
{
if
(
pt
.
Z
!=
0
||
!
m_ZFill
)
return
;
else
if
(
pt
==
e1
.
Bot
)
pt
.
Z
=
e1
.
Bot
.
Z
;
else
if
(
pt
==
e1
.
Top
)
pt
.
Z
=
e1
.
Top
.
Z
;
else
if
(
pt
==
e2
.
Bot
)
pt
.
Z
=
e2
.
Bot
.
Z
;
else
if
(
pt
==
e2
.
Top
)
pt
.
Z
=
e2
.
Top
.
Z
;
else
(
*
m_ZFill
)(
e1
.
Bot
,
e1
.
Top
,
e2
.
Bot
,
e2
.
Top
,
pt
);
}
//------------------------------------------------------------------------------
#endif
void
Clipper
::
IntersectEdges
(
TEdge
*
e1
,
TEdge
*
e2
,
IntPoint
&
Pt
)
{
bool
e1Contributing
=
(
e1
->
OutIdx
>=
0
);
bool
e2Contributing
=
(
e2
->
OutIdx
>=
0
);
#ifdef use_xyz
SetZ
(
Pt
,
*
e1
,
*
e2
);
#endif
#ifdef use_lines
//if either edge is on an OPEN path ...
if
(
e1
->
WindDelta
==
0
||
e2
->
WindDelta
==
0
)
{
//ignore subject-subject open path intersections UNLESS they
//are both open paths, AND they are both 'contributing maximas' ...
if
(
e1
->
WindDelta
==
0
&&
e2
->
WindDelta
==
0
)
return
;
//if intersecting a subj line with a subj poly ...
else
if
(
e1
->
PolyTyp
==
e2
->
PolyTyp
&&
e1
->
WindDelta
!=
e2
->
WindDelta
&&
m_ClipType
==
ctUnion
)
{
if
(
e1
->
WindDelta
==
0
)
{
if
(
e2Contributing
)
{
AddOutPt
(
e1
,
Pt
);
if
(
e1Contributing
)
e1
->
OutIdx
=
Unassigned
;
}
}
else
{
if
(
e1Contributing
)
{
AddOutPt
(
e2
,
Pt
);
if
(
e2Contributing
)
e2
->
OutIdx
=
Unassigned
;
}
}
}
else
if
(
e1
->
PolyTyp
!=
e2
->
PolyTyp
)
{
//toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ...
if
((
e1
->
WindDelta
==
0
)
&&
abs
(
e2
->
WindCnt
)
==
1
&&
(
m_ClipType
!=
ctUnion
||
e2
->
WindCnt2
==
0
))
{
AddOutPt
(
e1
,
Pt
);
if
(
e1Contributing
)
e1
->
OutIdx
=
Unassigned
;
}
else
if
((
e2
->
WindDelta
==
0
)
&&
(
abs
(
e1
->
WindCnt
)
==
1
)
&&
(
m_ClipType
!=
ctUnion
||
e1
->
WindCnt2
==
0
))
{
AddOutPt
(
e2
,
Pt
);
if
(
e2Contributing
)
e2
->
OutIdx
=
Unassigned
;
}
}
return
;
}
#endif
//update winding counts...
//assumes that e1 will be to the Right of e2 ABOVE the intersection
if
(
e1
->
PolyTyp
==
e2
->
PolyTyp
)
{
if
(
IsEvenOddFillType
(
*
e1
)
)
{
int
oldE1WindCnt
=
e1
->
WindCnt
;
e1
->
WindCnt
=
e2
->
WindCnt
;
e2
->
WindCnt
=
oldE1WindCnt
;
}
else
{
if
(
e1
->
WindCnt
+
e2
->
WindDelta
==
0
)
e1
->
WindCnt
=
-
e1
->
WindCnt
;
else
e1
->
WindCnt
+=
e2
->
WindDelta
;
if
(
e2
->
WindCnt
-
e1
->
WindDelta
==
0
)
e2
->
WindCnt
=
-
e2
->
WindCnt
;
else
e2
->
WindCnt
-=
e1
->
WindDelta
;
}
}
else
{
if
(
!
IsEvenOddFillType
(
*
e2
))
e1
->
WindCnt2
+=
e2
->
WindDelta
;
else
e1
->
WindCnt2
=
(
e1
->
WindCnt2
==
0
)
?
1
:
0
;
if
(
!
IsEvenOddFillType
(
*
e1
))
e2
->
WindCnt2
-=
e1
->
WindDelta
;
else
e2
->
WindCnt2
=
(
e2
->
WindCnt2
==
0
)
?
1
:
0
;
}
PolyFillType
e1FillType
,
e2FillType
,
e1FillType2
,
e2FillType2
;
if
(
e1
->
PolyTyp
==
ptSubject
)
{
e1FillType
=
m_SubjFillType
;
e1FillType2
=
m_ClipFillType
;
}
else
{
e1FillType
=
m_ClipFillType
;
e1FillType2
=
m_SubjFillType
;
}
if
(
e2
->
PolyTyp
==
ptSubject
)
{
e2FillType
=
m_SubjFillType
;
e2FillType2
=
m_ClipFillType
;
}
else
{
e2FillType
=
m_ClipFillType
;
e2FillType2
=
m_SubjFillType
;
}
cInt
e1Wc
,
e2Wc
;
switch
(
e1FillType
)
{
case
pftPositive
:
e1Wc
=
e1
->
WindCnt
;
break
;
case
pftNegative
:
e1Wc
=
-
e1
->
WindCnt
;
break
;
default:
e1Wc
=
Abs
(
e1
->
WindCnt
);
}
switch
(
e2FillType
)
{
case
pftPositive
:
e2Wc
=
e2
->
WindCnt
;
break
;
case
pftNegative
:
e2Wc
=
-
e2
->
WindCnt
;
break
;
default:
e2Wc
=
Abs
(
e2
->
WindCnt
);
}
if
(
e1Contributing
&&
e2Contributing
)
{
if
((
e1Wc
!=
0
&&
e1Wc
!=
1
)
||
(
e2Wc
!=
0
&&
e2Wc
!=
1
)
||
(
e1
->
PolyTyp
!=
e2
->
PolyTyp
&&
m_ClipType
!=
ctXor
)
)
{
AddLocalMaxPoly
(
e1
,
e2
,
Pt
);
}
else
{
AddOutPt
(
e1
,
Pt
);
AddOutPt
(
e2
,
Pt
);
SwapSides
(
*
e1
,
*
e2
);
SwapPolyIndexes
(
*
e1
,
*
e2
);
}
}
else
if
(
e1Contributing
)
{
if
(
e2Wc
==
0
||
e2Wc
==
1
)
{
AddOutPt
(
e1
,
Pt
);
SwapSides
(
*
e1
,
*
e2
);
SwapPolyIndexes
(
*
e1
,
*
e2
);
}
}
else
if
(
e2Contributing
)
{
if
(
e1Wc
==
0
||
e1Wc
==
1
)
{
AddOutPt
(
e2
,
Pt
);
SwapSides
(
*
e1
,
*
e2
);
SwapPolyIndexes
(
*
e1
,
*
e2
);
}
}
else
if
(
(
e1Wc
==
0
||
e1Wc
==
1
)
&&
(
e2Wc
==
0
||
e2Wc
==
1
))
{
//neither edge is currently contributing ...
cInt
e1Wc2
,
e2Wc2
;
switch
(
e1FillType2
)
{
case
pftPositive
:
e1Wc2
=
e1
->
WindCnt2
;
break
;
case
pftNegative
:
e1Wc2
=
-
e1
->
WindCnt2
;
break
;
default:
e1Wc2
=
Abs
(
e1
->
WindCnt2
);
}
switch
(
e2FillType2
)
{
case
pftPositive
:
e2Wc2
=
e2
->
WindCnt2
;
break
;
case
pftNegative
:
e2Wc2
=
-
e2
->
WindCnt2
;
break
;
default:
e2Wc2
=
Abs
(
e2
->
WindCnt2
);
}
if
(
e1
->
PolyTyp
!=
e2
->
PolyTyp
)
{
AddLocalMinPoly
(
e1
,
e2
,
Pt
);
}
else
if
(
e1Wc
==
1
&&
e2Wc
==
1
)
switch
(
m_ClipType
)
{
case
ctIntersection
:
if
(
e1Wc2
>
0
&&
e2Wc2
>
0
)
AddLocalMinPoly
(
e1
,
e2
,
Pt
);
break
;
case
ctUnion
:
if
(
e1Wc2
<=
0
&&
e2Wc2
<=
0
)
AddLocalMinPoly
(
e1
,
e2
,
Pt
);
break
;
case
ctDifference
:
if
(((
e1
->
PolyTyp
==
ptClip
)
&&
(
e1Wc2
>
0
)
&&
(
e2Wc2
>
0
))
||
((
e1
->
PolyTyp
==
ptSubject
)
&&
(
e1Wc2
<=
0
)
&&
(
e2Wc2
<=
0
)))
AddLocalMinPoly
(
e1
,
e2
,
Pt
);
break
;
case
ctXor
:
AddLocalMinPoly
(
e1
,
e2
,
Pt
);
}
else
SwapSides
(
*
e1
,
*
e2
);
}
}
//------------------------------------------------------------------------------
void
Clipper
::
SetHoleState
(
TEdge
*
e
,
OutRec
*
outrec
)
{
TEdge
*
e2
=
e
->
PrevInAEL
;
TEdge
*
eTmp
=
0
;
while
(
e2
)
{
if
(
e2
->
OutIdx
>=
0
&&
e2
->
WindDelta
!=
0
)
{
if
(
!
eTmp
)
eTmp
=
e2
;
else
if
(
eTmp
->
OutIdx
==
e2
->
OutIdx
)
eTmp
=
0
;
}
e2
=
e2
->
PrevInAEL
;
}
if
(
!
eTmp
)
{
outrec
->
FirstLeft
=
0
;
outrec
->
IsHole
=
false
;
}
else
{
outrec
->
FirstLeft
=
m_PolyOuts
[
eTmp
->
OutIdx
];
outrec
->
IsHole
=
!
outrec
->
FirstLeft
->
IsHole
;
}
}
//------------------------------------------------------------------------------
OutRec
*
GetLowermostRec
(
OutRec
*
outRec1
,
OutRec
*
outRec2
)
{
//work out which polygon fragment has the correct hole state ...
if
(
!
outRec1
->
BottomPt
)
outRec1
->
BottomPt
=
GetBottomPt
(
outRec1
->
Pts
);
if
(
!
outRec2
->
BottomPt
)
outRec2
->
BottomPt
=
GetBottomPt
(
outRec2
->
Pts
);
OutPt
*
OutPt1
=
outRec1
->
BottomPt
;
OutPt
*
OutPt2
=
outRec2
->
BottomPt
;
if
(
OutPt1
->
Pt
.
Y
>
OutPt2
->
Pt
.
Y
)
return
outRec1
;
else
if
(
OutPt1
->
Pt
.
Y
<
OutPt2
->
Pt
.
Y
)
return
outRec2
;
else
if
(
OutPt1
->
Pt
.
X
<
OutPt2
->
Pt
.
X
)
return
outRec1
;
else
if
(
OutPt1
->
Pt
.
X
>
OutPt2
->
Pt
.
X
)
return
outRec2
;
else
if
(
OutPt1
->
Next
==
OutPt1
)
return
outRec2
;
else
if
(
OutPt2
->
Next
==
OutPt2
)
return
outRec1
;
else
if
(
FirstIsBottomPt
(
OutPt1
,
OutPt2
))
return
outRec1
;
else
return
outRec2
;
}
//------------------------------------------------------------------------------
bool
OutRec1RightOfOutRec2
(
OutRec
*
outRec1
,
OutRec
*
outRec2
)
{
do
{
outRec1
=
outRec1
->
FirstLeft
;
if
(
outRec1
==
outRec2
)
return
true
;
}
while
(
outRec1
);
return
false
;
}
//------------------------------------------------------------------------------
OutRec
*
Clipper
::
GetOutRec
(
int
Idx
)
{
OutRec
*
outrec
=
m_PolyOuts
[
Idx
];
while
(
outrec
!=
m_PolyOuts
[
outrec
->
Idx
])
outrec
=
m_PolyOuts
[
outrec
->
Idx
];
return
outrec
;
}
//------------------------------------------------------------------------------
void
Clipper
::
AppendPolygon
(
TEdge
*
e1
,
TEdge
*
e2
)
{
//get the start and ends of both output polygons ...
OutRec
*
outRec1
=
m_PolyOuts
[
e1
->
OutIdx
];
OutRec
*
outRec2
=
m_PolyOuts
[
e2
->
OutIdx
];
OutRec
*
holeStateRec
;
if
(
OutRec1RightOfOutRec2
(
outRec1
,
outRec2
))
holeStateRec
=
outRec2
;
else
if
(
OutRec1RightOfOutRec2
(
outRec2
,
outRec1
))
holeStateRec
=
outRec1
;
else
holeStateRec
=
GetLowermostRec
(
outRec1
,
outRec2
);
//get the start and ends of both output polygons and
//join e2 poly onto e1 poly and delete pointers to e2 ...
OutPt
*
p1_lft
=
outRec1
->
Pts
;
OutPt
*
p1_rt
=
p1_lft
->
Prev
;
OutPt
*
p2_lft
=
outRec2
->
Pts
;
OutPt
*
p2_rt
=
p2_lft
->
Prev
;
//join e2 poly onto e1 poly and delete pointers to e2 ...
if
(
e1
->
Side
==
esLeft
)
{
if
(
e2
->
Side
==
esLeft
)
{
//z y x a b c
ReversePolyPtLinks
(
p2_lft
);
p2_lft
->
Next
=
p1_lft
;
p1_lft
->
Prev
=
p2_lft
;
p1_rt
->
Next
=
p2_rt
;
p2_rt
->
Prev
=
p1_rt
;
outRec1
->
Pts
=
p2_rt
;
}
else
{
//x y z a b c
p2_rt
->
Next
=
p1_lft
;
p1_lft
->
Prev
=
p2_rt
;
p2_lft
->
Prev
=
p1_rt
;
p1_rt
->
Next
=
p2_lft
;
outRec1
->
Pts
=
p2_lft
;
}
}
else
{
if
(
e2
->
Side
==
esRight
)
{
//a b c z y x
ReversePolyPtLinks
(
p2_lft
);
p1_rt
->
Next
=
p2_rt
;
p2_rt
->
Prev
=
p1_rt
;
p2_lft
->
Next
=
p1_lft
;
p1_lft
->
Prev
=
p2_lft
;
}
else
{
//a b c x y z
p1_rt
->
Next
=
p2_lft
;
p2_lft
->
Prev
=
p1_rt
;
p1_lft
->
Prev
=
p2_rt
;
p2_rt
->
Next
=
p1_lft
;
}
}
outRec1
->
BottomPt
=
0
;
if
(
holeStateRec
==
outRec2
)
{
if
(
outRec2
->
FirstLeft
!=
outRec1
)
outRec1
->
FirstLeft
=
outRec2
->
FirstLeft
;
outRec1
->
IsHole
=
outRec2
->
IsHole
;
}
outRec2
->
Pts
=
0
;
outRec2
->
BottomPt
=
0
;
outRec2
->
FirstLeft
=
outRec1
;
int
OKIdx
=
e1
->
OutIdx
;
int
ObsoleteIdx
=
e2
->
OutIdx
;
e1
->
OutIdx
=
Unassigned
;
//nb: safe because we only get here via AddLocalMaxPoly
e2
->
OutIdx
=
Unassigned
;
TEdge
*
e
=
m_ActiveEdges
;
while
(
e
)
{
if
(
e
->
OutIdx
==
ObsoleteIdx
)
{
e
->
OutIdx
=
OKIdx
;
e
->
Side
=
e1
->
Side
;
break
;
}
e
=
e
->
NextInAEL
;
}
outRec2
->
Idx
=
outRec1
->
Idx
;
}
//------------------------------------------------------------------------------
OutPt
*
Clipper
::
AddOutPt
(
TEdge
*
e
,
const
IntPoint
&
pt
)
{
if
(
e
->
OutIdx
<
0
)
{
OutRec
*
outRec
=
CreateOutRec
();
outRec
->
IsOpen
=
(
e
->
WindDelta
==
0
);
OutPt
*
newOp
=
new
OutPt
;
outRec
->
Pts
=
newOp
;
newOp
->
Idx
=
outRec
->
Idx
;
newOp
->
Pt
=
pt
;
newOp
->
Next
=
newOp
;
newOp
->
Prev
=
newOp
;
if
(
!
outRec
->
IsOpen
)
SetHoleState
(
e
,
outRec
);
e
->
OutIdx
=
outRec
->
Idx
;
return
newOp
;
}
else
{
OutRec
*
outRec
=
m_PolyOuts
[
e
->
OutIdx
];
//OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
OutPt
*
op
=
outRec
->
Pts
;
bool
ToFront
=
(
e
->
Side
==
esLeft
);
if
(
ToFront
&&
(
pt
==
op
->
Pt
))
return
op
;
else
if
(
!
ToFront
&&
(
pt
==
op
->
Prev
->
Pt
))
return
op
->
Prev
;
OutPt
*
newOp
=
new
OutPt
;
newOp
->
Idx
=
outRec
->
Idx
;
newOp
->
Pt
=
pt
;
newOp
->
Next
=
op
;
newOp
->
Prev
=
op
->
Prev
;
newOp
->
Prev
->
Next
=
newOp
;
op
->
Prev
=
newOp
;
if
(
ToFront
)
outRec
->
Pts
=
newOp
;
return
newOp
;
}
}
//------------------------------------------------------------------------------
OutPt
*
Clipper
::
GetLastOutPt
(
TEdge
*
e
)
{
OutRec
*
outRec
=
m_PolyOuts
[
e
->
OutIdx
];
if
(
e
->
Side
==
esLeft
)
return
outRec
->
Pts
;
else
return
outRec
->
Pts
->
Prev
;
}
//------------------------------------------------------------------------------
void
Clipper
::
ProcessHorizontals
()
{
TEdge
*
horzEdge
;
while
(
PopEdgeFromSEL
(
horzEdge
))
ProcessHorizontal
(
horzEdge
);
}
//------------------------------------------------------------------------------
inline
bool
IsMinima
(
TEdge
*
e
)
{
return
e
&&
(
e
->
Prev
->
NextInLML
!=
e
)
&&
(
e
->
Next
->
NextInLML
!=
e
);
}
//------------------------------------------------------------------------------
inline
bool
IsMaxima
(
TEdge
*
e
,
const
cInt
Y
)
{
return
e
&&
e
->
Top
.
Y
==
Y
&&
!
e
->
NextInLML
;
}
//------------------------------------------------------------------------------
inline
bool
IsIntermediate
(
TEdge
*
e
,
const
cInt
Y
)
{
return
e
->
Top
.
Y
==
Y
&&
e
->
NextInLML
;
}
//------------------------------------------------------------------------------
TEdge
*
GetMaximaPair
(
TEdge
*
e
)
{
if
((
e
->
Next
->
Top
==
e
->
Top
)
&&
!
e
->
Next
->
NextInLML
)
return
e
->
Next
;
else
if
((
e
->
Prev
->
Top
==
e
->
Top
)
&&
!
e
->
Prev
->
NextInLML
)
return
e
->
Prev
;
else
return
0
;
}
//------------------------------------------------------------------------------
TEdge
*
GetMaximaPairEx
(
TEdge
*
e
)
{
//as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal)
TEdge
*
result
=
GetMaximaPair
(
e
);
if
(
result
&&
(
result
->
OutIdx
==
Skip
||
(
result
->
NextInAEL
==
result
->
PrevInAEL
&&
!
IsHorizontal
(
*
result
))))
return
0
;
return
result
;
}
//------------------------------------------------------------------------------
void
Clipper
::
SwapPositionsInSEL
(
TEdge
*
Edge1
,
TEdge
*
Edge2
)
{
if
(
!
(
Edge1
->
NextInSEL
)
&&
!
(
Edge1
->
PrevInSEL
)
)
return
;
if
(
!
(
Edge2
->
NextInSEL
)
&&
!
(
Edge2
->
PrevInSEL
)
)
return
;
if
(
Edge1
->
NextInSEL
==
Edge2
)
{
TEdge
*
Next
=
Edge2
->
NextInSEL
;
if
(
Next
)
Next
->
PrevInSEL
=
Edge1
;
TEdge
*
Prev
=
Edge1
->
PrevInSEL
;
if
(
Prev
)
Prev
->
NextInSEL
=
Edge2
;
Edge2
->
PrevInSEL
=
Prev
;
Edge2
->
NextInSEL
=
Edge1
;
Edge1
->
PrevInSEL
=
Edge2
;
Edge1
->
NextInSEL
=
Next
;
}
else
if
(
Edge2
->
NextInSEL
==
Edge1
)
{
TEdge
*
Next
=
Edge1
->
NextInSEL
;
if
(
Next
)
Next
->
PrevInSEL
=
Edge2
;
TEdge
*
Prev
=
Edge2
->
PrevInSEL
;
if
(
Prev
)
Prev
->
NextInSEL
=
Edge1
;
Edge1
->
PrevInSEL
=
Prev
;
Edge1
->
NextInSEL
=
Edge2
;
Edge2
->
PrevInSEL
=
Edge1
;
Edge2
->
NextInSEL
=
Next
;
}
else
{
TEdge
*
Next
=
Edge1
->
NextInSEL
;
TEdge
*
Prev
=
Edge1
->
PrevInSEL
;
Edge1
->
NextInSEL
=
Edge2
->
NextInSEL
;
if
(
Edge1
->
NextInSEL
)
Edge1
->
NextInSEL
->
PrevInSEL
=
Edge1
;
Edge1
->
PrevInSEL
=
Edge2
->
PrevInSEL
;
if
(
Edge1
->
PrevInSEL
)
Edge1
->
PrevInSEL
->
NextInSEL
=
Edge1
;
Edge2
->
NextInSEL
=
Next
;
if
(
Edge2
->
NextInSEL
)
Edge2
->
NextInSEL
->
PrevInSEL
=
Edge2
;
Edge2
->
PrevInSEL
=
Prev
;
if
(
Edge2
->
PrevInSEL
)
Edge2
->
PrevInSEL
->
NextInSEL
=
Edge2
;
}
if
(
!
Edge1
->
PrevInSEL
)
m_SortedEdges
=
Edge1
;
else
if
(
!
Edge2
->
PrevInSEL
)
m_SortedEdges
=
Edge2
;
}
//------------------------------------------------------------------------------
TEdge
*
GetNextInAEL
(
TEdge
*
e
,
Direction
dir
)
{
return
dir
==
dLeftToRight
?
e
->
NextInAEL
:
e
->
PrevInAEL
;
}
//------------------------------------------------------------------------------
void
GetHorzDirection
(
TEdge
&
HorzEdge
,
Direction
&
Dir
,
cInt
&
Left
,
cInt
&
Right
)
{
if
(
HorzEdge
.
Bot
.
X
<
HorzEdge
.
Top
.
X
)
{
Left
=
HorzEdge
.
Bot
.
X
;
Right
=
HorzEdge
.
Top
.
X
;
Dir
=
dLeftToRight
;
}
else
{
Left
=
HorzEdge
.
Top
.
X
;
Right
=
HorzEdge
.
Bot
.
X
;
Dir
=
dRightToLeft
;
}
}
//------------------------------------------------------------------------
/*******************************************************************************
* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or *
* Bottom of a scanbeam) are processed as if layered. The order in which HEs *
* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] *
* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), *
* and with other non-horizontal edges [*]. Once these intersections are *
* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into *
* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. *
*******************************************************************************/
void
Clipper
::
ProcessHorizontal
(
TEdge
*
horzEdge
)
{
Direction
dir
;
cInt
horzLeft
,
horzRight
;
bool
IsOpen
=
(
horzEdge
->
WindDelta
==
0
);
GetHorzDirection
(
*
horzEdge
,
dir
,
horzLeft
,
horzRight
);
TEdge
*
eLastHorz
=
horzEdge
,
*
eMaxPair
=
0
;
while
(
eLastHorz
->
NextInLML
&&
IsHorizontal
(
*
eLastHorz
->
NextInLML
))
eLastHorz
=
eLastHorz
->
NextInLML
;
if
(
!
eLastHorz
->
NextInLML
)
eMaxPair
=
GetMaximaPair
(
eLastHorz
);
MaximaList
::
const_iterator
maxIt
;
MaximaList
::
const_reverse_iterator
maxRit
;
if
(
m_Maxima
.
size
()
>
0
)
{
//get the first maxima in range (X) ...
if
(
dir
==
dLeftToRight
)
{
maxIt
=
m_Maxima
.
begin
();
while
(
maxIt
!=
m_Maxima
.
end
()
&&
*
maxIt
<=
horzEdge
->
Bot
.
X
)
maxIt
++
;
if
(
maxIt
!=
m_Maxima
.
end
()
&&
*
maxIt
>=
eLastHorz
->
Top
.
X
)
maxIt
=
m_Maxima
.
end
();
}
else
{
maxRit
=
m_Maxima
.
rbegin
();
while
(
maxRit
!=
m_Maxima
.
rend
()
&&
*
maxRit
>
horzEdge
->
Bot
.
X
)
maxRit
++
;
if
(
maxRit
!=
m_Maxima
.
rend
()
&&
*
maxRit
<=
eLastHorz
->
Top
.
X
)
maxRit
=
m_Maxima
.
rend
();
}
}
OutPt
*
op1
=
0
;
for
(;;)
//loop through consec. horizontal edges
{
bool
IsLastHorz
=
(
horzEdge
==
eLastHorz
);
TEdge
*
e
=
GetNextInAEL
(
horzEdge
,
dir
);
while
(
e
)
{
//this code block inserts extra coords into horizontal edges (in output
//polygons) whereever maxima touch these horizontal edges. This helps
//'simplifying' polygons (ie if the Simplify property is set).
if
(
m_Maxima
.
size
()
>
0
)
{
if
(
dir
==
dLeftToRight
)
{
while
(
maxIt
!=
m_Maxima
.
end
()
&&
*
maxIt
<
e
->
Curr
.
X
)
{
if
(
horzEdge
->
OutIdx
>=
0
&&
!
IsOpen
)
AddOutPt
(
horzEdge
,
IntPoint
(
*
maxIt
,
horzEdge
->
Bot
.
Y
));
maxIt
++
;
}
}
else
{
while
(
maxRit
!=
m_Maxima
.
rend
()
&&
*
maxRit
>
e
->
Curr
.
X
)
{
if
(
horzEdge
->
OutIdx
>=
0
&&
!
IsOpen
)
AddOutPt
(
horzEdge
,
IntPoint
(
*
maxRit
,
horzEdge
->
Bot
.
Y
));
maxRit
++
;
}
}
};
if
((
dir
==
dLeftToRight
&&
e
->
Curr
.
X
>
horzRight
)
||
(
dir
==
dRightToLeft
&&
e
->
Curr
.
X
<
horzLeft
))
break
;
//Also break if we've got to the end of an intermediate horizontal edge ...
//nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
if
(
e
->
Curr
.
X
==
horzEdge
->
Top
.
X
&&
horzEdge
->
NextInLML
&&
e
->
Dx
<
horzEdge
->
NextInLML
->
Dx
)
break
;
if
(
horzEdge
->
OutIdx
>=
0
&&
!
IsOpen
)
//note: may be done multiple times
{
op1
=
AddOutPt
(
horzEdge
,
e
->
Curr
);
TEdge
*
eNextHorz
=
m_SortedEdges
;
while
(
eNextHorz
)
{
if
(
eNextHorz
->
OutIdx
>=
0
&&
HorzSegmentsOverlap
(
horzEdge
->
Bot
.
X
,
horzEdge
->
Top
.
X
,
eNextHorz
->
Bot
.
X
,
eNextHorz
->
Top
.
X
))
{
OutPt
*
op2
=
GetLastOutPt
(
eNextHorz
);
AddJoin
(
op2
,
op1
,
eNextHorz
->
Top
);
}
eNextHorz
=
eNextHorz
->
NextInSEL
;
}
AddGhostJoin
(
op1
,
horzEdge
->
Bot
);
}
//OK, so far we're still in range of the horizontal Edge but make sure
//we're at the last of consec. horizontals when matching with eMaxPair
if
(
e
==
eMaxPair
&&
IsLastHorz
)
{
if
(
horzEdge
->
OutIdx
>=
0
)
AddLocalMaxPoly
(
horzEdge
,
eMaxPair
,
horzEdge
->
Top
);
DeleteFromAEL
(
horzEdge
);
DeleteFromAEL
(
eMaxPair
);
return
;
}
if
(
dir
==
dLeftToRight
)
{
IntPoint
Pt
=
IntPoint
(
e
->
Curr
.
X
,
horzEdge
->
Curr
.
Y
);
IntersectEdges
(
horzEdge
,
e
,
Pt
);
}
else
{
IntPoint
Pt
=
IntPoint
(
e
->
Curr
.
X
,
horzEdge
->
Curr
.
Y
);
IntersectEdges
(
e
,
horzEdge
,
Pt
);
}
TEdge
*
eNext
=
GetNextInAEL
(
e
,
dir
);
SwapPositionsInAEL
(
horzEdge
,
e
);
e
=
eNext
;
}
//end while(e)
//Break out of loop if HorzEdge.NextInLML is not also horizontal ...
if
(
!
horzEdge
->
NextInLML
||
!
IsHorizontal
(
*
horzEdge
->
NextInLML
))
break
;
UpdateEdgeIntoAEL
(
horzEdge
);
if
(
horzEdge
->
OutIdx
>=
0
)
AddOutPt
(
horzEdge
,
horzEdge
->
Bot
);
GetHorzDirection
(
*
horzEdge
,
dir
,
horzLeft
,
horzRight
);
}
//end for (;;)
if
(
horzEdge
->
OutIdx
>=
0
&&
!
op1
)
{
op1
=
GetLastOutPt
(
horzEdge
);
TEdge
*
eNextHorz
=
m_SortedEdges
;
while
(
eNextHorz
)
{
if
(
eNextHorz
->
OutIdx
>=
0
&&
HorzSegmentsOverlap
(
horzEdge
->
Bot
.
X
,
horzEdge
->
Top
.
X
,
eNextHorz
->
Bot
.
X
,
eNextHorz
->
Top
.
X
))
{
OutPt
*
op2
=
GetLastOutPt
(
eNextHorz
);
AddJoin
(
op2
,
op1
,
eNextHorz
->
Top
);
}
eNextHorz
=
eNextHorz
->
NextInSEL
;
}
AddGhostJoin
(
op1
,
horzEdge
->
Top
);
}
if
(
horzEdge
->
NextInLML
)
{
if
(
horzEdge
->
OutIdx
>=
0
)
{
op1
=
AddOutPt
(
horzEdge
,
horzEdge
->
Top
);
UpdateEdgeIntoAEL
(
horzEdge
);
if
(
horzEdge
->
WindDelta
==
0
)
return
;
//nb: HorzEdge is no longer horizontal here
TEdge
*
ePrev
=
horzEdge
->
PrevInAEL
;
TEdge
*
eNext
=
horzEdge
->
NextInAEL
;
if
(
ePrev
&&
ePrev
->
Curr
.
X
==
horzEdge
->
Bot
.
X
&&
ePrev
->
Curr
.
Y
==
horzEdge
->
Bot
.
Y
&&
ePrev
->
WindDelta
!=
0
&&
(
ePrev
->
OutIdx
>=
0
&&
ePrev
->
Curr
.
Y
>
ePrev
->
Top
.
Y
&&
SlopesEqual
(
*
horzEdge
,
*
ePrev
,
m_UseFullRange
)))
{
OutPt
*
op2
=
AddOutPt
(
ePrev
,
horzEdge
->
Bot
);
AddJoin
(
op1
,
op2
,
horzEdge
->
Top
);
}
else
if
(
eNext
&&
eNext
->
Curr
.
X
==
horzEdge
->
Bot
.
X
&&
eNext
->
Curr
.
Y
==
horzEdge
->
Bot
.
Y
&&
eNext
->
WindDelta
!=
0
&&
eNext
->
OutIdx
>=
0
&&
eNext
->
Curr
.
Y
>
eNext
->
Top
.
Y
&&
SlopesEqual
(
*
horzEdge
,
*
eNext
,
m_UseFullRange
))
{
OutPt
*
op2
=
AddOutPt
(
eNext
,
horzEdge
->
Bot
);
AddJoin
(
op1
,
op2
,
horzEdge
->
Top
);
}
}
else
UpdateEdgeIntoAEL
(
horzEdge
);
}
else
{
if
(
horzEdge
->
OutIdx
>=
0
)
AddOutPt
(
horzEdge
,
horzEdge
->
Top
);
DeleteFromAEL
(
horzEdge
);
}
}
//------------------------------------------------------------------------------
bool
Clipper
::
ProcessIntersections
(
const
cInt
topY
)
{
if
(
!
m_ActiveEdges
)
return
true
;
try
{
BuildIntersectList
(
topY
);
size_t
IlSize
=
m_IntersectList
.
size
();
if
(
IlSize
==
0
)
return
true
;
if
(
IlSize
==
1
||
FixupIntersectionOrder
())
ProcessIntersectList
();
else
return
false
;
}
catch
(...)
{
m_SortedEdges
=
0
;
DisposeIntersectNodes
();
throw
clipperException
(
"ProcessIntersections error"
);
}
m_SortedEdges
=
0
;
return
true
;
}
//------------------------------------------------------------------------------
void
Clipper
::
DisposeIntersectNodes
()
{
for
(
size_t
i
=
0
;
i
<
m_IntersectList
.
size
();
++
i
)
delete
m_IntersectList
[
i
];
m_IntersectList
.
clear
();
}
//------------------------------------------------------------------------------
void
Clipper
::
BuildIntersectList
(
const
cInt
topY
)
{
if
(
!
m_ActiveEdges
)
return
;
//prepare for sorting ...
TEdge
*
e
=
m_ActiveEdges
;
m_SortedEdges
=
e
;
while
(
e
)
{
e
->
PrevInSEL
=
e
->
PrevInAEL
;
e
->
NextInSEL
=
e
->
NextInAEL
;
e
->
Curr
.
X
=
TopX
(
*
e
,
topY
);
e
=
e
->
NextInAEL
;
}
//bubblesort ...
bool
isModified
;
do
{
isModified
=
false
;
e
=
m_SortedEdges
;
while
(
e
->
NextInSEL
)
{
TEdge
*
eNext
=
e
->
NextInSEL
;
IntPoint
Pt
;
if
(
e
->
Curr
.
X
>
eNext
->
Curr
.
X
)
{
IntersectPoint
(
*
e
,
*
eNext
,
Pt
);
if
(
Pt
.
Y
<
topY
)
Pt
=
IntPoint
(
TopX
(
*
e
,
topY
),
topY
);
IntersectNode
*
newNode
=
new
IntersectNode
;
newNode
->
Edge1
=
e
;
newNode
->
Edge2
=
eNext
;
newNode
->
Pt
=
Pt
;
m_IntersectList
.
push_back
(
newNode
);
SwapPositionsInSEL
(
e
,
eNext
);
isModified
=
true
;
}
else
e
=
eNext
;
}
if
(
e
->
PrevInSEL
)
e
->
PrevInSEL
->
NextInSEL
=
0
;
else
break
;
}
while
(
isModified
);
m_SortedEdges
=
0
;
//important
}
//------------------------------------------------------------------------------
void
Clipper
::
ProcessIntersectList
()
{
for
(
size_t
i
=
0
;
i
<
m_IntersectList
.
size
();
++
i
)
{
IntersectNode
*
iNode
=
m_IntersectList
[
i
];
{
IntersectEdges
(
iNode
->
Edge1
,
iNode
->
Edge2
,
iNode
->
Pt
);
SwapPositionsInAEL
(
iNode
->
Edge1
,
iNode
->
Edge2
);
}
delete
iNode
;
}
m_IntersectList
.
clear
();
}
//------------------------------------------------------------------------------
bool
IntersectListSort
(
IntersectNode
*
node1
,
IntersectNode
*
node2
)
{
return
node2
->
Pt
.
Y
<
node1
->
Pt
.
Y
;
}
//------------------------------------------------------------------------------
inline
bool
EdgesAdjacent
(
const
IntersectNode
&
inode
)
{
return
(
inode
.
Edge1
->
NextInSEL
==
inode
.
Edge2
)
||
(
inode
.
Edge1
->
PrevInSEL
==
inode
.
Edge2
);
}
//------------------------------------------------------------------------------
bool
Clipper
::
FixupIntersectionOrder
()
{
//pre-condition: intersections are sorted Bottom-most first.
//Now it's crucial that intersections are made only between adjacent edges,
//so to ensure this the order of intersections may need adjusting ...
CopyAELToSEL
();
std
::
sort
(
m_IntersectList
.
begin
(),
m_IntersectList
.
end
(),
IntersectListSort
);
size_t
cnt
=
m_IntersectList
.
size
();
for
(
size_t
i
=
0
;
i
<
cnt
;
++
i
)
{
if
(
!
EdgesAdjacent
(
*
m_IntersectList
[
i
]))
{
size_t
j
=
i
+
1
;
while
(
j
<
cnt
&&
!
EdgesAdjacent
(
*
m_IntersectList
[
j
]))
j
++
;
if
(
j
==
cnt
)
return
false
;
std
::
swap
(
m_IntersectList
[
i
],
m_IntersectList
[
j
]);
}
SwapPositionsInSEL
(
m_IntersectList
[
i
]
->
Edge1
,
m_IntersectList
[
i
]
->
Edge2
);
}
return
true
;
}
//------------------------------------------------------------------------------
void
Clipper
::
DoMaxima
(
TEdge
*
e
)
{
TEdge
*
eMaxPair
=
GetMaximaPairEx
(
e
);
if
(
!
eMaxPair
)
{
if
(
e
->
OutIdx
>=
0
)
AddOutPt
(
e
,
e
->
Top
);
DeleteFromAEL
(
e
);
return
;
}
TEdge
*
eNext
=
e
->
NextInAEL
;
while
(
eNext
&&
eNext
!=
eMaxPair
)
{
IntersectEdges
(
e
,
eNext
,
e
->
Top
);
SwapPositionsInAEL
(
e
,
eNext
);
eNext
=
e
->
NextInAEL
;
}
if
(
e
->
OutIdx
==
Unassigned
&&
eMaxPair
->
OutIdx
==
Unassigned
)
{
DeleteFromAEL
(
e
);
DeleteFromAEL
(
eMaxPair
);
}
else
if
(
e
->
OutIdx
>=
0
&&
eMaxPair
->
OutIdx
>=
0
)
{
if
(
e
->
OutIdx
>=
0
)
AddLocalMaxPoly
(
e
,
eMaxPair
,
e
->
Top
);
DeleteFromAEL
(
e
);
DeleteFromAEL
(
eMaxPair
);
}
#ifdef use_lines
else
if
(
e
->
WindDelta
==
0
)
{
if
(
e
->
OutIdx
>=
0
)
{
AddOutPt
(
e
,
e
->
Top
);
e
->
OutIdx
=
Unassigned
;
}
DeleteFromAEL
(
e
);
if
(
eMaxPair
->
OutIdx
>=
0
)
{
AddOutPt
(
eMaxPair
,
e
->
Top
);
eMaxPair
->
OutIdx
=
Unassigned
;
}
DeleteFromAEL
(
eMaxPair
);
}
#endif
else
throw
clipperException
(
"DoMaxima error"
);
}
//------------------------------------------------------------------------------
void
Clipper
::
ProcessEdgesAtTopOfScanbeam
(
const
cInt
topY
)
{
TEdge
*
e
=
m_ActiveEdges
;
while
(
e
)
{
//1. process maxima, treating them as if they're 'bent' horizontal edges,
// but exclude maxima with horizontal edges. nb: e can't be a horizontal.
bool
IsMaximaEdge
=
IsMaxima
(
e
,
topY
);
if
(
IsMaximaEdge
)
{
TEdge
*
eMaxPair
=
GetMaximaPairEx
(
e
);
IsMaximaEdge
=
(
!
eMaxPair
||
!
IsHorizontal
(
*
eMaxPair
));
}
if
(
IsMaximaEdge
)
{
if
(
m_StrictSimple
)
m_Maxima
.
push_back
(
e
->
Top
.
X
);
TEdge
*
ePrev
=
e
->
PrevInAEL
;
DoMaxima
(
e
);
if
(
!
ePrev
)
e
=
m_ActiveEdges
;
else
e
=
ePrev
->
NextInAEL
;
}
else
{
//2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
if
(
IsIntermediate
(
e
,
topY
)
&&
IsHorizontal
(
*
e
->
NextInLML
))
{
UpdateEdgeIntoAEL
(
e
);
if
(
e
->
OutIdx
>=
0
)
AddOutPt
(
e
,
e
->
Bot
);
AddEdgeToSEL
(
e
);
}
else
{
e
->
Curr
.
X
=
TopX
(
*
e
,
topY
);
e
->
Curr
.
Y
=
topY
;
}
//When StrictlySimple and 'e' is being touched by another edge, then
//make sure both edges have a vertex here ...
if
(
m_StrictSimple
)
{
TEdge
*
ePrev
=
e
->
PrevInAEL
;
if
((
e
->
OutIdx
>=
0
)
&&
(
e
->
WindDelta
!=
0
)
&&
ePrev
&&
(
ePrev
->
OutIdx
>=
0
)
&&
(
ePrev
->
Curr
.
X
==
e
->
Curr
.
X
)
&&
(
ePrev
->
WindDelta
!=
0
))
{
IntPoint
pt
=
e
->
Curr
;
#ifdef use_xyz
SetZ
(
pt
,
*
ePrev
,
*
e
);
#endif
OutPt
*
op
=
AddOutPt
(
ePrev
,
pt
);
OutPt
*
op2
=
AddOutPt
(
e
,
pt
);
AddJoin
(
op
,
op2
,
pt
);
//StrictlySimple (type-3) join
}
}
e
=
e
->
NextInAEL
;
}
}
//3. Process horizontals at the Top of the scanbeam ...
m_Maxima
.
sort
();
ProcessHorizontals
();
m_Maxima
.
clear
();
//4. Promote intermediate vertices ...
e
=
m_ActiveEdges
;
while
(
e
)
{
if
(
IsIntermediate
(
e
,
topY
))
{
OutPt
*
op
=
0
;
if
(
e
->
OutIdx
>=
0
)
op
=
AddOutPt
(
e
,
e
->
Top
);
UpdateEdgeIntoAEL
(
e
);
//if output polygons share an edge, they'll need joining later ...
TEdge
*
ePrev
=
e
->
PrevInAEL
;
TEdge
*
eNext
=
e
->
NextInAEL
;
if
(
ePrev
&&
ePrev
->
Curr
.
X
==
e
->
Bot
.
X
&&
ePrev
->
Curr
.
Y
==
e
->
Bot
.
Y
&&
op
&&
ePrev
->
OutIdx
>=
0
&&
ePrev
->
Curr
.
Y
>
ePrev
->
Top
.
Y
&&
SlopesEqual
(
e
->
Curr
,
e
->
Top
,
ePrev
->
Curr
,
ePrev
->
Top
,
m_UseFullRange
)
&&
(
e
->
WindDelta
!=
0
)
&&
(
ePrev
->
WindDelta
!=
0
))
{
OutPt
*
op2
=
AddOutPt
(
ePrev
,
e
->
Bot
);
AddJoin
(
op
,
op2
,
e
->
Top
);
}
else
if
(
eNext
&&
eNext
->
Curr
.
X
==
e
->
Bot
.
X
&&
eNext
->
Curr
.
Y
==
e
->
Bot
.
Y
&&
op
&&
eNext
->
OutIdx
>=
0
&&
eNext
->
Curr
.
Y
>
eNext
->
Top
.
Y
&&
SlopesEqual
(
e
->
Curr
,
e
->
Top
,
eNext
->
Curr
,
eNext
->
Top
,
m_UseFullRange
)
&&
(
e
->
WindDelta
!=
0
)
&&
(
eNext
->
WindDelta
!=
0
))
{
OutPt
*
op2
=
AddOutPt
(
eNext
,
e
->
Bot
);
AddJoin
(
op
,
op2
,
e
->
Top
);
}
}
e
=
e
->
NextInAEL
;
}
}
//------------------------------------------------------------------------------
void
Clipper
::
FixupOutPolyline
(
OutRec
&
outrec
)
{
OutPt
*
pp
=
outrec
.
Pts
;
OutPt
*
lastPP
=
pp
->
Prev
;
while
(
pp
!=
lastPP
)
{
pp
=
pp
->
Next
;
if
(
pp
->
Pt
==
pp
->
Prev
->
Pt
)
{
if
(
pp
==
lastPP
)
lastPP
=
pp
->
Prev
;
OutPt
*
tmpPP
=
pp
->
Prev
;
tmpPP
->
Next
=
pp
->
Next
;
pp
->
Next
->
Prev
=
tmpPP
;
delete
pp
;
pp
=
tmpPP
;
}
}
if
(
pp
==
pp
->
Prev
)
{
DisposeOutPts
(
pp
);
outrec
.
Pts
=
0
;
return
;
}
}
//------------------------------------------------------------------------------
void
Clipper
::
FixupOutPolygon
(
OutRec
&
outrec
)
{
//FixupOutPolygon() - removes duplicate points and simplifies consecutive
//parallel edges by removing the middle vertex.
OutPt
*
lastOK
=
0
;
outrec
.
BottomPt
=
0
;
OutPt
*
pp
=
outrec
.
Pts
;
bool
preserveCol
=
m_PreserveCollinear
||
m_StrictSimple
;
for
(;;)
{
if
(
pp
->
Prev
==
pp
||
pp
->
Prev
==
pp
->
Next
)
{
DisposeOutPts
(
pp
);
outrec
.
Pts
=
0
;
return
;
}
//test for duplicate points and collinear edges ...
if
((
pp
->
Pt
==
pp
->
Next
->
Pt
)
||
(
pp
->
Pt
==
pp
->
Prev
->
Pt
)
||
(
SlopesEqual
(
pp
->
Prev
->
Pt
,
pp
->
Pt
,
pp
->
Next
->
Pt
,
m_UseFullRange
)
&&
(
!
preserveCol
||
!
Pt2IsBetweenPt1AndPt3
(
pp
->
Prev
->
Pt
,
pp
->
Pt
,
pp
->
Next
->
Pt
))))
{
lastOK
=
0
;
OutPt
*
tmp
=
pp
;
pp
->
Prev
->
Next
=
pp
->
Next
;
pp
->
Next
->
Prev
=
pp
->
Prev
;
pp
=
pp
->
Prev
;
delete
tmp
;
}
else
if
(
pp
==
lastOK
)
break
;
else
{
if
(
!
lastOK
)
lastOK
=
pp
;
pp
=
pp
->
Next
;
}
}
outrec
.
Pts
=
pp
;
}
//------------------------------------------------------------------------------
int
PointCount
(
OutPt
*
Pts
)
{
if
(
!
Pts
)
return
0
;
int
result
=
0
;
OutPt
*
p
=
Pts
;
do
{
result
++
;
p
=
p
->
Next
;
}
while
(
p
!=
Pts
);
return
result
;
}
//------------------------------------------------------------------------------
void
Clipper
::
BuildResult
(
Paths
&
polys
)
{
polys
.
reserve
(
m_PolyOuts
.
size
());
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
++
i
)
{
if
(
!
m_PolyOuts
[
i
]
->
Pts
)
continue
;
Path
pg
;
OutPt
*
p
=
m_PolyOuts
[
i
]
->
Pts
->
Prev
;
int
cnt
=
PointCount
(
p
);
if
(
cnt
<
2
)
continue
;
pg
.
reserve
(
cnt
);
for
(
int
i
=
0
;
i
<
cnt
;
++
i
)
{
pg
.
push_back
(
p
->
Pt
);
p
=
p
->
Prev
;
}
polys
.
push_back
(
pg
);
}
}
//------------------------------------------------------------------------------
void
Clipper
::
BuildResult2
(
PolyTree
&
polytree
)
{
polytree
.
Clear
();
polytree
.
AllNodes
.
reserve
(
m_PolyOuts
.
size
());
//add each output polygon/contour to polytree ...
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
i
++
)
{
OutRec
*
outRec
=
m_PolyOuts
[
i
];
int
cnt
=
PointCount
(
outRec
->
Pts
);
if
((
outRec
->
IsOpen
&&
cnt
<
2
)
||
(
!
outRec
->
IsOpen
&&
cnt
<
3
))
continue
;
FixHoleLinkage
(
*
outRec
);
PolyNode
*
pn
=
new
PolyNode
();
//nb: polytree takes ownership of all the PolyNodes
polytree
.
AllNodes
.
push_back
(
pn
);
outRec
->
PolyNd
=
pn
;
pn
->
Parent
=
0
;
pn
->
Index
=
0
;
pn
->
Contour
.
reserve
(
cnt
);
OutPt
*
op
=
outRec
->
Pts
->
Prev
;
for
(
int
j
=
0
;
j
<
cnt
;
j
++
)
{
pn
->
Contour
.
push_back
(
op
->
Pt
);
op
=
op
->
Prev
;
}
}
//fixup PolyNode links etc ...
polytree
.
Childs
.
reserve
(
m_PolyOuts
.
size
());
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
i
++
)
{
OutRec
*
outRec
=
m_PolyOuts
[
i
];
if
(
!
outRec
->
PolyNd
)
continue
;
if
(
outRec
->
IsOpen
)
{
outRec
->
PolyNd
->
m_IsOpen
=
true
;
polytree
.
AddChild
(
*
outRec
->
PolyNd
);
}
else
if
(
outRec
->
FirstLeft
&&
outRec
->
FirstLeft
->
PolyNd
)
outRec
->
FirstLeft
->
PolyNd
->
AddChild
(
*
outRec
->
PolyNd
);
else
polytree
.
AddChild
(
*
outRec
->
PolyNd
);
}
}
//------------------------------------------------------------------------------
void
SwapIntersectNodes
(
IntersectNode
&
int1
,
IntersectNode
&
int2
)
{
//just swap the contents (because fIntersectNodes is a single-linked-list)
IntersectNode
inode
=
int1
;
//gets a copy of Int1
int1
.
Edge1
=
int2
.
Edge1
;
int1
.
Edge2
=
int2
.
Edge2
;
int1
.
Pt
=
int2
.
Pt
;
int2
.
Edge1
=
inode
.
Edge1
;
int2
.
Edge2
=
inode
.
Edge2
;
int2
.
Pt
=
inode
.
Pt
;
}
//------------------------------------------------------------------------------
inline
bool
E2InsertsBeforeE1
(
TEdge
&
e1
,
TEdge
&
e2
)
{
if
(
e2
.
Curr
.
X
==
e1
.
Curr
.
X
)
{
if
(
e2
.
Top
.
Y
>
e1
.
Top
.
Y
)
return
e2
.
Top
.
X
<
TopX
(
e1
,
e2
.
Top
.
Y
);
else
return
e1
.
Top
.
X
>
TopX
(
e2
,
e1
.
Top
.
Y
);
}
else
return
e2
.
Curr
.
X
<
e1
.
Curr
.
X
;
}
//------------------------------------------------------------------------------
bool
GetOverlap
(
const
cInt
a1
,
const
cInt
a2
,
const
cInt
b1
,
const
cInt
b2
,
cInt
&
Left
,
cInt
&
Right
)
{
if
(
a1
<
a2
)
{
if
(
b1
<
b2
)
{
Left
=
std
::
max
(
a1
,
b1
);
Right
=
std
::
min
(
a2
,
b2
);}
else
{
Left
=
std
::
max
(
a1
,
b2
);
Right
=
std
::
min
(
a2
,
b1
);}
}
else
{
if
(
b1
<
b2
)
{
Left
=
std
::
max
(
a2
,
b1
);
Right
=
std
::
min
(
a1
,
b2
);}
else
{
Left
=
std
::
max
(
a2
,
b2
);
Right
=
std
::
min
(
a1
,
b1
);}
}
return
Left
<
Right
;
}
//------------------------------------------------------------------------------
inline
void
UpdateOutPtIdxs
(
OutRec
&
outrec
)
{
OutPt
*
op
=
outrec
.
Pts
;
do
{
op
->
Idx
=
outrec
.
Idx
;
op
=
op
->
Prev
;
}
while
(
op
!=
outrec
.
Pts
);
}
//------------------------------------------------------------------------------
void
Clipper
::
InsertEdgeIntoAEL
(
TEdge
*
edge
,
TEdge
*
startEdge
)
{
if
(
!
m_ActiveEdges
)
{
edge
->
PrevInAEL
=
0
;
edge
->
NextInAEL
=
0
;
m_ActiveEdges
=
edge
;
}
else
if
(
!
startEdge
&&
E2InsertsBeforeE1
(
*
m_ActiveEdges
,
*
edge
))
{
edge
->
PrevInAEL
=
0
;
edge
->
NextInAEL
=
m_ActiveEdges
;
m_ActiveEdges
->
PrevInAEL
=
edge
;
m_ActiveEdges
=
edge
;
}
else
{
if
(
!
startEdge
)
startEdge
=
m_ActiveEdges
;
while
(
startEdge
->
NextInAEL
&&
!
E2InsertsBeforeE1
(
*
startEdge
->
NextInAEL
,
*
edge
))
startEdge
=
startEdge
->
NextInAEL
;
edge
->
NextInAEL
=
startEdge
->
NextInAEL
;
if
(
startEdge
->
NextInAEL
)
startEdge
->
NextInAEL
->
PrevInAEL
=
edge
;
edge
->
PrevInAEL
=
startEdge
;
startEdge
->
NextInAEL
=
edge
;
}
}
//----------------------------------------------------------------------
OutPt
*
DupOutPt
(
OutPt
*
outPt
,
bool
InsertAfter
)
{
OutPt
*
result
=
new
OutPt
;
result
->
Pt
=
outPt
->
Pt
;
result
->
Idx
=
outPt
->
Idx
;
if
(
InsertAfter
)
{
result
->
Next
=
outPt
->
Next
;
result
->
Prev
=
outPt
;
outPt
->
Next
->
Prev
=
result
;
outPt
->
Next
=
result
;
}
else
{
result
->
Prev
=
outPt
->
Prev
;
result
->
Next
=
outPt
;
outPt
->
Prev
->
Next
=
result
;
outPt
->
Prev
=
result
;
}
return
result
;
}
//------------------------------------------------------------------------------
bool
JoinHorz
(
OutPt
*
op1
,
OutPt
*
op1b
,
OutPt
*
op2
,
OutPt
*
op2b
,
const
IntPoint
Pt
,
bool
DiscardLeft
)
{
Direction
Dir1
=
(
op1
->
Pt
.
X
>
op1b
->
Pt
.
X
?
dRightToLeft
:
dLeftToRight
);
Direction
Dir2
=
(
op2
->
Pt
.
X
>
op2b
->
Pt
.
X
?
dRightToLeft
:
dLeftToRight
);
if
(
Dir1
==
Dir2
)
return
false
;
//When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we
//want Op1b to be on the Right. (And likewise with Op2 and Op2b.)
//So, to facilitate this while inserting Op1b and Op2b ...
//when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,
//otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)
if
(
Dir1
==
dLeftToRight
)
{
while
(
op1
->
Next
->
Pt
.
X
<=
Pt
.
X
&&
op1
->
Next
->
Pt
.
X
>=
op1
->
Pt
.
X
&&
op1
->
Next
->
Pt
.
Y
==
Pt
.
Y
)
op1
=
op1
->
Next
;
if
(
DiscardLeft
&&
(
op1
->
Pt
.
X
!=
Pt
.
X
))
op1
=
op1
->
Next
;
op1b
=
DupOutPt
(
op1
,
!
DiscardLeft
);
if
(
op1b
->
Pt
!=
Pt
)
{
op1
=
op1b
;
op1
->
Pt
=
Pt
;
op1b
=
DupOutPt
(
op1
,
!
DiscardLeft
);
}
}
else
{
while
(
op1
->
Next
->
Pt
.
X
>=
Pt
.
X
&&
op1
->
Next
->
Pt
.
X
<=
op1
->
Pt
.
X
&&
op1
->
Next
->
Pt
.
Y
==
Pt
.
Y
)
op1
=
op1
->
Next
;
if
(
!
DiscardLeft
&&
(
op1
->
Pt
.
X
!=
Pt
.
X
))
op1
=
op1
->
Next
;
op1b
=
DupOutPt
(
op1
,
DiscardLeft
);
if
(
op1b
->
Pt
!=
Pt
)
{
op1
=
op1b
;
op1
->
Pt
=
Pt
;
op1b
=
DupOutPt
(
op1
,
DiscardLeft
);
}
}
if
(
Dir2
==
dLeftToRight
)
{
while
(
op2
->
Next
->
Pt
.
X
<=
Pt
.
X
&&
op2
->
Next
->
Pt
.
X
>=
op2
->
Pt
.
X
&&
op2
->
Next
->
Pt
.
Y
==
Pt
.
Y
)
op2
=
op2
->
Next
;
if
(
DiscardLeft
&&
(
op2
->
Pt
.
X
!=
Pt
.
X
))
op2
=
op2
->
Next
;
op2b
=
DupOutPt
(
op2
,
!
DiscardLeft
);
if
(
op2b
->
Pt
!=
Pt
)
{
op2
=
op2b
;
op2
->
Pt
=
Pt
;
op2b
=
DupOutPt
(
op2
,
!
DiscardLeft
);
};
}
else
{
while
(
op2
->
Next
->
Pt
.
X
>=
Pt
.
X
&&
op2
->
Next
->
Pt
.
X
<=
op2
->
Pt
.
X
&&
op2
->
Next
->
Pt
.
Y
==
Pt
.
Y
)
op2
=
op2
->
Next
;
if
(
!
DiscardLeft
&&
(
op2
->
Pt
.
X
!=
Pt
.
X
))
op2
=
op2
->
Next
;
op2b
=
DupOutPt
(
op2
,
DiscardLeft
);
if
(
op2b
->
Pt
!=
Pt
)
{
op2
=
op2b
;
op2
->
Pt
=
Pt
;
op2b
=
DupOutPt
(
op2
,
DiscardLeft
);
};
};
if
((
Dir1
==
dLeftToRight
)
==
DiscardLeft
)
{
op1
->
Prev
=
op2
;
op2
->
Next
=
op1
;
op1b
->
Next
=
op2b
;
op2b
->
Prev
=
op1b
;
}
else
{
op1
->
Next
=
op2
;
op2
->
Prev
=
op1
;
op1b
->
Prev
=
op2b
;
op2b
->
Next
=
op1b
;
}
return
true
;
}
//------------------------------------------------------------------------------
bool
Clipper
::
JoinPoints
(
Join
*
j
,
OutRec
*
outRec1
,
OutRec
*
outRec2
)
{
OutPt
*
op1
=
j
->
OutPt1
,
*
op1b
;
OutPt
*
op2
=
j
->
OutPt2
,
*
op2b
;
//There are 3 kinds of joins for output polygons ...
//1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
//along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
//2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
//location at the Bottom of the overlapping segment (& Join.OffPt is above).
//3. StrictSimple joins where edges touch but are not collinear and where
//Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
bool
isHorizontal
=
(
j
->
OutPt1
->
Pt
.
Y
==
j
->
OffPt
.
Y
);
if
(
isHorizontal
&&
(
j
->
OffPt
==
j
->
OutPt1
->
Pt
)
&&
(
j
->
OffPt
==
j
->
OutPt2
->
Pt
))
{
//Strictly Simple join ...
if
(
outRec1
!=
outRec2
)
return
false
;
op1b
=
j
->
OutPt1
->
Next
;
while
(
op1b
!=
op1
&&
(
op1b
->
Pt
==
j
->
OffPt
))
op1b
=
op1b
->
Next
;
bool
reverse1
=
(
op1b
->
Pt
.
Y
>
j
->
OffPt
.
Y
);
op2b
=
j
->
OutPt2
->
Next
;
while
(
op2b
!=
op2
&&
(
op2b
->
Pt
==
j
->
OffPt
))
op2b
=
op2b
->
Next
;
bool
reverse2
=
(
op2b
->
Pt
.
Y
>
j
->
OffPt
.
Y
);
if
(
reverse1
==
reverse2
)
return
false
;
if
(
reverse1
)
{
op1b
=
DupOutPt
(
op1
,
false
);
op2b
=
DupOutPt
(
op2
,
true
);
op1
->
Prev
=
op2
;
op2
->
Next
=
op1
;
op1b
->
Next
=
op2b
;
op2b
->
Prev
=
op1b
;
j
->
OutPt1
=
op1
;
j
->
OutPt2
=
op1b
;
return
true
;
}
else
{
op1b
=
DupOutPt
(
op1
,
true
);
op2b
=
DupOutPt
(
op2
,
false
);
op1
->
Next
=
op2
;
op2
->
Prev
=
op1
;
op1b
->
Prev
=
op2b
;
op2b
->
Next
=
op1b
;
j
->
OutPt1
=
op1
;
j
->
OutPt2
=
op1b
;
return
true
;
}
}
else
if
(
isHorizontal
)
{
//treat horizontal joins differently to non-horizontal joins since with
//them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt
//may be anywhere along the horizontal edge.
op1b
=
op1
;
while
(
op1
->
Prev
->
Pt
.
Y
==
op1
->
Pt
.
Y
&&
op1
->
Prev
!=
op1b
&&
op1
->
Prev
!=
op2
)
op1
=
op1
->
Prev
;
while
(
op1b
->
Next
->
Pt
.
Y
==
op1b
->
Pt
.
Y
&&
op1b
->
Next
!=
op1
&&
op1b
->
Next
!=
op2
)
op1b
=
op1b
->
Next
;
if
(
op1b
->
Next
==
op1
||
op1b
->
Next
==
op2
)
return
false
;
//a flat 'polygon'
op2b
=
op2
;
while
(
op2
->
Prev
->
Pt
.
Y
==
op2
->
Pt
.
Y
&&
op2
->
Prev
!=
op2b
&&
op2
->
Prev
!=
op1b
)
op2
=
op2
->
Prev
;
while
(
op2b
->
Next
->
Pt
.
Y
==
op2b
->
Pt
.
Y
&&
op2b
->
Next
!=
op2
&&
op2b
->
Next
!=
op1
)
op2b
=
op2b
->
Next
;
if
(
op2b
->
Next
==
op2
||
op2b
->
Next
==
op1
)
return
false
;
//a flat 'polygon'
cInt
Left
,
Right
;
//Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges
if
(
!
GetOverlap
(
op1
->
Pt
.
X
,
op1b
->
Pt
.
X
,
op2
->
Pt
.
X
,
op2b
->
Pt
.
X
,
Left
,
Right
))
return
false
;
//DiscardLeftSide: when overlapping edges are joined, a spike will created
//which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
//on the discard Side as either may still be needed for other joins ...
IntPoint
Pt
;
bool
DiscardLeftSide
;
if
(
op1
->
Pt
.
X
>=
Left
&&
op1
->
Pt
.
X
<=
Right
)
{
Pt
=
op1
->
Pt
;
DiscardLeftSide
=
(
op1
->
Pt
.
X
>
op1b
->
Pt
.
X
);
}
else
if
(
op2
->
Pt
.
X
>=
Left
&&
op2
->
Pt
.
X
<=
Right
)
{
Pt
=
op2
->
Pt
;
DiscardLeftSide
=
(
op2
->
Pt
.
X
>
op2b
->
Pt
.
X
);
}
else
if
(
op1b
->
Pt
.
X
>=
Left
&&
op1b
->
Pt
.
X
<=
Right
)
{
Pt
=
op1b
->
Pt
;
DiscardLeftSide
=
op1b
->
Pt
.
X
>
op1
->
Pt
.
X
;
}
else
{
Pt
=
op2b
->
Pt
;
DiscardLeftSide
=
(
op2b
->
Pt
.
X
>
op2
->
Pt
.
X
);
}
j
->
OutPt1
=
op1
;
j
->
OutPt2
=
op2
;
return
JoinHorz
(
op1
,
op1b
,
op2
,
op2b
,
Pt
,
DiscardLeftSide
);
}
else
{
//nb: For non-horizontal joins ...
// 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y
// 2. Jr.OutPt1.Pt > Jr.OffPt.Y
//make sure the polygons are correctly oriented ...
op1b
=
op1
->
Next
;
while
((
op1b
->
Pt
==
op1
->
Pt
)
&&
(
op1b
!=
op1
))
op1b
=
op1b
->
Next
;
bool
Reverse1
=
((
op1b
->
Pt
.
Y
>
op1
->
Pt
.
Y
)
||
!
SlopesEqual
(
op1
->
Pt
,
op1b
->
Pt
,
j
->
OffPt
,
m_UseFullRange
));
if
(
Reverse1
)
{
op1b
=
op1
->
Prev
;
while
((
op1b
->
Pt
==
op1
->
Pt
)
&&
(
op1b
!=
op1
))
op1b
=
op1b
->
Prev
;
if
((
op1b
->
Pt
.
Y
>
op1
->
Pt
.
Y
)
||
!
SlopesEqual
(
op1
->
Pt
,
op1b
->
Pt
,
j
->
OffPt
,
m_UseFullRange
))
return
false
;
};
op2b
=
op2
->
Next
;
while
((
op2b
->
Pt
==
op2
->
Pt
)
&&
(
op2b
!=
op2
))
op2b
=
op2b
->
Next
;
bool
Reverse2
=
((
op2b
->
Pt
.
Y
>
op2
->
Pt
.
Y
)
||
!
SlopesEqual
(
op2
->
Pt
,
op2b
->
Pt
,
j
->
OffPt
,
m_UseFullRange
));
if
(
Reverse2
)
{
op2b
=
op2
->
Prev
;
while
((
op2b
->
Pt
==
op2
->
Pt
)
&&
(
op2b
!=
op2
))
op2b
=
op2b
->
Prev
;
if
((
op2b
->
Pt
.
Y
>
op2
->
Pt
.
Y
)
||
!
SlopesEqual
(
op2
->
Pt
,
op2b
->
Pt
,
j
->
OffPt
,
m_UseFullRange
))
return
false
;
}
if
((
op1b
==
op1
)
||
(
op2b
==
op2
)
||
(
op1b
==
op2b
)
||
((
outRec1
==
outRec2
)
&&
(
Reverse1
==
Reverse2
)))
return
false
;
if
(
Reverse1
)
{
op1b
=
DupOutPt
(
op1
,
false
);
op2b
=
DupOutPt
(
op2
,
true
);
op1
->
Prev
=
op2
;
op2
->
Next
=
op1
;
op1b
->
Next
=
op2b
;
op2b
->
Prev
=
op1b
;
j
->
OutPt1
=
op1
;
j
->
OutPt2
=
op1b
;
return
true
;
}
else
{
op1b
=
DupOutPt
(
op1
,
true
);
op2b
=
DupOutPt
(
op2
,
false
);
op1
->
Next
=
op2
;
op2
->
Prev
=
op1
;
op1b
->
Prev
=
op2b
;
op2b
->
Next
=
op1b
;
j
->
OutPt1
=
op1
;
j
->
OutPt2
=
op1b
;
return
true
;
}
}
}
//----------------------------------------------------------------------
static
OutRec
*
ParseFirstLeft
(
OutRec
*
FirstLeft
)
{
while
(
FirstLeft
&&
!
FirstLeft
->
Pts
)
FirstLeft
=
FirstLeft
->
FirstLeft
;
return
FirstLeft
;
}
//------------------------------------------------------------------------------
void
Clipper
::
FixupFirstLefts1
(
OutRec
*
OldOutRec
,
OutRec
*
NewOutRec
)
{
//tests if NewOutRec contains the polygon before reassigning FirstLeft
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
++
i
)
{
OutRec
*
outRec
=
m_PolyOuts
[
i
];
OutRec
*
firstLeft
=
ParseFirstLeft
(
outRec
->
FirstLeft
);
if
(
outRec
->
Pts
&&
firstLeft
==
OldOutRec
)
{
if
(
Poly2ContainsPoly1
(
outRec
->
Pts
,
NewOutRec
->
Pts
))
outRec
->
FirstLeft
=
NewOutRec
;
}
}
}
//----------------------------------------------------------------------
void
Clipper
::
FixupFirstLefts2
(
OutRec
*
InnerOutRec
,
OutRec
*
OuterOutRec
)
{
//A polygon has split into two such that one is now the inner of the other.
//It's possible that these polygons now wrap around other polygons, so check
//every polygon that's also contained by OuterOutRec's FirstLeft container
//(including 0) to see if they've become inner to the new inner polygon ...
OutRec
*
orfl
=
OuterOutRec
->
FirstLeft
;
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
++
i
)
{
OutRec
*
outRec
=
m_PolyOuts
[
i
];
if
(
!
outRec
->
Pts
||
outRec
==
OuterOutRec
||
outRec
==
InnerOutRec
)
continue
;
OutRec
*
firstLeft
=
ParseFirstLeft
(
outRec
->
FirstLeft
);
if
(
firstLeft
!=
orfl
&&
firstLeft
!=
InnerOutRec
&&
firstLeft
!=
OuterOutRec
)
continue
;
if
(
Poly2ContainsPoly1
(
outRec
->
Pts
,
InnerOutRec
->
Pts
))
outRec
->
FirstLeft
=
InnerOutRec
;
else
if
(
Poly2ContainsPoly1
(
outRec
->
Pts
,
OuterOutRec
->
Pts
))
outRec
->
FirstLeft
=
OuterOutRec
;
else
if
(
outRec
->
FirstLeft
==
InnerOutRec
||
outRec
->
FirstLeft
==
OuterOutRec
)
outRec
->
FirstLeft
=
orfl
;
}
}
//----------------------------------------------------------------------
void
Clipper
::
FixupFirstLefts3
(
OutRec
*
OldOutRec
,
OutRec
*
NewOutRec
)
{
//reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
for
(
PolyOutList
::
size_type
i
=
0
;
i
<
m_PolyOuts
.
size
();
++
i
)
{
OutRec
*
outRec
=
m_PolyOuts
[
i
];
OutRec
*
firstLeft
=
ParseFirstLeft
(
outRec
->
FirstLeft
);
if
(
outRec
->
Pts
&&
outRec
->
FirstLeft
==
OldOutRec
)
outRec
->
FirstLeft
=
NewOutRec
;
}
}
//----------------------------------------------------------------------
void
Clipper
::
JoinCommonEdges
()
{
for
(
JoinList
::
size_type
i
=
0
;
i
<
m_Joins
.
size
();
i
++
)
{
Join
*
join
=
m_Joins
[
i
];
OutRec
*
outRec1
=
GetOutRec
(
join
->
OutPt1
->
Idx
);
OutRec
*
outRec2
=
GetOutRec
(
join
->
OutPt2
->
Idx
);
if
(
!
outRec1
->
Pts
||
!
outRec2
->
Pts
)
continue
;
if
(
outRec1
->
IsOpen
||
outRec2
->
IsOpen
)
continue
;
//get the polygon fragment with the correct hole state (FirstLeft)
//before calling JoinPoints() ...
OutRec
*
holeStateRec
;
if
(
outRec1
==
outRec2
)
holeStateRec
=
outRec1
;
else
if
(
OutRec1RightOfOutRec2
(
outRec1
,
outRec2
))
holeStateRec
=
outRec2
;
else
if
(
OutRec1RightOfOutRec2
(
outRec2
,
outRec1
))
holeStateRec
=
outRec1
;
else
holeStateRec
=
GetLowermostRec
(
outRec1
,
outRec2
);
if
(
!
JoinPoints
(
join
,
outRec1
,
outRec2
))
continue
;
if
(
outRec1
==
outRec2
)
{
//instead of joining two polygons, we've just created a new one by
//splitting one polygon into two.
outRec1
->
Pts
=
join
->
OutPt1
;
outRec1
->
BottomPt
=
0
;
outRec2
=
CreateOutRec
();
outRec2
->
Pts
=
join
->
OutPt2
;
//update all OutRec2.Pts Idx's ...
UpdateOutPtIdxs
(
*
outRec2
);
if
(
Poly2ContainsPoly1
(
outRec2
->
Pts
,
outRec1
->
Pts
))
{
//outRec1 contains outRec2 ...
outRec2
->
IsHole
=
!
outRec1
->
IsHole
;
outRec2
->
FirstLeft
=
outRec1
;
if
(
m_UsingPolyTree
)
FixupFirstLefts2
(
outRec2
,
outRec1
);
if
((
outRec2
->
IsHole
^
m_ReverseOutput
)
==
(
Area
(
*
outRec2
)
>
0
))
ReversePolyPtLinks
(
outRec2
->
Pts
);
}
else
if
(
Poly2ContainsPoly1
(
outRec1
->
Pts
,
outRec2
->
Pts
))
{
//outRec2 contains outRec1 ...
outRec2
->
IsHole
=
outRec1
->
IsHole
;
outRec1
->
IsHole
=
!
outRec2
->
IsHole
;
outRec2
->
FirstLeft
=
outRec1
->
FirstLeft
;
outRec1
->
FirstLeft
=
outRec2
;
if
(
m_UsingPolyTree
)
FixupFirstLefts2
(
outRec1
,
outRec2
);
if
((
outRec1
->
IsHole
^
m_ReverseOutput
)
==
(
Area
(
*
outRec1
)
>
0
))
ReversePolyPtLinks
(
outRec1
->
Pts
);
}
else
{
//the 2 polygons are completely separate ...
outRec2
->
IsHole
=
outRec1
->
IsHole
;
outRec2
->
FirstLeft
=
outRec1
->
FirstLeft
;
//fixup FirstLeft pointers that may need reassigning to OutRec2
if
(
m_UsingPolyTree
)
FixupFirstLefts1
(
outRec1
,
outRec2
);
}
}
else
{
//joined 2 polygons together ...
outRec2
->
Pts
=
0
;
outRec2
->
BottomPt
=
0
;
outRec2
->
Idx
=
outRec1
->
Idx
;
outRec1
->
IsHole
=
holeStateRec
->
IsHole
;
if
(
holeStateRec
==
outRec2
)
outRec1
->
FirstLeft
=
outRec2
->
FirstLeft
;
outRec2
->
FirstLeft
=
outRec1
;
if
(
m_UsingPolyTree
)
FixupFirstLefts3
(
outRec2
,
outRec1
);
}
}
}
//------------------------------------------------------------------------------
// ClipperOffset support functions ...
//------------------------------------------------------------------------------
DoublePoint
GetUnitNormal
(
const
IntPoint
&
pt1
,
const
IntPoint
&
pt2
)
{
if
(
pt2
.
X
==
pt1
.
X
&&
pt2
.
Y
==
pt1
.
Y
)
return
DoublePoint
(
0
,
0
);
double
Dx
=
(
double
)(
pt2
.
X
-
pt1
.
X
);
double
dy
=
(
double
)(
pt2
.
Y
-
pt1
.
Y
);
double
f
=
1
*
1.0
/
std
::
sqrt
(
Dx
*
Dx
+
dy
*
dy
);
Dx
*=
f
;
dy
*=
f
;
return
DoublePoint
(
dy
,
-
Dx
);
}
//------------------------------------------------------------------------------
// ClipperOffset class
//------------------------------------------------------------------------------
ClipperOffset
::
ClipperOffset
(
double
miterLimit
,
double
arcTolerance
)
{
this
->
MiterLimit
=
miterLimit
;
this
->
ArcTolerance
=
arcTolerance
;
m_lowest
.
X
=
-
1
;
}
//------------------------------------------------------------------------------
ClipperOffset
::~
ClipperOffset
()
{
Clear
();
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
Clear
()
{
for
(
int
i
=
0
;
i
<
m_polyNodes
.
ChildCount
();
++
i
)
delete
m_polyNodes
.
Childs
[
i
];
m_polyNodes
.
Childs
.
clear
();
m_lowest
.
X
=
-
1
;
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
AddPath
(
const
Path
&
path
,
JoinType
joinType
,
EndType
endType
)
{
int
highI
=
(
int
)
path
.
size
()
-
1
;
if
(
highI
<
0
)
return
;
PolyNode
*
newNode
=
new
PolyNode
();
newNode
->
m_jointype
=
joinType
;
newNode
->
m_endtype
=
endType
;
//strip duplicate points from path and also get index to the lowest point ...
if
(
endType
==
etClosedLine
||
endType
==
etClosedPolygon
)
while
(
highI
>
0
&&
path
[
0
]
==
path
[
highI
])
highI
--
;
newNode
->
Contour
.
reserve
(
highI
+
1
);
newNode
->
Contour
.
push_back
(
path
[
0
]);
int
j
=
0
,
k
=
0
;
for
(
int
i
=
1
;
i
<=
highI
;
i
++
)
if
(
newNode
->
Contour
[
j
]
!=
path
[
i
])
{
j
++
;
newNode
->
Contour
.
push_back
(
path
[
i
]);
if
(
path
[
i
].
Y
>
newNode
->
Contour
[
k
].
Y
||
(
path
[
i
].
Y
==
newNode
->
Contour
[
k
].
Y
&&
path
[
i
].
X
<
newNode
->
Contour
[
k
].
X
))
k
=
j
;
}
if
(
endType
==
etClosedPolygon
&&
j
<
2
)
{
delete
newNode
;
return
;
}
m_polyNodes
.
AddChild
(
*
newNode
);
//if this path's lowest pt is lower than all the others then update m_lowest
if
(
endType
!=
etClosedPolygon
)
return
;
if
(
m_lowest
.
X
<
0
)
m_lowest
=
IntPoint
(
m_polyNodes
.
ChildCount
()
-
1
,
k
);
else
{
IntPoint
ip
=
m_polyNodes
.
Childs
[(
int
)
m_lowest
.
X
]
->
Contour
[(
int
)
m_lowest
.
Y
];
if
(
newNode
->
Contour
[
k
].
Y
>
ip
.
Y
||
(
newNode
->
Contour
[
k
].
Y
==
ip
.
Y
&&
newNode
->
Contour
[
k
].
X
<
ip
.
X
))
m_lowest
=
IntPoint
(
m_polyNodes
.
ChildCount
()
-
1
,
k
);
}
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
AddPaths
(
const
Paths
&
paths
,
JoinType
joinType
,
EndType
endType
)
{
for
(
Paths
::
size_type
i
=
0
;
i
<
paths
.
size
();
++
i
)
AddPath
(
paths
[
i
],
joinType
,
endType
);
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
FixOrientations
()
{
//fixup orientations of all closed paths if the orientation of the
//closed path with the lowermost vertex is wrong ...
if
(
m_lowest
.
X
>=
0
&&
!
Orientation
(
m_polyNodes
.
Childs
[(
int
)
m_lowest
.
X
]
->
Contour
))
{
for
(
int
i
=
0
;
i
<
m_polyNodes
.
ChildCount
();
++
i
)
{
PolyNode
&
node
=
*
m_polyNodes
.
Childs
[
i
];
if
(
node
.
m_endtype
==
etClosedPolygon
||
(
node
.
m_endtype
==
etClosedLine
&&
Orientation
(
node
.
Contour
)))
ReversePath
(
node
.
Contour
);
}
}
else
{
for
(
int
i
=
0
;
i
<
m_polyNodes
.
ChildCount
();
++
i
)
{
PolyNode
&
node
=
*
m_polyNodes
.
Childs
[
i
];
if
(
node
.
m_endtype
==
etClosedLine
&&
!
Orientation
(
node
.
Contour
))
ReversePath
(
node
.
Contour
);
}
}
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
Execute
(
Paths
&
solution
,
double
delta
)
{
solution
.
clear
();
FixOrientations
();
DoOffset
(
delta
);
//now clean up 'corners' ...
Clipper
clpr
;
clpr
.
AddPaths
(
m_destPolys
,
ptSubject
,
true
);
if
(
delta
>
0
)
{
clpr
.
Execute
(
ctUnion
,
solution
,
pftPositive
,
pftPositive
);
}
else
{
IntRect
r
=
clpr
.
GetBounds
();
Path
outer
(
4
);
outer
[
0
]
=
IntPoint
(
r
.
left
-
10
,
r
.
bottom
+
10
);
outer
[
1
]
=
IntPoint
(
r
.
right
+
10
,
r
.
bottom
+
10
);
outer
[
2
]
=
IntPoint
(
r
.
right
+
10
,
r
.
top
-
10
);
outer
[
3
]
=
IntPoint
(
r
.
left
-
10
,
r
.
top
-
10
);
clpr
.
AddPath
(
outer
,
ptSubject
,
true
);
clpr
.
ReverseSolution
(
true
);
clpr
.
Execute
(
ctUnion
,
solution
,
pftNegative
,
pftNegative
);
if
(
solution
.
size
()
>
0
)
solution
.
erase
(
solution
.
begin
());
}
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
Execute
(
PolyTree
&
solution
,
double
delta
)
{
solution
.
Clear
();
FixOrientations
();
DoOffset
(
delta
);
//now clean up 'corners' ...
Clipper
clpr
;
clpr
.
AddPaths
(
m_destPolys
,
ptSubject
,
true
);
if
(
delta
>
0
)
{
clpr
.
Execute
(
ctUnion
,
solution
,
pftPositive
,
pftPositive
);
}
else
{
IntRect
r
=
clpr
.
GetBounds
();
Path
outer
(
4
);
outer
[
0
]
=
IntPoint
(
r
.
left
-
10
,
r
.
bottom
+
10
);
outer
[
1
]
=
IntPoint
(
r
.
right
+
10
,
r
.
bottom
+
10
);
outer
[
2
]
=
IntPoint
(
r
.
right
+
10
,
r
.
top
-
10
);
outer
[
3
]
=
IntPoint
(
r
.
left
-
10
,
r
.
top
-
10
);
clpr
.
AddPath
(
outer
,
ptSubject
,
true
);
clpr
.
ReverseSolution
(
true
);
clpr
.
Execute
(
ctUnion
,
solution
,
pftNegative
,
pftNegative
);
//remove the outer PolyNode rectangle ...
if
(
solution
.
ChildCount
()
==
1
&&
solution
.
Childs
[
0
]
->
ChildCount
()
>
0
)
{
PolyNode
*
outerNode
=
solution
.
Childs
[
0
];
solution
.
Childs
.
reserve
(
outerNode
->
ChildCount
());
solution
.
Childs
[
0
]
=
outerNode
->
Childs
[
0
];
solution
.
Childs
[
0
]
->
Parent
=
outerNode
->
Parent
;
for
(
int
i
=
1
;
i
<
outerNode
->
ChildCount
();
++
i
)
solution
.
AddChild
(
*
outerNode
->
Childs
[
i
]);
}
else
solution
.
Clear
();
}
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
DoOffset
(
double
delta
)
{
m_destPolys
.
clear
();
m_delta
=
delta
;
//if Zero offset, just copy any CLOSED polygons to m_p and return ...
if
(
NEAR_ZERO
(
delta
))
{
m_destPolys
.
reserve
(
m_polyNodes
.
ChildCount
());
for
(
int
i
=
0
;
i
<
m_polyNodes
.
ChildCount
();
i
++
)
{
PolyNode
&
node
=
*
m_polyNodes
.
Childs
[
i
];
if
(
node
.
m_endtype
==
etClosedPolygon
)
m_destPolys
.
push_back
(
node
.
Contour
);
}
return
;
}
//see offset_triginometry3.svg in the documentation folder ...
if
(
MiterLimit
>
2
)
m_miterLim
=
2
/
(
MiterLimit
*
MiterLimit
);
else
m_miterLim
=
0.5
;
double
y
;
if
(
ArcTolerance
<=
0.0
)
y
=
def_arc_tolerance
;
else
if
(
ArcTolerance
>
std
::
fabs
(
delta
)
*
def_arc_tolerance
)
y
=
std
::
fabs
(
delta
)
*
def_arc_tolerance
;
else
y
=
ArcTolerance
;
//see offset_triginometry2.svg in the documentation folder ...
double
steps
=
pi
/
std
::
acos
(
1
-
y
/
std
::
fabs
(
delta
));
if
(
steps
>
std
::
fabs
(
delta
)
*
pi
)
steps
=
std
::
fabs
(
delta
)
*
pi
;
//ie excessive precision check
m_sin
=
std
::
sin
(
two_pi
/
steps
);
m_cos
=
std
::
cos
(
two_pi
/
steps
);
m_StepsPerRad
=
steps
/
two_pi
;
if
(
delta
<
0.0
)
m_sin
=
-
m_sin
;
m_destPolys
.
reserve
(
m_polyNodes
.
ChildCount
()
*
2
);
for
(
int
i
=
0
;
i
<
m_polyNodes
.
ChildCount
();
i
++
)
{
PolyNode
&
node
=
*
m_polyNodes
.
Childs
[
i
];
m_srcPoly
=
node
.
Contour
;
int
len
=
(
int
)
m_srcPoly
.
size
();
if
(
len
==
0
||
(
delta
<=
0
&&
(
len
<
3
||
node
.
m_endtype
!=
etClosedPolygon
)))
continue
;
m_destPoly
.
clear
();
if
(
len
==
1
)
{
if
(
node
.
m_jointype
==
jtRound
)
{
double
X
=
1.0
,
Y
=
0.0
;
for
(
cInt
j
=
1
;
j
<=
steps
;
j
++
)
{
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
0
].
X
+
X
*
delta
),
Round
(
m_srcPoly
[
0
].
Y
+
Y
*
delta
)));
double
X2
=
X
;
X
=
X
*
m_cos
-
m_sin
*
Y
;
Y
=
X2
*
m_sin
+
Y
*
m_cos
;
}
}
else
{
double
X
=
-
1.0
,
Y
=
-
1.0
;
for
(
int
j
=
0
;
j
<
4
;
++
j
)
{
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
0
].
X
+
X
*
delta
),
Round
(
m_srcPoly
[
0
].
Y
+
Y
*
delta
)));
if
(
X
<
0
)
X
=
1
;
else
if
(
Y
<
0
)
Y
=
1
;
else
X
=
-
1
;
}
}
m_destPolys
.
push_back
(
m_destPoly
);
continue
;
}
//build m_normals ...
m_normals
.
clear
();
m_normals
.
reserve
(
len
);
for
(
int
j
=
0
;
j
<
len
-
1
;
++
j
)
m_normals
.
push_back
(
GetUnitNormal
(
m_srcPoly
[
j
],
m_srcPoly
[
j
+
1
]));
if
(
node
.
m_endtype
==
etClosedLine
||
node
.
m_endtype
==
etClosedPolygon
)
m_normals
.
push_back
(
GetUnitNormal
(
m_srcPoly
[
len
-
1
],
m_srcPoly
[
0
]));
else
m_normals
.
push_back
(
DoublePoint
(
m_normals
[
len
-
2
]));
if
(
node
.
m_endtype
==
etClosedPolygon
)
{
int
k
=
len
-
1
;
for
(
int
j
=
0
;
j
<
len
;
++
j
)
OffsetPoint
(
j
,
k
,
node
.
m_jointype
);
m_destPolys
.
push_back
(
m_destPoly
);
}
else
if
(
node
.
m_endtype
==
etClosedLine
)
{
int
k
=
len
-
1
;
for
(
int
j
=
0
;
j
<
len
;
++
j
)
OffsetPoint
(
j
,
k
,
node
.
m_jointype
);
m_destPolys
.
push_back
(
m_destPoly
);
m_destPoly
.
clear
();
//re-build m_normals ...
DoublePoint
n
=
m_normals
[
len
-
1
];
for
(
int
j
=
len
-
1
;
j
>
0
;
j
--
)
m_normals
[
j
]
=
DoublePoint
(
-
m_normals
[
j
-
1
].
X
,
-
m_normals
[
j
-
1
].
Y
);
m_normals
[
0
]
=
DoublePoint
(
-
n
.
X
,
-
n
.
Y
);
k
=
0
;
for
(
int
j
=
len
-
1
;
j
>=
0
;
j
--
)
OffsetPoint
(
j
,
k
,
node
.
m_jointype
);
m_destPolys
.
push_back
(
m_destPoly
);
}
else
{
int
k
=
0
;
for
(
int
j
=
1
;
j
<
len
-
1
;
++
j
)
OffsetPoint
(
j
,
k
,
node
.
m_jointype
);
IntPoint
pt1
;
if
(
node
.
m_endtype
==
etOpenButt
)
{
int
j
=
len
-
1
;
pt1
=
IntPoint
((
cInt
)
Round
(
m_srcPoly
[
j
].
X
+
m_normals
[
j
].
X
*
delta
),
(
cInt
)
Round
(
m_srcPoly
[
j
].
Y
+
m_normals
[
j
].
Y
*
delta
));
m_destPoly
.
push_back
(
pt1
);
pt1
=
IntPoint
((
cInt
)
Round
(
m_srcPoly
[
j
].
X
-
m_normals
[
j
].
X
*
delta
),
(
cInt
)
Round
(
m_srcPoly
[
j
].
Y
-
m_normals
[
j
].
Y
*
delta
));
m_destPoly
.
push_back
(
pt1
);
}
else
{
int
j
=
len
-
1
;
k
=
len
-
2
;
m_sinA
=
0
;
m_normals
[
j
]
=
DoublePoint
(
-
m_normals
[
j
].
X
,
-
m_normals
[
j
].
Y
);
if
(
node
.
m_endtype
==
etOpenSquare
)
DoSquare
(
j
,
k
);
else
DoRound
(
j
,
k
);
}
//re-build m_normals ...
for
(
int
j
=
len
-
1
;
j
>
0
;
j
--
)
m_normals
[
j
]
=
DoublePoint
(
-
m_normals
[
j
-
1
].
X
,
-
m_normals
[
j
-
1
].
Y
);
m_normals
[
0
]
=
DoublePoint
(
-
m_normals
[
1
].
X
,
-
m_normals
[
1
].
Y
);
k
=
len
-
1
;
for
(
int
j
=
k
-
1
;
j
>
0
;
--
j
)
OffsetPoint
(
j
,
k
,
node
.
m_jointype
);
if
(
node
.
m_endtype
==
etOpenButt
)
{
pt1
=
IntPoint
((
cInt
)
Round
(
m_srcPoly
[
0
].
X
-
m_normals
[
0
].
X
*
delta
),
(
cInt
)
Round
(
m_srcPoly
[
0
].
Y
-
m_normals
[
0
].
Y
*
delta
));
m_destPoly
.
push_back
(
pt1
);
pt1
=
IntPoint
((
cInt
)
Round
(
m_srcPoly
[
0
].
X
+
m_normals
[
0
].
X
*
delta
),
(
cInt
)
Round
(
m_srcPoly
[
0
].
Y
+
m_normals
[
0
].
Y
*
delta
));
m_destPoly
.
push_back
(
pt1
);
}
else
{
k
=
1
;
m_sinA
=
0
;
if
(
node
.
m_endtype
==
etOpenSquare
)
DoSquare
(
0
,
1
);
else
DoRound
(
0
,
1
);
}
m_destPolys
.
push_back
(
m_destPoly
);
}
}
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
OffsetPoint
(
int
j
,
int
&
k
,
JoinType
jointype
)
{
//cross product ...
m_sinA
=
(
m_normals
[
k
].
X
*
m_normals
[
j
].
Y
-
m_normals
[
j
].
X
*
m_normals
[
k
].
Y
);
if
(
std
::
fabs
(
m_sinA
*
m_delta
)
<
1.0
)
{
//dot product ...
double
cosA
=
(
m_normals
[
k
].
X
*
m_normals
[
j
].
X
+
m_normals
[
j
].
Y
*
m_normals
[
k
].
Y
);
if
(
cosA
>
0
)
// angle => 0 degrees
{
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
m_normals
[
k
].
X
*
m_delta
),
Round
(
m_srcPoly
[
j
].
Y
+
m_normals
[
k
].
Y
*
m_delta
)));
return
;
}
//else angle => 180 degrees
}
else
if
(
m_sinA
>
1.0
)
m_sinA
=
1.0
;
else
if
(
m_sinA
<
-
1.0
)
m_sinA
=
-
1.0
;
if
(
m_sinA
*
m_delta
<
0
)
{
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
m_normals
[
k
].
X
*
m_delta
),
Round
(
m_srcPoly
[
j
].
Y
+
m_normals
[
k
].
Y
*
m_delta
)));
m_destPoly
.
push_back
(
m_srcPoly
[
j
]);
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
m_normals
[
j
].
X
*
m_delta
),
Round
(
m_srcPoly
[
j
].
Y
+
m_normals
[
j
].
Y
*
m_delta
)));
}
else
switch
(
jointype
)
{
case
jtMiter
:
{
double
r
=
1
+
(
m_normals
[
j
].
X
*
m_normals
[
k
].
X
+
m_normals
[
j
].
Y
*
m_normals
[
k
].
Y
);
if
(
r
>=
m_miterLim
)
DoMiter
(
j
,
k
,
r
);
else
DoSquare
(
j
,
k
);
break
;
}
case
jtSquare
:
DoSquare
(
j
,
k
);
break
;
case
jtRound
:
DoRound
(
j
,
k
);
break
;
}
k
=
j
;
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
DoSquare
(
int
j
,
int
k
)
{
double
dx
=
std
::
tan
(
std
::
atan2
(
m_sinA
,
m_normals
[
k
].
X
*
m_normals
[
j
].
X
+
m_normals
[
k
].
Y
*
m_normals
[
j
].
Y
)
/
4
);
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
m_delta
*
(
m_normals
[
k
].
X
-
m_normals
[
k
].
Y
*
dx
)),
Round
(
m_srcPoly
[
j
].
Y
+
m_delta
*
(
m_normals
[
k
].
Y
+
m_normals
[
k
].
X
*
dx
))));
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
m_delta
*
(
m_normals
[
j
].
X
+
m_normals
[
j
].
Y
*
dx
)),
Round
(
m_srcPoly
[
j
].
Y
+
m_delta
*
(
m_normals
[
j
].
Y
-
m_normals
[
j
].
X
*
dx
))));
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
DoMiter
(
int
j
,
int
k
,
double
r
)
{
double
q
=
m_delta
/
r
;
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
(
m_normals
[
k
].
X
+
m_normals
[
j
].
X
)
*
q
),
Round
(
m_srcPoly
[
j
].
Y
+
(
m_normals
[
k
].
Y
+
m_normals
[
j
].
Y
)
*
q
)));
}
//------------------------------------------------------------------------------
void
ClipperOffset
::
DoRound
(
int
j
,
int
k
)
{
double
a
=
std
::
atan2
(
m_sinA
,
m_normals
[
k
].
X
*
m_normals
[
j
].
X
+
m_normals
[
k
].
Y
*
m_normals
[
j
].
Y
);
int
steps
=
std
::
max
((
int
)
Round
(
m_StepsPerRad
*
std
::
fabs
(
a
)),
1
);
double
X
=
m_normals
[
k
].
X
,
Y
=
m_normals
[
k
].
Y
,
X2
;
for
(
int
i
=
0
;
i
<
steps
;
++
i
)
{
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
X
*
m_delta
),
Round
(
m_srcPoly
[
j
].
Y
+
Y
*
m_delta
)));
X2
=
X
;
X
=
X
*
m_cos
-
m_sin
*
Y
;
Y
=
X2
*
m_sin
+
Y
*
m_cos
;
}
m_destPoly
.
push_back
(
IntPoint
(
Round
(
m_srcPoly
[
j
].
X
+
m_normals
[
j
].
X
*
m_delta
),
Round
(
m_srcPoly
[
j
].
Y
+
m_normals
[
j
].
Y
*
m_delta
)));
}
//------------------------------------------------------------------------------
// Miscellaneous public functions
//------------------------------------------------------------------------------
void
Clipper
::
DoSimplePolygons
()
{
PolyOutList
::
size_type
i
=
0
;
while
(
i
<
m_PolyOuts
.
size
())
{
OutRec
*
outrec
=
m_PolyOuts
[
i
++
];
OutPt
*
op
=
outrec
->
Pts
;
if
(
!
op
||
outrec
->
IsOpen
)
continue
;
do
//for each Pt in Polygon until duplicate found do ...
{
OutPt
*
op2
=
op
->
Next
;
while
(
op2
!=
outrec
->
Pts
)
{
if
((
op
->
Pt
==
op2
->
Pt
)
&&
op2
->
Next
!=
op
&&
op2
->
Prev
!=
op
)
{
//split the polygon into two ...
OutPt
*
op3
=
op
->
Prev
;
OutPt
*
op4
=
op2
->
Prev
;
op
->
Prev
=
op4
;
op4
->
Next
=
op
;
op2
->
Prev
=
op3
;
op3
->
Next
=
op2
;
outrec
->
Pts
=
op
;
OutRec
*
outrec2
=
CreateOutRec
();
outrec2
->
Pts
=
op2
;
UpdateOutPtIdxs
(
*
outrec2
);
if
(
Poly2ContainsPoly1
(
outrec2
->
Pts
,
outrec
->
Pts
))
{
//OutRec2 is contained by OutRec1 ...
outrec2
->
IsHole
=
!
outrec
->
IsHole
;
outrec2
->
FirstLeft
=
outrec
;
if
(
m_UsingPolyTree
)
FixupFirstLefts2
(
outrec2
,
outrec
);
}
else
if
(
Poly2ContainsPoly1
(
outrec
->
Pts
,
outrec2
->
Pts
))
{
//OutRec1 is contained by OutRec2 ...
outrec2
->
IsHole
=
outrec
->
IsHole
;
outrec
->
IsHole
=
!
outrec2
->
IsHole
;
outrec2
->
FirstLeft
=
outrec
->
FirstLeft
;
outrec
->
FirstLeft
=
outrec2
;
if
(
m_UsingPolyTree
)
FixupFirstLefts2
(
outrec
,
outrec2
);
}
else
{
//the 2 polygons are separate ...
outrec2
->
IsHole
=
outrec
->
IsHole
;
outrec2
->
FirstLeft
=
outrec
->
FirstLeft
;
if
(
m_UsingPolyTree
)
FixupFirstLefts1
(
outrec
,
outrec2
);
}
op2
=
op
;
//ie get ready for the Next iteration
}
op2
=
op2
->
Next
;
}
op
=
op
->
Next
;
}
while
(
op
!=
outrec
->
Pts
);
}
}
//------------------------------------------------------------------------------
void
ReversePath
(
Path
&
p
)
{
std
::
reverse
(
p
.
begin
(),
p
.
end
());
}
//------------------------------------------------------------------------------
void
ReversePaths
(
Paths
&
p
)
{
for
(
Paths
::
size_type
i
=
0
;
i
<
p
.
size
();
++
i
)
ReversePath
(
p
[
i
]);
}
//------------------------------------------------------------------------------
void
SimplifyPolygon
(
const
Path
&
in_poly
,
Paths
&
out_polys
,
PolyFillType
fillType
)
{
Clipper
c
;
c
.
StrictlySimple
(
true
);
c
.
AddPath
(
in_poly
,
ptSubject
,
true
);
c
.
Execute
(
ctUnion
,
out_polys
,
fillType
,
fillType
);
}
//------------------------------------------------------------------------------
void
SimplifyPolygons
(
const
Paths
&
in_polys
,
Paths
&
out_polys
,
PolyFillType
fillType
)
{
Clipper
c
;
c
.
StrictlySimple
(
true
);
c
.
AddPaths
(
in_polys
,
ptSubject
,
true
);
c
.
Execute
(
ctUnion
,
out_polys
,
fillType
,
fillType
);
}
//------------------------------------------------------------------------------
void
SimplifyPolygons
(
Paths
&
polys
,
PolyFillType
fillType
)
{
SimplifyPolygons
(
polys
,
polys
,
fillType
);
}
//------------------------------------------------------------------------------
inline
double
DistanceSqrd
(
const
IntPoint
&
pt1
,
const
IntPoint
&
pt2
)
{
double
Dx
=
((
double
)
pt1
.
X
-
pt2
.
X
);
double
dy
=
((
double
)
pt1
.
Y
-
pt2
.
Y
);
return
(
Dx
*
Dx
+
dy
*
dy
);
}
//------------------------------------------------------------------------------
double
DistanceFromLineSqrd
(
const
IntPoint
&
pt
,
const
IntPoint
&
ln1
,
const
IntPoint
&
ln2
)
{
//The equation of a line in general form (Ax + By + C = 0)
//given 2 points (x,y) & (x,y) is ...
//(y - y)x + (x - x)y + (y - y)x - (x - x)y = 0
//A = (y - y); B = (x - x); C = (y - y)x - (x - x)y
//perpendicular distance of point (x,y) = (Ax + By + C)/Sqrt(A + B)
//see http://en.wikipedia.org/wiki/Perpendicular_distance
double
A
=
double
(
ln1
.
Y
-
ln2
.
Y
);
double
B
=
double
(
ln2
.
X
-
ln1
.
X
);
double
C
=
A
*
ln1
.
X
+
B
*
ln1
.
Y
;
C
=
A
*
pt
.
X
+
B
*
pt
.
Y
-
C
;
return
(
C
*
C
)
/
(
A
*
A
+
B
*
B
);
}
//---------------------------------------------------------------------------
bool
SlopesNearCollinear
(
const
IntPoint
&
pt1
,
const
IntPoint
&
pt2
,
const
IntPoint
&
pt3
,
double
distSqrd
)
{
//this function is more accurate when the point that's geometrically
//between the other 2 points is the one that's tested for distance.
//ie makes it more likely to pick up 'spikes' ...
if
(
Abs
(
pt1
.
X
-
pt2
.
X
)
>
Abs
(
pt1
.
Y
-
pt2
.
Y
))
{
if
((
pt1
.
X
>
pt2
.
X
)
==
(
pt1
.
X
<
pt3
.
X
))
return
DistanceFromLineSqrd
(
pt1
,
pt2
,
pt3
)
<
distSqrd
;
else
if
((
pt2
.
X
>
pt1
.
X
)
==
(
pt2
.
X
<
pt3
.
X
))
return
DistanceFromLineSqrd
(
pt2
,
pt1
,
pt3
)
<
distSqrd
;
else
return
DistanceFromLineSqrd
(
pt3
,
pt1
,
pt2
)
<
distSqrd
;
}
else
{
if
((
pt1
.
Y
>
pt2
.
Y
)
==
(
pt1
.
Y
<
pt3
.
Y
))
return
DistanceFromLineSqrd
(
pt1
,
pt2
,
pt3
)
<
distSqrd
;
else
if
((
pt2
.
Y
>
pt1
.
Y
)
==
(
pt2
.
Y
<
pt3
.
Y
))
return
DistanceFromLineSqrd
(
pt2
,
pt1
,
pt3
)
<
distSqrd
;
else
return
DistanceFromLineSqrd
(
pt3
,
pt1
,
pt2
)
<
distSqrd
;
}
}
//------------------------------------------------------------------------------
bool
PointsAreClose
(
IntPoint
pt1
,
IntPoint
pt2
,
double
distSqrd
)
{
double
Dx
=
(
double
)
pt1
.
X
-
pt2
.
X
;
double
dy
=
(
double
)
pt1
.
Y
-
pt2
.
Y
;
return
((
Dx
*
Dx
)
+
(
dy
*
dy
)
<=
distSqrd
);
}
//------------------------------------------------------------------------------
OutPt
*
ExcludeOp
(
OutPt
*
op
)
{
OutPt
*
result
=
op
->
Prev
;
result
->
Next
=
op
->
Next
;
op
->
Next
->
Prev
=
result
;
result
->
Idx
=
0
;
return
result
;
}
//------------------------------------------------------------------------------
void
CleanPolygon
(
const
Path
&
in_poly
,
Path
&
out_poly
,
double
distance
)
{
//distance = proximity in units/pixels below which vertices
//will be stripped. Default ~= sqrt(2).
size_t
size
=
in_poly
.
size
();
if
(
size
==
0
)
{
out_poly
.
clear
();
return
;
}
OutPt
*
outPts
=
new
OutPt
[
size
];
for
(
size_t
i
=
0
;
i
<
size
;
++
i
)
{
outPts
[
i
].
Pt
=
in_poly
[
i
];
outPts
[
i
].
Next
=
&
outPts
[(
i
+
1
)
%
size
];
outPts
[
i
].
Next
->
Prev
=
&
outPts
[
i
];
outPts
[
i
].
Idx
=
0
;
}
double
distSqrd
=
distance
*
distance
;
OutPt
*
op
=
&
outPts
[
0
];
while
(
op
->
Idx
==
0
&&
op
->
Next
!=
op
->
Prev
)
{
if
(
PointsAreClose
(
op
->
Pt
,
op
->
Prev
->
Pt
,
distSqrd
))
{
op
=
ExcludeOp
(
op
);
size
--
;
}
else
if
(
PointsAreClose
(
op
->
Prev
->
Pt
,
op
->
Next
->
Pt
,
distSqrd
))
{
ExcludeOp
(
op
->
Next
);
op
=
ExcludeOp
(
op
);
size
-=
2
;
}
else
if
(
SlopesNearCollinear
(
op
->
Prev
->
Pt
,
op
->
Pt
,
op
->
Next
->
Pt
,
distSqrd
))
{
op
=
ExcludeOp
(
op
);
size
--
;
}
else
{
op
->
Idx
=
1
;
op
=
op
->
Next
;
}
}
if
(
size
<
3
)
size
=
0
;
out_poly
.
resize
(
size
);
for
(
size_t
i
=
0
;
i
<
size
;
++
i
)
{
out_poly
[
i
]
=
op
->
Pt
;
op
=
op
->
Next
;
}
delete
[]
outPts
;
}
//------------------------------------------------------------------------------
void
CleanPolygon
(
Path
&
poly
,
double
distance
)
{
CleanPolygon
(
poly
,
poly
,
distance
);
}
//------------------------------------------------------------------------------
void
CleanPolygons
(
const
Paths
&
in_polys
,
Paths
&
out_polys
,
double
distance
)
{
out_polys
.
resize
(
in_polys
.
size
());
for
(
Paths
::
size_type
i
=
0
;
i
<
in_polys
.
size
();
++
i
)
CleanPolygon
(
in_polys
[
i
],
out_polys
[
i
],
distance
);
}
//------------------------------------------------------------------------------
void
CleanPolygons
(
Paths
&
polys
,
double
distance
)
{
CleanPolygons
(
polys
,
polys
,
distance
);
}
//------------------------------------------------------------------------------
void
Minkowski
(
const
Path
&
poly
,
const
Path
&
path
,
Paths
&
solution
,
bool
isSum
,
bool
isClosed
)
{
int
delta
=
(
isClosed
?
1
:
0
);
size_t
polyCnt
=
poly
.
size
();
size_t
pathCnt
=
path
.
size
();
Paths
pp
;
pp
.
reserve
(
pathCnt
);
if
(
isSum
)
for
(
size_t
i
=
0
;
i
<
pathCnt
;
++
i
)
{
Path
p
;
p
.
reserve
(
polyCnt
);
for
(
size_t
j
=
0
;
j
<
poly
.
size
();
++
j
)
p
.
push_back
(
IntPoint
(
path
[
i
].
X
+
poly
[
j
].
X
,
path
[
i
].
Y
+
poly
[
j
].
Y
));
pp
.
push_back
(
p
);
}
else
for
(
size_t
i
=
0
;
i
<
pathCnt
;
++
i
)
{
Path
p
;
p
.
reserve
(
polyCnt
);
for
(
size_t
j
=
0
;
j
<
poly
.
size
();
++
j
)
p
.
push_back
(
IntPoint
(
path
[
i
].
X
-
poly
[
j
].
X
,
path
[
i
].
Y
-
poly
[
j
].
Y
));
pp
.
push_back
(
p
);
}
solution
.
clear
();
solution
.
reserve
((
pathCnt
+
delta
)
*
(
polyCnt
+
1
));
for
(
size_t
i
=
0
;
i
<
pathCnt
-
1
+
delta
;
++
i
)
for
(
size_t
j
=
0
;
j
<
polyCnt
;
++
j
)
{
Path
quad
;
quad
.
reserve
(
4
);
quad
.
push_back
(
pp
[
i
%
pathCnt
][
j
%
polyCnt
]);
quad
.
push_back
(
pp
[(
i
+
1
)
%
pathCnt
][
j
%
polyCnt
]);
quad
.
push_back
(
pp
[(
i
+
1
)
%
pathCnt
][(
j
+
1
)
%
polyCnt
]);
quad
.
push_back
(
pp
[
i
%
pathCnt
][(
j
+
1
)
%
polyCnt
]);
if
(
!
Orientation
(
quad
))
ReversePath
(
quad
);
solution
.
push_back
(
quad
);
}
}
//------------------------------------------------------------------------------
void
MinkowskiSum
(
const
Path
&
pattern
,
const
Path
&
path
,
Paths
&
solution
,
bool
pathIsClosed
)
{
Minkowski
(
pattern
,
path
,
solution
,
true
,
pathIsClosed
);
Clipper
c
;
c
.
AddPaths
(
solution
,
ptSubject
,
true
);
c
.
Execute
(
ctUnion
,
solution
,
pftNonZero
,
pftNonZero
);
}
//------------------------------------------------------------------------------
void
TranslatePath
(
const
Path
&
input
,
Path
&
output
,
const
IntPoint
delta
)
{
//precondition: input != output
output
.
resize
(
input
.
size
());
for
(
size_t
i
=
0
;
i
<
input
.
size
();
++
i
)
output
[
i
]
=
IntPoint
(
input
[
i
].
X
+
delta
.
X
,
input
[
i
].
Y
+
delta
.
Y
);
}
//------------------------------------------------------------------------------
void
MinkowskiSum
(
const
Path
&
pattern
,
const
Paths
&
paths
,
Paths
&
solution
,
bool
pathIsClosed
)
{
Clipper
c
;
for
(
size_t
i
=
0
;
i
<
paths
.
size
();
++
i
)
{
Paths
tmp
;
Minkowski
(
pattern
,
paths
[
i
],
tmp
,
true
,
pathIsClosed
);
c
.
AddPaths
(
tmp
,
ptSubject
,
true
);
if
(
pathIsClosed
)
{
Path
tmp2
;
TranslatePath
(
paths
[
i
],
tmp2
,
pattern
[
0
]);
c
.
AddPath
(
tmp2
,
ptClip
,
true
);
}
}
c
.
Execute
(
ctUnion
,
solution
,
pftNonZero
,
pftNonZero
);
}
//------------------------------------------------------------------------------
void
MinkowskiDiff
(
const
Path
&
poly1
,
const
Path
&
poly2
,
Paths
&
solution
)
{
Minkowski
(
poly1
,
poly2
,
solution
,
false
,
true
);
Clipper
c
;
c
.
AddPaths
(
solution
,
ptSubject
,
true
);
c
.
Execute
(
ctUnion
,
solution
,
pftNonZero
,
pftNonZero
);
}
//------------------------------------------------------------------------------
enum
NodeType
{
ntAny
,
ntOpen
,
ntClosed
};
void
AddPolyNodeToPaths
(
const
PolyNode
&
polynode
,
NodeType
nodetype
,
Paths
&
paths
)
{
bool
match
=
true
;
if
(
nodetype
==
ntClosed
)
match
=
!
polynode
.
IsOpen
();
else
if
(
nodetype
==
ntOpen
)
return
;
if
(
!
polynode
.
Contour
.
empty
()
&&
match
)
paths
.
push_back
(
polynode
.
Contour
);
for
(
int
i
=
0
;
i
<
polynode
.
ChildCount
();
++
i
)
AddPolyNodeToPaths
(
*
polynode
.
Childs
[
i
],
nodetype
,
paths
);
}
//------------------------------------------------------------------------------
void
PolyTreeToPaths
(
const
PolyTree
&
polytree
,
Paths
&
paths
)
{
paths
.
resize
(
0
);
paths
.
reserve
(
polytree
.
Total
());
AddPolyNodeToPaths
(
polytree
,
ntAny
,
paths
);
}
//------------------------------------------------------------------------------
void
ClosedPathsFromPolyTree
(
const
PolyTree
&
polytree
,
Paths
&
paths
)
{
paths
.
resize
(
0
);
paths
.
reserve
(
polytree
.
Total
());
AddPolyNodeToPaths
(
polytree
,
ntClosed
,
paths
);
}
//------------------------------------------------------------------------------
void
OpenPathsFromPolyTree
(
PolyTree
&
polytree
,
Paths
&
paths
)
{
paths
.
resize
(
0
);
paths
.
reserve
(
polytree
.
Total
());
//Open paths are top level only, so ...
for
(
int
i
=
0
;
i
<
polytree
.
ChildCount
();
++
i
)
if
(
polytree
.
Childs
[
i
]
->
IsOpen
())
paths
.
push_back
(
polytree
.
Childs
[
i
]
->
Contour
);
}
//------------------------------------------------------------------------------
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
IntPoint
&
p
)
{
s
<<
"("
<<
p
.
X
<<
","
<<
p
.
Y
<<
")"
;
return
s
;
}
//------------------------------------------------------------------------------
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
Path
&
p
)
{
if
(
p
.
empty
())
return
s
;
Path
::
size_type
last
=
p
.
size
()
-
1
;
for
(
Path
::
size_type
i
=
0
;
i
<
last
;
i
++
)
s
<<
"("
<<
p
[
i
].
X
<<
","
<<
p
[
i
].
Y
<<
"), "
;
s
<<
"("
<<
p
[
last
].
X
<<
","
<<
p
[
last
].
Y
<<
")
\n
"
;
return
s
;
}
//------------------------------------------------------------------------------
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
Paths
&
p
)
{
for
(
Paths
::
size_type
i
=
0
;
i
<
p
.
size
();
i
++
)
s
<<
p
[
i
];
s
<<
"
\n
"
;
return
s
;
}
//------------------------------------------------------------------------------
}
//ClipperLib namespace
ppocr/postprocess/lanms/include/clipper/clipper.hpp
0 → 100644
View file @
26219d5f
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.4.0 *
* Date : 2 July 2015 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2015 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.2.6"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
//#define use_int32
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define use_xyz
//use_lines: Enables line clipping. Adds a very minor cost to performance.
#define use_lines
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <vector>
#include <list>
#include <set>
#include <stdexcept>
#include <cstring>
#include <cstdlib>
#include <ostream>
#include <functional>
#include <queue>
namespace
ClipperLib
{
enum
ClipType
{
ctIntersection
,
ctUnion
,
ctDifference
,
ctXor
};
enum
PolyType
{
ptSubject
,
ptClip
};
//By far the most widely used winding rules for polygon filling are
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
//see http://glprogramming.com/red/chapter11.html
enum
PolyFillType
{
pftEvenOdd
,
pftNonZero
,
pftPositive
,
pftNegative
};
#ifdef use_int32
typedef
int
cInt
;
static
cInt
const
loRange
=
0x7FFF
;
static
cInt
const
hiRange
=
0x7FFF
;
#else
typedef
signed
long
long
cInt
;
static
cInt
const
loRange
=
0x3FFFFFFF
;
static
cInt
const
hiRange
=
0x3FFFFFFFFFFFFFFFLL
;
typedef
signed
long
long
long64
;
//used by Int128 class
typedef
unsigned
long
long
ulong64
;
#endif
struct
IntPoint
{
cInt
X
;
cInt
Y
;
#ifdef use_xyz
cInt
Z
;
IntPoint
(
cInt
x
=
0
,
cInt
y
=
0
,
cInt
z
=
0
)
:
X
(
x
),
Y
(
y
),
Z
(
z
)
{};
#else
IntPoint
(
cInt
x
=
0
,
cInt
y
=
0
)
:
X
(
x
),
Y
(
y
)
{};
#endif
friend
inline
bool
operator
==
(
const
IntPoint
&
a
,
const
IntPoint
&
b
)
{
return
a
.
X
==
b
.
X
&&
a
.
Y
==
b
.
Y
;
}
friend
inline
bool
operator
!=
(
const
IntPoint
&
a
,
const
IntPoint
&
b
)
{
return
a
.
X
!=
b
.
X
||
a
.
Y
!=
b
.
Y
;
}
};
//------------------------------------------------------------------------------
typedef
std
::
vector
<
IntPoint
>
Path
;
typedef
std
::
vector
<
Path
>
Paths
;
inline
Path
&
operator
<<
(
Path
&
poly
,
const
IntPoint
&
p
)
{
poly
.
push_back
(
p
);
return
poly
;}
inline
Paths
&
operator
<<
(
Paths
&
polys
,
const
Path
&
p
)
{
polys
.
push_back
(
p
);
return
polys
;}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
IntPoint
&
p
);
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
Path
&
p
);
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
Paths
&
p
);
struct
DoublePoint
{
double
X
;
double
Y
;
DoublePoint
(
double
x
=
0
,
double
y
=
0
)
:
X
(
x
),
Y
(
y
)
{}
DoublePoint
(
IntPoint
ip
)
:
X
((
double
)
ip
.
X
),
Y
((
double
)
ip
.
Y
)
{}
};
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef
void
(
*
ZFillCallback
)(
IntPoint
&
e1bot
,
IntPoint
&
e1top
,
IntPoint
&
e2bot
,
IntPoint
&
e2top
,
IntPoint
&
pt
);
#endif
enum
InitOptions
{
ioReverseSolution
=
1
,
ioStrictlySimple
=
2
,
ioPreserveCollinear
=
4
};
enum
JoinType
{
jtSquare
,
jtRound
,
jtMiter
};
enum
EndType
{
etClosedPolygon
,
etClosedLine
,
etOpenButt
,
etOpenSquare
,
etOpenRound
};
class
PolyNode
;
typedef
std
::
vector
<
PolyNode
*
>
PolyNodes
;
class
PolyNode
{
public:
PolyNode
();
virtual
~
PolyNode
(){};
Path
Contour
;
PolyNodes
Childs
;
PolyNode
*
Parent
;
PolyNode
*
GetNext
()
const
;
bool
IsHole
()
const
;
bool
IsOpen
()
const
;
int
ChildCount
()
const
;
private:
unsigned
Index
;
//node index in Parent.Childs
bool
m_IsOpen
;
JoinType
m_jointype
;
EndType
m_endtype
;
PolyNode
*
GetNextSiblingUp
()
const
;
void
AddChild
(
PolyNode
&
child
);
friend
class
Clipper
;
//to access Index
friend
class
ClipperOffset
;
};
class
PolyTree
:
public
PolyNode
{
public:
~
PolyTree
(){
Clear
();};
PolyNode
*
GetFirst
()
const
;
void
Clear
();
int
Total
()
const
;
private:
PolyNodes
AllNodes
;
friend
class
Clipper
;
//to access AllNodes
};
bool
Orientation
(
const
Path
&
poly
);
double
Area
(
const
Path
&
poly
);
int
PointInPolygon
(
const
IntPoint
&
pt
,
const
Path
&
path
);
void
SimplifyPolygon
(
const
Path
&
in_poly
,
Paths
&
out_polys
,
PolyFillType
fillType
=
pftEvenOdd
);
void
SimplifyPolygons
(
const
Paths
&
in_polys
,
Paths
&
out_polys
,
PolyFillType
fillType
=
pftEvenOdd
);
void
SimplifyPolygons
(
Paths
&
polys
,
PolyFillType
fillType
=
pftEvenOdd
);
void
CleanPolygon
(
const
Path
&
in_poly
,
Path
&
out_poly
,
double
distance
=
1.415
);
void
CleanPolygon
(
Path
&
poly
,
double
distance
=
1.415
);
void
CleanPolygons
(
const
Paths
&
in_polys
,
Paths
&
out_polys
,
double
distance
=
1.415
);
void
CleanPolygons
(
Paths
&
polys
,
double
distance
=
1.415
);
void
MinkowskiSum
(
const
Path
&
pattern
,
const
Path
&
path
,
Paths
&
solution
,
bool
pathIsClosed
);
void
MinkowskiSum
(
const
Path
&
pattern
,
const
Paths
&
paths
,
Paths
&
solution
,
bool
pathIsClosed
);
void
MinkowskiDiff
(
const
Path
&
poly1
,
const
Path
&
poly2
,
Paths
&
solution
);
void
PolyTreeToPaths
(
const
PolyTree
&
polytree
,
Paths
&
paths
);
void
ClosedPathsFromPolyTree
(
const
PolyTree
&
polytree
,
Paths
&
paths
);
void
OpenPathsFromPolyTree
(
PolyTree
&
polytree
,
Paths
&
paths
);
void
ReversePath
(
Path
&
p
);
void
ReversePaths
(
Paths
&
p
);
struct
IntRect
{
cInt
left
;
cInt
top
;
cInt
right
;
cInt
bottom
;
};
//enums that are used internally ...
enum
EdgeSide
{
esLeft
=
1
,
esRight
=
2
};
//forward declarations (for stuff used internally) ...
struct
TEdge
;
struct
IntersectNode
;
struct
LocalMinimum
;
struct
OutPt
;
struct
OutRec
;
struct
Join
;
typedef
std
::
vector
<
OutRec
*
>
PolyOutList
;
typedef
std
::
vector
<
TEdge
*
>
EdgeList
;
typedef
std
::
vector
<
Join
*
>
JoinList
;
typedef
std
::
vector
<
IntersectNode
*
>
IntersectList
;
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
//instantiated directly. This class simply abstracts the conversion of sets of
//polygon coordinates into edge objects that are stored in a LocalMinima list.
class
ClipperBase
{
public:
ClipperBase
();
virtual
~
ClipperBase
();
virtual
bool
AddPath
(
const
Path
&
pg
,
PolyType
PolyTyp
,
bool
Closed
);
bool
AddPaths
(
const
Paths
&
ppg
,
PolyType
PolyTyp
,
bool
Closed
);
virtual
void
Clear
();
IntRect
GetBounds
();
bool
PreserveCollinear
()
{
return
m_PreserveCollinear
;};
void
PreserveCollinear
(
bool
value
)
{
m_PreserveCollinear
=
value
;};
protected:
void
DisposeLocalMinimaList
();
TEdge
*
AddBoundsToLML
(
TEdge
*
e
,
bool
IsClosed
);
virtual
void
Reset
();
TEdge
*
ProcessBound
(
TEdge
*
E
,
bool
IsClockwise
);
void
InsertScanbeam
(
const
cInt
Y
);
bool
PopScanbeam
(
cInt
&
Y
);
bool
LocalMinimaPending
();
bool
PopLocalMinima
(
cInt
Y
,
const
LocalMinimum
*&
locMin
);
OutRec
*
CreateOutRec
();
void
DisposeAllOutRecs
();
void
DisposeOutRec
(
PolyOutList
::
size_type
index
);
void
SwapPositionsInAEL
(
TEdge
*
edge1
,
TEdge
*
edge2
);
void
DeleteFromAEL
(
TEdge
*
e
);
void
UpdateEdgeIntoAEL
(
TEdge
*&
e
);
typedef
std
::
vector
<
LocalMinimum
>
MinimaList
;
MinimaList
::
iterator
m_CurrentLM
;
MinimaList
m_MinimaList
;
bool
m_UseFullRange
;
EdgeList
m_edges
;
bool
m_PreserveCollinear
;
bool
m_HasOpenPaths
;
PolyOutList
m_PolyOuts
;
TEdge
*
m_ActiveEdges
;
typedef
std
::
priority_queue
<
cInt
>
ScanbeamList
;
ScanbeamList
m_Scanbeam
;
};
//------------------------------------------------------------------------------
class
Clipper
:
public
virtual
ClipperBase
{
public:
Clipper
(
int
initOptions
=
0
);
bool
Execute
(
ClipType
clipType
,
Paths
&
solution
,
PolyFillType
fillType
=
pftEvenOdd
);
bool
Execute
(
ClipType
clipType
,
Paths
&
solution
,
PolyFillType
subjFillType
,
PolyFillType
clipFillType
);
bool
Execute
(
ClipType
clipType
,
PolyTree
&
polytree
,
PolyFillType
fillType
=
pftEvenOdd
);
bool
Execute
(
ClipType
clipType
,
PolyTree
&
polytree
,
PolyFillType
subjFillType
,
PolyFillType
clipFillType
);
bool
ReverseSolution
()
{
return
m_ReverseOutput
;
};
void
ReverseSolution
(
bool
value
)
{
m_ReverseOutput
=
value
;};
bool
StrictlySimple
()
{
return
m_StrictSimple
;};
void
StrictlySimple
(
bool
value
)
{
m_StrictSimple
=
value
;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz
void
ZFillFunction
(
ZFillCallback
zFillFunc
);
#endif
protected:
virtual
bool
ExecuteInternal
();
private:
JoinList
m_Joins
;
JoinList
m_GhostJoins
;
IntersectList
m_IntersectList
;
ClipType
m_ClipType
;
typedef
std
::
list
<
cInt
>
MaximaList
;
MaximaList
m_Maxima
;
TEdge
*
m_SortedEdges
;
bool
m_ExecuteLocked
;
PolyFillType
m_ClipFillType
;
PolyFillType
m_SubjFillType
;
bool
m_ReverseOutput
;
bool
m_UsingPolyTree
;
bool
m_StrictSimple
;
#ifdef use_xyz
ZFillCallback
m_ZFill
;
//custom callback
#endif
void
SetWindingCount
(
TEdge
&
edge
);
bool
IsEvenOddFillType
(
const
TEdge
&
edge
)
const
;
bool
IsEvenOddAltFillType
(
const
TEdge
&
edge
)
const
;
void
InsertLocalMinimaIntoAEL
(
const
cInt
botY
);
void
InsertEdgeIntoAEL
(
TEdge
*
edge
,
TEdge
*
startEdge
);
void
AddEdgeToSEL
(
TEdge
*
edge
);
bool
PopEdgeFromSEL
(
TEdge
*&
edge
);
void
CopyAELToSEL
();
void
DeleteFromSEL
(
TEdge
*
e
);
void
SwapPositionsInSEL
(
TEdge
*
edge1
,
TEdge
*
edge2
);
bool
IsContributing
(
const
TEdge
&
edge
)
const
;
bool
IsTopHorz
(
const
cInt
XPos
);
void
DoMaxima
(
TEdge
*
e
);
void
ProcessHorizontals
();
void
ProcessHorizontal
(
TEdge
*
horzEdge
);
void
AddLocalMaxPoly
(
TEdge
*
e1
,
TEdge
*
e2
,
const
IntPoint
&
pt
);
OutPt
*
AddLocalMinPoly
(
TEdge
*
e1
,
TEdge
*
e2
,
const
IntPoint
&
pt
);
OutRec
*
GetOutRec
(
int
idx
);
void
AppendPolygon
(
TEdge
*
e1
,
TEdge
*
e2
);
void
IntersectEdges
(
TEdge
*
e1
,
TEdge
*
e2
,
IntPoint
&
pt
);
OutPt
*
AddOutPt
(
TEdge
*
e
,
const
IntPoint
&
pt
);
OutPt
*
GetLastOutPt
(
TEdge
*
e
);
bool
ProcessIntersections
(
const
cInt
topY
);
void
BuildIntersectList
(
const
cInt
topY
);
void
ProcessIntersectList
();
void
ProcessEdgesAtTopOfScanbeam
(
const
cInt
topY
);
void
BuildResult
(
Paths
&
polys
);
void
BuildResult2
(
PolyTree
&
polytree
);
void
SetHoleState
(
TEdge
*
e
,
OutRec
*
outrec
);
void
DisposeIntersectNodes
();
bool
FixupIntersectionOrder
();
void
FixupOutPolygon
(
OutRec
&
outrec
);
void
FixupOutPolyline
(
OutRec
&
outrec
);
bool
IsHole
(
TEdge
*
e
);
bool
FindOwnerFromSplitRecs
(
OutRec
&
outRec
,
OutRec
*&
currOrfl
);
void
FixHoleLinkage
(
OutRec
&
outrec
);
void
AddJoin
(
OutPt
*
op1
,
OutPt
*
op2
,
const
IntPoint
offPt
);
void
ClearJoins
();
void
ClearGhostJoins
();
void
AddGhostJoin
(
OutPt
*
op
,
const
IntPoint
offPt
);
bool
JoinPoints
(
Join
*
j
,
OutRec
*
outRec1
,
OutRec
*
outRec2
);
void
JoinCommonEdges
();
void
DoSimplePolygons
();
void
FixupFirstLefts1
(
OutRec
*
OldOutRec
,
OutRec
*
NewOutRec
);
void
FixupFirstLefts2
(
OutRec
*
InnerOutRec
,
OutRec
*
OuterOutRec
);
void
FixupFirstLefts3
(
OutRec
*
OldOutRec
,
OutRec
*
NewOutRec
);
#ifdef use_xyz
void
SetZ
(
IntPoint
&
pt
,
TEdge
&
e1
,
TEdge
&
e2
);
#endif
};
//------------------------------------------------------------------------------
class
ClipperOffset
{
public:
ClipperOffset
(
double
miterLimit
=
2.0
,
double
roundPrecision
=
0.25
);
~
ClipperOffset
();
void
AddPath
(
const
Path
&
path
,
JoinType
joinType
,
EndType
endType
);
void
AddPaths
(
const
Paths
&
paths
,
JoinType
joinType
,
EndType
endType
);
void
Execute
(
Paths
&
solution
,
double
delta
);
void
Execute
(
PolyTree
&
solution
,
double
delta
);
void
Clear
();
double
MiterLimit
;
double
ArcTolerance
;
private:
Paths
m_destPolys
;
Path
m_srcPoly
;
Path
m_destPoly
;
std
::
vector
<
DoublePoint
>
m_normals
;
double
m_delta
,
m_sinA
,
m_sin
,
m_cos
;
double
m_miterLim
,
m_StepsPerRad
;
IntPoint
m_lowest
;
PolyNode
m_polyNodes
;
void
FixOrientations
();
void
DoOffset
(
double
delta
);
void
OffsetPoint
(
int
j
,
int
&
k
,
JoinType
jointype
);
void
DoSquare
(
int
j
,
int
k
);
void
DoMiter
(
int
j
,
int
k
,
double
r
);
void
DoRound
(
int
j
,
int
k
);
};
//------------------------------------------------------------------------------
class
clipperException
:
public
std
::
exception
{
public:
clipperException
(
const
char
*
description
)
:
m_descr
(
description
)
{}
virtual
~
clipperException
()
throw
()
{}
virtual
const
char
*
what
()
const
throw
()
{
return
m_descr
.
c_str
();}
private:
std
::
string
m_descr
;
};
//------------------------------------------------------------------------------
}
//ClipperLib namespace
#endif //clipper_hpp
ppocr/postprocess/lanms/include/pybind11/attr.h
0 → 100644
View file @
26219d5f
/*
pybind11/attr.h: Infrastructure for processing custom
type and function attributes
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include "cast.h"
NAMESPACE_BEGIN
(
pybind11
)
/// \addtogroup annotations
/// @{
/// Annotation for methods
struct
is_method
{
handle
class_
;
is_method
(
const
handle
&
c
)
:
class_
(
c
)
{
}
};
/// Annotation for operators
struct
is_operator
{
};
/// Annotation for parent scope
struct
scope
{
handle
value
;
scope
(
const
handle
&
s
)
:
value
(
s
)
{
}
};
/// Annotation for documentation
struct
doc
{
const
char
*
value
;
doc
(
const
char
*
value
)
:
value
(
value
)
{
}
};
/// Annotation for function names
struct
name
{
const
char
*
value
;
name
(
const
char
*
value
)
:
value
(
value
)
{
}
};
/// Annotation indicating that a function is an overload associated with a given "sibling"
struct
sibling
{
handle
value
;
sibling
(
const
handle
&
value
)
:
value
(
value
.
ptr
())
{
}
};
/// Annotation indicating that a class derives from another given type
template
<
typename
T
>
struct
base
{
PYBIND11_DEPRECATED
(
"base<T>() was deprecated in favor of specifying 'T' as a template argument to class_"
)
base
()
{
}
};
/// Keep patient alive while nurse lives
template
<
size_t
Nurse
,
size_t
Patient
>
struct
keep_alive
{
};
/// Annotation indicating that a class is involved in a multiple inheritance relationship
struct
multiple_inheritance
{
};
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
struct
dynamic_attr
{
};
/// Annotation which enables the buffer protocol for a type
struct
buffer_protocol
{
};
/// Annotation which requests that a special metaclass is created for a type
struct
metaclass
{
handle
value
;
PYBIND11_DEPRECATED
(
"py::metaclass() is no longer required. It's turned on by default now."
)
metaclass
()
{}
/// Override pybind11's default metaclass
explicit
metaclass
(
handle
value
)
:
value
(
value
)
{
}
};
/// Annotation to mark enums as an arithmetic type
struct
arithmetic
{
};
/** \rst
A call policy which places one or more guard variables (``Ts...``) around the function call.
For example, this definition:
.. code-block:: cpp
m.def("foo", foo, py::call_guard<T>());
is equivalent to the following pseudocode:
.. code-block:: cpp
m.def("foo", [](args...) {
T scope_guard;
return foo(args...); // forwarded arguments
});
\endrst */
template
<
typename
...
Ts
>
struct
call_guard
;
template
<
>
struct
call_guard
<>
{
using
type
=
detail
::
void_type
;
};
template
<
typename
T
>
struct
call_guard
<
T
>
{
static_assert
(
std
::
is_default_constructible
<
T
>::
value
,
"The guard type must be default constructible"
);
using
type
=
T
;
};
template
<
typename
T
,
typename
...
Ts
>
struct
call_guard
<
T
,
Ts
...
>
{
struct
type
{
T
guard
{};
// Compose multiple guard types with left-to-right default-constructor order
typename
call_guard
<
Ts
...
>::
type
next
{};
};
};
/// @} annotations
NAMESPACE_BEGIN
(
detail
)
/* Forward declarations */
enum
op_id
:
int
;
enum
op_type
:
int
;
struct
undefined_t
;
template
<
op_id
id
,
op_type
ot
,
typename
L
=
undefined_t
,
typename
R
=
undefined_t
>
struct
op_
;
template
<
typename
...
Args
>
struct
init
;
template
<
typename
...
Args
>
struct
init_alias
;
inline
void
keep_alive_impl
(
size_t
Nurse
,
size_t
Patient
,
function_call
&
call
,
handle
ret
);
/// Internal data structure which holds metadata about a keyword argument
struct
argument_record
{
const
char
*
name
;
///< Argument name
const
char
*
descr
;
///< Human-readable version of the argument value
handle
value
;
///< Associated Python object
bool
convert
:
1
;
///< True if the argument is allowed to convert when loading
bool
none
:
1
;
///< True if None is allowed when loading
argument_record
(
const
char
*
name
,
const
char
*
descr
,
handle
value
,
bool
convert
,
bool
none
)
:
name
(
name
),
descr
(
descr
),
value
(
value
),
convert
(
convert
),
none
(
none
)
{
}
};
/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.)
struct
function_record
{
function_record
()
:
is_constructor
(
false
),
is_stateless
(
false
),
is_operator
(
false
),
has_args
(
false
),
has_kwargs
(
false
),
is_method
(
false
)
{
}
/// Function name
char
*
name
=
nullptr
;
/* why no C++ strings? They generate heavier code.. */
// User-specified documentation string
char
*
doc
=
nullptr
;
/// Human-readable version of the function signature
char
*
signature
=
nullptr
;
/// List of registered keyword arguments
std
::
vector
<
argument_record
>
args
;
/// Pointer to lambda function which converts arguments and performs the actual call
handle
(
*
impl
)
(
function_call
&
)
=
nullptr
;
/// Storage for the wrapped function pointer and captured data, if any
void
*
data
[
3
]
=
{
};
/// Pointer to custom destructor for 'data' (if needed)
void
(
*
free_data
)
(
function_record
*
ptr
)
=
nullptr
;
/// Return value policy associated with this function
return_value_policy
policy
=
return_value_policy
::
automatic
;
/// True if name == '__init__'
bool
is_constructor
:
1
;
/// True if this is a stateless function pointer
bool
is_stateless
:
1
;
/// True if this is an operator (__add__), etc.
bool
is_operator
:
1
;
/// True if the function has a '*args' argument
bool
has_args
:
1
;
/// True if the function has a '**kwargs' argument
bool
has_kwargs
:
1
;
/// True if this is a method
bool
is_method
:
1
;
/// Number of arguments (including py::args and/or py::kwargs, if present)
std
::
uint16_t
nargs
;
/// Python method object
PyMethodDef
*
def
=
nullptr
;
/// Python handle to the parent scope (a class or a module)
handle
scope
;
/// Python handle to the sibling function representing an overload chain
handle
sibling
;
/// Pointer to next overload
function_record
*
next
=
nullptr
;
};
/// Special data structure which (temporarily) holds metadata about a bound class
struct
type_record
{
PYBIND11_NOINLINE
type_record
()
:
multiple_inheritance
(
false
),
dynamic_attr
(
false
),
buffer_protocol
(
false
)
{
}
/// Handle to the parent scope
handle
scope
;
/// Name of the class
const
char
*
name
=
nullptr
;
// Pointer to RTTI type_info data structure
const
std
::
type_info
*
type
=
nullptr
;
/// How large is the underlying C++ type?
size_t
type_size
=
0
;
/// How large is the type's holder?
size_t
holder_size
=
0
;
/// The global operator new can be overridden with a class-specific variant
void
*
(
*
operator_new
)(
size_t
)
=
::
operator
new
;
/// Function pointer to class_<..>::init_instance
void
(
*
init_instance
)(
instance
*
,
const
void
*
)
=
nullptr
;
/// Function pointer to class_<..>::dealloc
void
(
*
dealloc
)(
const
detail
::
value_and_holder
&
)
=
nullptr
;
/// List of base classes of the newly created type
list
bases
;
/// Optional docstring
const
char
*
doc
=
nullptr
;
/// Custom metaclass (optional)
handle
metaclass
;
/// Multiple inheritance marker
bool
multiple_inheritance
:
1
;
/// Does the class manage a __dict__?
bool
dynamic_attr
:
1
;
/// Does the class implement the buffer protocol?
bool
buffer_protocol
:
1
;
/// Is the default (unique_ptr) holder type used?
bool
default_holder
:
1
;
PYBIND11_NOINLINE
void
add_base
(
const
std
::
type_info
&
base
,
void
*
(
*
caster
)(
void
*
))
{
auto
base_info
=
detail
::
get_type_info
(
base
,
false
);
if
(
!
base_info
)
{
std
::
string
tname
(
base
.
name
());
detail
::
clean_type_id
(
tname
);
pybind11_fail
(
"generic_type: type
\"
"
+
std
::
string
(
name
)
+
"
\"
referenced unknown base type
\"
"
+
tname
+
"
\"
"
);
}
if
(
default_holder
!=
base_info
->
default_holder
)
{
std
::
string
tname
(
base
.
name
());
detail
::
clean_type_id
(
tname
);
pybind11_fail
(
"generic_type: type
\"
"
+
std
::
string
(
name
)
+
"
\"
"
+
(
default_holder
?
"does not have"
:
"has"
)
+
" a non-default holder type while its base
\"
"
+
tname
+
"
\"
"
+
(
base_info
->
default_holder
?
"does not"
:
"does"
));
}
bases
.
append
((
PyObject
*
)
base_info
->
type
);
if
(
base_info
->
type
->
tp_dictoffset
!=
0
)
dynamic_attr
=
true
;
if
(
caster
)
base_info
->
implicit_casts
.
emplace_back
(
type
,
caster
);
}
};
inline
function_call
::
function_call
(
function_record
&
f
,
handle
p
)
:
func
(
f
),
parent
(
p
)
{
args
.
reserve
(
f
.
nargs
);
args_convert
.
reserve
(
f
.
nargs
);
}
/**
* Partial template specializations to process custom attributes provided to
* cpp_function_ and class_. These are either used to initialize the respective
* fields in the type_record and function_record data structures or executed at
* runtime to deal with custom call policies (e.g. keep_alive).
*/
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
process_attribute
;
template
<
typename
T
>
struct
process_attribute_default
{
/// Default implementation: do nothing
static
void
init
(
const
T
&
,
function_record
*
)
{
}
static
void
init
(
const
T
&
,
type_record
*
)
{
}
static
void
precall
(
function_call
&
)
{
}
static
void
postcall
(
function_call
&
,
handle
)
{
}
};
/// Process an attribute specifying the function's name
template
<
>
struct
process_attribute
<
name
>
:
process_attribute_default
<
name
>
{
static
void
init
(
const
name
&
n
,
function_record
*
r
)
{
r
->
name
=
const_cast
<
char
*>
(
n
.
value
);
}
};
/// Process an attribute specifying the function's docstring
template
<
>
struct
process_attribute
<
doc
>
:
process_attribute_default
<
doc
>
{
static
void
init
(
const
doc
&
n
,
function_record
*
r
)
{
r
->
doc
=
const_cast
<
char
*>
(
n
.
value
);
}
};
/// Process an attribute specifying the function's docstring (provided as a C-style string)
template
<
>
struct
process_attribute
<
const
char
*>
:
process_attribute_default
<
const
char
*>
{
static
void
init
(
const
char
*
d
,
function_record
*
r
)
{
r
->
doc
=
const_cast
<
char
*>
(
d
);
}
static
void
init
(
const
char
*
d
,
type_record
*
r
)
{
r
->
doc
=
const_cast
<
char
*>
(
d
);
}
};
template
<
>
struct
process_attribute
<
char
*>
:
process_attribute
<
const
char
*>
{
};
/// Process an attribute indicating the function's return value policy
template
<
>
struct
process_attribute
<
return_value_policy
>
:
process_attribute_default
<
return_value_policy
>
{
static
void
init
(
const
return_value_policy
&
p
,
function_record
*
r
)
{
r
->
policy
=
p
;
}
};
/// Process an attribute which indicates that this is an overloaded function associated with a given sibling
template
<
>
struct
process_attribute
<
sibling
>
:
process_attribute_default
<
sibling
>
{
static
void
init
(
const
sibling
&
s
,
function_record
*
r
)
{
r
->
sibling
=
s
.
value
;
}
};
/// Process an attribute which indicates that this function is a method
template
<
>
struct
process_attribute
<
is_method
>
:
process_attribute_default
<
is_method
>
{
static
void
init
(
const
is_method
&
s
,
function_record
*
r
)
{
r
->
is_method
=
true
;
r
->
scope
=
s
.
class_
;
}
};
/// Process an attribute which indicates the parent scope of a method
template
<
>
struct
process_attribute
<
scope
>
:
process_attribute_default
<
scope
>
{
static
void
init
(
const
scope
&
s
,
function_record
*
r
)
{
r
->
scope
=
s
.
value
;
}
};
/// Process an attribute which indicates that this function is an operator
template
<
>
struct
process_attribute
<
is_operator
>
:
process_attribute_default
<
is_operator
>
{
static
void
init
(
const
is_operator
&
,
function_record
*
r
)
{
r
->
is_operator
=
true
;
}
};
/// Process a keyword argument attribute (*without* a default value)
template
<
>
struct
process_attribute
<
arg
>
:
process_attribute_default
<
arg
>
{
static
void
init
(
const
arg
&
a
,
function_record
*
r
)
{
if
(
r
->
is_method
&&
r
->
args
.
empty
())
r
->
args
.
emplace_back
(
"self"
,
nullptr
,
handle
(),
true
/*convert*/
,
false
/*none not allowed*/
);
r
->
args
.
emplace_back
(
a
.
name
,
nullptr
,
handle
(),
!
a
.
flag_noconvert
,
a
.
flag_none
);
}
};
/// Process a keyword argument attribute (*with* a default value)
template
<
>
struct
process_attribute
<
arg_v
>
:
process_attribute_default
<
arg_v
>
{
static
void
init
(
const
arg_v
&
a
,
function_record
*
r
)
{
if
(
r
->
is_method
&&
r
->
args
.
empty
())
r
->
args
.
emplace_back
(
"self"
,
nullptr
/*descr*/
,
handle
()
/*parent*/
,
true
/*convert*/
,
false
/*none not allowed*/
);
if
(
!
a
.
value
)
{
#if !defined(NDEBUG)
std
::
string
descr
(
"'"
);
if
(
a
.
name
)
descr
+=
std
::
string
(
a
.
name
)
+
": "
;
descr
+=
a
.
type
+
"'"
;
if
(
r
->
is_method
)
{
if
(
r
->
name
)
descr
+=
" in method '"
+
(
std
::
string
)
str
(
r
->
scope
)
+
"."
+
(
std
::
string
)
r
->
name
+
"'"
;
else
descr
+=
" in method of '"
+
(
std
::
string
)
str
(
r
->
scope
)
+
"'"
;
}
else
if
(
r
->
name
)
{
descr
+=
" in function '"
+
(
std
::
string
)
r
->
name
+
"'"
;
}
pybind11_fail
(
"arg(): could not convert default argument "
+
descr
+
" into a Python object (type not registered yet?)"
);
#else
pybind11_fail
(
"arg(): could not convert default argument "
"into a Python object (type not registered yet?). "
"Compile in debug mode for more information."
);
#endif
}
r
->
args
.
emplace_back
(
a
.
name
,
a
.
descr
,
a
.
value
.
inc_ref
(),
!
a
.
flag_noconvert
,
a
.
flag_none
);
}
};
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that)
template
<
typename
T
>
struct
process_attribute
<
T
,
enable_if_t
<
is_pyobject
<
T
>::
value
>>
:
process_attribute_default
<
handle
>
{
static
void
init
(
const
handle
&
h
,
type_record
*
r
)
{
r
->
bases
.
append
(
h
);
}
};
/// Process a parent class attribute (deprecated, does not support multiple inheritance)
template
<
typename
T
>
struct
process_attribute
<
base
<
T
>>
:
process_attribute_default
<
base
<
T
>>
{
static
void
init
(
const
base
<
T
>
&
,
type_record
*
r
)
{
r
->
add_base
(
typeid
(
T
),
nullptr
);
}
};
/// Process a multiple inheritance attribute
template
<
>
struct
process_attribute
<
multiple_inheritance
>
:
process_attribute_default
<
multiple_inheritance
>
{
static
void
init
(
const
multiple_inheritance
&
,
type_record
*
r
)
{
r
->
multiple_inheritance
=
true
;
}
};
template
<
>
struct
process_attribute
<
dynamic_attr
>
:
process_attribute_default
<
dynamic_attr
>
{
static
void
init
(
const
dynamic_attr
&
,
type_record
*
r
)
{
r
->
dynamic_attr
=
true
;
}
};
template
<
>
struct
process_attribute
<
buffer_protocol
>
:
process_attribute_default
<
buffer_protocol
>
{
static
void
init
(
const
buffer_protocol
&
,
type_record
*
r
)
{
r
->
buffer_protocol
=
true
;
}
};
template
<
>
struct
process_attribute
<
metaclass
>
:
process_attribute_default
<
metaclass
>
{
static
void
init
(
const
metaclass
&
m
,
type_record
*
r
)
{
r
->
metaclass
=
m
.
value
;
}
};
/// Process an 'arithmetic' attribute for enums (does nothing here)
template
<
>
struct
process_attribute
<
arithmetic
>
:
process_attribute_default
<
arithmetic
>
{};
template
<
typename
...
Ts
>
struct
process_attribute
<
call_guard
<
Ts
...
>>
:
process_attribute_default
<
call_guard
<
Ts
...
>>
{
};
/**
* Process a keep_alive call policy -- invokes keep_alive_impl during the
* pre-call handler if both Nurse, Patient != 0 and use the post-call handler
* otherwise
*/
template
<
size_t
Nurse
,
size_t
Patient
>
struct
process_attribute
<
keep_alive
<
Nurse
,
Patient
>>
:
public
process_attribute_default
<
keep_alive
<
Nurse
,
Patient
>>
{
template
<
size_t
N
=
Nurse
,
size_t
P
=
Patient
,
enable_if_t
<
N
!=
0
&&
P
!=
0
,
int
>
=
0
>
static
void
precall
(
function_call
&
call
)
{
keep_alive_impl
(
Nurse
,
Patient
,
call
,
handle
());
}
template
<
size_t
N
=
Nurse
,
size_t
P
=
Patient
,
enable_if_t
<
N
!=
0
&&
P
!=
0
,
int
>
=
0
>
static
void
postcall
(
function_call
&
,
handle
)
{
}
template
<
size_t
N
=
Nurse
,
size_t
P
=
Patient
,
enable_if_t
<
N
==
0
||
P
==
0
,
int
>
=
0
>
static
void
precall
(
function_call
&
)
{
}
template
<
size_t
N
=
Nurse
,
size_t
P
=
Patient
,
enable_if_t
<
N
==
0
||
P
==
0
,
int
>
=
0
>
static
void
postcall
(
function_call
&
call
,
handle
ret
)
{
keep_alive_impl
(
Nurse
,
Patient
,
call
,
ret
);
}
};
/// Recursively iterate over variadic template arguments
template
<
typename
...
Args
>
struct
process_attributes
{
static
void
init
(
const
Args
&
...
args
,
function_record
*
r
)
{
int
unused
[]
=
{
0
,
(
process_attribute
<
typename
std
::
decay
<
Args
>::
type
>::
init
(
args
,
r
),
0
)
...
};
ignore_unused
(
unused
);
}
static
void
init
(
const
Args
&
...
args
,
type_record
*
r
)
{
int
unused
[]
=
{
0
,
(
process_attribute
<
typename
std
::
decay
<
Args
>::
type
>::
init
(
args
,
r
),
0
)
...
};
ignore_unused
(
unused
);
}
static
void
precall
(
function_call
&
call
)
{
int
unused
[]
=
{
0
,
(
process_attribute
<
typename
std
::
decay
<
Args
>::
type
>::
precall
(
call
),
0
)
...
};
ignore_unused
(
unused
);
}
static
void
postcall
(
function_call
&
call
,
handle
fn_ret
)
{
int
unused
[]
=
{
0
,
(
process_attribute
<
typename
std
::
decay
<
Args
>::
type
>::
postcall
(
call
,
fn_ret
),
0
)
...
};
ignore_unused
(
unused
);
}
};
template
<
typename
T
>
using
is_call_guard
=
is_instantiation
<
call_guard
,
T
>
;
/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found)
template
<
typename
...
Extra
>
using
extract_guard_t
=
typename
exactly_one_t
<
is_call_guard
,
call_guard
<>
,
Extra
...
>::
type
;
/// Check the number of named arguments at compile time
template
<
typename
...
Extra
,
size_t
named
=
constexpr_sum
(
std
::
is_base_of
<
arg
,
Extra
>
::
value
...),
size_t
self
=
constexpr_sum
(
std
::
is_same
<
is_method
,
Extra
>::
value
...)
>
constexpr
bool
expected_num_args
(
size_t
nargs
,
bool
has_args
,
bool
has_kwargs
)
{
return
named
==
0
||
(
self
+
named
+
has_args
+
has_kwargs
)
==
nargs
;
}
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
pybind11
)
ppocr/postprocess/lanms/include/pybind11/buffer_info.h
0 → 100644
View file @
26219d5f
/*
pybind11/buffer_info.h: Python buffer object interface
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include "common.h"
NAMESPACE_BEGIN
(
pybind11
)
/// Information record describing a Python buffer object
struct
buffer_info
{
void
*
ptr
=
nullptr
;
// Pointer to the underlying storage
ssize_t
itemsize
=
0
;
// Size of individual items in bytes
ssize_t
size
=
0
;
// Total number of entries
std
::
string
format
;
// For homogeneous buffers, this should be set to format_descriptor<T>::format()
ssize_t
ndim
=
0
;
// Number of dimensions
std
::
vector
<
ssize_t
>
shape
;
// Shape of the tensor (1 entry per dimension)
std
::
vector
<
ssize_t
>
strides
;
// Number of entries between adjacent entries (for each per dimension)
buffer_info
()
{
}
buffer_info
(
void
*
ptr
,
ssize_t
itemsize
,
const
std
::
string
&
format
,
ssize_t
ndim
,
detail
::
any_container
<
ssize_t
>
shape_in
,
detail
::
any_container
<
ssize_t
>
strides_in
)
:
ptr
(
ptr
),
itemsize
(
itemsize
),
size
(
1
),
format
(
format
),
ndim
(
ndim
),
shape
(
std
::
move
(
shape_in
)),
strides
(
std
::
move
(
strides_in
))
{
if
(
ndim
!=
(
ssize_t
)
shape
.
size
()
||
ndim
!=
(
ssize_t
)
strides
.
size
())
pybind11_fail
(
"buffer_info: ndim doesn't match shape and/or strides length"
);
for
(
size_t
i
=
0
;
i
<
(
size_t
)
ndim
;
++
i
)
size
*=
shape
[
i
];
}
template
<
typename
T
>
buffer_info
(
T
*
ptr
,
detail
::
any_container
<
ssize_t
>
shape_in
,
detail
::
any_container
<
ssize_t
>
strides_in
)
:
buffer_info
(
private_ctr_tag
(),
ptr
,
sizeof
(
T
),
format_descriptor
<
T
>::
format
(),
static_cast
<
ssize_t
>
(
shape_in
->
size
()),
std
::
move
(
shape_in
),
std
::
move
(
strides_in
))
{
}
buffer_info
(
void
*
ptr
,
ssize_t
itemsize
,
const
std
::
string
&
format
,
ssize_t
size
)
:
buffer_info
(
ptr
,
itemsize
,
format
,
1
,
{
size
},
{
itemsize
})
{
}
template
<
typename
T
>
buffer_info
(
T
*
ptr
,
ssize_t
size
)
:
buffer_info
(
ptr
,
sizeof
(
T
),
format_descriptor
<
T
>::
format
(),
size
)
{
}
explicit
buffer_info
(
Py_buffer
*
view
,
bool
ownview
=
true
)
:
buffer_info
(
view
->
buf
,
view
->
itemsize
,
view
->
format
,
view
->
ndim
,
{
view
->
shape
,
view
->
shape
+
view
->
ndim
},
{
view
->
strides
,
view
->
strides
+
view
->
ndim
})
{
this
->
view
=
view
;
this
->
ownview
=
ownview
;
}
buffer_info
(
const
buffer_info
&
)
=
delete
;
buffer_info
&
operator
=
(
const
buffer_info
&
)
=
delete
;
buffer_info
(
buffer_info
&&
other
)
{
(
*
this
)
=
std
::
move
(
other
);
}
buffer_info
&
operator
=
(
buffer_info
&&
rhs
)
{
ptr
=
rhs
.
ptr
;
itemsize
=
rhs
.
itemsize
;
size
=
rhs
.
size
;
format
=
std
::
move
(
rhs
.
format
);
ndim
=
rhs
.
ndim
;
shape
=
std
::
move
(
rhs
.
shape
);
strides
=
std
::
move
(
rhs
.
strides
);
std
::
swap
(
view
,
rhs
.
view
);
std
::
swap
(
ownview
,
rhs
.
ownview
);
return
*
this
;
}
~
buffer_info
()
{
if
(
view
&&
ownview
)
{
PyBuffer_Release
(
view
);
delete
view
;
}
}
private:
struct
private_ctr_tag
{
};
buffer_info
(
private_ctr_tag
,
void
*
ptr
,
ssize_t
itemsize
,
const
std
::
string
&
format
,
ssize_t
ndim
,
detail
::
any_container
<
ssize_t
>
&&
shape_in
,
detail
::
any_container
<
ssize_t
>
&&
strides_in
)
:
buffer_info
(
ptr
,
itemsize
,
format
,
ndim
,
std
::
move
(
shape_in
),
std
::
move
(
strides_in
))
{
}
Py_buffer
*
view
=
nullptr
;
bool
ownview
=
false
;
};
NAMESPACE_BEGIN
(
detail
)
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
compare_buffer_info
{
static
bool
compare
(
const
buffer_info
&
b
)
{
return
b
.
format
==
format_descriptor
<
T
>::
format
()
&&
b
.
itemsize
==
(
ssize_t
)
sizeof
(
T
);
}
};
template
<
typename
T
>
struct
compare_buffer_info
<
T
,
detail
::
enable_if_t
<
std
::
is_integral
<
T
>::
value
>>
{
static
bool
compare
(
const
buffer_info
&
b
)
{
return
(
size_t
)
b
.
itemsize
==
sizeof
(
T
)
&&
(
b
.
format
==
format_descriptor
<
T
>::
value
||
((
sizeof
(
T
)
==
sizeof
(
long
))
&&
b
.
format
==
(
std
::
is_unsigned
<
T
>::
value
?
"L"
:
"l"
))
||
((
sizeof
(
T
)
==
sizeof
(
size_t
))
&&
b
.
format
==
(
std
::
is_unsigned
<
T
>::
value
?
"N"
:
"n"
)));
}
};
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
pybind11
)
Prev
1
…
10
11
12
13
14
Next
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