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
85706e16
Commit
85706e16
authored
Jul 02, 2020
by
LDOUBLEV
Browse files
add deploy lite demo
parent
6f456775
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
6130 additions
and
0 deletions
+6130
-0
.gitignore
.gitignore
+1
-0
deploy/lite/Makefile
deploy/lite/Makefile
+61
-0
deploy/lite/ocr_db_crnn.cc
deploy/lite/ocr_db_crnn.cc
+333
-0
deploy/lite/readme.md
deploy/lite/readme.md
+162
-0
deploy/lite/utils/clipper.cpp
deploy/lite/utils/clipper.cpp
+4629
-0
deploy/lite/utils/clipper.hpp
deploy/lite/utils/clipper.hpp
+406
-0
deploy/lite/utils/crnn_process.cpp
deploy/lite/utils/crnn_process.cpp
+168
-0
deploy/lite/utils/db_post_process.cpp
deploy/lite/utils/db_post_process.cpp
+370
-0
No files found.
.gitignore
View file @
85706e16
...
@@ -18,3 +18,4 @@ output/
...
@@ -18,3 +18,4 @@ output/
*.idea
*.idea
*.log
*.log
.clang-format
deploy/lite/Makefile
0 → 100644
View file @
85706e16
ARM_ABI
=
arm8
export
ARM_ABI
include
../Makefile.def
LITE_ROOT
=
../../../
THIRD_PARTY_DIR
=
${LITE_ROOT}
/third_party
OPENCV_VERSION
=
opencv4.1.0
OPENCV_LIBS
=
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/libs/libopencv_imgcodecs.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/libs/libopencv_imgproc.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/libs/libopencv_core.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/libtegra_hal.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/liblibjpeg-turbo.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/liblibwebp.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/liblibpng.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/liblibjasper.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/liblibtiff.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/libIlmImf.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/libtbb.a
\
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/3rdparty/libs/libcpufeatures.a
OPENCV_INCLUDE
=
-I
../../../third_party/
${OPENCV_VERSION}
/arm64-v8a/include
CXX_INCLUDES
=
$(INCLUDES)
${OPENCV_INCLUDE}
-I
$(LITE_ROOT)
/cxx/include
CXX_LIBS
=
${OPENCV_LIBS}
-L
$(LITE_ROOT)
/cxx/lib/
-lpaddle_light_api_shared
$(SYSTEM_LIBS)
###############################################################
# How to use one of static libaray: #
# `libpaddle_api_full_bundled.a` #
# `libpaddle_api_light_bundled.a` #
###############################################################
# Note: default use lite's shared library. #
###############################################################
# 1. Comment above line using `libpaddle_light_api_shared.so`
# 2. Undo comment below line using `libpaddle_api_light_bundled.a`
#CXX_LIBS = $(LITE_ROOT)/cxx/lib/libpaddle_api_light_bundled.a $(SYSTEM_LIBS)
ocr_db_crnn
:
fetch_opencv ocr_db_crnn.o
$(CC)
$(SYSROOT_LINK)
$(CXXFLAGS_LINK)
ocr_db_crnn.o
-o
ocr_db_crnn
$(CXX_LIBS)
$(LDFLAGS)
ocr_db_crnn.o
:
ocr_db_crnn.cc
$(CC)
$(SYSROOT_COMPLILE)
$(CXX_DEFINES)
$(CXX_INCLUDES)
$(CXX_FLAGS)
-o
ocr_db_crnn.o
-c
ocr_db_crnn.cc
fetch_opencv
:
@
test
-d
${THIRD_PARTY_DIR}
||
mkdir
${THIRD_PARTY_DIR}
@
test
-e
${THIRD_PARTY_DIR}
/
${OPENCV_VERSION}
.tar.gz
||
\
(
echo
"fetch opencv libs"
&&
\
wget
-P
${THIRD_PARTY_DIR}
https://paddle-inference-dist.bj.bcebos.com/
${OPENCV_VERSION}
.tar.gz
)
@
test
-d
${THIRD_PARTY_DIR}
/
${OPENCV_VERSION}
||
\
tar
-zxvf
${THIRD_PARTY_DIR}
/
${OPENCV_VERSION}
.tar.gz
-C
${THIRD_PARTY_DIR}
.PHONY
:
clean
clean
:
rm
-f
ocr_db_crnn.o
rm
-f
ocr_db_crnnn
deploy/lite/ocr_db_crnn.cc
0 → 100644
View file @
85706e16
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iostream>
#include <vector>
#include <chrono>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h" // NOLINT
#include "utils/db_post_process.cpp"
#include "utils/crnn_process.cpp"
#include <cstring>
#include <fstream>
using
namespace
paddle
::
lite_api
;
// NOLINT
struct
Object
{
cv
::
Rect
rec
;
int
class_id
;
float
prob
;
};
int64_t
ShapeProduction
(
const
shape_t
&
shape
)
{
int64_t
res
=
1
;
for
(
auto
i
:
shape
)
res
*=
i
;
return
res
;
}
// fill tensor with mean and scale and trans layout: nhwc -> nchw, neon speed up
void
neon_mean_scale
(
const
float
*
din
,
float
*
dout
,
int
size
,
const
std
::
vector
<
float
>
mean
,
const
std
::
vector
<
float
>
scale
)
{
if
(
mean
.
size
()
!=
3
||
scale
.
size
()
!=
3
)
{
std
::
cerr
<<
"[ERROR] mean or scale size must equal to 3
\n
"
;
exit
(
1
);
}
float32x4_t
vmean0
=
vdupq_n_f32
(
mean
[
0
]);
float32x4_t
vmean1
=
vdupq_n_f32
(
mean
[
1
]);
float32x4_t
vmean2
=
vdupq_n_f32
(
mean
[
2
]);
float32x4_t
vscale0
=
vdupq_n_f32
(
scale
[
0
]);
float32x4_t
vscale1
=
vdupq_n_f32
(
scale
[
1
]);
float32x4_t
vscale2
=
vdupq_n_f32
(
scale
[
2
]);
float
*
dout_c0
=
dout
;
float
*
dout_c1
=
dout
+
size
;
float
*
dout_c2
=
dout
+
size
*
2
;
int
i
=
0
;
for
(;
i
<
size
-
3
;
i
+=
4
)
{
float32x4x3_t
vin3
=
vld3q_f32
(
din
);
float32x4_t
vsub0
=
vsubq_f32
(
vin3
.
val
[
0
],
vmean0
);
float32x4_t
vsub1
=
vsubq_f32
(
vin3
.
val
[
1
],
vmean1
);
float32x4_t
vsub2
=
vsubq_f32
(
vin3
.
val
[
2
],
vmean2
);
float32x4_t
vs0
=
vmulq_f32
(
vsub0
,
vscale0
);
float32x4_t
vs1
=
vmulq_f32
(
vsub1
,
vscale1
);
float32x4_t
vs2
=
vmulq_f32
(
vsub2
,
vscale2
);
vst1q_f32
(
dout_c0
,
vs0
);
vst1q_f32
(
dout_c1
,
vs1
);
vst1q_f32
(
dout_c2
,
vs2
);
din
+=
12
;
dout_c0
+=
4
;
dout_c1
+=
4
;
dout_c2
+=
4
;
}
for
(;
i
<
size
;
i
++
)
{
*
(
dout_c0
++
)
=
(
*
(
din
++
)
-
mean
[
0
])
*
scale
[
0
];
*
(
dout_c1
++
)
=
(
*
(
din
++
)
-
mean
[
1
])
*
scale
[
1
];
*
(
dout_c2
++
)
=
(
*
(
din
++
)
-
mean
[
2
])
*
scale
[
2
];
}
}
// resize image to a size multiple of 32 which is required by the network
cv
::
Mat
resize_img_type0
(
const
cv
::
Mat
img
,
int
max_size_len
,
float
*
ratio_h
,
float
*
ratio_w
){
int
w
=
img
.
cols
;
int
h
=
img
.
rows
;
float
ratio
=
1.
f
;
int
max_wh
=
w
>=
h
?
w
:
h
;
if
(
max_wh
>
max_size_len
){
if
(
h
>
w
){
ratio
=
float
(
max_size_len
)
/
float
(
h
);
}
else
{
ratio
=
float
(
max_size_len
)
/
float
(
w
);
}
}
int
resize_h
=
int
(
float
(
h
)
*
ratio
);
int
resize_w
=
int
(
float
(
w
)
*
ratio
);
if
(
resize_h
%
32
==
0
)
resize_h
=
resize_h
;
else
if
(
resize_h
/
32
<
1
)
resize_h
=
32
;
else
resize_h
=
(
resize_h
/
32
-
1
)
*
32
;
if
(
resize_w
%
32
==
0
)
resize_w
=
resize_w
;
else
if
(
resize_w
/
32
<
1
)
resize_w
=
32
;
else
resize_w
=
(
resize_w
/
32
-
1
)
*
32
;
cv
::
Mat
resize_img
;
cv
::
resize
(
img
,
resize_img
,
cv
::
Size
(
resize_w
,
resize_h
));
*
ratio_h
=
float
(
resize_h
)
/
float
(
h
);
*
ratio_w
=
float
(
resize_w
)
/
float
(
w
);
return
resize_img
;
}
using
namespace
std
;
void
RunRecModel
(
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
boxes
,
cv
::
Mat
img
,
std
::
string
rec_model_file
){
MobileConfig
config
;
config
.
set_model_from_file
(
rec_model_file
);
std
::
shared_ptr
<
PaddlePredictor
>
predictor_crnn
=
CreatePaddlePredictor
<
MobileConfig
>
(
config
);
std
::
vector
<
float
>
mean
=
{
0.5
f
,
0.5
f
,
0.5
f
};
std
::
vector
<
float
>
scale
=
{
1
/
0.5
f
,
1
/
0.5
f
,
1
/
0.5
f
};
cv
::
Mat
srcimg
;
img
.
copyTo
(
srcimg
);
cv
::
Mat
crop_img
;
cv
::
Mat
resize_img
;
std
::
string
dict_path
=
"./ppocr_keys_v1.txt"
;
auto
charactor_dict
=
read_dict
(
dict_path
);
std
::
cout
<<
"The predicted text is :"
<<
std
::
endl
;
int
index
=
0
;
for
(
int
i
=
boxes
.
size
()
-
1
;
i
>=
0
;
i
--
)
{
crop_img
=
get_rotate_crop_image
(
srcimg
,
boxes
[
i
]);
float
wh_ratio
=
float
(
crop_img
.
cols
)
/
float
(
crop_img
.
rows
);
resize_img
=
crnn_resize_img
(
crop_img
,
wh_ratio
);
resize_img
.
convertTo
(
resize_img
,
CV_32FC3
,
1
/
255.
f
);
const
float
*
dimg
=
reinterpret_cast
<
const
float
*>
(
resize_img
.
data
);
std
::
unique_ptr
<
Tensor
>
input_tensor0
(
std
::
move
(
predictor_crnn
->
GetInput
(
0
)));
input_tensor0
->
Resize
({
1
,
3
,
resize_img
.
rows
,
resize_img
.
cols
});
auto
*
data0
=
input_tensor0
->
mutable_data
<
float
>
();
neon_mean_scale
(
dimg
,
data0
,
resize_img
.
rows
*
resize_img
.
cols
,
mean
,
scale
);
//// Run CRNN predictor
predictor_crnn
->
Run
();
// Get output and run postprocess
std
::
unique_ptr
<
const
Tensor
>
output_tensor0
(
std
::
move
(
predictor_crnn
->
GetOutput
(
0
)));
auto
*
rec_idx
=
output_tensor0
->
data
<
int
>
();
auto
rec_idx_lod
=
output_tensor0
->
lod
();
auto
shape_out
=
output_tensor0
->
shape
();
std
::
vector
<
int
>
pred_idx
;
for
(
int
n
=
int
(
rec_idx_lod
[
0
][
0
]);
n
<
int
(
rec_idx_lod
[
0
][
1
]
*
2
);
n
+=
2
)
{
pred_idx
.
push_back
(
int
(
rec_idx
[
n
]));
}
if
(
pred_idx
.
size
()
<
1e-3
)
continue
;
std
::
cout
<<
std
::
endl
;
index
+=
1
;
std
::
cout
<<
index
<<
"
\t
"
;
for
(
int
n
=
0
;
n
<
pred_idx
.
size
();
n
++
)
{
std
::
cout
<<
charactor_dict
[
pred_idx
[
n
]];
}
////get score
std
::
unique_ptr
<
const
Tensor
>
output_tensor1
(
std
::
move
(
predictor_crnn
->
GetOutput
(
1
)));
auto
*
predict_batch
=
output_tensor1
->
data
<
float
>
();
auto
predict_shape
=
output_tensor1
->
shape
();
auto
predict_lod
=
output_tensor1
->
lod
();
int
argmax_idx
;
int
blank
=
predict_shape
[
1
];
float
score
=
0.
f
;
int
count
=
0
;
float
max_value
=
0.0
f
;
for
(
int
n
=
predict_lod
[
0
][
0
];
n
<
predict_lod
[
0
][
1
]
-
1
;
n
++
)
{
argmax_idx
=
int
(
argmax
(
&
predict_batch
[
n
*
predict_shape
[
1
]],
&
predict_batch
[(
n
+
1
)
*
predict_shape
[
1
]]));
max_value
=
float
(
*
std
::
max_element
(
&
predict_batch
[
n
*
predict_shape
[
1
]],
&
predict_batch
[(
n
+
1
)
*
predict_shape
[
1
]]));
if
(
blank
-
1
-
argmax_idx
>
1e-5
)
{
score
+=
max_value
;
count
+=
1
;
}
}
score
/=
count
;
std
::
cout
<<
"
\t
score: "
<<
score
<<
std
::
endl
;
}
}
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
RunDetModel
(
std
::
string
model_file
,
cv
::
Mat
img
)
{
// Set MobileConfig
MobileConfig
config
;
config
.
set_model_from_file
(
model_file
);
std
::
shared_ptr
<
PaddlePredictor
>
predictor
=
CreatePaddlePredictor
<
MobileConfig
>
(
config
);
// Read img
int
max_side_len
=
960
;
float
ratio_h
{};
float
ratio_w
{};
cv
::
Mat
srcimg
;
img
.
copyTo
(
srcimg
);
img
=
resize_img_type0
(
img
,
max_side_len
,
&
ratio_h
,
&
ratio_w
);
cv
::
Mat
img_fp
;
img
.
convertTo
(
img_fp
,
CV_32FC3
,
1.0
/
255.
f
);
// Prepare input data from image
std
::
unique_ptr
<
Tensor
>
input_tensor0
(
std
::
move
(
predictor
->
GetInput
(
0
)));
input_tensor0
->
Resize
({
1
,
3
,
img_fp
.
rows
,
img_fp
.
cols
});
auto
*
data0
=
input_tensor0
->
mutable_data
<
float
>
();
std
::
vector
<
float
>
mean
=
{
0.485
f
,
0.456
f
,
0.406
f
};
std
::
vector
<
float
>
scale
=
{
1
/
0.229
f
,
1
/
0.224
f
,
1
/
0.225
f
};
const
float
*
dimg
=
reinterpret_cast
<
const
float
*>
(
img_fp
.
data
);
neon_mean_scale
(
dimg
,
data0
,
img_fp
.
rows
*
img_fp
.
cols
,
mean
,
scale
);
// Run predictor
predictor
->
Run
();
// Get output and post process
std
::
unique_ptr
<
const
Tensor
>
output_tensor
(
std
::
move
(
predictor
->
GetOutput
(
0
)));
auto
*
outptr
=
output_tensor
->
data
<
float
>
();
auto
shape_out
=
output_tensor
->
shape
();
int64_t
out_numl
=
1
;
double
sum
=
0
;
for
(
auto
i
:
shape_out
)
{
out_numl
*=
i
;
}
// Save output
float
pred
[
shape_out
[
2
]][
shape_out
[
3
]];
unsigned
char
cbuf
[
shape_out
[
2
]][
shape_out
[
3
]];
for
(
int
i
=
0
;
i
<
int
(
shape_out
[
2
]
*
shape_out
[
3
]);
i
++
){
pred
[
int
(
i
/
int
(
shape_out
[
3
]))][
int
(
i
%
shape_out
[
3
])]
=
float
(
outptr
[
i
]);
cbuf
[
int
(
i
/
int
(
shape_out
[
3
]))][
int
(
i
%
shape_out
[
3
])]
=
(
unsigned
char
)
((
outptr
[
i
])
*
255
);
}
cv
::
Mat
cbuf_map
(
shape_out
[
2
],
shape_out
[
3
],
CV_8UC1
,
(
unsigned
char
*
)
cbuf
);
cv
::
Mat
pred_map
(
shape_out
[
2
],
shape_out
[
3
],
CV_32F
,
(
float
*
)
pred
);
const
double
threshold
=
0.3
*
255
;
const
double
maxvalue
=
255
;
cv
::
Mat
bit_map
;
cv
::
threshold
(
cbuf_map
,
bit_map
,
threshold
,
maxvalue
,
cv
::
THRESH_BINARY
);
auto
boxes
=
boxes_from_bitmap
(
pred_map
,
bit_map
);
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
filter_boxes
=
filter_tag_det_res
(
boxes
,
ratio_h
,
ratio_w
,
srcimg
);
//// visualization
cv
::
Point
rook_points
[
filter_boxes
.
size
()][
4
];
for
(
int
n
=
0
;
n
<
filter_boxes
.
size
();
n
++
){
for
(
int
m
=
0
;
m
<
filter_boxes
[
0
].
size
();
m
++
){
rook_points
[
n
][
m
]
=
cv
::
Point
(
int
(
filter_boxes
[
n
][
m
][
0
]),
int
(
filter_boxes
[
n
][
m
][
1
]));
}
}
cv
::
Mat
img_vis
;
srcimg
.
copyTo
(
img_vis
);
for
(
int
n
=
0
;
n
<
boxes
.
size
();
n
++
){
const
cv
::
Point
*
ppt
[
1
]
=
{
rook_points
[
n
]
};
int
npt
[]
=
{
4
};
cv
::
polylines
(
img_vis
,
ppt
,
npt
,
1
,
1
,
CV_RGB
(
0
,
255
,
0
),
2
,
8
,
0
);
}
cv
::
imwrite
(
"./imgs_vis/vis.jpg"
,
img_vis
);
std
::
cout
<<
"The detection visualized image saved in ./imgs_vis/"
<<
std
::
endl
;
return
filter_boxes
;
}
int
main
(
int
argc
,
char
**
argv
)
{
if
(
argc
<
4
)
{
std
::
cerr
<<
"[ERROR] usage: "
<<
argv
[
0
]
<<
" det_model_file rec_model_file image_path
\n
"
;
exit
(
1
);
}
std
::
string
det_model_file
=
argv
[
1
];
std
::
string
rec_model_file
=
argv
[
2
];
std
::
string
img_path
=
argv
[
3
];
auto
start
=
std
::
chrono
::
system_clock
::
now
();
cv
::
Mat
srcimg
=
cv
::
imread
(
img_path
,
cv
::
IMREAD_COLOR
);
auto
boxes
=
RunDetModel
(
det_model_file
,
srcimg
);
RunRecModel
(
boxes
,
srcimg
,
rec_model_file
);
auto
end
=
std
::
chrono
::
system_clock
::
now
();
auto
duration
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
microseconds
>
(
end
-
start
);
std
::
cout
<<
"花费了"
<<
double
(
duration
.
count
())
*
std
::
chrono
::
microseconds
::
period
::
num
/
std
::
chrono
::
microseconds
::
period
::
den
<<
"秒"
<<
std
::
endl
;
return
0
;
}
deploy/lite/readme.md
0 → 100644
View file @
85706e16
# PaddleOCR 移动端部署
本教程介绍如何在移动端部署PaddleOCR超轻量中文检测、识别模型。
## 运行准备
-
电脑(编译Paddle-Lite)
-
安卓手机(armv7或armv8)
## 1. 准备环境
### 1.1 准备交叉编译环境
交叉编译环境用于编译Paddle-Lite和PaddleOCR的C++ demo。
支持多种开发环境,不同开发环境的编译流程请参考对应文档。:
1.
[
Docker
](
https://paddle-lite.readthedocs.io/zh/latest/user_guides/source_compile.html#docker
)
2.
[
Linux
](
https://paddle-lite.readthedocs.io/zh/latest/user_guides/source_compile.html#android
)
3.
[
MAC OS
](
https://paddle-lite.readthedocs.io/zh/latest/user_guides/source_compile.html#id13
)
4.
[
Windows
](
https://paddle-lite.readthedocs.io/zh/latest/demo_guides/x86.html#windows
)
### 1.2 准备预编译库
预编译库有两种获取方式:
-
1. 直接下载,下载
[
链接
](
https://paddle-lite.readthedocs.io/zh/latest/user_guides/release_lib.html#android-toolchain-gcc
)
.
注意选择with_extra=ON,with_cv=ON的下载链接。
-
2. 编译Paddle-Lite得到,Paddle-Lite的编译方式如下:
```
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite
./lite/tools/build_android.sh --arch=armv8 --with_cv=ON --with_extra=ON
```
注意:编译Paddle-Lite获得预编译库时,需要打开--with_cv=ON --with_extra=ON两个选项,--arch表示arm版本,这里指定为armv8,
更多编译命令
介绍请参考
[
链接
](
https://paddle-lite.readthedocs.io/zh/latest/user_guides/Compile/Android.html#id2
)
。
直接下载预编译库并解压后,可以得到'inference_lite_lib.android.armv8/'文件夹,通过编译Paddle-Lite得到的预编译库位于
'Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/'文件夹下。
预编译库的文件目录如下:
```
inference_lite_lib.android.armv8/
|-- cxx C++ 预测库和头文件
| |-- include C++ 头文件
| | |-- paddle_api.h
| | |-- paddle_image_preprocess.h
| | |-- paddle_lite_factory_helper.h
| | |-- paddle_place.h
| | |-- paddle_use_kernels.h
| | |-- paddle_use_ops.h
| | `-- paddle_use_passes.h
| `-- lib C++预测库
| |-- libpaddle_api_light_bundled.a C++静态库
| `-- libpaddle_light_api_shared.so C++动态库
|-- java Java预测库
| |-- jar
| | `-- PaddlePredictor.jar
| |-- so
| | `-- libpaddle_lite_jni.so
| `-- src
|-- demo C++和Java示例代码
| |-- cxx C++ 预测库demo
| `-- java Java 预测库demo
```
## 2 开始运行
### 2.1 模型优化
Paddle-Lite 提供了多种策略来自动优化原始的模型,其中包括量化、子图融合、混合调度、Kernel优选等方法,使用Paddle_lite的opt工具可以自动
对模inference型进行优化,优化后的模型更轻量,模型运行速度更快。
模型优化需要使用Paddle-Lite的opt可执行文件,可以通过编译Paddle-Lite源码获得,编译步骤如下:
```
# 如果准备环境中已经clone了Paddle-Lite,则不用重新clone Paddle-Lite
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite
# 启动编译
./lite/tools/build.sh build_optimize_tool
```
编译完成后,opt文件位于'build.opt/lite/api/'下,可通过如下方式查看opt的运行选项和使用方式;
```
cd build.opt/lite/api/
./opt
```
|选项|说明|
|:-:|:-:|
|--model_dir|待优化的PaddlePaddle模型(非combined形式)的路径|
|--model_file|待优化的PaddlePaddle模型(combined形式)的网络结构文件路径|
|--param_file|待优化的PaddlePaddle模型(combined形式)的权重文件路径|
|--optimize_out_type|输出模型类型,目前支持两种类型:protobuf和naive_buffer,其中naive_buffer是一种更轻量级的序列化/反序列化实现。若您需要在mobile端执行模型预测,请将此选项设置为naive_buffer。默认为protobuf|
|--optimize_out|优化模型的输出路径|
|--valid_targets|指定模型可执行的backend,默认为arm。目前可支持x86、arm、opencl、npu、xpu,可以同时指定多个backend(以空格分隔),Model Optimize Tool将会自动选择最佳方式。如果需要支持华为NPU(Kirin 810/990 Soc搭载的达芬奇架构NPU),应当设置为npu, arm|
|--record_tailoring_info|当使用 根据模型裁剪库文件 功能时,则设置该选项为true,以记录优化后模型含有的kernel和OP信息,默认为false|
--model_dir适用于待优化的模型是非combined方式,PaddleOCR的inference模型是combined方式,即模型结构和模型参数使用单独一个文件存储。
下面以PaddleOCR的超轻量中文模型为例,介绍使用编译好的opt文件完成inference模型到Paddle-Lite优化模型的转换。
```
# 下载PaddleOCR的超轻量文inference模型,并解压
wget https://paddleocr.bj.bcebos.com/ch_models/ch_det_mv3_db_infer.tar && tar xf ch_det_mv3_db_infer.tar
wget https://paddleocr.bj.bcebos.com/ch_models/ch_rec_mv3_crnn_infer.tar && tar xf ch_rec_mv3_crnn_infer.tar
# 转换检测模型
./opt --model_file=./ch_det_mv3_db/model --param_file=./ch_det_mv3_db/params --optimize_out_type=naive_buffer --optimize_out=./ch_det_mv3_db_opt --valid_targets=arm
# 转换识别模型
./opt --model_file=./ch_rec_mv3_crnn/model --param_file=./ch_rec_mv3_crnn/params --optimize_out_type=naive_buffer --optimize_out=./ch_rec_mv3_crnn_opt --valid_targets=arm
```
转换成功后,当前目录下会多出ch_det_mv3_db_opt.nb, ch_rec_mv3_crnn_opt.nb结尾的文件,即是转换成功的模型文件。
### 2.2 与手机联调
首先需要进行一些准备工作。
1.
准备一台arm8的安卓手机,如果编译的预测库和opt文件是armv7,则需要arm7的手机。
2.
打开手机的USB调试选项,选择文件传输模式,连接电脑
3.
电脑上安装adb工具,用于调试。在电脑终端中输入'adb devices',如果有类似以下输出,则表示安装成功。
```
List of devices attached
744be294 device
```
4.
准备预测库、模型和预测文件,在预测库inference_lite_lib.android.armv8/demo/cxx/下新建一个ocr/文件夹,并将转换后的nb模型、
PaddleOCR repo中PaddleOCR/deploy/lite/ 下的所有文件放在新建的ocr文件夹下。执行完成后,ocr文件夹下将有如下文件格式:
```
demo/cxx/ocr/
|-- debug/ 新建debug文件夹存放模型文件
| |--ch_det_mv3_db_opt.nb 优化后的检测模型文件
| |--ch_rec_mv3_crnn_opt.nb 优化后的识别模型文件
|-- utils/
| |-- clipper.cpp Clipper库的cpp文件
| |-- clipper.hpp Clipper库的hpp文件
| |-- crnn_process.cpp 识别模型CRNN的预处理和后处理cpp文件
| |-- db_post_process.cpp 检测模型DB的后处理cpp文件
|-- Makefile 编译文件
|-- ocr_db_crnn.cc C++预测文件
```
5.
编译C++预测文件,准备测试图像,准备字典文件
```
cd demo/cxx/ocr/
# 执行编译
make
# 将编译的可执行文件移动到debug文件夹中
mv ocr_db_crnn ./debug/
```
准备测试图像,以PaddleOCR/doc/imgs/12.jpg为例,将测试的图像复制到demo/cxx/ocr/debug/文件夹下。
准备字典文件,将PaddleOCR/ppocr/utils/ppocr_keys_v1.txt复制到demo/cxx/ocr/debug/文件夹下。
上述步骤完成后就可以使用adb将文件push到手机上运行,步骤如下:
```
adb push debug /data/local/tmp/
adb shell
cd /data/local/tmp/debug
export LD_LIBRARY_PATH=/data/local/tmp/debug:$LD_LIBRARY_PATH
./ocr_db_crnn ch_det_mv3_db_opt.nb ch_rec_mv3_crnn_opt.nb ./12.jpg
```
如果对代码做了修改,则需要重新编译并push到手机上。
deploy/lite/utils/clipper.cpp
0 → 100755
View file @
85706e16
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.4.2 *
* Date : 27 February 2017 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2017 *
* *
* 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
()
:
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
&&
prevE
->
Top
.
Y
<
Pt
.
Y
&&
e
->
Top
.
Y
<
Pt
.
Y
)
{
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
{
#ifdef use_xyz
if
(
dir
==
dLeftToRight
)
SetZ
(
e
->
Curr
,
*
horzEdge
,
*
e
);
else
SetZ
(
e
->
Curr
,
*
e
,
*
horzEdge
);
#endif
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
;
#ifdef use_xyz
e
->
Curr
.
Z
=
topY
==
e
->
Top
.
Y
?
e
->
Top
.
Z
:
(
topY
==
e
->
Bot
.
Y
?
e
->
Bot
.
Z
:
0
);
#endif
}
//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
&&
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
deploy/lite/utils/clipper.hpp
0 → 100755
View file @
85706e16
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.4.2 *
* Date : 27 February 2017 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2017 *
* *
* 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.4.2"
//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:
//PolyNode& operator =(PolyNode& other);
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:
//PolyTree& operator =(PolyTree& other);
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
deploy/lite/utils/crnn_process.cpp
0 → 100644
View file @
85706e16
// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iostream>
#include <vector>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "math.h"
#include <iostream>
#include <cstring>
#include <fstream>
#define character_type "ch"
#define max_dict_length 6624
const
std
::
vector
<
int
>
rec_image_shape
{
3
,
32
,
320
};
cv
::
Mat
crnn_resize_norm_img
(
cv
::
Mat
img
,
float
wh_ratio
){
int
imgC
,
imgH
,
imgW
;
imgC
=
rec_image_shape
[
0
];
imgW
=
rec_image_shape
[
2
];
imgH
=
rec_image_shape
[
1
];
if
(
character_type
==
"ch"
)
imgW
=
int
(
32
*
wh_ratio
);
float
ratio
=
float
(
img
.
cols
)
/
float
(
img
.
rows
);
int
resize_w
,
resize_h
;
if
(
ceilf
(
imgH
*
ratio
)
>
imgW
)
resize_w
=
imgW
;
else
resize_w
=
int
(
ceilf
(
imgH
*
ratio
));
cv
::
Mat
resize_img
;
cv
::
resize
(
img
,
resize_img
,
cv
::
Size
(
resize_w
,
imgH
),
0.
f
,
0.
f
,
cv
::
INTER_CUBIC
);
resize_img
.
convertTo
(
resize_img
,
CV_32FC3
,
1
/
255.
f
);
for
(
int
h
=
0
;
h
<
resize_img
.
rows
;
h
++
){
for
(
int
w
=
0
;
w
<
resize_img
.
cols
;
w
++
){
resize_img
.
at
<
cv
::
Vec3f
>
(
h
,
w
)[
0
]
=
(
resize_img
.
at
<
cv
::
Vec3f
>
(
h
,
w
)[
0
]
-
0.5
)
*
2
;
resize_img
.
at
<
cv
::
Vec3f
>
(
h
,
w
)[
1
]
=
(
resize_img
.
at
<
cv
::
Vec3f
>
(
h
,
w
)[
1
]
-
0.5
)
*
2
;
resize_img
.
at
<
cv
::
Vec3f
>
(
h
,
w
)[
2
]
=
(
resize_img
.
at
<
cv
::
Vec3f
>
(
h
,
w
)[
2
]
-
0.5
)
*
2
;
}
}
cv
::
Mat
dist
;
cv
::
copyMakeBorder
(
resize_img
,
dist
,
0
,
0
,
0
,
int
(
imgW
-
resize_w
),
cv
::
BORDER_CONSTANT
,
{
0
,
0
,
0
});
return
dist
;
}
cv
::
Mat
crnn_resize_img
(
cv
::
Mat
img
,
float
wh_ratio
){
int
imgC
,
imgH
,
imgW
;
imgC
=
rec_image_shape
[
0
];
imgW
=
rec_image_shape
[
2
];
imgH
=
rec_image_shape
[
1
];
if
(
character_type
==
"ch"
)
imgW
=
int
(
32
*
wh_ratio
);
float
ratio
=
float
(
img
.
cols
)
/
float
(
img
.
rows
);
int
resize_w
,
resize_h
;
if
(
ceilf
(
imgH
*
ratio
)
>
imgW
)
resize_w
=
imgW
;
else
resize_w
=
int
(
ceilf
(
imgH
*
ratio
));
cv
::
Mat
resize_img
;
cv
::
resize
(
img
,
resize_img
,
cv
::
Size
(
resize_w
,
imgH
),
0.
f
,
0.
f
,
cv
::
INTER_LINEAR
);
return
resize_img
;
}
std
::
basic_string
<
char
,
std
::
char_traits
<
char
>
,
std
::
allocator
<
char
>>
*
read_dict
(
std
::
string
path
){
std
::
ifstream
ifs
;
std
::
string
charactors
[
max_dict_length
];
ifs
.
open
(
path
);
if
(
!
ifs
.
is_open
())
{
std
::
cout
<<
"open file "
<<
path
<<
" failed"
<<
std
::
endl
;
}
else
{
std
::
string
con
=
""
;
int
count
=
0
;
while
(
ifs
)
{
getline
(
ifs
,
charactors
[
count
]);
count
++
;
}
}
return
charactors
;
}
cv
::
Mat
get_rotate_crop_image
(
cv
::
Mat
srcimage
,
std
::
vector
<
std
::
vector
<
int
>>
box
){
cv
::
Mat
image
;
srcimage
.
copyTo
(
image
);
std
::
vector
<
std
::
vector
<
int
>>
points
=
box
;
int
x_collect
[
4
]
=
{
box
[
0
][
0
],
box
[
1
][
0
],
box
[
2
][
0
],
box
[
3
][
0
]};
int
y_collect
[
4
]
=
{
box
[
0
][
1
],
box
[
1
][
1
],
box
[
2
][
1
],
box
[
3
][
1
]};
int
left
=
int
(
*
std
::
min_element
(
x_collect
,
x_collect
+
4
));
int
right
=
int
(
*
std
::
max_element
(
x_collect
,
x_collect
+
4
));
int
top
=
int
(
*
std
::
min_element
(
y_collect
,
y_collect
+
4
));
int
bottom
=
int
(
*
std
::
max_element
(
y_collect
,
y_collect
+
4
));
cv
::
Mat
img_crop
;
image
(
cv
::
Rect
(
left
,
top
,
right
-
left
,
bottom
-
top
)).
copyTo
(
img_crop
);
for
(
int
i
=
0
;
i
<
points
.
size
();
i
++
){
points
[
i
][
0
]
-=
left
;
points
[
i
][
1
]
-=
top
;
}
int
img_crop_width
=
int
(
sqrt
(
pow
(
points
[
0
][
0
]
-
points
[
1
][
0
],
2
)
+
pow
(
points
[
0
][
1
]
-
points
[
1
][
1
],
2
)));
int
img_crop_height
=
int
(
sqrt
(
pow
(
points
[
0
][
0
]
-
points
[
3
][
0
],
2
)
+
pow
(
points
[
0
][
1
]
-
points
[
3
][
1
],
2
)));
cv
::
Point2f
pts_std
[
4
];
pts_std
[
0
]
=
cv
::
Point2f
(
0.
,
0.
);
pts_std
[
1
]
=
cv
::
Point2f
(
img_crop_width
,
0.
);
pts_std
[
2
]
=
cv
::
Point2f
(
img_crop_width
,
img_crop_height
);
pts_std
[
3
]
=
cv
::
Point2f
(
0.
f
,
img_crop_height
);
cv
::
Point2f
pointsf
[
4
];
pointsf
[
0
]
=
cv
::
Point2f
(
points
[
0
][
0
],
points
[
0
][
1
]);
pointsf
[
1
]
=
cv
::
Point2f
(
points
[
1
][
0
],
points
[
1
][
1
]);
pointsf
[
2
]
=
cv
::
Point2f
(
points
[
2
][
0
],
points
[
2
][
1
]);
pointsf
[
3
]
=
cv
::
Point2f
(
points
[
3
][
0
],
points
[
3
][
1
]);
cv
::
Mat
M
=
cv
::
getPerspectiveTransform
(
pointsf
,
pts_std
);
cv
::
Mat
dst_img
;
cv
::
warpPerspective
(
img_crop
,
dst_img
,
M
,
cv
::
Size
(
img_crop_width
,
img_crop_height
),
cv
::
BORDER_REPLICATE
);
if
(
float
(
dst_img
.
rows
)
>=
float
(
dst_img
.
cols
)
*
1.5
){
cv
::
Mat
srcCopy
=
cv
::
Mat
(
dst_img
.
rows
,
dst_img
.
cols
,
dst_img
.
depth
());
cv
::
transpose
(
dst_img
,
srcCopy
);
cv
::
flip
(
srcCopy
,
srcCopy
,
0
);
return
srcCopy
;
}
else
{
return
dst_img
;
}
}
template
<
class
ForwardIterator
>
inline
size_t
argmax
(
ForwardIterator
first
,
ForwardIterator
last
)
{
return
std
::
distance
(
first
,
std
::
max_element
(
first
,
last
));
}
\ No newline at end of file
deploy/lite/utils/db_post_process.cpp
0 → 100644
View file @
85706e16
// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iostream>
#include <vector>
#include <math.h>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "clipper.hpp"
#include "clipper.cpp"
void
getcontourarea
(
float
**
box
,
float
unclip_ratio
,
float
&
distance
){
int
pts_num
=
4
;
float
area
=
0.0
f
;
float
dist
=
0.0
f
;
for
(
int
i
=
0
;
i
<
pts_num
;
i
++
){
area
+=
box
[
i
][
0
]
*
box
[(
i
+
1
)
%
pts_num
][
1
]
-
box
[
i
][
1
]
*
box
[(
i
+
1
)
%
pts_num
][
0
];
dist
+=
sqrtf
(
(
box
[
i
][
0
]
-
box
[(
i
+
1
)
%
pts_num
][
0
])
*
(
box
[
i
][
0
]
-
box
[(
i
+
1
)
%
pts_num
][
0
])
+
(
box
[
i
][
1
]
-
box
[(
i
+
1
)
%
pts_num
][
1
])
*
(
box
[
i
][
1
]
-
box
[(
i
+
1
)
%
pts_num
][
1
])
);
}
area
=
fabs
(
float
(
area
/
2.0
));
distance
=
area
*
unclip_ratio
/
dist
;
}
cv
::
RotatedRect
unclip
(
float
**
box
){
float
unclip_ratio
=
2.0
;
float
distance
=
1.0
;
getcontourarea
(
box
,
unclip_ratio
,
distance
);
ClipperLib
::
ClipperOffset
offset
;
ClipperLib
::
Path
p
;
p
<<
ClipperLib
::
IntPoint
(
int
(
box
[
0
][
0
]),
int
(
box
[
0
][
1
]))
<<
ClipperLib
::
IntPoint
(
int
(
box
[
1
][
0
]),
int
(
box
[
1
][
1
]))
<<
ClipperLib
::
IntPoint
(
int
(
box
[
2
][
0
]),
int
(
box
[
2
][
1
]))
<<
ClipperLib
::
IntPoint
(
int
(
box
[
3
][
0
]),
int
(
box
[
3
][
1
]));
offset
.
AddPath
(
p
,
ClipperLib
::
jtRound
,
ClipperLib
::
etClosedPolygon
);
ClipperLib
::
Paths
soln
;
offset
.
Execute
(
soln
,
distance
);
std
::
vector
<
cv
::
Point2f
>
points
;
for
(
int
j
=
0
;
j
<
soln
.
size
();
j
++
){
for
(
int
i
=
0
;
i
<
soln
[
soln
.
size
()
-
1
].
size
();
i
++
){
points
.
emplace_back
(
soln
[
j
][
i
].
X
,
soln
[
j
][
i
].
Y
);
}
}
cv
::
RotatedRect
res
=
cv
::
minAreaRect
(
points
);
return
res
;
}
float
**
Mat2Vec
(
cv
::
Mat
mat
)
{
auto
**
array
=
new
float
*
[
mat
.
rows
];
for
(
int
i
=
0
;
i
<
mat
.
rows
;
++
i
)
array
[
i
]
=
new
float
[
mat
.
cols
];
for
(
int
i
=
0
;
i
<
mat
.
rows
;
++
i
)
{
for
(
int
j
=
0
;
j
<
mat
.
cols
;
++
j
)
{
array
[
i
][
j
]
=
mat
.
at
<
float
>
(
i
,
j
);
}
}
return
array
;
}
void
quickSort
(
float
**
s
,
int
l
,
int
r
)
{
if
(
l
<
r
)
{
int
i
=
l
,
j
=
r
;
float
x
=
s
[
l
][
0
];
float
*
xp
=
s
[
l
];
while
(
i
<
j
)
{
while
(
i
<
j
&&
s
[
j
][
0
]
>=
x
)
j
--
;
if
(
i
<
j
)
std
::
swap
(
s
[
i
++
],
s
[
j
]);
while
(
i
<
j
&&
s
[
i
][
0
]
<
x
)
i
++
;
if
(
i
<
j
)
std
::
swap
(
s
[
j
--
],
s
[
i
]);
}
s
[
i
]
=
xp
;
quickSort
(
s
,
l
,
i
-
1
);
quickSort
(
s
,
i
+
1
,
r
);
}
}
void
quickSort_vector
(
std
::
vector
<
std
::
vector
<
int
>>
&
box
,
int
l
,
int
r
,
int
axis
){
if
(
l
<
r
){
int
i
=
l
,
j
=
r
;
int
x
=
box
[
l
][
axis
];
std
::
vector
<
int
>
xp
(
box
[
l
]);
while
(
i
<
j
)
{
while
(
i
<
j
&&
box
[
j
][
axis
]
>=
x
)
j
--
;
if
(
i
<
j
)
std
::
swap
(
box
[
i
++
],
box
[
j
]);
while
(
i
<
j
&&
box
[
i
][
axis
]
<
x
)
i
++
;
if
(
i
<
j
)
std
::
swap
(
box
[
j
--
],
box
[
i
]);
}
box
[
i
]
=
xp
;
quickSort_vector
(
box
,
l
,
i
-
1
,
axis
);
quickSort_vector
(
box
,
i
+
1
,
r
,
axis
);
}
}
std
::
vector
<
std
::
vector
<
int
>>
order_points_clockwise
(
std
::
vector
<
std
::
vector
<
int
>>
pts
){
std
::
vector
<
std
::
vector
<
int
>>
box
=
pts
;
quickSort_vector
(
box
,
0
,
int
(
box
.
size
()
-
1
),
0
);
std
::
vector
<
std
::
vector
<
int
>>
leftmost
=
{
box
[
0
],
box
[
1
]};
std
::
vector
<
std
::
vector
<
int
>>
rightmost
=
{
box
[
2
],
box
[
3
]};
if
(
leftmost
[
0
][
1
]
>
leftmost
[
1
][
1
])
std
::
swap
(
leftmost
[
0
],
leftmost
[
1
]);
if
(
rightmost
[
0
][
1
]
>
rightmost
[
1
][
1
])
std
::
swap
(
rightmost
[
0
],
rightmost
[
1
]);
std
::
vector
<
std
::
vector
<
int
>>
rect
=
{
leftmost
[
0
],
rightmost
[
0
],
rightmost
[
1
],
leftmost
[
1
]};
return
rect
;
}
float
**
get_mini_boxes
(
cv
::
RotatedRect
box
,
float
&
ssid
){
ssid
=
box
.
size
.
width
>=
box
.
size
.
height
?
box
.
size
.
height
:
box
.
size
.
width
;
cv
::
Mat
points
;
cv
::
boxPoints
(
box
,
points
);
// sorted box points
auto
array
=
Mat2Vec
(
points
);
quickSort
(
array
,
0
,
3
);
float
*
idx1
=
array
[
0
],
*
idx2
=
array
[
1
],
*
idx3
=
array
[
2
],
*
idx4
=
array
[
3
];
if
(
array
[
3
][
1
]
<=
array
[
2
][
1
])
{
idx2
=
array
[
3
];
idx3
=
array
[
2
];
}
else
{
idx2
=
array
[
2
];
idx3
=
array
[
3
];
}
if
(
array
[
1
][
1
]
<=
array
[
0
][
1
]){
idx1
=
array
[
1
];
idx4
=
array
[
0
];
}
else
{
idx1
=
array
[
0
];
idx4
=
array
[
1
];
}
array
[
0
]
=
idx1
;
array
[
1
]
=
idx2
;
array
[
2
]
=
idx3
;
array
[
3
]
=
idx4
;
return
array
;
}
template
<
class
T
>
T
clamp
(
T
x
,
T
min
,
T
max
)
{
if
(
x
>
max
)
return
max
;
if
(
x
<
min
)
return
min
;
return
x
;
}
float
clampf
(
float
x
,
float
min
,
float
max
){
if
(
x
>
max
)
return
max
;
if
(
x
<
min
)
return
min
;
return
x
;
}
float
box_score_fast
(
float
**
box_array
,
cv
::
Mat
pred
){
auto
array
=
box_array
;
int
width
=
pred
.
cols
;
int
height
=
pred
.
rows
;
float
box_x
[
4
]
=
{
array
[
0
][
0
],
array
[
1
][
0
],
array
[
2
][
0
],
array
[
3
][
0
]};
float
box_y
[
4
]
=
{
array
[
0
][
1
],
array
[
1
][
1
],
array
[
2
][
1
],
array
[
3
][
1
]};
int
xmin
=
clamp
(
int
(
std
::
floorf
(
*
(
std
::
min_element
(
box_x
,
box_x
+
4
)))),
0
,
width
-
1
);
int
xmax
=
clamp
(
int
(
std
::
ceilf
(
*
(
std
::
max_element
(
box_x
,
box_x
+
4
)))),
0
,
width
-
1
);
int
ymin
=
clamp
(
int
(
std
::
floorf
(
*
(
std
::
min_element
(
box_y
,
box_y
+
4
)))),
0
,
height
-
1
);
int
ymax
=
clamp
(
int
(
std
::
ceilf
(
*
(
std
::
max_element
(
box_y
,
box_y
+
4
)))),
0
,
height
-
1
);
cv
::
Mat
mask
;
mask
=
cv
::
Mat
::
zeros
(
ymax
-
ymin
+
1
,
xmax
-
xmin
+
1
,
CV_8UC1
);
cv
::
Point
root_point
[
4
];
root_point
[
0
]
=
cv
::
Point
(
int
(
array
[
0
][
0
])
-
xmin
,
int
(
array
[
0
][
1
])
-
ymin
);
root_point
[
1
]
=
cv
::
Point
(
int
(
array
[
1
][
0
])
-
xmin
,
int
(
array
[
1
][
1
])
-
ymin
);
root_point
[
2
]
=
cv
::
Point
(
int
(
array
[
2
][
0
])
-
xmin
,
int
(
array
[
2
][
1
])
-
ymin
);
root_point
[
3
]
=
cv
::
Point
(
int
(
array
[
3
][
0
])
-
xmin
,
int
(
array
[
3
][
1
])
-
ymin
);
const
cv
::
Point
*
ppt
[
1
]
=
{
root_point
};
int
npt
[]
=
{
4
};
cv
::
fillPoly
(
mask
,
ppt
,
npt
,
1
,
cv
::
Scalar
(
1
));
cv
::
Mat
croppedImg
;
pred
(
cv
::
Rect
(
xmin
,
ymin
,
xmax
-
xmin
+
1
,
ymax
-
ymin
+
1
)).
copyTo
(
croppedImg
);
auto
score
=
cv
::
mean
(
croppedImg
,
mask
)[
0
];
return
score
;
}
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
boxes_from_bitmap
(
const
cv
::
Mat
pred
,
const
cv
::
Mat
bitmap
)
{
const
int
min_size
=
3
;
const
int
max_candidates
=
1000
;
const
float
box_thresh
=
0.5
;
int
width
=
bitmap
.
cols
;
int
height
=
bitmap
.
rows
;
std
::
vector
<
std
::
vector
<
cv
::
Point
>>
contours
;
std
::
vector
<
cv
::
Vec4i
>
hierarchy
;
cv
::
findContours
(
bitmap
,
contours
,
hierarchy
,
cv
::
RETR_LIST
,
cv
::
CHAIN_APPROX_SIMPLE
);
int
num_contours
=
contours
.
size
()
>=
max_candidates
?
max_candidates
:
contours
.
size
();
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
boxes
;
for
(
int
_i
=
0
;
_i
<
num_contours
;
_i
++
)
{
float
ssid
;
cv
::
RotatedRect
box
=
cv
::
minAreaRect
(
contours
[
_i
]);
auto
array
=
get_mini_boxes
(
box
,
ssid
);
auto
box_for_unclip
=
array
;
//end get_mini_box
if
(
ssid
<
min_size
)
{
continue
;
}
float
score
;
score
=
box_score_fast
(
array
,
pred
);
//end box_score_fast
if
(
score
<
box_thresh
)
continue
;
// start for unclip
cv
::
RotatedRect
points
=
unclip
(
box_for_unclip
);
// end for unclip
cv
::
RotatedRect
clipbox
=
points
;
auto
cliparray
=
get_mini_boxes
(
clipbox
,
ssid
);
if
(
ssid
<
min_size
+
2
)
continue
;
int
dest_width
=
pred
.
cols
;
int
dest_height
=
pred
.
rows
;
std
::
vector
<
std
::
vector
<
int
>>
intcliparray
;
for
(
int
num_pt
=
0
;
num_pt
<
4
;
num_pt
++
){
std
::
vector
<
int
>
a
{
int
(
clampf
(
roundf
(
cliparray
[
num_pt
][
0
]
/
float
(
width
)
*
float
(
dest_width
)),
0
,
float
(
dest_width
))
),
int
(
clampf
(
roundf
(
cliparray
[
num_pt
][
1
]
/
float
(
height
)
*
float
(
dest_height
)),
0
,
float
(
dest_height
))
)};
intcliparray
.
push_back
(
a
);
}
boxes
.
push_back
(
intcliparray
);
}
//end for
return
boxes
;
}
int
_max
(
int
a
,
int
b
){
return
a
>=
b
?
a
:
b
;
}
int
_min
(
int
a
,
int
b
){
return
a
>=
b
?
b
:
a
;
}
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
filter_tag_det_res
(
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
boxes
,
float
ratio_h
,
float
ratio_w
,
cv
::
Mat
srcimg
){
int
oriimg_h
=
srcimg
.
rows
;
int
oriimg_w
=
srcimg
.
cols
;
std
::
vector
<
std
::
vector
<
std
::
vector
<
int
>>>
root_points
;
for
(
int
n
=
0
;
n
<
boxes
.
size
();
n
++
){
boxes
[
n
]
=
order_points_clockwise
(
boxes
[
n
]);
for
(
int
m
=
0
;
m
<
boxes
[
0
].
size
();
m
++
){
boxes
[
n
][
m
][
0
]
/=
ratio_w
;
boxes
[
n
][
m
][
1
]
/=
ratio_h
;
boxes
[
n
][
m
][
0
]
=
int
(
_min
(
_max
(
boxes
[
n
][
m
][
0
],
0
),
oriimg_w
-
1
));
boxes
[
n
][
m
][
1
]
=
int
(
_min
(
_max
(
boxes
[
n
][
m
][
1
],
0
),
oriimg_h
-
1
));
}
}
for
(
int
n
=
0
;
n
<
boxes
.
size
();
n
++
){
int
rect_width
,
rect_height
;
rect_width
=
int
(
sqrt
(
pow
(
boxes
[
n
][
0
][
0
]
-
boxes
[
n
][
1
][
0
],
2
)
+
pow
(
boxes
[
n
][
0
][
1
]
-
boxes
[
n
][
1
][
1
],
2
)));
rect_height
=
int
(
sqrt
(
pow
(
boxes
[
n
][
0
][
0
]
-
boxes
[
n
][
3
][
0
],
2
)
+
pow
(
boxes
[
n
][
0
][
1
]
-
boxes
[
n
][
3
][
1
],
2
)));
if
(
rect_width
<=
10
||
rect_height
<=
10
)
continue
;
root_points
.
push_back
(
boxes
[
n
]);
}
return
root_points
;
}
/*
using namespace std;
// read data from txt file
cv::Mat readtxt2(std::string path, int imgw, int imgh, int imgc) {
std::cout << "read data file from txt file! " << std::endl;
ifstream in(path);
string line;
int count = 0;
int i = 0, j = 0;
std::vector<float> img_mean = {0.485, 0.456, 0.406};
std::vector<float> img_std = {0.229, 0.224, 0.225};
float trainData[imgh][imgw*imgc];
while (getline(in, line)) {
stringstream ss(line);
double x;
while (ss >> x) {
// trainData[i][j] = float(x) * img_std[j % 3] + img_mean[j % 3];
trainData[i][j] = float(x);
j++;
}
i++;
j = 0;
}
cv::Mat pred_map(imgh, imgw*imgc, CV_32FC1, (float *) trainData);
cv::Mat reshape_img = pred_map.reshape(imgc, imgh);
return reshape_img;
}
*/
//using namespace std;
//
//void writetxt(vector<vector<float>> data, std::string save_path){
//
// ofstream fout(save_path);
//
// for (int i = 0; i < data.size(); i++) {
// for (int j=0; j< data[0].size(); j++){
// fout << data[i][j] << " ";
// }
// fout << endl;
// }
// fout << endl;
// fout.close();
//}
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