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
2401626a
Commit
2401626a
authored
Jun 23, 2020
by
dyning
Browse files
Merge branch 'develop' of
https://github.com/MissPenguin/PaddleOCR
into develop
parents
160bb06e
b0171a71
Changes
33
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
9818 additions
and
10 deletions
+9818
-10
ppocr/data/det/db_process.py
ppocr/data/det/db_process.py
+2
-2
ppocr/data/det/east_process.py
ppocr/data/det/east_process.py
+29
-6
ppocr/modeling/heads/det_east_head.py
ppocr/modeling/heads/det_east_head.py
+2
-1
ppocr/postprocess/east_postprocess.py
ppocr/postprocess/east_postprocess.py
+9
-1
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
ppocr/postprocess/lanms/include/pybind11/cast.h
ppocr/postprocess/lanms/include/pybind11/cast.h
+2058
-0
ppocr/postprocess/lanms/include/pybind11/chrono.h
ppocr/postprocess/lanms/include/pybind11/chrono.h
+162
-0
ppocr/postprocess/lanms/include/pybind11/class_support.h
ppocr/postprocess/lanms/include/pybind11/class_support.h
+603
-0
ppocr/postprocess/lanms/include/pybind11/common.h
ppocr/postprocess/lanms/include/pybind11/common.h
+857
-0
ppocr/postprocess/lanms/include/pybind11/complex.h
ppocr/postprocess/lanms/include/pybind11/complex.h
+61
-0
ppocr/postprocess/lanms/include/pybind11/descr.h
ppocr/postprocess/lanms/include/pybind11/descr.h
+185
-0
No files found.
ppocr/data/det/db_process.py
View file @
2401626a
...
...
@@ -125,8 +125,8 @@ class DBProcessTest(object):
def
__init__
(
self
,
params
):
super
(
DBProcessTest
,
self
).
__init__
()
self
.
resize_type
=
0
if
'
de
t_image_shape'
in
params
:
self
.
image_shape
=
params
[
'
de
t_image_shape'
]
if
'
tes
t_image_shape'
in
params
:
self
.
image_shape
=
params
[
'
tes
t_image_shape'
]
# print(self.image_shape)
self
.
resize_type
=
1
if
'max_side_len'
in
params
:
...
...
ppocr/data/det/east_process.py
View file @
2401626a
...
...
@@ -455,17 +455,23 @@ class EASTProcessTrain(object):
class
EASTProcessTest
(
object
):
def
__init__
(
self
,
params
):
super
(
EASTProcessTest
,
self
).
__init__
()
self
.
resize_type
=
0
if
'test_image_shape'
in
params
:
self
.
image_shape
=
params
[
'test_image_shape'
]
# print(self.image_shape)
self
.
resize_type
=
1
if
'max_side_len'
in
params
:
self
.
max_side_len
=
params
[
'max_side_len'
]
else
:
self
.
max_side_len
=
2400
def
resize_image
(
self
,
im
):
def
resize_image
_type0
(
self
,
im
):
"""
resize image to a size multiple of 32 which is required by the network
:param im: the resized image
:param max_side_len: limit of max image size to avoid out of memory in gpu
:return: the resized image and the resize ratio
args:
img(array): array with shape [h, w, c]
return(tuple):
img, (ratio_h, ratio_w)
"""
max_side_len
=
self
.
max_side_len
h
,
w
,
_
=
im
.
shape
...
...
@@ -495,13 +501,30 @@ class EASTProcessTest(object):
resize_w
=
32
else
:
resize_w
=
(
resize_w
//
32
-
1
)
*
32
im
=
cv2
.
resize
(
im
,
(
int
(
resize_w
),
int
(
resize_h
)))
try
:
if
int
(
resize_w
)
<=
0
or
int
(
resize_h
)
<=
0
:
return
None
,
(
None
,
None
)
im
=
cv2
.
resize
(
im
,
(
int
(
resize_w
),
int
(
resize_h
)))
except
:
print
(
im
.
shape
,
resize_w
,
resize_h
)
sys
.
exit
(
0
)
ratio_h
=
resize_h
/
float
(
h
)
ratio_w
=
resize_w
/
float
(
w
)
return
im
,
(
ratio_h
,
ratio_w
)
def
resize_image_type1
(
self
,
im
):
resize_h
,
resize_w
=
self
.
image_shape
ori_h
,
ori_w
=
im
.
shape
[:
2
]
# (h, w, c)
im
=
cv2
.
resize
(
im
,
(
int
(
resize_w
),
int
(
resize_h
)))
ratio_h
=
float
(
resize_h
)
/
ori_h
ratio_w
=
float
(
resize_w
)
/
ori_w
return
im
,
(
ratio_h
,
ratio_w
)
def
__call__
(
self
,
im
):
im
,
(
ratio_h
,
ratio_w
)
=
self
.
resize_image
(
im
)
if
self
.
resize_type
==
0
:
im
,
(
ratio_h
,
ratio_w
)
=
self
.
resize_image_type0
(
im
)
else
:
im
,
(
ratio_h
,
ratio_w
)
=
self
.
resize_image_type1
(
im
)
img_mean
=
[
0.485
,
0.456
,
0.406
]
img_std
=
[
0.229
,
0.224
,
0.225
]
im
=
im
[:,
:,
::
-
1
].
astype
(
np
.
float32
)
...
...
ppocr/modeling/heads/det_east_head.py
View file @
2401626a
...
...
@@ -18,6 +18,7 @@ from __future__ import print_function
import
paddle.fluid
as
fluid
from
..common_functions
import
conv_bn_layer
,
deconv_bn_layer
from
collections
import
OrderedDict
class
EASTHead
(
object
):
...
...
@@ -110,7 +111,7 @@ class EASTHead(object):
def
__call__
(
self
,
inputs
):
f_common
=
self
.
unet_fusion
(
inputs
)
f_score
,
f_geo
=
self
.
detector_header
(
f_common
)
predicts
=
{}
predicts
=
OrderedDict
()
predicts
[
'f_score'
]
=
f_score
predicts
[
'f_geo'
]
=
f_geo
return
predicts
ppocr/postprocess/east_postprocess.py
View file @
2401626a
...
...
@@ -20,6 +20,13 @@ import numpy as np
from
.locality_aware_nms
import
nms_locality
import
cv2
import
os
import
sys
__dir__
=
os
.
path
.
dirname
(
__file__
)
sys
.
path
.
append
(
__dir__
)
sys
.
path
.
append
(
os
.
path
.
join
(
__dir__
,
'..'
))
import
lanms
class
EASTPostPocess
(
object
):
"""
...
...
@@ -66,7 +73,8 @@ class EASTPostPocess(object):
boxes
=
np
.
zeros
((
text_box_restored
.
shape
[
0
],
9
),
dtype
=
np
.
float32
)
boxes
[:,
:
8
]
=
text_box_restored
.
reshape
((
-
1
,
8
))
boxes
[:,
8
]
=
score_map
[
xy_text
[:,
0
],
xy_text
[:,
1
]]
boxes
=
nms_locality
(
boxes
.
astype
(
np
.
float64
),
nms_thresh
)
# boxes = nms_locality(boxes.astype(np.float64), nms_thresh)
boxes
=
lanms
.
merge_quadrangle_n9
(
boxes
,
nms_thresh
)
if
boxes
.
shape
[
0
]
==
0
:
return
[]
# Here we filter some low score boxes by the average score map,
...
...
ppocr/postprocess/lanms/.gitignore
0 → 100644
View file @
2401626a
adaptor.so
ppocr/postprocess/lanms/.ycm_extra_conf.py
0 → 100644
View file @
2401626a
#!/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 @
2401626a
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 @
2401626a
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 @
2401626a
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 @
2401626a
#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 @
2401626a
/*******************************************************************************
* *
* 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 @
2401626a
/*******************************************************************************
* *
* 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 @
2401626a
/*
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 @
2401626a
/*
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
)
ppocr/postprocess/lanms/include/pybind11/cast.h
0 → 100644
View file @
2401626a
/*
pybind11/cast.h: Partial template specializations to cast between
C++ and Python types
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 "pytypes.h"
#include "typeid.h"
#include "descr.h"
#include <array>
#include <limits>
#include <tuple>
#if defined(PYBIND11_CPP17)
# if defined(__has_include)
# if __has_include(<string_view>)
# define PYBIND11_HAS_STRING_VIEW
# endif
# elif defined(_MSC_VER)
# define PYBIND11_HAS_STRING_VIEW
# endif
#endif
#ifdef PYBIND11_HAS_STRING_VIEW
#include <string_view>
#endif
NAMESPACE_BEGIN
(
pybind11
)
NAMESPACE_BEGIN
(
detail
)
// Forward declarations:
inline
PyTypeObject
*
make_static_property_type
();
inline
PyTypeObject
*
make_default_metaclass
();
inline
PyObject
*
make_object_base_type
(
PyTypeObject
*
metaclass
);
struct
value_and_holder
;
/// Additional type information which does not fit into the PyTypeObject
struct
type_info
{
PyTypeObject
*
type
;
const
std
::
type_info
*
cpptype
;
size_t
type_size
,
holder_size_in_ptrs
;
void
*
(
*
operator_new
)(
size_t
);
void
(
*
init_instance
)(
instance
*
,
const
void
*
);
void
(
*
dealloc
)(
const
value_and_holder
&
v_h
);
std
::
vector
<
PyObject
*
(
*
)(
PyObject
*
,
PyTypeObject
*
)
>
implicit_conversions
;
std
::
vector
<
std
::
pair
<
const
std
::
type_info
*
,
void
*
(
*
)(
void
*
)
>>
implicit_casts
;
std
::
vector
<
bool
(
*
)(
PyObject
*
,
void
*&
)
>
*
direct_conversions
;
buffer_info
*
(
*
get_buffer
)(
PyObject
*
,
void
*
)
=
nullptr
;
void
*
get_buffer_data
=
nullptr
;
/* A simple type never occurs as a (direct or indirect) parent
* of a class that makes use of multiple inheritance */
bool
simple_type
:
1
;
/* True if there is no multiple inheritance in this type's inheritance tree */
bool
simple_ancestors
:
1
;
/* for base vs derived holder_type checks */
bool
default_holder
:
1
;
};
// Store the static internals pointer in a version-specific function so that we're guaranteed it
// will be distinct for modules compiled for different pybind11 versions. Without this, some
// compilers (i.e. gcc) can use the same static pointer storage location across different .so's,
// even though the `get_internals()` function itself is local to each shared object.
template
<
int
=
PYBIND11_VERSION_MAJOR
,
int
=
PYBIND11_VERSION_MINOR
>
internals
*&
get_internals_ptr
()
{
static
internals
*
internals_ptr
=
nullptr
;
return
internals_ptr
;
}
PYBIND11_NOINLINE
inline
internals
&
get_internals
()
{
internals
*&
internals_ptr
=
get_internals_ptr
();
if
(
internals_ptr
)
return
*
internals_ptr
;
handle
builtins
(
PyEval_GetBuiltins
());
const
char
*
id
=
PYBIND11_INTERNALS_ID
;
if
(
builtins
.
contains
(
id
)
&&
isinstance
<
capsule
>
(
builtins
[
id
]))
{
internals_ptr
=
*
static_cast
<
internals
**>
(
capsule
(
builtins
[
id
]));
}
else
{
internals_ptr
=
new
internals
();
#if defined(WITH_THREAD)
PyEval_InitThreads
();
PyThreadState
*
tstate
=
PyThreadState_Get
();
internals_ptr
->
tstate
=
PyThread_create_key
();
PyThread_set_key_value
(
internals_ptr
->
tstate
,
tstate
);
internals_ptr
->
istate
=
tstate
->
interp
;
#endif
builtins
[
id
]
=
capsule
(
&
internals_ptr
);
internals_ptr
->
registered_exception_translators
.
push_front
(
[](
std
::
exception_ptr
p
)
->
void
{
try
{
if
(
p
)
std
::
rethrow_exception
(
p
);
}
catch
(
error_already_set
&
e
)
{
e
.
restore
();
return
;
}
catch
(
const
builtin_exception
&
e
)
{
e
.
set_error
();
return
;
}
catch
(
const
std
::
bad_alloc
&
e
)
{
PyErr_SetString
(
PyExc_MemoryError
,
e
.
what
());
return
;
}
catch
(
const
std
::
domain_error
&
e
)
{
PyErr_SetString
(
PyExc_ValueError
,
e
.
what
());
return
;
}
catch
(
const
std
::
invalid_argument
&
e
)
{
PyErr_SetString
(
PyExc_ValueError
,
e
.
what
());
return
;
}
catch
(
const
std
::
length_error
&
e
)
{
PyErr_SetString
(
PyExc_ValueError
,
e
.
what
());
return
;
}
catch
(
const
std
::
out_of_range
&
e
)
{
PyErr_SetString
(
PyExc_IndexError
,
e
.
what
());
return
;
}
catch
(
const
std
::
range_error
&
e
)
{
PyErr_SetString
(
PyExc_ValueError
,
e
.
what
());
return
;
}
catch
(
const
std
::
exception
&
e
)
{
PyErr_SetString
(
PyExc_RuntimeError
,
e
.
what
());
return
;
}
catch
(...)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"Caught an unknown exception!"
);
return
;
}
}
);
internals_ptr
->
static_property_type
=
make_static_property_type
();
internals_ptr
->
default_metaclass
=
make_default_metaclass
();
internals_ptr
->
instance_base
=
make_object_base_type
(
internals_ptr
->
default_metaclass
);
}
return
*
internals_ptr
;
}
/// A life support system for temporary objects created by `type_caster::load()`.
/// Adding a patient will keep it alive up until the enclosing function returns.
class
loader_life_support
{
public:
/// A new patient frame is created when a function is entered
loader_life_support
()
{
get_internals
().
loader_patient_stack
.
push_back
(
nullptr
);
}
/// ... and destroyed after it returns
~
loader_life_support
()
{
auto
&
stack
=
get_internals
().
loader_patient_stack
;
if
(
stack
.
empty
())
pybind11_fail
(
"loader_life_support: internal error"
);
auto
ptr
=
stack
.
back
();
stack
.
pop_back
();
Py_CLEAR
(
ptr
);
// A heuristic to reduce the stack's capacity (e.g. after long recursive calls)
if
(
stack
.
capacity
()
>
16
&&
stack
.
size
()
!=
0
&&
stack
.
capacity
()
/
stack
.
size
()
>
2
)
stack
.
shrink_to_fit
();
}
/// This can only be used inside a pybind11-bound function, either by `argument_loader`
/// at argument preparation time or by `py::cast()` at execution time.
PYBIND11_NOINLINE
static
void
add_patient
(
handle
h
)
{
auto
&
stack
=
get_internals
().
loader_patient_stack
;
if
(
stack
.
empty
())
throw
cast_error
(
"When called outside a bound function, py::cast() cannot "
"do Python -> C++ conversions which require the creation "
"of temporary values"
);
auto
&
list_ptr
=
stack
.
back
();
if
(
list_ptr
==
nullptr
)
{
list_ptr
=
PyList_New
(
1
);
if
(
!
list_ptr
)
pybind11_fail
(
"loader_life_support: error allocating list"
);
PyList_SET_ITEM
(
list_ptr
,
0
,
h
.
inc_ref
().
ptr
());
}
else
{
auto
result
=
PyList_Append
(
list_ptr
,
h
.
ptr
());
if
(
result
==
-
1
)
pybind11_fail
(
"loader_life_support: error adding patient"
);
}
}
};
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair
// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
// just created.
inline
std
::
pair
<
decltype
(
internals
::
registered_types_py
)
::
iterator
,
bool
>
all_type_info_get_cache
(
PyTypeObject
*
type
);
// Populates a just-created cache entry.
PYBIND11_NOINLINE
inline
void
all_type_info_populate
(
PyTypeObject
*
t
,
std
::
vector
<
type_info
*>
&
bases
)
{
std
::
vector
<
PyTypeObject
*>
check
;
for
(
handle
parent
:
reinterpret_borrow
<
tuple
>
(
t
->
tp_bases
))
check
.
push_back
((
PyTypeObject
*
)
parent
.
ptr
());
auto
const
&
type_dict
=
get_internals
().
registered_types_py
;
for
(
size_t
i
=
0
;
i
<
check
.
size
();
i
++
)
{
auto
type
=
check
[
i
];
// Ignore Python2 old-style class super type:
if
(
!
PyType_Check
((
PyObject
*
)
type
))
continue
;
// Check `type` in the current set of registered python types:
auto
it
=
type_dict
.
find
(
type
);
if
(
it
!=
type_dict
.
end
())
{
// We found a cache entry for it, so it's either pybind-registered or has pre-computed
// pybind bases, but we have to make sure we haven't already seen the type(s) before: we
// want to follow Python/virtual C++ rules that there should only be one instance of a
// common base.
for
(
auto
*
tinfo
:
it
->
second
)
{
// NB: Could use a second set here, rather than doing a linear search, but since
// having a large number of immediate pybind11-registered types seems fairly
// unlikely, that probably isn't worthwhile.
bool
found
=
false
;
for
(
auto
*
known
:
bases
)
{
if
(
known
==
tinfo
)
{
found
=
true
;
break
;
}
}
if
(
!
found
)
bases
.
push_back
(
tinfo
);
}
}
else
if
(
type
->
tp_bases
)
{
// It's some python type, so keep follow its bases classes to look for one or more
// registered types
if
(
i
+
1
==
check
.
size
())
{
// When we're at the end, we can pop off the current element to avoid growing
// `check` when adding just one base (which is typical--.e. when there is no
// multiple inheritance)
check
.
pop_back
();
i
--
;
}
for
(
handle
parent
:
reinterpret_borrow
<
tuple
>
(
type
->
tp_bases
))
check
.
push_back
((
PyTypeObject
*
)
parent
.
ptr
());
}
}
}
/**
* Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will
* be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side
* derived class that uses single inheritance. Will contain as many types as required for a Python
* class that uses multiple inheritance to inherit (directly or indirectly) from multiple
* pybind-registered classes. Will be empty if neither the type nor any base classes are
* pybind-registered.
*
* The value is cached for the lifetime of the Python type.
*/
inline
const
std
::
vector
<
detail
::
type_info
*>
&
all_type_info
(
PyTypeObject
*
type
)
{
auto
ins
=
all_type_info_get_cache
(
type
);
if
(
ins
.
second
)
// New cache entry: populate it
all_type_info_populate
(
type
,
ins
.
first
->
second
);
return
ins
.
first
->
second
;
}
/**
* Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any
* ancestors are pybind11-registered. Throws an exception if there are multiple bases--use
* `all_type_info` instead if you want to support multiple bases.
*/
PYBIND11_NOINLINE
inline
detail
::
type_info
*
get_type_info
(
PyTypeObject
*
type
)
{
auto
&
bases
=
all_type_info
(
type
);
if
(
bases
.
size
()
==
0
)
return
nullptr
;
if
(
bases
.
size
()
>
1
)
pybind11_fail
(
"pybind11::detail::get_type_info: type has multiple pybind11-registered bases"
);
return
bases
.
front
();
}
PYBIND11_NOINLINE
inline
detail
::
type_info
*
get_type_info
(
const
std
::
type_info
&
tp
,
bool
throw_if_missing
=
false
)
{
auto
&
types
=
get_internals
().
registered_types_cpp
;
auto
it
=
types
.
find
(
std
::
type_index
(
tp
));
if
(
it
!=
types
.
end
())
return
(
detail
::
type_info
*
)
it
->
second
;
if
(
throw_if_missing
)
{
std
::
string
tname
=
tp
.
name
();
detail
::
clean_type_id
(
tname
);
pybind11_fail
(
"pybind11::detail::get_type_info: unable to find type info for
\"
"
+
tname
+
"
\"
"
);
}
return
nullptr
;
}
PYBIND11_NOINLINE
inline
handle
get_type_handle
(
const
std
::
type_info
&
tp
,
bool
throw_if_missing
)
{
detail
::
type_info
*
type_info
=
get_type_info
(
tp
,
throw_if_missing
);
return
handle
(
type_info
?
((
PyObject
*
)
type_info
->
type
)
:
nullptr
);
}
struct
value_and_holder
{
instance
*
inst
;
size_t
index
;
const
detail
::
type_info
*
type
;
void
**
vh
;
value_and_holder
(
instance
*
i
,
const
detail
::
type_info
*
type
,
size_t
vpos
,
size_t
index
)
:
inst
{
i
},
index
{
index
},
type
{
type
},
vh
{
inst
->
simple_layout
?
inst
->
simple_value_holder
:
&
inst
->
nonsimple
.
values_and_holders
[
vpos
]}
{}
// Used for past-the-end iterator
value_and_holder
(
size_t
index
)
:
index
{
index
}
{}
template
<
typename
V
=
void
>
V
*&
value_ptr
()
const
{
return
reinterpret_cast
<
V
*&>
(
vh
[
0
]);
}
// True if this `value_and_holder` has a non-null value pointer
explicit
operator
bool
()
const
{
return
value_ptr
();
}
template
<
typename
H
>
H
&
holder
()
const
{
return
reinterpret_cast
<
H
&>
(
vh
[
1
]);
}
bool
holder_constructed
()
const
{
return
inst
->
simple_layout
?
inst
->
simple_holder_constructed
:
inst
->
nonsimple
.
status
[
index
]
&
instance
::
status_holder_constructed
;
}
void
set_holder_constructed
()
{
if
(
inst
->
simple_layout
)
inst
->
simple_holder_constructed
=
true
;
else
inst
->
nonsimple
.
status
[
index
]
|=
instance
::
status_holder_constructed
;
}
bool
instance_registered
()
const
{
return
inst
->
simple_layout
?
inst
->
simple_instance_registered
:
inst
->
nonsimple
.
status
[
index
]
&
instance
::
status_instance_registered
;
}
void
set_instance_registered
()
{
if
(
inst
->
simple_layout
)
inst
->
simple_instance_registered
=
true
;
else
inst
->
nonsimple
.
status
[
index
]
|=
instance
::
status_instance_registered
;
}
};
// Container for accessing and iterating over an instance's values/holders
struct
values_and_holders
{
private:
instance
*
inst
;
using
type_vec
=
std
::
vector
<
detail
::
type_info
*>
;
const
type_vec
&
tinfo
;
public:
values_and_holders
(
instance
*
inst
)
:
inst
{
inst
},
tinfo
(
all_type_info
(
Py_TYPE
(
inst
)))
{}
struct
iterator
{
private:
instance
*
inst
;
const
type_vec
*
types
;
value_and_holder
curr
;
friend
struct
values_and_holders
;
iterator
(
instance
*
inst
,
const
type_vec
*
tinfo
)
:
inst
{
inst
},
types
{
tinfo
},
curr
(
inst
/* instance */
,
types
->
empty
()
?
nullptr
:
(
*
types
)[
0
]
/* type info */
,
0
,
/* vpos: (non-simple types only): the first vptr comes first */
0
/* index */
)
{}
// Past-the-end iterator:
iterator
(
size_t
end
)
:
curr
(
end
)
{}
public:
bool
operator
==
(
const
iterator
&
other
)
{
return
curr
.
index
==
other
.
curr
.
index
;
}
bool
operator
!=
(
const
iterator
&
other
)
{
return
curr
.
index
!=
other
.
curr
.
index
;
}
iterator
&
operator
++
()
{
if
(
!
inst
->
simple_layout
)
curr
.
vh
+=
1
+
(
*
types
)[
curr
.
index
]
->
holder_size_in_ptrs
;
++
curr
.
index
;
curr
.
type
=
curr
.
index
<
types
->
size
()
?
(
*
types
)[
curr
.
index
]
:
nullptr
;
return
*
this
;
}
value_and_holder
&
operator
*
()
{
return
curr
;
}
value_and_holder
*
operator
->
()
{
return
&
curr
;
}
};
iterator
begin
()
{
return
iterator
(
inst
,
&
tinfo
);
}
iterator
end
()
{
return
iterator
(
tinfo
.
size
());
}
iterator
find
(
const
type_info
*
find_type
)
{
auto
it
=
begin
(),
endit
=
end
();
while
(
it
!=
endit
&&
it
->
type
!=
find_type
)
++
it
;
return
it
;
}
size_t
size
()
{
return
tinfo
.
size
();
}
};
/**
* Extracts C++ value and holder pointer references from an instance (which may contain multiple
* values/holders for python-side multiple inheritance) that match the given type. Throws an error
* if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If
* `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned,
* regardless of type (and the resulting .type will be nullptr).
*
* The returned object should be short-lived: in particular, it must not outlive the called-upon
* instance.
*/
PYBIND11_NOINLINE
inline
value_and_holder
instance
::
get_value_and_holder
(
const
type_info
*
find_type
/*= nullptr default in common.h*/
)
{
// Optimize common case:
if
(
!
find_type
||
Py_TYPE
(
this
)
==
find_type
->
type
)
return
value_and_holder
(
this
,
find_type
,
0
,
0
);
detail
::
values_and_holders
vhs
(
this
);
auto
it
=
vhs
.
find
(
find_type
);
if
(
it
!=
vhs
.
end
())
return
*
it
;
#if defined(NDEBUG)
pybind11_fail
(
"pybind11::detail::instance::get_value_and_holder: "
"type is not a pybind11 base of the given instance "
"(compile in debug mode for type details)"
);
#else
pybind11_fail
(
"pybind11::detail::instance::get_value_and_holder: `"
+
std
::
string
(
find_type
->
type
->
tp_name
)
+
"' is not a pybind11 base of the given `"
+
std
::
string
(
Py_TYPE
(
this
)
->
tp_name
)
+
"' instance"
);
#endif
}
PYBIND11_NOINLINE
inline
void
instance
::
allocate_layout
()
{
auto
&
tinfo
=
all_type_info
(
Py_TYPE
(
this
));
const
size_t
n_types
=
tinfo
.
size
();
if
(
n_types
==
0
)
pybind11_fail
(
"instance allocation failed: new instance has no pybind11-registered base types"
);
simple_layout
=
n_types
==
1
&&
tinfo
.
front
()
->
holder_size_in_ptrs
<=
instance_simple_holder_in_ptrs
();
// Simple path: no python-side multiple inheritance, and a small-enough holder
if
(
simple_layout
)
{
simple_value_holder
[
0
]
=
nullptr
;
simple_holder_constructed
=
false
;
simple_instance_registered
=
false
;
}
else
{
// multiple base types or a too-large holder
// Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer,
// [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool
// values that tracks whether each associated holder has been initialized. Each [block] is
// padded, if necessary, to an integer multiple of sizeof(void *).
size_t
space
=
0
;
for
(
auto
t
:
tinfo
)
{
space
+=
1
;
// value pointer
space
+=
t
->
holder_size_in_ptrs
;
// holder instance
}
size_t
flags_at
=
space
;
space
+=
size_in_ptrs
(
n_types
);
// status bytes (holder_constructed and instance_registered)
// Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
// in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6
// they default to using pymalloc, which is designed to be efficient for small allocations
// like the one we're doing here; in earlier versions (and for larger allocations) they are
// just wrappers around malloc.
#if PY_VERSION_HEX >= 0x03050000
nonsimple
.
values_and_holders
=
(
void
**
)
PyMem_Calloc
(
space
,
sizeof
(
void
*
));
if
(
!
nonsimple
.
values_and_holders
)
throw
std
::
bad_alloc
();
#else
nonsimple
.
values_and_holders
=
(
void
**
)
PyMem_New
(
void
*
,
space
);
if
(
!
nonsimple
.
values_and_holders
)
throw
std
::
bad_alloc
();
std
::
memset
(
nonsimple
.
values_and_holders
,
0
,
space
*
sizeof
(
void
*
));
#endif
nonsimple
.
status
=
reinterpret_cast
<
uint8_t
*>
(
&
nonsimple
.
values_and_holders
[
flags_at
]);
}
owned
=
true
;
}
PYBIND11_NOINLINE
inline
void
instance
::
deallocate_layout
()
{
if
(
!
simple_layout
)
PyMem_Free
(
nonsimple
.
values_and_holders
);
}
PYBIND11_NOINLINE
inline
bool
isinstance_generic
(
handle
obj
,
const
std
::
type_info
&
tp
)
{
handle
type
=
detail
::
get_type_handle
(
tp
,
false
);
if
(
!
type
)
return
false
;
return
isinstance
(
obj
,
type
);
}
PYBIND11_NOINLINE
inline
std
::
string
error_string
()
{
if
(
!
PyErr_Occurred
())
{
PyErr_SetString
(
PyExc_RuntimeError
,
"Unknown internal error occurred"
);
return
"Unknown internal error occurred"
;
}
error_scope
scope
;
// Preserve error state
std
::
string
errorString
;
if
(
scope
.
type
)
{
errorString
+=
handle
(
scope
.
type
).
attr
(
"__name__"
).
cast
<
std
::
string
>
();
errorString
+=
": "
;
}
if
(
scope
.
value
)
errorString
+=
(
std
::
string
)
str
(
scope
.
value
);
PyErr_NormalizeException
(
&
scope
.
type
,
&
scope
.
value
,
&
scope
.
trace
);
#if PY_MAJOR_VERSION >= 3
if
(
scope
.
trace
!=
nullptr
)
PyException_SetTraceback
(
scope
.
value
,
scope
.
trace
);
#endif
#if !defined(PYPY_VERSION)
if
(
scope
.
trace
)
{
PyTracebackObject
*
trace
=
(
PyTracebackObject
*
)
scope
.
trace
;
/* Get the deepest trace possible */
while
(
trace
->
tb_next
)
trace
=
trace
->
tb_next
;
PyFrameObject
*
frame
=
trace
->
tb_frame
;
errorString
+=
"
\n\n
At:
\n
"
;
while
(
frame
)
{
int
lineno
=
PyFrame_GetLineNumber
(
frame
);
errorString
+=
" "
+
handle
(
frame
->
f_code
->
co_filename
).
cast
<
std
::
string
>
()
+
"("
+
std
::
to_string
(
lineno
)
+
"): "
+
handle
(
frame
->
f_code
->
co_name
).
cast
<
std
::
string
>
()
+
"
\n
"
;
frame
=
frame
->
f_back
;
}
trace
=
trace
->
tb_next
;
}
#endif
return
errorString
;
}
PYBIND11_NOINLINE
inline
handle
get_object_handle
(
const
void
*
ptr
,
const
detail
::
type_info
*
type
)
{
auto
&
instances
=
get_internals
().
registered_instances
;
auto
range
=
instances
.
equal_range
(
ptr
);
for
(
auto
it
=
range
.
first
;
it
!=
range
.
second
;
++
it
)
{
for
(
auto
vh
:
values_and_holders
(
it
->
second
))
{
if
(
vh
.
type
==
type
)
return
handle
((
PyObject
*
)
it
->
second
);
}
}
return
handle
();
}
inline
PyThreadState
*
get_thread_state_unchecked
()
{
#if defined(PYPY_VERSION)
return
PyThreadState_GET
();
#elif PY_VERSION_HEX < 0x03000000
return
_PyThreadState_Current
;
#elif PY_VERSION_HEX < 0x03050000
return
(
PyThreadState
*
)
_Py_atomic_load_relaxed
(
&
_PyThreadState_Current
);
#elif PY_VERSION_HEX < 0x03050200
return
(
PyThreadState
*
)
_PyThreadState_Current
.
value
;
#else
return
_PyThreadState_UncheckedGet
();
#endif
}
// Forward declarations
inline
void
keep_alive_impl
(
handle
nurse
,
handle
patient
);
inline
PyObject
*
make_new_instance
(
PyTypeObject
*
type
,
bool
allocate_value
=
true
);
class
type_caster_generic
{
public:
PYBIND11_NOINLINE
type_caster_generic
(
const
std
::
type_info
&
type_info
)
:
typeinfo
(
get_type_info
(
type_info
))
{
}
bool
load
(
handle
src
,
bool
convert
)
{
return
load_impl
<
type_caster_generic
>
(
src
,
convert
);
}
PYBIND11_NOINLINE
static
handle
cast
(
const
void
*
_src
,
return_value_policy
policy
,
handle
parent
,
const
detail
::
type_info
*
tinfo
,
void
*
(
*
copy_constructor
)(
const
void
*
),
void
*
(
*
move_constructor
)(
const
void
*
),
const
void
*
existing_holder
=
nullptr
)
{
if
(
!
tinfo
)
// no type info: error will be set already
return
handle
();
void
*
src
=
const_cast
<
void
*>
(
_src
);
if
(
src
==
nullptr
)
return
none
().
release
();
auto
it_instances
=
get_internals
().
registered_instances
.
equal_range
(
src
);
for
(
auto
it_i
=
it_instances
.
first
;
it_i
!=
it_instances
.
second
;
++
it_i
)
{
for
(
auto
instance_type
:
detail
::
all_type_info
(
Py_TYPE
(
it_i
->
second
)))
{
if
(
instance_type
&&
instance_type
==
tinfo
)
return
handle
((
PyObject
*
)
it_i
->
second
).
inc_ref
();
}
}
auto
inst
=
reinterpret_steal
<
object
>
(
make_new_instance
(
tinfo
->
type
,
false
/* don't allocate value */
));
auto
wrapper
=
reinterpret_cast
<
instance
*>
(
inst
.
ptr
());
wrapper
->
owned
=
false
;
void
*&
valueptr
=
values_and_holders
(
wrapper
).
begin
()
->
value_ptr
();
switch
(
policy
)
{
case
return_value_policy
::
automatic
:
case
return_value_policy
::
take_ownership
:
valueptr
=
src
;
wrapper
->
owned
=
true
;
break
;
case
return_value_policy
::
automatic_reference
:
case
return_value_policy
::
reference
:
valueptr
=
src
;
wrapper
->
owned
=
false
;
break
;
case
return_value_policy
::
copy
:
if
(
copy_constructor
)
valueptr
=
copy_constructor
(
src
);
else
throw
cast_error
(
"return_value_policy = copy, but the "
"object is non-copyable!"
);
wrapper
->
owned
=
true
;
break
;
case
return_value_policy
::
move
:
if
(
move_constructor
)
valueptr
=
move_constructor
(
src
);
else
if
(
copy_constructor
)
valueptr
=
copy_constructor
(
src
);
else
throw
cast_error
(
"return_value_policy = move, but the "
"object is neither movable nor copyable!"
);
wrapper
->
owned
=
true
;
break
;
case
return_value_policy
::
reference_internal
:
valueptr
=
src
;
wrapper
->
owned
=
false
;
keep_alive_impl
(
inst
,
parent
);
break
;
default:
throw
cast_error
(
"unhandled return_value_policy: should not happen!"
);
}
tinfo
->
init_instance
(
wrapper
,
existing_holder
);
return
inst
.
release
();
}
protected:
// Base methods for generic caster; there are overridden in copyable_holder_caster
void
load_value
(
const
value_and_holder
&
v_h
)
{
value
=
v_h
.
value_ptr
();
}
bool
try_implicit_casts
(
handle
src
,
bool
convert
)
{
for
(
auto
&
cast
:
typeinfo
->
implicit_casts
)
{
type_caster_generic
sub_caster
(
*
cast
.
first
);
if
(
sub_caster
.
load
(
src
,
convert
))
{
value
=
cast
.
second
(
sub_caster
.
value
);
return
true
;
}
}
return
false
;
}
bool
try_direct_conversions
(
handle
src
)
{
for
(
auto
&
converter
:
*
typeinfo
->
direct_conversions
)
{
if
(
converter
(
src
.
ptr
(),
value
))
return
true
;
}
return
false
;
}
void
check_holder_compat
()
{}
// Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant
// bits of code between here and copyable_holder_caster where the two classes need different
// logic (without having to resort to virtual inheritance).
template
<
typename
ThisT
>
PYBIND11_NOINLINE
bool
load_impl
(
handle
src
,
bool
convert
)
{
if
(
!
src
||
!
typeinfo
)
return
false
;
if
(
src
.
is_none
())
{
// Defer accepting None to other overloads (if we aren't in convert mode):
if
(
!
convert
)
return
false
;
value
=
nullptr
;
return
true
;
}
auto
&
this_
=
static_cast
<
ThisT
&>
(
*
this
);
this_
.
check_holder_compat
();
PyTypeObject
*
srctype
=
Py_TYPE
(
src
.
ptr
());
// Case 1: If src is an exact type match for the target type then we can reinterpret_cast
// the instance's value pointer to the target type:
if
(
srctype
==
typeinfo
->
type
)
{
this_
.
load_value
(
reinterpret_cast
<
instance
*>
(
src
.
ptr
())
->
get_value_and_holder
());
return
true
;
}
// Case 2: We have a derived class
else
if
(
PyType_IsSubtype
(
srctype
,
typeinfo
->
type
))
{
auto
&
bases
=
all_type_info
(
srctype
);
bool
no_cpp_mi
=
typeinfo
->
simple_type
;
// Case 2a: the python type is a Python-inherited derived class that inherits from just
// one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of
// the right type and we can use reinterpret_cast.
// (This is essentially the same as case 2b, but because not using multiple inheritance
// is extremely common, we handle it specially to avoid the loop iterator and type
// pointer lookup overhead)
if
(
bases
.
size
()
==
1
&&
(
no_cpp_mi
||
bases
.
front
()
->
type
==
typeinfo
->
type
))
{
this_
.
load_value
(
reinterpret_cast
<
instance
*>
(
src
.
ptr
())
->
get_value_and_holder
());
return
true
;
}
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
// can safely reinterpret_cast to the relevant pointer.
else
if
(
bases
.
size
()
>
1
)
{
for
(
auto
base
:
bases
)
{
if
(
no_cpp_mi
?
PyType_IsSubtype
(
base
->
type
,
typeinfo
->
type
)
:
base
->
type
==
typeinfo
->
type
)
{
this_
.
load_value
(
reinterpret_cast
<
instance
*>
(
src
.
ptr
())
->
get_value_and_holder
(
base
));
return
true
;
}
}
}
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
// in the registered bases, above, so try implicit casting (needed for proper C++ casting
// when MI is involved).
if
(
this_
.
try_implicit_casts
(
src
,
convert
))
return
true
;
}
// Perform an implicit conversion
if
(
convert
)
{
for
(
auto
&
converter
:
typeinfo
->
implicit_conversions
)
{
auto
temp
=
reinterpret_steal
<
object
>
(
converter
(
src
.
ptr
(),
typeinfo
->
type
));
if
(
load_impl
<
ThisT
>
(
temp
,
false
))
{
loader_life_support
::
add_patient
(
temp
);
return
true
;
}
}
if
(
this_
.
try_direct_conversions
(
src
))
return
true
;
}
return
false
;
}
// Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
// isn't needed or can't be used. If the type is unknown, sets the error and returns a pair
// with .second = nullptr. (p.first = nullptr is not an error: it becomes None).
PYBIND11_NOINLINE
static
std
::
pair
<
const
void
*
,
const
type_info
*>
src_and_type
(
const
void
*
src
,
const
std
::
type_info
&
cast_type
,
const
std
::
type_info
*
rtti_type
=
nullptr
)
{
auto
&
internals
=
get_internals
();
auto
it
=
internals
.
registered_types_cpp
.
find
(
std
::
type_index
(
cast_type
));
if
(
it
!=
internals
.
registered_types_cpp
.
end
())
return
{
src
,
(
const
type_info
*
)
it
->
second
};
// Not found, set error:
std
::
string
tname
=
rtti_type
?
rtti_type
->
name
()
:
cast_type
.
name
();
detail
::
clean_type_id
(
tname
);
std
::
string
msg
=
"Unregistered type : "
+
tname
;
PyErr_SetString
(
PyExc_TypeError
,
msg
.
c_str
());
return
{
nullptr
,
nullptr
};
}
const
type_info
*
typeinfo
=
nullptr
;
void
*
value
=
nullptr
;
};
/**
* Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster
* needs to provide `operator T*()` and `operator T&()` operators.
*
* If the type supports moving the value away via an `operator T&&() &&` method, it should use
* `movable_cast_op_type` instead.
*/
template
<
typename
T
>
using
cast_op_type
=
conditional_t
<
std
::
is_pointer
<
remove_reference_t
<
T
>>::
value
,
typename
std
::
add_pointer
<
intrinsic_t
<
T
>>::
type
,
typename
std
::
add_lvalue_reference
<
intrinsic_t
<
T
>>::
type
>
;
/**
* Determine suitable casting operator for a type caster with a movable value. Such a type caster
* needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be
* called in appropriate contexts where the value can be moved rather than copied.
*
* These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro.
*/
template
<
typename
T
>
using
movable_cast_op_type
=
conditional_t
<
std
::
is_pointer
<
typename
std
::
remove_reference
<
T
>::
type
>::
value
,
typename
std
::
add_pointer
<
intrinsic_t
<
T
>>::
type
,
conditional_t
<
std
::
is_rvalue_reference
<
T
>::
value
,
typename
std
::
add_rvalue_reference
<
intrinsic_t
<
T
>>::
type
,
typename
std
::
add_lvalue_reference
<
intrinsic_t
<
T
>>::
type
>>
;
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
is_copy_constructible
:
std
::
is_copy_constructible
<
T
>
{};
// Specialization for types that appear to be copy constructible but also look like stl containers
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
// so, copy constructability depends on whether the value_type is copy constructible.
template
<
typename
Container
>
struct
is_copy_constructible
<
Container
,
enable_if_t
<
all_of
<
std
::
is_copy_constructible
<
Container
>
,
std
::
is_same
<
typename
Container
::
value_type
&
,
typename
Container
::
reference
>
>::
value
>>
:
is_copy_constructible
<
typename
Container
::
value_type
>
{};
#if !defined(PYBIND11_CPP17)
// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the
// two types aren't themselves copy constructible).
template
<
typename
T1
,
typename
T2
>
struct
is_copy_constructible
<
std
::
pair
<
T1
,
T2
>>
:
all_of
<
is_copy_constructible
<
T1
>
,
is_copy_constructible
<
T2
>>
{};
#endif
/// Generic type caster for objects stored on the heap
template
<
typename
type
>
class
type_caster_base
:
public
type_caster_generic
{
using
itype
=
intrinsic_t
<
type
>
;
public:
static
PYBIND11_DESCR
name
()
{
return
type_descr
(
_
<
type
>
());
}
type_caster_base
()
:
type_caster_base
(
typeid
(
type
))
{
}
explicit
type_caster_base
(
const
std
::
type_info
&
info
)
:
type_caster_generic
(
info
)
{
}
static
handle
cast
(
const
itype
&
src
,
return_value_policy
policy
,
handle
parent
)
{
if
(
policy
==
return_value_policy
::
automatic
||
policy
==
return_value_policy
::
automatic_reference
)
policy
=
return_value_policy
::
copy
;
return
cast
(
&
src
,
policy
,
parent
);
}
static
handle
cast
(
itype
&&
src
,
return_value_policy
,
handle
parent
)
{
return
cast
(
&
src
,
return_value_policy
::
move
,
parent
);
}
// Returns a (pointer, type_info) pair taking care of necessary RTTI type lookup for a
// polymorphic type. If the instance isn't derived, returns the non-RTTI base version.
template
<
typename
T
=
itype
,
enable_if_t
<
std
::
is_polymorphic
<
T
>
::
value
,
int
>
=
0
>
static
std
::
pair
<
const
void
*
,
const
type_info
*>
src_and_type
(
const
itype
*
src
)
{
const
void
*
vsrc
=
src
;
auto
&
internals
=
get_internals
();
auto
&
cast_type
=
typeid
(
itype
);
const
std
::
type_info
*
instance_type
=
nullptr
;
if
(
vsrc
)
{
instance_type
=
&
typeid
(
*
src
);
if
(
!
same_type
(
cast_type
,
*
instance_type
))
{
// This is a base pointer to a derived type; if it is a pybind11-registered type, we
// can get the correct derived pointer (which may be != base pointer) by a
// dynamic_cast to most derived type:
auto
it
=
internals
.
registered_types_cpp
.
find
(
std
::
type_index
(
*
instance_type
));
if
(
it
!=
internals
.
registered_types_cpp
.
end
())
return
{
dynamic_cast
<
const
void
*>
(
src
),
(
const
type_info
*
)
it
->
second
};
}
}
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so
// don't do a cast
return
type_caster_generic
::
src_and_type
(
vsrc
,
cast_type
,
instance_type
);
}
// Non-polymorphic type, so no dynamic casting; just call the generic version directly
template
<
typename
T
=
itype
,
enable_if_t
<!
std
::
is_polymorphic
<
T
>
::
value
,
int
>
=
0
>
static
std
::
pair
<
const
void
*
,
const
type_info
*>
src_and_type
(
const
itype
*
src
)
{
return
type_caster_generic
::
src_and_type
(
src
,
typeid
(
itype
));
}
static
handle
cast
(
const
itype
*
src
,
return_value_policy
policy
,
handle
parent
)
{
auto
st
=
src_and_type
(
src
);
return
type_caster_generic
::
cast
(
st
.
first
,
policy
,
parent
,
st
.
second
,
make_copy_constructor
(
src
),
make_move_constructor
(
src
));
}
static
handle
cast_holder
(
const
itype
*
src
,
const
void
*
holder
)
{
auto
st
=
src_and_type
(
src
);
return
type_caster_generic
::
cast
(
st
.
first
,
return_value_policy
::
take_ownership
,
{},
st
.
second
,
nullptr
,
nullptr
,
holder
);
}
template
<
typename
T
>
using
cast_op_type
=
cast_op_type
<
T
>
;
operator
itype
*
()
{
return
(
type
*
)
value
;
}
operator
itype
&
()
{
if
(
!
value
)
throw
reference_cast_error
();
return
*
((
itype
*
)
value
);
}
protected:
using
Constructor
=
void
*
(
*
)(
const
void
*
);
/* Only enabled when the types are {copy,move}-constructible *and* when the type
does not have a private operator new implementation. */
template
<
typename
T
,
typename
=
enable_if_t
<
is_copy_constructible
<
T
>
::
value
>>
static
auto
make_copy_constructor
(
const
T
*
x
)
->
decltype
(
new
T
(
*
x
),
Constructor
{})
{
return
[](
const
void
*
arg
)
->
void
*
{
return
new
T
(
*
reinterpret_cast
<
const
T
*>
(
arg
));
};
}
template
<
typename
T
,
typename
=
enable_if_t
<
std
::
is_move_constructible
<
T
>
::
value
>>
static
auto
make_move_constructor
(
const
T
*
x
)
->
decltype
(
new
T
(
std
::
move
(
*
const_cast
<
T
*>
(
x
))),
Constructor
{})
{
return
[](
const
void
*
arg
)
->
void
*
{
return
new
T
(
std
::
move
(
*
const_cast
<
T
*>
(
reinterpret_cast
<
const
T
*>
(
arg
))));
};
}
static
Constructor
make_copy_constructor
(...)
{
return
nullptr
;
}
static
Constructor
make_move_constructor
(...)
{
return
nullptr
;
}
};
template
<
typename
type
,
typename
SFINAE
=
void
>
class
type_caster
:
public
type_caster_base
<
type
>
{
};
template
<
typename
type
>
using
make_caster
=
type_caster
<
intrinsic_t
<
type
>>
;
// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
template
<
typename
T
>
typename
make_caster
<
T
>::
template
cast_op_type
<
T
>
cast_op
(
make_caster
<
T
>
&
caster
)
{
return
caster
.
operator
typename
make_caster
<
T
>::
template
cast_op_type
<
T
>();
}
template
<
typename
T
>
typename
make_caster
<
T
>::
template
cast_op_type
<
typename
std
::
add_rvalue_reference
<
T
>
::
type
>
cast_op
(
make_caster
<
T
>
&&
caster
)
{
return
std
::
move
(
caster
).
operator
typename
make_caster
<
T
>::
template
cast_op_type
<
typename
std
::
add_rvalue_reference
<
T
>
::
type
>
();
}
template
<
typename
type
>
class
type_caster
<
std
::
reference_wrapper
<
type
>>
{
private:
using
caster_t
=
make_caster
<
type
>
;
caster_t
subcaster
;
using
subcaster_cast_op_type
=
typename
caster_t
::
template
cast_op_type
<
type
>;
static_assert
(
std
::
is_same
<
typename
std
::
remove_const
<
type
>::
type
&
,
subcaster_cast_op_type
>::
value
,
"std::reference_wrapper<T> caster requires T to have a caster with an `T &` operator"
);
public:
bool
load
(
handle
src
,
bool
convert
)
{
return
subcaster
.
load
(
src
,
convert
);
}
static
PYBIND11_DESCR
name
()
{
return
caster_t
::
name
();
}
static
handle
cast
(
const
std
::
reference_wrapper
<
type
>
&
src
,
return_value_policy
policy
,
handle
parent
)
{
// It is definitely wrong to take ownership of this pointer, so mask that rvp
if
(
policy
==
return_value_policy
::
take_ownership
||
policy
==
return_value_policy
::
automatic
)
policy
=
return_value_policy
::
automatic_reference
;
return
caster_t
::
cast
(
&
src
.
get
(),
policy
,
parent
);
}
template
<
typename
T
>
using
cast_op_type
=
std
::
reference_wrapper
<
type
>
;
operator
std
::
reference_wrapper
<
type
>
()
{
return
subcaster
.
operator
subcaster_cast_op_type
&
();
}
};
#define PYBIND11_TYPE_CASTER(type, py_name) \
protected: \
type value; \
public: \
static PYBIND11_DESCR name() { return type_descr(py_name); } \
template <typename T_, enable_if_t<std::is_same<type, remove_cv_t<T_>>::value, int> = 0> \
static handle cast(T_ *src, return_value_policy policy, handle parent) { \
if (!src) return none().release(); \
if (policy == return_value_policy::take_ownership) { \
auto h = cast(std::move(*src), policy, parent); delete src; return h; \
} else { \
return cast(*src, policy, parent); \
} \
} \
operator type*() { return &value; } \
operator type&() { return value; } \
operator type&&() && { return std::move(value); } \
template <typename T_> using cast_op_type = pybind11::detail::movable_cast_op_type<T_>
template
<
typename
CharT
>
using
is_std_char_type
=
any_of
<
std
::
is_same
<
CharT
,
char
>
,
/* std::string */
std
::
is_same
<
CharT
,
char16_t
>
,
/* std::u16string */
std
::
is_same
<
CharT
,
char32_t
>
,
/* std::u32string */
std
::
is_same
<
CharT
,
wchar_t
>
/* std::wstring */
>
;
template
<
typename
T
>
struct
type_caster
<
T
,
enable_if_t
<
std
::
is_arithmetic
<
T
>::
value
&&
!
is_std_char_type
<
T
>::
value
>>
{
using
_py_type_0
=
conditional_t
<
sizeof
(
T
)
<=
sizeof
(
long
),
long
,
long
long
>
;
using
_py_type_1
=
conditional_t
<
std
::
is_signed
<
T
>::
value
,
_py_type_0
,
typename
std
::
make_unsigned
<
_py_type_0
>::
type
>
;
using
py_type
=
conditional_t
<
std
::
is_floating_point
<
T
>::
value
,
double
,
_py_type_1
>
;
public:
bool
load
(
handle
src
,
bool
convert
)
{
py_type
py_value
;
if
(
!
src
)
return
false
;
if
(
std
::
is_floating_point
<
T
>::
value
)
{
if
(
convert
||
PyFloat_Check
(
src
.
ptr
()))
py_value
=
(
py_type
)
PyFloat_AsDouble
(
src
.
ptr
());
else
return
false
;
}
else
if
(
PyFloat_Check
(
src
.
ptr
()))
{
return
false
;
}
else
if
(
std
::
is_unsigned
<
py_type
>::
value
)
{
py_value
=
as_unsigned
<
py_type
>
(
src
.
ptr
());
}
else
{
// signed integer:
py_value
=
sizeof
(
T
)
<=
sizeof
(
long
)
?
(
py_type
)
PyLong_AsLong
(
src
.
ptr
())
:
(
py_type
)
PYBIND11_LONG_AS_LONGLONG
(
src
.
ptr
());
}
bool
py_err
=
py_value
==
(
py_type
)
-
1
&&
PyErr_Occurred
();
if
(
py_err
||
(
std
::
is_integral
<
T
>::
value
&&
sizeof
(
py_type
)
!=
sizeof
(
T
)
&&
(
py_value
<
(
py_type
)
std
::
numeric_limits
<
T
>::
min
()
||
py_value
>
(
py_type
)
std
::
numeric_limits
<
T
>::
max
())))
{
bool
type_error
=
py_err
&&
PyErr_ExceptionMatches
(
#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION)
PyExc_SystemError
#else
PyExc_TypeError
#endif
);
PyErr_Clear
();
if
(
type_error
&&
convert
&&
PyNumber_Check
(
src
.
ptr
()))
{
auto
tmp
=
reinterpret_borrow
<
object
>
(
std
::
is_floating_point
<
T
>::
value
?
PyNumber_Float
(
src
.
ptr
())
:
PyNumber_Long
(
src
.
ptr
()));
PyErr_Clear
();
return
load
(
tmp
,
false
);
}
return
false
;
}
value
=
(
T
)
py_value
;
return
true
;
}
static
handle
cast
(
T
src
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
if
(
std
::
is_floating_point
<
T
>::
value
)
{
return
PyFloat_FromDouble
((
double
)
src
);
}
else
if
(
sizeof
(
T
)
<=
sizeof
(
long
))
{
if
(
std
::
is_signed
<
T
>::
value
)
return
PyLong_FromLong
((
long
)
src
);
else
return
PyLong_FromUnsignedLong
((
unsigned
long
)
src
);
}
else
{
if
(
std
::
is_signed
<
T
>::
value
)
return
PyLong_FromLongLong
((
long
long
)
src
);
else
return
PyLong_FromUnsignedLongLong
((
unsigned
long
long
)
src
);
}
}
PYBIND11_TYPE_CASTER
(
T
,
_
<
std
::
is_integral
<
T
>::
value
>
(
"int"
,
"float"
));
};
template
<
typename
T
>
struct
void_caster
{
public:
bool
load
(
handle
src
,
bool
)
{
if
(
src
&&
src
.
is_none
())
return
true
;
return
false
;
}
static
handle
cast
(
T
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
return
none
().
inc_ref
();
}
PYBIND11_TYPE_CASTER
(
T
,
_
(
"None"
));
};
template
<
>
class
type_caster
<
void_type
>
:
public
void_caster
<
void_type
>
{};
template
<
>
class
type_caster
<
void
>
:
public
type_caster
<
void_type
>
{
public:
using
type_caster
<
void_type
>::
cast
;
bool
load
(
handle
h
,
bool
)
{
if
(
!
h
)
{
return
false
;
}
else
if
(
h
.
is_none
())
{
value
=
nullptr
;
return
true
;
}
/* Check if this is a capsule */
if
(
isinstance
<
capsule
>
(
h
))
{
value
=
reinterpret_borrow
<
capsule
>
(
h
);
return
true
;
}
/* Check if this is a C++ type */
auto
&
bases
=
all_type_info
((
PyTypeObject
*
)
h
.
get_type
().
ptr
());
if
(
bases
.
size
()
==
1
)
{
// Only allowing loading from a single-value type
value
=
values_and_holders
(
reinterpret_cast
<
instance
*>
(
h
.
ptr
())).
begin
()
->
value_ptr
();
return
true
;
}
/* Fail */
return
false
;
}
static
handle
cast
(
const
void
*
ptr
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
if
(
ptr
)
return
capsule
(
ptr
).
release
();
else
return
none
().
inc_ref
();
}
template
<
typename
T
>
using
cast_op_type
=
void
*&
;
operator
void
*&
()
{
return
value
;
}
static
PYBIND11_DESCR
name
()
{
return
type_descr
(
_
(
"capsule"
));
}
private:
void
*
value
=
nullptr
;
};
template
<
>
class
type_caster
<
std
::
nullptr_t
>
:
public
void_caster
<
std
::
nullptr_t
>
{
};
template
<
>
class
type_caster
<
bool
>
{
public:
bool
load
(
handle
src
,
bool
convert
)
{
if
(
!
src
)
return
false
;
else
if
(
src
.
ptr
()
==
Py_True
)
{
value
=
true
;
return
true
;
}
else
if
(
src
.
ptr
()
==
Py_False
)
{
value
=
false
;
return
true
;
}
else
if
(
convert
||
!
strcmp
(
"numpy.bool_"
,
Py_TYPE
(
src
.
ptr
())
->
tp_name
))
{
// (allow non-implicit conversion for numpy booleans)
Py_ssize_t
res
=
-
1
;
if
(
src
.
is_none
())
{
res
=
0
;
// None is implicitly converted to False
}
#if defined(PYPY_VERSION)
// On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists
else
if
(
hasattr
(
src
,
PYBIND11_BOOL_ATTR
))
{
res
=
PyObject_IsTrue
(
src
.
ptr
());
}
#else
// Alternate approach for CPython: this does the same as the above, but optimized
// using the CPython API so as to avoid an unneeded attribute lookup.
else
if
(
auto
tp_as_number
=
src
.
ptr
()
->
ob_type
->
tp_as_number
)
{
if
(
PYBIND11_NB_BOOL
(
tp_as_number
))
{
res
=
(
*
PYBIND11_NB_BOOL
(
tp_as_number
))(
src
.
ptr
());
}
}
#endif
if
(
res
==
0
||
res
==
1
)
{
value
=
(
bool
)
res
;
return
true
;
}
}
return
false
;
}
static
handle
cast
(
bool
src
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
return
handle
(
src
?
Py_True
:
Py_False
).
inc_ref
();
}
PYBIND11_TYPE_CASTER
(
bool
,
_
(
"bool"
));
};
// Helper class for UTF-{8,16,32} C++ stl strings:
template
<
typename
StringType
,
bool
IsView
=
false
>
struct
string_caster
{
using
CharT
=
typename
StringType
::
value_type
;
// Simplify life by being able to assume standard char sizes (the standard only guarantees
// minimums, but Python requires exact sizes)
static_assert
(
!
std
::
is_same
<
CharT
,
char
>::
value
||
sizeof
(
CharT
)
==
1
,
"Unsupported char size != 1"
);
static_assert
(
!
std
::
is_same
<
CharT
,
char16_t
>::
value
||
sizeof
(
CharT
)
==
2
,
"Unsupported char16_t size != 2"
);
static_assert
(
!
std
::
is_same
<
CharT
,
char32_t
>::
value
||
sizeof
(
CharT
)
==
4
,
"Unsupported char32_t size != 4"
);
// wchar_t can be either 16 bits (Windows) or 32 (everywhere else)
static_assert
(
!
std
::
is_same
<
CharT
,
wchar_t
>::
value
||
sizeof
(
CharT
)
==
2
||
sizeof
(
CharT
)
==
4
,
"Unsupported wchar_t size != 2/4"
);
static
constexpr
size_t
UTF_N
=
8
*
sizeof
(
CharT
);
bool
load
(
handle
src
,
bool
)
{
#if PY_MAJOR_VERSION < 3
object
temp
;
#endif
handle
load_src
=
src
;
if
(
!
src
)
{
return
false
;
}
else
if
(
!
PyUnicode_Check
(
load_src
.
ptr
()))
{
#if PY_MAJOR_VERSION >= 3
return
load_bytes
(
load_src
);
#else
if
(
sizeof
(
CharT
)
==
1
)
{
return
load_bytes
(
load_src
);
}
// The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false
if
(
!
PYBIND11_BYTES_CHECK
(
load_src
.
ptr
()))
return
false
;
temp
=
reinterpret_steal
<
object
>
(
PyUnicode_FromObject
(
load_src
.
ptr
()));
if
(
!
temp
)
{
PyErr_Clear
();
return
false
;
}
load_src
=
temp
;
#endif
}
object
utfNbytes
=
reinterpret_steal
<
object
>
(
PyUnicode_AsEncodedString
(
load_src
.
ptr
(),
UTF_N
==
8
?
"utf-8"
:
UTF_N
==
16
?
"utf-16"
:
"utf-32"
,
nullptr
));
if
(
!
utfNbytes
)
{
PyErr_Clear
();
return
false
;
}
const
CharT
*
buffer
=
reinterpret_cast
<
const
CharT
*>
(
PYBIND11_BYTES_AS_STRING
(
utfNbytes
.
ptr
()));
size_t
length
=
(
size_t
)
PYBIND11_BYTES_SIZE
(
utfNbytes
.
ptr
())
/
sizeof
(
CharT
);
if
(
UTF_N
>
8
)
{
buffer
++
;
length
--
;
}
// Skip BOM for UTF-16/32
value
=
StringType
(
buffer
,
length
);
// If we're loading a string_view we need to keep the encoded Python object alive:
if
(
IsView
)
loader_life_support
::
add_patient
(
utfNbytes
);
return
true
;
}
static
handle
cast
(
const
StringType
&
src
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
const
char
*
buffer
=
reinterpret_cast
<
const
char
*>
(
src
.
data
());
ssize_t
nbytes
=
ssize_t
(
src
.
size
()
*
sizeof
(
CharT
));
handle
s
=
decode_utfN
(
buffer
,
nbytes
);
if
(
!
s
)
throw
error_already_set
();
return
s
;
}
PYBIND11_TYPE_CASTER
(
StringType
,
_
(
PYBIND11_STRING_NAME
));
private:
static
handle
decode_utfN
(
const
char
*
buffer
,
ssize_t
nbytes
)
{
#if !defined(PYPY_VERSION)
return
UTF_N
==
8
?
PyUnicode_DecodeUTF8
(
buffer
,
nbytes
,
nullptr
)
:
UTF_N
==
16
?
PyUnicode_DecodeUTF16
(
buffer
,
nbytes
,
nullptr
,
nullptr
)
:
PyUnicode_DecodeUTF32
(
buffer
,
nbytes
,
nullptr
,
nullptr
);
#else
// PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version
// sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a
// non-const char * arguments, which is also a nuissance, so bypass the whole thing by just
// passing the encoding as a string value, which works properly:
return
PyUnicode_Decode
(
buffer
,
nbytes
,
UTF_N
==
8
?
"utf-8"
:
UTF_N
==
16
?
"utf-16"
:
"utf-32"
,
nullptr
);
#endif
}
// When loading into a std::string or char*, accept a bytes object as-is (i.e.
// without any encoding/decoding attempt). For other C++ char sizes this is a no-op.
// which supports loading a unicode from a str, doesn't take this path.
template
<
typename
C
=
CharT
>
bool
load_bytes
(
enable_if_t
<
sizeof
(
C
)
==
1
,
handle
>
src
)
{
if
(
PYBIND11_BYTES_CHECK
(
src
.
ptr
()))
{
// We were passed a Python 3 raw bytes; accept it into a std::string or char*
// without any encoding attempt.
const
char
*
bytes
=
PYBIND11_BYTES_AS_STRING
(
src
.
ptr
());
if
(
bytes
)
{
value
=
StringType
(
bytes
,
(
size_t
)
PYBIND11_BYTES_SIZE
(
src
.
ptr
()));
return
true
;
}
}
return
false
;
}
template
<
typename
C
=
CharT
>
bool
load_bytes
(
enable_if_t
<
sizeof
(
C
)
!=
1
,
handle
>
)
{
return
false
;
}
};
template
<
typename
CharT
,
class
Traits
,
class
Allocator
>
struct
type_caster
<
std
::
basic_string
<
CharT
,
Traits
,
Allocator
>
,
enable_if_t
<
is_std_char_type
<
CharT
>::
value
>>
:
string_caster
<
std
::
basic_string
<
CharT
,
Traits
,
Allocator
>>
{};
#ifdef PYBIND11_HAS_STRING_VIEW
template
<
typename
CharT
,
class
Traits
>
struct
type_caster
<
std
::
basic_string_view
<
CharT
,
Traits
>
,
enable_if_t
<
is_std_char_type
<
CharT
>::
value
>>
:
string_caster
<
std
::
basic_string_view
<
CharT
,
Traits
>
,
true
>
{};
#endif
// Type caster for C-style strings. We basically use a std::string type caster, but also add the
// ability to use None as a nullptr char* (which the string caster doesn't allow).
template
<
typename
CharT
>
struct
type_caster
<
CharT
,
enable_if_t
<
is_std_char_type
<
CharT
>::
value
>>
{
using
StringType
=
std
::
basic_string
<
CharT
>
;
using
StringCaster
=
type_caster
<
StringType
>
;
StringCaster
str_caster
;
bool
none
=
false
;
public:
bool
load
(
handle
src
,
bool
convert
)
{
if
(
!
src
)
return
false
;
if
(
src
.
is_none
())
{
// Defer accepting None to other overloads (if we aren't in convert mode):
if
(
!
convert
)
return
false
;
none
=
true
;
return
true
;
}
return
str_caster
.
load
(
src
,
convert
);
}
static
handle
cast
(
const
CharT
*
src
,
return_value_policy
policy
,
handle
parent
)
{
if
(
src
==
nullptr
)
return
pybind11
::
none
().
inc_ref
();
return
StringCaster
::
cast
(
StringType
(
src
),
policy
,
parent
);
}
static
handle
cast
(
CharT
src
,
return_value_policy
policy
,
handle
parent
)
{
if
(
std
::
is_same
<
char
,
CharT
>::
value
)
{
handle
s
=
PyUnicode_DecodeLatin1
((
const
char
*
)
&
src
,
1
,
nullptr
);
if
(
!
s
)
throw
error_already_set
();
return
s
;
}
return
StringCaster
::
cast
(
StringType
(
1
,
src
),
policy
,
parent
);
}
operator
CharT
*
()
{
return
none
?
nullptr
:
const_cast
<
CharT
*>
(
static_cast
<
StringType
&>
(
str_caster
).
c_str
());
}
operator
CharT
()
{
if
(
none
)
throw
value_error
(
"Cannot convert None to a character"
);
auto
&
value
=
static_cast
<
StringType
&>
(
str_caster
);
size_t
str_len
=
value
.
size
();
if
(
str_len
==
0
)
throw
value_error
(
"Cannot convert empty string to a character"
);
// If we're in UTF-8 mode, we have two possible failures: one for a unicode character that
// is too high, and one for multiple unicode characters (caught later), so we need to figure
// out how long the first encoded character is in bytes to distinguish between these two
// errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those
// can fit into a single char value.
if
(
StringCaster
::
UTF_N
==
8
&&
str_len
>
1
&&
str_len
<=
4
)
{
unsigned
char
v0
=
static_cast
<
unsigned
char
>
(
value
[
0
]);
size_t
char0_bytes
=
!
(
v0
&
0x80
)
?
1
:
// low bits only: 0-127
(
v0
&
0xE0
)
==
0xC0
?
2
:
// 0b110xxxxx - start of 2-byte sequence
(
v0
&
0xF0
)
==
0xE0
?
3
:
// 0b1110xxxx - start of 3-byte sequence
4
;
// 0b11110xxx - start of 4-byte sequence
if
(
char0_bytes
==
str_len
)
{
// If we have a 128-255 value, we can decode it into a single char:
if
(
char0_bytes
==
2
&&
(
v0
&
0xFC
)
==
0xC0
)
{
// 0x110000xx 0x10xxxxxx
return
static_cast
<
CharT
>
(((
v0
&
3
)
<<
6
)
+
(
static_cast
<
unsigned
char
>
(
value
[
1
])
&
0x3F
));
}
// Otherwise we have a single character, but it's > U+00FF
throw
value_error
(
"Character code point not in range(0x100)"
);
}
}
// UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a
// surrogate pair with total length 2 instantly indicates a range error (but not a "your
// string was too long" error).
else
if
(
StringCaster
::
UTF_N
==
16
&&
str_len
==
2
)
{
char16_t
v0
=
static_cast
<
char16_t
>
(
value
[
0
]);
if
(
v0
>=
0xD800
&&
v0
<
0xE000
)
throw
value_error
(
"Character code point not in range(0x10000)"
);
}
if
(
str_len
!=
1
)
throw
value_error
(
"Expected a character, but multi-character string found"
);
return
value
[
0
];
}
static
PYBIND11_DESCR
name
()
{
return
type_descr
(
_
(
PYBIND11_STRING_NAME
));
}
template
<
typename
_T
>
using
cast_op_type
=
remove_reference_t
<
pybind11
::
detail
::
cast_op_type
<
_T
>>
;
};
// Base implementation for std::tuple and std::pair
template
<
template
<
typename
...
>
class
Tuple
,
typename
...
Ts
>
class
tuple_caster
{
using
type
=
Tuple
<
Ts
...
>
;
static
constexpr
auto
size
=
sizeof
...(
Ts
);
using
indices
=
make_index_sequence
<
size
>
;
public:
bool
load
(
handle
src
,
bool
convert
)
{
if
(
!
isinstance
<
sequence
>
(
src
))
return
false
;
const
auto
seq
=
reinterpret_borrow
<
sequence
>
(
src
);
if
(
seq
.
size
()
!=
size
)
return
false
;
return
load_impl
(
seq
,
convert
,
indices
{});
}
template
<
typename
T
>
static
handle
cast
(
T
&&
src
,
return_value_policy
policy
,
handle
parent
)
{
return
cast_impl
(
std
::
forward
<
T
>
(
src
),
policy
,
parent
,
indices
{});
}
static
PYBIND11_DESCR
name
()
{
return
type_descr
(
_
(
"Tuple["
)
+
detail
::
concat
(
make_caster
<
Ts
>::
name
()...)
+
_
(
"]"
));
}
template
<
typename
T
>
using
cast_op_type
=
type
;
operator
type
()
&
{
return
implicit_cast
(
indices
{});
}
operator
type
()
&&
{
return
std
::
move
(
*
this
).
implicit_cast
(
indices
{});
}
protected:
template
<
size_t
...
Is
>
type
implicit_cast
(
index_sequence
<
Is
...
>
)
&
{
return
type
(
cast_op
<
Ts
>
(
std
::
get
<
Is
>
(
subcasters
))...);
}
template
<
size_t
...
Is
>
type
implicit_cast
(
index_sequence
<
Is
...
>
)
&&
{
return
type
(
cast_op
<
Ts
>
(
std
::
move
(
std
::
get
<
Is
>
(
subcasters
)))...);
}
static
constexpr
bool
load_impl
(
const
sequence
&
,
bool
,
index_sequence
<>
)
{
return
true
;
}
template
<
size_t
...
Is
>
bool
load_impl
(
const
sequence
&
seq
,
bool
convert
,
index_sequence
<
Is
...
>
)
{
for
(
bool
r
:
{
std
::
get
<
Is
>
(
subcasters
).
load
(
seq
[
Is
],
convert
)...})
if
(
!
r
)
return
false
;
return
true
;
}
/* Implementation: Convert a C++ tuple into a Python tuple */
template
<
typename
T
,
size_t
...
Is
>
static
handle
cast_impl
(
T
&&
src
,
return_value_policy
policy
,
handle
parent
,
index_sequence
<
Is
...
>
)
{
std
::
array
<
object
,
size
>
entries
{{
reinterpret_steal
<
object
>
(
make_caster
<
Ts
>::
cast
(
std
::
get
<
Is
>
(
std
::
forward
<
T
>
(
src
)),
policy
,
parent
))...
}};
for
(
const
auto
&
entry
:
entries
)
if
(
!
entry
)
return
handle
();
tuple
result
(
size
);
int
counter
=
0
;
for
(
auto
&
entry
:
entries
)
PyTuple_SET_ITEM
(
result
.
ptr
(),
counter
++
,
entry
.
release
().
ptr
());
return
result
.
release
();
}
Tuple
<
make_caster
<
Ts
>
...
>
subcasters
;
};
template
<
typename
T1
,
typename
T2
>
class
type_caster
<
std
::
pair
<
T1
,
T2
>>
:
public
tuple_caster
<
std
::
pair
,
T1
,
T2
>
{};
template
<
typename
...
Ts
>
class
type_caster
<
std
::
tuple
<
Ts
...
>>
:
public
tuple_caster
<
std
::
tuple
,
Ts
...
>
{};
/// Helper class which abstracts away certain actions. Users can provide specializations for
/// custom holders, but it's only necessary if the type has a non-standard interface.
template
<
typename
T
>
struct
holder_helper
{
static
auto
get
(
const
T
&
p
)
->
decltype
(
p
.
get
())
{
return
p
.
get
();
}
};
/// Type caster for holder types like std::shared_ptr, etc.
template
<
typename
type
,
typename
holder_type
>
struct
copyable_holder_caster
:
public
type_caster_base
<
type
>
{
public:
using
base
=
type_caster_base
<
type
>
;
static_assert
(
std
::
is_base_of
<
base
,
type_caster
<
type
>>::
value
,
"Holder classes are only supported for custom types"
);
using
base
::
base
;
using
base
::
cast
;
using
base
::
typeinfo
;
using
base
::
value
;
bool
load
(
handle
src
,
bool
convert
)
{
return
base
::
template
load_impl
<
copyable_holder_caster
<
type
,
holder_type
>
>
(
src
,
convert
);
}
explicit
operator
type
*
()
{
return
this
->
value
;
}
explicit
operator
type
&
()
{
return
*
(
this
->
value
);
}
explicit
operator
holder_type
*
()
{
return
&
holder
;
}
// Workaround for Intel compiler bug
// see pybind11 issue 94
#if defined(__ICC) || defined(__INTEL_COMPILER)
operator
holder_type
&
()
{
return
holder
;
}
#else
explicit
operator
holder_type
&
()
{
return
holder
;
}
#endif
static
handle
cast
(
const
holder_type
&
src
,
return_value_policy
,
handle
)
{
const
auto
*
ptr
=
holder_helper
<
holder_type
>::
get
(
src
);
return
type_caster_base
<
type
>::
cast_holder
(
ptr
,
&
src
);
}
protected:
friend
class
type_caster_generic
;
void
check_holder_compat
()
{
if
(
typeinfo
->
default_holder
)
throw
cast_error
(
"Unable to load a custom holder type from a default-holder instance"
);
}
bool
load_value
(
const
value_and_holder
&
v_h
)
{
if
(
v_h
.
holder_constructed
())
{
value
=
v_h
.
value_ptr
();
holder
=
v_h
.
holder
<
holder_type
>
();
return
true
;
}
else
{
throw
cast_error
(
"Unable to cast from non-held to held instance (T& to Holder<T>) "
#if defined(NDEBUG)
"(compile in debug mode for type information)"
);
#else
"of type '"
+
type_id
<
holder_type
>
()
+
"''"
);
#endif
}
}
template
<
typename
T
=
holder_type
,
detail
::
enable_if_t
<!
std
::
is_constructible
<
T
,
const
T
&
,
type
*
>
::
value
,
int
>
=
0
>
bool
try_implicit_casts
(
handle
,
bool
)
{
return
false
;
}
template
<
typename
T
=
holder_type
,
detail
::
enable_if_t
<
std
::
is_constructible
<
T
,
const
T
&
,
type
*
>
::
value
,
int
>
=
0
>
bool
try_implicit_casts
(
handle
src
,
bool
convert
)
{
for
(
auto
&
cast
:
typeinfo
->
implicit_casts
)
{
copyable_holder_caster
sub_caster
(
*
cast
.
first
);
if
(
sub_caster
.
load
(
src
,
convert
))
{
value
=
cast
.
second
(
sub_caster
.
value
);
holder
=
holder_type
(
sub_caster
.
holder
,
(
type
*
)
value
);
return
true
;
}
}
return
false
;
}
static
bool
try_direct_conversions
(
handle
)
{
return
false
;
}
holder_type
holder
;
};
/// Specialize for the common std::shared_ptr, so users don't need to
template
<
typename
T
>
class
type_caster
<
std
::
shared_ptr
<
T
>>
:
public
copyable_holder_caster
<
T
,
std
::
shared_ptr
<
T
>>
{
};
template
<
typename
type
,
typename
holder_type
>
struct
move_only_holder_caster
{
static_assert
(
std
::
is_base_of
<
type_caster_base
<
type
>
,
type_caster
<
type
>>::
value
,
"Holder classes are only supported for custom types"
);
static
handle
cast
(
holder_type
&&
src
,
return_value_policy
,
handle
)
{
auto
*
ptr
=
holder_helper
<
holder_type
>::
get
(
src
);
return
type_caster_base
<
type
>::
cast_holder
(
ptr
,
&
src
);
}
static
PYBIND11_DESCR
name
()
{
return
type_caster_base
<
type
>::
name
();
}
};
template
<
typename
type
,
typename
deleter
>
class
type_caster
<
std
::
unique_ptr
<
type
,
deleter
>>
:
public
move_only_holder_caster
<
type
,
std
::
unique_ptr
<
type
,
deleter
>>
{
};
template
<
typename
type
,
typename
holder_type
>
using
type_caster_holder
=
conditional_t
<
is_copy_constructible
<
holder_type
>::
value
,
copyable_holder_caster
<
type
,
holder_type
>
,
move_only_holder_caster
<
type
,
holder_type
>>
;
template
<
typename
T
,
bool
Value
=
false
>
struct
always_construct_holder
{
static
constexpr
bool
value
=
Value
;
};
/// Create a specialization for custom holder types (silently ignores std::shared_ptr)
#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \
namespace pybind11 { namespace detail { \
template <typename type> \
struct always_construct_holder<holder_type> : always_construct_holder<void, ##__VA_ARGS__> { }; \
template <typename type> \
class type_caster<holder_type, enable_if_t<!is_shared_ptr<holder_type>::value>> \
: public type_caster_holder<type, holder_type> { }; \
}}
// PYBIND11_DECLARE_HOLDER_TYPE holder types:
template
<
typename
base
,
typename
holder
>
struct
is_holder_type
:
std
::
is_base_of
<
detail
::
type_caster_holder
<
base
,
holder
>
,
detail
::
type_caster
<
holder
>>
{};
// Specialization for always-supported unique_ptr holders:
template
<
typename
base
,
typename
deleter
>
struct
is_holder_type
<
base
,
std
::
unique_ptr
<
base
,
deleter
>>
:
std
::
true_type
{};
template
<
typename
T
>
struct
handle_type_name
{
static
PYBIND11_DESCR
name
()
{
return
_
<
T
>
();
}
};
template
<
>
struct
handle_type_name
<
bytes
>
{
static
PYBIND11_DESCR
name
()
{
return
_
(
PYBIND11_BYTES_NAME
);
}
};
template
<
>
struct
handle_type_name
<
args
>
{
static
PYBIND11_DESCR
name
()
{
return
_
(
"*args"
);
}
};
template
<
>
struct
handle_type_name
<
kwargs
>
{
static
PYBIND11_DESCR
name
()
{
return
_
(
"**kwargs"
);
}
};
template
<
typename
type
>
struct
pyobject_caster
{
template
<
typename
T
=
type
,
enable_if_t
<
std
::
is_same
<
T
,
handle
>
::
value
,
int
>
=
0
>
bool
load
(
handle
src
,
bool
/* convert */
)
{
value
=
src
;
return
static_cast
<
bool
>
(
value
);
}
template
<
typename
T
=
type
,
enable_if_t
<
std
::
is_base_of
<
object
,
T
>
::
value
,
int
>
=
0
>
bool
load
(
handle
src
,
bool
/* convert */
)
{
if
(
!
isinstance
<
type
>
(
src
))
return
false
;
value
=
reinterpret_borrow
<
type
>
(
src
);
return
true
;
}
static
handle
cast
(
const
handle
&
src
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
return
src
.
inc_ref
();
}
PYBIND11_TYPE_CASTER
(
type
,
handle_type_name
<
type
>::
name
());
};
template
<
typename
T
>
class
type_caster
<
T
,
enable_if_t
<
is_pyobject
<
T
>::
value
>>
:
public
pyobject_caster
<
T
>
{
};
// Our conditions for enabling moving are quite restrictive:
// At compile time:
// - T needs to be a non-const, non-pointer, non-reference type
// - type_caster<T>::operator T&() must exist
// - the type must be move constructible (obviously)
// At run-time:
// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it
// must have ref_count() == 1)h
// If any of the above are not satisfied, we fall back to copying.
template
<
typename
T
>
using
move_is_plain_type
=
satisfies_none_of
<
T
,
std
::
is_void
,
std
::
is_pointer
,
std
::
is_reference
,
std
::
is_const
>
;
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
move_always
:
std
::
false_type
{};
template
<
typename
T
>
struct
move_always
<
T
,
enable_if_t
<
all_of
<
move_is_plain_type
<
T
>
,
negation
<
is_copy_constructible
<
T
>>
,
std
::
is_move_constructible
<
T
>
,
std
::
is_same
<
decltype
(
std
::
declval
<
make_caster
<
T
>>
().
operator
T
&
()),
T
&>
>::
value
>>
:
std
::
true_type
{};
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
move_if_unreferenced
:
std
::
false_type
{};
template
<
typename
T
>
struct
move_if_unreferenced
<
T
,
enable_if_t
<
all_of
<
move_is_plain_type
<
T
>
,
negation
<
move_always
<
T
>>
,
std
::
is_move_constructible
<
T
>
,
std
::
is_same
<
decltype
(
std
::
declval
<
make_caster
<
T
>>
().
operator
T
&
()),
T
&>
>::
value
>>
:
std
::
true_type
{};
template
<
typename
T
>
using
move_never
=
none_of
<
move_always
<
T
>
,
move_if_unreferenced
<
T
>>
;
// Detect whether returning a `type` from a cast on type's type_caster is going to result in a
// reference or pointer to a local variable of the type_caster. Basically, only
// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe;
// everything else returns a reference/pointer to a local variable.
template
<
typename
type
>
using
cast_is_temporary_value_reference
=
bool_constant
<
(
std
::
is_reference
<
type
>::
value
||
std
::
is_pointer
<
type
>::
value
)
&&
!
std
::
is_base_of
<
type_caster_generic
,
make_caster
<
type
>>::
value
>
;
// When a value returned from a C++ function is being cast back to Python, we almost always want to
// force `policy = move`, regardless of the return value policy the function/method was declared
// with. Some classes (most notably Eigen::Ref and related) need to avoid this, and so can do so by
// specializing this struct.
template
<
typename
Return
,
typename
SFINAE
=
void
>
struct
return_value_policy_override
{
static
return_value_policy
policy
(
return_value_policy
p
)
{
return
!
std
::
is_lvalue_reference
<
Return
>::
value
&&
!
std
::
is_pointer
<
Return
>::
value
?
return_value_policy
::
move
:
p
;
}
};
// Basic python -> C++ casting; throws if casting fails
template
<
typename
T
,
typename
SFINAE
>
type_caster
<
T
,
SFINAE
>
&
load_type
(
type_caster
<
T
,
SFINAE
>
&
conv
,
const
handle
&
handle
)
{
if
(
!
conv
.
load
(
handle
,
true
))
{
#if defined(NDEBUG)
throw
cast_error
(
"Unable to cast Python instance to C++ type (compile in debug mode for details)"
);
#else
throw
cast_error
(
"Unable to cast Python instance of type "
+
(
std
::
string
)
str
(
handle
.
get_type
())
+
" to C++ type '"
+
type_id
<
T
>
()
+
"''"
);
#endif
}
return
conv
;
}
// Wrapper around the above that also constructs and returns a type_caster
template
<
typename
T
>
make_caster
<
T
>
load_type
(
const
handle
&
handle
)
{
make_caster
<
T
>
conv
;
load_type
(
conv
,
handle
);
return
conv
;
}
NAMESPACE_END
(
detail
)
// pytype -> C++ type
template
<
typename
T
,
detail
::
enable_if_t
<!
detail
::
is_pyobject
<
T
>
::
value
,
int
>
=
0
>
T
cast
(
const
handle
&
handle
)
{
using
namespace
detail
;
static_assert
(
!
cast_is_temporary_value_reference
<
T
>::
value
,
"Unable to cast type to reference: value is local to type caster"
);
return
cast_op
<
T
>
(
load_type
<
T
>
(
handle
));
}
// pytype -> pytype (calls converting constructor)
template
<
typename
T
,
detail
::
enable_if_t
<
detail
::
is_pyobject
<
T
>
::
value
,
int
>
=
0
>
T
cast
(
const
handle
&
handle
)
{
return
T
(
reinterpret_borrow
<
object
>
(
handle
));
}
// C++ type -> py::object
template
<
typename
T
,
detail
::
enable_if_t
<!
detail
::
is_pyobject
<
T
>
::
value
,
int
>
=
0
>
object
cast
(
const
T
&
value
,
return_value_policy
policy
=
return_value_policy
::
automatic_reference
,
handle
parent
=
handle
())
{
if
(
policy
==
return_value_policy
::
automatic
)
policy
=
std
::
is_pointer
<
T
>::
value
?
return_value_policy
::
take_ownership
:
return_value_policy
::
copy
;
else
if
(
policy
==
return_value_policy
::
automatic_reference
)
policy
=
std
::
is_pointer
<
T
>::
value
?
return_value_policy
::
reference
:
return_value_policy
::
copy
;
return
reinterpret_steal
<
object
>
(
detail
::
make_caster
<
T
>::
cast
(
value
,
policy
,
parent
));
}
template
<
typename
T
>
T
handle
::
cast
()
const
{
return
pybind11
::
cast
<
T
>
(
*
this
);
}
template
<
>
inline
void
handle
::
cast
()
const
{
return
;
}
template
<
typename
T
>
detail
::
enable_if_t
<!
detail
::
move_never
<
T
>::
value
,
T
>
move
(
object
&&
obj
)
{
if
(
obj
.
ref_count
()
>
1
)
#if defined(NDEBUG)
throw
cast_error
(
"Unable to cast Python instance to C++ rvalue: instance has multiple references"
" (compile in debug mode for details)"
);
#else
throw
cast_error
(
"Unable to move from Python "
+
(
std
::
string
)
str
(
obj
.
get_type
())
+
" instance to C++ "
+
type_id
<
T
>
()
+
" instance: instance has multiple references"
);
#endif
// Move into a temporary and return that, because the reference may be a local value of `conv`
T
ret
=
std
::
move
(
detail
::
load_type
<
T
>
(
obj
).
operator
T
&
());
return
ret
;
}
// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does:
// - If we have to move (because T has no copy constructor), do it. This will fail if the moved
// object has multiple references, but trying to copy will fail to compile.
// - If both movable and copyable, check ref count: if 1, move; otherwise copy
// - Otherwise (not movable), copy.
template
<
typename
T
>
detail
::
enable_if_t
<
detail
::
move_always
<
T
>::
value
,
T
>
cast
(
object
&&
object
)
{
return
move
<
T
>
(
std
::
move
(
object
));
}
template
<
typename
T
>
detail
::
enable_if_t
<
detail
::
move_if_unreferenced
<
T
>::
value
,
T
>
cast
(
object
&&
object
)
{
if
(
object
.
ref_count
()
>
1
)
return
cast
<
T
>
(
object
);
else
return
move
<
T
>
(
std
::
move
(
object
));
}
template
<
typename
T
>
detail
::
enable_if_t
<
detail
::
move_never
<
T
>::
value
,
T
>
cast
(
object
&&
object
)
{
return
cast
<
T
>
(
object
);
}
template
<
typename
T
>
T
object
::
cast
()
const
&
{
return
pybind11
::
cast
<
T
>
(
*
this
);
}
template
<
typename
T
>
T
object
::
cast
()
&&
{
return
pybind11
::
cast
<
T
>
(
std
::
move
(
*
this
));
}
template
<
>
inline
void
object
::
cast
()
const
&
{
return
;
}
template
<
>
inline
void
object
::
cast
()
&&
{
return
;
}
NAMESPACE_BEGIN
(
detail
)
// Declared in pytypes.h:
template
<
typename
T
,
enable_if_t
<!
is_pyobject
<
T
>
::
value
,
int
>>
object
object_or_cast
(
T
&&
o
)
{
return
pybind11
::
cast
(
std
::
forward
<
T
>
(
o
));
}
struct
overload_unused
{};
// Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro
template
<
typename
ret_type
>
using
overload_caster_t
=
conditional_t
<
cast_is_temporary_value_reference
<
ret_type
>::
value
,
make_caster
<
ret_type
>
,
overload_unused
>
;
// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then
// store the result in the given variable. For other types, this is a no-op.
template
<
typename
T
>
enable_if_t
<
cast_is_temporary_value_reference
<
T
>::
value
,
T
>
cast_ref
(
object
&&
o
,
make_caster
<
T
>
&
caster
)
{
return
cast_op
<
T
>
(
load_type
(
caster
,
o
));
}
template
<
typename
T
>
enable_if_t
<!
cast_is_temporary_value_reference
<
T
>::
value
,
T
>
cast_ref
(
object
&&
,
overload_unused
&
)
{
pybind11_fail
(
"Internal error: cast_ref fallback invoked"
);
}
// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even
// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in
// cases where pybind11::cast is valid.
template
<
typename
T
>
enable_if_t
<!
cast_is_temporary_value_reference
<
T
>::
value
,
T
>
cast_safe
(
object
&&
o
)
{
return
pybind11
::
cast
<
T
>
(
std
::
move
(
o
));
}
template
<
typename
T
>
enable_if_t
<
cast_is_temporary_value_reference
<
T
>::
value
,
T
>
cast_safe
(
object
&&
)
{
pybind11_fail
(
"Internal error: cast_safe fallback invoked"
);
}
template
<
>
inline
void
cast_safe
<
void
>
(
object
&&
)
{}
NAMESPACE_END
(
detail
)
template
<
return_value_policy
policy
=
return_value_policy
::
automatic_reference
,
typename
...
Args
>
tuple
make_tuple
(
Args
&&
...
args_
)
{
constexpr
size_t
size
=
sizeof
...(
Args
);
std
::
array
<
object
,
size
>
args
{
{
reinterpret_steal
<
object
>
(
detail
::
make_caster
<
Args
>::
cast
(
std
::
forward
<
Args
>
(
args_
),
policy
,
nullptr
))...
}
};
for
(
size_t
i
=
0
;
i
<
args
.
size
();
i
++
)
{
if
(
!
args
[
i
])
{
#if defined(NDEBUG)
throw
cast_error
(
"make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"
);
#else
std
::
array
<
std
::
string
,
size
>
argtypes
{
{
type_id
<
Args
>
()...}
};
throw
cast_error
(
"make_tuple(): unable to convert argument of type '"
+
argtypes
[
i
]
+
"' to Python object"
);
#endif
}
}
tuple
result
(
size
);
int
counter
=
0
;
for
(
auto
&
arg_value
:
args
)
PyTuple_SET_ITEM
(
result
.
ptr
(),
counter
++
,
arg_value
.
release
().
ptr
());
return
result
;
}
/// \ingroup annotations
/// Annotation for arguments
struct
arg
{
/// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument.
constexpr
explicit
arg
(
const
char
*
name
=
nullptr
)
:
name
(
name
),
flag_noconvert
(
false
),
flag_none
(
true
)
{
}
/// Assign a value to this argument
template
<
typename
T
>
arg_v
operator
=
(
T
&&
value
)
const
;
/// Indicate that the type should not be converted in the type caster
arg
&
noconvert
(
bool
flag
=
true
)
{
flag_noconvert
=
flag
;
return
*
this
;
}
/// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args)
arg
&
none
(
bool
flag
=
true
)
{
flag_none
=
flag
;
return
*
this
;
}
const
char
*
name
;
///< If non-null, this is a named kwargs argument
bool
flag_noconvert
:
1
;
///< If set, do not allow conversion (requires a supporting type caster!)
bool
flag_none
:
1
;
///< If set (the default), allow None to be passed to this argument
};
/// \ingroup annotations
/// Annotation for arguments with values
struct
arg_v
:
arg
{
private:
template
<
typename
T
>
arg_v
(
arg
&&
base
,
T
&&
x
,
const
char
*
descr
=
nullptr
)
:
arg
(
base
),
value
(
reinterpret_steal
<
object
>
(
detail
::
make_caster
<
T
>::
cast
(
x
,
return_value_policy
::
automatic
,
{})
)),
descr
(
descr
)
#if !defined(NDEBUG)
,
type
(
type_id
<
T
>
())
#endif
{
}
public:
/// Direct construction with name, default, and description
template
<
typename
T
>
arg_v
(
const
char
*
name
,
T
&&
x
,
const
char
*
descr
=
nullptr
)
:
arg_v
(
arg
(
name
),
std
::
forward
<
T
>
(
x
),
descr
)
{
}
/// Called internally when invoking `py::arg("a") = value`
template
<
typename
T
>
arg_v
(
const
arg
&
base
,
T
&&
x
,
const
char
*
descr
=
nullptr
)
:
arg_v
(
arg
(
base
),
std
::
forward
<
T
>
(
x
),
descr
)
{
}
/// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg&
arg_v
&
noconvert
(
bool
flag
=
true
)
{
arg
::
noconvert
(
flag
);
return
*
this
;
}
/// Same as `arg::nonone()`, but returns *this as arg_v&, not arg&
arg_v
&
none
(
bool
flag
=
true
)
{
arg
::
none
(
flag
);
return
*
this
;
}
/// The default value
object
value
;
/// The (optional) description of the default value
const
char
*
descr
;
#if !defined(NDEBUG)
/// The C++ type name of the default value (only available when compiled in debug mode)
std
::
string
type
;
#endif
};
template
<
typename
T
>
arg_v
arg
::
operator
=
(
T
&&
value
)
const
{
return
{
std
::
move
(
*
this
),
std
::
forward
<
T
>
(
value
)};
}
/// Alias for backward compatibility -- to be removed in version 2.0
template
<
typename
/*unused*/
>
using
arg_t
=
arg_v
;
inline
namespace
literals
{
/** \rst
String literal version of `arg`
\endrst */
constexpr
arg
operator
""
_a
(
const
char
*
name
,
size_t
)
{
return
arg
(
name
);
}
}
NAMESPACE_BEGIN
(
detail
)
// forward declaration (definition in attr.h)
struct
function_record
;
/// Internal data associated with a single function call
struct
function_call
{
function_call
(
function_record
&
f
,
handle
p
);
// Implementation in attr.h
/// The function data:
const
function_record
&
func
;
/// Arguments passed to the function:
std
::
vector
<
handle
>
args
;
/// The `convert` value the arguments should be loaded with
std
::
vector
<
bool
>
args_convert
;
/// The parent, if any
handle
parent
;
};
/// Helper class which loads arguments for C++ functions called from Python
template
<
typename
...
Args
>
class
argument_loader
{
using
indices
=
make_index_sequence
<
sizeof
...(
Args
)
>
;
template
<
typename
Arg
>
using
argument_is_args
=
std
::
is_same
<
intrinsic_t
<
Arg
>
,
args
>
;
template
<
typename
Arg
>
using
argument_is_kwargs
=
std
::
is_same
<
intrinsic_t
<
Arg
>
,
kwargs
>
;
// Get args/kwargs argument positions relative to the end of the argument list:
static
constexpr
auto
args_pos
=
constexpr_first
<
argument_is_args
,
Args
...
>
()
-
(
int
)
sizeof
...(
Args
),
kwargs_pos
=
constexpr_first
<
argument_is_kwargs
,
Args
...
>
()
-
(
int
)
sizeof
...(
Args
);
static
constexpr
bool
args_kwargs_are_last
=
kwargs_pos
>=
-
1
&&
args_pos
>=
kwargs_pos
-
1
;
static_assert
(
args_kwargs_are_last
,
"py::args/py::kwargs are only permitted as the last argument(s) of a function"
);
public:
static
constexpr
bool
has_kwargs
=
kwargs_pos
<
0
;
static
constexpr
bool
has_args
=
args_pos
<
0
;
static
PYBIND11_DESCR
arg_names
()
{
return
detail
::
concat
(
make_caster
<
Args
>::
name
()...);
}
bool
load_args
(
function_call
&
call
)
{
return
load_impl_sequence
(
call
,
indices
{});
}
template
<
typename
Return
,
typename
Guard
,
typename
Func
>
enable_if_t
<!
std
::
is_void
<
Return
>::
value
,
Return
>
call
(
Func
&&
f
)
&&
{
return
std
::
move
(
*
this
).
template
call_impl
<
Return
>(
std
::
forward
<
Func
>
(
f
),
indices
{},
Guard
{});
}
template
<
typename
Return
,
typename
Guard
,
typename
Func
>
enable_if_t
<
std
::
is_void
<
Return
>::
value
,
void_type
>
call
(
Func
&&
f
)
&&
{
std
::
move
(
*
this
).
template
call_impl
<
Return
>(
std
::
forward
<
Func
>
(
f
),
indices
{},
Guard
{});
return
void_type
();
}
private:
static
bool
load_impl_sequence
(
function_call
&
,
index_sequence
<>
)
{
return
true
;
}
template
<
size_t
...
Is
>
bool
load_impl_sequence
(
function_call
&
call
,
index_sequence
<
Is
...
>
)
{
for
(
bool
r
:
{
std
::
get
<
Is
>
(
argcasters
).
load
(
call
.
args
[
Is
],
call
.
args_convert
[
Is
])...})
if
(
!
r
)
return
false
;
return
true
;
}
template
<
typename
Return
,
typename
Func
,
size_t
...
Is
,
typename
Guard
>
Return
call_impl
(
Func
&&
f
,
index_sequence
<
Is
...
>
,
Guard
&&
)
{
return
std
::
forward
<
Func
>
(
f
)(
cast_op
<
Args
>
(
std
::
move
(
std
::
get
<
Is
>
(
argcasters
)))...);
}
std
::
tuple
<
make_caster
<
Args
>
...
>
argcasters
;
};
/// Helper class which collects only positional arguments for a Python function call.
/// A fancier version below can collect any argument, but this one is optimal for simple calls.
template
<
return_value_policy
policy
>
class
simple_collector
{
public:
template
<
typename
...
Ts
>
explicit
simple_collector
(
Ts
&&
...
values
)
:
m_args
(
pybind11
::
make_tuple
<
policy
>
(
std
::
forward
<
Ts
>
(
values
)...))
{
}
const
tuple
&
args
()
const
&
{
return
m_args
;
}
dict
kwargs
()
const
{
return
{};
}
tuple
args
()
&&
{
return
std
::
move
(
m_args
);
}
/// Call a Python function and pass the collected arguments
object
call
(
PyObject
*
ptr
)
const
{
PyObject
*
result
=
PyObject_CallObject
(
ptr
,
m_args
.
ptr
());
if
(
!
result
)
throw
error_already_set
();
return
reinterpret_steal
<
object
>
(
result
);
}
private:
tuple
m_args
;
};
/// Helper class which collects positional, keyword, * and ** arguments for a Python function call
template
<
return_value_policy
policy
>
class
unpacking_collector
{
public:
template
<
typename
...
Ts
>
explicit
unpacking_collector
(
Ts
&&
...
values
)
{
// Tuples aren't (easily) resizable so a list is needed for collection,
// but the actual function call strictly requires a tuple.
auto
args_list
=
list
();
int
_
[]
=
{
0
,
(
process
(
args_list
,
std
::
forward
<
Ts
>
(
values
)),
0
)...
};
ignore_unused
(
_
);
m_args
=
std
::
move
(
args_list
);
}
const
tuple
&
args
()
const
&
{
return
m_args
;
}
const
dict
&
kwargs
()
const
&
{
return
m_kwargs
;
}
tuple
args
()
&&
{
return
std
::
move
(
m_args
);
}
dict
kwargs
()
&&
{
return
std
::
move
(
m_kwargs
);
}
/// Call a Python function and pass the collected arguments
object
call
(
PyObject
*
ptr
)
const
{
PyObject
*
result
=
PyObject_Call
(
ptr
,
m_args
.
ptr
(),
m_kwargs
.
ptr
());
if
(
!
result
)
throw
error_already_set
();
return
reinterpret_steal
<
object
>
(
result
);
}
private:
template
<
typename
T
>
void
process
(
list
&
args_list
,
T
&&
x
)
{
auto
o
=
reinterpret_steal
<
object
>
(
detail
::
make_caster
<
T
>::
cast
(
std
::
forward
<
T
>
(
x
),
policy
,
{}));
if
(
!
o
)
{
#if defined(NDEBUG)
argument_cast_error
();
#else
argument_cast_error
(
std
::
to_string
(
args_list
.
size
()),
type_id
<
T
>
());
#endif
}
args_list
.
append
(
o
);
}
void
process
(
list
&
args_list
,
detail
::
args_proxy
ap
)
{
for
(
const
auto
&
a
:
ap
)
args_list
.
append
(
a
);
}
void
process
(
list
&
/*args_list*/
,
arg_v
a
)
{
if
(
!
a
.
name
)
#if defined(NDEBUG)
nameless_argument_error
();
#else
nameless_argument_error
(
a
.
type
);
#endif
if
(
m_kwargs
.
contains
(
a
.
name
))
{
#if defined(NDEBUG)
multiple_values_error
();
#else
multiple_values_error
(
a
.
name
);
#endif
}
if
(
!
a
.
value
)
{
#if defined(NDEBUG)
argument_cast_error
();
#else
argument_cast_error
(
a
.
name
,
a
.
type
);
#endif
}
m_kwargs
[
a
.
name
]
=
a
.
value
;
}
void
process
(
list
&
/*args_list*/
,
detail
::
kwargs_proxy
kp
)
{
if
(
!
kp
)
return
;
for
(
const
auto
&
k
:
reinterpret_borrow
<
dict
>
(
kp
))
{
if
(
m_kwargs
.
contains
(
k
.
first
))
{
#if defined(NDEBUG)
multiple_values_error
();
#else
multiple_values_error
(
str
(
k
.
first
));
#endif
}
m_kwargs
[
k
.
first
]
=
k
.
second
;
}
}
[[
noreturn
]]
static
void
nameless_argument_error
()
{
throw
type_error
(
"Got kwargs without a name; only named arguments "
"may be passed via py::arg() to a python function call. "
"(compile in debug mode for details)"
);
}
[[
noreturn
]]
static
void
nameless_argument_error
(
std
::
string
type
)
{
throw
type_error
(
"Got kwargs without a name of type '"
+
type
+
"'; only named "
"arguments may be passed via py::arg() to a python function call. "
);
}
[[
noreturn
]]
static
void
multiple_values_error
()
{
throw
type_error
(
"Got multiple values for keyword argument "
"(compile in debug mode for details)"
);
}
[[
noreturn
]]
static
void
multiple_values_error
(
std
::
string
name
)
{
throw
type_error
(
"Got multiple values for keyword argument '"
+
name
+
"'"
);
}
[[
noreturn
]]
static
void
argument_cast_error
()
{
throw
cast_error
(
"Unable to convert call argument to Python object "
"(compile in debug mode for details)"
);
}
[[
noreturn
]]
static
void
argument_cast_error
(
std
::
string
name
,
std
::
string
type
)
{
throw
cast_error
(
"Unable to convert call argument '"
+
name
+
"' of type '"
+
type
+
"' to Python object"
);
}
private:
tuple
m_args
;
dict
m_kwargs
;
};
/// Collect only positional arguments for a Python function call
template
<
return_value_policy
policy
,
typename
...
Args
,
typename
=
enable_if_t
<
all_of
<
is_positional
<
Args
>...
>::
value
>>
simple_collector
<
policy
>
collect_arguments
(
Args
&&
...
args
)
{
return
simple_collector
<
policy
>
(
std
::
forward
<
Args
>
(
args
)...);
}
/// Collect all arguments, including keywords and unpacking (only instantiated when needed)
template
<
return_value_policy
policy
,
typename
...
Args
,
typename
=
enable_if_t
<!
all_of
<
is_positional
<
Args
>...
>::
value
>>
unpacking_collector
<
policy
>
collect_arguments
(
Args
&&
...
args
)
{
// Following argument order rules for generalized unpacking according to PEP 448
static_assert
(
constexpr_last
<
is_positional
,
Args
...
>
()
<
constexpr_first
<
is_keyword_or_ds
,
Args
...
>
()
&&
constexpr_last
<
is_s_unpacking
,
Args
...
>
()
<
constexpr_first
<
is_ds_unpacking
,
Args
...
>
(),
"Invalid function call: positional args must precede keywords and ** unpacking; "
"* unpacking must precede ** unpacking"
);
return
unpacking_collector
<
policy
>
(
std
::
forward
<
Args
>
(
args
)...);
}
template
<
typename
Derived
>
template
<
return_value_policy
policy
,
typename
...
Args
>
object
object_api
<
Derived
>::
operator
()(
Args
&&
...
args
)
const
{
return
detail
::
collect_arguments
<
policy
>
(
std
::
forward
<
Args
>
(
args
)...).
call
(
derived
().
ptr
());
}
template
<
typename
Derived
>
template
<
return_value_policy
policy
,
typename
...
Args
>
object
object_api
<
Derived
>::
call
(
Args
&&
...
args
)
const
{
return
operator
()
<
policy
>
(
std
::
forward
<
Args
>
(
args
)...);
}
NAMESPACE_END
(
detail
)
#define PYBIND11_MAKE_OPAQUE(Type) \
namespace pybind11 { namespace detail { \
template<> class type_caster<Type> : public type_caster_base<Type> { }; \
}}
NAMESPACE_END
(
pybind11
)
ppocr/postprocess/lanms/include/pybind11/chrono.h
0 → 100644
View file @
2401626a
/*
pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime
Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
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 "pybind11.h"
#include <cmath>
#include <ctime>
#include <chrono>
#include <datetime.h>
// Backport the PyDateTime_DELTA functions from Python3.3 if required
#ifndef PyDateTime_DELTA_GET_DAYS
#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days)
#endif
#ifndef PyDateTime_DELTA_GET_SECONDS
#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds)
#endif
#ifndef PyDateTime_DELTA_GET_MICROSECONDS
#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds)
#endif
NAMESPACE_BEGIN
(
pybind11
)
NAMESPACE_BEGIN
(
detail
)
template
<
typename
type
>
class
duration_caster
{
public:
typedef
typename
type
::
rep
rep
;
typedef
typename
type
::
period
period
;
typedef
std
::
chrono
::
duration
<
uint_fast32_t
,
std
::
ratio
<
86400
>>
days
;
bool
load
(
handle
src
,
bool
)
{
using
namespace
std
::
chrono
;
// Lazy initialise the PyDateTime import
if
(
!
PyDateTimeAPI
)
{
PyDateTime_IMPORT
;
}
if
(
!
src
)
return
false
;
// If invoked with datetime.delta object
if
(
PyDelta_Check
(
src
.
ptr
()))
{
value
=
type
(
duration_cast
<
duration
<
rep
,
period
>>
(
days
(
PyDateTime_DELTA_GET_DAYS
(
src
.
ptr
()))
+
seconds
(
PyDateTime_DELTA_GET_SECONDS
(
src
.
ptr
()))
+
microseconds
(
PyDateTime_DELTA_GET_MICROSECONDS
(
src
.
ptr
()))));
return
true
;
}
// If invoked with a float we assume it is seconds and convert
else
if
(
PyFloat_Check
(
src
.
ptr
()))
{
value
=
type
(
duration_cast
<
duration
<
rep
,
period
>>
(
duration
<
double
>
(
PyFloat_AsDouble
(
src
.
ptr
()))));
return
true
;
}
else
return
false
;
}
// If this is a duration just return it back
static
const
std
::
chrono
::
duration
<
rep
,
period
>&
get_duration
(
const
std
::
chrono
::
duration
<
rep
,
period
>
&
src
)
{
return
src
;
}
// If this is a time_point get the time_since_epoch
template
<
typename
Clock
>
static
std
::
chrono
::
duration
<
rep
,
period
>
get_duration
(
const
std
::
chrono
::
time_point
<
Clock
,
std
::
chrono
::
duration
<
rep
,
period
>>
&
src
)
{
return
src
.
time_since_epoch
();
}
static
handle
cast
(
const
type
&
src
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
using
namespace
std
::
chrono
;
// Use overloaded function to get our duration from our source
// Works out if it is a duration or time_point and get the duration
auto
d
=
get_duration
(
src
);
// Lazy initialise the PyDateTime import
if
(
!
PyDateTimeAPI
)
{
PyDateTime_IMPORT
;
}
// Declare these special duration types so the conversions happen with the correct primitive types (int)
using
dd_t
=
duration
<
int
,
std
::
ratio
<
86400
>>
;
using
ss_t
=
duration
<
int
,
std
::
ratio
<
1
>>
;
using
us_t
=
duration
<
int
,
std
::
micro
>
;
auto
dd
=
duration_cast
<
dd_t
>
(
d
);
auto
subd
=
d
-
dd
;
auto
ss
=
duration_cast
<
ss_t
>
(
subd
);
auto
us
=
duration_cast
<
us_t
>
(
subd
-
ss
);
return
PyDelta_FromDSU
(
dd
.
count
(),
ss
.
count
(),
us
.
count
());
}
PYBIND11_TYPE_CASTER
(
type
,
_
(
"datetime.timedelta"
));
};
// This is for casting times on the system clock into datetime.datetime instances
template
<
typename
Duration
>
class
type_caster
<
std
::
chrono
::
time_point
<
std
::
chrono
::
system_clock
,
Duration
>>
{
public:
typedef
std
::
chrono
::
time_point
<
std
::
chrono
::
system_clock
,
Duration
>
type
;
bool
load
(
handle
src
,
bool
)
{
using
namespace
std
::
chrono
;
// Lazy initialise the PyDateTime import
if
(
!
PyDateTimeAPI
)
{
PyDateTime_IMPORT
;
}
if
(
!
src
)
return
false
;
if
(
PyDateTime_Check
(
src
.
ptr
()))
{
std
::
tm
cal
;
cal
.
tm_sec
=
PyDateTime_DATE_GET_SECOND
(
src
.
ptr
());
cal
.
tm_min
=
PyDateTime_DATE_GET_MINUTE
(
src
.
ptr
());
cal
.
tm_hour
=
PyDateTime_DATE_GET_HOUR
(
src
.
ptr
());
cal
.
tm_mday
=
PyDateTime_GET_DAY
(
src
.
ptr
());
cal
.
tm_mon
=
PyDateTime_GET_MONTH
(
src
.
ptr
())
-
1
;
cal
.
tm_year
=
PyDateTime_GET_YEAR
(
src
.
ptr
())
-
1900
;
cal
.
tm_isdst
=
-
1
;
value
=
system_clock
::
from_time_t
(
std
::
mktime
(
&
cal
))
+
microseconds
(
PyDateTime_DATE_GET_MICROSECOND
(
src
.
ptr
()));
return
true
;
}
else
return
false
;
}
static
handle
cast
(
const
std
::
chrono
::
time_point
<
std
::
chrono
::
system_clock
,
Duration
>
&
src
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
using
namespace
std
::
chrono
;
// Lazy initialise the PyDateTime import
if
(
!
PyDateTimeAPI
)
{
PyDateTime_IMPORT
;
}
std
::
time_t
tt
=
system_clock
::
to_time_t
(
src
);
// this function uses static memory so it's best to copy it out asap just in case
// otherwise other code that is using localtime may break this (not just python code)
std
::
tm
localtime
=
*
std
::
localtime
(
&
tt
);
// Declare these special duration types so the conversions happen with the correct primitive types (int)
using
us_t
=
duration
<
int
,
std
::
micro
>
;
return
PyDateTime_FromDateAndTime
(
localtime
.
tm_year
+
1900
,
localtime
.
tm_mon
+
1
,
localtime
.
tm_mday
,
localtime
.
tm_hour
,
localtime
.
tm_min
,
localtime
.
tm_sec
,
(
duration_cast
<
us_t
>
(
src
.
time_since_epoch
()
%
seconds
(
1
))).
count
());
}
PYBIND11_TYPE_CASTER
(
type
,
_
(
"datetime.datetime"
));
};
// Other clocks that are not the system clock are not measured as datetime.datetime objects
// since they are not measured on calendar time. So instead we just make them timedeltas
// Or if they have passed us a time as a float we convert that
template
<
typename
Clock
,
typename
Duration
>
class
type_caster
<
std
::
chrono
::
time_point
<
Clock
,
Duration
>>
:
public
duration_caster
<
std
::
chrono
::
time_point
<
Clock
,
Duration
>>
{
};
template
<
typename
Rep
,
typename
Period
>
class
type_caster
<
std
::
chrono
::
duration
<
Rep
,
Period
>>
:
public
duration_caster
<
std
::
chrono
::
duration
<
Rep
,
Period
>>
{
};
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
pybind11
)
ppocr/postprocess/lanms/include/pybind11/class_support.h
0 → 100644
View file @
2401626a
/*
pybind11/class_support.h: Python C API implementation details for py::class_
Copyright (c) 2017 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 "attr.h"
NAMESPACE_BEGIN
(
pybind11
)
NAMESPACE_BEGIN
(
detail
)
inline
PyTypeObject
*
type_incref
(
PyTypeObject
*
type
)
{
Py_INCREF
(
type
);
return
type
;
}
#if !defined(PYPY_VERSION)
/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance.
extern
"C"
inline
PyObject
*
pybind11_static_get
(
PyObject
*
self
,
PyObject
*
/*ob*/
,
PyObject
*
cls
)
{
return
PyProperty_Type
.
tp_descr_get
(
self
,
cls
,
cls
);
}
/// `pybind11_static_property.__set__()`: Just like the above `__get__()`.
extern
"C"
inline
int
pybind11_static_set
(
PyObject
*
self
,
PyObject
*
obj
,
PyObject
*
value
)
{
PyObject
*
cls
=
PyType_Check
(
obj
)
?
obj
:
(
PyObject
*
)
Py_TYPE
(
obj
);
return
PyProperty_Type
.
tp_descr_set
(
self
,
cls
,
value
);
}
/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()`
methods are modified to always use the object type instead of a concrete instance.
Return value: New reference. */
inline
PyTypeObject
*
make_static_property_type
()
{
constexpr
auto
*
name
=
"pybind11_static_property"
;
auto
name_obj
=
reinterpret_steal
<
object
>
(
PYBIND11_FROM_STRING
(
name
));
/* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
auto
heap_type
=
(
PyHeapTypeObject
*
)
PyType_Type
.
tp_alloc
(
&
PyType_Type
,
0
);
if
(
!
heap_type
)
pybind11_fail
(
"make_static_property_type(): error allocating type!"
);
heap_type
->
ht_name
=
name_obj
.
inc_ref
().
ptr
();
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
heap_type
->
ht_qualname
=
name_obj
.
inc_ref
().
ptr
();
#endif
auto
type
=
&
heap_type
->
ht_type
;
type
->
tp_name
=
name
;
type
->
tp_base
=
type_incref
(
&
PyProperty_Type
);
type
->
tp_flags
=
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HEAPTYPE
;
type
->
tp_descr_get
=
pybind11_static_get
;
type
->
tp_descr_set
=
pybind11_static_set
;
if
(
PyType_Ready
(
type
)
<
0
)
pybind11_fail
(
"make_static_property_type(): failure in PyType_Ready()!"
);
setattr
((
PyObject
*
)
type
,
"__module__"
,
str
(
"pybind11_builtins"
));
return
type
;
}
#else // PYPY
/** PyPy has some issues with the above C API, so we evaluate Python code instead.
This function will only be called once so performance isn't really a concern.
Return value: New reference. */
inline
PyTypeObject
*
make_static_property_type
()
{
auto
d
=
dict
();
PyObject
*
result
=
PyRun_String
(
R"(\
class pybind11_static_property(property):
def __get__(self, obj, cls):
return property.__get__(self, cls, cls)
def __set__(self, obj, value):
cls = obj if isinstance(obj, type) else type(obj)
property.__set__(self, cls, value)
)"
,
Py_file_input
,
d
.
ptr
(),
d
.
ptr
()
);
if
(
result
==
nullptr
)
throw
error_already_set
();
Py_DECREF
(
result
);
return
(
PyTypeObject
*
)
d
[
"pybind11_static_property"
].
cast
<
object
>
().
release
().
ptr
();
}
#endif // PYPY
/** Types with static properties need to handle `Type.static_prop = x` in a specific way.
By default, Python replaces the `static_property` itself, but for wrapped C++ types
we need to call `static_property.__set__()` in order to propagate the new value to
the underlying C++ data structure. */
extern
"C"
inline
int
pybind11_meta_setattro
(
PyObject
*
obj
,
PyObject
*
name
,
PyObject
*
value
)
{
// Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
PyObject
*
descr
=
_PyType_Lookup
((
PyTypeObject
*
)
obj
,
name
);
// The following assignment combinations are possible:
// 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)`
// 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop`
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
const
auto
static_prop
=
(
PyObject
*
)
get_internals
().
static_property_type
;
const
auto
call_descr_set
=
descr
&&
PyObject_IsInstance
(
descr
,
static_prop
)
&&
!
PyObject_IsInstance
(
value
,
static_prop
);
if
(
call_descr_set
)
{
// Call `static_property.__set__()` instead of replacing the `static_property`.
#if !defined(PYPY_VERSION)
return
Py_TYPE
(
descr
)
->
tp_descr_set
(
descr
,
obj
,
value
);
#else
if
(
PyObject
*
result
=
PyObject_CallMethod
(
descr
,
"__set__"
,
"OO"
,
obj
,
value
))
{
Py_DECREF
(
result
);
return
0
;
}
else
{
return
-
1
;
}
#endif
}
else
{
// Replace existing attribute.
return
PyType_Type
.
tp_setattro
(
obj
,
name
,
value
);
}
}
#if PY_MAJOR_VERSION >= 3
/**
* Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing
* methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function,
* when called on a class, or a PyMethod, when called on an instance. Override that behaviour here
* to do a special case bypass for PyInstanceMethod_Types.
*/
extern
"C"
inline
PyObject
*
pybind11_meta_getattro
(
PyObject
*
obj
,
PyObject
*
name
)
{
PyObject
*
descr
=
_PyType_Lookup
((
PyTypeObject
*
)
obj
,
name
);
if
(
descr
&&
PyInstanceMethod_Check
(
descr
))
{
Py_INCREF
(
descr
);
return
descr
;
}
else
{
return
PyType_Type
.
tp_getattro
(
obj
,
name
);
}
}
#endif
/** This metaclass is assigned by default to all pybind11 types and is required in order
for static properties to function correctly. Users may override this using `py::metaclass`.
Return value: New reference. */
inline
PyTypeObject
*
make_default_metaclass
()
{
constexpr
auto
*
name
=
"pybind11_type"
;
auto
name_obj
=
reinterpret_steal
<
object
>
(
PYBIND11_FROM_STRING
(
name
));
/* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
auto
heap_type
=
(
PyHeapTypeObject
*
)
PyType_Type
.
tp_alloc
(
&
PyType_Type
,
0
);
if
(
!
heap_type
)
pybind11_fail
(
"make_default_metaclass(): error allocating metaclass!"
);
heap_type
->
ht_name
=
name_obj
.
inc_ref
().
ptr
();
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
heap_type
->
ht_qualname
=
name_obj
.
inc_ref
().
ptr
();
#endif
auto
type
=
&
heap_type
->
ht_type
;
type
->
tp_name
=
name
;
type
->
tp_base
=
type_incref
(
&
PyType_Type
);
type
->
tp_flags
=
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HEAPTYPE
;
type
->
tp_setattro
=
pybind11_meta_setattro
;
#if PY_MAJOR_VERSION >= 3
type
->
tp_getattro
=
pybind11_meta_getattro
;
#endif
if
(
PyType_Ready
(
type
)
<
0
)
pybind11_fail
(
"make_default_metaclass(): failure in PyType_Ready()!"
);
setattr
((
PyObject
*
)
type
,
"__module__"
,
str
(
"pybind11_builtins"
));
return
type
;
}
/// For multiple inheritance types we need to recursively register/deregister base pointers for any
/// base classes with pointers that are difference from the instance value pointer so that we can
/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs.
inline
void
traverse_offset_bases
(
void
*
valueptr
,
const
detail
::
type_info
*
tinfo
,
instance
*
self
,
bool
(
*
f
)(
void
*
/*parentptr*/
,
instance
*
/*self*/
))
{
for
(
handle
h
:
reinterpret_borrow
<
tuple
>
(
tinfo
->
type
->
tp_bases
))
{
if
(
auto
parent_tinfo
=
get_type_info
((
PyTypeObject
*
)
h
.
ptr
()))
{
for
(
auto
&
c
:
parent_tinfo
->
implicit_casts
)
{
if
(
c
.
first
==
tinfo
->
cpptype
)
{
auto
*
parentptr
=
c
.
second
(
valueptr
);
if
(
parentptr
!=
valueptr
)
f
(
parentptr
,
self
);
traverse_offset_bases
(
parentptr
,
parent_tinfo
,
self
,
f
);
break
;
}
}
}
}
}
inline
bool
register_instance_impl
(
void
*
ptr
,
instance
*
self
)
{
get_internals
().
registered_instances
.
emplace
(
ptr
,
self
);
return
true
;
// unused, but gives the same signature as the deregister func
}
inline
bool
deregister_instance_impl
(
void
*
ptr
,
instance
*
self
)
{
auto
&
registered_instances
=
get_internals
().
registered_instances
;
auto
range
=
registered_instances
.
equal_range
(
ptr
);
for
(
auto
it
=
range
.
first
;
it
!=
range
.
second
;
++
it
)
{
if
(
Py_TYPE
(
self
)
==
Py_TYPE
(
it
->
second
))
{
registered_instances
.
erase
(
it
);
return
true
;
}
}
return
false
;
}
inline
void
register_instance
(
instance
*
self
,
void
*
valptr
,
const
type_info
*
tinfo
)
{
register_instance_impl
(
valptr
,
self
);
if
(
!
tinfo
->
simple_ancestors
)
traverse_offset_bases
(
valptr
,
tinfo
,
self
,
register_instance_impl
);
}
inline
bool
deregister_instance
(
instance
*
self
,
void
*
valptr
,
const
type_info
*
tinfo
)
{
bool
ret
=
deregister_instance_impl
(
valptr
,
self
);
if
(
!
tinfo
->
simple_ancestors
)
traverse_offset_bases
(
valptr
,
tinfo
,
self
,
deregister_instance_impl
);
return
ret
;
}
/// Instance creation function for all pybind11 types. It only allocates space for the C++ object
/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't
/// call the constructor -- an `__init__` function must do that (followed by an `init_instance`
/// to set up the holder and register the instance).
inline
PyObject
*
make_new_instance
(
PyTypeObject
*
type
,
bool
allocate_value
/*= true (in cast.h)*/
)
{
#if defined(PYPY_VERSION)
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
// object is a a plain Python type (i.e. not derived from an extension type). Fix it.
ssize_t
instance_size
=
static_cast
<
ssize_t
>
(
sizeof
(
instance
));
if
(
type
->
tp_basicsize
<
instance_size
)
{
type
->
tp_basicsize
=
instance_size
;
}
#endif
PyObject
*
self
=
type
->
tp_alloc
(
type
,
0
);
auto
inst
=
reinterpret_cast
<
instance
*>
(
self
);
// Allocate the value/holder internals:
inst
->
allocate_layout
();
inst
->
owned
=
true
;
// Allocate (if requested) the value pointers; otherwise leave them as nullptr
if
(
allocate_value
)
{
for
(
auto
&
v_h
:
values_and_holders
(
inst
))
{
void
*&
vptr
=
v_h
.
value_ptr
();
vptr
=
v_h
.
type
->
operator_new
(
v_h
.
type
->
type_size
);
}
}
return
self
;
}
/// Instance creation function for all pybind11 types. It only allocates space for the
/// C++ object, but doesn't call the constructor -- an `__init__` function must do that.
extern
"C"
inline
PyObject
*
pybind11_object_new
(
PyTypeObject
*
type
,
PyObject
*
,
PyObject
*
)
{
return
make_new_instance
(
type
);
}
/// An `__init__` function constructs the C++ object. Users should provide at least one
/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the
/// following default function will be used which simply throws an exception.
extern
"C"
inline
int
pybind11_object_init
(
PyObject
*
self
,
PyObject
*
,
PyObject
*
)
{
PyTypeObject
*
type
=
Py_TYPE
(
self
);
std
::
string
msg
;
#if defined(PYPY_VERSION)
msg
+=
handle
((
PyObject
*
)
type
).
attr
(
"__module__"
).
cast
<
std
::
string
>
()
+
"."
;
#endif
msg
+=
type
->
tp_name
;
msg
+=
": No constructor defined!"
;
PyErr_SetString
(
PyExc_TypeError
,
msg
.
c_str
());
return
-
1
;
}
inline
void
add_patient
(
PyObject
*
nurse
,
PyObject
*
patient
)
{
auto
&
internals
=
get_internals
();
auto
instance
=
reinterpret_cast
<
detail
::
instance
*>
(
nurse
);
instance
->
has_patients
=
true
;
Py_INCREF
(
patient
);
internals
.
patients
[
nurse
].
push_back
(
patient
);
}
inline
void
clear_patients
(
PyObject
*
self
)
{
auto
instance
=
reinterpret_cast
<
detail
::
instance
*>
(
self
);
auto
&
internals
=
get_internals
();
auto
pos
=
internals
.
patients
.
find
(
self
);
assert
(
pos
!=
internals
.
patients
.
end
());
// Clearing the patients can cause more Python code to run, which
// can invalidate the iterator. Extract the vector of patients
// from the unordered_map first.
auto
patients
=
std
::
move
(
pos
->
second
);
internals
.
patients
.
erase
(
pos
);
instance
->
has_patients
=
false
;
for
(
PyObject
*&
patient
:
patients
)
Py_CLEAR
(
patient
);
}
/// Clears all internal data from the instance and removes it from registered instances in
/// preparation for deallocation.
inline
void
clear_instance
(
PyObject
*
self
)
{
auto
instance
=
reinterpret_cast
<
detail
::
instance
*>
(
self
);
// Deallocate any values/holders, if present:
for
(
auto
&
v_h
:
values_and_holders
(
instance
))
{
if
(
v_h
)
{
// We have to deregister before we call dealloc because, for virtual MI types, we still
// need to be able to get the parent pointers.
if
(
v_h
.
instance_registered
()
&&
!
deregister_instance
(
instance
,
v_h
.
value_ptr
(),
v_h
.
type
))
pybind11_fail
(
"pybind11_object_dealloc(): Tried to deallocate unregistered instance!"
);
if
(
instance
->
owned
||
v_h
.
holder_constructed
())
v_h
.
type
->
dealloc
(
v_h
);
}
}
// Deallocate the value/holder layout internals:
instance
->
deallocate_layout
();
if
(
instance
->
weakrefs
)
PyObject_ClearWeakRefs
(
self
);
PyObject
**
dict_ptr
=
_PyObject_GetDictPtr
(
self
);
if
(
dict_ptr
)
Py_CLEAR
(
*
dict_ptr
);
if
(
instance
->
has_patients
)
clear_patients
(
self
);
}
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
extern
"C"
inline
void
pybind11_object_dealloc
(
PyObject
*
self
)
{
clear_instance
(
self
);
Py_TYPE
(
self
)
->
tp_free
(
self
);
}
/** Create the type which can be used as a common base for all classes. This is
needed in order to satisfy Python's requirements for multiple inheritance.
Return value: New reference. */
inline
PyObject
*
make_object_base_type
(
PyTypeObject
*
metaclass
)
{
constexpr
auto
*
name
=
"pybind11_object"
;
auto
name_obj
=
reinterpret_steal
<
object
>
(
PYBIND11_FROM_STRING
(
name
));
/* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
auto
heap_type
=
(
PyHeapTypeObject
*
)
metaclass
->
tp_alloc
(
metaclass
,
0
);
if
(
!
heap_type
)
pybind11_fail
(
"make_object_base_type(): error allocating type!"
);
heap_type
->
ht_name
=
name_obj
.
inc_ref
().
ptr
();
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
heap_type
->
ht_qualname
=
name_obj
.
inc_ref
().
ptr
();
#endif
auto
type
=
&
heap_type
->
ht_type
;
type
->
tp_name
=
name
;
type
->
tp_base
=
type_incref
(
&
PyBaseObject_Type
);
type
->
tp_basicsize
=
static_cast
<
ssize_t
>
(
sizeof
(
instance
));
type
->
tp_flags
=
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HEAPTYPE
;
type
->
tp_new
=
pybind11_object_new
;
type
->
tp_init
=
pybind11_object_init
;
type
->
tp_dealloc
=
pybind11_object_dealloc
;
/* Support weak references (needed for the keep_alive feature) */
type
->
tp_weaklistoffset
=
offsetof
(
instance
,
weakrefs
);
if
(
PyType_Ready
(
type
)
<
0
)
pybind11_fail
(
"PyType_Ready failed in make_object_base_type():"
+
error_string
());
setattr
((
PyObject
*
)
type
,
"__module__"
,
str
(
"pybind11_builtins"
));
assert
(
!
PyType_HasFeature
(
type
,
Py_TPFLAGS_HAVE_GC
));
return
(
PyObject
*
)
heap_type
;
}
/// dynamic_attr: Support for `d = instance.__dict__`.
extern
"C"
inline
PyObject
*
pybind11_get_dict
(
PyObject
*
self
,
void
*
)
{
PyObject
*&
dict
=
*
_PyObject_GetDictPtr
(
self
);
if
(
!
dict
)
dict
=
PyDict_New
();
Py_XINCREF
(
dict
);
return
dict
;
}
/// dynamic_attr: Support for `instance.__dict__ = dict()`.
extern
"C"
inline
int
pybind11_set_dict
(
PyObject
*
self
,
PyObject
*
new_dict
,
void
*
)
{
if
(
!
PyDict_Check
(
new_dict
))
{
PyErr_Format
(
PyExc_TypeError
,
"__dict__ must be set to a dictionary, not a '%.200s'"
,
Py_TYPE
(
new_dict
)
->
tp_name
);
return
-
1
;
}
PyObject
*&
dict
=
*
_PyObject_GetDictPtr
(
self
);
Py_INCREF
(
new_dict
);
Py_CLEAR
(
dict
);
dict
=
new_dict
;
return
0
;
}
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
extern
"C"
inline
int
pybind11_traverse
(
PyObject
*
self
,
visitproc
visit
,
void
*
arg
)
{
PyObject
*&
dict
=
*
_PyObject_GetDictPtr
(
self
);
Py_VISIT
(
dict
);
return
0
;
}
/// dynamic_attr: Allow the GC to clear the dictionary.
extern
"C"
inline
int
pybind11_clear
(
PyObject
*
self
)
{
PyObject
*&
dict
=
*
_PyObject_GetDictPtr
(
self
);
Py_CLEAR
(
dict
);
return
0
;
}
/// Give instances of this type a `__dict__` and opt into garbage collection.
inline
void
enable_dynamic_attributes
(
PyHeapTypeObject
*
heap_type
)
{
auto
type
=
&
heap_type
->
ht_type
;
#if defined(PYPY_VERSION)
pybind11_fail
(
std
::
string
(
type
->
tp_name
)
+
": dynamic attributes are "
"currently not supported in "
"conjunction with PyPy!"
);
#endif
type
->
tp_flags
|=
Py_TPFLAGS_HAVE_GC
;
type
->
tp_dictoffset
=
type
->
tp_basicsize
;
// place dict at the end
type
->
tp_basicsize
+=
(
ssize_t
)
sizeof
(
PyObject
*
);
// and allocate enough space for it
type
->
tp_traverse
=
pybind11_traverse
;
type
->
tp_clear
=
pybind11_clear
;
static
PyGetSetDef
getset
[]
=
{
{
const_cast
<
char
*>
(
"__dict__"
),
pybind11_get_dict
,
pybind11_set_dict
,
nullptr
,
nullptr
},
{
nullptr
,
nullptr
,
nullptr
,
nullptr
,
nullptr
}
};
type
->
tp_getset
=
getset
;
}
/// buffer_protocol: Fill in the view as specified by flags.
extern
"C"
inline
int
pybind11_getbuffer
(
PyObject
*
obj
,
Py_buffer
*
view
,
int
flags
)
{
// Look for a `get_buffer` implementation in this type's info or any bases (following MRO).
type_info
*
tinfo
=
nullptr
;
for
(
auto
type
:
reinterpret_borrow
<
tuple
>
(
Py_TYPE
(
obj
)
->
tp_mro
))
{
tinfo
=
get_type_info
((
PyTypeObject
*
)
type
.
ptr
());
if
(
tinfo
&&
tinfo
->
get_buffer
)
break
;
}
if
(
view
==
nullptr
||
obj
==
nullptr
||
!
tinfo
||
!
tinfo
->
get_buffer
)
{
if
(
view
)
view
->
obj
=
nullptr
;
PyErr_SetString
(
PyExc_BufferError
,
"pybind11_getbuffer(): Internal error"
);
return
-
1
;
}
std
::
memset
(
view
,
0
,
sizeof
(
Py_buffer
));
buffer_info
*
info
=
tinfo
->
get_buffer
(
obj
,
tinfo
->
get_buffer_data
);
view
->
obj
=
obj
;
view
->
ndim
=
1
;
view
->
internal
=
info
;
view
->
buf
=
info
->
ptr
;
view
->
itemsize
=
info
->
itemsize
;
view
->
len
=
view
->
itemsize
;
for
(
auto
s
:
info
->
shape
)
view
->
len
*=
s
;
if
((
flags
&
PyBUF_FORMAT
)
==
PyBUF_FORMAT
)
view
->
format
=
const_cast
<
char
*>
(
info
->
format
.
c_str
());
if
((
flags
&
PyBUF_STRIDES
)
==
PyBUF_STRIDES
)
{
view
->
ndim
=
(
int
)
info
->
ndim
;
view
->
strides
=
&
info
->
strides
[
0
];
view
->
shape
=
&
info
->
shape
[
0
];
}
Py_INCREF
(
view
->
obj
);
return
0
;
}
/// buffer_protocol: Release the resources of the buffer.
extern
"C"
inline
void
pybind11_releasebuffer
(
PyObject
*
,
Py_buffer
*
view
)
{
delete
(
buffer_info
*
)
view
->
internal
;
}
/// Give this type a buffer interface.
inline
void
enable_buffer_protocol
(
PyHeapTypeObject
*
heap_type
)
{
heap_type
->
ht_type
.
tp_as_buffer
=
&
heap_type
->
as_buffer
;
#if PY_MAJOR_VERSION < 3
heap_type
->
ht_type
.
tp_flags
|=
Py_TPFLAGS_HAVE_NEWBUFFER
;
#endif
heap_type
->
as_buffer
.
bf_getbuffer
=
pybind11_getbuffer
;
heap_type
->
as_buffer
.
bf_releasebuffer
=
pybind11_releasebuffer
;
}
/** Create a brand new Python type according to the `type_record` specification.
Return value: New reference. */
inline
PyObject
*
make_new_python_type
(
const
type_record
&
rec
)
{
auto
name
=
reinterpret_steal
<
object
>
(
PYBIND11_FROM_STRING
(
rec
.
name
));
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
auto
ht_qualname
=
name
;
if
(
rec
.
scope
&&
hasattr
(
rec
.
scope
,
"__qualname__"
))
{
ht_qualname
=
reinterpret_steal
<
object
>
(
PyUnicode_FromFormat
(
"%U.%U"
,
rec
.
scope
.
attr
(
"__qualname__"
).
ptr
(),
name
.
ptr
()));
}
#endif
object
module
;
if
(
rec
.
scope
)
{
if
(
hasattr
(
rec
.
scope
,
"__module__"
))
module
=
rec
.
scope
.
attr
(
"__module__"
);
else
if
(
hasattr
(
rec
.
scope
,
"__name__"
))
module
=
rec
.
scope
.
attr
(
"__name__"
);
}
#if !defined(PYPY_VERSION)
const
auto
full_name
=
module
?
str
(
module
).
cast
<
std
::
string
>
()
+
"."
+
rec
.
name
:
std
::
string
(
rec
.
name
);
#else
const
auto
full_name
=
std
::
string
(
rec
.
name
);
#endif
char
*
tp_doc
=
nullptr
;
if
(
rec
.
doc
&&
options
::
show_user_defined_docstrings
())
{
/* Allocate memory for docstring (using PyObject_MALLOC, since
Python will free this later on) */
size_t
size
=
strlen
(
rec
.
doc
)
+
1
;
tp_doc
=
(
char
*
)
PyObject_MALLOC
(
size
);
memcpy
((
void
*
)
tp_doc
,
rec
.
doc
,
size
);
}
auto
&
internals
=
get_internals
();
auto
bases
=
tuple
(
rec
.
bases
);
auto
base
=
(
bases
.
size
()
==
0
)
?
internals
.
instance_base
:
bases
[
0
].
ptr
();
/* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
auto
metaclass
=
rec
.
metaclass
.
ptr
()
?
(
PyTypeObject
*
)
rec
.
metaclass
.
ptr
()
:
internals
.
default_metaclass
;
auto
heap_type
=
(
PyHeapTypeObject
*
)
metaclass
->
tp_alloc
(
metaclass
,
0
);
if
(
!
heap_type
)
pybind11_fail
(
std
::
string
(
rec
.
name
)
+
": Unable to create type object!"
);
heap_type
->
ht_name
=
name
.
release
().
ptr
();
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
heap_type
->
ht_qualname
=
ht_qualname
.
release
().
ptr
();
#endif
auto
type
=
&
heap_type
->
ht_type
;
type
->
tp_name
=
strdup
(
full_name
.
c_str
());
type
->
tp_doc
=
tp_doc
;
type
->
tp_base
=
type_incref
((
PyTypeObject
*
)
base
);
type
->
tp_basicsize
=
static_cast
<
ssize_t
>
(
sizeof
(
instance
));
if
(
bases
.
size
()
>
0
)
type
->
tp_bases
=
bases
.
release
().
ptr
();
/* Don't inherit base __init__ */
type
->
tp_init
=
pybind11_object_init
;
/* Supported protocols */
type
->
tp_as_number
=
&
heap_type
->
as_number
;
type
->
tp_as_sequence
=
&
heap_type
->
as_sequence
;
type
->
tp_as_mapping
=
&
heap_type
->
as_mapping
;
/* Flags */
type
->
tp_flags
|=
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HEAPTYPE
;
#if PY_MAJOR_VERSION < 3
type
->
tp_flags
|=
Py_TPFLAGS_CHECKTYPES
;
#endif
if
(
rec
.
dynamic_attr
)
enable_dynamic_attributes
(
heap_type
);
if
(
rec
.
buffer_protocol
)
enable_buffer_protocol
(
heap_type
);
if
(
PyType_Ready
(
type
)
<
0
)
pybind11_fail
(
std
::
string
(
rec
.
name
)
+
": PyType_Ready failed ("
+
error_string
()
+
")!"
);
assert
(
rec
.
dynamic_attr
?
PyType_HasFeature
(
type
,
Py_TPFLAGS_HAVE_GC
)
:
!
PyType_HasFeature
(
type
,
Py_TPFLAGS_HAVE_GC
));
/* Register type with the parent scope */
if
(
rec
.
scope
)
setattr
(
rec
.
scope
,
rec
.
name
,
(
PyObject
*
)
type
);
if
(
module
)
// Needed by pydoc
setattr
((
PyObject
*
)
type
,
"__module__"
,
module
);
return
(
PyObject
*
)
type
;
}
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
pybind11
)
ppocr/postprocess/lanms/include/pybind11/common.h
0 → 100644
View file @
2401626a
/*
pybind11/common.h -- Basic macros
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
#if !defined(NAMESPACE_BEGIN)
# define NAMESPACE_BEGIN(name) namespace name {
#endif
#if !defined(NAMESPACE_END)
# define NAMESPACE_END(name) }
#endif
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# if __cplusplus >= 201402L
# define PYBIND11_CPP14
# if __cplusplus > 201402L
/* Temporary: should be updated to >= the final C++17 value once known */
# define PYBIND11_CPP17
# endif
# endif
#elif defined(_MSC_VER)
// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented)
# if _MSVC_LANG >= 201402L
# define PYBIND11_CPP14
# if _MSVC_LANG > 201402L && _MSC_VER >= 1910
# define PYBIND11_CPP17
# endif
# endif
#endif
// Compiler version assertions
#if defined(__INTEL_COMPILER)
# if __INTEL_COMPILER < 1500
# error pybind11 requires Intel C++ compiler v15 or newer
# endif
#elif defined(__clang__) && !defined(__apple_build_version__)
# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3)
# error pybind11 requires clang 3.3 or newer
# endif
#elif defined(__clang__)
// Apple changes clang version macros to its Xcode version; the first Xcode release based on
// (upstream) clang 3.3 was Xcode 5:
# if __clang_major__ < 5
# error pybind11 requires Xcode/clang 5.0 or newer
# endif
#elif defined(__GNUG__)
# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
# error pybind11 requires gcc 4.8 or newer
# endif
#elif defined(_MSC_VER)
// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features
// (e.g. std::negation) added in 2015u3:
# if _MSC_FULL_VER < 190024210
# error pybind11 requires MSVC 2015 update 3 or newer
# endif
#endif
#if !defined(PYBIND11_EXPORT)
# if defined(WIN32) || defined(_WIN32)
# define PYBIND11_EXPORT __declspec(dllexport)
# else
# define PYBIND11_EXPORT __attribute__ ((visibility("default")))
# endif
#endif
#if defined(_MSC_VER)
# define PYBIND11_NOINLINE __declspec(noinline)
#else
# define PYBIND11_NOINLINE __attribute__ ((noinline))
#endif
#if defined(PYBIND11_CPP14)
# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]]
#else
# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason)))
#endif
#define PYBIND11_VERSION_MAJOR 2
#define PYBIND11_VERSION_MINOR 2
#define PYBIND11_VERSION_PATCH dev0
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER)
# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4)
# define HAVE_ROUND 1
# endif
# pragma warning(push)
# pragma warning(disable: 4510 4610 4512 4005)
# if defined(_DEBUG)
# define PYBIND11_DEBUG_MARKER
# undef _DEBUG
# endif
#endif
#include <Python.h>
#include <frameobject.h>
#include <pythread.h>
#if defined(_WIN32) && (defined(min) || defined(max))
# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows
#endif
#if defined(isalnum)
# undef isalnum
# undef isalpha
# undef islower
# undef isspace
# undef isupper
# undef tolower
# undef toupper
#endif
#if defined(_MSC_VER)
# if defined(PYBIND11_DEBUG_MARKER)
# define _DEBUG
# undef PYBIND11_DEBUG_MARKER
# endif
# pragma warning(pop)
#endif
#include <cstddef>
#include <cstring>
#include <forward_list>
#include <vector>
#include <string>
#include <stdexcept>
#include <unordered_set>
#include <unordered_map>
#include <memory>
#include <typeindex>
#include <type_traits>
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
#define PYBIND11_BYTES_CHECK PyBytes_Check
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
#define PYBIND11_BYTES_SIZE PyBytes_Size
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
#define PYBIND11_BYTES_NAME "bytes"
#define PYBIND11_STRING_NAME "str"
#define PYBIND11_SLICE_OBJECT PyObject
#define PYBIND11_FROM_STRING PyUnicode_FromString
#define PYBIND11_STR_TYPE ::pybind11::str
#define PYBIND11_BOOL_ATTR "__bool__"
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
#define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
#else
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION
#define PYBIND11_BYTES_CHECK PyString_Check
#define PYBIND11_BYTES_FROM_STRING PyString_FromString
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize
#define PYBIND11_BYTES_AS_STRING PyString_AsString
#define PYBIND11_BYTES_SIZE PyString_Size
#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
#define PYBIND11_BYTES_NAME "str"
#define PYBIND11_STRING_NAME "unicode"
#define PYBIND11_SLICE_OBJECT PySliceObject
#define PYBIND11_FROM_STRING PyString_FromString
#define PYBIND11_STR_TYPE ::pybind11::bytes
#define PYBIND11_BOOL_ATTR "__nonzero__"
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero)
#define PYBIND11_PLUGIN_IMPL(name) \
static PyObject *pybind11_init_wrapper(); \
extern "C" PYBIND11_EXPORT void init##name() { \
(void)pybind11_init_wrapper(); \
} \
PyObject *pybind11_init_wrapper()
#endif
#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200
extern
"C"
{
struct
_Py_atomic_address
{
void
*
value
;
};
PyAPI_DATA
(
_Py_atomic_address
)
_PyThreadState_Current
;
}
#endif
#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code
#define PYBIND11_STRINGIFY(x) #x
#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
#define PYBIND11_INTERNALS_ID "__pybind11_" \
PYBIND11_TOSTRING(PYBIND11_VERSION_MAJOR) "_" PYBIND11_TOSTRING(PYBIND11_VERSION_MINOR) "__"
/** \rst
***Deprecated in favor of PYBIND11_MODULE***
This macro creates the entry point that will be invoked when the Python interpreter
imports a plugin library. Please create a `module` in the function body and return
the pointer to its underlying Python object at the end.
.. code-block:: cpp
PYBIND11_PLUGIN(example) {
pybind11::module m("example", "pybind11 example plugin");
/// Set up bindings here
return m.ptr();
}
\endrst */
#define PYBIND11_PLUGIN(name) \
PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \
static PyObject *pybind11_init(); \
PYBIND11_PLUGIN_IMPL(name) { \
int major, minor; \
if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
return nullptr; \
} else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
PyErr_Format(PyExc_ImportError, \
"Python version mismatch: module was compiled for " \
"version %i.%i, while the interpreter is running " \
"version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
major, minor); \
return nullptr; \
} \
try { \
return pybind11_init(); \
} catch (pybind11::error_already_set &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} \
} \
PyObject *pybind11_init()
/** \rst
This macro creates the entry point that will be invoked when the Python interpreter
imports an extension module. The module name is given as the fist argument and it
should not be in quotes. The second macro argument defines a variable of type
`py::module` which can be used to initialize the module.
.. code-block:: cpp
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example module";
// Add bindings here
m.def("foo", []() {
return "Hello, World!";
});
}
\endrst */
#define PYBIND11_MODULE(name, variable) \
static void pybind11_init_##name(pybind11::module &); \
PYBIND11_PLUGIN_IMPL(name) { \
int major, minor; \
if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
return nullptr; \
} else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
PyErr_Format(PyExc_ImportError, \
"Python version mismatch: module was compiled for " \
"version %i.%i, while the interpreter is running " \
"version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
major, minor); \
return nullptr; \
} \
auto m = pybind11::module(#name); \
try { \
pybind11_init_##name(m); \
return m.ptr(); \
} catch (pybind11::error_already_set &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} \
} \
void pybind11_init_##name(pybind11::module &variable)
NAMESPACE_BEGIN
(
pybind11
)
using
ssize_t
=
Py_ssize_t
;
using
size_t
=
std
::
size_t
;
/// Approach used to cast a previously unknown C++ instance into a Python object
enum
class
return_value_policy
:
uint8_t
{
/** This is the default return value policy, which falls back to the policy
return_value_policy::take_ownership when the return value is a pointer.
Otherwise, it uses return_value::move or return_value::copy for rvalue
and lvalue references, respectively. See below for a description of what
all of these different policies do. */
automatic
=
0
,
/** As above, but use policy return_value_policy::reference when the return
value is a pointer. This is the default conversion policy for function
arguments when calling Python functions manually from C++ code (i.e. via
handle::operator()). You probably won't need to use this. */
automatic_reference
,
/** Reference an existing object (i.e. do not create a new copy) and take
ownership. Python will call the destructor and delete operator when the
object’s reference count reaches zero. Undefined behavior ensues when
the C++ side does the same.. */
take_ownership
,
/** Create a new copy of the returned object, which will be owned by
Python. This policy is comparably safe because the lifetimes of the two
instances are decoupled. */
copy
,
/** Use std::move to move the return value contents into a new instance
that will be owned by Python. This policy is comparably safe because the
lifetimes of the two instances (move source and destination) are
decoupled. */
move
,
/** Reference an existing object, but do not take ownership. The C++ side
is responsible for managing the object’s lifetime and deallocating it
when it is no longer used. Warning: undefined behavior will ensue when
the C++ side deletes an object that is still referenced and used by
Python. */
reference
,
/** This policy only applies to methods and properties. It references the
object without taking ownership similar to the above
return_value_policy::reference policy. In contrast to that policy, the
function or property’s implicit this argument (called the parent) is
considered to be the the owner of the return value (the child).
pybind11 then couples the lifetime of the parent to the child via a
reference relationship that ensures that the parent cannot be garbage
collected while Python is still using the child. More advanced
variations of this scheme are also possible using combinations of
return_value_policy::reference and the keep_alive call policy */
reference_internal
};
NAMESPACE_BEGIN
(
detail
)
inline
static
constexpr
int
log2
(
size_t
n
,
int
k
=
0
)
{
return
(
n
<=
1
)
?
k
:
log2
(
n
>>
1
,
k
+
1
);
}
// Returns the size as a multiple of sizeof(void *), rounded up.
inline
static
constexpr
size_t
size_in_ptrs
(
size_t
s
)
{
return
1
+
((
s
-
1
)
>>
log2
(
sizeof
(
void
*
)));
}
/**
* The space to allocate for simple layout instance holders (see below) in multiple of the size of
* a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required
* to holder either a std::unique_ptr or std::shared_ptr (which is almost always
* sizeof(std::shared_ptr<T>)).
*/
constexpr
size_t
instance_simple_holder_in_ptrs
()
{
static_assert
(
sizeof
(
std
::
shared_ptr
<
int
>
)
>=
sizeof
(
std
::
unique_ptr
<
int
>
),
"pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"
);
return
size_in_ptrs
(
sizeof
(
std
::
shared_ptr
<
int
>
));
}
// Forward declarations
struct
type_info
;
struct
value_and_holder
;
/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof')
struct
instance
{
PyObject_HEAD
/// Storage for pointers and holder; see simple_layout, below, for a description
union
{
void
*
simple_value_holder
[
1
+
instance_simple_holder_in_ptrs
()];
struct
{
void
**
values_and_holders
;
uint8_t
*
status
;
}
nonsimple
;
};
/// Weak references (needed for keep alive):
PyObject
*
weakrefs
;
/// If true, the pointer is owned which means we're free to manage it with a holder.
bool
owned
:
1
;
/**
* An instance has two possible value/holder layouts.
*
* Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer
* and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied
* whenever there is no python-side multiple inheritance of bound C++ types *and* the type's
* holder will fit in the default space (which is large enough to hold either a std::unique_ptr
* or std::shared_ptr).
*
* Non-simple layout applies when using custom holders that require more space than `shared_ptr`
* (which is typically the size of two pointers), or when multiple inheritance is used on the
* python side. Non-simple layout allocates the required amount of memory to have multiple
* bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a
* pointer to allocated space of the required space to hold a a sequence of value pointers and
* holders followed `status`, a set of bit flags (1 byte each), i.e.
* [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of
* `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the
* beginning of the [bb...] block (but not independently allocated).
*
* Status bits indicate whether the associated holder is constructed (&
* status_holder_constructed) and whether the value pointer is registered (&
* status_instance_registered) in `registered_instances`.
*/
bool
simple_layout
:
1
;
/// For simple layout, tracks whether the holder has been constructed
bool
simple_holder_constructed
:
1
;
/// For simple layout, tracks whether the instance is registered in `registered_instances`
bool
simple_instance_registered
:
1
;
/// If true, get_internals().patients has an entry for this object
bool
has_patients
:
1
;
/// Initializes all of the above type/values/holders data
void
allocate_layout
();
/// Destroys/deallocates all of the above
void
deallocate_layout
();
/// Returns the value_and_holder wrapper for the given type (or the first, if `find_type`
/// omitted)
value_and_holder
get_value_and_holder
(
const
type_info
*
find_type
=
nullptr
);
/// Bit values for the non-simple status flags
static
constexpr
uint8_t
status_holder_constructed
=
1
;
static
constexpr
uint8_t
status_instance_registered
=
2
;
};
static_assert
(
std
::
is_standard_layout
<
instance
>::
value
,
"Internal error: `pybind11::detail::instance` is not standard layout!"
);
struct
overload_hash
{
inline
size_t
operator
()(
const
std
::
pair
<
const
PyObject
*
,
const
char
*>&
v
)
const
{
size_t
value
=
std
::
hash
<
const
void
*>
()(
v
.
first
);
value
^=
std
::
hash
<
const
void
*>
()(
v
.
second
)
+
0x9e3779b9
+
(
value
<<
6
)
+
(
value
>>
2
);
return
value
;
}
};
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
// other stls, this means `typeid(A)` from one module won't equal `typeid(A)` from another module
// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under
// stdlibc++, this doesn't happen: equality and the type_index hash are based on the type name,
// which works. If not under a known-good stl, provide our own name-based hasher and equality
// functions that use the type name.
#if defined(__GLIBCXX__)
inline
bool
same_type
(
const
std
::
type_info
&
lhs
,
const
std
::
type_info
&
rhs
)
{
return
lhs
==
rhs
;
}
using
type_hash
=
std
::
hash
<
std
::
type_index
>
;
using
type_equal_to
=
std
::
equal_to
<
std
::
type_index
>
;
#else
inline
bool
same_type
(
const
std
::
type_info
&
lhs
,
const
std
::
type_info
&
rhs
)
{
return
lhs
.
name
()
==
rhs
.
name
()
||
std
::
strcmp
(
lhs
.
name
(),
rhs
.
name
())
==
0
;
}
struct
type_hash
{
size_t
operator
()(
const
std
::
type_index
&
t
)
const
{
size_t
hash
=
5381
;
const
char
*
ptr
=
t
.
name
();
while
(
auto
c
=
static_cast
<
unsigned
char
>
(
*
ptr
++
))
hash
=
(
hash
*
33
)
^
c
;
return
hash
;
}
};
struct
type_equal_to
{
bool
operator
()(
const
std
::
type_index
&
lhs
,
const
std
::
type_index
&
rhs
)
const
{
return
lhs
.
name
()
==
rhs
.
name
()
||
std
::
strcmp
(
lhs
.
name
(),
rhs
.
name
())
==
0
;
}
};
#endif
template
<
typename
value_type
>
using
type_map
=
std
::
unordered_map
<
std
::
type_index
,
value_type
,
type_hash
,
type_equal_to
>
;
/// Internal data structure used to track registered instances and types
struct
internals
{
type_map
<
void
*>
registered_types_cpp
;
// std::type_index -> type_info
std
::
unordered_map
<
PyTypeObject
*
,
std
::
vector
<
type_info
*>>
registered_types_py
;
// PyTypeObject* -> base type_info(s)
std
::
unordered_multimap
<
const
void
*
,
instance
*>
registered_instances
;
// void * -> instance*
std
::
unordered_set
<
std
::
pair
<
const
PyObject
*
,
const
char
*>
,
overload_hash
>
inactive_overload_cache
;
type_map
<
std
::
vector
<
bool
(
*
)(
PyObject
*
,
void
*&
)
>>
direct_conversions
;
std
::
unordered_map
<
const
PyObject
*
,
std
::
vector
<
PyObject
*>>
patients
;
std
::
forward_list
<
void
(
*
)
(
std
::
exception_ptr
)
>
registered_exception_translators
;
std
::
unordered_map
<
std
::
string
,
void
*>
shared_data
;
// Custom data to be shared across extensions
std
::
vector
<
PyObject
*>
loader_patient_stack
;
// Used by `loader_life_support`
PyTypeObject
*
static_property_type
;
PyTypeObject
*
default_metaclass
;
PyObject
*
instance_base
;
#if defined(WITH_THREAD)
decltype
(
PyThread_create_key
())
tstate
=
0
;
// Usually an int but a long on Cygwin64 with Python 3.x
PyInterpreterState
*
istate
=
nullptr
;
#endif
};
/// Return a reference to the current 'internals' information
inline
internals
&
get_internals
();
/// from __cpp_future__ import (convenient aliases from C++14/17)
#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910)
using
std
::
enable_if_t
;
using
std
::
conditional_t
;
using
std
::
remove_cv_t
;
using
std
::
remove_reference_t
;
#else
template
<
bool
B
,
typename
T
=
void
>
using
enable_if_t
=
typename
std
::
enable_if
<
B
,
T
>::
type
;
template
<
bool
B
,
typename
T
,
typename
F
>
using
conditional_t
=
typename
std
::
conditional
<
B
,
T
,
F
>::
type
;
template
<
typename
T
>
using
remove_cv_t
=
typename
std
::
remove_cv
<
T
>::
type
;
template
<
typename
T
>
using
remove_reference_t
=
typename
std
::
remove_reference
<
T
>::
type
;
#endif
/// Index sequences
#if defined(PYBIND11_CPP14)
using
std
::
index_sequence
;
using
std
::
make_index_sequence
;
#else
template
<
size_t
...>
struct
index_sequence
{
};
template
<
size_t
N
,
size_t
...
S
>
struct
make_index_sequence_impl
:
make_index_sequence_impl
<
N
-
1
,
N
-
1
,
S
...
>
{
};
template
<
size_t
...
S
>
struct
make_index_sequence_impl
<
0
,
S
...
>
{
typedef
index_sequence
<
S
...
>
type
;
};
template
<
size_t
N
>
using
make_index_sequence
=
typename
make_index_sequence_impl
<
N
>::
type
;
#endif
/// Make an index sequence of the indices of true arguments
template
<
typename
ISeq
,
size_t
,
bool
...>
struct
select_indices_impl
{
using
type
=
ISeq
;
};
template
<
size_t
...
IPrev
,
size_t
I
,
bool
B
,
bool
...
Bs
>
struct
select_indices_impl
<
index_sequence
<
IPrev
...
>
,
I
,
B
,
Bs
...
>
:
select_indices_impl
<
conditional_t
<
B
,
index_sequence
<
IPrev
...,
I
>
,
index_sequence
<
IPrev
...
>>
,
I
+
1
,
Bs
...
>
{};
template
<
bool
...
Bs
>
using
select_indices
=
typename
select_indices_impl
<
index_sequence
<>
,
0
,
Bs
...
>::
type
;
/// Backports of std::bool_constant and std::negation to accomodate older compilers
template
<
bool
B
>
using
bool_constant
=
std
::
integral_constant
<
bool
,
B
>
;
template
<
typename
T
>
struct
negation
:
bool_constant
<!
T
::
value
>
{
};
template
<
typename
...
>
struct
void_t_impl
{
using
type
=
void
;
};
template
<
typename
...
Ts
>
using
void_t
=
typename
void_t_impl
<
Ts
...
>::
type
;
/// Compile-time all/any/none of that check the boolean value of all template types
#ifdef __cpp_fold_expressions
template
<
class
...
Ts
>
using
all_of
=
bool_constant
<
(
Ts
::
value
&&
...)
>
;
template
<
class
...
Ts
>
using
any_of
=
bool_constant
<
(
Ts
::
value
||
...)
>
;
#elif !defined(_MSC_VER)
template
<
bool
...>
struct
bools
{};
template
<
class
...
Ts
>
using
all_of
=
std
::
is_same
<
bools
<
Ts
::
value
...,
true
>
,
bools
<
true
,
Ts
::
value
...
>>
;
template
<
class
...
Ts
>
using
any_of
=
negation
<
all_of
<
negation
<
Ts
>
...
>>
;
#else
// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit
// at a slight loss of compilation efficiency).
template
<
class
...
Ts
>
using
all_of
=
std
::
conjunction
<
Ts
...
>
;
template
<
class
...
Ts
>
using
any_of
=
std
::
disjunction
<
Ts
...
>
;
#endif
template
<
class
...
Ts
>
using
none_of
=
negation
<
any_of
<
Ts
...
>>
;
template
<
class
T
,
template
<
class
>
class
...
Predicates
>
using
satisfies_all_of
=
all_of
<
Predicates
<
T
>
...
>
;
template
<
class
T
,
template
<
class
>
class
...
Predicates
>
using
satisfies_any_of
=
any_of
<
Predicates
<
T
>
...
>
;
template
<
class
T
,
template
<
class
>
class
...
Predicates
>
using
satisfies_none_of
=
none_of
<
Predicates
<
T
>
...
>
;
/// Strip the class from a method type
template
<
typename
T
>
struct
remove_class
{
};
template
<
typename
C
,
typename
R
,
typename
...
A
>
struct
remove_class
<
R
(
C
::*
)(
A
...)
>
{
typedef
R
type
(
A
...);
};
template
<
typename
C
,
typename
R
,
typename
...
A
>
struct
remove_class
<
R
(
C
::*
)(
A
...)
const
>
{
typedef
R
type
(
A
...);
};
/// Helper template to strip away type modifiers
template
<
typename
T
>
struct
intrinsic_type
{
typedef
T
type
;
};
template
<
typename
T
>
struct
intrinsic_type
<
const
T
>
{
typedef
typename
intrinsic_type
<
T
>::
type
type
;
};
template
<
typename
T
>
struct
intrinsic_type
<
T
*>
{
typedef
typename
intrinsic_type
<
T
>::
type
type
;
};
template
<
typename
T
>
struct
intrinsic_type
<
T
&>
{
typedef
typename
intrinsic_type
<
T
>::
type
type
;
};
template
<
typename
T
>
struct
intrinsic_type
<
T
&&>
{
typedef
typename
intrinsic_type
<
T
>::
type
type
;
};
template
<
typename
T
,
size_t
N
>
struct
intrinsic_type
<
const
T
[
N
]
>
{
typedef
typename
intrinsic_type
<
T
>::
type
type
;
};
template
<
typename
T
,
size_t
N
>
struct
intrinsic_type
<
T
[
N
]
>
{
typedef
typename
intrinsic_type
<
T
>::
type
type
;
};
template
<
typename
T
>
using
intrinsic_t
=
typename
intrinsic_type
<
T
>::
type
;
/// Helper type to replace 'void' in some expressions
struct
void_type
{
};
/// Helper template which holds a list of types
template
<
typename
...
>
struct
type_list
{
};
/// Compile-time integer sum
#ifdef __cpp_fold_expressions
template
<
typename
...
Ts
>
constexpr
size_t
constexpr_sum
(
Ts
...
ns
)
{
return
(
0
+
...
+
size_t
{
ns
});
}
#else
constexpr
size_t
constexpr_sum
()
{
return
0
;
}
template
<
typename
T
,
typename
...
Ts
>
constexpr
size_t
constexpr_sum
(
T
n
,
Ts
...
ns
)
{
return
size_t
{
n
}
+
constexpr_sum
(
ns
...);
}
#endif
NAMESPACE_BEGIN
(
constexpr_impl
)
/// Implementation details for constexpr functions
constexpr
int
first
(
int
i
)
{
return
i
;
}
template
<
typename
T
,
typename
...
Ts
>
constexpr
int
first
(
int
i
,
T
v
,
Ts
...
vs
)
{
return
v
?
i
:
first
(
i
+
1
,
vs
...);
}
constexpr
int
last
(
int
/*i*/
,
int
result
)
{
return
result
;
}
template
<
typename
T
,
typename
...
Ts
>
constexpr
int
last
(
int
i
,
int
result
,
T
v
,
Ts
...
vs
)
{
return
last
(
i
+
1
,
v
?
i
:
result
,
vs
...);
}
NAMESPACE_END
(
constexpr_impl
)
/// Return the index of the first type in Ts which satisfies Predicate<T>. Returns sizeof...(Ts) if
/// none match.
template
<
template
<
typename
>
class
Predicate
,
typename
...
Ts
>
constexpr
int
constexpr_first
()
{
return
constexpr_impl
::
first
(
0
,
Predicate
<
Ts
>::
value
...);
}
/// Return the index of the last type in Ts which satisfies Predicate<T>, or -1 if none match.
template
<
template
<
typename
>
class
Predicate
,
typename
...
Ts
>
constexpr
int
constexpr_last
()
{
return
constexpr_impl
::
last
(
0
,
-
1
,
Predicate
<
Ts
>::
value
...);
}
/// Return the Nth element from the parameter pack
template
<
size_t
N
,
typename
T
,
typename
...
Ts
>
struct
pack_element
{
using
type
=
typename
pack_element
<
N
-
1
,
Ts
...
>::
type
;
};
template
<
typename
T
,
typename
...
Ts
>
struct
pack_element
<
0
,
T
,
Ts
...
>
{
using
type
=
T
;
};
/// Return the one and only type which matches the predicate, or Default if none match.
/// If more than one type matches the predicate, fail at compile-time.
template
<
template
<
typename
>
class
Predicate
,
typename
Default
,
typename
...
Ts
>
struct
exactly_one
{
static
constexpr
auto
found
=
constexpr_sum
(
Predicate
<
Ts
>::
value
...);
static_assert
(
found
<=
1
,
"Found more than one type matching the predicate"
);
static
constexpr
auto
index
=
found
?
constexpr_first
<
Predicate
,
Ts
...
>
()
:
0
;
using
type
=
conditional_t
<
found
,
typename
pack_element
<
index
,
Ts
...
>::
type
,
Default
>
;
};
template
<
template
<
typename
>
class
P
,
typename
Default
>
struct
exactly_one
<
P
,
Default
>
{
using
type
=
Default
;
};
template
<
template
<
typename
>
class
Predicate
,
typename
Default
,
typename
...
Ts
>
using
exactly_one_t
=
typename
exactly_one
<
Predicate
,
Default
,
Ts
...
>::
type
;
/// Defer the evaluation of type T until types Us are instantiated
template
<
typename
T
,
typename
...
/*Us*/
>
struct
deferred_type
{
using
type
=
T
;
};
template
<
typename
T
,
typename
...
Us
>
using
deferred_t
=
typename
deferred_type
<
T
,
Us
...
>::
type
;
/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of<T, T>::value == false`,
/// unlike `std::is_base_of`)
template
<
typename
Base
,
typename
Derived
>
using
is_strict_base_of
=
bool_constant
<
std
::
is_base_of
<
Base
,
Derived
>::
value
&&
!
std
::
is_same
<
Base
,
Derived
>::
value
>
;
template
<
template
<
typename
...
>
class
Base
>
struct
is_template_base_of_impl
{
template
<
typename
...
Us
>
static
std
::
true_type
check
(
Base
<
Us
...
>
*
);
static
std
::
false_type
check
(...);
};
/// Check if a template is the base of a type. For example:
/// `is_template_base_of<Base, T>` is true if `struct T : Base<U> {}` where U can be anything
template
<
template
<
typename
...
>
class
Base
,
typename
T
>
#if !defined(_MSC_VER)
using
is_template_base_of
=
decltype
(
is_template_base_of_impl
<
Base
>::
check
((
remove_cv_t
<
T
>*
)
nullptr
));
#else // MSVC2015 has trouble with decltype in template aliases
struct
is_template_base_of
:
decltype
(
is_template_base_of_impl
<
Base
>::
check
((
remove_cv_t
<
T
>*
)
nullptr
))
{
};
#endif
/// Check if T is an instantiation of the template `Class`. For example:
/// `is_instantiation<shared_ptr, T>` is true if `T == shared_ptr<U>` where U can be anything.
template
<
template
<
typename
...
>
class
Class
,
typename
T
>
struct
is_instantiation
:
std
::
false_type
{
};
template
<
template
<
typename
...
>
class
Class
,
typename
...
Us
>
struct
is_instantiation
<
Class
,
Class
<
Us
...
>>
:
std
::
true_type
{
};
/// Check if T is std::shared_ptr<U> where U can be anything
template
<
typename
T
>
using
is_shared_ptr
=
is_instantiation
<
std
::
shared_ptr
,
T
>
;
/// Check if T looks like an input iterator
template
<
typename
T
,
typename
=
void
>
struct
is_input_iterator
:
std
::
false_type
{};
template
<
typename
T
>
struct
is_input_iterator
<
T
,
void_t
<
decltype
(
*
std
::
declval
<
T
&>
()),
decltype
(
++
std
::
declval
<
T
&>
())
>>
:
std
::
true_type
{};
/// Ignore that a variable is unused in compiler warnings
inline
void
ignore_unused
(
const
int
*
)
{
}
/// Apply a function over each element of a parameter pack
#ifdef __cpp_fold_expressions
#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...)
#else
using
expand_side_effects
=
bool
[];
#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false }
#endif
NAMESPACE_END
(
detail
)
/// Returns a named pointer that is shared among all extension modules (using the same
/// pybind11 version) running in the current interpreter. Names starting with underscores
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
inline
PYBIND11_NOINLINE
void
*
get_shared_data
(
const
std
::
string
&
name
)
{
auto
&
internals
=
detail
::
get_internals
();
auto
it
=
internals
.
shared_data
.
find
(
name
);
return
it
!=
internals
.
shared_data
.
end
()
?
it
->
second
:
nullptr
;
}
/// Set the shared data that can be later recovered by `get_shared_data()`.
inline
PYBIND11_NOINLINE
void
*
set_shared_data
(
const
std
::
string
&
name
,
void
*
data
)
{
detail
::
get_internals
().
shared_data
[
name
]
=
data
;
return
data
;
}
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
/// such entry exists. Otherwise, a new object of default-constructible type `T` is
/// added to the shared data under the given name and a reference to it is returned.
template
<
typename
T
>
T
&
get_or_create_shared_data
(
const
std
::
string
&
name
)
{
auto
&
internals
=
detail
::
get_internals
();
auto
it
=
internals
.
shared_data
.
find
(
name
);
T
*
ptr
=
(
T
*
)
(
it
!=
internals
.
shared_data
.
end
()
?
it
->
second
:
nullptr
);
if
(
!
ptr
)
{
ptr
=
new
T
();
internals
.
shared_data
[
name
]
=
ptr
;
}
return
*
ptr
;
}
/// C++ bindings of builtin Python exceptions
class
builtin_exception
:
public
std
::
runtime_error
{
public:
using
std
::
runtime_error
::
runtime_error
;
/// Set the error using the Python C API
virtual
void
set_error
()
const
=
0
;
};
#define PYBIND11_RUNTIME_EXCEPTION(name, type) \
class name : public builtin_exception { public: \
using builtin_exception::builtin_exception; \
name() : name("") { } \
void set_error() const override { PyErr_SetString(type, what()); } \
};
PYBIND11_RUNTIME_EXCEPTION
(
stop_iteration
,
PyExc_StopIteration
)
PYBIND11_RUNTIME_EXCEPTION
(
index_error
,
PyExc_IndexError
)
PYBIND11_RUNTIME_EXCEPTION
(
key_error
,
PyExc_KeyError
)
PYBIND11_RUNTIME_EXCEPTION
(
value_error
,
PyExc_ValueError
)
PYBIND11_RUNTIME_EXCEPTION
(
type_error
,
PyExc_TypeError
)
PYBIND11_RUNTIME_EXCEPTION
(
cast_error
,
PyExc_RuntimeError
)
/// Thrown when pybind11::cast or handle::call fail due to a type casting error
PYBIND11_RUNTIME_EXCEPTION
(
reference_cast_error
,
PyExc_RuntimeError
)
/// Used internally
[[
noreturn
]]
PYBIND11_NOINLINE
inline
void
pybind11_fail
(
const
char
*
reason
)
{
throw
std
::
runtime_error
(
reason
);
}
[[
noreturn
]]
PYBIND11_NOINLINE
inline
void
pybind11_fail
(
const
std
::
string
&
reason
)
{
throw
std
::
runtime_error
(
reason
);
}
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
format_descriptor
{
};
NAMESPACE_BEGIN
(
detail
)
// Returns the index of the given type in the type char array below, and in the list in numpy.h
// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double;
// complex float,double,long double. Note that the long double types only participate when long
// double is actually longer than double (it isn't under MSVC).
// NB: not only the string below but also complex.h and numpy.h rely on this order.
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
is_fmt_numeric
{
static
constexpr
bool
value
=
false
;
};
template
<
typename
T
>
struct
is_fmt_numeric
<
T
,
enable_if_t
<
std
::
is_arithmetic
<
T
>::
value
>>
{
static
constexpr
bool
value
=
true
;
static
constexpr
int
index
=
std
::
is_same
<
T
,
bool
>::
value
?
0
:
1
+
(
std
::
is_integral
<
T
>::
value
?
detail
::
log2
(
sizeof
(
T
))
*
2
+
std
::
is_unsigned
<
T
>::
value
:
8
+
(
std
::
is_same
<
T
,
double
>::
value
?
1
:
std
::
is_same
<
T
,
long
double
>::
value
?
2
:
0
));
};
NAMESPACE_END
(
detail
)
template
<
typename
T
>
struct
format_descriptor
<
T
,
detail
::
enable_if_t
<
std
::
is_arithmetic
<
T
>::
value
>>
{
static
constexpr
const
char
c
=
"?bBhHiIqQfdg"
[
detail
::
is_fmt_numeric
<
T
>::
index
];
static
constexpr
const
char
value
[
2
]
=
{
c
,
'\0'
};
static
std
::
string
format
()
{
return
std
::
string
(
1
,
c
);
}
};
template
<
typename
T
>
constexpr
const
char
format_descriptor
<
T
,
detail
::
enable_if_t
<
std
::
is_arithmetic
<
T
>::
value
>>::
value
[
2
];
/// RAII wrapper that temporarily clears any Python error state
struct
error_scope
{
PyObject
*
type
,
*
value
,
*
trace
;
error_scope
()
{
PyErr_Fetch
(
&
type
,
&
value
,
&
trace
);
}
~
error_scope
()
{
PyErr_Restore
(
type
,
value
,
trace
);
}
};
/// Dummy destructor wrapper that can be used to expose classes with a private destructor
struct
nodelete
{
template
<
typename
T
>
void
operator
()(
T
*
)
{
}
};
// overload_cast requires variable templates: C++14
#if defined(PYBIND11_CPP14)
#define PYBIND11_OVERLOAD_CAST 1
NAMESPACE_BEGIN
(
detail
)
template
<
typename
...
Args
>
struct
overload_cast_impl
{
template
<
typename
Return
>
constexpr
auto
operator
()(
Return
(
*
pf
)(
Args
...))
const
noexcept
->
decltype
(
pf
)
{
return
pf
;
}
template
<
typename
Return
,
typename
Class
>
constexpr
auto
operator
()(
Return
(
Class
::*
pmf
)(
Args
...),
std
::
false_type
=
{})
const
noexcept
->
decltype
(
pmf
)
{
return
pmf
;
}
template
<
typename
Return
,
typename
Class
>
constexpr
auto
operator
()(
Return
(
Class
::*
pmf
)(
Args
...)
const
,
std
::
true_type
)
const
noexcept
->
decltype
(
pmf
)
{
return
pmf
;
}
};
NAMESPACE_END
(
detail
)
/// Syntax sugar for resolving overloaded function pointers:
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
template
<
typename
...
Args
>
static
constexpr
detail
::
overload_cast_impl
<
Args
...
>
overload_cast
=
{};
// MSVC 2015 only accepts this particular initialization syntax for this variable template.
/// Const member function selector for overload_cast
/// - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
/// - sweet: overload_cast<Arg>(&Class::func, const_)
static
constexpr
auto
const_
=
std
::
true_type
{};
#else // no overload_cast: providing something that static_assert-fails:
template
<
typename
...
Args
>
struct
overload_cast
{
static_assert
(
detail
::
deferred_t
<
std
::
false_type
,
Args
...
>::
value
,
"pybind11::overload_cast<...> requires compiling in C++14 mode"
);
};
#endif // overload_cast
NAMESPACE_BEGIN
(
detail
)
// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from
// any standard container (or C-style array) supporting std::begin/std::end, any singleton
// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair.
template
<
typename
T
>
class
any_container
{
std
::
vector
<
T
>
v
;
public:
any_container
()
=
default
;
// Can construct from a pair of iterators
template
<
typename
It
,
typename
=
enable_if_t
<
is_input_iterator
<
It
>
::
value
>>
any_container
(
It
first
,
It
last
)
:
v
(
first
,
last
)
{
}
// Implicit conversion constructor from any arbitrary container type with values convertible to T
template
<
typename
Container
,
typename
=
enable_if_t
<
std
::
is_convertible
<
decltype
(
*
std
::
begin
(
std
::
declval
<
const
Container
&
>())),
T
>::
value
>>
any_container
(
const
Container
&
c
)
:
any_container
(
std
::
begin
(
c
),
std
::
end
(
c
))
{
}
// initializer_list's aren't deducible, so don't get matched by the above template; we need this
// to explicitly allow implicit conversion from one:
template
<
typename
TIn
,
typename
=
enable_if_t
<
std
::
is_convertible
<
TIn
,
T
>
::
value
>>
any_container
(
const
std
::
initializer_list
<
TIn
>
&
c
)
:
any_container
(
c
.
begin
(),
c
.
end
())
{
}
// Avoid copying if given an rvalue vector of the correct type.
any_container
(
std
::
vector
<
T
>
&&
v
)
:
v
(
std
::
move
(
v
))
{
}
// Moves the vector out of an rvalue any_container
operator
std
::
vector
<
T
>
&&
()
&&
{
return
std
::
move
(
v
);
}
// Dereferencing obtains a reference to the underlying vector
std
::
vector
<
T
>
&
operator
*
()
{
return
v
;
}
const
std
::
vector
<
T
>
&
operator
*
()
const
{
return
v
;
}
// -> lets you call methods on the underlying vector
std
::
vector
<
T
>
*
operator
->
()
{
return
&
v
;
}
const
std
::
vector
<
T
>
*
operator
->
()
const
{
return
&
v
;
}
};
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
pybind11
)
ppocr/postprocess/lanms/include/pybind11/complex.h
0 → 100644
View file @
2401626a
/*
pybind11/complex.h: Complex number support
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 "pybind11.h"
#include <complex>
/// glibc defines I as a macro which breaks things, e.g., boost template names
#ifdef I
# undef I
#endif
NAMESPACE_BEGIN
(
pybind11
)
template
<
typename
T
>
struct
format_descriptor
<
std
::
complex
<
T
>
,
detail
::
enable_if_t
<
std
::
is_floating_point
<
T
>::
value
>>
{
static
constexpr
const
char
c
=
format_descriptor
<
T
>::
c
;
static
constexpr
const
char
value
[
3
]
=
{
'Z'
,
c
,
'\0'
};
static
std
::
string
format
()
{
return
std
::
string
(
value
);
}
};
template
<
typename
T
>
constexpr
const
char
format_descriptor
<
std
::
complex
<
T
>
,
detail
::
enable_if_t
<
std
::
is_floating_point
<
T
>::
value
>>::
value
[
3
];
NAMESPACE_BEGIN
(
detail
)
template
<
typename
T
>
struct
is_fmt_numeric
<
std
::
complex
<
T
>
,
detail
::
enable_if_t
<
std
::
is_floating_point
<
T
>::
value
>>
{
static
constexpr
bool
value
=
true
;
static
constexpr
int
index
=
is_fmt_numeric
<
T
>::
index
+
3
;
};
template
<
typename
T
>
class
type_caster
<
std
::
complex
<
T
>>
{
public:
bool
load
(
handle
src
,
bool
convert
)
{
if
(
!
src
)
return
false
;
if
(
!
convert
&&
!
PyComplex_Check
(
src
.
ptr
()))
return
false
;
Py_complex
result
=
PyComplex_AsCComplex
(
src
.
ptr
());
if
(
result
.
real
==
-
1.0
&&
PyErr_Occurred
())
{
PyErr_Clear
();
return
false
;
}
value
=
std
::
complex
<
T
>
((
T
)
result
.
real
,
(
T
)
result
.
imag
);
return
true
;
}
static
handle
cast
(
const
std
::
complex
<
T
>
&
src
,
return_value_policy
/* policy */
,
handle
/* parent */
)
{
return
PyComplex_FromDoubles
((
double
)
src
.
real
(),
(
double
)
src
.
imag
());
}
PYBIND11_TYPE_CASTER
(
std
::
complex
<
T
>
,
_
(
"complex"
));
};
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
pybind11
)
ppocr/postprocess/lanms/include/pybind11/descr.h
0 → 100644
View file @
2401626a
/*
pybind11/descr.h: Helper type for concatenating type signatures
either at runtime (C++11) or compile time (C++14)
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
)
NAMESPACE_BEGIN
(
detail
)
/* Concatenate type signatures at compile time using C++14 */
#if defined(PYBIND11_CPP14) && !defined(_MSC_VER)
#define PYBIND11_CONSTEXPR_DESCR
template
<
size_t
Size1
,
size_t
Size2
>
class
descr
{
template
<
size_t
Size1_
,
size_t
Size2_
>
friend
class
descr
;
public:
constexpr
descr
(
char
const
(
&
text
)
[
Size1
+
1
],
const
std
::
type_info
*
const
(
&
types
)[
Size2
+
1
])
:
descr
(
text
,
types
,
make_index_sequence
<
Size1
>
(),
make_index_sequence
<
Size2
>
())
{
}
constexpr
const
char
*
text
()
const
{
return
m_text
;
}
constexpr
const
std
::
type_info
*
const
*
types
()
const
{
return
m_types
;
}
template
<
size_t
OtherSize1
,
size_t
OtherSize2
>
constexpr
descr
<
Size1
+
OtherSize1
,
Size2
+
OtherSize2
>
operator
+
(
const
descr
<
OtherSize1
,
OtherSize2
>
&
other
)
const
{
return
concat
(
other
,
make_index_sequence
<
Size1
>
(),
make_index_sequence
<
Size2
>
(),
make_index_sequence
<
OtherSize1
>
(),
make_index_sequence
<
OtherSize2
>
());
}
protected:
template
<
size_t
...
Indices1
,
size_t
...
Indices2
>
constexpr
descr
(
char
const
(
&
text
)
[
Size1
+
1
],
const
std
::
type_info
*
const
(
&
types
)
[
Size2
+
1
],
index_sequence
<
Indices1
...
>
,
index_sequence
<
Indices2
...
>
)
:
m_text
{
text
[
Indices1
]...,
'\0'
},
m_types
{
types
[
Indices2
]...,
nullptr
}
{}
template
<
size_t
OtherSize1
,
size_t
OtherSize2
,
size_t
...
Indices1
,
size_t
...
Indices2
,
size_t
...
OtherIndices1
,
size_t
...
OtherIndices2
>
constexpr
descr
<
Size1
+
OtherSize1
,
Size2
+
OtherSize2
>
concat
(
const
descr
<
OtherSize1
,
OtherSize2
>
&
other
,
index_sequence
<
Indices1
...
>
,
index_sequence
<
Indices2
...
>
,
index_sequence
<
OtherIndices1
...
>
,
index_sequence
<
OtherIndices2
...
>
)
const
{
return
descr
<
Size1
+
OtherSize1
,
Size2
+
OtherSize2
>
(
{
m_text
[
Indices1
]...,
other
.
m_text
[
OtherIndices1
]...,
'\0'
},
{
m_types
[
Indices2
]...,
other
.
m_types
[
OtherIndices2
]...,
nullptr
}
);
}
protected:
char
m_text
[
Size1
+
1
];
const
std
::
type_info
*
m_types
[
Size2
+
1
];
};
template
<
size_t
Size
>
constexpr
descr
<
Size
-
1
,
0
>
_
(
char
const
(
&
text
)[
Size
])
{
return
descr
<
Size
-
1
,
0
>
(
text
,
{
nullptr
});
}
template
<
size_t
Rem
,
size_t
...
Digits
>
struct
int_to_str
:
int_to_str
<
Rem
/
10
,
Rem
%
10
,
Digits
...
>
{
};
template
<
size_t
...
Digits
>
struct
int_to_str
<
0
,
Digits
...
>
{
static
constexpr
auto
digits
=
descr
<
sizeof
...(
Digits
),
0
>
({
(
'0'
+
Digits
)...,
'\0'
},
{
nullptr
});
};
// Ternary description (like std::conditional)
template
<
bool
B
,
size_t
Size1
,
size_t
Size2
>
constexpr
enable_if_t
<
B
,
descr
<
Size1
-
1
,
0
>>
_
(
char
const
(
&
text1
)[
Size1
],
char
const
(
&
)[
Size2
])
{
return
_
(
text1
);
}
template
<
bool
B
,
size_t
Size1
,
size_t
Size2
>
constexpr
enable_if_t
<!
B
,
descr
<
Size2
-
1
,
0
>>
_
(
char
const
(
&
)[
Size1
],
char
const
(
&
text2
)[
Size2
])
{
return
_
(
text2
);
}
template
<
bool
B
,
size_t
SizeA1
,
size_t
SizeA2
,
size_t
SizeB1
,
size_t
SizeB2
>
constexpr
enable_if_t
<
B
,
descr
<
SizeA1
,
SizeA2
>>
_
(
descr
<
SizeA1
,
SizeA2
>
d
,
descr
<
SizeB1
,
SizeB2
>
)
{
return
d
;
}
template
<
bool
B
,
size_t
SizeA1
,
size_t
SizeA2
,
size_t
SizeB1
,
size_t
SizeB2
>
constexpr
enable_if_t
<!
B
,
descr
<
SizeB1
,
SizeB2
>>
_
(
descr
<
SizeA1
,
SizeA2
>
,
descr
<
SizeB1
,
SizeB2
>
d
)
{
return
d
;
}
template
<
size_t
Size
>
auto
constexpr
_
()
->
decltype
(
int_to_str
<
Size
/
10
,
Size
%
10
>::
digits
)
{
return
int_to_str
<
Size
/
10
,
Size
%
10
>::
digits
;
}
template
<
typename
Type
>
constexpr
descr
<
1
,
1
>
_
()
{
return
descr
<
1
,
1
>
({
'%'
,
'\0'
},
{
&
typeid
(
Type
),
nullptr
});
}
inline
constexpr
descr
<
0
,
0
>
concat
()
{
return
_
(
""
);
}
template
<
size_t
Size1
,
size_t
Size2
,
typename
...
Args
>
auto
constexpr
concat
(
descr
<
Size1
,
Size2
>
descr
)
{
return
descr
;
}
template
<
size_t
Size1
,
size_t
Size2
,
typename
...
Args
>
auto
constexpr
concat
(
descr
<
Size1
,
Size2
>
descr
,
Args
&&
...
args
)
{
return
descr
+
_
(
", "
)
+
concat
(
args
...);
}
template
<
size_t
Size1
,
size_t
Size2
>
auto
constexpr
type_descr
(
descr
<
Size1
,
Size2
>
descr
)
{
return
_
(
"{"
)
+
descr
+
_
(
"}"
);
}
#define PYBIND11_DESCR constexpr auto
#else
/* Simpler C++11 implementation based on run-time memory allocation and copying */
class
descr
{
public:
PYBIND11_NOINLINE
descr
(
const
char
*
text
,
const
std
::
type_info
*
const
*
types
)
{
size_t
nChars
=
len
(
text
),
nTypes
=
len
(
types
);
m_text
=
new
char
[
nChars
];
m_types
=
new
const
std
::
type_info
*
[
nTypes
];
memcpy
(
m_text
,
text
,
nChars
*
sizeof
(
char
));
memcpy
(
m_types
,
types
,
nTypes
*
sizeof
(
const
std
::
type_info
*
));
}
PYBIND11_NOINLINE
descr
operator
+
(
descr
&&
d2
)
&&
{
descr
r
;
size_t
nChars1
=
len
(
m_text
),
nTypes1
=
len
(
m_types
);
size_t
nChars2
=
len
(
d2
.
m_text
),
nTypes2
=
len
(
d2
.
m_types
);
r
.
m_text
=
new
char
[
nChars1
+
nChars2
-
1
];
r
.
m_types
=
new
const
std
::
type_info
*
[
nTypes1
+
nTypes2
-
1
];
memcpy
(
r
.
m_text
,
m_text
,
(
nChars1
-
1
)
*
sizeof
(
char
));
memcpy
(
r
.
m_text
+
nChars1
-
1
,
d2
.
m_text
,
nChars2
*
sizeof
(
char
));
memcpy
(
r
.
m_types
,
m_types
,
(
nTypes1
-
1
)
*
sizeof
(
std
::
type_info
*
));
memcpy
(
r
.
m_types
+
nTypes1
-
1
,
d2
.
m_types
,
nTypes2
*
sizeof
(
std
::
type_info
*
));
delete
[]
m_text
;
delete
[]
m_types
;
delete
[]
d2
.
m_text
;
delete
[]
d2
.
m_types
;
return
r
;
}
char
*
text
()
{
return
m_text
;
}
const
std
::
type_info
*
*
types
()
{
return
m_types
;
}
protected:
PYBIND11_NOINLINE
descr
()
{
}
template
<
typename
T
>
static
size_t
len
(
const
T
*
ptr
)
{
// return length including null termination
const
T
*
it
=
ptr
;
while
(
*
it
++
!=
(
T
)
0
)
;
return
static_cast
<
size_t
>
(
it
-
ptr
);
}
const
std
::
type_info
**
m_types
=
nullptr
;
char
*
m_text
=
nullptr
;
};
/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */
PYBIND11_NOINLINE
inline
descr
_
(
const
char
*
text
)
{
const
std
::
type_info
*
types
[
1
]
=
{
nullptr
};
return
descr
(
text
,
types
);
}
template
<
bool
B
>
PYBIND11_NOINLINE
enable_if_t
<
B
,
descr
>
_
(
const
char
*
text1
,
const
char
*
)
{
return
_
(
text1
);
}
template
<
bool
B
>
PYBIND11_NOINLINE
enable_if_t
<!
B
,
descr
>
_
(
char
const
*
,
const
char
*
text2
)
{
return
_
(
text2
);
}
template
<
bool
B
>
PYBIND11_NOINLINE
enable_if_t
<
B
,
descr
>
_
(
descr
d
,
descr
)
{
return
d
;
}
template
<
bool
B
>
PYBIND11_NOINLINE
enable_if_t
<!
B
,
descr
>
_
(
descr
,
descr
d
)
{
return
d
;
}
template
<
typename
Type
>
PYBIND11_NOINLINE
descr
_
()
{
const
std
::
type_info
*
types
[
2
]
=
{
&
typeid
(
Type
),
nullptr
};
return
descr
(
"%"
,
types
);
}
template
<
size_t
Size
>
PYBIND11_NOINLINE
descr
_
()
{
const
std
::
type_info
*
types
[
1
]
=
{
nullptr
};
return
descr
(
std
::
to_string
(
Size
).
c_str
(),
types
);
}
PYBIND11_NOINLINE
inline
descr
concat
()
{
return
_
(
""
);
}
PYBIND11_NOINLINE
inline
descr
concat
(
descr
&&
d
)
{
return
d
;
}
template
<
typename
...
Args
>
PYBIND11_NOINLINE
descr
concat
(
descr
&&
d
,
Args
&&
...
args
)
{
return
std
::
move
(
d
)
+
_
(
", "
)
+
concat
(
std
::
forward
<
Args
>
(
args
)...);
}
PYBIND11_NOINLINE
inline
descr
type_descr
(
descr
&&
d
)
{
return
_
(
"{"
)
+
std
::
move
(
d
)
+
_
(
"}"
);
}
#define PYBIND11_DESCR ::pybind11::detail::descr
#endif
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
pybind11
)
Prev
1
2
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