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
2814d997
Commit
2814d997
authored
Jun 23, 2020
by
zhangxin
Browse files
Merge remote-tracking branch 'upstream/develop' into zxdev
parents
9717944c
dcd0ed64
Changes
37
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
8732 additions
and
13 deletions
+8732
-13
README.md
README.md
+8
-1
doc/paddlehelp.jpg
doc/paddlehelp.jpg
+0
-0
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/data/rec/img_tools.py
ppocr/data/rec/img_tools.py
+2
-2
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
+16
-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
No files found.
README.md
View file @
2814d997
...
@@ -93,6 +93,9 @@ python3 tools/infer/predict_system.py --image_dir="./doc/imgs/11.jpg" --det_mode
...
@@ -93,6 +93,9 @@ python3 tools/infer/predict_system.py --image_dir="./doc/imgs/11.jpg" --det_mode
-
[
文本识别模型训练/评估/预测
](
./doc/doc_ch/recognition.md
)
-
[
文本识别模型训练/评估/预测
](
./doc/doc_ch/recognition.md
)
-
[
基于预测引擎推理
](
./doc/doc_ch/inference.md
)
-
[
基于预测引擎推理
](
./doc/doc_ch/inference.md
)
-
[
数据集
](
./doc/doc_ch/datasets.md
)
-
[
数据集
](
./doc/doc_ch/datasets.md
)
-
[
FAQ
](
#FAQ
)
-
[
联系我们
](
#欢迎加入PaddleOCR技术交流群
)
-
[
参考文献
](
#参考文献
)
## 文本检测算法
## 文本检测算法
...
@@ -170,6 +173,7 @@ PaddleOCR文本识别算法的训练和使用请参考文档教程中[文本识
...
@@ -170,6 +173,7 @@ PaddleOCR文本识别算法的训练和使用请参考文档教程中[文本识




<a
name=
"FAQ"
></a>
## FAQ
## FAQ
1.
**转换attention识别模型时报错:KeyError: 'predict'**
1.
**转换attention识别模型时报错:KeyError: 'predict'**
问题已解,请更新到最新代码。
问题已解,请更新到最新代码。
...
@@ -185,9 +189,12 @@ PaddleOCR文本识别算法的训练和使用请参考文档教程中[文本识
...
@@ -185,9 +189,12 @@ PaddleOCR文本识别算法的训练和使用请参考文档教程中[文本识
[
more
](
./doc/doc_ch/FAQ.md
)
[
more
](
./doc/doc_ch/FAQ.md
)
<a
name=
"欢迎加入PaddleOCR技术交流群"
></a>
## 欢迎加入PaddleOCR技术交流群
## 欢迎加入PaddleOCR技术交流群
加微信:paddlehelp,备注OCR,小助手拉你进群~
扫描二维码或者加微信:paddlehelp,备注OCR,小助手拉你进群~
<img
src=
"./doc/paddlehelp.jpg"
width =
"200"
height =
"200"
/>
<a
name=
"参考文献"
></a>
## 参考文献
## 参考文献
```
```
1. EAST:
1. EAST:
...
...
doc/paddlehelp.jpg
0 → 100644
View file @
2814d997
39.5 KB
ppocr/data/det/db_process.py
View file @
2814d997
...
@@ -125,8 +125,8 @@ class DBProcessTest(object):
...
@@ -125,8 +125,8 @@ class DBProcessTest(object):
def
__init__
(
self
,
params
):
def
__init__
(
self
,
params
):
super
(
DBProcessTest
,
self
).
__init__
()
super
(
DBProcessTest
,
self
).
__init__
()
self
.
resize_type
=
0
self
.
resize_type
=
0
if
'
de
t_image_shape'
in
params
:
if
'
tes
t_image_shape'
in
params
:
self
.
image_shape
=
params
[
'
de
t_image_shape'
]
self
.
image_shape
=
params
[
'
tes
t_image_shape'
]
# print(self.image_shape)
# print(self.image_shape)
self
.
resize_type
=
1
self
.
resize_type
=
1
if
'max_side_len'
in
params
:
if
'max_side_len'
in
params
:
...
...
ppocr/data/det/east_process.py
View file @
2814d997
...
@@ -455,17 +455,23 @@ class EASTProcessTrain(object):
...
@@ -455,17 +455,23 @@ class EASTProcessTrain(object):
class
EASTProcessTest
(
object
):
class
EASTProcessTest
(
object
):
def
__init__
(
self
,
params
):
def
__init__
(
self
,
params
):
super
(
EASTProcessTest
,
self
).
__init__
()
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
:
if
'max_side_len'
in
params
:
self
.
max_side_len
=
params
[
'max_side_len'
]
self
.
max_side_len
=
params
[
'max_side_len'
]
else
:
else
:
self
.
max_side_len
=
2400
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
resize image to a size multiple of 32 which is required by the network
:param im: the resized image
args:
:param max_side_len: limit of max image size to avoid out of memory in gpu
img(array): array with shape [h, w, c]
:return: the resized image and the resize ratio
return(tuple):
img, (ratio_h, ratio_w)
"""
"""
max_side_len
=
self
.
max_side_len
max_side_len
=
self
.
max_side_len
h
,
w
,
_
=
im
.
shape
h
,
w
,
_
=
im
.
shape
...
@@ -495,13 +501,30 @@ class EASTProcessTest(object):
...
@@ -495,13 +501,30 @@ class EASTProcessTest(object):
resize_w
=
32
resize_w
=
32
else
:
else
:
resize_w
=
(
resize_w
//
32
-
1
)
*
32
resize_w
=
(
resize_w
//
32
-
1
)
*
32
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
)))
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_h
=
resize_h
/
float
(
h
)
ratio_w
=
resize_w
/
float
(
w
)
ratio_w
=
resize_w
/
float
(
w
)
return
im
,
(
ratio_h
,
ratio_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
):
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_mean
=
[
0.485
,
0.456
,
0.406
]
img_std
=
[
0.229
,
0.224
,
0.225
]
img_std
=
[
0.229
,
0.224
,
0.225
]
im
=
im
[:,
:,
::
-
1
].
astype
(
np
.
float32
)
im
=
im
[:,
:,
::
-
1
].
astype
(
np
.
float32
)
...
...
ppocr/data/rec/img_tools.py
View file @
2814d997
...
@@ -108,8 +108,8 @@ def process_image(img,
...
@@ -108,8 +108,8 @@ def process_image(img,
if
len
(
text
)
==
0
or
len
(
text
)
>
max_text_length
:
if
len
(
text
)
==
0
or
len
(
text
)
>
max_text_length
:
logger
.
info
(
logger
.
info
(
"Warning in ppocr/data/rec/img_tools.py:line106: Wrong data type."
"Warning in ppocr/data/rec/img_tools.py:line106: Wrong data type."
"Excepted string with length between
0
and {}, but "
"Excepted string with length between
1
and {}, but "
"got '{}'
"
.
format
(
max_text_length
,
label
))
"got '{}'
. Label is '{}'
"
.
format
(
max_text_length
,
len
(
text
),
label
))
return
None
return
None
else
:
else
:
if
loss_type
==
"ctc"
:
if
loss_type
==
"ctc"
:
...
...
ppocr/modeling/heads/det_east_head.py
View file @
2814d997
...
@@ -18,6 +18,7 @@ from __future__ import print_function
...
@@ -18,6 +18,7 @@ from __future__ import print_function
import
paddle.fluid
as
fluid
import
paddle.fluid
as
fluid
from
..common_functions
import
conv_bn_layer
,
deconv_bn_layer
from
..common_functions
import
conv_bn_layer
,
deconv_bn_layer
from
collections
import
OrderedDict
class
EASTHead
(
object
):
class
EASTHead
(
object
):
...
@@ -110,7 +111,7 @@ class EASTHead(object):
...
@@ -110,7 +111,7 @@ class EASTHead(object):
def
__call__
(
self
,
inputs
):
def
__call__
(
self
,
inputs
):
f_common
=
self
.
unet_fusion
(
inputs
)
f_common
=
self
.
unet_fusion
(
inputs
)
f_score
,
f_geo
=
self
.
detector_header
(
f_common
)
f_score
,
f_geo
=
self
.
detector_header
(
f_common
)
predicts
=
{}
predicts
=
OrderedDict
()
predicts
[
'f_score'
]
=
f_score
predicts
[
'f_score'
]
=
f_score
predicts
[
'f_geo'
]
=
f_geo
predicts
[
'f_geo'
]
=
f_geo
return
predicts
return
predicts
ppocr/postprocess/east_postprocess.py
View file @
2814d997
...
@@ -20,6 +20,12 @@ import numpy as np
...
@@ -20,6 +20,12 @@ import numpy as np
from
.locality_aware_nms
import
nms_locality
from
.locality_aware_nms
import
nms_locality
import
cv2
import
cv2
import
os
import
sys
__dir__
=
os
.
path
.
dirname
(
__file__
)
sys
.
path
.
append
(
__dir__
)
sys
.
path
.
append
(
os
.
path
.
join
(
__dir__
,
'..'
))
class
EASTPostPocess
(
object
):
class
EASTPostPocess
(
object
):
"""
"""
...
@@ -31,6 +37,11 @@ class EASTPostPocess(object):
...
@@ -31,6 +37,11 @@ class EASTPostPocess(object):
self
.
cover_thresh
=
params
[
'cover_thresh'
]
self
.
cover_thresh
=
params
[
'cover_thresh'
]
self
.
nms_thresh
=
params
[
'nms_thresh'
]
self
.
nms_thresh
=
params
[
'nms_thresh'
]
# c++ la-nms is faster, but only support python 3.5
self
.
is_python35
=
False
if
sys
.
version_info
.
major
==
3
and
sys
.
version_info
.
minor
==
5
:
self
.
is_python35
=
True
def
restore_rectangle_quad
(
self
,
origin
,
geometry
):
def
restore_rectangle_quad
(
self
,
origin
,
geometry
):
"""
"""
Restore rectangle from quadrangle.
Restore rectangle from quadrangle.
...
@@ -66,6 +77,10 @@ class EASTPostPocess(object):
...
@@ -66,6 +77,10 @@ class EASTPostPocess(object):
boxes
=
np
.
zeros
((
text_box_restored
.
shape
[
0
],
9
),
dtype
=
np
.
float32
)
boxes
=
np
.
zeros
((
text_box_restored
.
shape
[
0
],
9
),
dtype
=
np
.
float32
)
boxes
[:,
:
8
]
=
text_box_restored
.
reshape
((
-
1
,
8
))
boxes
[:,
:
8
]
=
text_box_restored
.
reshape
((
-
1
,
8
))
boxes
[:,
8
]
=
score_map
[
xy_text
[:,
0
],
xy_text
[:,
1
]]
boxes
[:,
8
]
=
score_map
[
xy_text
[:,
0
],
xy_text
[:,
1
]]
if
self
.
is_python35
:
import
lanms
boxes
=
lanms
.
merge_quadrangle_n9
(
boxes
,
nms_thresh
)
else
:
boxes
=
nms_locality
(
boxes
.
astype
(
np
.
float64
),
nms_thresh
)
boxes
=
nms_locality
(
boxes
.
astype
(
np
.
float64
),
nms_thresh
)
if
boxes
.
shape
[
0
]
==
0
:
if
boxes
.
shape
[
0
]
==
0
:
return
[]
return
[]
...
...
ppocr/postprocess/lanms/.gitignore
0 → 100644
View file @
2814d997
adaptor.so
ppocr/postprocess/lanms/.ycm_extra_conf.py
0 → 100644
View file @
2814d997
#!/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 @
2814d997
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 @
2814d997
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 @
2814d997
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 @
2814d997
#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 @
2814d997
/*******************************************************************************
* *
* 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 @
2814d997
/*******************************************************************************
* *
* 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 @
2814d997
/*
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 @
2814d997
/*
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 @
2814d997
/*
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 @
2814d997
/*
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 @
2814d997
/*
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
)
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