diff --git a/Data/sandbox-log.txt b/Data/sandbox-log.txt index a3b0b10..ea2250c 100644 --- a/Data/sandbox-log.txt +++ b/Data/sandbox-log.txt @@ -1,14 +1,6688 @@ -[10:24:19 PM] Info: Starting... +[05:55:19 PM] Info: Starting... KP3D version 2 =============================== Copyright (C) kpworld.xyz 2018-2024 Contact me! @kp_cftsz -[10:24:19 PM] Info: Initializing SDL -[10:24:20 PM] Info: Initializing OpenGL -[10:24:20 PM] Info: OpenGL version: 4.6.0 NVIDIA 536.23 -[10:24:20 PM] Info: Initializing GLEW -[10:24:20 PM] Info: Initializing SDL_mixer -[10:24:20 PM] Info: Reticulating splines... -[10:24:20 PM] Info: Ready! +[05:55:19 PM] Info: Initializing SDL +[05:55:20 PM] Info: Initializing OpenGL +[05:55:20 PM] Info: OpenGL version: 4.6.0 NVIDIA 536.23 +[05:55:20 PM] Info: Initializing GLEW +[05:55:20 PM] Info: Initializing SDL_mixer +[05:55:20 PM] Info: Reticulating splines... +[05:55:20 PM] Info: Ready! +[05:55:20 PM] Info: Split: +[05:55:20 PM] Info: 0 size: 4 +[05:55:20 PM] Info: 1 size: 5 +[05:55:20 PM] Info: 2 size: 3 +[05:55:20 PM] Info: 3 size: 3 +[05:55:20 PM] Info: Split: +[05:55:20 PM] Info: 0 size: 4 +[05:55:20 PM] Info: 1 size: 5 +[05:55:20 PM] Info: 2 size: 3 +[05:55:20 PM] Info: 3 size: 3 +[05:55:26 PM] Info: Split: +[05:55:26 PM] Info: 0 size: 4 +[05:55:26 PM] Info: 1 size: 5 +[05:55:26 PM] Info: 2 size: 3 +[05:55:26 PM] Info: 3 size: 3 +[05:55:26 PM] Info: Split: +[05:55:26 PM] Info: 0 size: 4 +[05:55:26 PM] Info: 1 size: 5 +[05:55:26 PM] Info: 2 size: 3 +[05:55:26 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 8 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 4 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 3 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 5 +[05:55:27 PM] Info: 1 size: 3 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:27 PM] Info: Split: +[05:55:27 PM] Info: 0 size: 3 +[05:55:27 PM] Info: 1 size: 5 +[05:55:27 PM] Info: 2 size: 3 +[05:55:27 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 8 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 4 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 5 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 4 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 5 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 7 +[05:55:28 PM] Info: 1 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 7 +[05:55:28 PM] Info: 1 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 5 +[05:55:28 PM] Info: 1 size: 3 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:28 PM] Info: Split: +[05:55:28 PM] Info: 0 size: 3 +[05:55:28 PM] Info: 1 size: 5 +[05:55:28 PM] Info: 2 size: 3 +[05:55:28 PM] Info: 3 size: 4 +[05:55:30 PM] Info: Split: +[05:55:30 PM] Info: 0 size: 5 +[05:55:30 PM] Info: 1 size: 3 +[05:55:30 PM] Info: 2 size: 3 +[05:55:30 PM] Info: 3 size: 4 +[05:55:30 PM] Info: Split: +[05:55:30 PM] Info: 0 size: 3 +[05:55:30 PM] Info: 1 size: 5 +[05:55:30 PM] Info: 2 size: 3 +[05:55:30 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 7 +[05:55:31 PM] Info: 1 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 7 +[05:55:31 PM] Info: 1 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 4 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 5 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 4 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 5 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 8 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 8 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 8 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 8 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 5 +[05:55:31 PM] Info: 1 size: 3 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 8 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 5 +[05:55:31 PM] Info: 2 size: 3 +[05:55:31 PM] Info: 3 size: 4 +[05:55:31 PM] Info: Split: +[05:55:31 PM] Info: 0 size: 3 +[05:55:31 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 8 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 5 +[05:55:32 PM] Info: 1 size: 3 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:32 PM] Info: Split: +[05:55:32 PM] Info: 0 size: 3 +[05:55:32 PM] Info: 1 size: 5 +[05:55:32 PM] Info: 2 size: 3 +[05:55:32 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 5 +[05:55:33 PM] Info: 1 size: 3 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 3 +[05:55:33 PM] Info: 1 size: 5 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 5 +[05:55:33 PM] Info: 1 size: 3 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 3 +[05:55:33 PM] Info: 1 size: 5 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 5 +[05:55:33 PM] Info: 1 size: 3 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 3 +[05:55:33 PM] Info: 1 size: 5 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 5 +[05:55:33 PM] Info: 1 size: 3 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 3 +[05:55:33 PM] Info: 1 size: 5 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 5 +[05:55:33 PM] Info: 1 size: 3 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 3 +[05:55:33 PM] Info: 1 size: 5 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 5 +[05:55:33 PM] Info: 1 size: 3 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:33 PM] Info: Split: +[05:55:33 PM] Info: 0 size: 3 +[05:55:33 PM] Info: 1 size: 5 +[05:55:33 PM] Info: 2 size: 3 +[05:55:33 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 8 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 5 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 3 +[05:55:34 PM] Info: 1 size: 5 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 4 +[05:55:34 PM] Info: Split: +[05:55:34 PM] Info: 0 size: 4 +[05:55:34 PM] Info: 1 size: 3 +[05:55:34 PM] Info: 2 size: 3 +[05:55:34 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 3 +[05:55:35 PM] Info: 1 size: 5 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 4 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 5 +[05:55:35 PM] Info: Split: +[05:55:35 PM] Info: 0 size: 5 +[05:55:35 PM] Info: 1 size: 3 +[05:55:35 PM] Info: 2 size: 3 +[05:55:35 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 4 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 5 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 7 +[05:55:36 PM] Info: 1 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 7 +[05:55:36 PM] Info: 1 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:36 PM] Info: 1 size: 5 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 5 +[05:55:36 PM] Info: 1 size: 3 +[05:55:36 PM] Info: 2 size: 3 +[05:55:36 PM] Info: 3 size: 4 +[05:55:36 PM] Info: Split: +[05:55:36 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 5 +[05:55:37 PM] Info: 1 size: 3 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:37 PM] Info: Split: +[05:55:37 PM] Info: 0 size: 3 +[05:55:37 PM] Info: 1 size: 5 +[05:55:37 PM] Info: 2 size: 3 +[05:55:37 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 7 +[05:55:38 PM] Info: 1 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 7 +[05:55:38 PM] Info: 1 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 4 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 5 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 4 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 5 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 5 +[05:55:38 PM] Info: 1 size: 3 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 5 +[05:55:38 PM] Info: 2 size: 3 +[05:55:38 PM] Info: 3 size: 4 +[05:55:38 PM] Info: Split: +[05:55:38 PM] Info: 0 size: 3 +[05:55:38 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 5 +[05:55:40 PM] Info: 1 size: 3 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 5 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 5 +[05:55:40 PM] Info: 1 size: 3 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 5 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 5 +[05:55:40 PM] Info: 1 size: 3 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 5 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 5 +[05:55:40 PM] Info: 1 size: 3 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 5 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 5 +[05:55:40 PM] Info: 1 size: 3 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 5 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 5 +[05:55:40 PM] Info: 1 size: 3 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 5 +[05:55:40 PM] Info: 2 size: 3 +[05:55:40 PM] Info: 3 size: 4 +[05:55:40 PM] Info: Split: +[05:55:40 PM] Info: 0 size: 3 +[05:55:40 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 5 +[05:55:41 PM] Info: 1 size: 3 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 5 +[05:55:41 PM] Info: 2 size: 3 +[05:55:41 PM] Info: 3 size: 4 +[05:55:41 PM] Info: Split: +[05:55:41 PM] Info: 0 size: 3 +[05:55:41 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 5 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 5 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 5 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 5 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 5 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 5 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 5 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 5 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 4 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 4 +[05:55:42 PM] Info: 1 size: 5 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 4 +[05:55:42 PM] Info: 1 size: 5 +[05:55:42 PM] Info: 2 size: 3 +[05:55:42 PM] Info: 3 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 8 +[05:55:42 PM] Info: 1 size: 3 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:42 PM] Info: Split: +[05:55:42 PM] Info: 0 size: 3 +[05:55:42 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 8 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 4 +[05:55:43 PM] Info: 1 size: 5 +[05:55:43 PM] Info: 2 size: 3 +[05:55:43 PM] Info: 3 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 4 +[05:55:43 PM] Info: 1 size: 5 +[05:55:43 PM] Info: 2 size: 3 +[05:55:43 PM] Info: 3 size: 3 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 5 +[05:55:43 PM] Info: 1 size: 3 +[05:55:43 PM] Info: 2 size: 3 +[05:55:43 PM] Info: 3 size: 4 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 5 +[05:55:43 PM] Info: 2 size: 3 +[05:55:43 PM] Info: 3 size: 4 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:43 PM] Info: Split: +[05:55:43 PM] Info: 0 size: 3 +[05:55:43 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 5 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: 2 size: 3 +[05:55:46 PM] Info: 3 size: 4 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 5 +[05:55:46 PM] Info: 2 size: 3 +[05:55:46 PM] Info: 3 size: 4 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 4 +[05:55:46 PM] Info: 1 size: 5 +[05:55:46 PM] Info: 2 size: 3 +[05:55:46 PM] Info: 3 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 4 +[05:55:46 PM] Info: 1 size: 5 +[05:55:46 PM] Info: 2 size: 3 +[05:55:46 PM] Info: 3 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 8 +[05:55:46 PM] Info: 1 size: 3 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 +[05:55:46 PM] Info: Split: +[05:55:46 PM] Info: 0 size: 3 +[05:55:46 PM] Info: 1 size: 8 diff --git a/KP3Dii/KP3Dii.vcxproj b/KP3Dii/KP3Dii.vcxproj index 29bf168..9ae87fb 100644 --- a/KP3Dii/KP3Dii.vcxproj +++ b/KP3Dii/KP3Dii.vcxproj @@ -143,6 +143,7 @@ + @@ -173,6 +174,9 @@ + + + @@ -193,6 +197,7 @@ + diff --git a/KP3Dii/KP3Dii.vcxproj.filters b/KP3Dii/KP3Dii.vcxproj.filters index b4ef0b6..227e954 100644 --- a/KP3Dii/KP3Dii.vcxproj.filters +++ b/KP3Dii/KP3Dii.vcxproj.filters @@ -39,6 +39,7 @@ + @@ -88,6 +89,10 @@ + + + + diff --git a/KP3Dii/ext/include/clipper2/clipper.core.h b/KP3Dii/ext/include/clipper2/clipper.core.h new file mode 100644 index 0000000..d3ad715 --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.core.h @@ -0,0 +1,1073 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 12 May 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : Core Clipper Library structures and functions * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#ifndef CLIPPER_CORE_H +#define CLIPPER_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clipper2/clipper.version.h" + +namespace Clipper2Lib +{ + +#if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS) + + class Clipper2Exception : public std::exception { + public: + explicit Clipper2Exception(const char* description) : + m_descr(description) {} + virtual const char* what() const noexcept override { return m_descr.c_str(); } + private: + std::string m_descr; + }; + + static const char* precision_error = + "Precision exceeds the permitted range"; + static const char* range_error = + "Values exceed permitted range"; + static const char* scale_error = + "Invalid scale (either 0 or too large)"; + static const char* non_pair_error = + "There must be 2 values for each coordinate"; + static const char* undefined_error = + "There is an undefined error in Clipper2"; +#endif + + // error codes (2^n) + const int precision_error_i = 1; // non-fatal + const int scale_error_i = 2; // non-fatal + const int non_pair_error_i = 4; // non-fatal + const int undefined_error_i = 32; // fatal + const int range_error_i = 64; + +#ifndef PI + static const double PI = 3.141592653589793238; +#endif + +#ifdef CLIPPER2_MAX_DECIMAL_PRECISION + const int CLIPPER2_MAX_DEC_PRECISION = CLIPPER2_MAX_DECIMAL_PRECISION; +#else + const int CLIPPER2_MAX_DEC_PRECISION = 8; // see Discussions #564 +#endif + + static const int64_t MAX_COORD = INT64_MAX >> 2; + static const int64_t MIN_COORD = -MAX_COORD; + static const int64_t INVALID = INT64_MAX; + const double max_coord = static_cast(MAX_COORD); + const double min_coord = static_cast(MIN_COORD); + + static const double MAX_DBL = (std::numeric_limits::max)(); + + static void DoError([[maybe_unused]] int error_code) + { +#if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS) + switch (error_code) + { + case precision_error_i: + throw Clipper2Exception(precision_error); + case scale_error_i: + throw Clipper2Exception(scale_error); + case non_pair_error_i: + throw Clipper2Exception(non_pair_error); + case undefined_error_i: + throw Clipper2Exception(undefined_error); + case range_error_i: + throw Clipper2Exception(range_error); + } +#else + ++error_code; // only to stop compiler warning +#endif + } + + // can we call std::round on T? (default false) (#824) + template + struct is_round_invocable : std::false_type {}; + + template + struct is_round_invocable()))>> : std::true_type {}; + + + //By far the most widely used filling rules for polygons are EvenOdd + //and NonZero, sometimes called Alternate and Winding respectively. + //https://en.wikipedia.org/wiki/Nonzero-rule + enum class FillRule { EvenOdd, NonZero, Positive, Negative }; + + // Point ------------------------------------------------------------------------ + + template + struct Point { + T x; + T y; +#ifdef USINGZ + int64_t z; + + template + inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0) + { + if constexpr (std::is_integral_v && + is_round_invocable::value && !std::is_integral_v) + { + x = static_cast(std::round(x_)); + y = static_cast(std::round(y_)); + z = z_; + } + else + { + x = static_cast(x_); + y = static_cast(y_); + z = z_; + } + } + + explicit Point() : x(0), y(0), z(0) {}; + + template + Point(const T2 x_, const T2 y_, const int64_t z_ = 0) + { + Init(x_, y_); + z = z_; + } + + template + explicit Point(const Point& p) + { + Init(p.x, p.y, p.z); + } + + template + explicit Point(const Point& p, int64_t z_) + { + Init(p.x, p.y, z_); + } + + Point operator * (const double scale) const + { + return Point(x * scale, y * scale, z); + } + + void SetZ(const int64_t z_value) { z = z_value; } + + friend std::ostream& operator<<(std::ostream& os, const Point& point) + { + os << point.x << "," << point.y << "," << point.z; + return os; + } + +#else + + template + inline void Init(const T2 x_ = 0, const T2 y_ = 0) + { + if constexpr (std::is_integral_v && + is_round_invocable::value && !std::is_integral_v) + { + x = static_cast(std::round(x_)); + y = static_cast(std::round(y_)); + } + else + { + x = static_cast(x_); + y = static_cast(y_); + } + } + + explicit Point() : x(0), y(0) {}; + + template + Point(const T2 x_, const T2 y_) { Init(x_, y_); } + + template + explicit Point(const Point& p) { Init(p.x, p.y); } + + Point operator * (const double scale) const + { + return Point(x * scale, y * scale); + } + + friend std::ostream& operator<<(std::ostream& os, const Point& point) + { + os << point.x << "," << point.y; + return os; + } +#endif + + friend bool operator==(const Point& a, const Point& b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const Point& a, const Point& b) + { + return !(a == b); + } + + inline Point operator-() const + { + return Point(-x, -y); + } + + inline Point operator+(const Point& b) const + { + return Point(x + b.x, y + b.y); + } + + inline Point operator-(const Point& b) const + { + return Point(x - b.x, y - b.y); + } + + inline void Negate() { x = -x; y = -y; } + + }; + + //nb: using 'using' here (instead of typedef) as they can be used in templates + using Point64 = Point; + using PointD = Point; + + template + using Path = std::vector>; + template + using Paths = std::vector>; + + using Path64 = Path; + using PathD = Path; + using Paths64 = std::vector< Path64>; + using PathsD = std::vector< PathD>; + + static const Point64 InvalidPoint64 = Point64( + (std::numeric_limits::max)(), + (std::numeric_limits::max)()); + static const PointD InvalidPointD = PointD( + (std::numeric_limits::max)(), + (std::numeric_limits::max)()); + + template + static inline Point MidPoint(const Point& p1, const Point& p2) + { + Point result; + result.x = (p1.x + p2.x) / 2; + result.y = (p1.y + p2.y) / 2; + return result; + } + + // Rect ------------------------------------------------------------------------ + + template + struct Rect; + + using Rect64 = Rect; + using RectD = Rect; + + template + struct Rect { + T left; + T top; + T right; + T bottom; + + Rect(T l, T t, T r, T b) : + left(l), + top(t), + right(r), + bottom(b) {} + + Rect(bool is_valid = true) + { + if (is_valid) + { + left = right = top = bottom = 0; + } + else + { + left = top = (std::numeric_limits::max)(); + right = bottom = std::numeric_limits::lowest(); + } + } + + static Rect InvalidRect() + { + return { + (std::numeric_limits::max)(), + (std::numeric_limits::max)(), + std::numeric_limits::lowest(), + std::numeric_limits::lowest() }; + } + + bool IsValid() const { return left != (std::numeric_limits::max)(); } + + T Width() const { return right - left; } + T Height() const { return bottom - top; } + void Width(T width) { right = left + width; } + void Height(T height) { bottom = top + height; } + + Point MidPoint() const + { + return Point((left + right) / 2, (top + bottom) / 2); + } + + Path AsPath() const + { + Path result; + result.reserve(4); + result.push_back(Point(left, top)); + result.push_back(Point(right, top)); + result.push_back(Point(right, bottom)); + result.push_back(Point(left, bottom)); + return result; + } + + bool Contains(const Point& pt) const + { + return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom; + } + + bool Contains(const Rect& rec) const + { + return rec.left >= left && rec.right <= right && + rec.top >= top && rec.bottom <= bottom; + } + + void Scale(double scale) { + left *= scale; + top *= scale; + right *= scale; + bottom *= scale; + } + + bool IsEmpty() const { return bottom <= top || right <= left; }; + + bool Intersects(const Rect& rec) const + { + return ((std::max)(left, rec.left) <= (std::min)(right, rec.right)) && + ((std::max)(top, rec.top) <= (std::min)(bottom, rec.bottom)); + }; + + bool operator==(const Rect& other) const { + return left == other.left && right == other.right && + top == other.top && bottom == other.bottom; + } + + Rect& operator+=(const Rect& other) + { + left = (std::min)(left, other.left); + top = (std::min)(top, other.top); + right = (std::max)(right, other.right); + bottom = (std::max)(bottom, other.bottom); + return *this; + } + + Rect operator+(const Rect& other) const + { + Rect result = *this; + result += other; + return result; + } + + friend std::ostream& operator<<(std::ostream& os, const Rect& rect) { + os << "(" << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom << ") "; + return os; + } + }; + + template + inline Rect ScaleRect(const Rect& rect, double scale) + { + Rect result; + + if constexpr (std::is_integral_v && + is_round_invocable::value && !std::is_integral_v) + { + result.left = static_cast(std::round(rect.left * scale)); + result.top = static_cast(std::round(rect.top * scale)); + result.right = static_cast(std::round(rect.right * scale)); + result.bottom = static_cast(std::round(rect.bottom * scale)); + } + else + { + result.left = static_cast(rect.left * scale); + result.top = static_cast(rect.top * scale); + result.right = static_cast(rect.right * scale); + result.bottom = static_cast(rect.bottom * scale); + } + return result; + } + + static const Rect64 InvalidRect64 = Rect64::InvalidRect(); + static const RectD InvalidRectD = RectD::InvalidRect(); + + template + Rect GetBounds(const Path& path) + { + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); + for (const auto& p : path) + { + if (p.x < xmin) xmin = p.x; + if (p.x > xmax) xmax = p.x; + if (p.y < ymin) ymin = p.y; + if (p.y > ymax) ymax = p.y; + } + return Rect(xmin, ymin, xmax, ymax); + } + + template + Rect GetBounds(const Paths& paths) + { + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); + for (const Path& path : paths) + for (const Point& p : path) + { + if (p.x < xmin) xmin = p.x; + if (p.x > xmax) xmax = p.x; + if (p.y < ymin) ymin = p.y; + if (p.y > ymax) ymax = p.y; + } + return Rect(xmin, ymin, xmax, ymax); + } + + template + Rect GetBounds(const Path& path) + { + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); + for (const auto& p : path) + { + if (p.x < xmin) xmin = static_cast(p.x); + if (p.x > xmax) xmax = static_cast(p.x); + if (p.y < ymin) ymin = static_cast(p.y); + if (p.y > ymax) ymax = static_cast(p.y); + } + return Rect(xmin, ymin, xmax, ymax); + } + + template + Rect GetBounds(const Paths& paths) + { + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); + for (const Path& path : paths) + for (const Point& p : path) + { + if (p.x < xmin) xmin = static_cast(p.x); + if (p.x > xmax) xmax = static_cast(p.x); + if (p.y < ymin) ymin = static_cast(p.y); + if (p.y > ymax) ymax = static_cast(p.y); + } + return Rect(xmin, ymin, xmax, ymax); + } + + template + std::ostream& operator << (std::ostream& outstream, const Path& path) + { + if (!path.empty()) + { + auto pt = path.cbegin(), last = path.cend() - 1; + while (pt != last) + outstream << *pt++ << ", "; + outstream << *last << std::endl; + } + return outstream; + } + + template + std::ostream& operator << (std::ostream& outstream, const Paths& paths) + { + for (auto p : paths) + outstream << p; + return outstream; + } + + + template + inline Path ScalePath(const Path& path, + double scale_x, double scale_y, int& error_code) + { + Path result; + if (scale_x == 0 || scale_y == 0) + { + error_code |= scale_error_i; + DoError(scale_error_i); + // if no exception, treat as non-fatal error + if (scale_x == 0) scale_x = 1.0; + if (scale_y == 0) scale_y = 1.0; + } + + result.reserve(path.size()); +#ifdef USINGZ + std::transform(path.begin(), path.end(), back_inserter(result), + [scale_x, scale_y](const auto& pt) + { return Point(pt.x * scale_x, pt.y * scale_y, pt.z); }); +#else + std::transform(path.begin(), path.end(), back_inserter(result), + [scale_x, scale_y](const auto& pt) + { return Point(pt.x * scale_x, pt.y * scale_y); }); +#endif + return result; + } + + template + inline Path ScalePath(const Path& path, + double scale, int& error_code) + { + return ScalePath(path, scale, scale, error_code); + } + + template + inline Paths ScalePaths(const Paths& paths, + double scale_x, double scale_y, int& error_code) + { + Paths result; + + if constexpr (std::is_integral_v) + { + RectD r = GetBounds(paths); + if ((r.left * scale_x) < min_coord || + (r.right * scale_x) > max_coord || + (r.top * scale_y) < min_coord || + (r.bottom * scale_y) > max_coord) + { + error_code |= range_error_i; + DoError(range_error_i); + return result; // empty path + } + } + + result.reserve(paths.size()); + std::transform(paths.begin(), paths.end(), back_inserter(result), + [=, &error_code](const auto& path) + { return ScalePath(path, scale_x, scale_y, error_code); }); + return result; + } + + template + inline Paths ScalePaths(const Paths& paths, + double scale, int& error_code) + { + return ScalePaths(paths, scale, scale, error_code); + } + + template + inline Path TransformPath(const Path& path) + { + Path result; + result.reserve(path.size()); + std::transform(path.cbegin(), path.cend(), std::back_inserter(result), + [](const Point& pt) {return Point(pt); }); + return result; + } + + template + inline Paths TransformPaths(const Paths& paths) + { + Paths result; + std::transform(paths.cbegin(), paths.cend(), std::back_inserter(result), + [](const Path& path) {return TransformPath(path); }); + return result; + } + + template + inline double Sqr(T val) + { + return static_cast(val) * static_cast(val); + } + + template + inline bool NearEqual(const Point& p1, + const Point& p2, double max_dist_sqrd) + { + return Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y) < max_dist_sqrd; + } + + template + inline Path StripNearEqual(const Path& path, + double max_dist_sqrd, bool is_closed_path) + { + if (path.size() == 0) return Path(); + Path result; + result.reserve(path.size()); + typename Path::const_iterator path_iter = path.cbegin(); + Point first_pt = *path_iter++, last_pt = first_pt; + result.push_back(first_pt); + for (; path_iter != path.cend(); ++path_iter) + { + if (!NearEqual(*path_iter, last_pt, max_dist_sqrd)) + { + last_pt = *path_iter; + result.push_back(last_pt); + } + } + if (!is_closed_path) return result; + while (result.size() > 1 && + NearEqual(result.back(), first_pt, max_dist_sqrd)) result.pop_back(); + return result; + } + + template + inline Paths StripNearEqual(const Paths& paths, + double max_dist_sqrd, bool is_closed_path) + { + Paths result; + result.reserve(paths.size()); + for (typename Paths::const_iterator paths_citer = paths.cbegin(); + paths_citer != paths.cend(); ++paths_citer) + { + result.push_back(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path)); + } + return result; + } + + template + inline void StripDuplicates( Path& path, bool is_closed_path) + { + //https://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector#:~:text=Let%27s%20compare%20three%20approaches%3A + path.erase(std::unique(path.begin(), path.end()), path.end()); + if (is_closed_path) + while (path.size() > 1 && path.back() == path.front()) path.pop_back(); + } + + template + inline void StripDuplicates( Paths& paths, bool is_closed_path) + { + for (typename Paths::iterator paths_citer = paths.begin(); + paths_citer != paths.end(); ++paths_citer) + { + StripDuplicates(*paths_citer, is_closed_path); + } + } + + // Miscellaneous ------------------------------------------------------------ + + inline void CheckPrecisionRange(int& precision, int& error_code) + { + if (precision >= -CLIPPER2_MAX_DEC_PRECISION && + precision <= CLIPPER2_MAX_DEC_PRECISION) return; + error_code |= precision_error_i; // non-fatal error + DoError(precision_error_i); // does nothing when exceptions are disabled + precision = precision > 0 ? CLIPPER2_MAX_DEC_PRECISION : -CLIPPER2_MAX_DEC_PRECISION; + } + + inline void CheckPrecisionRange(int& precision) + { + int error_code = 0; + CheckPrecisionRange(precision, error_code); + } + + inline int TriSign(int64_t x) // returns 0, 1 or -1 + { + return (x > 0) - (x < 0); + } + + struct MultiplyUInt64Result + { + const uint64_t result = 0; + const uint64_t carry = 0; + + bool operator==(const MultiplyUInt64Result& other) const + { + return result == other.result && carry == other.carry; + }; + }; + + inline MultiplyUInt64Result Multiply(uint64_t a, uint64_t b) // #834, #835 + { + const auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; }; + const auto hi = [](uint64_t x) { return x >> 32; }; + + const uint64_t x1 = lo(a) * lo(b); + const uint64_t x2 = hi(a) * lo(b) + hi(x1); + const uint64_t x3 = lo(a) * hi(b) + lo(x2); + const uint64_t result = lo(x3) << 32 | lo(x1); + const uint64_t carry = hi(a) * hi(b) + hi(x2) + hi(x3); + + return { result, carry }; + } + + // returns true if (and only if) a * b == c * d + inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d) + { +#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX + const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b); + const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d); + return ab == cd; +#else + // nb: unsigned values needed for calculating overflow carry + const auto abs_a = static_cast(std::abs(a)); + const auto abs_b = static_cast(std::abs(b)); + const auto abs_c = static_cast(std::abs(c)); + const auto abs_d = static_cast(std::abs(d)); + + const auto abs_ab = Multiply(abs_a, abs_b); + const auto abs_cd = Multiply(abs_c, abs_d); + + // nb: it's important to differentiate 0 values here from other values + const auto sign_ab = TriSign(a) * TriSign(b); + const auto sign_cd = TriSign(c) * TriSign(d); + + return abs_ab == abs_cd && sign_ab == sign_cd; +#endif + } + + template + inline bool IsCollinear(const Point& pt1, + const Point& sharedPt, const Point& pt2) // #777 + { + const auto a = sharedPt.x - pt1.x; + const auto b = pt2.y - sharedPt.y; + const auto c = sharedPt.y - pt1.y; + const auto d = pt2.x - sharedPt.x; + // When checking for collinearity with very large coordinate values + // then ProductsAreEqual is more accurate than using CrossProduct. + return ProductsAreEqual(a, b, c, d); + } + + + template + inline double CrossProduct(const Point& pt1, const Point& pt2, const Point& pt3) { + return (static_cast(pt2.x - pt1.x) * static_cast(pt3.y - + pt2.y) - static_cast(pt2.y - pt1.y) * static_cast(pt3.x - pt2.x)); + } + + template + inline double CrossProduct(const Point& vec1, const Point& vec2) + { + return static_cast(vec1.y * vec2.x) - static_cast(vec2.y * vec1.x); + } + + template + inline double DotProduct(const Point& pt1, const Point& pt2, const Point& pt3) { + return (static_cast(pt2.x - pt1.x) * static_cast(pt3.x - pt2.x) + + static_cast(pt2.y - pt1.y) * static_cast(pt3.y - pt2.y)); + } + + template + inline double DotProduct(const Point& vec1, const Point& vec2) + { + return static_cast(vec1.x * vec2.x) + static_cast(vec1.y * vec2.y); + } + + template + inline double DistanceSqr(const Point pt1, const Point pt2) + { + return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y); + } + + template + inline double PerpendicDistFromLineSqrd(const Point& pt, + const Point& line1, const Point& line2) + { + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double a = static_cast(pt.x - line1.x); + double b = static_cast(pt.y - line1.y); + double c = static_cast(line2.x - line1.x); + double d = static_cast(line2.y - line1.y); + if (c == 0 && d == 0) return 0; + return Sqr(a * d - c * b) / (c * c + d * d); + } + + template + inline double Area(const Path& path) + { + size_t cnt = path.size(); + if (cnt < 3) return 0.0; + double a = 0.0; + typename Path::const_iterator it1, it2 = path.cend() - 1, stop = it2; + if (!(cnt & 1)) ++stop; + for (it1 = path.cbegin(); it1 != stop;) + { + a += static_cast(it2->y + it1->y) * (it2->x - it1->x); + it2 = it1 + 1; + a += static_cast(it1->y + it2->y) * (it1->x - it2->x); + it1 += 2; + } + if (cnt & 1) + a += static_cast(it2->y + it1->y) * (it2->x - it1->x); + return (a * 0.5); + } + + template + inline double Area(const Paths& paths) + { + double a = 0.0; + for (typename Paths::const_iterator paths_iter = paths.cbegin(); + paths_iter != paths.cend(); ++paths_iter) + { + a += Area(*paths_iter); + } + return a; + } + + template + inline bool IsPositive(const Path& poly) + { + // A curve has positive orientation [and area] if a region 'R' + // is on the left when traveling around the outside of 'R'. + //https://mathworld.wolfram.com/CurveOrientation.html + //nb: This statement is premised on using Cartesian coordinates + return Area(poly) >= 0; + } + +#if CLIPPER2_HI_PRECISION + // caution: this will compromise performance + // https://github.com/AngusJohnson/Clipper2/issues/317#issuecomment-1314023253 + // See also CPP/BenchMark/GetIntersectPtBenchmark.cpp + #define CC_MIN(x,y) ((x)>(y)?(y):(x)) + #define CC_MAX(x,y) ((x)<(y)?(y):(x)) + template + inline bool GetSegmentIntersectPt(const Point& ln1a, const Point& ln1b, + const Point& ln2a, const Point& ln2b, Point& ip) + { + double ln1dy = static_cast(ln1b.y - ln1a.y); + double ln1dx = static_cast(ln1a.x - ln1b.x); + double ln2dy = static_cast(ln2b.y - ln2a.y); + double ln2dx = static_cast(ln2a.x - ln2b.x); + double det = (ln2dy * ln1dx) - (ln1dy * ln2dx); + if (det == 0.0) return false; + T bb0minx = CC_MIN(ln1a.x, ln1b.x); + T bb0miny = CC_MIN(ln1a.y, ln1b.y); + T bb0maxx = CC_MAX(ln1a.x, ln1b.x); + T bb0maxy = CC_MAX(ln1a.y, ln1b.y); + T bb1minx = CC_MIN(ln2a.x, ln2b.x); + T bb1miny = CC_MIN(ln2a.y, ln2b.y); + T bb1maxx = CC_MAX(ln2a.x, ln2b.x); + T bb1maxy = CC_MAX(ln2a.y, ln2b.y); + + if constexpr (std::is_integral_v) + { + int64_t originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) >> 1; + int64_t originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) >> 1; + double ln0c = (ln1dy * static_cast(ln1a.x - originx)) + + (ln1dx * static_cast(ln1a.y - originy)); + double ln1c = (ln2dy * static_cast(ln2a.x - originx)) + + (ln2dx * static_cast(ln2a.y - originy)); + double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det; + double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det; + + ip.x = originx + (T)nearbyint(hitx); + ip.y = originy + (T)nearbyint(hity); + } + else + { + double originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) / 2.0; + double originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) / 2.0; + double ln0c = (ln1dy * static_cast(ln1a.x - originx)) + + (ln1dx * static_cast(ln1a.y - originy)); + double ln1c = (ln2dy * static_cast(ln2a.x - originx)) + + (ln2dx * static_cast(ln2a.y - originy)); + double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det; + double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det; + + ip.x = originx + static_cast(hitx); + ip.y = originy + static_cast(hity); + } + return true; +} +#else + template + inline bool GetSegmentIntersectPt(const Point& ln1a, const Point& ln1b, + const Point& ln2a, const Point& ln2b, Point& ip) + { + // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection + double dx1 = static_cast(ln1b.x - ln1a.x); + double dy1 = static_cast(ln1b.y - ln1a.y); + double dx2 = static_cast(ln2b.x - ln2a.x); + double dy2 = static_cast(ln2b.y - ln2a.y); + + double det = dy1 * dx2 - dy2 * dx1; + if (det == 0.0) return false; + double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det; + if (t <= 0.0) ip = ln1a; + else if (t >= 1.0) ip = ln1b; + else + { + ip.x = static_cast(ln1a.x + t * dx1); + ip.y = static_cast(ln1a.y + t * dy1); + } + return true; + } +#endif + + template + inline Point TranslatePoint(const Point& pt, double dx, double dy) + { +#ifdef USINGZ + return Point(pt.x + dx, pt.y + dy, pt.z); +#else + return Point(pt.x + dx, pt.y + dy); +#endif + } + + + template + inline Point ReflectPoint(const Point& pt, const Point& pivot) + { +#ifdef USINGZ + return Point(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z); +#else + return Point(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y)); +#endif + } + + template + inline int GetSign(const T& val) + { + if (!val) return 0; + return (val > 0) ? 1 : -1; + } + + inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b, + const Point64& seg2a, const Point64& seg2b, bool inclusive = false) + { + if (inclusive) + { + double res1 = CrossProduct(seg1a, seg2a, seg2b); + double res2 = CrossProduct(seg1b, seg2a, seg2b); + if (res1 * res2 > 0) return false; + double res3 = CrossProduct(seg2a, seg1a, seg1b); + double res4 = CrossProduct(seg2b, seg1a, seg1b); + if (res3 * res4 > 0) return false; + return (res1 || res2 || res3 || res4); // ensures not collinear + } + else { + return (GetSign(CrossProduct(seg1a, seg2a, seg2b)) * + GetSign(CrossProduct(seg1b, seg2a, seg2b)) < 0) && + (GetSign(CrossProduct(seg2a, seg1a, seg1b)) * + GetSign(CrossProduct(seg2b, seg1a, seg1b)) < 0); + } + } + + template + inline Point GetClosestPointOnSegment(const Point& offPt, + const Point& seg1, const Point& seg2) + { + if (seg1.x == seg2.x && seg1.y == seg2.y) return seg1; + double dx = static_cast(seg2.x - seg1.x); + double dy = static_cast(seg2.y - seg1.y); + double q = + (static_cast(offPt.x - seg1.x) * dx + + static_cast(offPt.y - seg1.y) * dy) / + (Sqr(dx) + Sqr(dy)); + if (q < 0) q = 0; else if (q > 1) q = 1; + if constexpr (std::is_integral_v) + return Point( + seg1.x + static_cast(nearbyint(q * dx)), + seg1.y + static_cast(nearbyint(q * dy))); + else + return Point( + seg1.x + static_cast(q * dx), + seg1.y + static_cast(q * dy)); + } + + enum class PointInPolygonResult { IsOn, IsInside, IsOutside }; + + template + inline PointInPolygonResult PointInPolygon(const Point& pt, const Path& polygon) + { + if (polygon.size() < 3) + return PointInPolygonResult::IsOutside; + + int val = 0; + typename Path::const_iterator cbegin = polygon.cbegin(), first = cbegin, curr, prev; + typename Path::const_iterator cend = polygon.cend(); + + while (first != cend && first->y == pt.y) ++first; + if (first == cend) // not a proper polygon + return PointInPolygonResult::IsOutside; + + bool is_above = first->y < pt.y, starting_above = is_above; + curr = first +1; + while (true) + { + if (curr == cend) + { + if (cend == first || first == cbegin) break; + cend = first; + curr = cbegin; + } + + if (is_above) + { + while (curr != cend && curr->y < pt.y) ++curr; + if (curr == cend) continue; + } + else + { + while (curr != cend && curr->y > pt.y) ++curr; + if (curr == cend) continue; + } + + if (curr == cbegin) + prev = polygon.cend() - 1; //nb: NOT cend (since might equal first) + else + prev = curr - 1; + + if (curr->y == pt.y) + { + if (curr->x == pt.x || + (curr->y == prev->y && + ((pt.x < prev->x) != (pt.x < curr->x)))) + return PointInPolygonResult::IsOn; + ++curr; + if (curr == first) break; + continue; + } + + if (pt.x < curr->x && pt.x < prev->x) + { + // we're only interested in edges crossing on the left + } + else if (pt.x > prev->x && pt.x > curr->x) + val = 1 - val; // toggle val + else + { + double d = CrossProduct(*prev, *curr, pt); + if (d == 0) return PointInPolygonResult::IsOn; + if ((d < 0) == is_above) val = 1 - val; + } + is_above = !is_above; + ++curr; + } + + if (is_above != starting_above) + { + cend = polygon.cend(); + if (curr == cend) curr = cbegin; + if (curr == cbegin) prev = cend - 1; + else prev = curr - 1; + double d = CrossProduct(*prev, *curr, pt); + if (d == 0) return PointInPolygonResult::IsOn; + if ((d < 0) == is_above) val = 1 - val; + } + + return (val == 0) ? + PointInPolygonResult::IsOutside : + PointInPolygonResult::IsInside; + } + +} // namespace + +#endif // CLIPPER_CORE_H diff --git a/KP3Dii/ext/include/clipper2/clipper.engine.h b/KP3Dii/ext/include/clipper2/clipper.engine.h new file mode 100644 index 0000000..f610883 --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.engine.h @@ -0,0 +1,641 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 5 July 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : This is the main polygon clipping module * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#ifndef CLIPPER_ENGINE_H +#define CLIPPER_ENGINE_H + +#include +#include //#541 +#include +#include +#include +#include +#include +#include + +#include "clipper2/clipper.core.h" + +namespace Clipper2Lib { + + struct Scanline; + struct IntersectNode; + struct Active; + struct Vertex; + struct LocalMinima; + struct OutRec; + struct HorzSegment; + + //Note: all clipping operations except for Difference are commutative. + enum class ClipType { None, Intersection, Union, Difference, Xor }; + + enum class PathType { Subject, Clip }; + enum class JoinWith { None, Left, Right }; + + enum class VertexFlags : uint32_t { + None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 + }; + + constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) + { + return (enum VertexFlags)(uint32_t(a) & uint32_t(b)); + } + + constexpr enum VertexFlags operator |(enum VertexFlags a, enum VertexFlags b) + { + return (enum VertexFlags)(uint32_t(a) | uint32_t(b)); + } + + struct Vertex { + Point64 pt; + Vertex* next = nullptr; + Vertex* prev = nullptr; + VertexFlags flags = VertexFlags::None; + }; + + struct OutPt { + Point64 pt; + OutPt* next = nullptr; + OutPt* prev = nullptr; + OutRec* outrec; + HorzSegment* horz = nullptr; + + OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) { + next = this; + prev = this; + } + }; + + class PolyPath; + class PolyPath64; + class PolyPathD; + using PolyTree64 = PolyPath64; + using PolyTreeD = PolyPathD; + + struct OutRec; + typedef std::vector OutRecList; + + //OutRec: contains a path in the clipping solution. Edges in the AEL will + //have OutRec pointers assigned when they form part of the clipping solution. + struct OutRec { + size_t idx = 0; + OutRec* owner = nullptr; + Active* front_edge = nullptr; + Active* back_edge = nullptr; + OutPt* pts = nullptr; + PolyPath* polypath = nullptr; + OutRecList* splits = nullptr; + OutRec* recursive_split = nullptr; + Rect64 bounds = {}; + Path64 path; + bool is_open = false; + + ~OutRec() { + if (splits) delete splits; + // nb: don't delete the split pointers + // as these are owned by ClipperBase's outrec_list_ + }; + }; + + /////////////////////////////////////////////////////////////////// + //Important: UP and DOWN here are premised on Y-axis positive down + //displays, which is the orientation used in Clipper's development. + /////////////////////////////////////////////////////////////////// + + struct Active { + Point64 bot; + Point64 top; + int64_t curr_x = 0; //current (updated at every new scanline) + double dx = 0.0; + int wind_dx = 1; //1 or -1 depending on winding direction + int wind_cnt = 0; + int wind_cnt2 = 0; //winding count of the opposite polytype + OutRec* outrec = nullptr; + //AEL: 'active edge list' (Vatti's AET - active edge table) + // a linked list of all edges (from left to right) that are present + // (or 'active') within the current scanbeam (a horizontal 'beam' that + // sweeps from bottom to top over the paths in the clipping operation). + Active* prev_in_ael = nullptr; + Active* next_in_ael = nullptr; + //SEL: 'sorted edge list' (Vatti's ST - sorted table) + // linked list used when sorting edges into their new positions at the + // top of scanbeams, but also (re)used to process horizontals. + Active* prev_in_sel = nullptr; + Active* next_in_sel = nullptr; + Active* jump = nullptr; + Vertex* vertex_top = nullptr; + LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti) + bool is_left_bound = false; + JoinWith join_with = JoinWith::None; + }; + + struct LocalMinima { + Vertex* vertex; + PathType polytype; + bool is_open; + LocalMinima(Vertex* v, PathType pt, bool open) : + vertex(v), polytype(pt), is_open(open){} + }; + + struct IntersectNode { + Point64 pt; + Active* edge1; + Active* edge2; + IntersectNode() : pt(Point64(0,0)), edge1(NULL), edge2(NULL) {} + IntersectNode(Active* e1, Active* e2, Point64& pt_) : + pt(pt_), edge1(e1), edge2(e2) {} + }; + + struct HorzSegment { + OutPt* left_op; + OutPt* right_op = nullptr; + bool left_to_right = true; + HorzSegment() : left_op(nullptr) { } + explicit HorzSegment(OutPt* op) : left_op(op) { } + }; + + struct HorzJoin { + OutPt* op1 = nullptr; + OutPt* op2 = nullptr; + HorzJoin() {}; + explicit HorzJoin(OutPt* ltr, OutPt* rtl) : op1(ltr), op2(rtl) { } + }; + +#ifdef USINGZ + typedef std::function ZCallback64; + + typedef std::function ZCallbackD; +#endif + + typedef std::vector HorzSegmentList; + typedef std::unique_ptr LocalMinima_ptr; + typedef std::vector LocalMinimaList; + typedef std::vector IntersectNodeList; + + // ReuseableDataContainer64 ------------------------------------------------ + + class ReuseableDataContainer64 { + private: + friend class ClipperBase; + LocalMinimaList minima_list_; + std::vector vertex_lists_; + void AddLocMin(Vertex& vert, PathType polytype, bool is_open); + public: + virtual ~ReuseableDataContainer64(); + void Clear(); + void AddPaths(const Paths64& paths, PathType polytype, bool is_open); + }; + + // ClipperBase ------------------------------------------------------------- + + class ClipperBase { + private: + ClipType cliptype_ = ClipType::None; + FillRule fillrule_ = FillRule::EvenOdd; + FillRule fillpos = FillRule::Positive; + int64_t bot_y_ = 0; + bool minima_list_sorted_ = false; + bool using_polytree_ = false; + Active* actives_ = nullptr; + Active *sel_ = nullptr; + LocalMinimaList minima_list_; //pointers in case of memory reallocs + LocalMinimaList::iterator current_locmin_iter_; + std::vector vertex_lists_; + std::priority_queue scanline_list_; + IntersectNodeList intersect_nodes_; + HorzSegmentList horz_seg_list_; + std::vector horz_join_list_; + void Reset(); + inline void InsertScanline(int64_t y); + inline bool PopScanline(int64_t &y); + inline bool PopLocalMinima(int64_t y, LocalMinima*& local_minima); + void DisposeAllOutRecs(); + void DisposeVerticesAndLocalMinima(); + void DeleteEdges(Active*& e); + inline void AddLocMin(Vertex &vert, PathType polytype, bool is_open); + bool IsContributingClosed(const Active &e) const; + inline bool IsContributingOpen(const Active &e) const; + void SetWindCountForClosedPathEdge(Active &edge); + void SetWindCountForOpenPathEdge(Active &e); + void InsertLocalMinimaIntoAEL(int64_t bot_y); + void InsertLeftEdge(Active &e); + inline void PushHorz(Active &e); + inline bool PopHorz(Active *&e); + inline OutPt* StartOpenPath(Active &e, const Point64& pt); + inline void UpdateEdgeIntoAEL(Active *e); + void IntersectEdges(Active &e1, Active &e2, const Point64& pt); + inline void DeleteFromAEL(Active &e); + inline void AdjustCurrXAndCopyToSEL(const int64_t top_y); + void DoIntersections(const int64_t top_y); + void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y); + bool BuildIntersectList(const int64_t top_y); + void ProcessIntersectList(); + void SwapPositionsInAEL(Active& edge1, Active& edge2); + OutRec* NewOutRec(); + OutPt* AddOutPt(const Active &e, const Point64& pt); + OutPt* AddLocalMinPoly(Active &e1, Active &e2, + const Point64& pt, bool is_new = false); + OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt); + void DoHorizontal(Active &horz); + bool ResetHorzDirection(const Active &horz, const Vertex* max_vertex, + int64_t &horz_left, int64_t &horz_right); + void DoTopOfScanbeam(const int64_t top_y); + Active *DoMaxima(Active &e); + void JoinOutrecPaths(Active &e1, Active &e2); + void FixSelfIntersects(OutRec* outrec); + void DoSplitOp(OutRec* outRec, OutPt* splitOp); + + inline void AddTrialHorzJoin(OutPt* op); + void ConvertHorzSegsToJoins(); + void ProcessHorzJoins(); + + void Split(Active& e, const Point64& pt); + inline void CheckJoinLeft(Active& e, + const Point64& pt, bool check_curr_x = false); + inline void CheckJoinRight(Active& e, + const Point64& pt, bool check_curr_x = false); + protected: + bool preserve_collinear_ = true; + bool reverse_solution_ = false; + int error_code_ = 0; + bool has_open_paths_ = false; + bool succeeded_ = true; + OutRecList outrec_list_; //pointers in case list memory reallocated + bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees); + void CleanCollinear(OutRec* outrec); + bool CheckBounds(OutRec* outrec); + bool CheckSplitOwner(OutRec* outrec, OutRecList* splits); + void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath); +#ifdef USINGZ + ZCallback64 zCallback_ = nullptr; + void SetZ(const Active& e1, const Active& e2, Point64& pt); +#endif + void CleanUp(); // unlike Clear, CleanUp preserves added paths + void AddPath(const Path64& path, PathType polytype, bool is_open); + void AddPaths(const Paths64& paths, PathType polytype, bool is_open); + public: + virtual ~ClipperBase(); + int ErrorCode() const { return error_code_; }; + void PreserveCollinear(bool val) { preserve_collinear_ = val; }; + bool PreserveCollinear() const { return preserve_collinear_;}; + void ReverseSolution(bool val) { reverse_solution_ = val; }; + bool ReverseSolution() const { return reverse_solution_; }; + void Clear(); + void AddReuseableData(const ReuseableDataContainer64& reuseable_data); +#ifdef USINGZ + int64_t DefaultZ = 0; +#endif + }; + + // PolyPath / PolyTree -------------------------------------------------------- + + //PolyTree: is intended as a READ-ONLY data structure for CLOSED paths returned + //by clipping operations. While this structure is more complex than the + //alternative Paths structure, it does preserve path 'ownership' - ie those + //paths that contain (or own) other paths. This will be useful to some users. + + class PolyPath { + protected: + PolyPath* parent_; + public: + PolyPath(PolyPath* parent = nullptr): parent_(parent){} + virtual ~PolyPath() {}; + //https://en.cppreference.com/w/cpp/language/rule_of_three + PolyPath(const PolyPath&) = delete; + PolyPath& operator=(const PolyPath&) = delete; + + unsigned Level() const + { + unsigned result = 0; + const PolyPath* p = parent_; + while (p) { ++result; p = p->parent_; } + return result; + } + + virtual PolyPath* AddChild(const Path64& path) = 0; + + virtual void Clear() = 0; + virtual size_t Count() const { return 0; } + + const PolyPath* Parent() const { return parent_; } + + bool IsHole() const + { + unsigned lvl = Level(); + //Even levels except level 0 + return lvl && !(lvl & 1); + } + }; + + typedef typename std::vector> PolyPath64List; + typedef typename std::vector> PolyPathDList; + + class PolyPath64 : public PolyPath { + private: + PolyPath64List childs_; + Path64 polygon_; + public: + explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {} + explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; } + + ~PolyPath64() { + childs_.resize(0); + } + + PolyPath64* operator [] (size_t index) const + { + return childs_[index].get(); //std::unique_ptr + } + + PolyPath64* Child(size_t index) const + { + return childs_[index].get(); + } + + PolyPath64List::const_iterator begin() const { return childs_.cbegin(); } + PolyPath64List::const_iterator end() const { return childs_.cend(); } + + PolyPath64* AddChild(const Path64& path) override + { + return childs_.emplace_back(std::make_unique(this, path)).get(); + } + + void Clear() override + { + childs_.resize(0); + } + + size_t Count() const override + { + return childs_.size(); + } + + const Path64& Polygon() const { return polygon_; }; + + double Area() const + { + return std::accumulate(childs_.cbegin(), childs_.cend(), + Clipper2Lib::Area(polygon_), + [](double a, const auto& child) {return a + child->Area(); }); + } + + }; + + class PolyPathD : public PolyPath { + private: + PolyPathDList childs_; + double scale_; + PathD polygon_; + public: + explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent) + { + scale_ = parent ? parent->scale_ : 1.0; + } + + explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent) + { + scale_ = parent ? parent->scale_ : 1.0; + int error_code = 0; + polygon_ = ScalePath(path, scale_, error_code); + } + + explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent) + { + scale_ = parent ? parent->scale_ : 1.0; + polygon_ = path; + } + + ~PolyPathD() { + childs_.resize(0); + } + + PolyPathD* operator [] (size_t index) const + { + return childs_[index].get(); + } + + PolyPathD* Child(size_t index) const + { + return childs_[index].get(); + } + + PolyPathDList::const_iterator begin() const { return childs_.cbegin(); } + PolyPathDList::const_iterator end() const { return childs_.cend(); } + + void SetScale(double value) { scale_ = value; } + double Scale() const { return scale_; } + + PolyPathD* AddChild(const Path64& path) override + { + return childs_.emplace_back(std::make_unique(this, path)).get(); + } + + PolyPathD* AddChild(const PathD& path) + { + return childs_.emplace_back(std::make_unique(this, path)).get(); + } + + void Clear() override + { + childs_.resize(0); + } + + size_t Count() const override + { + return childs_.size(); + } + + const PathD& Polygon() const { return polygon_; }; + + double Area() const + { + return std::accumulate(childs_.begin(), childs_.end(), + Clipper2Lib::Area(polygon_), + [](double a, const auto& child) {return a + child->Area(); }); + } + }; + + class Clipper64 : public ClipperBase + { + private: + void BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen); + void BuildTree64(PolyPath64& polytree, Paths64& open_paths); + public: +#ifdef USINGZ + void SetZCallback(ZCallback64 cb) { zCallback_ = cb; } +#endif + + void AddSubject(const Paths64& subjects) + { + AddPaths(subjects, PathType::Subject, false); + } + void AddOpenSubject(const Paths64& open_subjects) + { + AddPaths(open_subjects, PathType::Subject, true); + } + void AddClip(const Paths64& clips) + { + AddPaths(clips, PathType::Clip, false); + } + + bool Execute(ClipType clip_type, + FillRule fill_rule, Paths64& closed_paths) + { + Paths64 dummy; + return Execute(clip_type, fill_rule, closed_paths, dummy); + } + + bool Execute(ClipType clip_type, FillRule fill_rule, + Paths64& closed_paths, Paths64& open_paths) + { + closed_paths.clear(); + open_paths.clear(); + if (ExecuteInternal(clip_type, fill_rule, false)) + BuildPaths64(closed_paths, &open_paths); + CleanUp(); + return succeeded_; + } + + bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree) + { + Paths64 dummy; + return Execute(clip_type, fill_rule, polytree, dummy); + } + + bool Execute(ClipType clip_type, + FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths) + { + if (ExecuteInternal(clip_type, fill_rule, true)) + { + open_paths.clear(); + polytree.Clear(); + BuildTree64(polytree, open_paths); + } + CleanUp(); + return succeeded_; + } + }; + + class ClipperD : public ClipperBase { + private: + double scale_ = 1.0, invScale_ = 1.0; +#ifdef USINGZ + ZCallbackD zCallbackD_ = nullptr; +#endif + void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen); + void BuildTreeD(PolyPathD& polytree, PathsD& open_paths); + public: + explicit ClipperD(int precision = 2) : ClipperBase() + { + CheckPrecisionRange(precision, error_code_); + // to optimize scaling / descaling precision + // set the scale to a power of double's radix (2) (#25) + scale_ = std::pow(std::numeric_limits::radix, + std::ilogb(std::pow(10, precision)) + 1); + invScale_ = 1 / scale_; + } + +#ifdef USINGZ + void SetZCallback(ZCallbackD cb) { zCallbackD_ = cb; }; + + void ZCB(const Point64& e1bot, const Point64& e1top, + const Point64& e2bot, const Point64& e2top, Point64& pt) + { + // de-scale (x & y) + // temporarily convert integers to their initial float values + // this will slow clipping marginally but will make it much easier + // to understand the coordinates passed to the callback function + PointD tmp = PointD(pt) * invScale_; + PointD e1b = PointD(e1bot) * invScale_; + PointD e1t = PointD(e1top) * invScale_; + PointD e2b = PointD(e2bot) * invScale_; + PointD e2t = PointD(e2top) * invScale_; + zCallbackD_(e1b,e1t, e2b, e2t, tmp); + pt.z = tmp.z; // only update 'z' + }; + + void CheckCallback() + { + if(zCallbackD_) + // if the user defined float point callback has been assigned + // then assign the proxy callback function + ClipperBase::zCallback_ = + std::bind(&ClipperD::ZCB, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5); + else + ClipperBase::zCallback_ = nullptr; + } + +#endif + + void AddSubject(const PathsD& subjects) + { + AddPaths(ScalePaths(subjects, scale_, error_code_), PathType::Subject, false); + } + + void AddOpenSubject(const PathsD& open_subjects) + { + AddPaths(ScalePaths(open_subjects, scale_, error_code_), PathType::Subject, true); + } + + void AddClip(const PathsD& clips) + { + AddPaths(ScalePaths(clips, scale_, error_code_), PathType::Clip, false); + } + + bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths) + { + PathsD dummy; + return Execute(clip_type, fill_rule, closed_paths, dummy); + } + + bool Execute(ClipType clip_type, + FillRule fill_rule, PathsD& closed_paths, PathsD& open_paths) + { +#ifdef USINGZ + CheckCallback(); +#endif + if (ExecuteInternal(clip_type, fill_rule, false)) + { + BuildPathsD(closed_paths, &open_paths); + } + CleanUp(); + return succeeded_; + } + + bool Execute(ClipType clip_type, FillRule fill_rule, PolyTreeD& polytree) + { + PathsD dummy; + return Execute(clip_type, fill_rule, polytree, dummy); + } + + bool Execute(ClipType clip_type, + FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths) + { +#ifdef USINGZ + CheckCallback(); +#endif + if (ExecuteInternal(clip_type, fill_rule, true)) + { + polytree.Clear(); + polytree.SetScale(invScale_); + open_paths.clear(); + BuildTreeD(polytree, open_paths); + } + CleanUp(); + return succeeded_; + } + + }; + +} // namespace + +#endif // CLIPPER_ENGINE_H diff --git a/KP3Dii/ext/include/clipper2/clipper.export.h b/KP3Dii/ext/include/clipper2/clipper.export.h new file mode 100644 index 0000000..53a4453 --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.export.h @@ -0,0 +1,600 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 14 May 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : This module exports the Clipper2 Library (ie DLL/so) * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + + +/* + Boolean clipping: + cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4 + fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3 + + Polygon offsetting (inflate/deflate): + jointype: Square=0, Bevel=1, Round=2, Miter=3 + endtype: Polygon=0, Joined=1, Butt=2, Square=3, Round=4 + +The path structures used extensively in other parts of this library are all +based on std::vector classes. Since C++ classes can't be accessed by other +languages, these paths are converted into very simple array data structures +(of either int64_t for CPath64 or double for CPathD) that can be parsed by +just about any programming language. + +CPath64 and CPathD: +These are arrays of consecutive x and y path coordinates preceeded by +a pair of values containing the path's length (N) and a 0 value. +__________________________________ +|counter|coord1|coord2|...|coordN| +|N, 0 |x1, y1|x2, y2|...|xN, yN| +__________________________________ + +CPaths64 and CPathsD: +These are also arrays containing any number of consecutive CPath64 or +CPathD structures. But preceeding these consecutive paths, there is pair of +values that contain the total length of the array structure (A) and the +number of CPath64 or CPathD it contains (C). The space these structures will +occupy in memory = A * sizeof(int64_t) or A * sizeof(double) respectively. +_______________________________ +|counter|path1|path2|...|pathC| +|A , C | | +_______________________________ + +CPolytree64 and CPolytreeD: +These are also arrays consisting of CPolyPath structures that represent +individual paths in a tree structure. However, the very first (ie top) +CPolyPath is just the tree container that doesn't have a path. And because +of that, its structure will be very slightly different from the remaining +CPolyPath. This difference will be discussed below. + +CPolyPath64 and CPolyPathD: +These are simple arrays consisting of a series of path coordinates followed +by any number of child (ie nested) CPolyPath. Preceeding these are two values +indicating the length of the path (N) and the number of child CPolyPath (C). +____________________________________________________________ +|counter|coord1|coord2|...|coordN| child1|child2|...|childC| +|N , C |x1, y1|x2, y2|...|xN, yN| | +____________________________________________________________ + +As mentioned above, the very first CPolyPath structure is just a container +that owns (both directly and indirectly) every other CPolyPath in the tree. +Since this first CPolyPath has no path, instead of a path length, its very +first value will contain the total length of the CPolytree array (not its +total bytes length). + +Again, all theses exported structures (CPaths64, CPathsD, CPolyTree64 & +CPolyTreeD) are arrays of either type int64_t or double, and the first +value in these arrays will always be the length of that array. + +These array structures are allocated in heap memory which will eventually +need to be released. However, since applications dynamically linking to +these functions may use different memory managers, the only safe way to +free up this memory is to use the exported DisposeArray64 and +DisposeArrayD functions (see below). +*/ + + +#ifndef CLIPPER2_EXPORT_H +#define CLIPPER2_EXPORT_H + +#include +#include + +#include "clipper2/clipper.core.h" +#include "clipper2/clipper.engine.h" +#include "clipper2/clipper.offset.h" +#include "clipper2/clipper.rectclip.h" + +namespace Clipper2Lib { + +typedef int64_t* CPath64; +typedef int64_t* CPaths64; +typedef double* CPathD; +typedef double* CPathsD; + +typedef int64_t* CPolyPath64; +typedef int64_t* CPolyTree64; +typedef double* CPolyPathD; +typedef double* CPolyTreeD; + +template +struct CRect { + T left; + T top; + T right; + T bottom; +}; + +typedef CRect CRect64; +typedef CRect CRectD; + +template +inline bool CRectIsEmpty(const CRect& rect) +{ + return (rect.right <= rect.left) || (rect.bottom <= rect.top); +} + +template +inline Rect CRectToRect(const CRect& rect) +{ + Rect result; + result.left = rect.left; + result.top = rect.top; + result.right = rect.right; + result.bottom = rect.bottom; + return result; +} + +#ifdef _WIN32 + #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport) +#else + #define EXTERN_DLL_EXPORT extern "C" +#endif + + +////////////////////////////////////////////////////// +// EXPORTED FUNCTION DECLARATIONS +////////////////////////////////////////////////////// + +EXTERN_DLL_EXPORT const char* Version(); + +EXTERN_DLL_EXPORT void DisposeArray64(int64_t*& p) +{ + delete[] p; +} + +EXTERN_DLL_EXPORT void DisposeArrayD(double*& p) +{ + delete[] p; +} + +EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, + uint8_t fillrule, const CPaths64 subjects, + const CPaths64 subjects_open, const CPaths64 clips, + CPaths64& solution, CPaths64& solution_open, + bool preserve_collinear = true, bool reverse_solution = false); + +EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype, + uint8_t fillrule, const CPaths64 subjects, + const CPaths64 subjects_open, const CPaths64 clips, + CPolyTree64& sol_tree, CPaths64& solution_open, + bool preserve_collinear = true, bool reverse_solution = false); + +EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype, + uint8_t fillrule, const CPathsD subjects, + const CPathsD subjects_open, const CPathsD clips, + CPathsD& solution, CPathsD& solution_open, int precision = 2, + bool preserve_collinear = true, bool reverse_solution = false); + +EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype, + uint8_t fillrule, const CPathsD subjects, + const CPathsD subjects_open, const CPathsD clips, + CPolyTreeD& solution, CPathsD& solution_open, int precision = 2, + bool preserve_collinear = true, bool reverse_solution = false); + +EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, + double delta, uint8_t jointype, uint8_t endtype, + double miter_limit = 2.0, double arc_tolerance = 0.0, + bool reverse_solution = false); +EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, + double delta, uint8_t jointype, uint8_t endtype, + int precision = 2, double miter_limit = 2.0, + double arc_tolerance = 0.0, bool reverse_solution = false); + +// RectClip & RectClipLines: +EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, + const CPaths64 paths); +EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, + const CPathsD paths, int precision = 2); +EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, + const CPaths64 paths); +EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, + const CPathsD paths, int precision = 2); + +////////////////////////////////////////////////////// +// INTERNAL FUNCTIONS +////////////////////////////////////////////////////// + +template +static void GetPathCountAndCPathsArrayLen(const Paths& paths, + size_t& cnt, size_t& array_len) +{ + array_len = 2; + cnt = 0; + for (const Path& path : paths) + if (path.size()) + { + array_len += path.size() * 2 + 2; + ++cnt; + } +} + +static size_t GetPolyPath64ArrayLen(const PolyPath64& pp) +{ + size_t result = 2; // poly_length + child_count + result += pp.Polygon().size() * 2; + //plus nested children :) + for (size_t i = 0; i < pp.Count(); ++i) + result += GetPolyPath64ArrayLen(*pp[i]); + return result; +} + +static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree, + size_t& cnt, size_t& array_len) +{ + cnt = tree.Count(); // nb: top level count only + array_len = GetPolyPath64ArrayLen(tree); +} + +template +static T* CreateCPaths(const Paths& paths) +{ + size_t cnt = 0, array_len = 0; + GetPathCountAndCPathsArrayLen(paths, cnt, array_len); + T* result = new T[array_len], * v = result; + *v++ = array_len; + *v++ = cnt; + for (const Path& path : paths) + { + if (!path.size()) continue; + *v++ = path.size(); + *v++ = 0; + for (const Point& pt : path) + { + *v++ = pt.x; + *v++ = pt.y; + } + } + return result; +} + + +CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale) +{ + if (!paths.size()) return nullptr; + size_t cnt, array_len; + GetPathCountAndCPathsArrayLen(paths, cnt, array_len); + CPathsD result = new double[array_len], v = result; + *v++ = (double)array_len; + *v++ = (double)cnt; + for (const Path64& path : paths) + { + if (!path.size()) continue; + *v = (double)path.size(); + ++v; *v++ = 0; + for (const Point64& pt : path) + { + *v++ = pt.x * scale; + *v++ = pt.y * scale; + } + } + return result; +} + +template +static Path ConvertCPath(T* path) +{ + Path result; + if (!path) return result; + T* v = path; + size_t cnt = static_cast(*v); + v += 2; // skip 0 value + result.reserve(cnt); + for (size_t j = 0; j < cnt; ++j) + { + T x = *v++, y = *v++; + result.push_back(Point(x, y)); + } + return result; +} + +template +static Paths ConvertCPaths(T* paths) +{ + Paths result; + if (!paths) return result; + T* v = paths; ++v; + size_t cnt = static_cast(*v++); + result.reserve(cnt); + for (size_t i = 0; i < cnt; ++i) + { + size_t cnt2 = static_cast(*v); + v += 2; + Path path; + path.reserve(cnt2); + for (size_t j = 0; j < cnt2; ++j) + { + T x = *v++, y = *v++; + path.push_back(Point(x, y)); + } + result.push_back(path); + } + return result; +} + + +static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale) +{ + Paths64 result; + if (!paths) return result; + double* v = paths; + ++v; // skip the first value (0) + size_t cnt = static_cast(*v++); + result.reserve(cnt); + for (size_t i = 0; i < cnt; ++i) + { + size_t cnt2 = static_cast(*v); + v += 2; + Path64 path; + path.reserve(cnt2); + for (size_t j = 0; j < cnt2; ++j) + { + double x = *v++ * scale; + double y = *v++ * scale; + path.push_back(Point64(x, y)); + } + result.push_back(path); + } + return result; +} + +template +static void CreateCPolyPath(const PolyPath64* pp, T*& v, T scale) +{ + *v++ = static_cast(pp->Polygon().size()); + *v++ = static_cast(pp->Count()); + for (const Point64& pt : pp->Polygon()) + { + *v++ = static_cast(pt.x * scale); + *v++ = static_cast(pt.y * scale); + } + for (size_t i = 0; i < pp->Count(); ++i) + CreateCPolyPath(pp->Child(i), v, scale); +} + +template +static T* CreateCPolyTree(const PolyTree64& tree, T scale) +{ + if (scale == 0) scale = 1; + size_t cnt, array_len; + GetPolytreeCountAndCStorageSize(tree, cnt, array_len); + if (!cnt) return nullptr; + // allocate storage + T* result = new T[array_len]; + T* v = result; + + *v++ = static_cast(array_len); + *v++ = static_cast(tree.Count()); + for (size_t i = 0; i < tree.Count(); ++i) + CreateCPolyPath(tree.Child(i), v, scale); + return result; +} + +////////////////////////////////////////////////////// +// EXPORTED FUNCTION DEFINITIONS +////////////////////////////////////////////////////// + +EXTERN_DLL_EXPORT const char* Version() +{ + return CLIPPER2_VERSION; +} + +EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, + uint8_t fillrule, const CPaths64 subjects, + const CPaths64 subjects_open, const CPaths64 clips, + CPaths64& solution, CPaths64& solution_open, + bool preserve_collinear, bool reverse_solution) +{ + if (cliptype > static_cast(ClipType::Xor)) return -4; + if (fillrule > static_cast(FillRule::Negative)) return -3; + + Paths64 sub, sub_open, clp, sol, sol_open; + sub = ConvertCPaths(subjects); + sub_open = ConvertCPaths(subjects_open); + clp = ConvertCPaths(clips); + + Clipper64 clipper; + clipper.PreserveCollinear(preserve_collinear); + clipper.ReverseSolution(reverse_solution); + if (sub.size() > 0) clipper.AddSubject(sub); + if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); + if (clp.size() > 0) clipper.AddClip(clp); + if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) + return -1; // clipping bug - should never happen :) + solution = CreateCPaths(sol); + solution_open = CreateCPaths(sol_open); + return 0; //success !! +} + +EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype, + uint8_t fillrule, const CPaths64 subjects, + const CPaths64 subjects_open, const CPaths64 clips, + CPolyTree64& sol_tree, CPaths64& solution_open, + bool preserve_collinear, bool reverse_solution) +{ + if (cliptype > static_cast(ClipType::Xor)) return -4; + if (fillrule > static_cast(FillRule::Negative)) return -3; + Paths64 sub, sub_open, clp, sol_open; + sub = ConvertCPaths(subjects); + sub_open = ConvertCPaths(subjects_open); + clp = ConvertCPaths(clips); + + PolyTree64 tree; + Clipper64 clipper; + clipper.PreserveCollinear(preserve_collinear); + clipper.ReverseSolution(reverse_solution); + if (sub.size() > 0) clipper.AddSubject(sub); + if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); + if (clp.size() > 0) clipper.AddClip(clp); + if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open)) + return -1; // clipping bug - should never happen :) + + sol_tree = CreateCPolyTree(tree, (int64_t)1); + solution_open = CreateCPaths(sol_open); + return 0; //success !! +} + +EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype, + uint8_t fillrule, const CPathsD subjects, + const CPathsD subjects_open, const CPathsD clips, + CPathsD& solution, CPathsD& solution_open, int precision, + bool preserve_collinear, bool reverse_solution) +{ + if (precision < -8 || precision > 8) return -5; + if (cliptype > static_cast(ClipType::Xor)) return -4; + if (fillrule > static_cast(FillRule::Negative)) return -3; + const double scale = std::pow(10, precision); + + Paths64 sub, sub_open, clp, sol, sol_open; + sub = ConvertCPathsDToPaths64(subjects, scale); + sub_open = ConvertCPathsDToPaths64(subjects_open, scale); + clp = ConvertCPathsDToPaths64(clips, scale); + + Clipper64 clipper; + clipper.PreserveCollinear(preserve_collinear); + clipper.ReverseSolution(reverse_solution); + if (sub.size() > 0) clipper.AddSubject(sub); + if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); + if (clp.size() > 0) clipper.AddClip(clp); + if (!clipper.Execute(ClipType(cliptype), + FillRule(fillrule), sol, sol_open)) return -1; + solution = CreateCPathsDFromPaths64(sol, 1 / scale); + solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale); + return 0; +} + +EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype, + uint8_t fillrule, const CPathsD subjects, + const CPathsD subjects_open, const CPathsD clips, + CPolyTreeD& solution, CPathsD& solution_open, int precision, + bool preserve_collinear, bool reverse_solution) +{ + if (precision < -8 || precision > 8) return -5; + if (cliptype > static_cast(ClipType::Xor)) return -4; + if (fillrule > static_cast(FillRule::Negative)) return -3; + + double scale = std::pow(10, precision); + + int err = 0; + Paths64 sub, sub_open, clp, sol_open; + sub = ConvertCPathsDToPaths64(subjects, scale); + sub_open = ConvertCPathsDToPaths64(subjects_open, scale); + clp = ConvertCPathsDToPaths64(clips, scale); + + PolyTree64 tree; + Clipper64 clipper; + clipper.PreserveCollinear(preserve_collinear); + clipper.ReverseSolution(reverse_solution); + if (sub.size() > 0) clipper.AddSubject(sub); + if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); + if (clp.size() > 0) clipper.AddClip(clp); + if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open)) + return -1; // clipping bug - should never happen :) + + solution = CreateCPolyTree(tree, 1/scale); + solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale); + return 0; //success !! +} + +EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, + double delta, uint8_t jointype, uint8_t endtype, double miter_limit, + double arc_tolerance, bool reverse_solution) +{ + Paths64 pp; + pp = ConvertCPaths(paths); + ClipperOffset clip_offset( miter_limit, + arc_tolerance, reverse_solution); + clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); + Paths64 result; + clip_offset.Execute(delta, result); + return CreateCPaths(result); +} + +EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, + double delta, uint8_t jointype, uint8_t endtype, + int precision, double miter_limit, + double arc_tolerance, bool reverse_solution) +{ + if (precision < -8 || precision > 8 || !paths) return nullptr; + + const double scale = std::pow(10, precision); + ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution); + Paths64 pp = ConvertCPathsDToPaths64(paths, scale); + clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); + Paths64 result; + clip_offset.Execute(delta * scale, result); + + return CreateCPathsDFromPaths64(result, 1 / scale); +} + +EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths) +{ + if (CRectIsEmpty(rect) || !paths) return nullptr; + Rect64 r64 = CRectToRect(rect); + class RectClip64 rc(r64); + Paths64 pp = ConvertCPaths(paths); + Paths64 result = rc.Execute(pp); + return CreateCPaths(result); +} + +EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, const CPathsD paths, int precision) +{ + if (CRectIsEmpty(rect) || !paths) return nullptr; + if (precision < -8 || precision > 8) return nullptr; + const double scale = std::pow(10, precision); + + RectD r = CRectToRect(rect); + Rect64 rec = ScaleRect(r, scale); + Paths64 pp = ConvertCPathsDToPaths64(paths, scale); + class RectClip64 rc(rec); + Paths64 result = rc.Execute(pp); + + return CreateCPathsDFromPaths64(result, 1 / scale); +} + +EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, + const CPaths64 paths) +{ + if (CRectIsEmpty(rect) || !paths) return nullptr; + Rect64 r = CRectToRect(rect); + class RectClipLines64 rcl (r); + Paths64 pp = ConvertCPaths(paths); + Paths64 result = rcl.Execute(pp); + return CreateCPaths(result); +} + +EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, + const CPathsD paths, int precision) +{ + if (CRectIsEmpty(rect) || !paths) return nullptr; + if (precision < -8 || precision > 8) return nullptr; + + const double scale = std::pow(10, precision); + Rect64 r = ScaleRect(CRectToRect(rect), scale); + class RectClipLines64 rcl(r); + Paths64 pp = ConvertCPathsDToPaths64(paths, scale); + Paths64 result = rcl.Execute(pp); + return CreateCPathsDFromPaths64(result, 1 / scale); +} + +EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) +{ + Path64 path = ConvertCPath(cpath); + Path64 pattern = ConvertCPath(cpattern); + Paths64 solution = MinkowskiSum(pattern, path, is_closed); + return CreateCPaths(solution); +} + +EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) +{ + Path64 path = ConvertCPath(cpath); + Path64 pattern = ConvertCPath(cpattern); + Paths64 solution = MinkowskiDiff(pattern, path, is_closed); + return CreateCPaths(solution); +} + +} // end Clipper2Lib namespace + +#endif // CLIPPER2_EXPORT_H diff --git a/KP3Dii/ext/include/clipper2/clipper.h b/KP3Dii/ext/include/clipper2/clipper.h new file mode 100644 index 0000000..a2fe5c3 --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.h @@ -0,0 +1,769 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 27 April 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : This module provides a simple interface to the Clipper Library * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#ifndef CLIPPER_H +#define CLIPPER_H + +#include +#include +#include + +#include "clipper2/clipper.core.h" +#include "clipper2/clipper.engine.h" +#include "clipper2/clipper.offset.h" +#include "clipper2/clipper.minkowski.h" +#include "clipper2/clipper.rectclip.h" + +namespace Clipper2Lib { + + inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule, + const Paths64& subjects, const Paths64& clips) + { + Paths64 result; + Clipper64 clipper; + clipper.AddSubject(subjects); + clipper.AddClip(clips); + clipper.Execute(cliptype, fillrule, result); + return result; + } + + inline void BooleanOp(ClipType cliptype, FillRule fillrule, + const Paths64& subjects, const Paths64& clips, PolyTree64& solution) + { + Paths64 sol_open; + Clipper64 clipper; + clipper.AddSubject(subjects); + clipper.AddClip(clips); + clipper.Execute(cliptype, fillrule, solution, sol_open); + } + + inline PathsD BooleanOp(ClipType cliptype, FillRule fillrule, + const PathsD& subjects, const PathsD& clips, int precision = 2) + { + int error_code = 0; + CheckPrecisionRange(precision, error_code); + PathsD result; + if (error_code) return result; + ClipperD clipper(precision); + clipper.AddSubject(subjects); + clipper.AddClip(clips); + clipper.Execute(cliptype, fillrule, result); + return result; + } + + inline void BooleanOp(ClipType cliptype, FillRule fillrule, + const PathsD& subjects, const PathsD& clips, + PolyTreeD& polytree, int precision = 2) + { + polytree.Clear(); + int error_code = 0; + CheckPrecisionRange(precision, error_code); + if (error_code) return; + ClipperD clipper(precision); + clipper.AddSubject(subjects); + clipper.AddClip(clips); + clipper.Execute(cliptype, fillrule, polytree); + } + + inline Paths64 Intersect(const Paths64& subjects, const Paths64& clips, FillRule fillrule) + { + return BooleanOp(ClipType::Intersection, fillrule, subjects, clips); + } + + inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) + { + return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec); + } + + inline Paths64 Union(const Paths64& subjects, const Paths64& clips, FillRule fillrule) + { + return BooleanOp(ClipType::Union, fillrule, subjects, clips); + } + + inline PathsD Union(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) + { + return BooleanOp(ClipType::Union, fillrule, subjects, clips, decimal_prec); + } + + inline Paths64 Union(const Paths64& subjects, FillRule fillrule) + { + Paths64 result; + Clipper64 clipper; + clipper.AddSubject(subjects); + clipper.Execute(ClipType::Union, fillrule, result); + return result; + } + + inline PathsD Union(const PathsD& subjects, FillRule fillrule, int precision = 2) + { + PathsD result; + int error_code = 0; + CheckPrecisionRange(precision, error_code); + if (error_code) return result; + ClipperD clipper(precision); + clipper.AddSubject(subjects); + clipper.Execute(ClipType::Union, fillrule, result); + return result; + } + + inline Paths64 Difference(const Paths64& subjects, const Paths64& clips, FillRule fillrule) + { + return BooleanOp(ClipType::Difference, fillrule, subjects, clips); + } + + inline PathsD Difference(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) + { + return BooleanOp(ClipType::Difference, fillrule, subjects, clips, decimal_prec); + } + + inline Paths64 Xor(const Paths64& subjects, const Paths64& clips, FillRule fillrule) + { + return BooleanOp(ClipType::Xor, fillrule, subjects, clips); + } + + inline PathsD Xor(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) + { + return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec); + } + + inline Paths64 InflatePaths(const Paths64& paths, double delta, + JoinType jt, EndType et, double miter_limit = 2.0, + double arc_tolerance = 0.0) + { + if (!delta) return paths; + ClipperOffset clip_offset(miter_limit, arc_tolerance); + clip_offset.AddPaths(paths, jt, et); + Paths64 solution; + clip_offset.Execute(delta, solution); + return solution; + } + + inline PathsD InflatePaths(const PathsD& paths, double delta, + JoinType jt, EndType et, double miter_limit = 2.0, + int precision = 2, double arc_tolerance = 0.0) + { + int error_code = 0; + CheckPrecisionRange(precision, error_code); + if (!delta) return paths; + if (error_code) return PathsD(); + const double scale = std::pow(10, precision); + ClipperOffset clip_offset(miter_limit, arc_tolerance); + clip_offset.AddPaths(ScalePaths(paths, scale, error_code), jt, et); + if (error_code) return PathsD(); + Paths64 solution; + clip_offset.Execute(delta * scale, solution); + return ScalePaths(solution, 1 / scale, error_code); + } + + template + inline Path TranslatePath(const Path& path, T dx, T dy) + { + Path result; + result.reserve(path.size()); + std::transform(path.begin(), path.end(), back_inserter(result), + [dx, dy](const auto& pt) { return Point(pt.x + dx, pt.y +dy); }); + return result; + } + + inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy) + { + return TranslatePath(path, dx, dy); + } + + inline PathD TranslatePath(const PathD& path, double dx, double dy) + { + return TranslatePath(path, dx, dy); + } + + template + inline Paths TranslatePaths(const Paths& paths, T dx, T dy) + { + Paths result; + result.reserve(paths.size()); + std::transform(paths.begin(), paths.end(), back_inserter(result), + [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); }); + return result; + } + + inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy) + { + return TranslatePaths(paths, dx, dy); + } + + inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy) + { + return TranslatePaths(paths, dx, dy); + } + + inline Paths64 RectClip(const Rect64& rect, const Paths64& paths) + { + if (rect.IsEmpty() || paths.empty()) return Paths64(); + RectClip64 rc(rect); + return rc.Execute(paths); + } + + inline Paths64 RectClip(const Rect64& rect, const Path64& path) + { + if (rect.IsEmpty() || path.empty()) return Paths64(); + RectClip64 rc(rect); + return rc.Execute(Paths64{ path }); + } + + inline PathsD RectClip(const RectD& rect, const PathsD& paths, int precision = 2) + { + if (rect.IsEmpty() || paths.empty()) return PathsD(); + int error_code = 0; + CheckPrecisionRange(precision, error_code); + if (error_code) return PathsD(); + const double scale = std::pow(10, precision); + Rect64 r = ScaleRect(rect, scale); + RectClip64 rc(r); + Paths64 pp = ScalePaths(paths, scale, error_code); + if (error_code) return PathsD(); // ie: error_code result is lost + return ScalePaths( + rc.Execute(pp), 1 / scale, error_code); + } + + inline PathsD RectClip(const RectD& rect, const PathD& path, int precision = 2) + { + return RectClip(rect, PathsD{ path }, precision); + } + + inline Paths64 RectClipLines(const Rect64& rect, const Paths64& lines) + { + if (rect.IsEmpty() || lines.empty()) return Paths64(); + RectClipLines64 rcl(rect); + return rcl.Execute(lines); + } + + inline Paths64 RectClipLines(const Rect64& rect, const Path64& line) + { + return RectClipLines(rect, Paths64{ line }); + } + + inline PathsD RectClipLines(const RectD& rect, const PathsD& lines, int precision = 2) + { + if (rect.IsEmpty() || lines.empty()) return PathsD(); + int error_code = 0; + CheckPrecisionRange(precision, error_code); + if (error_code) return PathsD(); + const double scale = std::pow(10, precision); + Rect64 r = ScaleRect(rect, scale); + RectClipLines64 rcl(r); + Paths64 p = ScalePaths(lines, scale, error_code); + if (error_code) return PathsD(); + p = rcl.Execute(p); + return ScalePaths(p, 1 / scale, error_code); + } + + inline PathsD RectClipLines(const RectD& rect, const PathD& line, int precision = 2) + { + return RectClipLines(rect, PathsD{ line }, precision); + } + + namespace details + { + + inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths) + { + paths.push_back(polypath.Polygon()); + for (const auto& child : polypath) + PolyPathToPaths64(*child, paths); + } + + inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths) + { + paths.push_back(polypath.Polygon()); + for (const auto& child : polypath) + PolyPathToPathsD(*child, paths); + } + + inline bool PolyPath64ContainsChildren(const PolyPath64& pp) + { + for (const auto& child : pp) + { + // return false if this child isn't fully contained by its parent + + // checking for a single vertex outside is a bit too crude since + // it doesn't account for rounding errors. It's better to check + // for consecutive vertices found outside the parent's polygon. + + int outsideCnt = 0; + for (const Point64& pt : child->Polygon()) + { + PointInPolygonResult result = PointInPolygon(pt, pp.Polygon()); + if (result == PointInPolygonResult::IsInside) --outsideCnt; + else if (result == PointInPolygonResult::IsOutside) ++outsideCnt; + if (outsideCnt > 1) return false; + else if (outsideCnt < -1) break; + } + + // now check any nested children too + if (child->Count() > 0 && !PolyPath64ContainsChildren(*child)) + return false; + } + return true; + } + + static void OutlinePolyPath(std::ostream& os, + size_t idx, bool isHole, size_t count, const std::string& preamble) + { + std::string plural = (count == 1) ? "." : "s."; + if (isHole) + os << preamble << "+- Hole (" << idx << ") contains " << count << + " nested polygon" << plural << std::endl; + else + os << preamble << "+- Polygon (" << idx << ") contains " << count << + " hole" << plural << std::endl; + } + + static void OutlinePolyPath64(std::ostream& os, const PolyPath64& pp, + size_t idx, std::string preamble) + { + OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble); + for (size_t i = 0; i < pp.Count(); ++i) + if (pp.Child(i)->Count()) + details::OutlinePolyPath64(os, *pp.Child(i), i, preamble + " "); + } + + static void OutlinePolyPathD(std::ostream& os, const PolyPathD& pp, + size_t idx, std::string preamble) + { + OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble); + for (size_t i = 0; i < pp.Count(); ++i) + if (pp.Child(i)->Count()) + details::OutlinePolyPathD(os, *pp.Child(i), i, preamble + " "); + } + + template + inline constexpr void MakePathGeneric(const T an_array, + size_t array_size, std::vector& result) + { + result.reserve(array_size / 2); + for (size_t i = 0; i < array_size; i +=2) +#ifdef USINGZ + result.push_back( U{ an_array[i], an_array[i + 1], 0} ); +#else + result.push_back( U{ an_array[i], an_array[i + 1]} ); +#endif + } + + } // end details namespace + + inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp) + { + std::string plural = (pp.Count() == 1) ? " polygon." : " polygons."; + os << std::endl << "Polytree with " << pp.Count() << plural << std::endl; + for (size_t i = 0; i < pp.Count(); ++i) + if (pp.Child(i)->Count()) + details::OutlinePolyPath64(os, *pp.Child(i), i, " "); + os << std::endl << std::endl; + return os; + } + + inline std::ostream& operator<< (std::ostream& os, const PolyTreeD& pp) + { + std::string plural = (pp.Count() == 1) ? " polygon." : " polygons."; + os << std::endl << "Polytree with " << pp.Count() << plural << std::endl; + for (size_t i = 0; i < pp.Count(); ++i) + if (pp.Child(i)->Count()) + details::OutlinePolyPathD(os, *pp.Child(i), i, " "); + os << std::endl << std::endl; + if (!pp.Level()) os << std::endl; + return os; + } + + inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree) + { + Paths64 result; + for (const auto& child : polytree) + details::PolyPathToPaths64(*child, result); + return result; + } + + inline PathsD PolyTreeToPathsD(const PolyTreeD& polytree) + { + PathsD result; + for (const auto& child : polytree) + details::PolyPathToPathsD(*child, result); + return result; + } + + inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree) + { + for (const auto& child : polytree) + if (child->Count() > 0 && + !details::PolyPath64ContainsChildren(*child)) + return false; + return true; + } + + template::value && + !std::is_same::value, bool + >::type = true> + inline Path64 MakePath(const std::vector& list) + { + const auto size = list.size() - list.size() % 2; + if (list.size() != size) + DoError(non_pair_error_i); // non-fatal without exception handling + Path64 result; + details::MakePathGeneric(list, size, result); + return result; + } + + template::value && + !std::is_same::value, bool + >::type = true> + inline Path64 MakePath(const T(&list)[N]) + { + // Make the compiler error on unpaired value (i.e. no runtime effects). + static_assert(N % 2 == 0, "MakePath requires an even number of arguments"); + Path64 result; + details::MakePathGeneric(list, N, result); + return result; + } + + template::value && + !std::is_same::value, bool + >::type = true> + inline PathD MakePathD(const std::vector& list) + { + const auto size = list.size() - list.size() % 2; + if (list.size() != size) + DoError(non_pair_error_i); // non-fatal without exception handling + PathD result; + details::MakePathGeneric(list, size, result); + return result; + } + + template::value && + !std::is_same::value, bool + >::type = true> + inline PathD MakePathD(const T(&list)[N]) + { + // Make the compiler error on unpaired value (i.e. no runtime effects). + static_assert(N % 2 == 0, "MakePath requires an even number of arguments"); + PathD result; + details::MakePathGeneric(list, N, result); + return result; + } + +#ifdef USINGZ + template + inline Path64 MakePathZ(const T2(&list)[N]) + { + static_assert(N % 3 == 0 && std::numeric_limits::is_integer, + "MakePathZ requires integer values in multiples of 3"); + std::size_t size = N / 3; + Path64 result(size); + for (size_t i = 0; i < size; ++i) + result[i] = Point64(list[i * 3], + list[i * 3 + 1], list[i * 3 + 2]); + return result; + } + + template + inline PathD MakePathZD(const T2(&list)[N]) + { + static_assert(N % 3 == 0, + "MakePathZD requires values in multiples of 3"); + std::size_t size = N / 3; + PathD result(size); + if constexpr (std::numeric_limits::is_integer) + for (size_t i = 0; i < size; ++i) + result[i] = PointD(list[i * 3], + list[i * 3 + 1], list[i * 3 + 2]); + else + for (size_t i = 0; i < size; ++i) + result[i] = PointD(list[i * 3], list[i * 3 + 1], + static_cast(list[i * 3 + 2])); + return result; + } +#endif + + inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false) + { + size_t len = p.size(); + if (len < 3) + { + if (!is_open_path || len < 2 || p[0] == p[1]) return Path64(); + else return p; + } + + Path64 dst; + dst.reserve(len); + Path64::const_iterator srcIt = p.cbegin(), prevIt, stop = p.cend() - 1; + + if (!is_open_path) + { + while (srcIt != stop && IsCollinear(*stop, *srcIt, *(srcIt + 1))) + ++srcIt; + while (srcIt != stop && IsCollinear(*(stop - 1), *stop, *srcIt)) + --stop; + if (srcIt == stop) return Path64(); + } + + prevIt = srcIt++; + dst.push_back(*prevIt); + for (; srcIt != stop; ++srcIt) + { + if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1))) + { + prevIt = srcIt; + dst.push_back(*prevIt); + } + } + + if (is_open_path) + dst.push_back(*srcIt); + else if (!IsCollinear(*prevIt, *stop, dst[0])) + dst.push_back(*stop); + else + { + while (dst.size() > 2 && + IsCollinear(dst[dst.size() - 1], dst[dst.size() - 2], dst[0])) + dst.pop_back(); + if (dst.size() < 3) return Path64(); + } + return dst; + } + + inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false) + { + int error_code = 0; + CheckPrecisionRange(precision, error_code); + if (error_code) return PathD(); + const double scale = std::pow(10, precision); + Path64 p = ScalePath(path, scale, error_code); + if (error_code) return PathD(); + p = TrimCollinear(p, is_open_path); + return ScalePath(p, 1/scale, error_code); + } + + template + inline double Distance(const Point pt1, const Point pt2) + { + return std::sqrt(DistanceSqr(pt1, pt2)); + } + + template + inline double Length(const Path& path, bool is_closed_path = false) + { + double result = 0.0; + if (path.size() < 2) return result; + auto it = path.cbegin(), stop = path.end() - 1; + for (; it != stop; ++it) + result += Distance(*it, *(it + 1)); + if (is_closed_path) + result += Distance(*stop, *path.cbegin()); + return result; + } + + + template + inline bool NearCollinear(const Point& pt1, const Point& pt2, const Point& pt3, double sin_sqrd_min_angle_rads) + { + double cp = std::abs(CrossProduct(pt1, pt2, pt3)); + return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads; + } + + template + inline Path Ellipse(const Rect& rect, size_t steps = 0) + { + return Ellipse(rect.MidPoint(), + static_cast(rect.Width()) *0.5, + static_cast(rect.Height()) * 0.5, steps); + } + + template + inline Path Ellipse(const Point& center, + double radiusX, double radiusY = 0, size_t steps = 0) + { + if (radiusX <= 0) return Path(); + if (radiusY <= 0) radiusY = radiusX; + if (steps <= 2) + steps = static_cast(PI * sqrt((radiusX + radiusY) / 2)); + + double si = std::sin(2 * PI / steps); + double co = std::cos(2 * PI / steps); + double dx = co, dy = si; + Path result; + result.reserve(steps); + result.push_back(Point(center.x + radiusX, static_cast(center.y))); + for (size_t i = 1; i < steps; ++i) + { + result.push_back(Point(center.x + radiusX * dx, center.y + radiusY * dy)); + double x = dx * co - dy * si; + dy = dy * co + dx * si; + dx = x; + } + return result; + } + + inline size_t GetNext(size_t current, size_t high, + const std::vector& flags) + { + ++current; + while (current <= high && flags[current]) ++current; + if (current <= high) return current; + current = 0; + while (flags[current]) ++current; + return current; + } + + inline size_t GetPrior(size_t current, size_t high, + const std::vector& flags) + { + if (current == 0) current = high; + else --current; + while (current > 0 && flags[current]) --current; + if (!flags[current]) return current; + current = high; + while (flags[current]) --current; + return current; + } + + template + inline Path SimplifyPath(const Path &path, + double epsilon, bool isClosedPath = true) + { + const size_t len = path.size(), high = len -1; + const double epsSqr = Sqr(epsilon); + if (len < 4) return Path(path); + + std::vector flags(len); + std::vector distSqr(len); + size_t prior = high, curr = 0, start, next, prior2; + if (isClosedPath) + { + distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]); + distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]); + } + else + { + distSqr[0] = MAX_DBL; + distSqr[high] = MAX_DBL; + } + for (size_t i = 1; i < high; ++i) + distSqr[i] = PerpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]); + + for (;;) + { + if (distSqr[curr] > epsSqr) + { + start = curr; + do + { + curr = GetNext(curr, high, flags); + } while (curr != start && distSqr[curr] > epsSqr); + if (curr == start) break; + } + + prior = GetPrior(curr, high, flags); + next = GetNext(curr, high, flags); + if (next == prior) break; + + // flag for removal the smaller of adjacent 'distances' + if (distSqr[next] < distSqr[curr]) + { + prior2 = prior; + prior = curr; + curr = next; + next = GetNext(next, high, flags); + } + else + prior2 = GetPrior(prior, high, flags); + + flags[curr] = true; + curr = next; + next = GetNext(next, high, flags); + + if (isClosedPath || ((curr != high) && (curr != 0))) + distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]); + if (isClosedPath || ((prior != 0) && (prior != high))) + distSqr[prior] = PerpendicDistFromLineSqrd(path[prior], path[prior2], path[curr]); + } + Path result; + result.reserve(len); + for (typename Path::size_type i = 0; i < len; ++i) + if (!flags[i]) result.push_back(path[i]); + return result; + } + + template + inline Paths SimplifyPaths(const Paths &paths, + double epsilon, bool isClosedPath = true) + { + Paths result; + result.reserve(paths.size()); + for (const auto& path : paths) + result.push_back(SimplifyPath(path, epsilon, isClosedPath)); + return result; + } + + template + inline void RDP(const Path path, std::size_t begin, + std::size_t end, double epsSqrd, std::vector& flags) + { + typename Path::size_type idx = 0; + double max_d = 0; + while (end > begin && path[begin] == path[end]) flags[end--] = false; + for (typename Path::size_type i = begin + 1; i < end; ++i) + { + // PerpendicDistFromLineSqrd - avoids expensive Sqrt() + double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]); + if (d <= max_d) continue; + max_d = d; + idx = i; + } + if (max_d <= epsSqrd) return; + flags[idx] = true; + if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags); + if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags); + } + + template + inline Path RamerDouglasPeucker(const Path& path, double epsilon) + { + const typename Path::size_type len = path.size(); + if (len < 5) return Path(path); + std::vector flags(len); + flags[0] = true; + flags[len - 1] = true; + RDP(path, 0, len - 1, Sqr(epsilon), flags); + Path result; + result.reserve(len); + for (typename Path::size_type i = 0; i < len; ++i) + if (flags[i]) + result.push_back(path[i]); + return result; + } + + template + inline Paths RamerDouglasPeucker(const Paths& paths, double epsilon) + { + Paths result; + result.reserve(paths.size()); + std::transform(paths.begin(), paths.end(), back_inserter(result), + [epsilon](const auto& path) + { return RamerDouglasPeucker(path, epsilon); }); + return result; + } + +} // end Clipper2Lib namespace + +#endif // CLIPPER_H diff --git a/KP3Dii/ext/include/clipper2/clipper.minkowski.h b/KP3Dii/ext/include/clipper2/clipper.minkowski.h new file mode 100644 index 0000000..a3ddcf8 --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.minkowski.h @@ -0,0 +1,120 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 1 November 2023 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2023 * +* Purpose : Minkowski Sum and Difference * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#ifndef CLIPPER_MINKOWSKI_H +#define CLIPPER_MINKOWSKI_H + +#include +#include +#include +#include "clipper2/clipper.core.h" + +namespace Clipper2Lib +{ + + namespace detail + { + inline Paths64 Minkowski(const Path64& pattern, const Path64& path, bool isSum, bool isClosed) + { + size_t delta = isClosed ? 0 : 1; + size_t patLen = pattern.size(), pathLen = path.size(); + if (patLen == 0 || pathLen == 0) return Paths64(); + Paths64 tmp; + tmp.reserve(pathLen); + + if (isSum) + { + for (const Point64& p : path) + { + Path64 path2(pattern.size()); + std::transform(pattern.cbegin(), pattern.cend(), + path2.begin(), [p](const Point64& pt2) {return p + pt2; }); + tmp.push_back(path2); + } + } + else + { + for (const Point64& p : path) + { + Path64 path2(pattern.size()); + std::transform(pattern.cbegin(), pattern.cend(), + path2.begin(), [p](const Point64& pt2) {return p - pt2; }); + tmp.push_back(path2); + } + } + + Paths64 result; + result.reserve((pathLen - delta) * patLen); + size_t g = isClosed ? pathLen - 1 : 0; + for (size_t h = patLen - 1, i = delta; i < pathLen; ++i) + { + for (size_t j = 0; j < patLen; j++) + { + Path64 quad; + quad.reserve(4); + { + quad.push_back(tmp[g][h]); + quad.push_back(tmp[i][h]); + quad.push_back(tmp[i][j]); + quad.push_back(tmp[g][j]); + }; + if (!IsPositive(quad)) + std::reverse(quad.begin(), quad.end()); + result.push_back(quad); + h = j; + } + g = i; + } + return result; + } + + inline Paths64 Union(const Paths64& subjects, FillRule fillrule) + { + Paths64 result; + Clipper64 clipper; + clipper.AddSubject(subjects); + clipper.Execute(ClipType::Union, fillrule, result); + return result; + } + + } // namespace internal + + inline Paths64 MinkowskiSum(const Path64& pattern, const Path64& path, bool isClosed) + { + return detail::Union(detail::Minkowski(pattern, path, true, isClosed), FillRule::NonZero); + } + + inline PathsD MinkowskiSum(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2) + { + int error_code = 0; + double scale = pow(10, decimalPlaces); + Path64 pat64 = ScalePath(pattern, scale, error_code); + Path64 path64 = ScalePath(path, scale, error_code); + Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero); + return ScalePaths(tmp, 1 / scale, error_code); + } + + inline Paths64 MinkowskiDiff(const Path64& pattern, const Path64& path, bool isClosed) + { + return detail::Union(detail::Minkowski(pattern, path, false, isClosed), FillRule::NonZero); + } + + inline PathsD MinkowskiDiff(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2) + { + int error_code = 0; + double scale = pow(10, decimalPlaces); + Path64 pat64 = ScalePath(pattern, scale, error_code); + Path64 path64 = ScalePath(path, scale, error_code); + Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero); + return ScalePaths(tmp, 1 / scale, error_code); + } + +} // Clipper2Lib namespace + +#endif // CLIPPER_MINKOWSKI_H diff --git a/KP3Dii/ext/include/clipper2/clipper.offset.h b/KP3Dii/ext/include/clipper2/clipper.offset.h new file mode 100644 index 0000000..bb075a6 --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.offset.h @@ -0,0 +1,124 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 24 March 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : Path Offset (Inflate/Shrink) * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#ifndef CLIPPER_OFFSET_H_ +#define CLIPPER_OFFSET_H_ + +#include "clipper.core.h" +#include "clipper.engine.h" + +namespace Clipper2Lib { + +enum class JoinType { Square, Bevel, Round, Miter }; +//Square : Joins are 'squared' at exactly the offset distance (more complex code) +//Bevel : Similar to Square, but the offset distance varies with angle (simple code & faster) + +enum class EndType {Polygon, Joined, Butt, Square, Round}; +//Butt : offsets both sides of a path, with square blunt ends +//Square : offsets both sides of a path, with square extended ends +//Round : offsets both sides of a path, with round extended ends +//Joined : offsets both sides of a path, with joined ends +//Polygon: offsets only one side of a closed path + +typedef std::function DeltaCallback64; + +class ClipperOffset { +private: + + class Group { + public: + Paths64 paths_in; + std::optional lowest_path_idx{}; + bool is_reversed = false; + JoinType join_type; + EndType end_type; + Group(const Paths64& _paths, JoinType _join_type, EndType _end_type); + }; + + int error_code_ = 0; + double delta_ = 0.0; + double group_delta_ = 0.0; + double temp_lim_ = 0.0; + double steps_per_rad_ = 0.0; + double step_sin_ = 0.0; + double step_cos_ = 0.0; + PathD norms; + Path64 path_out; + Paths64* solution = nullptr; + PolyTree64* solution_tree = nullptr; + std::vector groups_; + JoinType join_type_ = JoinType::Bevel; + EndType end_type_ = EndType::Polygon; + + double miter_limit_ = 0.0; + double arc_tolerance_ = 0.0; + bool preserve_collinear_ = false; + bool reverse_solution_ = false; + +#ifdef USINGZ + ZCallback64 zCallback64_ = nullptr; + void ZCB(const Point64& bot1, const Point64& top1, + const Point64& bot2, const Point64& top2, Point64& ip); +#endif + DeltaCallback64 deltaCallback64_ = nullptr; + size_t CalcSolutionCapacity(); + bool CheckReverseOrientation(); + void DoBevel(const Path64& path, size_t j, size_t k); + void DoSquare(const Path64& path, size_t j, size_t k); + void DoMiter(const Path64& path, size_t j, size_t k, double cos_a); + void DoRound(const Path64& path, size_t j, size_t k, double angle); + void BuildNormals(const Path64& path); + void OffsetPolygon(Group& group, const Path64& path); + void OffsetOpenJoined(Group& group, const Path64& path); + void OffsetOpenPath(Group& group, const Path64& path); + void OffsetPoint(Group& group, const Path64& path, size_t j, size_t k); + void DoGroupOffset(Group &group); + void ExecuteInternal(double delta); +public: + explicit ClipperOffset(double miter_limit = 2.0, + double arc_tolerance = 0.0, + bool preserve_collinear = false, + bool reverse_solution = false) : + miter_limit_(miter_limit), arc_tolerance_(arc_tolerance), + preserve_collinear_(preserve_collinear), + reverse_solution_(reverse_solution) { }; + + ~ClipperOffset() { Clear(); }; + + int ErrorCode() const { return error_code_; }; + void AddPath(const Path64& path, JoinType jt_, EndType et_); + void AddPaths(const Paths64& paths, JoinType jt_, EndType et_); + void Clear() { groups_.clear(); norms.clear(); }; + + void Execute(double delta, Paths64& paths); + void Execute(double delta, PolyTree64& polytree); + void Execute(DeltaCallback64 delta_cb, Paths64& paths); + + double MiterLimit() const { return miter_limit_; } + void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; } + + //ArcTolerance: needed for rounded offsets (See offset_triginometry2.svg) + double ArcTolerance() const { return arc_tolerance_; } + void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; } + + bool PreserveCollinear() const { return preserve_collinear_; } + void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;} + + bool ReverseSolution() const { return reverse_solution_; } + void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;} + +#ifdef USINGZ + void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; } +#endif + void SetDeltaCallback(DeltaCallback64 cb) { deltaCallback64_ = cb; } + +}; + +} +#endif /* CLIPPER_OFFSET_H_ */ diff --git a/KP3Dii/ext/include/clipper2/clipper.rectclip.h b/KP3Dii/ext/include/clipper2/clipper.rectclip.h new file mode 100644 index 0000000..bfcfacf --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.rectclip.h @@ -0,0 +1,82 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 5 July 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : FAST rectangular clipping * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#ifndef CLIPPER_RECTCLIP_H +#define CLIPPER_RECTCLIP_H + +#include +#include +#include +#include "clipper2/clipper.core.h" + +namespace Clipper2Lib +{ + + // Location: the order is important here, see StartLocsIsClockwise() + enum class Location { Left, Top, Right, Bottom, Inside }; + + class OutPt2; + typedef std::vector OutPt2List; + + class OutPt2 { + public: + Point64 pt; + size_t owner_idx = 0; + OutPt2List* edge = nullptr; + OutPt2* next = nullptr; + OutPt2* prev = nullptr; + }; + + //------------------------------------------------------------------------------ + // RectClip64 + //------------------------------------------------------------------------------ + + class RectClip64 { + private: + void ExecuteInternal(const Path64& path); + Path64 GetPath(OutPt2*& op); + protected: + const Rect64 rect_; + const Path64 rect_as_path_; + const Point64 rect_mp_; + Rect64 path_bounds_; + std::deque op_container_; + OutPt2List results_; // each path can be broken into multiples + OutPt2List edges_[8]; // clockwise and counter-clockwise + std::vector start_locs_; + void CheckEdges(); + void TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw); + void GetNextLocation(const Path64& path, + Location& loc, size_t& i, size_t highI); + OutPt2* Add(Point64 pt, bool start_new = false); + void AddCorner(Location prev, Location curr); + void AddCorner(Location& loc, bool isClockwise); + public: + explicit RectClip64(const Rect64& rect) : + rect_(rect), + rect_as_path_(rect.AsPath()), + rect_mp_(rect.MidPoint()) {} + Paths64 Execute(const Paths64& paths); + }; + + //------------------------------------------------------------------------------ + // RectClipLines64 + //------------------------------------------------------------------------------ + + class RectClipLines64 : public RectClip64 { + private: + void ExecuteInternal(const Path64& path); + Path64 GetPath(OutPt2*& op); + public: + explicit RectClipLines64(const Rect64& rect) : RectClip64(rect) {}; + Paths64 Execute(const Paths64& paths); + }; + +} // Clipper2Lib namespace +#endif // CLIPPER_RECTCLIP_H diff --git a/KP3Dii/ext/include/clipper2/clipper.version.h b/KP3Dii/ext/include/clipper2/clipper.version.h new file mode 100644 index 0000000..6146409 --- /dev/null +++ b/KP3Dii/ext/include/clipper2/clipper.version.h @@ -0,0 +1,6 @@ +#ifndef CLIPPER_VERSION_H +#define CLIPPER_VERSION_H + +constexpr auto CLIPPER2_VERSION = "1.4.0"; + +#endif // CLIPPER_VERSION_H diff --git a/KP3Dii/ext/src/clip2tri/clip2tri.cpp b/KP3Dii/ext/src/clip2tri/clip2tri.cpp index 420775f..dfaf561 100644 --- a/KP3Dii/ext/src/clip2tri/clip2tri.cpp +++ b/KP3Dii/ext/src/clip2tri/clip2tri.cpp @@ -243,7 +243,6 @@ bool clip2tri::triangulateComplex(vector &outputTriangles, const Path &ou // KP3D modification: steiner points vector steiners; - // steiners.reserve(10000); if (steiner_points) { for (U32 j = 0; j < steiner_points->size(); j++) diff --git a/KP3Dii/ext/src/clipper2/clipper.engine.cpp b/KP3Dii/ext/src/clipper2/clipper.engine.cpp new file mode 100644 index 0000000..8f12026 --- /dev/null +++ b/KP3Dii/ext/src/clipper2/clipper.engine.cpp @@ -0,0 +1,3149 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 27 April 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : This is the main polygon clipping module * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "clipper2/clipper.engine.h" +#include "clipper2/clipper.h" + +// https://github.com/AngusJohnson/Clipper2/discussions/334 +// #discussioncomment-4248602 +#if defined(_MSC_VER) && ( defined(_M_AMD64) || defined(_M_X64) ) +#include +#include +#define fmin(a,b) _mm_cvtsd_f64(_mm_min_sd(_mm_set_sd(a),_mm_set_sd(b))) +#define fmax(a,b) _mm_cvtsd_f64(_mm_max_sd(_mm_set_sd(a),_mm_set_sd(b))) +#define nearbyint(a) _mm_cvtsd_si64(_mm_set_sd(a)) /* Note: expression type is (int64_t) */ +#endif + +namespace Clipper2Lib { + + static const Rect64 invalid_rect = Rect64(false); + + // Every closed path (ie polygon) is made up of a series of vertices forming edge + // 'bounds' that alternate between ascending bounds (containing edges going up + // relative to the Y-axis) and descending bounds. 'Local Minima' refers to + // vertices where ascending and descending bounds join at the bottom, and + // 'Local Maxima' are where ascending and descending bounds join at the top. + + struct Scanline { + int64_t y = 0; + Scanline* next = nullptr; + + explicit Scanline(int64_t y_) : y(y_) {} + }; + + struct HorzSegSorter { + inline bool operator()(const HorzSegment& hs1, const HorzSegment& hs2) + { + if (!hs1.right_op || !hs2.right_op) return (hs1.right_op); + return hs2.left_op->pt.x > hs1.left_op->pt.x; + } + }; + + struct LocMinSorter { + inline bool operator()(const LocalMinima_ptr& locMin1, + const LocalMinima_ptr& locMin2) + { + if (locMin2->vertex->pt.y != locMin1->vertex->pt.y) + return locMin2->vertex->pt.y < locMin1->vertex->pt.y; + else + return locMin2->vertex->pt.x > locMin1->vertex->pt.x; + } + }; + + + inline bool IsOdd(int val) + { + return (val & 1) ? true : false; + } + + + inline bool IsHotEdge(const Active& e) + { + return (e.outrec); + } + + + inline bool IsOpen(const Active& e) + { + return (e.local_min->is_open); + } + + + inline bool IsOpenEnd(const Vertex& v) + { + return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) != + VertexFlags::None; + } + + + inline bool IsOpenEnd(const Active& ae) + { + return IsOpenEnd(*ae.vertex_top); + } + + + inline Active* GetPrevHotEdge(const Active& e) + { + Active* prev = e.prev_in_ael; + while (prev && (IsOpen(*prev) || !IsHotEdge(*prev))) + prev = prev->prev_in_ael; + return prev; + } + + inline bool IsFront(const Active& e) + { + return (&e == e.outrec->front_edge); + } + + inline bool IsInvalidPath(OutPt* op) + { + return (!op || op->next == op); + } + + /******************************************************************************* + * Dx: 0(90deg) * + * | * + * +inf (180deg) <--- o ---> -inf (0deg) * + *******************************************************************************/ + + inline double GetDx(const Point64& pt1, const Point64& pt2) + { + double dy = double(pt2.y - pt1.y); + if (dy != 0) + return double(pt2.x - pt1.x) / dy; + else if (pt2.x > pt1.x) + return -std::numeric_limits::max(); + else + return std::numeric_limits::max(); + } + + inline int64_t TopX(const Active& ae, const int64_t currentY) + { + if ((currentY == ae.top.y) || (ae.top.x == ae.bot.x)) return ae.top.x; + else if (currentY == ae.bot.y) return ae.bot.x; + else return ae.bot.x + static_cast(nearbyint(ae.dx * (currentY - ae.bot.y))); + // nb: std::nearbyint (or std::round) substantially *improves* performance here + // as it greatly improves the likelihood of edge adjacency in ProcessIntersectList(). + } + + + inline bool IsHorizontal(const Active& e) + { + return (e.top.y == e.bot.y); + } + + + inline bool IsHeadingRightHorz(const Active& e) + { + return e.dx == -std::numeric_limits::max(); + } + + + inline bool IsHeadingLeftHorz(const Active& e) + { + return e.dx == std::numeric_limits::max(); + } + + + inline void SwapActives(Active*& e1, Active*& e2) + { + Active* e = e1; + e1 = e2; + e2 = e; + } + + inline PathType GetPolyType(const Active& e) + { + return e.local_min->polytype; + } + + inline bool IsSamePolyType(const Active& e1, const Active& e2) + { + return e1.local_min->polytype == e2.local_min->polytype; + } + + inline void SetDx(Active& e) + { + e.dx = GetDx(e.bot, e.top); + } + + inline Vertex* NextVertex(const Active& e) + { + if (e.wind_dx > 0) + return e.vertex_top->next; + else + return e.vertex_top->prev; + } + + //PrevPrevVertex: useful to get the (inverted Y-axis) top of the + //alternate edge (ie left or right bound) during edge insertion. + inline Vertex* PrevPrevVertex(const Active& ae) + { + if (ae.wind_dx > 0) + return ae.vertex_top->prev->prev; + else + return ae.vertex_top->next->next; + } + + + inline Active* ExtractFromSEL(Active* ae) + { + Active* res = ae->next_in_sel; + if (res) + res->prev_in_sel = ae->prev_in_sel; + ae->prev_in_sel->next_in_sel = res; + return res; + } + + + inline void Insert1Before2InSEL(Active* ae1, Active* ae2) + { + ae1->prev_in_sel = ae2->prev_in_sel; + if (ae1->prev_in_sel) + ae1->prev_in_sel->next_in_sel = ae1; + ae1->next_in_sel = ae2; + ae2->prev_in_sel = ae1; + } + + inline bool IsMaxima(const Vertex& v) + { + return ((v.flags & VertexFlags::LocalMax) != VertexFlags::None); + } + + + inline bool IsMaxima(const Active& e) + { + return IsMaxima(*e.vertex_top); + } + + inline Vertex* GetCurrYMaximaVertex_Open(const Active& e) + { + Vertex* result = e.vertex_top; + if (e.wind_dx > 0) + while ((result->next->pt.y == result->pt.y) && + ((result->flags & (VertexFlags::OpenEnd | + VertexFlags::LocalMax)) == VertexFlags::None)) + result = result->next; + else + while (result->prev->pt.y == result->pt.y && + ((result->flags & (VertexFlags::OpenEnd | + VertexFlags::LocalMax)) == VertexFlags::None)) + result = result->prev; + if (!IsMaxima(*result)) result = nullptr; // not a maxima + return result; + } + + inline Vertex* GetCurrYMaximaVertex(const Active& e) + { + Vertex* result = e.vertex_top; + if (e.wind_dx > 0) + while (result->next->pt.y == result->pt.y) result = result->next; + else + while (result->prev->pt.y == result->pt.y) result = result->prev; + if (!IsMaxima(*result)) result = nullptr; // not a maxima + return result; + } + + Active* GetMaximaPair(const Active& e) + { + Active* e2; + e2 = e.next_in_ael; + while (e2) + { + if (e2->vertex_top == e.vertex_top) return e2; // Found! + e2 = e2->next_in_ael; + } + return nullptr; + } + + inline int PointCount(OutPt* op) + { + OutPt* op2 = op; + int cnt = 0; + do + { + op2 = op2->next; + ++cnt; + } while (op2 != op); + return cnt; + } + + inline OutPt* DuplicateOp(OutPt* op, bool insert_after) + { + OutPt* result = new OutPt(op->pt, op->outrec); + if (insert_after) + { + result->next = op->next; + result->next->prev = result; + result->prev = op; + op->next = result; + } + else + { + result->prev = op->prev; + result->prev->next = result; + result->next = op; + op->prev = result; + } + return result; + } + + inline OutPt* DisposeOutPt(OutPt* op) + { + OutPt* result = op->next; + op->prev->next = op->next; + op->next->prev = op->prev; + delete op; + return result; + } + + + inline void DisposeOutPts(OutRec* outrec) + { + OutPt* op = outrec->pts; + op->prev->next = nullptr; + while (op) + { + OutPt* tmp = op; + op = op->next; + delete tmp; + }; + outrec->pts = nullptr; + } + + + bool IntersectListSort(const IntersectNode& a, const IntersectNode& b) + { + //note different inequality tests ... + return (a.pt.y == b.pt.y) ? (a.pt.x < b.pt.x) : (a.pt.y > b.pt.y); + } + + + inline void SetSides(OutRec& outrec, Active& start_edge, Active& end_edge) + { + outrec.front_edge = &start_edge; + outrec.back_edge = &end_edge; + } + + + void SwapOutrecs(Active& e1, Active& e2) + { + OutRec* or1 = e1.outrec; + OutRec* or2 = e2.outrec; + if (or1 == or2) + { + Active* e = or1->front_edge; + or1->front_edge = or1->back_edge; + or1->back_edge = e; + return; + } + if (or1) + { + if (&e1 == or1->front_edge) + or1->front_edge = &e2; + else + or1->back_edge = &e2; + } + if (or2) + { + if (&e2 == or2->front_edge) + or2->front_edge = &e1; + else + or2->back_edge = &e1; + } + e1.outrec = or2; + e2.outrec = or1; + } + + + double Area(OutPt* op) + { + //https://en.wikipedia.org/wiki/Shoelace_formula + double result = 0.0; + OutPt* op2 = op; + do + { + result += static_cast(op2->prev->pt.y + op2->pt.y) * + static_cast(op2->prev->pt.x - op2->pt.x); + op2 = op2->next; + } while (op2 != op); + return result * 0.5; + } + + inline double AreaTriangle(const Point64& pt1, + const Point64& pt2, const Point64& pt3) + { + return (static_cast(pt3.y + pt1.y) * static_cast(pt3.x - pt1.x) + + static_cast(pt1.y + pt2.y) * static_cast(pt1.x - pt2.x) + + static_cast(pt2.y + pt3.y) * static_cast(pt2.x - pt3.x)); + } + + void ReverseOutPts(OutPt* op) + { + if (!op) return; + + OutPt* op1 = op; + OutPt* op2; + + do + { + op2 = op1->next; + op1->next = op1->prev; + op1->prev = op2; + op1 = op2; + } while (op1 != op); + } + + inline void SwapSides(OutRec& outrec) + { + Active* e2 = outrec.front_edge; + outrec.front_edge = outrec.back_edge; + outrec.back_edge = e2; + outrec.pts = outrec.pts->next; + } + + inline OutRec* GetRealOutRec(OutRec* outrec) + { + while (outrec && !outrec->pts) outrec = outrec->owner; + return outrec; + } + + inline bool IsValidOwner(OutRec* outrec, OutRec* testOwner) + { + // prevent outrec owning itself either directly or indirectly + while (testOwner && testOwner != outrec) testOwner = testOwner->owner; + return !testOwner; + } + + inline void UncoupleOutRec(Active ae) + { + OutRec* outrec = ae.outrec; + if (!outrec) return; + outrec->front_edge->outrec = nullptr; + outrec->back_edge->outrec = nullptr; + outrec->front_edge = nullptr; + outrec->back_edge = nullptr; + } + + + inline bool PtsReallyClose(const Point64& pt1, const Point64& pt2) + { + return (std::llabs(pt1.x - pt2.x) < 2) && (std::llabs(pt1.y - pt2.y) < 2); + } + + inline bool IsVerySmallTriangle(const OutPt& op) + { + return op.next->next == op.prev && + (PtsReallyClose(op.prev->pt, op.next->pt) || + PtsReallyClose(op.pt, op.next->pt) || + PtsReallyClose(op.pt, op.prev->pt)); + } + + inline bool IsValidClosedPath(const OutPt* op) + { + return op && (op->next != op) && (op->next != op->prev) && + !IsVerySmallTriangle(*op); + } + + inline bool OutrecIsAscending(const Active* hotEdge) + { + return (hotEdge == hotEdge->outrec->front_edge); + } + + inline void SwapFrontBackSides(OutRec& outrec) + { + Active* tmp = outrec.front_edge; + outrec.front_edge = outrec.back_edge; + outrec.back_edge = tmp; + outrec.pts = outrec.pts->next; + } + + inline bool EdgesAdjacentInAEL(const IntersectNode& inode) + { + return (inode.edge1->next_in_ael == inode.edge2) || (inode.edge1->prev_in_ael == inode.edge2); + } + + inline bool IsJoined(const Active& e) + { + return e.join_with != JoinWith::None; + } + + inline void SetOwner(OutRec* outrec, OutRec* new_owner) + { + //precondition1: new_owner is never null + while (new_owner->owner && !new_owner->owner->pts) + new_owner->owner = new_owner->owner->owner; + OutRec* tmp = new_owner; + while (tmp && tmp != outrec) tmp = tmp->owner; + if (tmp) new_owner->owner = outrec->owner; + outrec->owner = new_owner; + } + + static PointInPolygonResult PointInOpPolygon(const Point64& pt, OutPt* op) + { + if (op == op->next || op->prev == op->next) + return PointInPolygonResult::IsOutside; + + OutPt* op2 = op; + do + { + if (op->pt.y != pt.y) break; + op = op->next; + } while (op != op2); + if (op->pt.y == pt.y) // not a proper polygon + return PointInPolygonResult::IsOutside; + + bool is_above = op->pt.y < pt.y, starting_above = is_above; + int val = 0; + op2 = op->next; + while (op2 != op) + { + if (is_above) + while (op2 != op && op2->pt.y < pt.y) op2 = op2->next; + else + while (op2 != op && op2->pt.y > pt.y) op2 = op2->next; + if (op2 == op) break; + + // must have touched or crossed the pt.Y horizonal + // and this must happen an even number of times + + if (op2->pt.y == pt.y) // touching the horizontal + { + if (op2->pt.x == pt.x || (op2->pt.y == op2->prev->pt.y && + (pt.x < op2->prev->pt.x) != (pt.x < op2->pt.x))) + return PointInPolygonResult::IsOn; + + op2 = op2->next; + if (op2 == op) break; + continue; + } + + if (pt.x < op2->pt.x && pt.x < op2->prev->pt.x); + // do nothing because + // we're only interested in edges crossing on the left + else if ((pt.x > op2->prev->pt.x && pt.x > op2->pt.x)) + val = 1 - val; // toggle val + else + { + double d = CrossProduct(op2->prev->pt, op2->pt, pt); + if (d == 0) return PointInPolygonResult::IsOn; + if ((d < 0) == is_above) val = 1 - val; + } + is_above = !is_above; + op2 = op2->next; + } + + if (is_above != starting_above) + { + double d = CrossProduct(op2->prev->pt, op2->pt, pt); + if (d == 0) return PointInPolygonResult::IsOn; + if ((d < 0) == is_above) val = 1 - val; + } + + if (val == 0) return PointInPolygonResult::IsOutside; + else return PointInPolygonResult::IsInside; + } + + inline Path64 GetCleanPath(OutPt* op) + { + Path64 result; + OutPt* op2 = op; + while (op2->next != op && + ((op2->pt.x == op2->next->pt.x && op2->pt.x == op2->prev->pt.x) || + (op2->pt.y == op2->next->pt.y && op2->pt.y == op2->prev->pt.y))) op2 = op2->next; + result.push_back(op2->pt); + OutPt* prevOp = op2; + op2 = op2->next; + while (op2 != op) + { + if ((op2->pt.x != op2->next->pt.x || op2->pt.x != prevOp->pt.x) && + (op2->pt.y != op2->next->pt.y || op2->pt.y != prevOp->pt.y)) + { + result.push_back(op2->pt); + prevOp = op2; + } + op2 = op2->next; + } + return result; + } + + inline bool Path1InsidePath2(OutPt* op1, OutPt* op2) + { + // we need to make some accommodation for rounding errors + // so we won't jump if the first vertex is found outside + PointInPolygonResult result; + int outside_cnt = 0; + OutPt* op = op1; + do + { + result = PointInOpPolygon(op->pt, op2); + if (result == PointInPolygonResult::IsOutside) ++outside_cnt; + else if (result == PointInPolygonResult::IsInside) --outside_cnt; + op = op->next; + } while (op != op1 && std::abs(outside_cnt) < 2); + if (std::abs(outside_cnt) > 1) return (outside_cnt < 0); + // since path1's location is still equivocal, check its midpoint + Point64 mp = GetBounds(GetCleanPath(op1)).MidPoint(); + Path64 path2 = GetCleanPath(op2); + return PointInPolygon(mp, path2) != PointInPolygonResult::IsOutside; + } + + //------------------------------------------------------------------------------ + //------------------------------------------------------------------------------ + + void AddLocMin(LocalMinimaList& list, + Vertex& vert, PathType polytype, bool is_open) + { + //make sure the vertex is added only once ... + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + + vert.flags = (vert.flags | VertexFlags::LocalMin); + list.push_back(std::make_unique (&vert, polytype, is_open)); + } + + void AddPaths_(const Paths64& paths, PathType polytype, bool is_open, + std::vector& vertexLists, LocalMinimaList& locMinList) + { + const auto total_vertex_count = + std::accumulate(paths.begin(), paths.end(), size_t(0), + [](const auto& a, const Path64& path) + {return a + path.size(); }); + if (total_vertex_count == 0) return; + + Vertex* vertices = new Vertex[total_vertex_count], * v = vertices; + for (const Path64& path : paths) + { + //for each path create a circular double linked list of vertices + Vertex* v0 = v, * curr_v = v, * prev_v = nullptr; + + if (path.empty()) + continue; + + v->prev = nullptr; + int cnt = 0; + for (const Point64& pt : path) + { + if (prev_v) + { + if (prev_v->pt == pt) continue; // ie skips duplicates + prev_v->next = curr_v; + } + curr_v->prev = prev_v; + curr_v->pt = pt; + curr_v->flags = VertexFlags::None; + prev_v = curr_v++; + cnt++; + } + if (!prev_v || !prev_v->prev) continue; + if (!is_open && prev_v->pt == v0->pt) + prev_v = prev_v->prev; + prev_v->next = v0; + v0->prev = prev_v; + v = curr_v; // ie get ready for next path + if (cnt < 2 || (cnt == 2 && !is_open)) continue; + + //now find and assign local minima + bool going_up, going_up0; + if (is_open) + { + curr_v = v0->next; + while (curr_v != v0 && curr_v->pt.y == v0->pt.y) + curr_v = curr_v->next; + going_up = curr_v->pt.y <= v0->pt.y; + if (going_up) + { + v0->flags = VertexFlags::OpenStart; + AddLocMin(locMinList , *v0, polytype, true); + } + else + v0->flags = VertexFlags::OpenStart | VertexFlags::LocalMax; + } + else // closed path + { + prev_v = v0->prev; + while (prev_v != v0 && prev_v->pt.y == v0->pt.y) + prev_v = prev_v->prev; + if (prev_v == v0) + continue; // only open paths can be completely flat + going_up = prev_v->pt.y > v0->pt.y; + } + + going_up0 = going_up; + prev_v = v0; + curr_v = v0->next; + while (curr_v != v0) + { + if (curr_v->pt.y > prev_v->pt.y && going_up) + { + prev_v->flags = (prev_v->flags | VertexFlags::LocalMax); + going_up = false; + } + else if (curr_v->pt.y < prev_v->pt.y && !going_up) + { + going_up = true; + AddLocMin(locMinList, *prev_v, polytype, is_open); + } + prev_v = curr_v; + curr_v = curr_v->next; + } + + if (is_open) + { + prev_v->flags = prev_v->flags | VertexFlags::OpenEnd; + if (going_up) + prev_v->flags = prev_v->flags | VertexFlags::LocalMax; + else + AddLocMin(locMinList, *prev_v, polytype, is_open); + } + else if (going_up != going_up0) + { + if (going_up0) AddLocMin(locMinList, *prev_v, polytype, false); + else prev_v->flags = prev_v->flags | VertexFlags::LocalMax; + } + } // end processing current path + + vertexLists.emplace_back(vertices); + } + + //------------------------------------------------------------------------------ + // ReuseableDataContainer64 methods ... + //------------------------------------------------------------------------------ + + void ReuseableDataContainer64::AddLocMin(Vertex& vert, PathType polytype, bool is_open) + { + //make sure the vertex is added only once ... + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + + vert.flags = (vert.flags | VertexFlags::LocalMin); + minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); + } + + void ReuseableDataContainer64::AddPaths(const Paths64& paths, + PathType polytype, bool is_open) + { + AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_); + } + + ReuseableDataContainer64::~ReuseableDataContainer64() + { + Clear(); + } + + void ReuseableDataContainer64::Clear() + { + minima_list_.clear(); + for (auto v : vertex_lists_) delete[] v; + vertex_lists_.clear(); + } + + //------------------------------------------------------------------------------ + // ClipperBase methods ... + //------------------------------------------------------------------------------ + + ClipperBase::~ClipperBase() + { + Clear(); + } + + void ClipperBase::DeleteEdges(Active*& e) + { + while (e) + { + Active* e2 = e; + e = e->next_in_ael; + delete e2; + } + } + + void ClipperBase::CleanUp() + { + DeleteEdges(actives_); + scanline_list_ = std::priority_queue(); + intersect_nodes_.clear(); + DisposeAllOutRecs(); + horz_seg_list_.clear(); + horz_join_list_.clear(); + } + + + void ClipperBase::Clear() + { + CleanUp(); + DisposeVerticesAndLocalMinima(); + current_locmin_iter_ = minima_list_.begin(); + minima_list_sorted_ = false; + has_open_paths_ = false; + } + + + void ClipperBase::Reset() + { + if (!minima_list_sorted_) + { + std::stable_sort(minima_list_.begin(), minima_list_.end(), LocMinSorter()); //#594 + minima_list_sorted_ = true; + } + LocalMinimaList::const_reverse_iterator i; + for (i = minima_list_.rbegin(); i != minima_list_.rend(); ++i) + InsertScanline((*i)->vertex->pt.y); + + current_locmin_iter_ = minima_list_.begin(); + actives_ = nullptr; + sel_ = nullptr; + succeeded_ = true; + } + + +#ifdef USINGZ + void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip) + { + if (!zCallback_) return; + // prioritize subject over clip vertices by passing + // subject vertices before clip vertices in the callback + if (GetPolyType(e1) == PathType::Subject) + { + if (ip == e1.bot) ip.z = e1.bot.z; + else if (ip == e1.top) ip.z = e1.top.z; + else if (ip == e2.bot) ip.z = e2.bot.z; + else if (ip == e2.top) ip.z = e2.top.z; + else ip.z = DefaultZ; + zCallback_(e1.bot, e1.top, e2.bot, e2.top, ip); + } + else + { + if (ip == e2.bot) ip.z = e2.bot.z; + else if (ip == e2.top) ip.z = e2.top.z; + else if (ip == e1.bot) ip.z = e1.bot.z; + else if (ip == e1.top) ip.z = e1.top.z; + else ip.z = DefaultZ; + zCallback_(e2.bot, e2.top, e1.bot, e1.top, ip); + } + } +#endif + + void ClipperBase::AddPath(const Path64& path, PathType polytype, bool is_open) + { + Paths64 tmp; + tmp.push_back(path); + AddPaths(tmp, polytype, is_open); + } + + void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open) + { + if (is_open) has_open_paths_ = true; + minima_list_sorted_ = false; + AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_); + } + + void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data) + { + // nb: reuseable_data will continue to own the vertices + // and remains responsible for their clean up. + succeeded_ = false; + minima_list_sorted_ = false; + LocalMinimaList::const_iterator i; + for (i = reuseable_data.minima_list_.cbegin(); i != reuseable_data.minima_list_.cend(); ++i) + { + minima_list_.push_back(std::make_unique ((*i)->vertex, (*i)->polytype, (*i)->is_open)); + if ((*i)->is_open) has_open_paths_ = true; + } + } + + void ClipperBase::InsertScanline(int64_t y) + { + scanline_list_.push(y); + } + + + bool ClipperBase::PopScanline(int64_t& y) + { + if (scanline_list_.empty()) return false; + y = scanline_list_.top(); + scanline_list_.pop(); + while (!scanline_list_.empty() && y == scanline_list_.top()) + scanline_list_.pop(); // Pop duplicates. + return true; + } + + + bool ClipperBase::PopLocalMinima(int64_t y, LocalMinima*& local_minima) + { + if (current_locmin_iter_ == minima_list_.end() || (*current_locmin_iter_)->vertex->pt.y != y) return false; + local_minima = (current_locmin_iter_++)->get(); + return true; + } + + void ClipperBase::DisposeAllOutRecs() + { + for (auto outrec : outrec_list_) + { + if (outrec->pts) DisposeOutPts(outrec); + delete outrec; + } + outrec_list_.resize(0); + } + + void ClipperBase::DisposeVerticesAndLocalMinima() + { + minima_list_.clear(); + for (auto v : vertex_lists_) delete[] v; + vertex_lists_.clear(); + } + + + void ClipperBase::AddLocMin(Vertex& vert, PathType polytype, bool is_open) + { + //make sure the vertex is added only once ... + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + + vert.flags = (vert.flags | VertexFlags::LocalMin); + minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); + } + + bool ClipperBase::IsContributingClosed(const Active& e) const + { + switch (fillrule_) + { + case FillRule::EvenOdd: + break; + case FillRule::NonZero: + if (abs(e.wind_cnt) != 1) return false; + break; + case FillRule::Positive: + if (e.wind_cnt != 1) return false; + break; + case FillRule::Negative: + if (e.wind_cnt != -1) return false; + break; + } + + switch (cliptype_) + { + case ClipType::None: + return false; + case ClipType::Intersection: + switch (fillrule_) + { + case FillRule::Positive: + return (e.wind_cnt2 > 0); + case FillRule::Negative: + return (e.wind_cnt2 < 0); + default: + return (e.wind_cnt2 != 0); + } + break; + + case ClipType::Union: + switch (fillrule_) + { + case FillRule::Positive: + return (e.wind_cnt2 <= 0); + case FillRule::Negative: + return (e.wind_cnt2 >= 0); + default: + return (e.wind_cnt2 == 0); + } + break; + + case ClipType::Difference: + bool result; + switch (fillrule_) + { + case FillRule::Positive: + result = (e.wind_cnt2 <= 0); + break; + case FillRule::Negative: + result = (e.wind_cnt2 >= 0); + break; + default: + result = (e.wind_cnt2 == 0); + } + if (GetPolyType(e) == PathType::Subject) + return result; + else + return !result; + break; + + case ClipType::Xor: return true; break; + } + return false; // we should never get here + } + + + inline bool ClipperBase::IsContributingOpen(const Active& e) const + { + bool is_in_clip, is_in_subj; + switch (fillrule_) + { + case FillRule::Positive: + is_in_clip = e.wind_cnt2 > 0; + is_in_subj = e.wind_cnt > 0; + break; + case FillRule::Negative: + is_in_clip = e.wind_cnt2 < 0; + is_in_subj = e.wind_cnt < 0; + break; + default: + is_in_clip = e.wind_cnt2 != 0; + is_in_subj = e.wind_cnt != 0; + } + + switch (cliptype_) + { + case ClipType::Intersection: return is_in_clip; + case ClipType::Union: return (!is_in_subj && !is_in_clip); + default: return !is_in_clip; + } + } + + + void ClipperBase::SetWindCountForClosedPathEdge(Active& e) + { + //Wind counts refer to polygon regions not edges, so here an edge's WindCnt + //indicates the higher of the wind counts for the two regions touching the + //edge. (NB Adjacent regions can only ever have their wind counts differ by + //one. Also, open paths have no meaningful wind directions or counts.) + + Active* e2 = e.prev_in_ael; + //find the nearest closed path edge of the same PolyType in AEL (heading left) + PathType pt = GetPolyType(e); + while (e2 && (GetPolyType(*e2) != pt || IsOpen(*e2))) e2 = e2->prev_in_ael; + + if (!e2) + { + e.wind_cnt = e.wind_dx; + e2 = actives_; + } + else if (fillrule_ == FillRule::EvenOdd) + { + e.wind_cnt = e.wind_dx; + e.wind_cnt2 = e2->wind_cnt2; + e2 = e2->next_in_ael; + } + else + { + //NonZero, positive, or negative filling here ... + //if e's WindCnt is in the SAME direction as its WindDx, then polygon + //filling will be on the right of 'e'. + //NB neither e2.WindCnt nor e2.WindDx should ever be 0. + if (e2->wind_cnt * e2->wind_dx < 0) + { + //opposite directions so 'e' is outside 'e2' ... + if (abs(e2->wind_cnt) > 1) + { + //outside prev poly but still inside another. + if (e2->wind_dx * e.wind_dx < 0) + //reversing direction so use the same WC + e.wind_cnt = e2->wind_cnt; + else + //otherwise keep 'reducing' the WC by 1 (ie towards 0) ... + e.wind_cnt = e2->wind_cnt + e.wind_dx; + } + else + //now outside all polys of same polytype so set own WC ... + e.wind_cnt = (IsOpen(e) ? 1 : e.wind_dx); + } + else + { + //'e' must be inside 'e2' + if (e2->wind_dx * e.wind_dx < 0) + //reversing direction so use the same WC + e.wind_cnt = e2->wind_cnt; + else + //otherwise keep 'increasing' the WC by 1 (ie away from 0) ... + e.wind_cnt = e2->wind_cnt + e.wind_dx; + } + e.wind_cnt2 = e2->wind_cnt2; + e2 = e2->next_in_ael; // ie get ready to calc WindCnt2 + } + + //update wind_cnt2 ... + if (fillrule_ == FillRule::EvenOdd) + while (e2 != &e) + { + if (GetPolyType(*e2) != pt && !IsOpen(*e2)) + e.wind_cnt2 = (e.wind_cnt2 == 0 ? 1 : 0); + e2 = e2->next_in_ael; + } + else + while (e2 != &e) + { + if (GetPolyType(*e2) != pt && !IsOpen(*e2)) + e.wind_cnt2 += e2->wind_dx; + e2 = e2->next_in_ael; + } + } + + + void ClipperBase::SetWindCountForOpenPathEdge(Active& e) + { + Active* e2 = actives_; + if (fillrule_ == FillRule::EvenOdd) + { + int cnt1 = 0, cnt2 = 0; + while (e2 != &e) + { + if (GetPolyType(*e2) == PathType::Clip) + cnt2++; + else if (!IsOpen(*e2)) + cnt1++; + e2 = e2->next_in_ael; + } + e.wind_cnt = (IsOdd(cnt1) ? 1 : 0); + e.wind_cnt2 = (IsOdd(cnt2) ? 1 : 0); + } + else + { + while (e2 != &e) + { + if (GetPolyType(*e2) == PathType::Clip) + e.wind_cnt2 += e2->wind_dx; + else if (!IsOpen(*e2)) + e.wind_cnt += e2->wind_dx; + e2 = e2->next_in_ael; + } + } + } + + bool IsValidAelOrder(const Active& resident, const Active& newcomer) + { + if (newcomer.curr_x != resident.curr_x) + return newcomer.curr_x > resident.curr_x; + + //get the turning direction a1.top, a2.bot, a2.top + double d = CrossProduct(resident.top, newcomer.bot, newcomer.top); + if (d != 0) return d < 0; + + //edges must be collinear to get here + //for starting open paths, place them according to + //the direction they're about to turn + if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y)) + { + return CrossProduct(newcomer.bot, + resident.top, NextVertex(resident)->pt) <= 0; + } + else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y)) + { + return CrossProduct(newcomer.bot, + newcomer.top, NextVertex(newcomer)->pt) >= 0; + } + + int64_t y = newcomer.bot.y; + bool newcomerIsLeft = newcomer.is_left_bound; + + if (resident.bot.y != y || resident.local_min->vertex->pt.y != y) + return newcomer.is_left_bound; + //resident must also have just been inserted + else if (resident.is_left_bound != newcomerIsLeft) + return newcomerIsLeft; + else if (IsCollinear(PrevPrevVertex(resident)->pt, + resident.bot, resident.top)) return true; + else + //compare turning direction of the alternate bound + return (CrossProduct(PrevPrevVertex(resident)->pt, + newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft; + } + + + void ClipperBase::InsertLeftEdge(Active& e) + { + Active* e2; + if (!actives_) + { + e.prev_in_ael = nullptr; + e.next_in_ael = nullptr; + actives_ = &e; + } + else if (!IsValidAelOrder(*actives_, e)) + { + e.prev_in_ael = nullptr; + e.next_in_ael = actives_; + actives_->prev_in_ael = &e; + actives_ = &e; + } + else + { + e2 = actives_; + while (e2->next_in_ael && IsValidAelOrder(*e2->next_in_ael, e)) + e2 = e2->next_in_ael; + if (e2->join_with == JoinWith::Right) + e2 = e2->next_in_ael; + if (!e2) return; // should never happen and stops compiler warning :) + e.next_in_ael = e2->next_in_ael; + if (e2->next_in_ael) e2->next_in_ael->prev_in_ael = &e; + e.prev_in_ael = e2; + e2->next_in_ael = &e; + } + } + + + void InsertRightEdge(Active& e, Active& e2) + { + e2.next_in_ael = e.next_in_ael; + if (e.next_in_ael) e.next_in_ael->prev_in_ael = &e2; + e2.prev_in_ael = &e; + e.next_in_ael = &e2; + } + + + void ClipperBase::InsertLocalMinimaIntoAEL(int64_t bot_y) + { + LocalMinima* local_minima; + Active* left_bound, * right_bound; + //Add any local minima (if any) at BotY ... + //nb: horizontal local minima edges should contain locMin.vertex.prev + + while (PopLocalMinima(bot_y, local_minima)) + { + if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::None) + { + left_bound = nullptr; + } + else + { + left_bound = new Active(); + left_bound->bot = local_minima->vertex->pt; + left_bound->curr_x = left_bound->bot.x; + left_bound->wind_dx = -1; + left_bound->vertex_top = local_minima->vertex->prev; // ie descending + left_bound->top = left_bound->vertex_top->pt; + left_bound->local_min = local_minima; + SetDx(*left_bound); + } + + if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::None) + { + right_bound = nullptr; + } + else + { + right_bound = new Active(); + right_bound->bot = local_minima->vertex->pt; + right_bound->curr_x = right_bound->bot.x; + right_bound->wind_dx = 1; + right_bound->vertex_top = local_minima->vertex->next; // ie ascending + right_bound->top = right_bound->vertex_top->pt; + right_bound->local_min = local_minima; + SetDx(*right_bound); + } + + //Currently LeftB is just the descending bound and RightB is the ascending. + //Now if the LeftB isn't on the left of RightB then we need swap them. + if (left_bound && right_bound) + { + if (IsHorizontal(*left_bound)) + { + if (IsHeadingRightHorz(*left_bound)) SwapActives(left_bound, right_bound); + } + else if (IsHorizontal(*right_bound)) + { + if (IsHeadingLeftHorz(*right_bound)) SwapActives(left_bound, right_bound); + } + else if (left_bound->dx < right_bound->dx) + SwapActives(left_bound, right_bound); + } + else if (!left_bound) + { + left_bound = right_bound; + right_bound = nullptr; + } + + bool contributing; + left_bound->is_left_bound = true; + InsertLeftEdge(*left_bound); + + if (IsOpen(*left_bound)) + { + SetWindCountForOpenPathEdge(*left_bound); + contributing = IsContributingOpen(*left_bound); + } + else + { + SetWindCountForClosedPathEdge(*left_bound); + contributing = IsContributingClosed(*left_bound); + } + + if (right_bound) + { + right_bound->is_left_bound = false; + right_bound->wind_cnt = left_bound->wind_cnt; + right_bound->wind_cnt2 = left_bound->wind_cnt2; + InsertRightEdge(*left_bound, *right_bound); /////// + if (contributing) + { + AddLocalMinPoly(*left_bound, *right_bound, left_bound->bot, true); + if (!IsHorizontal(*left_bound)) + CheckJoinLeft(*left_bound, left_bound->bot); + } + + while (right_bound->next_in_ael && + IsValidAelOrder(*right_bound->next_in_ael, *right_bound)) + { + IntersectEdges(*right_bound, *right_bound->next_in_ael, right_bound->bot); + SwapPositionsInAEL(*right_bound, *right_bound->next_in_ael); + } + + if (IsHorizontal(*right_bound)) + PushHorz(*right_bound); + else + { + CheckJoinRight(*right_bound, right_bound->bot); + InsertScanline(right_bound->top.y); + } + } + else if (contributing) + { + StartOpenPath(*left_bound, left_bound->bot); + } + + if (IsHorizontal(*left_bound)) + PushHorz(*left_bound); + else + InsertScanline(left_bound->top.y); + } // while (PopLocalMinima()) + } + + + inline void ClipperBase::PushHorz(Active& e) + { + e.next_in_sel = (sel_ ? sel_ : nullptr); + sel_ = &e; + } + + + inline bool ClipperBase::PopHorz(Active*& e) + { + e = sel_; + if (!e) return false; + sel_ = sel_->next_in_sel; + return true; + } + + + OutPt* ClipperBase::AddLocalMinPoly(Active& e1, Active& e2, + const Point64& pt, bool is_new) + { + OutRec* outrec = NewOutRec(); + e1.outrec = outrec; + e2.outrec = outrec; + + if (IsOpen(e1)) + { + outrec->owner = nullptr; + outrec->is_open = true; + if (e1.wind_dx > 0) + SetSides(*outrec, e1, e2); + else + SetSides(*outrec, e2, e1); + } + else + { + Active* prevHotEdge = GetPrevHotEdge(e1); + //e.windDx is the winding direction of the **input** paths + //and unrelated to the winding direction of output polygons. + //Output orientation is determined by e.outrec.frontE which is + //the ascending edge (see AddLocalMinPoly). + if (prevHotEdge) + { + if (using_polytree_) + SetOwner(outrec, prevHotEdge->outrec); + if (OutrecIsAscending(prevHotEdge) == is_new) + SetSides(*outrec, e2, e1); + else + SetSides(*outrec, e1, e2); + } + else + { + outrec->owner = nullptr; + if (is_new) + SetSides(*outrec, e1, e2); + else + SetSides(*outrec, e2, e1); + } + } + + OutPt* op = new OutPt(pt, outrec); + outrec->pts = op; + return op; + } + + + OutPt* ClipperBase::AddLocalMaxPoly(Active& e1, Active& e2, const Point64& pt) + { + if (IsJoined(e1)) Split(e1, pt); + if (IsJoined(e2)) Split(e2, pt); + + if (IsFront(e1) == IsFront(e2)) + { + if (IsOpenEnd(e1)) + SwapFrontBackSides(*e1.outrec); + else if (IsOpenEnd(e2)) + SwapFrontBackSides(*e2.outrec); + else + { + succeeded_ = false; + return nullptr; + } + } + + OutPt* result = AddOutPt(e1, pt); + if (e1.outrec == e2.outrec) + { + OutRec& outrec = *e1.outrec; + outrec.pts = result; + + if (using_polytree_) + { + Active* e = GetPrevHotEdge(e1); + if (!e) + outrec.owner = nullptr; + else + SetOwner(&outrec, e->outrec); + // nb: outRec.owner here is likely NOT the real + // owner but this will be checked in RecursiveCheckOwners() + } + + UncoupleOutRec(e1); + result = outrec.pts; + if (outrec.owner && !outrec.owner->front_edge) + outrec.owner = GetRealOutRec(outrec.owner); + } + //and to preserve the winding orientation of outrec ... + else if (IsOpen(e1)) + { + if (e1.wind_dx < 0) + JoinOutrecPaths(e1, e2); + else + JoinOutrecPaths(e2, e1); + } + else if (e1.outrec->idx < e2.outrec->idx) + JoinOutrecPaths(e1, e2); + else + JoinOutrecPaths(e2, e1); + return result; + } + + void ClipperBase::JoinOutrecPaths(Active& e1, Active& e2) + { + //join e2 outrec path onto e1 outrec path and then delete e2 outrec path + //pointers. (NB Only very rarely do the joining ends share the same coords.) + OutPt* p1_st = e1.outrec->pts; + OutPt* p2_st = e2.outrec->pts; + OutPt* p1_end = p1_st->next; + OutPt* p2_end = p2_st->next; + if (IsFront(e1)) + { + p2_end->prev = p1_st; + p1_st->next = p2_end; + p2_st->next = p1_end; + p1_end->prev = p2_st; + e1.outrec->pts = p2_st; + e1.outrec->front_edge = e2.outrec->front_edge; + if (e1.outrec->front_edge) + e1.outrec->front_edge->outrec = e1.outrec; + } + else + { + p1_end->prev = p2_st; + p2_st->next = p1_end; + p1_st->next = p2_end; + p2_end->prev = p1_st; + e1.outrec->back_edge = e2.outrec->back_edge; + if (e1.outrec->back_edge) + e1.outrec->back_edge->outrec = e1.outrec; + } + + //after joining, the e2.OutRec must contains no vertices ... + e2.outrec->front_edge = nullptr; + e2.outrec->back_edge = nullptr; + e2.outrec->pts = nullptr; + + if (IsOpenEnd(e1)) + { + e2.outrec->pts = e1.outrec->pts; + e1.outrec->pts = nullptr; + } + else + SetOwner(e2.outrec, e1.outrec); + + //and e1 and e2 are maxima and are about to be dropped from the Actives list. + e1.outrec = nullptr; + e2.outrec = nullptr; + } + + OutRec* ClipperBase::NewOutRec() + { + OutRec* result = new OutRec(); + result->idx = outrec_list_.size(); + outrec_list_.push_back(result); + result->pts = nullptr; + result->owner = nullptr; + result->polypath = nullptr; + result->is_open = false; + result->splits = nullptr; + return result; + } + + + OutPt* ClipperBase::AddOutPt(const Active& e, const Point64& pt) + { + OutPt* new_op = nullptr; + + //Outrec.OutPts: a circular doubly-linked-list of POutPt where ... + //op_front[.Prev]* ~~~> op_back & op_back == op_front.Next + OutRec* outrec = e.outrec; + bool to_front = IsFront(e); + OutPt* op_front = outrec->pts; + OutPt* op_back = op_front->next; + + if (to_front) + { + if (pt == op_front->pt) + return op_front; + } + else if (pt == op_back->pt) + return op_back; + + new_op = new OutPt(pt, outrec); + op_back->prev = new_op; + new_op->prev = op_front; + new_op->next = op_back; + op_front->next = new_op; + if (to_front) outrec->pts = new_op; + return new_op; + } + + void ClipperBase::CleanCollinear(OutRec* outrec) + { + outrec = GetRealOutRec(outrec); + if (!outrec || outrec->is_open) return; + if (!IsValidClosedPath(outrec->pts)) + { + DisposeOutPts(outrec); + return; + } + + OutPt* startOp = outrec->pts, * op2 = startOp; + for (; ; ) + { + //NB if preserveCollinear == true, then only remove 180 deg. spikes + if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt) && + (op2->pt == op2->prev->pt || + op2->pt == op2->next->pt || !preserve_collinear_ || + DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0)) + { + + if (op2 == outrec->pts) outrec->pts = op2->prev; + + op2 = DisposeOutPt(op2); + if (!IsValidClosedPath(op2)) + { + DisposeOutPts(outrec); + return; + } + startOp = op2; + continue; + } + op2 = op2->next; + if (op2 == startOp) break; + } + FixSelfIntersects(outrec); + } + + void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp) + { + // splitOp.prev -> splitOp && + // splitOp.next -> splitOp.next.next are intersecting + OutPt* prevOp = splitOp->prev; + OutPt* nextNextOp = splitOp->next->next; + outrec->pts = prevOp; + + Point64 ip; + GetSegmentIntersectPt(prevOp->pt, splitOp->pt, + splitOp->next->pt, nextNextOp->pt, ip); + +#ifdef USINGZ + if (zCallback_) zCallback_(prevOp->pt, splitOp->pt, + splitOp->next->pt, nextNextOp->pt, ip); +#endif + double area1 = Area(outrec->pts); + double absArea1 = std::fabs(area1); + if (absArea1 < 2) + { + DisposeOutPts(outrec); + return; + } + + double area2 = AreaTriangle(ip, splitOp->pt, splitOp->next->pt); + double absArea2 = std::fabs(area2); + + // de-link splitOp and splitOp.next from the path + // while inserting the intersection point + if (ip == prevOp->pt || ip == nextNextOp->pt) + { + nextNextOp->prev = prevOp; + prevOp->next = nextNextOp; + } + else + { + OutPt* newOp2 = new OutPt(ip, prevOp->outrec); + newOp2->prev = prevOp; + newOp2->next = nextNextOp; + nextNextOp->prev = newOp2; + prevOp->next = newOp2; + } + + // area1 is the path's area *before* splitting, whereas area2 is + // the area of the triangle containing splitOp & splitOp.next. + // So the only way for these areas to have the same sign is if + // the split triangle is larger than the path containing prevOp or + // if there's more than one self-intersection. + if (absArea2 >= 1 && + (absArea2 > absArea1 || (area2 > 0) == (area1 > 0))) + { + OutRec* newOr = NewOutRec(); + newOr->owner = outrec->owner; + + splitOp->outrec = newOr; + splitOp->next->outrec = newOr; + OutPt* newOp = new OutPt(ip, newOr); + newOp->prev = splitOp->next; + newOp->next = splitOp; + newOr->pts = newOp; + splitOp->prev = newOp; + splitOp->next->next = newOp; + + if (using_polytree_) + { + if (Path1InsidePath2(prevOp, newOp)) + { + newOr->splits = new OutRecList(); + newOr->splits->push_back(outrec); + } + else + { + if (!outrec->splits) outrec->splits = new OutRecList(); + outrec->splits->push_back(newOr); + } + } + } + else + { + delete splitOp->next; + delete splitOp; + } + } + + void ClipperBase::FixSelfIntersects(OutRec* outrec) + { + OutPt* op2 = outrec->pts; + for (; ; ) + { + // triangles can't self-intersect + if (op2->prev == op2->next->next) break; + if (SegmentsIntersect(op2->prev->pt, + op2->pt, op2->next->pt, op2->next->next->pt)) + { + if (op2 == outrec->pts || op2->next == outrec->pts) + outrec->pts = outrec->pts->prev; + DoSplitOp(outrec, op2); + if (!outrec->pts) break; + op2 = outrec->pts; + continue; + } + else + op2 = op2->next; + + if (op2 == outrec->pts) break; + } + } + + + inline void UpdateOutrecOwner(OutRec* outrec) + { + OutPt* opCurr = outrec->pts; + for (; ; ) + { + opCurr->outrec = outrec; + opCurr = opCurr->next; + if (opCurr == outrec->pts) return; + } + } + + + OutPt* ClipperBase::StartOpenPath(Active& e, const Point64& pt) + { + OutRec* outrec = NewOutRec(); + outrec->is_open = true; + + if (e.wind_dx > 0) + { + outrec->front_edge = &e; + outrec->back_edge = nullptr; + } + else + { + outrec->front_edge = nullptr; + outrec->back_edge = &e; + } + + e.outrec = outrec; + + OutPt* op = new OutPt(pt, outrec); + outrec->pts = op; + return op; + } + + inline void TrimHorz(Active& horzEdge, bool preserveCollinear) + { + bool wasTrimmed = false; + Point64 pt = NextVertex(horzEdge)->pt; + while (pt.y == horzEdge.top.y) + { + //always trim 180 deg. spikes (in closed paths) + //but otherwise break if preserveCollinear = true + if (preserveCollinear && + ((pt.x < horzEdge.top.x) != (horzEdge.bot.x < horzEdge.top.x))) + break; + + horzEdge.vertex_top = NextVertex(horzEdge); + horzEdge.top = pt; + wasTrimmed = true; + if (IsMaxima(horzEdge)) break; + pt = NextVertex(horzEdge)->pt; + } + + if (wasTrimmed) SetDx(horzEdge); // +/-infinity + } + + + inline void ClipperBase::UpdateEdgeIntoAEL(Active* e) + { + e->bot = e->top; + e->vertex_top = NextVertex(*e); + e->top = e->vertex_top->pt; + e->curr_x = e->bot.x; + SetDx(*e); + + if (IsJoined(*e)) Split(*e, e->bot); + + if (IsHorizontal(*e)) + { + if (!IsOpen(*e)) TrimHorz(*e, preserve_collinear_); + return; + } + + InsertScanline(e->top.y); + CheckJoinLeft(*e, e->bot); + CheckJoinRight(*e, e->bot, true); // (#500) + } + + Active* FindEdgeWithMatchingLocMin(Active* e) + { + Active* result = e->next_in_ael; + while (result) + { + if (result->local_min == e->local_min) return result; + else if (!IsHorizontal(*result) && e->bot != result->bot) result = nullptr; + else result = result->next_in_ael; + } + result = e->prev_in_ael; + while (result) + { + if (result->local_min == e->local_min) return result; + else if (!IsHorizontal(*result) && e->bot != result->bot) return nullptr; + else result = result->prev_in_ael; + } + return result; + } + + + void ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt) + { + //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... + if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2))) + { + if (IsOpen(e1) && IsOpen(e2)) return; + Active* edge_o, * edge_c; + if (IsOpen(e1)) + { + edge_o = &e1; + edge_c = &e2; + } + else + { + edge_o = &e2; + edge_c = &e1; + } + if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety + + if (abs(edge_c->wind_cnt) != 1) return; + switch (cliptype_) + { + case ClipType::Union: + if (!IsHotEdge(*edge_c)) return; + break; + default: + if (edge_c->local_min->polytype == PathType::Subject) + return; + } + + switch (fillrule_) + { + case FillRule::Positive: + if (edge_c->wind_cnt != 1) return; + break; + case FillRule::Negative: + if (edge_c->wind_cnt != -1) return; + break; + default: + if (std::abs(edge_c->wind_cnt) != 1) return; + } + +#ifdef USINGZ + OutPt* resultOp; +#endif + //toggle contribution ... + if (IsHotEdge(*edge_o)) + { +#ifdef USINGZ + resultOp = AddOutPt(*edge_o, pt); +#else + AddOutPt(*edge_o, pt); +#endif + if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr; + else edge_o->outrec->back_edge = nullptr; + edge_o->outrec = nullptr; + } + + //horizontal edges can pass under open paths at a LocMins + else if (pt == edge_o->local_min->vertex->pt && + !IsOpenEnd(*edge_o->local_min->vertex)) + { + //find the other side of the LocMin and + //if it's 'hot' join up with it ... + Active* e3 = FindEdgeWithMatchingLocMin(edge_o); + if (e3 && IsHotEdge(*e3)) + { + edge_o->outrec = e3->outrec; + if (edge_o->wind_dx > 0) + SetSides(*e3->outrec, *edge_o, *e3); + else + SetSides(*e3->outrec, *e3, *edge_o); + return; + } + else +#ifdef USINGZ + resultOp = StartOpenPath(*edge_o, pt); +#else + StartOpenPath(*edge_o, pt); +#endif + } + else +#ifdef USINGZ + resultOp = StartOpenPath(*edge_o, pt); +#else + StartOpenPath(*edge_o, pt); +#endif + +#ifdef USINGZ + if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt); +#endif + return; + } // end of an open path intersection + + //MANAGING CLOSED PATHS FROM HERE ON + + if (IsJoined(e1)) Split(e1, pt); + if (IsJoined(e2)) Split(e2, pt); + + //UPDATE WINDING COUNTS... + + int old_e1_windcnt, old_e2_windcnt; + if (e1.local_min->polytype == e2.local_min->polytype) + { + if (fillrule_ == FillRule::EvenOdd) + { + old_e1_windcnt = e1.wind_cnt; + e1.wind_cnt = e2.wind_cnt; + e2.wind_cnt = old_e1_windcnt; + } + else + { + if (e1.wind_cnt + e2.wind_dx == 0) + e1.wind_cnt = -e1.wind_cnt; + else + e1.wind_cnt += e2.wind_dx; + if (e2.wind_cnt - e1.wind_dx == 0) + e2.wind_cnt = -e2.wind_cnt; + else + e2.wind_cnt -= e1.wind_dx; + } + } + else + { + if (fillrule_ != FillRule::EvenOdd) + { + e1.wind_cnt2 += e2.wind_dx; + e2.wind_cnt2 -= e1.wind_dx; + } + else + { + e1.wind_cnt2 = (e1.wind_cnt2 == 0 ? 1 : 0); + e2.wind_cnt2 = (e2.wind_cnt2 == 0 ? 1 : 0); + } + } + + switch (fillrule_) + { + case FillRule::EvenOdd: + case FillRule::NonZero: + old_e1_windcnt = abs(e1.wind_cnt); + old_e2_windcnt = abs(e2.wind_cnt); + break; + default: + if (fillrule_ == fillpos) + { + old_e1_windcnt = e1.wind_cnt; + old_e2_windcnt = e2.wind_cnt; + } + else + { + old_e1_windcnt = -e1.wind_cnt; + old_e2_windcnt = -e2.wind_cnt; + } + break; + } + + const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1; + const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1; + + if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || + (!IsHotEdge(e2) && !e2_windcnt_in_01)) + return; + + //NOW PROCESS THE INTERSECTION ... +#ifdef USINGZ + OutPt* resultOp = nullptr; +#endif + //if both edges are 'hot' ... + if (IsHotEdge(e1) && IsHotEdge(e2)) + { + if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) || + (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor)) + { +#ifdef USINGZ + resultOp = AddLocalMaxPoly(e1, e2, pt); + if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); +#else + AddLocalMaxPoly(e1, e2, pt); +#endif + } + else if (IsFront(e1) || (e1.outrec == e2.outrec)) + { + //this 'else if' condition isn't strictly needed but + //it's sensible to split polygons that ony touch at + //a common vertex (not at common edges). + +#ifdef USINGZ + resultOp = AddLocalMaxPoly(e1, e2, pt); + OutPt* op2 = AddLocalMinPoly(e1, e2, pt); + if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); + if (zCallback_) SetZ(e1, e2, op2->pt); +#else + AddLocalMaxPoly(e1, e2, pt); + AddLocalMinPoly(e1, e2, pt); +#endif + } + else + { +#ifdef USINGZ + resultOp = AddOutPt(e1, pt); + OutPt* op2 = AddOutPt(e2, pt); + if (zCallback_) + { + SetZ(e1, e2, resultOp->pt); + SetZ(e1, e2, op2->pt); + } +#else + AddOutPt(e1, pt); + AddOutPt(e2, pt); +#endif + SwapOutrecs(e1, e2); + } + } + else if (IsHotEdge(e1)) + { +#ifdef USINGZ + resultOp = AddOutPt(e1, pt); + if (zCallback_) SetZ(e1, e2, resultOp->pt); +#else + AddOutPt(e1, pt); +#endif + SwapOutrecs(e1, e2); + } + else if (IsHotEdge(e2)) + { +#ifdef USINGZ + resultOp = AddOutPt(e2, pt); + if (zCallback_) SetZ(e1, e2, resultOp->pt); +#else + AddOutPt(e2, pt); +#endif + SwapOutrecs(e1, e2); + } + else + { + int64_t e1Wc2, e2Wc2; + switch (fillrule_) + { + case FillRule::EvenOdd: + case FillRule::NonZero: + e1Wc2 = abs(e1.wind_cnt2); + e2Wc2 = abs(e2.wind_cnt2); + break; + default: + if (fillrule_ == fillpos) + { + e1Wc2 = e1.wind_cnt2; + e2Wc2 = e2.wind_cnt2; + } + else + { + e1Wc2 = -e1.wind_cnt2; + e2Wc2 = -e2.wind_cnt2; + } + break; + } + + if (!IsSamePolyType(e1, e2)) + { +#ifdef USINGZ + resultOp = AddLocalMinPoly(e1, e2, pt, false); + if (zCallback_) SetZ(e1, e2, resultOp->pt); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif + } + else if (old_e1_windcnt == 1 && old_e2_windcnt == 1) + { +#ifdef USINGZ + resultOp = nullptr; +#endif + switch (cliptype_) + { + case ClipType::Union: + if (e1Wc2 <= 0 && e2Wc2 <= 0) +#ifdef USINGZ + resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif + break; + case ClipType::Difference: + if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + { +#ifdef USINGZ + resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif + } + break; + case ClipType::Xor: +#ifdef USINGZ + resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif + break; + default: + if (e1Wc2 > 0 && e2Wc2 > 0) +#ifdef USINGZ + resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif + break; + } +#ifdef USINGZ + if (resultOp && zCallback_) SetZ(e1, e2, resultOp->pt); +#endif + } + } + } + + inline void ClipperBase::DeleteFromAEL(Active& e) + { + Active* prev = e.prev_in_ael; + Active* next = e.next_in_ael; + if (!prev && !next && (&e != actives_)) return; // already deleted + if (prev) + prev->next_in_ael = next; + else + actives_ = next; + if (next) next->prev_in_ael = prev; + delete& e; + } + + + inline void ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y) + { + Active* e = actives_; + sel_ = e; + while (e) + { + e->prev_in_sel = e->prev_in_ael; + e->next_in_sel = e->next_in_ael; + e->jump = e->next_in_sel; + if (e->join_with == JoinWith::Left) + e->curr_x = e->prev_in_ael->curr_x; // also avoids complications + else + e->curr_x = TopX(*e, top_y); + e = e->next_in_ael; + } + } + + bool ClipperBase::ExecuteInternal(ClipType ct, FillRule fillrule, bool use_polytrees) + { + cliptype_ = ct; + fillrule_ = fillrule; + using_polytree_ = use_polytrees; + Reset(); + int64_t y; + if (ct == ClipType::None || !PopScanline(y)) return true; + + while (succeeded_) + { + InsertLocalMinimaIntoAEL(y); + Active* e; + while (PopHorz(e)) DoHorizontal(*e); + if (horz_seg_list_.size() > 0) + { + ConvertHorzSegsToJoins(); + horz_seg_list_.clear(); + } + bot_y_ = y; // bot_y_ == bottom of scanbeam + if (!PopScanline(y)) break; // y new top of scanbeam + DoIntersections(y); + DoTopOfScanbeam(y); + while (PopHorz(e)) DoHorizontal(*e); + } + if (succeeded_) ProcessHorzJoins(); + return succeeded_; + } + + inline void FixOutRecPts(OutRec* outrec) + { + OutPt* op = outrec->pts; + do { + op->outrec = outrec; + op = op->next; + } while (op != outrec->pts); + } + + inline bool SetHorzSegHeadingForward(HorzSegment& hs, OutPt* opP, OutPt* opN) + { + if (opP->pt.x == opN->pt.x) return false; + if (opP->pt.x < opN->pt.x) + { + hs.left_op = opP; + hs.right_op = opN; + hs.left_to_right = true; + } + else + { + hs.left_op = opN; + hs.right_op = opP; + hs.left_to_right = false; + } + return true; + } + + inline bool UpdateHorzSegment(HorzSegment& hs) + { + OutPt* op = hs.left_op; + OutRec* outrec = GetRealOutRec(op->outrec); + bool outrecHasEdges = outrec->front_edge; + int64_t curr_y = op->pt.y; + OutPt* opP = op, * opN = op; + if (outrecHasEdges) + { + OutPt* opA = outrec->pts, * opZ = opA->next; + while (opP != opZ && opP->prev->pt.y == curr_y) + opP = opP->prev; + while (opN != opA && opN->next->pt.y == curr_y) + opN = opN->next; + } + else + { + while (opP->prev != opN && opP->prev->pt.y == curr_y) + opP = opP->prev; + while (opN->next != opP && opN->next->pt.y == curr_y) + opN = opN->next; + } + bool result = + SetHorzSegHeadingForward(hs, opP, opN) && + !hs.left_op->horz; + + if (result) + hs.left_op->horz = &hs; + else + hs.right_op = nullptr; // (for sorting) + return result; + } + + void ClipperBase::ConvertHorzSegsToJoins() + { + auto j = std::count_if(horz_seg_list_.begin(), + horz_seg_list_.end(), + [](HorzSegment& hs) { return UpdateHorzSegment(hs); }); + if (j < 2) return; + + std::stable_sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter()); + + HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2; + HorzSegmentList::iterator hs_end = hs1 +j; + HorzSegmentList::iterator hs_end1 = hs_end - 1; + + for (; hs1 != hs_end1; ++hs1) + { + for (hs2 = hs1 + 1; hs2 != hs_end; ++hs2) + { + if ((hs2->left_op->pt.x >= hs1->right_op->pt.x) || + (hs2->left_to_right == hs1->left_to_right) || + (hs2->right_op->pt.x <= hs1->left_op->pt.x)) continue; + int64_t curr_y = hs1->left_op->pt.y; + if (hs1->left_to_right) + { + while (hs1->left_op->next->pt.y == curr_y && + hs1->left_op->next->pt.x <= hs2->left_op->pt.x) + hs1->left_op = hs1->left_op->next; + while (hs2->left_op->prev->pt.y == curr_y && + hs2->left_op->prev->pt.x <= hs1->left_op->pt.x) + hs2->left_op = hs2->left_op->prev; + HorzJoin join = HorzJoin( + DuplicateOp(hs1->left_op, true), + DuplicateOp(hs2->left_op, false)); + horz_join_list_.push_back(join); + } + else + { + while (hs1->left_op->prev->pt.y == curr_y && + hs1->left_op->prev->pt.x <= hs2->left_op->pt.x) + hs1->left_op = hs1->left_op->prev; + while (hs2->left_op->next->pt.y == curr_y && + hs2->left_op->next->pt.x <= hs1->left_op->pt.x) + hs2->left_op = hs2->left_op->next; + HorzJoin join = HorzJoin( + DuplicateOp(hs2->left_op, true), + DuplicateOp(hs1->left_op, false)); + horz_join_list_.push_back(join); + } + } + } + } + + void MoveSplits(OutRec* fromOr, OutRec* toOr) + { + if (!fromOr->splits) return; + if (!toOr->splits) toOr->splits = new OutRecList(); + OutRecList::iterator orIter = fromOr->splits->begin(); + for (; orIter != fromOr->splits->end(); ++orIter) + toOr->splits->push_back(*orIter); + fromOr->splits->clear(); + } + + + void ClipperBase::ProcessHorzJoins() + { + for (const HorzJoin& j : horz_join_list_) + { + OutRec* or1 = GetRealOutRec(j.op1->outrec); + OutRec* or2 = GetRealOutRec(j.op2->outrec); + + OutPt* op1b = j.op1->next; + OutPt* op2b = j.op2->prev; + j.op1->next = j.op2; + j.op2->prev = j.op1; + op1b->prev = op2b; + op2b->next = op1b; + + if (or1 == or2) // 'join' is really a split + { + or2 = NewOutRec(); + or2->pts = op1b; + FixOutRecPts(or2); + + //if or1->pts has moved to or2 then update or1->pts!! + if (or1->pts->outrec == or2) + { + or1->pts = j.op1; + or1->pts->outrec = or1; + } + + if (using_polytree_) //#498, #520, #584, D#576, #618 + { + if (Path1InsidePath2(or1->pts, or2->pts)) + { + //swap or1's & or2's pts + OutPt* tmp = or1->pts; + or1->pts = or2->pts; + or2->pts = tmp; + FixOutRecPts(or1); + FixOutRecPts(or2); + //or2 is now inside or1 + or2->owner = or1; + } + else if (Path1InsidePath2(or2->pts, or1->pts)) + { + or2->owner = or1; + } + else + or2->owner = or1->owner; + + if (!or1->splits) or1->splits = new OutRecList(); + or1->splits->push_back(or2); + } + else + or2->owner = or1; + } + else + { + or2->pts = nullptr; + if (using_polytree_) + { + SetOwner(or2, or1); + MoveSplits(or2, or1); //#618 + } + else + or2->owner = or1; + } + } + } + + void ClipperBase::DoIntersections(const int64_t top_y) + { + if (BuildIntersectList(top_y)) + { + ProcessIntersectList(); + intersect_nodes_.clear(); + } + } + + void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y) + { + Point64 ip; + if (!GetSegmentIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip)) + ip = Point64(e1.curr_x, top_y); //parallel edges + + //rounding errors can occasionally place the calculated intersection + //point either below or above the scanbeam, so check and correct ... + if (ip.y > bot_y_ || ip.y < top_y) + { + double abs_dx1 = std::fabs(e1.dx); + double abs_dx2 = std::fabs(e2.dx); + if (abs_dx1 > 100 && abs_dx2 > 100) + { + if (abs_dx1 > abs_dx2) + ip = GetClosestPointOnSegment(ip, e1.bot, e1.top); + else + ip = GetClosestPointOnSegment(ip, e2.bot, e2.top); + } + else if (abs_dx1 > 100) + ip = GetClosestPointOnSegment(ip, e1.bot, e1.top); + else if (abs_dx2 > 100) + ip = GetClosestPointOnSegment(ip, e2.bot, e2.top); + else + { + if (ip.y < top_y) ip.y = top_y; + else ip.y = bot_y_; + if (abs_dx1 < abs_dx2) ip.x = TopX(e1, ip.y); + else ip.x = TopX(e2, ip.y); + } + } + intersect_nodes_.push_back(IntersectNode(&e1, &e2, ip)); + } + + bool ClipperBase::BuildIntersectList(const int64_t top_y) + { + if (!actives_ || !actives_->next_in_ael) return false; + + //Calculate edge positions at the top of the current scanbeam, and from this + //we will determine the intersections required to reach these new positions. + AdjustCurrXAndCopyToSEL(top_y); + //Find all edge intersections in the current scanbeam using a stable merge + //sort that ensures only adjacent edges are intersecting. Intersect info is + //stored in FIntersectList ready to be processed in ProcessIntersectList. + //Re merge sorts see https://stackoverflow.com/a/46319131/359538 + + Active* left = sel_, * right, * l_end, * r_end, * curr_base, * tmp; + + while (left && left->jump) + { + Active* prev_base = nullptr; + while (left && left->jump) + { + curr_base = left; + right = left->jump; + l_end = right; + r_end = right->jump; + left->jump = r_end; + while (left != l_end && right != r_end) + { + if (right->curr_x < left->curr_x) + { + tmp = right->prev_in_sel; + for (; ; ) + { + AddNewIntersectNode(*tmp, *right, top_y); + if (tmp == left) break; + tmp = tmp->prev_in_sel; + } + + tmp = right; + right = ExtractFromSEL(tmp); + l_end = right; + Insert1Before2InSEL(tmp, left); + if (left == curr_base) + { + curr_base = tmp; + curr_base->jump = r_end; + if (!prev_base) sel_ = curr_base; + else prev_base->jump = curr_base; + } + } + else left = left->next_in_sel; + } + prev_base = curr_base; + left = r_end; + } + left = sel_; + } + return intersect_nodes_.size() > 0; + } + + void ClipperBase::ProcessIntersectList() + { + //We now have a list of intersections required so that edges will be + //correctly positioned at the top of the scanbeam. However, it's important + //that edge intersections are processed from the bottom up, but it's also + //crucial that intersections only occur between adjacent edges. + + //First we do a quicksort so intersections proceed in a bottom up order ... + std::sort(intersect_nodes_.begin(), intersect_nodes_.end(), IntersectListSort); + //Now as we process these intersections, we must sometimes adjust the order + //to ensure that intersecting edges are always adjacent ... + + IntersectNodeList::iterator node_iter, node_iter2; + for (node_iter = intersect_nodes_.begin(); + node_iter != intersect_nodes_.end(); ++node_iter) + { + if (!EdgesAdjacentInAEL(*node_iter)) + { + node_iter2 = node_iter + 1; + while (!EdgesAdjacentInAEL(*node_iter2)) ++node_iter2; + std::swap(*node_iter, *node_iter2); + } + + IntersectNode& node = *node_iter; + IntersectEdges(*node.edge1, *node.edge2, node.pt); + SwapPositionsInAEL(*node.edge1, *node.edge2); + + node.edge1->curr_x = node.pt.x; + node.edge2->curr_x = node.pt.x; + CheckJoinLeft(*node.edge2, node.pt, true); + CheckJoinRight(*node.edge1, node.pt, true); + } + } + + void ClipperBase::SwapPositionsInAEL(Active& e1, Active& e2) + { + //preconditon: e1 must be immediately to the left of e2 + Active* next = e2.next_in_ael; + if (next) next->prev_in_ael = &e1; + Active* prev = e1.prev_in_ael; + if (prev) prev->next_in_ael = &e2; + e2.prev_in_ael = prev; + e2.next_in_ael = &e1; + e1.prev_in_ael = &e2; + e1.next_in_ael = next; + if (!e2.prev_in_ael) actives_ = &e2; + } + + inline OutPt* GetLastOp(const Active& hot_edge) + { + OutRec* outrec = hot_edge.outrec; + OutPt* result = outrec->pts; + if (&hot_edge != outrec->front_edge) + result = result->next; + return result; + } + + void ClipperBase::AddTrialHorzJoin(OutPt* op) + { + if (op->outrec->is_open) return; + horz_seg_list_.push_back(HorzSegment(op)); + } + + bool ClipperBase::ResetHorzDirection(const Active& horz, + const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right) + { + if (horz.bot.x == horz.top.x) + { + //the horizontal edge is going nowhere ... + horz_left = horz.curr_x; + horz_right = horz.curr_x; + Active* e = horz.next_in_ael; + while (e && e->vertex_top != max_vertex) e = e->next_in_ael; + return e != nullptr; + } + else if (horz.curr_x < horz.top.x) + { + horz_left = horz.curr_x; + horz_right = horz.top.x; + return true; + } + else + { + horz_left = horz.top.x; + horz_right = horz.curr_x; + return false; // right to left + } + } + + void ClipperBase::DoHorizontal(Active& horz) + /******************************************************************************* + * 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 the bottom vertices of * + * other HEs[#] and with non-horizontal edges [*]. Once these intersections * + * are completed, intermediate HEs are 'promoted' to the next edge in their * + * bounds, and they in turn may be intersected[%] by other HEs. * + * * + * eg: 3 horizontals at a scanline: / | / / * + * | / | (HE3)o ========%========== o * + * o ======= o(HE2) / | / / * + * o ============#=========*======*========#=========o (HE1) * + * / | / | / * + *******************************************************************************/ + { + Point64 pt; + bool horzIsOpen = IsOpen(horz); + int64_t y = horz.bot.y; + Vertex* vertex_max; + if (horzIsOpen) + vertex_max = GetCurrYMaximaVertex_Open(horz); + else + vertex_max = GetCurrYMaximaVertex(horz); + + //// remove 180 deg.spikes and also simplify + //// consecutive horizontals when PreserveCollinear = true + //if (!horzIsOpen && vertex_max != horz.vertex_top) + // TrimHorz(horz, PreserveCollinear); + + int64_t horz_left, horz_right; + bool is_left_to_right = + ResetHorzDirection(horz, vertex_max, horz_left, horz_right); + + if (IsHotEdge(horz)) + { +#ifdef USINGZ + OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y, horz.bot.z)); +#else + OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y)); +#endif + AddTrialHorzJoin(op); + } + + while (true) // loop through consec. horizontal edges + { + Active* e; + if (is_left_to_right) e = horz.next_in_ael; + else e = horz.prev_in_ael; + + while (e) + { + if (e->vertex_top == vertex_max) + { + if (IsHotEdge(horz) && IsJoined(*e)) + Split(*e, e->top); + + //if (IsHotEdge(horz) != IsHotEdge(*e)) + // DoError(undefined_error_i); + + if (IsHotEdge(horz)) + { + while (horz.vertex_top != vertex_max) + { + AddOutPt(horz, horz.top); + UpdateEdgeIntoAEL(&horz); + } + if (is_left_to_right) + AddLocalMaxPoly(horz, *e, horz.top); + else + AddLocalMaxPoly(*e, horz, horz.top); + } + DeleteFromAEL(*e); + DeleteFromAEL(horz); + return; + } + + //if horzEdge is a maxima, keep going until we reach + //its maxima pair, otherwise check for break conditions + if (vertex_max != horz.vertex_top || IsOpenEnd(horz)) + { + //otherwise stop when 'ae' is beyond the end of the horizontal line + if ((is_left_to_right && e->curr_x > horz_right) || + (!is_left_to_right && e->curr_x < horz_left)) break; + + if (e->curr_x == horz.top.x && !IsHorizontal(*e)) + { + pt = NextVertex(horz)->pt; + if (is_left_to_right) + { + //with open paths we'll only break once past horz's end + if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e)) + { + if (TopX(*e, pt.y) > pt.x) break; + } + //otherwise we'll only break when horz's outslope is greater than e's + else if (TopX(*e, pt.y) >= pt.x) break; + } + else + { + if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e)) + { + if (TopX(*e, pt.y) < pt.x) break; + } + else if (TopX(*e, pt.y) <= pt.x) break; + } + } + } + + pt = Point64(e->curr_x, horz.bot.y); + if (is_left_to_right) + { + IntersectEdges(horz, *e, pt); + SwapPositionsInAEL(horz, *e); + CheckJoinLeft(*e, pt); + horz.curr_x = e->curr_x; + e = horz.next_in_ael; + } + else + { + IntersectEdges(*e, horz, pt); + SwapPositionsInAEL(*e, horz); + CheckJoinRight(*e, pt); + horz.curr_x = e->curr_x; + e = horz.prev_in_ael; + } + + if (horz.outrec) + { + //nb: The outrec containining the op returned by IntersectEdges + //above may no longer be associated with horzEdge. + AddTrialHorzJoin(GetLastOp(horz)); + } + } + + //check if we've finished with (consecutive) horizontals ... + if (horzIsOpen && IsOpenEnd(horz)) // ie open at top + { + if (IsHotEdge(horz)) + { + AddOutPt(horz, horz.top); + if (IsFront(horz)) + horz.outrec->front_edge = nullptr; + else + horz.outrec->back_edge = nullptr; + horz.outrec = nullptr; + } + DeleteFromAEL(horz); + return; + } + else if (NextVertex(horz)->pt.y != horz.top.y) + break; + + //still more horizontals in bound to process ... + if (IsHotEdge(horz)) + AddOutPt(horz, horz.top); + UpdateEdgeIntoAEL(&horz); + + is_left_to_right = + ResetHorzDirection(horz, vertex_max, horz_left, horz_right); + } + + if (IsHotEdge(horz)) + { + OutPt* op = AddOutPt(horz, horz.top); + AddTrialHorzJoin(op); + } + + UpdateEdgeIntoAEL(&horz); // end of an intermediate horiz. + } + + void ClipperBase::DoTopOfScanbeam(const int64_t y) + { + sel_ = nullptr; // sel_ is reused to flag horizontals (see PushHorz below) + Active* e = actives_; + while (e) + { + //nb: 'e' will never be horizontal here + if (e->top.y == y) + { + e->curr_x = e->top.x; + if (IsMaxima(*e)) + { + e = DoMaxima(*e); // TOP OF BOUND (MAXIMA) + continue; + } + else + { + //INTERMEDIATE VERTEX ... + if (IsHotEdge(*e)) AddOutPt(*e, e->top); + UpdateEdgeIntoAEL(e); + if (IsHorizontal(*e)) + PushHorz(*e); // horizontals are processed later + } + } + else // i.e. not the top of the edge + e->curr_x = TopX(*e, y); + + e = e->next_in_ael; + } + } + + + Active* ClipperBase::DoMaxima(Active& e) + { + Active* next_e, * prev_e, * max_pair; + prev_e = e.prev_in_ael; + next_e = e.next_in_ael; + if (IsOpenEnd(e)) + { + if (IsHotEdge(e)) AddOutPt(e, e.top); + if (!IsHorizontal(e)) + { + if (IsHotEdge(e)) + { + if (IsFront(e)) + e.outrec->front_edge = nullptr; + else + e.outrec->back_edge = nullptr; + e.outrec = nullptr; + } + DeleteFromAEL(e); + } + return next_e; + } + + max_pair = GetMaximaPair(e); + if (!max_pair) return next_e; // eMaxPair is horizontal + + if (IsJoined(e)) Split(e, e.top); + if (IsJoined(*max_pair)) Split(*max_pair, max_pair->top); + + //only non-horizontal maxima here. + //process any edges between maxima pair ... + while (next_e != max_pair) + { + IntersectEdges(e, *next_e, e.top); + SwapPositionsInAEL(e, *next_e); + next_e = e.next_in_ael; + } + + if (IsOpen(e)) + { + if (IsHotEdge(e)) + AddLocalMaxPoly(e, *max_pair, e.top); + DeleteFromAEL(*max_pair); + DeleteFromAEL(e); + return (prev_e ? prev_e->next_in_ael : actives_); + } + + // e.next_in_ael== max_pair ... + if (IsHotEdge(e)) + AddLocalMaxPoly(e, *max_pair, e.top); + + DeleteFromAEL(e); + DeleteFromAEL(*max_pair); + return (prev_e ? prev_e->next_in_ael : actives_); + } + + void ClipperBase::Split(Active& e, const Point64& pt) + { + if (e.join_with == JoinWith::Right) + { + e.join_with = JoinWith::None; + e.next_in_ael->join_with = JoinWith::None; + AddLocalMinPoly(e, *e.next_in_ael, pt, true); + } + else + { + e.join_with = JoinWith::None; + e.prev_in_ael->join_with = JoinWith::None; + AddLocalMinPoly(*e.prev_in_ael, e, pt, true); + } + } + + void ClipperBase::CheckJoinLeft(Active& e, + const Point64& pt, bool check_curr_x) + { + Active* prev = e.prev_in_ael; + if (!prev || + !IsHotEdge(e) || !IsHotEdge(*prev) || + IsHorizontal(e) || IsHorizontal(*prev) || + IsOpen(e) || IsOpen(*prev) ) return; + if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) && + ((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins + + if (check_curr_x) + { + if (PerpendicDistFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return; + } + else if (e.curr_x != prev->curr_x) return; + if (!IsCollinear(e.top, pt, prev->top)) return; + + if (e.outrec->idx == prev->outrec->idx) + AddLocalMaxPoly(*prev, e, pt); + else if (e.outrec->idx < prev->outrec->idx) + JoinOutrecPaths(e, *prev); + else + JoinOutrecPaths(*prev, e); + prev->join_with = JoinWith::Right; + e.join_with = JoinWith::Left; + } + + void ClipperBase::CheckJoinRight(Active& e, + const Point64& pt, bool check_curr_x) + { + Active* next = e.next_in_ael; + if (!next || + !IsHotEdge(e) || !IsHotEdge(*next) || + IsHorizontal(e) || IsHorizontal(*next) || + IsOpen(e) || IsOpen(*next)) return; + if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) && + ((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins + + if (check_curr_x) + { + if (PerpendicDistFromLineSqrd(pt, next->bot, next->top) > 0.35) return; + } + else if (e.curr_x != next->curr_x) return; + if (!IsCollinear(e.top, pt, next->top)) return; + + if (e.outrec->idx == next->outrec->idx) + AddLocalMaxPoly(e, *next, pt); + else if (e.outrec->idx < next->outrec->idx) + JoinOutrecPaths(e, *next); + else + JoinOutrecPaths(*next, e); + + e.join_with = JoinWith::Right; + next->join_with = JoinWith::Left; + } + + inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2) + { + OutRec* outrec = GetRealOutRec(op->outrec); + op2 = op; + if (outrec->front_edge) + { + while (op->prev != outrec->pts && + op->prev->pt.y == op->pt.y) op = op->prev; + while (op2 != outrec->pts && + op2->next->pt.y == op2->pt.y) op2 = op2->next; + return op2 != op; + } + else + { + while (op->prev != op2 && op->prev->pt.y == op->pt.y) + op = op->prev; + while (op2->next != op && op2->next->pt.y == op2->pt.y) + op2 = op2->next; + return op2 != op && op2->next != op; + } + } + + bool BuildPath64(OutPt* op, bool reverse, bool isOpen, Path64& path) + { + if (!op || op->next == op || (!isOpen && op->next == op->prev)) + return false; + + path.resize(0); + Point64 lastPt; + OutPt* op2; + if (reverse) + { + lastPt = op->pt; + op2 = op->prev; + } + else + { + op = op->next; + lastPt = op->pt; + op2 = op->next; + } + path.push_back(lastPt); + + while (op2 != op) + { + if (op2->pt != lastPt) + { + lastPt = op2->pt; + path.push_back(lastPt); + } + if (reverse) + op2 = op2->prev; + else + op2 = op2->next; + } + + if (!isOpen && path.size() == 3 && IsVerySmallTriangle(*op2)) return false; + else return true; + } + + bool ClipperBase::CheckBounds(OutRec* outrec) + { + if (!outrec->pts) return false; + if (!outrec->bounds.IsEmpty()) return true; + CleanCollinear(outrec); + if (!outrec->pts || + !BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){ + return false;} + outrec->bounds = GetBounds(outrec->path); + return true; + } + + bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits) + { + for (auto split : *splits) + { + split = GetRealOutRec(split); + if(!split || split == outrec || split->recursive_split == outrec) continue; + split->recursive_split = outrec; // prevent infinite loops + + if (split->splits && CheckSplitOwner(outrec, split->splits)) + return true; + else if (CheckBounds(split) && + IsValidOwner(outrec, split) && + split->bounds.Contains(outrec->bounds) && + Path1InsidePath2(outrec->pts, split->pts)) + { + outrec->owner = split; //found in split + return true; + } + } + return false; + } + + void ClipperBase::RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath) + { + // pre-condition: outrec will have valid bounds + // post-condition: if a valid path, outrec will have a polypath + + if (outrec->polypath || outrec->bounds.IsEmpty()) return; + + while (outrec->owner) + { + if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break; + if (outrec->owner->pts && CheckBounds(outrec->owner) && + outrec->owner->bounds.Contains(outrec->bounds) && + Path1InsidePath2(outrec->pts, outrec->owner->pts)) break; + outrec->owner = outrec->owner->owner; + } + + if (outrec->owner) + { + if (!outrec->owner->polypath) + RecursiveCheckOwners(outrec->owner, polypath); + outrec->polypath = outrec->owner->polypath->AddChild(outrec->path); + } + else + outrec->polypath = polypath->AddChild(outrec->path); + } + + void Clipper64::BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen) + { + solutionClosed.resize(0); + solutionClosed.reserve(outrec_list_.size()); + if (solutionOpen) + { + solutionOpen->resize(0); + solutionOpen->reserve(outrec_list_.size()); + } + + // nb: outrec_list_.size() may change in the following + // while loop because polygons may be split during + // calls to CleanCollinear which calls FixSelfIntersects + for (size_t i = 0; i < outrec_list_.size(); ++i) + { + OutRec* outrec = outrec_list_[i]; + if (outrec->pts == nullptr) continue; + + Path64 path; + if (solutionOpen && outrec->is_open) + { + if (BuildPath64(outrec->pts, reverse_solution_, true, path)) + solutionOpen->emplace_back(std::move(path)); + } + else + { + // nb: CleanCollinear can add to outrec_list_ + CleanCollinear(outrec); + //closed paths should always return a Positive orientation + if (BuildPath64(outrec->pts, reverse_solution_, false, path)) + solutionClosed.emplace_back(std::move(path)); + } + } + } + + void Clipper64::BuildTree64(PolyPath64& polytree, Paths64& open_paths) + { + polytree.Clear(); + open_paths.resize(0); + if (has_open_paths_) + open_paths.reserve(outrec_list_.size()); + + // outrec_list_.size() is not static here because + // CheckBounds below can indirectly add additional + // OutRec (via FixOutRecPts & CleanCollinear) + for (size_t i = 0; i < outrec_list_.size(); ++i) + { + OutRec* outrec = outrec_list_[i]; + if (!outrec || !outrec->pts) continue; + if (outrec->is_open) + { + Path64 path; + if (BuildPath64(outrec->pts, reverse_solution_, true, path)) + open_paths.push_back(path); + continue; + } + + if (CheckBounds(outrec)) + RecursiveCheckOwners(outrec, &polytree); + } + } + + bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale) + { + if (!op || op->next == op || (!isOpen && op->next == op->prev)) + return false; + + path.resize(0); + Point64 lastPt; + OutPt* op2; + if (reverse) + { + lastPt = op->pt; + op2 = op->prev; + } + else + { + op = op->next; + lastPt = op->pt; + op2 = op->next; + } +#ifdef USINGZ + path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z)); +#else + path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); +#endif + + while (op2 != op) + { + if (op2->pt != lastPt) + { + lastPt = op2->pt; +#ifdef USINGZ + path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z)); +#else + path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); +#endif + + } + if (reverse) + op2 = op2->prev; + else + op2 = op2->next; + } + if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false; + return true; + } + + void ClipperD::BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen) + { + solutionClosed.resize(0); + solutionClosed.reserve(outrec_list_.size()); + if (solutionOpen) + { + solutionOpen->resize(0); + solutionOpen->reserve(outrec_list_.size()); + } + + // outrec_list_.size() is not static here because + // CleanCollinear below can indirectly add additional + // OutRec (via FixOutRecPts) + for (std::size_t i = 0; i < outrec_list_.size(); ++i) + { + OutRec* outrec = outrec_list_[i]; + if (outrec->pts == nullptr) continue; + + PathD path; + if (solutionOpen && outrec->is_open) + { + if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_)) + solutionOpen->emplace_back(std::move(path)); + } + else + { + CleanCollinear(outrec); + //closed paths should always return a Positive orientation + if (BuildPathD(outrec->pts, reverse_solution_, false, path, invScale_)) + solutionClosed.emplace_back(std::move(path)); + } + } + } + + void ClipperD::BuildTreeD(PolyPathD& polytree, PathsD& open_paths) + { + polytree.Clear(); + open_paths.resize(0); + if (has_open_paths_) + open_paths.reserve(outrec_list_.size()); + + // outrec_list_.size() is not static here because + // BuildPathD below can indirectly add additional OutRec //#607 + for (size_t i = 0; i < outrec_list_.size(); ++i) + { + OutRec* outrec = outrec_list_[i]; + if (!outrec || !outrec->pts) continue; + if (outrec->is_open) + { + PathD path; + if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_)) + open_paths.push_back(path); + continue; + } + + if (CheckBounds(outrec)) + RecursiveCheckOwners(outrec, &polytree); + } + } + +} // namespace clipper2lib diff --git a/KP3Dii/ext/src/clipper2/clipper.offset.cpp b/KP3Dii/ext/src/clipper2/clipper.offset.cpp new file mode 100644 index 0000000..508a7f0 --- /dev/null +++ b/KP3Dii/ext/src/clipper2/clipper.offset.cpp @@ -0,0 +1,645 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 17 April 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : Path Offset (Inflate/Shrink) * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#include +#include "clipper2/clipper.h" +#include "clipper2/clipper.offset.h" + +namespace Clipper2Lib { + +const double default_arc_tolerance = 0.25; +const double floating_point_tolerance = 1e-12; + +//------------------------------------------------------------------------------ +// Miscellaneous methods +//------------------------------------------------------------------------------ + +std::optional GetLowestClosedPathIdx(const Paths64& paths) +{ + std::optional result; + Point64 botPt = Point64(INT64_MAX, INT64_MIN); + for (size_t i = 0; i < paths.size(); ++i) + { + for (const Point64& pt : paths[i]) + { + if ((pt.y < botPt.y) || + ((pt.y == botPt.y) && (pt.x >= botPt.x))) continue; + result = i; + botPt.x = pt.x; + botPt.y = pt.y; + } + } + return result; +} + +PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) +{ + double dx, dy, inverse_hypot; + if (pt1 == pt2) return PointD(0.0, 0.0); + dx = static_cast(pt2.x - pt1.x); + dy = static_cast(pt2.y - pt1.y); + inverse_hypot = 1.0 / hypot(dx, dy); + dx *= inverse_hypot; + dy *= inverse_hypot; + return PointD(dy, -dx); +} + +inline bool AlmostZero(double value, double epsilon = 0.001) +{ + return std::fabs(value) < epsilon; +} + +inline double Hypot(double x, double y) +{ + //see https://stackoverflow.com/a/32436148/359538 + return std::sqrt(x * x + y * y); +} + +inline PointD NormalizeVector(const PointD& vec) +{ + double h = Hypot(vec.x, vec.y); + if (AlmostZero(h)) return PointD(0,0); + double inverseHypot = 1 / h; + return PointD(vec.x * inverseHypot, vec.y * inverseHypot); +} + +inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2) +{ + return NormalizeVector(PointD(vec1.x + vec2.x, vec1.y + vec2.y)); +} + +inline bool IsClosedPath(EndType et) +{ + return et == EndType::Polygon || et == EndType::Joined; +} + +inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) +{ +#ifdef USINGZ + return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z); +#else + return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta); +#endif +} + +inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) +{ +#ifdef USINGZ + return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z); +#else + return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta); +#endif +} + +inline void NegatePath(PathD& path) +{ + for (PointD& pt : path) + { + pt.x = -pt.x; + pt.y = -pt.y; +#ifdef USINGZ + pt.z = pt.z; +#endif + } +} + + +//------------------------------------------------------------------------------ +// ClipperOffset::Group methods +//------------------------------------------------------------------------------ + +ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType _end_type): + paths_in(_paths), join_type(_join_type), end_type(_end_type) +{ + bool is_joined = + (end_type == EndType::Polygon) || + (end_type == EndType::Joined); + for (Path64& p: paths_in) + StripDuplicates(p, is_joined); + + if (end_type == EndType::Polygon) + { + lowest_path_idx = GetLowestClosedPathIdx(paths_in); + // the lowermost path must be an outer path, so if its orientation is negative, + // then flag the whole group is 'reversed' (will negate delta etc.) + // as this is much more efficient than reversing every path. + is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0; + } + else + { + lowest_path_idx = std::nullopt; + is_reversed = false; + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset methods +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_) +{ + Paths64 paths; + paths.push_back(path); + AddPaths(paths, jt_, et_); +} + +void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_) +{ + if (paths.size() == 0) return; + groups_.push_back(Group(paths, jt_, et_)); +} + +void ClipperOffset::BuildNormals(const Path64& path) +{ + norms.clear(); + norms.reserve(path.size()); + if (path.size() == 0) return; + Path64::const_iterator path_iter, path_stop_iter = --path.cend(); + for (path_iter = path.cbegin(); path_iter != path_stop_iter; ++path_iter) + norms.push_back(GetUnitNormal(*path_iter,*(path_iter +1))); + norms.push_back(GetUnitNormal(*path_stop_iter, *(path.cbegin()))); +} + +void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k) +{ + PointD pt1, pt2; + if (j == k) + { + double abs_delta = std::abs(group_delta_); +#ifdef USINGZ + pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y, path[j].z); + pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y, path[j].z); +#else + pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y); + pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y); +#endif + } + else + { +#ifdef USINGZ + pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y, path[j].z); + pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y, path[j].z); +#else + pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y); + pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y); +#endif + } + path_out.push_back(Point64(pt1)); + path_out.push_back(Point64(pt2)); +} + +void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k) +{ + PointD vec; + if (j == k) + vec = PointD(norms[j].y, -norms[j].x); + else + vec = GetAvgUnitVector( + PointD(-norms[k].y, norms[k].x), + PointD(norms[j].y, -norms[j].x)); + + double abs_delta = std::abs(group_delta_); + + // now offset the original vertex delta units along unit vector + PointD ptQ = PointD(path[j]); + ptQ = TranslatePoint(ptQ, abs_delta * vec.x, abs_delta * vec.y); + // get perpendicular vertices + PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x); + PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x); + // get 2 vertices along one edge offset + PointD pt3 = GetPerpendicD(path[k], norms[k], group_delta_); + if (j == k) + { + PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_); + PointD pt = ptQ; + GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt); + //get the second intersect point through reflecion + path_out.push_back(Point64(ReflectPoint(pt, ptQ))); + path_out.push_back(Point64(pt)); + } + else + { + PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_); + PointD pt = ptQ; + GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt); + path_out.push_back(Point64(pt)); + //get the second intersect point through reflecion + path_out.push_back(Point64(ReflectPoint(pt, ptQ))); + } +} + +void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a) +{ + double q = group_delta_ / (cos_a + 1); +#ifdef USINGZ + path_out.push_back(Point64( + path[j].x + (norms[k].x + norms[j].x) * q, + path[j].y + (norms[k].y + norms[j].y) * q, + path[j].z)); +#else + path_out.push_back(Point64( + path[j].x + (norms[k].x + norms[j].x) * q, + path[j].y + (norms[k].y + norms[j].y) * q)); +#endif +} + +void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle) +{ + if (deltaCallback64_) { + // when deltaCallback64_ is assigned, group_delta_ won't be constant, + // so we'll need to do the following calculations for *every* vertex. + double abs_delta = std::fabs(group_delta_); + double arcTol = (arc_tolerance_ > floating_point_tolerance ? + std::min(abs_delta, arc_tolerance_) : + std::log10(2 + abs_delta) * default_arc_tolerance); + double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); + step_sin_ = std::sin(2 * PI / steps_per_360); + step_cos_ = std::cos(2 * PI / steps_per_360); + if (group_delta_ < 0.0) step_sin_ = -step_sin_; + steps_per_rad_ = steps_per_360 / (2 * PI); + } + + Point64 pt = path[j]; + PointD offsetVec = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_); + + if (j == k) offsetVec.Negate(); +#ifdef USINGZ + path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z)); +#else + path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y)); +#endif + int steps = static_cast(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456 + for (int i = 1; i < steps; ++i) // ie 1 less than steps + { + offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y, + offsetVec.x * step_sin_ + offsetVec.y * step_cos_); +#ifdef USINGZ + path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z)); +#else + path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y)); +#endif + } + path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_)); +} + +void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size_t k) +{ + // Let A = change in angle where edges join + // A == 0: ie no change in angle (flat join) + // A == PI: edges 'spike' + // sin(A) < 0: right turning + // cos(A) < 0: change in angle is more than 90 degree + + if (path[j] == path[k]) return; + + double sin_a = CrossProduct(norms[j], norms[k]); + double cos_a = DotProduct(norms[j], norms[k]); + if (sin_a > 1.0) sin_a = 1.0; + else if (sin_a < -1.0) sin_a = -1.0; + + if (deltaCallback64_) { + group_delta_ = deltaCallback64_(path, norms, j, k); + if (group.is_reversed) group_delta_ = -group_delta_; + } + if (std::fabs(group_delta_) <= floating_point_tolerance) + { + path_out.push_back(path[j]); + return; + } + + if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593) + { + // is concave (so insert 3 points that will create a negative region) +#ifdef USINGZ + path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z)); +#else + path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); +#endif + + // this extra point is the only simple way to ensure that path reversals + // (ie over-shrunk paths) are fully cleaned out with the trailing union op. + // However it's probably safe to skip this whenever an angle is almost flat. + if (cos_a < 0.99) path_out.push_back(path[j]); // (#405) + +#ifdef USINGZ + path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z)); +#else + path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_)); +#endif + } + else if (cos_a > 0.999 && join_type_ != JoinType::Round) + { + // almost straight - less than 2.5 degree (#424, #482, #526 & #724) + DoMiter(path, j, k, cos_a); + } + else if (join_type_ == JoinType::Miter) + { + // miter unless the angle is sufficiently acute to exceed ML + if (cos_a > temp_lim_ - 1) DoMiter(path, j, k, cos_a); + else DoSquare(path, j, k); + } + else if (join_type_ == JoinType::Round) + DoRound(path, j, k, std::atan2(sin_a, cos_a)); + else if ( join_type_ == JoinType::Bevel) + DoBevel(path, j, k); + else + DoSquare(path, j, k); +} + +void ClipperOffset::OffsetPolygon(Group& group, const Path64& path) +{ + path_out.clear(); + for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j) + OffsetPoint(group, path, j, k); + solution->push_back(path_out); +} + +void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path) +{ + OffsetPolygon(group, path); + Path64 reverse_path(path); + std::reverse(reverse_path.begin(), reverse_path.end()); + + //rebuild normals + std::reverse(norms.begin(), norms.end()); + norms.push_back(norms[0]); + norms.erase(norms.begin()); + NegatePath(norms); + + OffsetPolygon(group, reverse_path); +} + +void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path) +{ + // do the line start cap + if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0); + + if (std::fabs(group_delta_) <= floating_point_tolerance) + path_out.push_back(path[0]); + else + { + switch (end_type_) + { + case EndType::Butt: + DoBevel(path, 0, 0); + break; + case EndType::Round: + DoRound(path, 0, 0, PI); + break; + default: + DoSquare(path, 0, 0); + break; + } + } + + size_t highI = path.size() - 1; + // offset the left side going forward + for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j) + OffsetPoint(group, path, j, k); + + // reverse normals + for (size_t i = highI; i > 0; --i) + norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y); + norms[0] = norms[highI]; + + // do the line end cap + if (deltaCallback64_) + group_delta_ = deltaCallback64_(path, norms, highI, highI); + + if (std::fabs(group_delta_) <= floating_point_tolerance) + path_out.push_back(path[highI]); + else + { + switch (end_type_) + { + case EndType::Butt: + DoBevel(path, highI, highI); + break; + case EndType::Round: + DoRound(path, highI, highI, PI); + break; + default: + DoSquare(path, highI, highI); + break; + } + } + + for (size_t j = highI -1, k = highI; j > 0; k = j, --j) + OffsetPoint(group, path, j, k); + solution->push_back(path_out); +} + +void ClipperOffset::DoGroupOffset(Group& group) +{ + if (group.end_type == EndType::Polygon) + { + // a straight path (2 points) can now also be 'polygon' offset + // where the ends will be treated as (180 deg.) joins + if (!group.lowest_path_idx.has_value()) delta_ = std::abs(delta_); + group_delta_ = (group.is_reversed) ? -delta_ : delta_; + } + else + group_delta_ = std::abs(delta_);// *0.5; + + double abs_delta = std::fabs(group_delta_); + join_type_ = group.join_type; + end_type_ = group.end_type; + + if (group.join_type == JoinType::Round || group.end_type == EndType::Round) + { + // calculate the number of steps required to approximate a circle + // (see http://www.angusj.com/clipper2/Docs/Trigonometry.htm) + // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision + // will be relative to the size of the offset (delta). Obviously very + //large offsets will almost always require much less precision. + double arcTol = (arc_tolerance_ > floating_point_tolerance ? + std::min(abs_delta, arc_tolerance_) : + std::log10(2 + abs_delta) * default_arc_tolerance); + + double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); + step_sin_ = std::sin(2 * PI / steps_per_360); + step_cos_ = std::cos(2 * PI / steps_per_360); + if (group_delta_ < 0.0) step_sin_ = -step_sin_; + steps_per_rad_ = steps_per_360 / (2 * PI); + } + + //double min_area = PI * Sqr(group_delta_); + Paths64::const_iterator path_in_it = group.paths_in.cbegin(); + for ( ; path_in_it != group.paths_in.cend(); ++path_in_it) + { + Path64::size_type pathLen = path_in_it->size(); + path_out.clear(); + + if (pathLen == 1) // single point + { + if (deltaCallback64_) + { + group_delta_ = deltaCallback64_(*path_in_it, norms, 0, 0); + if (group.is_reversed) group_delta_ = -group_delta_; + abs_delta = std::fabs(group_delta_); + } + + if (group_delta_ < 1) continue; + const Point64& pt = (*path_in_it)[0]; + //single vertex so build a circle or square ... + if (group.join_type == JoinType::Round) + { + double radius = abs_delta; + size_t steps = steps_per_rad_ > 0 ? static_cast(std::ceil(steps_per_rad_ * 2 * PI)) : 0; //#617 + path_out = Ellipse(pt, radius, radius, steps); +#ifdef USINGZ + for (auto& p : path_out) p.z = pt.z; +#endif + } + else + { + int d = (int)std::ceil(abs_delta); + Rect64 r = Rect64(pt.x - d, pt.y - d, pt.x + d, pt.y + d); + path_out = r.AsPath(); +#ifdef USINGZ + for (auto& p : path_out) p.z = pt.z; +#endif + } + + solution->push_back(path_out); + continue; + } // end of offsetting a single point + + if ((pathLen == 2) && (group.end_type == EndType::Joined)) + end_type_ = (group.join_type == JoinType::Round) ? + EndType::Round : + EndType::Square; + + BuildNormals(*path_in_it); + if (end_type_ == EndType::Polygon) OffsetPolygon(group, *path_in_it); + else if (end_type_ == EndType::Joined) OffsetOpenJoined(group, *path_in_it); + else OffsetOpenPath(group, *path_in_it); + } +} + +#ifdef USINGZ +void ClipperOffset::ZCB(const Point64& bot1, const Point64& top1, + const Point64& bot2, const Point64& top2, Point64& ip) +{ + if (bot1.z && ((bot1.z == bot2.z) || (bot1.z == top2.z))) ip.z = bot1.z; + else if (bot2.z && (bot2.z == top1.z)) ip.z = bot2.z; + else if (top1.z && (top1.z == top2.z)) ip.z = top1.z; + else if (zCallback64_) zCallback64_(bot1, top1, bot2, top2, ip); +} +#endif + +size_t ClipperOffset::CalcSolutionCapacity() +{ + size_t result = 0; + for (const Group& g : groups_) + result += (g.end_type == EndType::Joined) ? g.paths_in.size() * 2 : g.paths_in.size(); + return result; +} + +bool ClipperOffset::CheckReverseOrientation() +{ + // nb: this assumes there's consistency in orientation between groups + bool is_reversed_orientation = false; + for (const Group& g : groups_) + if (g.end_type == EndType::Polygon) + { + is_reversed_orientation = g.is_reversed; + break; + } + return is_reversed_orientation; +} + +void ClipperOffset::ExecuteInternal(double delta) +{ + error_code_ = 0; + if (groups_.size() == 0) return; + solution->reserve(CalcSolutionCapacity()); + + if (std::abs(delta) < 0.5) // ie: offset is insignificant + { + Paths64::size_type sol_size = 0; + for (const Group& group : groups_) sol_size += group.paths_in.size(); + solution->reserve(sol_size); + for (const Group& group : groups_) + copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(*solution)); + } + else + { + + temp_lim_ = (miter_limit_ <= 1) ? + 2.0 : + 2.0 / (miter_limit_ * miter_limit_); + + delta_ = delta; + std::vector::iterator git; + for (git = groups_.begin(); git != groups_.end(); ++git) + { + DoGroupOffset(*git); + if (!error_code_) continue; // all OK + solution->clear(); + } + } + + if (!solution->size()) return; + + bool paths_reversed = CheckReverseOrientation(); + //clean up self-intersections ... + Clipper64 c; + c.PreserveCollinear(false); + //the solution should retain the orientation of the input + c.ReverseSolution(reverse_solution_ != paths_reversed); +#ifdef USINGZ + auto fp = std::bind(&ClipperOffset::ZCB, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5); + c.SetZCallback(fp); +#endif + c.AddSubject(*solution); + if (solution_tree) + { + if (paths_reversed) + c.Execute(ClipType::Union, FillRule::Negative, *solution_tree); + else + c.Execute(ClipType::Union, FillRule::Positive, *solution_tree); + } + else + { + if (paths_reversed) + c.Execute(ClipType::Union, FillRule::Negative, *solution); + else + c.Execute(ClipType::Union, FillRule::Positive, *solution); + } +} + +void ClipperOffset::Execute(double delta, Paths64& paths) +{ + paths.clear(); + solution = &paths; + solution_tree = nullptr; + ExecuteInternal(delta); +} + + +void ClipperOffset::Execute(double delta, PolyTree64& polytree) +{ + polytree.Clear(); + solution_tree = &polytree; + solution = new Paths64(); + ExecuteInternal(delta); + delete solution; + solution = nullptr; +} + +void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths) +{ + deltaCallback64_ = delta_cb; + Execute(1.0, paths); +} + +} // namespace diff --git a/KP3Dii/ext/src/clipper2/clipper.rectclip.cpp b/KP3Dii/ext/src/clipper2/clipper.rectclip.cpp new file mode 100644 index 0000000..23809b5 --- /dev/null +++ b/KP3Dii/ext/src/clipper2/clipper.rectclip.cpp @@ -0,0 +1,1028 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 5 July 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* Purpose : FAST rectangular clipping * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#include +#include "clipper2/clipper.h" +#include "clipper2/clipper.rectclip.h" + +namespace Clipper2Lib { + + //------------------------------------------------------------------------------ + // Miscellaneous methods + //------------------------------------------------------------------------------ + + inline bool Path1ContainsPath2(const Path64& path1, const Path64& path2) + { + int io_count = 0; + // precondition: no (significant) overlap + for (const Point64& pt : path2) + { + PointInPolygonResult pip = PointInPolygon(pt, path1); + switch (pip) + { + case PointInPolygonResult::IsOutside: ++io_count; break; + case PointInPolygonResult::IsInside: --io_count; break; + default: continue; + } + if (std::abs(io_count) > 1) break; + } + return io_count <= 0; + } + + inline bool GetLocation(const Rect64& rec, + const Point64& pt, Location& loc) + { + if (pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom) + { + loc = Location::Left; + return false; + } + else if (pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom) + { + loc = Location::Right; + return false; + } + else if (pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right) + { + loc = Location::Top; + return false; + } + else if (pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right) + { + loc = Location::Bottom; + return false; + } + else if (pt.x < rec.left) loc = Location::Left; + else if (pt.x > rec.right) loc = Location::Right; + else if (pt.y < rec.top) loc = Location::Top; + else if (pt.y > rec.bottom) loc = Location::Bottom; + else loc = Location::Inside; + return true; + } + + inline bool IsHorizontal(const Point64& pt1, const Point64& pt2) + { + return pt1.y == pt2.y; + } + + bool GetSegmentIntersection(const Point64& p1, + const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip) + { + double res1 = CrossProduct(p1, p3, p4); + double res2 = CrossProduct(p2, p3, p4); + if (res1 == 0) + { + ip = p1; + if (res2 == 0) return false; // segments are collinear + else if (p1 == p3 || p1 == p4) return true; + //else if (p2 == p3 || p2 == p4) { ip = p2; return true; } + else if (IsHorizontal(p3, p4)) return ((p1.x > p3.x) == (p1.x < p4.x)); + else return ((p1.y > p3.y) == (p1.y < p4.y)); + } + else if (res2 == 0) + { + ip = p2; + if (p2 == p3 || p2 == p4) return true; + else if (IsHorizontal(p3, p4)) return ((p2.x > p3.x) == (p2.x < p4.x)); + else return ((p2.y > p3.y) == (p2.y < p4.y)); + } + if ((res1 > 0) == (res2 > 0)) return false; + + double res3 = CrossProduct(p3, p1, p2); + double res4 = CrossProduct(p4, p1, p2); + if (res3 == 0) + { + ip = p3; + if (p3 == p1 || p3 == p2) return true; + else if (IsHorizontal(p1, p2)) return ((p3.x > p1.x) == (p3.x < p2.x)); + else return ((p3.y > p1.y) == (p3.y < p2.y)); + } + else if (res4 == 0) + { + ip = p4; + if (p4 == p1 || p4 == p2) return true; + else if (IsHorizontal(p1, p2)) return ((p4.x > p1.x) == (p4.x < p2.x)); + else return ((p4.y > p1.y) == (p4.y < p2.y)); + } + if ((res3 > 0) == (res4 > 0)) return false; + + // segments must intersect to get here + return GetSegmentIntersectPt(p1, p2, p3, p4, ip); + } + + inline bool GetIntersection(const Path64& rectPath, + const Point64& p, const Point64& p2, Location& loc, Point64& ip) + { + // gets the intersection closest to 'p' + // when Result = false, loc will remain unchanged + switch (loc) + { + case Location::Left: + if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true; + else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) + { + loc = Location::Top; + return true; + } + else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip)) + { + loc = Location::Bottom; + return true; + } + else return false; + + case Location::Top: + if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) return true; + else if ((p.x < rectPath[0].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) + { + loc = Location::Left; + return true; + } + else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip)) + { + loc = Location::Right; + return true; + } + else return false; + + case Location::Right: + if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip)) return true; + else if ((p.y < rectPath[1].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) + { + loc = Location::Top; + return true; + } + else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip)) + { + loc = Location::Bottom; + return true; + } + else return false; + + case Location::Bottom: + if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip)) return true; + else if ((p.x < rectPath[3].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) + { + loc = Location::Left; + return true; + } + else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip)) + { + loc = Location::Right; + return true; + } + else return false; + + default: // loc == rInside + if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) + { + loc = Location::Left; + return true; + } + else if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) + { + loc = Location::Top; + return true; + } + else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip)) + { + loc = Location::Right; + return true; + } + else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip)) + { + loc = Location::Bottom; + return true; + } + else return false; + } + } + + inline Location GetAdjacentLocation(Location loc, bool isClockwise) + { + int delta = (isClockwise) ? 1 : 3; + return static_cast((static_cast(loc) + delta) % 4); + } + + inline bool HeadingClockwise(Location prev, Location curr) + { + return (static_cast(prev) + 1) % 4 == static_cast(curr); + } + + inline bool AreOpposites(Location prev, Location curr) + { + return abs(static_cast(prev) - static_cast(curr)) == 2; + } + + inline bool IsClockwise(Location prev, Location curr, + const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp) + { + if (AreOpposites(prev, curr)) + return CrossProduct(prev_pt, rect_mp, curr_pt) < 0; + else + return HeadingClockwise(prev, curr); + } + + inline OutPt2* UnlinkOp(OutPt2* op) + { + if (op->next == op) return nullptr; + op->prev->next = op->next; + op->next->prev = op->prev; + return op->next; + } + + inline OutPt2* UnlinkOpBack(OutPt2* op) + { + if (op->next == op) return nullptr; + op->prev->next = op->next; + op->next->prev = op->prev; + return op->prev; + } + + inline uint32_t GetEdgesForPt(const Point64& pt, const Rect64& rec) + { + uint32_t result = 0; + if (pt.x == rec.left) result = 1; + else if (pt.x == rec.right) result = 4; + if (pt.y == rec.top) result += 2; + else if (pt.y == rec.bottom) result += 8; + return result; + } + + inline bool IsHeadingClockwise(const Point64& pt1, const Point64& pt2, int edgeIdx) + { + switch (edgeIdx) + { + case 0: return pt2.y < pt1.y; + case 1: return pt2.x > pt1.x; + case 2: return pt2.y > pt1.y; + default: return pt2.x < pt1.x; + } + } + + inline bool HasHorzOverlap(const Point64& left1, const Point64& right1, + const Point64& left2, const Point64& right2) + { + return (left1.x < right2.x) && (right1.x > left2.x); + } + + inline bool HasVertOverlap(const Point64& top1, const Point64& bottom1, + const Point64& top2, const Point64& bottom2) + { + return (top1.y < bottom2.y) && (bottom1.y > top2.y); + } + + inline void AddToEdge(OutPt2List& edge, OutPt2* op) + { + if (op->edge) return; + op->edge = &edge; + edge.push_back(op); + } + + inline void UncoupleEdge(OutPt2* op) + { + if (!op->edge) return; + for (size_t i = 0; i < op->edge->size(); ++i) + { + OutPt2* op2 = (*op->edge)[i]; + if (op2 == op) + { + (*op->edge)[i] = nullptr; + break; + } + } + op->edge = nullptr; + } + + inline void SetNewOwner(OutPt2* op, size_t new_idx) + { + op->owner_idx = new_idx; + OutPt2* op2 = op->next; + while (op2 != op) + { + op2->owner_idx = new_idx; + op2 = op2->next; + } + } + + //---------------------------------------------------------------------------- + // RectClip64 + //---------------------------------------------------------------------------- + + OutPt2* RectClip64::Add(Point64 pt, bool start_new) + { + // this method is only called by InternalExecute. + // Later splitting & rejoining won't create additional op's, + // though they will change the (non-storage) results_ count. + size_t curr_idx = results_.size(); + OutPt2* result; + if (curr_idx == 0 || start_new) + { + result = &op_container_.emplace_back(OutPt2()); + result->pt = pt; + result->next = result; + result->prev = result; + results_.push_back(result); + } + else + { + --curr_idx; + OutPt2* prevOp = results_[curr_idx]; + if (prevOp->pt == pt) return prevOp; + result = &op_container_.emplace_back(OutPt2()); + result->owner_idx = curr_idx; + result->pt = pt; + result->next = prevOp->next; + prevOp->next->prev = result; + prevOp->next = result; + result->prev = prevOp; + results_[curr_idx] = result; + } + return result; + } + + void RectClip64::AddCorner(Location prev, Location curr) + { + if (HeadingClockwise(prev, curr)) + Add(rect_as_path_[static_cast(prev)]); + else + Add(rect_as_path_[static_cast(curr)]); + } + + void RectClip64::AddCorner(Location& loc, bool isClockwise) + { + if (isClockwise) + { + Add(rect_as_path_[static_cast(loc)]); + loc = GetAdjacentLocation(loc, true); + } + else + { + loc = GetAdjacentLocation(loc, false); + Add(rect_as_path_[static_cast(loc)]); + } + } + + void RectClip64::GetNextLocation(const Path64& path, + Location& loc, size_t& i, size_t highI) + { + switch (loc) + { + case Location::Left: + while (i <= highI && path[i].x <= rect_.left) ++i; + if (i > highI) break; + else if (path[i].x >= rect_.right) loc = Location::Right; + else if (path[i].y <= rect_.top) loc = Location::Top; + else if (path[i].y >= rect_.bottom) loc = Location::Bottom; + else loc = Location::Inside; + break; + + case Location::Top: + while (i <= highI && path[i].y <= rect_.top) ++i; + if (i > highI) break; + else if (path[i].y >= rect_.bottom) loc = Location::Bottom; + else if (path[i].x <= rect_.left) loc = Location::Left; + else if (path[i].x >= rect_.right) loc = Location::Right; + else loc = Location::Inside; + break; + + case Location::Right: + while (i <= highI && path[i].x >= rect_.right) ++i; + if (i > highI) break; + else if (path[i].x <= rect_.left) loc = Location::Left; + else if (path[i].y <= rect_.top) loc = Location::Top; + else if (path[i].y >= rect_.bottom) loc = Location::Bottom; + else loc = Location::Inside; + break; + + case Location::Bottom: + while (i <= highI && path[i].y >= rect_.bottom) ++i; + if (i > highI) break; + else if (path[i].y <= rect_.top) loc = Location::Top; + else if (path[i].x <= rect_.left) loc = Location::Left; + else if (path[i].x >= rect_.right) loc = Location::Right; + else loc = Location::Inside; + break; + + case Location::Inside: + while (i <= highI) + { + if (path[i].x < rect_.left) loc = Location::Left; + else if (path[i].x > rect_.right) loc = Location::Right; + else if (path[i].y > rect_.bottom) loc = Location::Bottom; + else if (path[i].y < rect_.top) loc = Location::Top; + else { Add(path[i]); ++i; continue; } + break; //inner loop + } + break; + } //switch + } + + bool StartLocsAreClockwise(const std::vector& startlocs) + { + int result = 0; + for (size_t i = 1; i < startlocs.size(); ++i) + { + int d = static_cast(startlocs[i]) - static_cast(startlocs[i - 1]); + switch (d) + { + case -1: result -= 1; break; + case 1: result += 1; break; + case -3: result += 1; break; + case 3: result -= 1; break; + } + } + return result > 0; + } + + void RectClip64::ExecuteInternal(const Path64& path) + { + if (path.size() < 1) + return; + + size_t highI = path.size() - 1; + Location prev = Location::Inside, loc; + Location crossing_loc = Location::Inside; + Location first_cross_ = Location::Inside; + if (!GetLocation(rect_, path[highI], loc)) + { + size_t i = highI; + while (i > 0 && !GetLocation(rect_, path[i - 1], prev)) + --i; + if (i == 0) + { + // all of path must be inside fRect + for (const auto& pt : path) Add(pt); + return; + } + if (prev == Location::Inside) loc = Location::Inside; + } + Location starting_loc = loc; + + /////////////////////////////////////////////////// + size_t i = 0; + while (i <= highI) + { + prev = loc; + Location crossing_prev = crossing_loc; + + GetNextLocation(path, loc, i, highI); + + if (i > highI) break; + Point64 ip, ip2; + Point64 prev_pt = (i) ? + path[static_cast(i - 1)] : + path[highI]; + + crossing_loc = loc; + if (!GetIntersection(rect_as_path_, + path[i], prev_pt, crossing_loc, ip)) + { + // ie remaining outside + if (crossing_prev == Location::Inside) + { + bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_); + do { + start_locs_.push_back(prev); + prev = GetAdjacentLocation(prev, isClockw); + } while (prev != loc); + crossing_loc = crossing_prev; // still not crossed + } + else if (prev != Location::Inside && prev != loc) + { + bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_); + do { + AddCorner(prev, isClockw); + } while (prev != loc); + } + ++i; + continue; + } + + //////////////////////////////////////////////////// + // we must be crossing the rect boundary to get here + //////////////////////////////////////////////////// + + if (loc == Location::Inside) // path must be entering rect + { + if (first_cross_ == Location::Inside) + { + first_cross_ = crossing_loc; + start_locs_.push_back(prev); + } + else if (prev != crossing_loc) + { + bool isClockw = IsClockwise(prev, crossing_loc, prev_pt, path[i], rect_mp_); + do { + AddCorner(prev, isClockw); + } while (prev != crossing_loc); + } + } + else if (prev != Location::Inside) + { + // passing right through rect. 'ip' here will be the second + // intersect pt but we'll also need the first intersect pt (ip2) + loc = prev; + GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2); + if (crossing_prev != Location::Inside && crossing_prev != loc) //579 + AddCorner(crossing_prev, loc); + + if (first_cross_ == Location::Inside) + { + first_cross_ = loc; + start_locs_.push_back(prev); + } + + loc = crossing_loc; + Add(ip2); + if (ip == ip2) + { + // it's very likely that path[i] is on rect + GetLocation(rect_, path[i], loc); + AddCorner(crossing_loc, loc); + crossing_loc = loc; + continue; + } + } + else // path must be exiting rect + { + loc = crossing_loc; + if (first_cross_ == Location::Inside) + first_cross_ = crossing_loc; + } + + Add(ip); + + } //while i <= highI + /////////////////////////////////////////////////// + + if (first_cross_ == Location::Inside) + { + // path never intersects + if (starting_loc != Location::Inside) + { + // path is outside rect + // but being outside, it still may not contain rect + if (path_bounds_.Contains(rect_) && + Path1ContainsPath2(path, rect_as_path_)) + { + // yep, the path does fully contain rect + // so add rect to the solution + bool is_clockwise_path = StartLocsAreClockwise(start_locs_); + for (size_t j = 0; j < 4; ++j) + { + size_t k = is_clockwise_path ? j : 3 - j; // reverses result path + Add(rect_as_path_[k]); + // we may well need to do some splitting later, so + AddToEdge(edges_[k * 2], results_[0]); + } + } + } + } + else if (loc != Location::Inside && + (loc != first_cross_ || start_locs_.size() > 2)) + { + if (start_locs_.size() > 0) + { + prev = loc; + for (auto loc2 : start_locs_) + { + if (prev == loc2) continue; + AddCorner(prev, HeadingClockwise(prev, loc2)); + prev = loc2; + } + loc = prev; + } + if (loc != first_cross_) + AddCorner(loc, HeadingClockwise(loc, first_cross_)); + } + } + + void RectClip64::CheckEdges() + { + for (size_t i = 0; i < results_.size(); ++i) + { + OutPt2* op = results_[i]; + if (!op) continue; + OutPt2* op2 = op; + do + { + if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt)) + { + if (op2 == op) + { + op2 = UnlinkOpBack(op2); + if (!op2) break; + op = op2->prev; + } + else + { + op2 = UnlinkOpBack(op2); + if (!op2) break; + } + } + else + op2 = op2->next; + } while (op2 != op); + + if (!op2) + { + results_[i] = nullptr; + continue; + } + results_[i] = op; // safety first + + uint32_t edgeSet1 = GetEdgesForPt(op->prev->pt, rect_); + op2 = op; + do + { + uint32_t edgeSet2 = GetEdgesForPt(op2->pt, rect_); + if (edgeSet2 && !op2->edge) + { + uint32_t combinedSet = (edgeSet1 & edgeSet2); + for (int j = 0; j < 4; ++j) + { + if (combinedSet & (1 << j)) + { + if (IsHeadingClockwise(op2->prev->pt, op2->pt, j)) + AddToEdge(edges_[j * 2], op2); + else + AddToEdge(edges_[j * 2 + 1], op2); + } + } + } + edgeSet1 = edgeSet2; + op2 = op2->next; + } while (op2 != op); + } + } + + void RectClip64::TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw) + { + if (ccw.empty()) return; + bool isHorz = ((idx == 1) || (idx == 3)); + bool cwIsTowardLarger = ((idx == 1) || (idx == 2)); + size_t i = 0, j = 0; + OutPt2* p1, * p2, * p1a, * p2a, * op, * op2; + + while (i < cw.size()) + { + p1 = cw[i]; + if (!p1 || p1->next == p1->prev) + { + cw[i++] = nullptr; + j = 0; + continue; + } + + size_t jLim = ccw.size(); + while (j < jLim && + (!ccw[j] || ccw[j]->next == ccw[j]->prev)) ++j; + + if (j == jLim) + { + ++i; + j = 0; + continue; + } + + if (cwIsTowardLarger) + { + // p1 >>>> p1a; + // p2 <<<< p2a; + p1 = cw[i]->prev; + p1a = cw[i]; + p2 = ccw[j]; + p2a = ccw[j]->prev; + } + else + { + // p1 <<<< p1a; + // p2 >>>> p2a; + p1 = cw[i]; + p1a = cw[i]->prev; + p2 = ccw[j]->prev; + p2a = ccw[j]; + } + + if ((isHorz && !HasHorzOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)) || + (!isHorz && !HasVertOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt))) + { + ++j; + continue; + } + + // to get here we're either splitting or rejoining + bool isRejoining = cw[i]->owner_idx != ccw[j]->owner_idx; + + if (isRejoining) + { + results_[p2->owner_idx] = nullptr; + SetNewOwner(p2, p1->owner_idx); + } + + // do the split or re-join + if (cwIsTowardLarger) + { + // p1 >> | >> p1a; + // p2 << | << p2a; + p1->next = p2; + p2->prev = p1; + p1a->prev = p2a; + p2a->next = p1a; + } + else + { + // p1 << | << p1a; + // p2 >> | >> p2a; + p1->prev = p2; + p2->next = p1; + p1a->next = p2a; + p2a->prev = p1a; + } + + if (!isRejoining) + { + size_t new_idx = results_.size(); + results_.push_back(p1a); + SetNewOwner(p1a, new_idx); + } + + if (cwIsTowardLarger) + { + op = p2; + op2 = p1a; + } + else + { + op = p1; + op2 = p2a; + } + results_[op->owner_idx] = op; + results_[op2->owner_idx] = op2; + + // and now lots of work to get ready for the next loop + + bool opIsLarger, op2IsLarger; + if (isHorz) // X + { + opIsLarger = op->pt.x > op->prev->pt.x; + op2IsLarger = op2->pt.x > op2->prev->pt.x; + } + else // Y + { + opIsLarger = op->pt.y > op->prev->pt.y; + op2IsLarger = op2->pt.y > op2->prev->pt.y; + } + + if ((op->next == op->prev) || + (op->pt == op->prev->pt)) + { + if (op2IsLarger == cwIsTowardLarger) + { + cw[i] = op2; + ccw[j++] = nullptr; + } + else + { + ccw[j] = op2; + cw[i++] = nullptr; + } + } + else if ((op2->next == op2->prev) || + (op2->pt == op2->prev->pt)) + { + if (opIsLarger == cwIsTowardLarger) + { + cw[i] = op; + ccw[j++] = nullptr; + } + else + { + ccw[j] = op; + cw[i++] = nullptr; + } + } + else if (opIsLarger == op2IsLarger) + { + if (opIsLarger == cwIsTowardLarger) + { + cw[i] = op; + UncoupleEdge(op2); + AddToEdge(cw, op2); + ccw[j++] = nullptr; + } + else + { + cw[i++] = nullptr; + ccw[j] = op2; + UncoupleEdge(op); + AddToEdge(ccw, op); + j = 0; + } + } + else + { + if (opIsLarger == cwIsTowardLarger) + cw[i] = op; + else + ccw[j] = op; + if (op2IsLarger == cwIsTowardLarger) + cw[i] = op2; + else + ccw[j] = op2; + } + } + } + + Path64 RectClip64::GetPath(OutPt2*& op) + { + if (!op || op->next == op->prev) return Path64(); + + OutPt2* op2 = op->next; + while (op2 && op2 != op) + { + if (IsCollinear(op2->prev->pt, + op2->pt, op2->next->pt)) + { + op = op2->prev; + op2 = UnlinkOp(op2); + } + else + op2 = op2->next; + } + op = op2; // needed for op cleanup + if (!op2) return Path64(); + + Path64 result; + result.push_back(op->pt); + op2 = op->next; + while (op2 != op) + { + result.push_back(op2->pt); + op2 = op2->next; + } + return result; + } + + Paths64 RectClip64::Execute(const Paths64& paths) + { + Paths64 result; + if (rect_.IsEmpty()) return result; + + for (const Path64& path : paths) + { + if (path.size() < 3) continue; + path_bounds_ = GetBounds(path); + if (!rect_.Intersects(path_bounds_)) + continue; // the path must be completely outside rect_ + else if (rect_.Contains(path_bounds_)) + { + // the path must be completely inside rect_ + result.push_back(path); + continue; + } + + ExecuteInternal(path); + CheckEdges(); + for (size_t i = 0; i < 4; ++i) + TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]); + + for (OutPt2*& op : results_) + { + Path64 tmp = GetPath(op); + if (!tmp.empty()) + result.emplace_back(tmp); + } + + //clean up after every loop + op_container_ = std::deque(); + results_.clear(); + for (OutPt2List &edge : edges_) edge.clear(); + start_locs_.clear(); + } + return result; + } + + //------------------------------------------------------------------------------ + // RectClipLines64 + //------------------------------------------------------------------------------ + + Paths64 RectClipLines64::Execute(const Paths64& paths) + { + Paths64 result; + if (rect_.IsEmpty()) return result; + + for (const auto& path : paths) + { + Rect64 pathrec = GetBounds(path); + if (!rect_.Intersects(pathrec)) continue; + + ExecuteInternal(path); + + for (OutPt2*& op : results_) + { + Path64 tmp = GetPath(op); + if (!tmp.empty()) + result.emplace_back(tmp); + } + results_.clear(); + + op_container_ = std::deque(); + start_locs_.clear(); + } + return result; + } + + void RectClipLines64::ExecuteInternal(const Path64& path) + { + if (rect_.IsEmpty() || path.size() < 2) return; + + results_.clear(); + op_container_ = std::deque(); + start_locs_.clear(); + + size_t i = 1, highI = path.size() - 1; + + Location prev = Location::Inside, loc; + Location crossing_loc; + if (!GetLocation(rect_, path[0], loc)) + { + while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i; + if (i > highI) + { + // all of path must be inside fRect + for (const auto& pt : path) Add(pt); + return; + } + if (prev == Location::Inside) loc = Location::Inside; + i = 1; + } + if (loc == Location::Inside) Add(path[0]); + + /////////////////////////////////////////////////// + while (i <= highI) + { + prev = loc; + GetNextLocation(path, loc, i, highI); + if (i > highI) break; + Point64 ip, ip2; + Point64 prev_pt = path[static_cast(i - 1)]; + + crossing_loc = loc; + if (!GetIntersection(rect_as_path_, + path[i], prev_pt, crossing_loc, ip)) + { + // ie remaining outside + ++i; + continue; + } + + //////////////////////////////////////////////////// + // we must be crossing the rect boundary to get here + //////////////////////////////////////////////////// + + if (loc == Location::Inside) // path must be entering rect + { + Add(ip, true); + } + else if (prev != Location::Inside) + { + // passing right through rect. 'ip' here will be the second + // intersect pt but we'll also need the first intersect pt (ip2) + crossing_loc = prev; + GetIntersection(rect_as_path_, + prev_pt, path[i], crossing_loc, ip2); + Add(ip2, true); + Add(ip); + } + else // path must be exiting rect + { + Add(ip); + } + } //while i <= highI + /////////////////////////////////////////////////// + } + + Path64 RectClipLines64::GetPath(OutPt2*& op) + { + Path64 result; + if (!op || op == op->next) return result; + op = op->next; // starting at path beginning + result.push_back(op->pt); + OutPt2 *op2 = op->next; + while (op2 != op) + { + result.push_back(op2->pt); + op2 = op2->next; + } + return result; + } + +} // namespace diff --git a/KP3Dii/src/KP3D_Geometry.cpp b/KP3Dii/src/KP3D_Geometry.cpp new file mode 100644 index 0000000..2f5c02c --- /dev/null +++ b/KP3Dii/src/KP3D_Geometry.cpp @@ -0,0 +1,63 @@ +#include "KP3D_Geometry.h" + +#include + +#include "KP3D_Log.h" + +namespace { +const float EPSILON = 0.0001f; +} + +namespace kp3d { + +bool PointInLine(XYf point, XYf line_start, XYf line_end) +{ + float bounds = Distance(line_start, line_end) + EPSILON; + float test = (line_start.y - point.y) * line_end.x + (point.x - line_start.x) * line_end.y + + (line_start.x * point.y - point.x * line_start.y); + + bool in_bounds = Distance(point, line_start) < bounds && Distance(point, line_end) < bounds; + + return kp3d::FloatCmp(test, 0.0f, EPSILON) && in_bounds; +} + +float Distance(XYf a, XYf b) +{ + return sqrtf((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y)); +} + +std::vector> SplitComplexPolygon(std::vector polygon) +{ + using namespace Clipper2Lib; + + ClipperD c; + PathsD solution; + + PathD complex_polygon; + for (const Vec2& v: polygon) + complex_polygon.push_back({v.x, v.y}); + c.AddSubject({complex_polygon}); + PathsD clipper_output; + c.Execute(ClipType::Union, FillRule::NonZero, clipper_output); + + std::vector> output; + + for (const auto& clipped_polygon: clipper_output) + { + std::vector polygon_excerpt; + for (const auto& point: clipped_polygon) + polygon_excerpt.push_back({(float)point.x, (float)point.y}); + output.push_back(polygon_excerpt); + } + + if (output.size() > 1) + { + KP3D_LOG_INFO("Split: "); + for (int i = 0; i < output.size(); i++) + KP3D_LOG_INFO("{} size: {}", i, output[i].size()); + } + + return output; +} + +} // namespace kp3d diff --git a/KP3Dii/src/KP3D_Geometry.h b/KP3Dii/src/KP3D_Geometry.h new file mode 100644 index 0000000..dab73b8 --- /dev/null +++ b/KP3Dii/src/KP3D_Geometry.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#include "KP3D_Common.h" +#include "KP3D_Math.h" +#include "KP3D_Map.h" + +namespace kp3d { + +template +void InsertLine(std::vector& lines, size_t position, const T& new_line) +{ + if constexpr (std::is_same_v) + { + if (position > 0 && position < lines.size()) + lines[position - 1].end = new_line.start; + if (position < lines.size()) + lines[position].start = new_line.end; + } + lines.insert(lines.begin() + position, new_line); +} + +template +bool PointInPolygon(std::vector polygon, XYf p) +{ + bool c = false; + + for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i++) + { + float ix = 0.0f, iy = 0.0f, jx = 0.0f, jy = 0.0f; + + if constexpr (std::is_same_v) + { + ix = polygon[i].start.x, iy = polygon[i].start.y; + jx = polygon[j].start.x, jy = polygon[j].start.y; + } + else + { + ix = polygon[i].x, iy = polygon[i].y; + jx = polygon[j].x, jy = polygon[j].y; + } + + if (((iy > p.y) != (jy > p.y)) && (p.x < (jx - ix) * (p.y - iy) / (jy - iy) + ix)) + c = !c; + } + + return c; +} + +bool PointInLine(XYf point, XYf line_start, XYf line_end); + +float Distance(XYf a, XYf b); + +std::vector> SplitComplexPolygon(std::vector polygon); + +} // namespace kp3d diff --git a/KP3Dii/src/KP3D_Map.cpp b/KP3Dii/src/KP3D_Map.cpp index 4859f6a..8ed1f45 100644 --- a/KP3Dii/src/KP3D_Map.cpp +++ b/KP3Dii/src/KP3D_Map.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include #include @@ -11,69 +9,11 @@ #include "KP3D_Renderer3D.h" #include "KP3D_Shader.h" #include "KP3D_Noise.h" - - - -namespace std { -template<> -struct hash { - std::size_t operator()(const kp3d::Vec3& point) const { - return std::hash()(point.x) ^ std::hash()(point.y) ^ std::hash()(point.z); - } -}; -} - +#include "KP3D_Geometry.h" namespace { using namespace kp3d; -bool PointInPolygon(std::vector polygon, kp3d::XYf p) -{ - bool c = false; - - for (int i = 0, j = polygon.size() - 1; i < polygon.size(); j = i++) - { - float ix = polygon[i].start.x, iy = polygon[i].start.y; - float jx = polygon[j].start.x, jy = polygon[j].start.y; - - if (((iy > p.y) != (jy > p.y)) && (p.x < (jx - ix) * (p.y - iy) / (jy - iy) + ix)) - c = !c; - } - - return c; -} - -float Distance(XYf p1, XYf p2) -{ - return sqrtf((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)); -} - -bool PointInLine(float x1, float y1, float x2, float y2, float x0, float y0) -{ - bool in_bounds = false; - - float bounds = Distance({x2, y2}, {x0, y0}) + 0.0001f; - - if (Distance({x1, y1}, {x2, y2}) < bounds && Distance({x1, y1}, {x0, y0}) < bounds) - in_bounds = true; - - return kp3d::FloatCmp((y2 - y1) * x0 + (x1 - x2) * y0 + (x2 * y1 - x1 * y2), 0.0f, 0.0001f) && in_bounds; -} - -void InsertLine(std::vector& lines, size_t position, const Wall& newLine) { - if (position > 0 && position < lines.size()) { - // Update the end position of the line before the insertion point - lines[position - 1].end = newLine.start; - } - - if (position < lines.size()) { - // Update the start position of the line after the insertion point - lines[position].start = newLine.end; - } - - // Insert the new line into the vector - lines.insert(lines.begin() + position, newLine); -} } // namespace @@ -116,46 +56,6 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert) std::vector> subsector_polygons; // unused for now std::vector secs_inside; -#if 0 - for (Sector& s : sectors) - { - if (s.id == sector.id) - continue; - - std::vector ss; - - for (Wall& l : s.walls) - { - if (PointInPolygon(sector.walls, l.start)) - { - ss.push_back({l.start.x, l.start.y}); - } - } - - if (ss.size() > 0 && s.children.empty())//== s.walls.size()) - { - secs_inside.push_back(&s); - for (Wall& l : s.walls) - { - if (s.inverted) - { - if (FloatCmp(s.floor.base_height, sector.floor.base_height)) - s.floor.texture = nullptr; - //if (s.ceiling.base_height <= sector.ceiling.base_height + 0.000001f) - // s.ceiling.texture = nullptr; - continue; - } - else - { - - l.textures[TEX_FRONT] = nullptr; - } - } - - subsector_polygons.push_back(ss); - } - } -#endif for (Sector* sp : sector.children) { std::vector ss; @@ -271,15 +171,15 @@ void Map::BuildQuad(Sector& sector, Flat& flat_top, Flat& flat_bottom, const Tex Vec3 p3 = Vec3(pos_b.x, pos_a.y, pos_b.z); Vec3 p4 = Vec3(pos_b.x, pos_b.y, pos_b.z); - std::vector points; + std::vector points; points.emplace_back(0.0f); for (const Vertex3D& v: flat_bottom.triangulated_data) { - if (!PointInLine(v.position.x, v.position.z, pos_a.x, pos_a.z, pos_b.x, pos_b.z)) + if (!PointInLine({v.position.x, v.position.z}, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z})) continue; // Project 3D point into 2D - Vec3 mpos = {Distance({v.position.x, v.position.z}, {p1.x, p1.z}), v.position.y, 0}; + Vec2 mpos = {Distance({v.position.x, v.position.z}, {p1.x, p1.z}), v.position.y}; // Overwrite the start point if we've landed on it if (FloatCmp(v.position.x, p1.x, 0.0001f) && FloatCmp(v.position.z, p1.z, 0.0001f)) @@ -287,62 +187,91 @@ void Map::BuildQuad(Sector& sector, Flat& flat_top, Flat& flat_bottom, const Tex points.push_back(mpos); } - std::vector top_points; - //top_points.emplace_back(Distance({ p4.x, p4.z }, { p1.x, p1.z }), p4.y, 0); // This may be re-enabled, BUT + std::vector top_points; + top_points.emplace_back(Distance({ p4.x, p4.z }, { p1.x, p1.z }), p4.y); // This may be re-enabled, BUT for (const Vertex3D& v: flat_top.triangulated_data) { - if (!PointInLine(v.position.x, v.position.z, pos_a.x, pos_a.z, pos_b.x, pos_b.z)) + if (!PointInLine({v.position.x, v.position.z}, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z})) continue; + // Project 3D point into 2D - Vec3 mpos = {Distance({v.position.x, v.position.z}, {p1.x, p1.z}), v.position.y, 0}; + Vec2 mpos = {Distance({v.position.x, v.position.z}, {p1.x, p1.z}), v.position.y}; + + if (FloatCmp(v.position.x, p4.x, 0.0001f) && FloatCmp(v.position.z, p4.z, 0.0001f)) + top_points[0] = mpos; + top_points.push_back(mpos); } - std::sort(points.begin() + 1, points.end(), [&](Vec3 a, Vec3 b) { return a.x < b.x; }); - std::sort(top_points.begin() , top_points.end(), [&](Vec3 a, Vec3 b) { return a.x > b.x; }); // ^^^^ If you do, add +1 to begin() + std::sort(points.begin() + 1, points.end(), [&](Vec2 a, Vec2 b) { return a.x < b.x; }); + std::sort(top_points.begin() + 1 , top_points.end(), [&](Vec2 a, Vec2 b) { return a.x > b.x; }); // ^^^^ If you do, add +1 to begin() points.insert(points.end(), top_points.begin(), top_points.end()); - float angle = atan2({ p4.z - p1.z }, { p4.x - p1.x }); + float angle = atan2({p4.z - p1.z}, {p4.x - p1.x}); points.erase(unique(points.begin(), points.end()), points.end()); - std::vector> holes; // unused for now - std::vector wall_polygon; - for (Vec3& p: points) - wall_polygon.emplace_back(p.x, p.y); + // So now we have our polygon. + // Before we go through the standard process of using clip2tri to triangulate it, we're going to do some preprocessing. + // There's a chance we may have ended up with a complex polygon, which poly2tri isn't able to handle. So our solution is to + // break them up. + std::vector> polygons = SplitComplexPolygon(points); - c2t::clip2tri clipper; - std::vector clipper_out; - clipper.triangulate(holes, clipper_out, wall_polygon); - for (int i = 0; i < clipper_out.size(); i += 3) + //auto& polygon = polygons.front(); + for (const std::vector& polygon: polygons) { - c2t::Point a = clipper_out.at(i + 0); - c2t::Point b = clipper_out.at(i + 1); - c2t::Point c = clipper_out.at(i + 2); + std::vector> holes; // unused for now + std::vector wall_polygon; + for (Vec2 p : polygon) + wall_polygon.emplace_back(p.x, p.y); - if (flip) + c2t::clip2tri clipper; + std::vector clipper_out; + clipper.triangulate(holes, clipper_out, wall_polygon); + p2t::CDT cdt(); + + for (int i = 0; i < clipper_out.size(); i += 3) { - std::swap(b, c); + c2t::Point a = clipper_out.at(i + 0); + c2t::Point b = clipper_out.at(i + 1); + c2t::Point c = clipper_out.at(i + 2); + + // Safety clamp + if (a.x < 0) a.x = 0; + if (b.x < 0) b.x = 0; + if (c.x < 0) c.x = 0; + if (a.x > top_points[0].x) + a.x = top_points[0].x; + if (b.x > top_points[0].x) + b.x = top_points[0].x; + if (c.x > top_points[0].x) + c.x = top_points[0].x; + + //if (flip) + // std::swap(b, c); + + float au = a.x * 0.5f, av = a.y * 0.5f; + float bu = b.x * 0.5f, bv = b.y * 0.5f; + float cu = c.x * 0.5f, cv = c.y * 0.5f; + + // Build mesh data for wall + Vec3 fva = Vec3(a.x, a.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fva.x += p1.x; fva.z += p1.z; + Vec3 fvb = Vec3(b.x, b.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fvb.x += p1.x; fvb.z += p1.z; + Vec3 fvc = Vec3(c.x, c.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fvc.x += p1.x; fvc.z += p1.z; + + bool iba = PointInLine({ fva.x, fva.z }, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z }); + bool ibb = PointInLine({ fvb.x, fvb.z }, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z }); + bool ibc = PointInLine({ fvc.x, fvc.z }, { pos_a.x, pos_a.z }, { pos_b.x, pos_b.z }); + + // Fix up the UVs so they keep the right scale + float tw = texture ? texture_scale / texture->GetWidth() : 0.0f; + float th = texture ? texture_scale / texture->GetHeight() : 0.0f; + Vertex3D vtxa = Vertex3D(fva, Vec2(au * tw, av * th)); + Vertex3D vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * th)); + Vertex3D vtxc = Vertex3D(fvc, Vec2(cu * tw, cv * th)); + if (texture) + m_mesh.AddBatch(texture, { vtxa, vtxb, vtxc }, flip); } - - - float au = a.x * 0.5f, av = a.y * 0.5f; - float bu = b.x * 0.5f, bv = b.y * 0.5f; - float cu = c.x * 0.5f, cv = c.y * 0.5f; - - // Build mesh data for wall - Vec3 fva = Vec3(a.x, a.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fva.x += p1.x; fva.z += p1.z; - Vec3 fvb = Vec3(b.x, b.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fvb.x += p1.x; fvb.z += p1.z; - Vec3 fvc = Vec3(c.x, c.y, 0.0f).Rotated({ 0, 1, 0 }, -ToDegrees(angle)); fvc.x += p1.x; fvc.z += p1.z; - - // Fix up the UVs so they keep the right scale - float tw = texture ? texture_scale / texture->GetWidth() : 0.0f; - float th = texture ? texture_scale / texture->GetHeight() : 0.0f; - Vertex3D vtxa = Vertex3D(fva, Vec2(au * tw, av * th)); - Vertex3D vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * th)); - Vertex3D vtxc = Vertex3D(fvc, Vec2(cu * tw, cv * th)); - if (texture) - m_mesh.AddBatch(texture, {vtxa, vtxb, vtxc}, false); } } @@ -354,14 +283,18 @@ void Map::BuildWall(Sector& sector, Wall& wall) if (wall.flags & Wall::OPENING) { - bool flip = sector.floor.base_height > wall.portal->floor.base_height; - bool flip2 = sector.ceiling.base_height < wall.portal->ceiling.base_height; - //if (flip) - // flip2 ^= 1; - //if (og_flip2) - // flip ^= 1; - BuildQuad(sector, sector.floor, wall.portal->floor, wall.textures[TEX_LOWER], a, b, flip, false, false, wall.uv_offset[TEX_LOWER]); - BuildQuad(sector, sector.ceiling, wall.portal->ceiling, wall.textures[TEX_UPPER], a, b, flip2, false, false, wall.uv_offset[TEX_UPPER]); + Flat& ft = sector.floor; + Flat& fb = wall.portal->floor; + a.y = ft.base_height; + b.y = fb.base_height; + + BuildQuad(sector, ft, fb, wall.textures[TEX_LOWER], a, b, false, false, false, wall.uv_offset[TEX_LOWER]); + + Flat& ct = sector.ceiling; + Flat& cb = wall.portal->ceiling; + a.y = ct.base_height; + b.y = cb.base_height; + BuildQuad(sector, ct, cb, wall.textures[TEX_UPPER], a, b, true, false, false, wall.uv_offset[TEX_UPPER]); } } @@ -384,12 +317,12 @@ void Map::JoinSectors(Sector& sector) for (Wall& l : s.walls) { - if (PointInLine(l.start.x, l.start.y, pos_a.x, pos_a.z, pos_b.x, pos_b.z) && - PointInLine(l.end.x, l.end.y, pos_a.x, pos_a.z, pos_b.x, pos_b.z) && - !PosCmp({ l.start.x, 0.0f, l.start.y }, { pos_a.x, 0.0f, pos_a.z }) && - !PosCmp({ l.start.x, 0.0f, l.start.y }, { pos_b.x, 0.0f, pos_b.z }) && - !PosCmp({ l.end.x, 0.0f, l.end.y }, { pos_b.x, 0.0f, pos_b.z }) && - !PosCmp({ l.end.x, 0.0f, l.end.y }, { pos_a.x, 0.0f, pos_a.z })) + if (PointInLine(l.start, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}) && + PointInLine(l.end, {pos_a.x, pos_a.z}, {pos_b.x, pos_b.z}) && + !PosCmp({l.start.x, 0.0f, l.start.y}, {pos_a.x, 0.0f, pos_a.z}) && + !PosCmp({l.start.x, 0.0f, l.start.y}, {pos_b.x, 0.0f, pos_b.z}) && + !PosCmp({l.end.x, 0.0f, l.end.y}, {pos_b.x, 0.0f, pos_b.z}) && + !PosCmp({l.end.x, 0.0f, l.end.y}, {pos_a.x, 0.0f, pos_a.z})) { //KP3D_LOG_INFO("{} IS TOUCHING: {}", sector.id, s.id); XYf old_end = ld.end; @@ -413,6 +346,10 @@ void Map::JoinSectors(Sector& sector) InsertLine(sector.walls, i + 2, w2); l.textures[TEX_FRONT] = nullptr; + // Mark the other one as an opening + // This really shouldn't be necessary I think, but whatever + l.flags = Wall::OPENING; + l.portal = §or; } } } @@ -567,7 +504,6 @@ void Map::Init() const float grid_size = 1.0f / (texture_scale - 1.0f), tolerance = (1.0f / texture_scale); for (RenderBatch3D& b: m_mesh.GetBatchesRef()) { - std::unordered_set unique_points; for (Vertex3D& v: b.vertex_data) { v.position.x = std::round(v.position.x / grid_size) * grid_size; @@ -592,7 +528,6 @@ void Map::Init() #endif for (RenderBatch3D& b : m_mesh.GetBatchesRef()) { - std::unordered_set unique_points; for (size_t i = 0; i < b.vertex_data.size(); i += 3) { std::swap(b.vertex_data[i + 1], b.vertex_data[i + 2]);