From b14cfc92e4f30d7ebe9fbe35a01ca3fb23915381 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 21 Aug 2024 23:02:56 +0700 Subject: [PATCH] init uwp commit --- PCUT/Clipper.Core/Clipper.Core.csproj | 7 + PCUT/Clipper.Core/clipper.cs | 4913 +++++++++++++++++ PCUT/DeepNestLib.Core/D3.cs | 93 + PCUT/DeepNestLib.Core/DeepNestLib.Core.csproj | 15 + PCUT/DeepNestLib.Core/Extensions.cs | 42 + PCUT/DeepNestLib.Core/GeneticAlgorithm.cs | 202 + PCUT/DeepNestLib.Core/GeometryUtil.cs | 1447 +++++ PCUT/DeepNestLib.Core/IStringify.cs | 11 + PCUT/DeepNestLib.Core/NFP.cs | 213 + PCUT/DeepNestLib.Core/NfpKey.cs | 24 + PCUT/DeepNestLib.Core/PlacementItem.cs | 20 + PCUT/DeepNestLib.Core/PlacementTypeEnum.cs | 11 + PCUT/DeepNestLib.Core/PolygonBounds.cs | 17 + PCUT/DeepNestLib.Core/PopulationItem.cs | 19 + PCUT/DeepNestLib.Core/RectangleSheet.cs | 15 + PCUT/DeepNestLib.Core/Sheet.cs | 9 + PCUT/DeepNestLib.Core/SheetPlacement.cs | 18 + PCUT/DeepNestLib.Core/SheetPlacementItem.cs | 15 + PCUT/DeepNestLib.Core/Simplify.cs | 143 + PCUT/DeepNestLib.Core/SvgNestConfig.cs | 25 + PCUT/DeepNestLib.Core/SvgPoint.cs | 33 + PCUT/DeepNestLib.Core/_Clipper.cs | 60 + PCUT/Http.Core/Constants/HttpConstants.cs | 45 + PCUT/Http.Core/Contexts/UserContext.cs | 75 + .../Extensions/CategoryExtensions.cs | 37 + PCUT/Http.Core/Extensions/HttpExtensions.cs | 179 + .../Handlers/AuthenticationHandler.cs | 68 + PCUT/Http.Core/Handlers/DeviceAuthHandler.cs | 19 + PCUT/Http.Core/Http.Core.csproj | 16 + PCUT/Http.Core/HttpClientFactory.cs | 132 + PCUT/Http.Core/Models/LoginRequest.cs | 14 + PCUT/Http.Core/Models/RefreshTokenRequest.cs | 11 + PCUT/Http.Core/PathBuilder.cs | 94 + PCUT/MinkowskiCpp/Minkowski.cpp | 337 ++ PCUT/MinkowskiCpp/Minkowski.h | 17 + PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj | 451 ++ .../MinkowskiCpp/MinkowskiCpp.vcxproj.filters | 171 + .../boost_1_84_0/boost/config.hpp | 67 + .../boost/config/abi/borland_prefix.hpp | 27 + .../boost/config/abi/borland_suffix.hpp | 12 + .../boost/config/abi/msvc_prefix.hpp | 22 + .../boost/config/abi/msvc_suffix.hpp | 8 + .../boost_1_84_0/boost/config/abi_prefix.hpp | 25 + .../boost_1_84_0/boost/config/abi_suffix.hpp | 25 + .../boost/config/assert_cxx03.hpp | 211 + .../boost/config/assert_cxx11.hpp | 212 + .../boost/config/assert_cxx14.hpp | 47 + .../boost/config/assert_cxx17.hpp | 62 + .../boost/config/assert_cxx20.hpp | 59 + .../boost/config/assert_cxx23.hpp | 41 + .../boost/config/assert_cxx98.hpp | 23 + .../boost_1_84_0/boost/config/auto_link.hpp | 525 ++ .../boost/config/compiler/borland.hpp | 339 ++ .../boost/config/compiler/clang.hpp | 366 ++ .../boost/config/compiler/clang_version.hpp | 89 + .../boost/config/compiler/codegear.hpp | 385 ++ .../boost/config/compiler/comeau.hpp | 59 + .../boost/config/compiler/common_edg.hpp | 183 + .../boost/config/compiler/compaq_cxx.hpp | 19 + .../boost/config/compiler/cray.hpp | 446 ++ .../boost/config/compiler/diab.hpp | 26 + .../boost/config/compiler/digitalmars.hpp | 143 + .../boost/config/compiler/gcc.hpp | 383 ++ .../boost/config/compiler/gcc_xml.hpp | 114 + .../boost/config/compiler/greenhills.hpp | 28 + .../boost/config/compiler/hp_acc.hpp | 149 + .../boost/config/compiler/intel.hpp | 577 ++ .../boost/config/compiler/kai.hpp | 33 + .../boost/config/compiler/metrowerks.hpp | 198 + .../boost/config/compiler/mpw.hpp | 140 + .../boost/config/compiler/nvcc.hpp | 61 + .../boost/config/compiler/pathscale.hpp | 138 + .../boost/config/compiler/pgi.hpp | 23 + .../boost/config/compiler/sgi_mipspro.hpp | 29 + .../boost/config/compiler/sunpro_cc.hpp | 222 + .../boost/config/compiler/vacpp.hpp | 186 + .../boost/config/compiler/visualc.hpp | 395 ++ .../boost/config/compiler/xlcpp.hpp | 299 + .../boost/config/compiler/xlcpp_zos.hpp | 173 + .../boost/config/detail/cxx_composite.hpp | 217 + .../boost/config/detail/posix_features.hpp | 95 + .../config/detail/select_compiler_config.hpp | 157 + .../config/detail/select_platform_config.hpp | 147 + .../config/detail/select_stdlib_config.hpp | 121 + .../boost/config/detail/suffix.hpp | 1334 +++++ .../boost/config/header_deprecated.hpp | 26 + .../boost/config/helper_macros.hpp | 37 + .../boost/config/no_tr1/cmath.hpp | 28 + .../boost/config/no_tr1/complex.hpp | 28 + .../boost/config/no_tr1/functional.hpp | 28 + .../boost/config/no_tr1/memory.hpp | 28 + .../boost/config/no_tr1/utility.hpp | 28 + .../boost/config/platform/aix.hpp | 33 + .../boost/config/platform/amigaos.hpp | 15 + .../boost/config/platform/beos.hpp | 26 + .../boost/config/platform/bsd.hpp | 83 + .../boost/config/platform/cloudabi.hpp | 18 + .../boost/config/platform/cray.hpp | 18 + .../boost/config/platform/cygwin.hpp | 71 + .../boost/config/platform/haiku.hpp | 31 + .../boost/config/platform/hpux.hpp | 87 + .../boost/config/platform/irix.hpp | 31 + .../boost/config/platform/linux.hpp | 106 + .../boost/config/platform/macos.hpp | 87 + .../boost/config/platform/qnxnto.hpp | 31 + .../boost/config/platform/solaris.hpp | 31 + .../boost/config/platform/symbian.hpp | 97 + .../boost/config/platform/vms.hpp | 25 + .../boost/config/platform/vxworks.hpp | 422 ++ .../boost/config/platform/wasm.hpp | 23 + .../boost/config/platform/win32.hpp | 90 + .../boost/config/platform/zos.hpp | 32 + .../boost/config/pragma_message.hpp | 31 + .../boost/config/requires_threads.hpp | 92 + .../boost/config/stdlib/dinkumware.hpp | 324 ++ .../boost/config/stdlib/libcomo.hpp | 93 + .../boost/config/stdlib/libcpp.hpp | 180 + .../boost/config/stdlib/libstdcpp3.hpp | 482 ++ .../boost/config/stdlib/modena.hpp | 79 + .../boost_1_84_0/boost/config/stdlib/msl.hpp | 98 + .../boost/config/stdlib/roguewave.hpp | 208 + .../boost_1_84_0/boost/config/stdlib/sgi.hpp | 168 + .../boost/config/stdlib/stlport.hpp | 258 + .../boost/config/stdlib/vacpp.hpp | 74 + .../boost/config/stdlib/xlcpp_zos.hpp | 61 + .../boost_1_84_0/boost/config/user.hpp | 133 + .../boost/config/warning_disable.hpp | 47 + .../boost_1_84_0/boost/config/workaround.hpp | 305 + .../boost/polygon/detail/boolean_op.hpp | 442 ++ .../boost/polygon/detail/boolean_op_45.hpp | 1398 +++++ .../detail/iterator_compact_to_points.hpp | 74 + .../detail/iterator_geometry_to_set.hpp | 314 ++ .../detail/iterator_points_to_compact.hpp | 60 + .../boost/polygon/detail/max_cover.hpp | 281 + .../boost/polygon/detail/minkowski.hpp | 131 + .../polygon/detail/polygon_45_formation.hpp | 2238 ++++++++ .../polygon/detail/polygon_45_set_view.hpp | 380 ++ .../boost/polygon/detail/polygon_45_touch.hpp | 238 + .../polygon/detail/polygon_90_set_view.hpp | 490 ++ .../boost/polygon/detail/polygon_90_touch.hpp | 418 ++ .../detail/polygon_arbitrary_formation.hpp | 2907 ++++++++++ .../polygon/detail/polygon_formation.hpp | 2287 ++++++++ .../boost/polygon/detail/polygon_set_view.hpp | 222 + .../boost/polygon/detail/polygon_simplify.hpp | 116 + .../polygon/detail/polygon_sort_adaptor.hpp | 67 + .../boost/polygon/detail/property_merge.hpp | 588 ++ .../polygon/detail/property_merge_45.hpp | 160 + .../polygon/detail/rectangle_formation.hpp | 266 + .../boost/polygon/detail/scan_arbitrary.hpp | 2861 ++++++++++ .../boost/polygon/detail/voronoi_ctypes.hpp | 643 +++ .../polygon/detail/voronoi_predicates.hpp | 1533 +++++ .../polygon/detail/voronoi_robust_fpt.hpp | 501 ++ .../polygon/detail/voronoi_structures.hpp | 450 ++ .../boost/polygon/gmp_override.hpp | 128 + .../boost_1_84_0/boost/polygon/gtl.hpp | 35 + .../boost/polygon/interval_concept.hpp | 934 ++++ .../boost/polygon/interval_data.hpp | 118 + .../boost/polygon/interval_traits.hpp | 47 + .../boost_1_84_0/boost/polygon/isotropy.hpp | 525 ++ .../boost/polygon/point_concept.hpp | 469 ++ .../boost_1_84_0/boost/polygon/point_data.hpp | 138 + .../boost/polygon/point_traits.hpp | 48 + .../boost_1_84_0/boost/polygon/polygon.hpp | 92 + .../boost/polygon/polygon_45_data.hpp | 72 + .../boost/polygon/polygon_45_set_concept.hpp | 441 ++ .../boost/polygon/polygon_45_set_data.hpp | 1880 +++++++ .../boost/polygon/polygon_45_set_traits.hpp | 149 + .../polygon/polygon_45_with_holes_data.hpp | 107 + .../boost/polygon/polygon_90_data.hpp | 79 + .../boost/polygon/polygon_90_set_concept.hpp | 550 ++ .../boost/polygon/polygon_90_set_data.hpp | 989 ++++ .../boost/polygon/polygon_90_set_traits.hpp | 366 ++ .../polygon/polygon_90_with_holes_data.hpp | 115 + .../boost/polygon/polygon_data.hpp | 69 + .../boost/polygon/polygon_set_concept.hpp | 581 ++ .../boost/polygon/polygon_set_data.hpp | 1006 ++++ .../boost/polygon/polygon_set_traits.hpp | 133 + .../boost/polygon/polygon_traits.hpp | 1861 +++++++ .../boost/polygon/polygon_with_holes_data.hpp | 107 + .../boost/polygon/rectangle_concept.hpp | 1082 ++++ .../boost/polygon/rectangle_data.hpp | 63 + .../boost/polygon/rectangle_traits.hpp | 40 + .../boost/polygon/segment_concept.hpp | 696 +++ .../boost/polygon/segment_data.hpp | 121 + .../boost/polygon/segment_traits.hpp | 50 + .../boost/polygon/segment_utils.hpp | 164 + .../boost_1_84_0/boost/polygon/transform.hpp | 476 ++ .../boost_1_84_0/boost/polygon/voronoi.hpp | 157 + .../boost/polygon/voronoi_builder.hpp | 521 ++ .../boost/polygon/voronoi_diagram.hpp | 620 +++ .../boost/polygon/voronoi_geometry_type.hpp | 48 + PCUT/MinkowskiCpp/pch.cpp | 1 + PCUT/MinkowskiCpp/pch.h | 4 + PCUT/PCUT.API/ApiClient.cs | 80 + PCUT/PCUT.API/Managers/CategoriesBase.cs | 14 + PCUT/PCUT.API/Managers/CategoryManager.cs | 47 + PCUT/PCUT.API/Managers/ICategoriesBase.cs | 12 + PCUT/PCUT.API/Managers/IManager.cs | 17 + PCUT/PCUT.API/Managers/IUserManager.cs | 12 + PCUT/PCUT.API/Managers/ManagerBase.cs | 14 + PCUT/PCUT.API/Managers/UserManager.cs | 72 + PCUT/PCUT.API/PCUT.API.csproj | 15 + .../ApiResponse/MessageResponse.cs | 13 + .../ApiResponse/PaginationResponse.cs | 19 + .../ApiResponse/UserCredentialDto.cs | 19 + PCUT/PCUT.Entities/Category.cs | 19 + PCUT/PCUT.Entities/CategoryMetadata.cs | 22 + PCUT/PCUT.Entities/CdrFile.cs | 49 + PCUT/PCUT.Entities/FileData.cs | 30 + PCUT/PCUT.Entities/PCUT.Entities.csproj | 11 + PCUT/PCUT.Entities/Pagination.cs | 22 + PCUT/PCUT.Entities/PasswordChange.cs | 13 + PCUT/PCUT.Entities/UpsertCategory.cs | 13 + PCUT/PCUT.Entities/UpsertFile.cs | 46 + PCUT/PCUT.Entities/UpsertMetadata.cs | 20 + PCUT/PCUT.Entities/UpsertUser.cs | 31 + PCUT/PCUT.Entities/UserProfile.cs | 59 + PCUT/PCUT.sln | 169 + PCUT/PCUT/App.xaml | 33 + PCUT/PCUT/App.xaml.cs | 133 + PCUT/PCUT/Assets/Backgound.jpg | Bin 0 -> 138034 bytes PCUT/PCUT/Assets/LargeTile.scale-100.png | Bin 0 -> 7039 bytes PCUT/PCUT/Assets/LargeTile.scale-125.png | Bin 0 -> 8767 bytes PCUT/PCUT/Assets/LargeTile.scale-150.png | Bin 0 -> 10855 bytes PCUT/PCUT/Assets/LargeTile.scale-200.png | Bin 0 -> 14901 bytes PCUT/PCUT/Assets/LargeTile.scale-400.png | Bin 0 -> 32758 bytes PCUT/PCUT/Assets/LockScreenLogo.scale-200.png | Bin 0 -> 1430 bytes PCUT/PCUT/Assets/Logo-Pcut.jpg | Bin 0 -> 84165 bytes PCUT/PCUT/Assets/SmallTile.scale-100.png | Bin 0 -> 2189 bytes PCUT/PCUT/Assets/SmallTile.scale-125.png | Bin 0 -> 2780 bytes PCUT/PCUT/Assets/SmallTile.scale-150.png | Bin 0 -> 3267 bytes PCUT/PCUT/Assets/SmallTile.scale-200.png | Bin 0 -> 4394 bytes PCUT/PCUT/Assets/SmallTile.scale-400.png | Bin 0 -> 8955 bytes PCUT/PCUT/Assets/SplashScreen.scale-100.png | Bin 0 -> 7484 bytes PCUT/PCUT/Assets/SplashScreen.scale-125.png | Bin 0 -> 9442 bytes PCUT/PCUT/Assets/SplashScreen.scale-150.png | Bin 0 -> 11770 bytes PCUT/PCUT/Assets/SplashScreen.scale-200.png | Bin 0 -> 16378 bytes PCUT/PCUT/Assets/SplashScreen.scale-400.png | Bin 0 -> 38158 bytes .../Assets/Square150x150Logo.scale-100.png | Bin 0 -> 3397 bytes .../Assets/Square150x150Logo.scale-125.png | Bin 0 -> 4090 bytes .../Assets/Square150x150Logo.scale-150.png | Bin 0 -> 5081 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 0 -> 6880 bytes .../Assets/Square150x150Logo.scale-400.png | Bin 0 -> 14559 bytes ...go.altform-lightunplated_targetsize-16.png | Bin 0 -> 523 bytes ...go.altform-lightunplated_targetsize-24.png | Bin 0 -> 1360 bytes ...o.altform-lightunplated_targetsize-256.png | Bin 0 -> 15559 bytes ...go.altform-lightunplated_targetsize-32.png | Bin 0 -> 1893 bytes ...go.altform-lightunplated_targetsize-48.png | Bin 0 -> 2872 bytes ...x44Logo.altform-unplated_targetsize-16.png | Bin 0 -> 523 bytes ...44Logo.altform-unplated_targetsize-256.png | Bin 0 -> 15559 bytes ...x44Logo.altform-unplated_targetsize-32.png | Bin 0 -> 1893 bytes ...x44Logo.altform-unplated_targetsize-48.png | Bin 0 -> 2872 bytes .../PCUT/Assets/Square44x44Logo.scale-100.png | Bin 0 -> 1847 bytes .../PCUT/Assets/Square44x44Logo.scale-125.png | Bin 0 -> 2469 bytes .../PCUT/Assets/Square44x44Logo.scale-150.png | Bin 0 -> 2977 bytes .../PCUT/Assets/Square44x44Logo.scale-200.png | Bin 0 -> 3918 bytes .../PCUT/Assets/Square44x44Logo.scale-400.png | Bin 0 -> 7779 bytes .../Assets/Square44x44Logo.targetsize-16.png | Bin 0 -> 592 bytes .../Assets/Square44x44Logo.targetsize-24.png | Bin 0 -> 976 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 1360 bytes .../Assets/Square44x44Logo.targetsize-256.png | Bin 0 -> 11555 bytes .../Assets/Square44x44Logo.targetsize-32.png | Bin 0 -> 1354 bytes .../Assets/Square44x44Logo.targetsize-48.png | Bin 0 -> 2150 bytes PCUT/PCUT/Assets/StoreLogo.backup.png | Bin 0 -> 1451 bytes PCUT/PCUT/Assets/StoreLogo.scale-100.png | Bin 0 -> 2971 bytes PCUT/PCUT/Assets/StoreLogo.scale-125.png | Bin 0 -> 3708 bytes PCUT/PCUT/Assets/StoreLogo.scale-150.png | Bin 0 -> 4395 bytes PCUT/PCUT/Assets/StoreLogo.scale-200.png | Bin 0 -> 5877 bytes PCUT/PCUT/Assets/StoreLogo.scale-400.png | Bin 0 -> 12042 bytes .../PCUT/Assets/Wide310x150Logo.scale-100.png | Bin 0 -> 3569 bytes .../PCUT/Assets/Wide310x150Logo.scale-125.png | Bin 0 -> 4461 bytes .../PCUT/Assets/Wide310x150Logo.scale-150.png | Bin 0 -> 5417 bytes .../PCUT/Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 7484 bytes .../PCUT/Assets/Wide310x150Logo.scale-400.png | Bin 0 -> 16378 bytes PCUT/PCUT/Assets/add.png | Bin 0 -> 4206 bytes PCUT/PCUT/Assets/back.jpg | Bin 0 -> 58836 bytes PCUT/PCUT/Assets/home.png | Bin 0 -> 1185 bytes PCUT/PCUT/Assets/img_customer.png | Bin 0 -> 1310 bytes PCUT/PCUT/Assets/img_order.png | Bin 0 -> 891 bytes PCUT/PCUT/Assets/img_product.png | Bin 0 -> 673 bytes PCUT/PCUT/Assets/img_setting.png | Bin 0 -> 2244 bytes PCUT/PCUT/Assets/logo.jpg | Bin 0 -> 5196 bytes PCUT/PCUT/Assets/shutdown_mo.png | Bin 0 -> 945 bytes PCUT/PCUT/Assets/unnamed.jpg | Bin 0 -> 79469 bytes .../BundleArtifacts/PCUT.appinstaller.xml | 2 + PCUT/PCUT/BundleArtifacts/x64.txt | 6 + PCUT/PCUT/BundleArtifacts/x86.txt | 2 + PCUT/PCUT/Converters/BooleanConverter.cs | 34 + PCUT/PCUT/Converters/BooleanInverter.cs | 22 + .../Converters/BooleanToStringConverter.cs | 26 + .../BooleanToVisibilityConverter.cs | 27 + PCUT/PCUT/Converters/OpacityConverter.cs | 22 + .../Converters/UserRoleToBooleanConverter.cs | 22 + PCUT/PCUT/Converters/UserStatusConverter.cs | 22 + PCUT/PCUT/Converters/VisibilityConverter.cs | 36 + .../Converters/VisibilityToMarginConverter.cs | 24 + PCUT/PCUT/DeepNestApi/Background.cs | 1802 ++++++ PCUT/PCUT/DeepNestApi/DraftElement.cs | 19 + PCUT/PCUT/DeepNestApi/LineElement.cs | 26 + PCUT/PCUT/DeepNestApi/LocalContour.cs | 37 + PCUT/PCUT/DeepNestApi/NestingContext.cs | 357 ++ PCUT/PCUT/DeepNestApi/PolylineElement.cs | 46 + PCUT/PCUT/DeepNestApi/RawDetail.cs | 91 + PCUT/PCUT/DeepNestApi/SvgNest.cs | 1069 ++++ PCUT/PCUT/DeepNestApi/SvgParser.cs | 558 ++ PCUT/PCUT/Extensions/BindExtensions.cs | 35 + .../Converters/CircleElementConverter.cs | 35 + .../Converters/EllipseElementConverter.cs | 36 + .../Extensions/Converters/PathConverter.cs | 94 + .../Converters/PathElementConverter.cs | 23 + .../Converters/PolygonElementConverter.cs | 52 + .../Converters/RectElementConverter.cs | 76 + PCUT/PCUT/Extensions/EnumerableExtensions.cs | 46 + PCUT/PCUT/Extensions/HpglExtensions.cs | 173 + PCUT/PCUT/Extensions/ImageSourceExtensions.cs | 75 + .../PCUT/Extensions/ManipulationExtensions.cs | 15 + PCUT/PCUT/Extensions/NumericExtensions.cs | 49 + PCUT/PCUT/Extensions/PointExtensions.cs | 50 + PCUT/PCUT/Extensions/SettingExtensions.cs | 22 + PCUT/PCUT/Extensions/SvgExtensions.cs | 295 + .../PCUT/Extensions/SvgTransformExtensions.cs | 88 + .../Extensions/Transforms/MatrixTransform.cs | 35 + .../Extensions/Transforms/RotateTransform.cs | 40 + .../Extensions/Transforms/ScaleTransform.cs | 27 + .../Extensions/Transforms/SkewTransform.cs | 50 + .../Extensions/Transforms/SvgTransform.cs | 39 + .../Transforms/TranslateTransform.cs | 27 + PCUT/PCUT/Extensions/XmlExtensions.cs | 253 + PCUT/PCUT/Factories/FileFactory.cs | 112 + .../Converters/BooleanNegationConverter.cs | 22 + .../DateTimeToFormattedStringConverter.cs | 34 + PCUT/PCUT/Helpers/RelayCommand.cs | 67 + PCUT/PCUT/Models/Categories/CategoryModel.cs | 61 + .../Categories/CategoryTemplateSelector.cs | 26 + PCUT/PCUT/Models/NotificationBase.cs | 50 + PCUT/PCUT/Models/SelectableCollection.cs | 132 + PCUT/PCUT/Models/Users/UsersModel.cs | 9 + PCUT/PCUT/PCUT.csproj | 479 ++ PCUT/PCUT/Package.appxmanifest | 54 + PCUT/PCUT/Pages/AdminCenterPage.xaml | 44 + PCUT/PCUT/Pages/AdminCenterPage.xaml.cs | 92 + .../CategoriesViewPage.xaml | 236 + .../CategoriesViewPage.xaml.cs | 251 + .../CategoryUpsertDialog.xaml | 46 + .../CategoryUpsertDialog.xaml.cs | 37 + .../MetadataUpsertDialog.xaml | 30 + .../MetadataUpsertDialog.xaml.cs | 39 + PCUT/PCUT/Pages/DataCenterPage.xaml | 251 + PCUT/PCUT/Pages/DataCenterPage.xaml.cs | 160 + .../Pages/DesignCenter/CutFilesDialog.xaml | 69 + .../Pages/DesignCenter/CutFilesDialog.xaml.cs | 207 + .../Pages/DesignCenter/DesignCenterPage.xaml | 234 + .../DesignCenter/DesignCenterPage.xaml.cs | 196 + PCUT/PCUT/Pages/DesignCenter/EditPage.xaml | 112 + PCUT/PCUT/Pages/DesignCenter/EditPage.xaml.cs | 263 + .../Pages/DesignCenter/PortSettingDialog.xaml | 82 + .../DesignCenter/PortSettingDialog.xaml.cs | 38 + PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml | 68 + .../Pages/DesignCenter/SvgNestPage.xaml.cs | 425 ++ .../DesignCenter/SvgNestSettingDialog.xaml | 53 + .../DesignCenter/SvgNestSettingDialog.xaml.cs | 41 + .../Pages/FileManagement/FileAddDialog.xaml | 228 + .../FileManagement/FileAddDialog.xaml.cs | 78 + .../Pages/FileManagement/FileEditDialog.xaml | 228 + .../FileManagement/FileEditDialog.xaml.cs | 95 + .../Pages/FileManagement/FileManagerPage.xaml | 227 + .../FileManagement/FileManagerPage.xaml.cs | 218 + PCUT/PCUT/Pages/LoginPage.xaml | 95 + PCUT/PCUT/Pages/LoginPage.xaml.cs | 69 + PCUT/PCUT/Pages/MainMenuPage.xaml | 188 + PCUT/PCUT/Pages/MainMenuPage.xaml.cs | 156 + PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml | 14 + .../Pages/UserGuide/UserGuidePage.xaml.cs | 30 + .../UserManagement/ChangePasswordDialog.xaml | 27 + .../ChangePasswordDialog.xaml.cs | 73 + .../Pages/UserManagement/UpsertUserPage.xaml | 374 ++ .../UserManagement/UpsertUserPage.xaml.cs | 66 + .../Pages/UserManagement/UserListPage.xaml | 186 + .../Pages/UserManagement/UserListPage.xaml.cs | 176 + PCUT/PCUT/Properties/AssemblyInfo.cs | 29 + PCUT/PCUT/Properties/Default.rd.xml | 31 + PCUT/PCUT/ViewModels/CategoriesViewModel.cs | 127 + .../ViewModels/ChangePasswordViewModel.cs | 157 + PCUT/PCUT/ViewModels/ComponentImageModel.cs | 100 + PCUT/PCUT/ViewModels/CutFilesViewModel.cs | 64 + PCUT/PCUT/ViewModels/DesignCenterViewModel.cs | 209 + PCUT/PCUT/ViewModels/FileAddViewModel.cs | 187 + PCUT/PCUT/ViewModels/FileEditViewModel.cs | 249 + PCUT/PCUT/ViewModels/FileListViewModel.cs | 328 ++ PCUT/PCUT/ViewModels/ImageViewModel.cs | 57 + PCUT/PCUT/ViewModels/LoadingModel.cs | 25 + PCUT/PCUT/ViewModels/LogOutViewModel.cs | 12 + PCUT/PCUT/ViewModels/LoginViewModel.cs | 215 + PCUT/PCUT/ViewModels/MainMenuPageViewModel.cs | 103 + PCUT/PCUT/ViewModels/MetadataViewModel.cs | 226 + PCUT/PCUT/ViewModels/NestingProgressModel.cs | 31 + PCUT/PCUT/ViewModels/PaginationViewModel.cs | 139 + PCUT/PCUT/ViewModels/PortSettingViewModel.cs | 128 + PCUT/PCUT/ViewModels/SvgData.cs | 318 ++ PCUT/PCUT/ViewModels/SvgNestSettings.cs | 203 + .../ViewModels/UpsertCategoryViewModel.cs | 126 + .../ViewModels/UpsertMetadataViewModel.cs | 130 + PCUT/PCUT/ViewModels/UpsertUserViewModel.cs | 345 ++ PCUT/PCUT/ViewModels/UpsertViewModel.cs | 66 + PCUT/PCUT/ViewModels/UserViewModel.cs | 94 + README.md | 5 + 406 files changed, 73139 insertions(+) create mode 100644 PCUT/Clipper.Core/Clipper.Core.csproj create mode 100644 PCUT/Clipper.Core/clipper.cs create mode 100644 PCUT/DeepNestLib.Core/D3.cs create mode 100644 PCUT/DeepNestLib.Core/DeepNestLib.Core.csproj create mode 100644 PCUT/DeepNestLib.Core/Extensions.cs create mode 100644 PCUT/DeepNestLib.Core/GeneticAlgorithm.cs create mode 100644 PCUT/DeepNestLib.Core/GeometryUtil.cs create mode 100644 PCUT/DeepNestLib.Core/IStringify.cs create mode 100644 PCUT/DeepNestLib.Core/NFP.cs create mode 100644 PCUT/DeepNestLib.Core/NfpKey.cs create mode 100644 PCUT/DeepNestLib.Core/PlacementItem.cs create mode 100644 PCUT/DeepNestLib.Core/PlacementTypeEnum.cs create mode 100644 PCUT/DeepNestLib.Core/PolygonBounds.cs create mode 100644 PCUT/DeepNestLib.Core/PopulationItem.cs create mode 100644 PCUT/DeepNestLib.Core/RectangleSheet.cs create mode 100644 PCUT/DeepNestLib.Core/Sheet.cs create mode 100644 PCUT/DeepNestLib.Core/SheetPlacement.cs create mode 100644 PCUT/DeepNestLib.Core/SheetPlacementItem.cs create mode 100644 PCUT/DeepNestLib.Core/Simplify.cs create mode 100644 PCUT/DeepNestLib.Core/SvgNestConfig.cs create mode 100644 PCUT/DeepNestLib.Core/SvgPoint.cs create mode 100644 PCUT/DeepNestLib.Core/_Clipper.cs create mode 100644 PCUT/Http.Core/Constants/HttpConstants.cs create mode 100644 PCUT/Http.Core/Contexts/UserContext.cs create mode 100644 PCUT/Http.Core/Extensions/CategoryExtensions.cs create mode 100644 PCUT/Http.Core/Extensions/HttpExtensions.cs create mode 100644 PCUT/Http.Core/Handlers/AuthenticationHandler.cs create mode 100644 PCUT/Http.Core/Handlers/DeviceAuthHandler.cs create mode 100644 PCUT/Http.Core/Http.Core.csproj create mode 100644 PCUT/Http.Core/HttpClientFactory.cs create mode 100644 PCUT/Http.Core/Models/LoginRequest.cs create mode 100644 PCUT/Http.Core/Models/RefreshTokenRequest.cs create mode 100644 PCUT/Http.Core/PathBuilder.cs create mode 100644 PCUT/MinkowskiCpp/Minkowski.cpp create mode 100644 PCUT/MinkowskiCpp/Minkowski.h create mode 100644 PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj create mode 100644 PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj.filters create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_prefix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_suffix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_prefix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_suffix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_prefix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_suffix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx03.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx11.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx14.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx17.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx20.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx23.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx98.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/auto_link.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/borland.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang_version.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/codegear.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/comeau.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/common_edg.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/compaq_cxx.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/cray.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/diab.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/digitalmars.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc_xml.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/greenhills.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/hp_acc.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/intel.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/kai.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/metrowerks.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/mpw.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/nvcc.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pathscale.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pgi.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sgi_mipspro.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sunpro_cc.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/vacpp.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/visualc.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp_zos.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/cxx_composite.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/posix_features.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_compiler_config.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_platform_config.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_stdlib_config.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/suffix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/header_deprecated.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/helper_macros.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/cmath.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/complex.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/functional.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/memory.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/utility.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/aix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/amigaos.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/beos.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/bsd.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cloudabi.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cray.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cygwin.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/haiku.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/hpux.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/irix.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/linux.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/macos.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/qnxnto.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/solaris.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/symbian.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vms.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vxworks.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/wasm.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/win32.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/zos.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/pragma_message.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/requires_threads.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/dinkumware.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcomo.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcpp.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libstdcpp3.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/modena.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/msl.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/roguewave.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/sgi.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/stlport.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/vacpp.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/xlcpp_zos.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/user.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/warning_disable.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/config/workaround.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op_45.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_compact_to_points.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_geometry_to_set.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_points_to_compact.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/max_cover.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/minkowski.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_formation.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_set_view.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_touch.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_set_view.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_touch.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_arbitrary_formation.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_formation.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_set_view.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_simplify.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_sort_adaptor.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge_45.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/rectangle_formation.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/scan_arbitrary.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_ctypes.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_predicates.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_robust_fpt.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_structures.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gmp_override.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gtl.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_concept.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/isotropy.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_concept.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_concept.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_with_holes_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_concept.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_with_holes_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_concept.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_with_holes_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_concept.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_concept.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_data.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_traits.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_utils.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/transform.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_builder.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_diagram.hpp create mode 100644 PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_geometry_type.hpp create mode 100644 PCUT/MinkowskiCpp/pch.cpp create mode 100644 PCUT/MinkowskiCpp/pch.h create mode 100644 PCUT/PCUT.API/ApiClient.cs create mode 100644 PCUT/PCUT.API/Managers/CategoriesBase.cs create mode 100644 PCUT/PCUT.API/Managers/CategoryManager.cs create mode 100644 PCUT/PCUT.API/Managers/ICategoriesBase.cs create mode 100644 PCUT/PCUT.API/Managers/IManager.cs create mode 100644 PCUT/PCUT.API/Managers/IUserManager.cs create mode 100644 PCUT/PCUT.API/Managers/ManagerBase.cs create mode 100644 PCUT/PCUT.API/Managers/UserManager.cs create mode 100644 PCUT/PCUT.API/PCUT.API.csproj create mode 100644 PCUT/PCUT.Entities/ApiResponse/MessageResponse.cs create mode 100644 PCUT/PCUT.Entities/ApiResponse/PaginationResponse.cs create mode 100644 PCUT/PCUT.Entities/ApiResponse/UserCredentialDto.cs create mode 100644 PCUT/PCUT.Entities/Category.cs create mode 100644 PCUT/PCUT.Entities/CategoryMetadata.cs create mode 100644 PCUT/PCUT.Entities/CdrFile.cs create mode 100644 PCUT/PCUT.Entities/FileData.cs create mode 100644 PCUT/PCUT.Entities/PCUT.Entities.csproj create mode 100644 PCUT/PCUT.Entities/Pagination.cs create mode 100644 PCUT/PCUT.Entities/PasswordChange.cs create mode 100644 PCUT/PCUT.Entities/UpsertCategory.cs create mode 100644 PCUT/PCUT.Entities/UpsertFile.cs create mode 100644 PCUT/PCUT.Entities/UpsertMetadata.cs create mode 100644 PCUT/PCUT.Entities/UpsertUser.cs create mode 100644 PCUT/PCUT.Entities/UserProfile.cs create mode 100644 PCUT/PCUT.sln create mode 100644 PCUT/PCUT/App.xaml create mode 100644 PCUT/PCUT/App.xaml.cs create mode 100644 PCUT/PCUT/Assets/Backgound.jpg create mode 100644 PCUT/PCUT/Assets/LargeTile.scale-100.png create mode 100644 PCUT/PCUT/Assets/LargeTile.scale-125.png create mode 100644 PCUT/PCUT/Assets/LargeTile.scale-150.png create mode 100644 PCUT/PCUT/Assets/LargeTile.scale-200.png create mode 100644 PCUT/PCUT/Assets/LargeTile.scale-400.png create mode 100644 PCUT/PCUT/Assets/LockScreenLogo.scale-200.png create mode 100644 PCUT/PCUT/Assets/Logo-Pcut.jpg create mode 100644 PCUT/PCUT/Assets/SmallTile.scale-100.png create mode 100644 PCUT/PCUT/Assets/SmallTile.scale-125.png create mode 100644 PCUT/PCUT/Assets/SmallTile.scale-150.png create mode 100644 PCUT/PCUT/Assets/SmallTile.scale-200.png create mode 100644 PCUT/PCUT/Assets/SmallTile.scale-400.png create mode 100644 PCUT/PCUT/Assets/SplashScreen.scale-100.png create mode 100644 PCUT/PCUT/Assets/SplashScreen.scale-125.png create mode 100644 PCUT/PCUT/Assets/SplashScreen.scale-150.png create mode 100644 PCUT/PCUT/Assets/SplashScreen.scale-200.png create mode 100644 PCUT/PCUT/Assets/SplashScreen.scale-400.png create mode 100644 PCUT/PCUT/Assets/Square150x150Logo.scale-100.png create mode 100644 PCUT/PCUT/Assets/Square150x150Logo.scale-125.png create mode 100644 PCUT/PCUT/Assets/Square150x150Logo.scale-150.png create mode 100644 PCUT/PCUT/Assets/Square150x150Logo.scale-200.png create mode 100644 PCUT/PCUT/Assets/Square150x150Logo.scale-400.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-16.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-256.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-32.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-48.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.scale-100.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.scale-125.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.scale-150.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.scale-200.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.scale-400.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.targetsize-16.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.targetsize-24.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.targetsize-24_altform-unplated.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.targetsize-256.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.targetsize-32.png create mode 100644 PCUT/PCUT/Assets/Square44x44Logo.targetsize-48.png create mode 100644 PCUT/PCUT/Assets/StoreLogo.backup.png create mode 100644 PCUT/PCUT/Assets/StoreLogo.scale-100.png create mode 100644 PCUT/PCUT/Assets/StoreLogo.scale-125.png create mode 100644 PCUT/PCUT/Assets/StoreLogo.scale-150.png create mode 100644 PCUT/PCUT/Assets/StoreLogo.scale-200.png create mode 100644 PCUT/PCUT/Assets/StoreLogo.scale-400.png create mode 100644 PCUT/PCUT/Assets/Wide310x150Logo.scale-100.png create mode 100644 PCUT/PCUT/Assets/Wide310x150Logo.scale-125.png create mode 100644 PCUT/PCUT/Assets/Wide310x150Logo.scale-150.png create mode 100644 PCUT/PCUT/Assets/Wide310x150Logo.scale-200.png create mode 100644 PCUT/PCUT/Assets/Wide310x150Logo.scale-400.png create mode 100644 PCUT/PCUT/Assets/add.png create mode 100644 PCUT/PCUT/Assets/back.jpg create mode 100644 PCUT/PCUT/Assets/home.png create mode 100644 PCUT/PCUT/Assets/img_customer.png create mode 100644 PCUT/PCUT/Assets/img_order.png create mode 100644 PCUT/PCUT/Assets/img_product.png create mode 100644 PCUT/PCUT/Assets/img_setting.png create mode 100644 PCUT/PCUT/Assets/logo.jpg create mode 100644 PCUT/PCUT/Assets/shutdown_mo.png create mode 100644 PCUT/PCUT/Assets/unnamed.jpg create mode 100644 PCUT/PCUT/BundleArtifacts/PCUT.appinstaller.xml create mode 100644 PCUT/PCUT/BundleArtifacts/x64.txt create mode 100644 PCUT/PCUT/BundleArtifacts/x86.txt create mode 100644 PCUT/PCUT/Converters/BooleanConverter.cs create mode 100644 PCUT/PCUT/Converters/BooleanInverter.cs create mode 100644 PCUT/PCUT/Converters/BooleanToStringConverter.cs create mode 100644 PCUT/PCUT/Converters/BooleanToVisibilityConverter.cs create mode 100644 PCUT/PCUT/Converters/OpacityConverter.cs create mode 100644 PCUT/PCUT/Converters/UserRoleToBooleanConverter.cs create mode 100644 PCUT/PCUT/Converters/UserStatusConverter.cs create mode 100644 PCUT/PCUT/Converters/VisibilityConverter.cs create mode 100644 PCUT/PCUT/Converters/VisibilityToMarginConverter.cs create mode 100644 PCUT/PCUT/DeepNestApi/Background.cs create mode 100644 PCUT/PCUT/DeepNestApi/DraftElement.cs create mode 100644 PCUT/PCUT/DeepNestApi/LineElement.cs create mode 100644 PCUT/PCUT/DeepNestApi/LocalContour.cs create mode 100644 PCUT/PCUT/DeepNestApi/NestingContext.cs create mode 100644 PCUT/PCUT/DeepNestApi/PolylineElement.cs create mode 100644 PCUT/PCUT/DeepNestApi/RawDetail.cs create mode 100644 PCUT/PCUT/DeepNestApi/SvgNest.cs create mode 100644 PCUT/PCUT/DeepNestApi/SvgParser.cs create mode 100644 PCUT/PCUT/Extensions/BindExtensions.cs create mode 100644 PCUT/PCUT/Extensions/Converters/CircleElementConverter.cs create mode 100644 PCUT/PCUT/Extensions/Converters/EllipseElementConverter.cs create mode 100644 PCUT/PCUT/Extensions/Converters/PathConverter.cs create mode 100644 PCUT/PCUT/Extensions/Converters/PathElementConverter.cs create mode 100644 PCUT/PCUT/Extensions/Converters/PolygonElementConverter.cs create mode 100644 PCUT/PCUT/Extensions/Converters/RectElementConverter.cs create mode 100644 PCUT/PCUT/Extensions/EnumerableExtensions.cs create mode 100644 PCUT/PCUT/Extensions/HpglExtensions.cs create mode 100644 PCUT/PCUT/Extensions/ImageSourceExtensions.cs create mode 100644 PCUT/PCUT/Extensions/ManipulationExtensions.cs create mode 100644 PCUT/PCUT/Extensions/NumericExtensions.cs create mode 100644 PCUT/PCUT/Extensions/PointExtensions.cs create mode 100644 PCUT/PCUT/Extensions/SettingExtensions.cs create mode 100644 PCUT/PCUT/Extensions/SvgExtensions.cs create mode 100644 PCUT/PCUT/Extensions/SvgTransformExtensions.cs create mode 100644 PCUT/PCUT/Extensions/Transforms/MatrixTransform.cs create mode 100644 PCUT/PCUT/Extensions/Transforms/RotateTransform.cs create mode 100644 PCUT/PCUT/Extensions/Transforms/ScaleTransform.cs create mode 100644 PCUT/PCUT/Extensions/Transforms/SkewTransform.cs create mode 100644 PCUT/PCUT/Extensions/Transforms/SvgTransform.cs create mode 100644 PCUT/PCUT/Extensions/Transforms/TranslateTransform.cs create mode 100644 PCUT/PCUT/Extensions/XmlExtensions.cs create mode 100644 PCUT/PCUT/Factories/FileFactory.cs create mode 100644 PCUT/PCUT/Helpers/Converters/BooleanNegationConverter.cs create mode 100644 PCUT/PCUT/Helpers/Converters/DateTimeToFormattedStringConverter.cs create mode 100644 PCUT/PCUT/Helpers/RelayCommand.cs create mode 100644 PCUT/PCUT/Models/Categories/CategoryModel.cs create mode 100644 PCUT/PCUT/Models/Categories/CategoryTemplateSelector.cs create mode 100644 PCUT/PCUT/Models/NotificationBase.cs create mode 100644 PCUT/PCUT/Models/SelectableCollection.cs create mode 100644 PCUT/PCUT/Models/Users/UsersModel.cs create mode 100644 PCUT/PCUT/PCUT.csproj create mode 100644 PCUT/PCUT/Package.appxmanifest create mode 100644 PCUT/PCUT/Pages/AdminCenterPage.xaml create mode 100644 PCUT/PCUT/Pages/AdminCenterPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/CategoriesManagement/CategoriesViewPage.xaml create mode 100644 PCUT/PCUT/Pages/CategoriesManagement/CategoriesViewPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/CategoriesManagement/CategoryUpsertDialog.xaml create mode 100644 PCUT/PCUT/Pages/CategoriesManagement/CategoryUpsertDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/CategoriesManagement/MetadataUpsertDialog.xaml create mode 100644 PCUT/PCUT/Pages/CategoriesManagement/MetadataUpsertDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/DataCenterPage.xaml create mode 100644 PCUT/PCUT/Pages/DataCenterPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/DesignCenter/CutFilesDialog.xaml create mode 100644 PCUT/PCUT/Pages/DesignCenter/CutFilesDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml create mode 100644 PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/DesignCenter/EditPage.xaml create mode 100644 PCUT/PCUT/Pages/DesignCenter/EditPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml create mode 100644 PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml create mode 100644 PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml create mode 100644 PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml create mode 100644 PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml create mode 100644 PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml create mode 100644 PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/LoginPage.xaml create mode 100644 PCUT/PCUT/Pages/LoginPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/MainMenuPage.xaml create mode 100644 PCUT/PCUT/Pages/MainMenuPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml create mode 100644 PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml.cs create mode 100644 PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml create mode 100644 PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml.cs create mode 100644 PCUT/PCUT/Pages/UserManagement/UpsertUserPage.xaml create mode 100644 PCUT/PCUT/Pages/UserManagement/UpsertUserPage.xaml.cs create mode 100644 PCUT/PCUT/Pages/UserManagement/UserListPage.xaml create mode 100644 PCUT/PCUT/Pages/UserManagement/UserListPage.xaml.cs create mode 100644 PCUT/PCUT/Properties/AssemblyInfo.cs create mode 100644 PCUT/PCUT/Properties/Default.rd.xml create mode 100644 PCUT/PCUT/ViewModels/CategoriesViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/ChangePasswordViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/ComponentImageModel.cs create mode 100644 PCUT/PCUT/ViewModels/CutFilesViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/DesignCenterViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/FileAddViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/FileEditViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/FileListViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/ImageViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/LoadingModel.cs create mode 100644 PCUT/PCUT/ViewModels/LogOutViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/LoginViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/MainMenuPageViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/MetadataViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/NestingProgressModel.cs create mode 100644 PCUT/PCUT/ViewModels/PaginationViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/PortSettingViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/SvgData.cs create mode 100644 PCUT/PCUT/ViewModels/SvgNestSettings.cs create mode 100644 PCUT/PCUT/ViewModels/UpsertCategoryViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/UpsertMetadataViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/UpsertUserViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/UpsertViewModel.cs create mode 100644 PCUT/PCUT/ViewModels/UserViewModel.cs create mode 100644 README.md diff --git a/PCUT/Clipper.Core/Clipper.Core.csproj b/PCUT/Clipper.Core/Clipper.Core.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/PCUT/Clipper.Core/Clipper.Core.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/PCUT/Clipper.Core/clipper.cs b/PCUT/Clipper.Core/clipper.cs new file mode 100644 index 0000000..8c796e2 --- /dev/null +++ b/PCUT/Clipper.Core/clipper.cs @@ -0,0 +1,4913 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.2 * +* Date : 27 February 2017 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2017 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to performance. +//#define use_xyz + +//use_lines: Enables open path clipping. Adds a very minor cost to performance. +#define use_lines + + +using System; +using System.Collections.Generic; +//using System.Text; //for Int128.AsString() & StringBuilder +//using System.IO; //debugging with streamReader & StreamWriter +//using System.Windows.Forms; //debugging to clipboard + +namespace ClipperLib +{ + +#if use_int32 + using cInt = Int32; +#else + using cInt = Int64; +#endif + + using Path = List; + using Paths = List>; + + public struct DoublePoint + { + public double X; + public double Y; + + public DoublePoint(double x = 0, double y = 0) + { + this.X = x; this.Y = y; + } + public DoublePoint(DoublePoint dp) + { + this.X = dp.X; this.Y = dp.Y; + } + public DoublePoint(IntPoint ip) + { + this.X = ip.X; this.Y = ip.Y; + } + }; + + + //------------------------------------------------------------------------------ + // PolyTree & PolyNode classes + //------------------------------------------------------------------------------ + + public class PolyTree : PolyNode + { + internal List m_AllPolys = new List(); + + //The GC probably handles this cleanup more efficiently ... + //~PolyTree(){Clear();} + + public void Clear() + { + for (int i = 0; i < m_AllPolys.Count; i++) + m_AllPolys[i] = null; + m_AllPolys.Clear(); + m_Childs.Clear(); + } + + public PolyNode GetFirst() + { + if (m_Childs.Count > 0) + return m_Childs[0]; + else + return null; + } + + public int Total + { + get + { + int result = m_AllPolys.Count; + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && m_Childs[0] != m_AllPolys[0]) result--; + return result; + } + } + + } + + public class PolyNode + { + internal PolyNode m_Parent; + internal Path m_polygon = new Path(); + internal int m_Index; + internal JoinType m_jointype; + internal EndType m_endtype; + internal List m_Childs = new List(); + + private bool IsHoleNode() + { + bool result = true; + PolyNode node = m_Parent; + while (node != null) + { + result = !result; + node = node.m_Parent; + } + return result; + } + + public int ChildCount + { + get { return m_Childs.Count; } + } + + public Path Contour + { + get { return m_polygon; } + } + + internal void AddChild(PolyNode Child) + { + int cnt = m_Childs.Count; + m_Childs.Add(Child); + Child.m_Parent = this; + Child.m_Index = cnt; + } + + public PolyNode GetNext() + { + if (m_Childs.Count > 0) + return m_Childs[0]; + else + return GetNextSiblingUp(); + } + + internal PolyNode GetNextSiblingUp() + { + if (m_Parent == null) + return null; + else if (m_Index == m_Parent.m_Childs.Count - 1) + return m_Parent.GetNextSiblingUp(); + else + return m_Parent.m_Childs[m_Index + 1]; + } + + public List Childs + { + get { return m_Childs; } + } + + public PolyNode Parent + { + get { return m_Parent; } + } + + public bool IsHole + { + get { return IsHoleNode(); } + } + + public bool IsOpen { get; set; } + } + + + //------------------------------------------------------------------------------ + // Int128 struct (enables safe math on signed 64bit integers) + // eg Int128 val1((Int64)9223372036854775807); //ie 2^63 -1 + // Int128 val2((Int64)9223372036854775807); + // Int128 val3 = val1 * val2; + // val3.ToString => "85070591730234615847396907784232501249" (8.5e+37) + //------------------------------------------------------------------------------ + + internal struct Int128 + { + private Int64 hi; + private UInt64 lo; + + public Int128(Int64 _lo) + { + lo = (UInt64)_lo; + if (_lo < 0) hi = -1; + else hi = 0; + } + + public Int128(Int64 _hi, UInt64 _lo) + { + lo = _lo; + hi = _hi; + } + + public Int128(Int128 val) + { + hi = val.hi; + lo = val.lo; + } + + public bool IsNegative() + { + return hi < 0; + } + + public static bool operator ==(Int128 val1, Int128 val2) + { + if ((object)val1 == (object)val2) return true; + else if ((object)val1 == null || (object)val2 == null) return false; + return (val1.hi == val2.hi && val1.lo == val2.lo); + } + + public static bool operator !=(Int128 val1, Int128 val2) + { + return !(val1 == val2); + } + + public override bool Equals(System.Object obj) + { + if (obj == null || !(obj is Int128)) + return false; + Int128 i128 = (Int128)obj; + return (i128.hi == hi && i128.lo == lo); + } + + public override int GetHashCode() + { + return hi.GetHashCode() ^ lo.GetHashCode(); + } + + public static bool operator >(Int128 val1, Int128 val2) + { + if (val1.hi != val2.hi) + return val1.hi > val2.hi; + else + return val1.lo > val2.lo; + } + + public static bool operator <(Int128 val1, Int128 val2) + { + if (val1.hi != val2.hi) + return val1.hi < val2.hi; + else + return val1.lo < val2.lo; + } + + public static Int128 operator +(Int128 lhs, Int128 rhs) + { + lhs.hi += rhs.hi; + lhs.lo += rhs.lo; + if (lhs.lo < rhs.lo) lhs.hi++; + return lhs; + } + + public static Int128 operator -(Int128 lhs, Int128 rhs) + { + return lhs + -rhs; + } + + public static Int128 operator -(Int128 val) + { + if (val.lo == 0) + return new Int128(-val.hi, 0); + else + return new Int128(~val.hi, ~val.lo + 1); + } + + public static explicit operator double(Int128 val) + { + const double shift64 = 18446744073709551616.0; //2^64 + if (val.hi < 0) + { + if (val.lo == 0) + return (double)val.hi * shift64; + else + return -(double)(~val.lo + ~val.hi * shift64); + } + else + return (double)(val.lo + val.hi * shift64); + } + + //nb: Constructing two new Int128 objects every time we want to multiply longs + //is slow. So, although calling the Int128Mul method doesn't look as clean, the + //code runs significantly faster than if we'd used the * operator. + + public static Int128 Int128Mul(Int64 lhs, Int64 rhs) + { + bool negate = (lhs < 0) != (rhs < 0); + if (lhs < 0) lhs = -lhs; + if (rhs < 0) rhs = -rhs; + UInt64 int1Hi = (UInt64)lhs >> 32; + UInt64 int1Lo = (UInt64)lhs & 0xFFFFFFFF; + UInt64 int2Hi = (UInt64)rhs >> 32; + UInt64 int2Lo = (UInt64)rhs & 0xFFFFFFFF; + + //nb: see comments in clipper.pas + UInt64 a = int1Hi * int2Hi; + UInt64 b = int1Lo * int2Lo; + UInt64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + UInt64 lo; + Int64 hi; + hi = (Int64)(a + (c >> 32)); + + unchecked { lo = (c << 32) + b; } + if (lo < b) hi++; + Int128 result = new Int128(hi, lo); + return negate ? -result : result; + } + + }; + + //------------------------------------------------------------------------------ + //------------------------------------------------------------------------------ + + public struct IntPoint + { + public cInt X; + public cInt Y; +#if use_xyz + public cInt Z; + + public IntPoint(cInt x, cInt y, cInt z = 0) + { + this.X = x; this.Y = y; this.Z = z; + } + + public IntPoint(double x, double y, double z = 0) + { + this.X = (cInt)x; this.Y = (cInt)y; this.Z = (cInt)z; + } + + public IntPoint(DoublePoint dp) + { + this.X = (cInt)dp.X; this.Y = (cInt)dp.Y; this.Z = 0; + } + + public IntPoint(IntPoint pt) + { + this.X = pt.X; this.Y = pt.Y; this.Z = pt.Z; + } +#else + public IntPoint(cInt X, cInt Y) + { + this.X = X; this.Y = Y; + } + public IntPoint(double x, double y) + { + this.X = (cInt)x; this.Y = (cInt)y; + } + + public IntPoint(IntPoint pt) + { + this.X = pt.X; this.Y = pt.Y; + } +#endif + + public static bool operator ==(IntPoint a, IntPoint b) + { + return a.X == b.X && a.Y == b.Y; + } + + public static bool operator !=(IntPoint a, IntPoint b) + { + return a.X != b.X || a.Y != b.Y; + } + + public override bool Equals(object obj) + { + if (obj == null) return false; + if (obj is IntPoint) + { + IntPoint a = (IntPoint)obj; + return (X == a.X) && (Y == a.Y); + } + else return false; + } + + public override int GetHashCode() + { + //simply prevents a compiler warning + return base.GetHashCode(); + } + + }// end struct IntPoint + + public struct IntRect + { + public cInt left; + public cInt top; + public cInt right; + public cInt bottom; + + public IntRect(cInt l, cInt t, cInt r, cInt b) + { + this.left = l; this.top = t; + this.right = r; this.bottom = b; + } + public IntRect(IntRect ir) + { + this.left = ir.left; this.top = ir.top; + this.right = ir.right; this.bottom = ir.bottom; + } + } + + public enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; + public enum PolyType { ptSubject, ptClip }; + + //By far the most widely used winding rules for polygon filling are + //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) + //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) + //see http://glprogramming.com/red/chapter11.html + public enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + + public enum JoinType { jtSquare, jtRound, jtMiter }; + public enum EndType { etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound }; + + internal enum EdgeSide {esLeft, esRight}; + internal enum Direction {dRightToLeft, dLeftToRight}; + + internal class TEdge { + internal IntPoint Bot; + internal IntPoint Curr; //current (updated for every new scanbeam) + internal IntPoint Top; + internal IntPoint Delta; + internal double Dx; + internal PolyType PolyTyp; + internal EdgeSide Side; //side only refers to current side of solution poly + internal int WindDelta; //1 or -1 depending on winding direction + internal int WindCnt; + internal int WindCnt2; //winding count of the opposite polytype + internal int OutIdx; + internal TEdge Next; + internal TEdge Prev; + internal TEdge NextInLML; + internal TEdge NextInAEL; + internal TEdge PrevInAEL; + internal TEdge NextInSEL; + internal TEdge PrevInSEL; + }; + + public class IntersectNode + { + internal TEdge Edge1; + internal TEdge Edge2; + internal IntPoint Pt; + }; + + public class MyIntersectNodeSort : IComparer + { + public int Compare(IntersectNode node1, IntersectNode node2) + { + cInt i = node2.Pt.Y - node1.Pt.Y; + if (i > 0) return 1; + else if (i < 0) return -1; + else return 0; + } + } + + internal class LocalMinima + { + internal cInt Y; + internal TEdge LeftBound; + internal TEdge RightBound; + internal LocalMinima Next; + }; + + internal class Scanbeam + { + internal cInt Y; + internal Scanbeam Next; + }; + + internal class Maxima + { + internal cInt X; + internal Maxima Next; + internal Maxima Prev; + }; + + //OutRec: contains a path in the clipping solution. Edges in the AEL will + //carry a pointer to an OutRec when they are part of the clipping solution. + internal class OutRec + { + internal int Idx; + internal bool IsHole; + internal bool IsOpen; + internal OutRec FirstLeft; //see comments in clipper.pas + internal OutPt Pts; + internal OutPt BottomPt; + internal PolyNode PolyNode; + }; + + internal class OutPt + { + internal int Idx; + internal IntPoint Pt; + internal OutPt Next; + internal OutPt Prev; + }; + + internal class Join + { + internal OutPt OutPt1; + internal OutPt OutPt2; + internal IntPoint OffPt; + }; + + public class ClipperBase + { + internal const double horizontal = -3.4E+38; + internal const int Skip = -2; + internal const int Unassigned = -1; + internal const double tolerance = 1.0E-20; + internal static bool near_zero(double val){return (val > -tolerance) && (val < tolerance);} + +#if use_int32 + public const cInt loRange = 0x7FFF; + public const cInt hiRange = 0x7FFF; +#else + public const cInt loRange = 0x3FFFFFFF; + public const cInt hiRange = 0x3FFFFFFFFFFFFFFFL; +#endif + + internal LocalMinima m_MinimaList; + internal LocalMinima m_CurrentLM; + internal List> m_edges = new List>(); + internal Scanbeam m_Scanbeam; + internal List m_PolyOuts; + internal TEdge m_ActiveEdges; + internal bool m_UseFullRange; + internal bool m_HasOpenPaths; + + //------------------------------------------------------------------------------ + + public bool PreserveCollinear + { + get; + set; + } + //------------------------------------------------------------------------------ + + public void Swap(ref cInt val1, ref cInt val2) + { + cInt tmp = val1; + val1 = val2; + val2 = tmp; + } + //------------------------------------------------------------------------------ + + internal static bool IsHorizontal(TEdge e) + { + return e.Delta.Y == 0; + } + //------------------------------------------------------------------------------ + + internal bool PointIsVertex(IntPoint pt, OutPt pp) + { + OutPt pp2 = pp; + do + { + if (pp2.Pt == pt) return true; + pp2 = pp2.Next; + } + while (pp2 != pp); + return false; + } + //------------------------------------------------------------------------------ + + internal bool PointOnLineSegment(IntPoint pt, + IntPoint linePt1, IntPoint linePt2, bool UseFullRange) + { + if (UseFullRange) + return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || + ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || + (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && + ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && + ((Int128.Int128Mul((pt.X - linePt1.X), (linePt2.Y - linePt1.Y)) == + Int128.Int128Mul((linePt2.X - linePt1.X), (pt.Y - linePt1.Y))))); + else + return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || + ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || + (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && + ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && + ((pt.X - linePt1.X) * (linePt2.Y - linePt1.Y) == + (linePt2.X - linePt1.X) * (pt.Y - linePt1.Y))); + } + //------------------------------------------------------------------------------ + + internal bool PointOnPolygon(IntPoint pt, OutPt pp, bool UseFullRange) + { + OutPt pp2 = pp; + while (true) + { + if (PointOnLineSegment(pt, pp2.Pt, pp2.Next.Pt, UseFullRange)) + return true; + pp2 = pp2.Next; + if (pp2 == pp) break; + } + return false; + } + //------------------------------------------------------------------------------ + + internal static bool SlopesEqual(TEdge e1, TEdge e2, bool UseFullRange) + { + if (UseFullRange) + return Int128.Int128Mul(e1.Delta.Y, e2.Delta.X) == + Int128.Int128Mul(e1.Delta.X, e2.Delta.Y); + else return (cInt)(e1.Delta.Y) * (e2.Delta.X) == + (cInt)(e1.Delta.X) * (e2.Delta.Y); + } + //------------------------------------------------------------------------------ + + internal static bool SlopesEqual(IntPoint pt1, IntPoint pt2, + IntPoint pt3, bool UseFullRange) + { + if (UseFullRange) + return Int128.Int128Mul(pt1.Y - pt2.Y, pt2.X - pt3.X) == + Int128.Int128Mul(pt1.X - pt2.X, pt2.Y - pt3.Y); + else return + (cInt)(pt1.Y - pt2.Y) * (pt2.X - pt3.X) - (cInt)(pt1.X - pt2.X) * (pt2.Y - pt3.Y) == 0; + } + //------------------------------------------------------------------------------ + + internal static bool SlopesEqual(IntPoint pt1, IntPoint pt2, + IntPoint pt3, IntPoint pt4, bool UseFullRange) + { + if (UseFullRange) + return Int128.Int128Mul(pt1.Y - pt2.Y, pt3.X - pt4.X) == + Int128.Int128Mul(pt1.X - pt2.X, pt3.Y - pt4.Y); + else return + (cInt)(pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (cInt)(pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0; + } + //------------------------------------------------------------------------------ + + internal ClipperBase() //constructor (nb: no external instantiation) + { + m_MinimaList = null; + m_CurrentLM = null; + m_UseFullRange = false; + m_HasOpenPaths = false; + } + //------------------------------------------------------------------------------ + + public virtual void Clear() + { + DisposeLocalMinimaList(); + for (int i = 0; i < m_edges.Count; ++i) + { + for (int j = 0; j < m_edges[i].Count; ++j) m_edges[i][j] = null; + m_edges[i].Clear(); + } + m_edges.Clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; + } + //------------------------------------------------------------------------------ + + private void DisposeLocalMinimaList() + { + while( m_MinimaList != null ) + { + LocalMinima tmpLm = m_MinimaList.Next; + m_MinimaList = null; + m_MinimaList = tmpLm; + } + m_CurrentLM = null; + } + //------------------------------------------------------------------------------ + + void RangeTest(IntPoint Pt, ref bool useFullRange) + { + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw new ClipperException("Coordinate outside allowed range"); + } + else if (Pt.X > loRange || Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, ref useFullRange); + } + } + //------------------------------------------------------------------------------ + + private void InitEdge(TEdge e, TEdge eNext, + TEdge ePrev, IntPoint pt) + { + e.Next = eNext; + e.Prev = ePrev; + e.Curr = pt; + e.OutIdx = Unassigned; + } + //------------------------------------------------------------------------------ + + private void InitEdge2(TEdge e, PolyType polyType) + { + if (e.Curr.Y >= e.Next.Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next.Curr; + } + else + { + e.Top = e.Curr; + e.Bot = e.Next.Curr; + } + SetDx(e); + e.PolyTyp = polyType; + } + //------------------------------------------------------------------------------ + + private TEdge FindNextLocMin(TEdge E) + { + TEdge E2; + for (;;) + { + while (E.Bot != E.Prev.Bot || E.Curr == E.Top) E = E.Next; + if (E.Dx != horizontal && E.Prev.Dx != horizontal) break; + while (E.Prev.Dx == horizontal) E = E.Prev; + E2 = E; + while (E.Dx == horizontal) E = E.Next; + if (E.Top.Y == E.Prev.Bot.Y) continue; //ie just an intermediate horz. + if (E2.Prev.Bot.X < E.Bot.X) E = E2; + break; + } + return E; + } + //------------------------------------------------------------------------------ + + private TEdge ProcessBound(TEdge E, bool LeftBoundIsForward) + { + TEdge EStart, Result = E; + TEdge Horz; + + if (Result.OutIdx == Skip) + { + //check if there are edges beyond the skip edge in the bound and if so + //create another LocMin and calling ProcessBound once more ... + E = Result; + if (LeftBoundIsForward) + { + while (E.Top.Y == E.Next.Bot.Y) E = E.Next; + while (E != Result && E.Dx == horizontal) E = E.Prev; + } + else + { + while (E.Top.Y == E.Prev.Bot.Y) E = E.Prev; + while (E != Result && E.Dx == horizontal) E = E.Next; + } + if (E == Result) + { + if (LeftBoundIsForward) Result = E.Next; + else Result = E.Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (LeftBoundIsForward) + E = Result.Next; + else + E = Result.Prev; + LocalMinima locMin = new LocalMinima(); + locMin.Next = null; + locMin.Y = E.Bot.Y; + locMin.LeftBound = null; + locMin.RightBound = E; + E.WindDelta = 0; + Result = ProcessBound(E, LeftBoundIsForward); + InsertLocalMinima(locMin); + } + return Result; + } + + if (E.Dx == horizontal) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (LeftBoundIsForward) EStart = E.Prev; + else EStart = E.Next; + if (EStart.Dx == horizontal) //ie an adjoining horizontal skip edge + { + if (EStart.Bot.X != E.Bot.X && EStart.Top.X != E.Bot.X) + ReverseHorizontal(E); + } + else if (EStart.Bot.X != E.Bot.X) + ReverseHorizontal(E); + } + + EStart = E; + if (LeftBoundIsForward) + { + while (Result.Top.Y == Result.Next.Bot.Y && Result.Next.OutIdx != Skip) + Result = Result.Next; + if (Result.Dx == horizontal && Result.Next.OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (Horz.Prev.Dx == horizontal) Horz = Horz.Prev; + if (Horz.Prev.Top.X > Result.Next.Top.X) Result = Horz.Prev; + } + while (E != Result) + { + E.NextInLML = E.Next; + if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) + ReverseHorizontal(E); + E = E.Next; + } + if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) + ReverseHorizontal(E); + Result = Result.Next; //move to the edge just beyond current bound + } + else + { + while (Result.Top.Y == Result.Prev.Bot.Y && Result.Prev.OutIdx != Skip) + Result = Result.Prev; + if (Result.Dx == horizontal && Result.Prev.OutIdx != Skip) + { + Horz = Result; + while (Horz.Next.Dx == horizontal) Horz = Horz.Next; + if (Horz.Next.Top.X == Result.Prev.Top.X || + Horz.Next.Top.X > Result.Prev.Top.X) Result = Horz.Next; + } + + while (E != Result) + { + E.NextInLML = E.Prev; + if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X) + ReverseHorizontal(E); + E = E.Prev; + } + if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X) + ReverseHorizontal(E); + Result = Result.Prev; //move to the edge just beyond current bound + } + return Result; + } + //------------------------------------------------------------------------------ + + + public bool AddPath(Path pg, PolyType polyType, bool Closed) + { +#if use_lines + if (!Closed && polyType == PolyType.ptClip) + throw new ClipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw new ClipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.Count - 1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI - 1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + List edges = new List(highI+1); + for (int i = 0; i <= highI; i++) edges.Add(new TEdge()); + + bool IsFlat = true; + + //1. Basic (first) edge initialization ... + edges[1].Curr = pg[1]; + RangeTest(pg[0], ref m_UseFullRange); + RangeTest(pg[highI], ref m_UseFullRange); + InitEdge(edges[0], edges[1], edges[highI], pg[0]); + InitEdge(edges[highI], edges[0], edges[highI - 1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], ref m_UseFullRange); + InitEdge(edges[i], edges[i + 1], edges[i - 1], pg[i]); + } + TEdge eStart = edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge E = eStart, eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E.Curr == E.Next.Curr && (Closed || E.Next != eStart)) + { + if (E == E.Next) break; + if (E == eStart) eStart = E.Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E.Prev == E.Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E.Prev.Curr, E.Curr, E.Next.Curr, m_UseFullRange) && + (!PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E.Prev.Curr, E.Curr, E.Next.Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E.Next; + E = RemoveEdge(E); + E = E.Prev; + eLoopStop = E; + continue; + } + E = E.Next; + if ((E == eLoopStop) || (!Closed && E.Next == eStart)) break; + } + + if ((!Closed && (E == E.Next)) || (Closed && (E.Prev == E.Next))) + return false; + + if (!Closed) + { + m_HasOpenPaths = true; + eStart.Prev.OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(E, polyType); + E = E.Next; + if (IsFlat && E.Curr.Y != eStart.Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) return false; + E.Prev.OutIdx = Skip; + LocalMinima locMin = new LocalMinima(); + locMin.Next = null; + locMin.Y = E.Bot.Y; + locMin.LeftBound = null; + locMin.RightBound = E; + locMin.RightBound.Side = EdgeSide.esRight; + locMin.RightBound.WindDelta = 0; + for ( ; ; ) + { + if (E.Bot.X != E.Prev.Top.X) ReverseHorizontal(E); + if (E.Next.OutIdx == Skip) break; + E.NextInLML = E.Next; + E = E.Next; + } + InsertLocalMinima(locMin); + m_edges.Add(edges); + return true; + } + + m_edges.Add(edges); + bool leftBoundIsForward; + TEdge EMin = null; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E.Prev.Bot == E.Prev.Top) E = E.Next; + + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (EMin == null) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + LocalMinima locMin = new LocalMinima(); + locMin.Next = null; + locMin.Y = E.Bot.Y; + if (E.Dx < E.Prev.Dx) + { + locMin.LeftBound = E.Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E.Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + locMin.LeftBound.Side = EdgeSide.esLeft; + locMin.RightBound.Side = EdgeSide.esRight; + + if (!Closed) locMin.LeftBound.WindDelta = 0; + else if (locMin.LeftBound.Next == locMin.RightBound) + locMin.LeftBound.WindDelta = -1; + else locMin.LeftBound.WindDelta = 1; + locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E.OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2.OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound.OutIdx == Skip) + locMin.LeftBound = null; + else if (locMin.RightBound.OutIdx == Skip) + locMin.RightBound = null; + InsertLocalMinima(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; + + } + //------------------------------------------------------------------------------ + + public bool AddPaths(Paths ppg, PolyType polyType, bool closed) + { + bool result = false; + for (int i = 0; i < ppg.Count; ++i) + if (AddPath(ppg[i], polyType, closed)) result = true; + return result; + } + //------------------------------------------------------------------------------ + + internal bool Pt2IsBetweenPt1AndPt3(IntPoint pt1, IntPoint pt2, IntPoint pt3) + { + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) return false; + else if (pt1.X != pt3.X) return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); + } + //------------------------------------------------------------------------------ + + TEdge RemoveEdge(TEdge e) + { + //removes e from double_linked_list (but without removing from memory) + e.Prev.Next = e.Next; + e.Next.Prev = e.Prev; + TEdge result = e.Next; + e.Prev = null; //flag as removed (see ClipperBase.Clear) + return result; + } + //------------------------------------------------------------------------------ + + private void SetDx(TEdge e) + { + e.Delta.X = (e.Top.X - e.Bot.X); + e.Delta.Y = (e.Top.Y - e.Bot.Y); + if (e.Delta.Y == 0) e.Dx = horizontal; + else e.Dx = (double)(e.Delta.X) / (e.Delta.Y); + } + //--------------------------------------------------------------------------- + + private void InsertLocalMinima(LocalMinima newLm) + { + if( m_MinimaList == null ) + { + m_MinimaList = newLm; + } + else if( newLm.Y >= m_MinimaList.Y ) + { + newLm.Next = m_MinimaList; + m_MinimaList = newLm; + } else + { + LocalMinima tmpLm = m_MinimaList; + while( tmpLm.Next != null && ( newLm.Y < tmpLm.Next.Y ) ) + tmpLm = tmpLm.Next; + newLm.Next = tmpLm.Next; + tmpLm.Next = newLm; + } + } + //------------------------------------------------------------------------------ + + internal Boolean PopLocalMinima(cInt Y, out LocalMinima current) + { + current = m_CurrentLM; + if (m_CurrentLM != null && m_CurrentLM.Y == Y) + { + m_CurrentLM = m_CurrentLM.Next; + return true; + } + return false; + } + //------------------------------------------------------------------------------ + + private void ReverseHorizontal(TEdge e) + { + //swap horizontal edges' top and bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + Swap(ref e.Top.X, ref e.Bot.X); +#if use_xyz + Swap(ref e.Top.Z, ref e.Bot.Z); +#endif + } + //------------------------------------------------------------------------------ + + internal virtual void Reset() + { + m_CurrentLM = m_MinimaList; + if (m_CurrentLM == null) return; //ie nothing to process + + //reset all edges ... + m_Scanbeam = null; + LocalMinima lm = m_MinimaList; + while (lm != null) + { + InsertScanbeam(lm.Y); + TEdge e = lm.LeftBound; + if (e != null) + { + e.Curr = e.Bot; + e.OutIdx = Unassigned; + } + e = lm.RightBound; + if (e != null) + { + e.Curr = e.Bot; + e.OutIdx = Unassigned; + } + lm = lm.Next; + } + m_ActiveEdges = null; + } + //------------------------------------------------------------------------------ + + public static IntRect GetBounds(Paths paths) + { + int i = 0, cnt = paths.Count; + while (i < cnt && paths[i].Count == 0) i++; + if (i == cnt) return new IntRect(0,0,0,0); + IntRect result = new IntRect(); + result.left = paths[i][0].X; + result.right = result.left; + result.top = paths[i][0].Y; + result.bottom = result.top; + for (; i < cnt; i++) + for (int j = 0; j < paths[i].Count; j++) + { + if (paths[i][j].X < result.left) result.left = paths[i][j].X; + else if (paths[i][j].X > result.right) result.right = paths[i][j].X; + if (paths[i][j].Y < result.top) result.top = paths[i][j].Y; + else if (paths[i][j].Y > result.bottom) result.bottom = paths[i][j].Y; + } + return result; + } + //------------------------------------------------------------------------------ + + internal void InsertScanbeam(cInt Y) + { + //single-linked list: sorted descending, ignoring dups. + if (m_Scanbeam == null) + { + m_Scanbeam = new Scanbeam(); + m_Scanbeam.Next = null; + m_Scanbeam.Y = Y; + } + else if (Y > m_Scanbeam.Y) + { + Scanbeam newSb = new Scanbeam(); + newSb.Y = Y; + newSb.Next = m_Scanbeam; + m_Scanbeam = newSb; + } + else + { + Scanbeam sb2 = m_Scanbeam; + while (sb2.Next != null && (Y <= sb2.Next.Y)) sb2 = sb2.Next; + if (Y == sb2.Y) return; //ie ignores duplicates + Scanbeam newSb = new Scanbeam(); + newSb.Y = Y; + newSb.Next = sb2.Next; + sb2.Next = newSb; + } + } + //------------------------------------------------------------------------------ + + internal Boolean PopScanbeam(out cInt Y) + { + if (m_Scanbeam == null) + { + Y = 0; + return false; + } + Y = m_Scanbeam.Y; + m_Scanbeam = m_Scanbeam.Next; + return true; + } + //------------------------------------------------------------------------------ + + internal Boolean LocalMinimaPending() + { + return (m_CurrentLM != null); + } + //------------------------------------------------------------------------------ + + internal OutRec CreateOutRec() + { + OutRec result = new OutRec(); + result.Idx = Unassigned; + result.IsHole = false; + result.IsOpen = false; + result.FirstLeft = null; + result.Pts = null; + result.BottomPt = null; + result.PolyNode = null; + m_PolyOuts.Add(result); + result.Idx = m_PolyOuts.Count - 1; + return result; + } + //------------------------------------------------------------------------------ + + internal void DisposeOutRec(int index) + { + OutRec outRec = m_PolyOuts[index]; + outRec.Pts = null; + outRec = null; + m_PolyOuts[index] = null; + } + //------------------------------------------------------------------------------ + + internal void UpdateEdgeIntoAEL(ref TEdge e) + { + if (e.NextInLML == null) + throw new ClipperException("UpdateEdgeIntoAEL: invalid call"); + TEdge AelPrev = e.PrevInAEL; + TEdge AelNext = e.NextInAEL; + e.NextInLML.OutIdx = e.OutIdx; + if (AelPrev != null) + AelPrev.NextInAEL = e.NextInLML; + else m_ActiveEdges = e.NextInLML; + if (AelNext != null) + AelNext.PrevInAEL = e.NextInLML; + e.NextInLML.Side = e.Side; + e.NextInLML.WindDelta = e.WindDelta; + e.NextInLML.WindCnt = e.WindCnt; + e.NextInLML.WindCnt2 = e.WindCnt2; + e = e.NextInLML; + e.Curr = e.Bot; + e.PrevInAEL = AelPrev; + e.NextInAEL = AelNext; + if (!IsHorizontal(e)) InsertScanbeam(e.Top.Y); + } + //------------------------------------------------------------------------------ + + internal void SwapPositionsInAEL(TEdge edge1, TEdge edge2) + { + //check that one or other edge hasn't already been removed from AEL ... + if (edge1.NextInAEL == edge1.PrevInAEL || + edge2.NextInAEL == edge2.PrevInAEL) return; + + if (edge1.NextInAEL == edge2) + { + TEdge next = edge2.NextInAEL; + if (next != null) + next.PrevInAEL = edge1; + TEdge prev = edge1.PrevInAEL; + if (prev != null) + prev.NextInAEL = edge2; + edge2.PrevInAEL = prev; + edge2.NextInAEL = edge1; + edge1.PrevInAEL = edge2; + edge1.NextInAEL = next; + } + else if (edge2.NextInAEL == edge1) + { + TEdge next = edge1.NextInAEL; + if (next != null) + next.PrevInAEL = edge2; + TEdge prev = edge2.PrevInAEL; + if (prev != null) + prev.NextInAEL = edge1; + edge1.PrevInAEL = prev; + edge1.NextInAEL = edge2; + edge2.PrevInAEL = edge1; + edge2.NextInAEL = next; + } + else + { + TEdge next = edge1.NextInAEL; + TEdge prev = edge1.PrevInAEL; + edge1.NextInAEL = edge2.NextInAEL; + if (edge1.NextInAEL != null) + edge1.NextInAEL.PrevInAEL = edge1; + edge1.PrevInAEL = edge2.PrevInAEL; + if (edge1.PrevInAEL != null) + edge1.PrevInAEL.NextInAEL = edge1; + edge2.NextInAEL = next; + if (edge2.NextInAEL != null) + edge2.NextInAEL.PrevInAEL = edge2; + edge2.PrevInAEL = prev; + if (edge2.PrevInAEL != null) + edge2.PrevInAEL.NextInAEL = edge2; + } + + if (edge1.PrevInAEL == null) + m_ActiveEdges = edge1; + else if (edge2.PrevInAEL == null) + m_ActiveEdges = edge2; + } + //------------------------------------------------------------------------------ + + internal void DeleteFromAEL(TEdge e) + { + TEdge AelPrev = e.PrevInAEL; + TEdge AelNext = e.NextInAEL; + if (AelPrev == null && AelNext == null && (e != m_ActiveEdges)) + return; //already deleted + if (AelPrev != null) + AelPrev.NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext != null) + AelNext.PrevInAEL = AelPrev; + e.NextInAEL = null; + e.PrevInAEL = null; + } + //------------------------------------------------------------------------------ + + } //end ClipperBase + + public class Clipper : ClipperBase + { + //InitOptions that can be passed to the constructor ... + public const int ioReverseSolution = 1; + public const int ioStrictlySimple = 2; + public const int ioPreserveCollinear = 4; + + private ClipType m_ClipType; + private Maxima m_Maxima; + private TEdge m_SortedEdges; + private List m_IntersectList; + IComparer m_IntersectNodeComparer; + private bool m_ExecuteLocked; + private PolyFillType m_ClipFillType; + private PolyFillType m_SubjFillType; + private List m_Joins; + private List m_GhostJoins; + private bool m_UsingPolyTree; +#if use_xyz + public delegate void ZFillCallback(IntPoint bot1, IntPoint top1, + IntPoint bot2, IntPoint top2, ref IntPoint pt); + public ZFillCallback ZFillFunction { get; set; } +#endif + public Clipper(int InitOptions = 0): base() //constructor + { + m_Scanbeam = null; + m_Maxima = null; + m_ActiveEdges = null; + m_SortedEdges = null; + m_IntersectList = new List(); + m_IntersectNodeComparer = new MyIntersectNodeSort(); + m_ExecuteLocked = false; + m_UsingPolyTree = false; + m_PolyOuts = new List(); + m_Joins = new List(); + m_GhostJoins = new List(); + ReverseSolution = (ioReverseSolution & InitOptions) != 0; + StrictlySimple = (ioStrictlySimple & InitOptions) != 0; + PreserveCollinear = (ioPreserveCollinear & InitOptions) != 0; +#if use_xyz + ZFillFunction = null; +#endif + } + //------------------------------------------------------------------------------ + + private void InsertMaxima(cInt X) + { + //double-linked list: sorted ascending, ignoring dups. + Maxima newMax = new Maxima(); + newMax.X = X; + if (m_Maxima == null) + { + m_Maxima = newMax; + m_Maxima.Next = null; + m_Maxima.Prev = null; + } + else if (X < m_Maxima.X) + { + newMax.Next = m_Maxima; + newMax.Prev = null; + m_Maxima = newMax; + } + else + { + Maxima m = m_Maxima; + while (m.Next != null && (X >= m.Next.X)) m = m.Next; + if (X == m.X) return; //ie ignores duplicates (& CG to clean up newMax) + //insert newMax between m and m.Next ... + newMax.Next = m.Next; + newMax.Prev = m; + if (m.Next != null) m.Next.Prev = newMax; + m.Next = newMax; + } + } + //------------------------------------------------------------------------------ + + public bool ReverseSolution + { + get; + set; + } + //------------------------------------------------------------------------------ + + public bool StrictlySimple + { + get; + set; + } + //------------------------------------------------------------------------------ + + public bool Execute(ClipType clipType, Paths solution, + PolyFillType FillType = PolyFillType.pftEvenOdd) + { + return Execute(clipType, solution, FillType, FillType); + } + //------------------------------------------------------------------------------ + + public bool Execute(ClipType clipType, PolyTree polytree, + PolyFillType FillType = PolyFillType.pftEvenOdd) + { + return Execute(clipType, polytree, FillType, FillType); + } + //------------------------------------------------------------------------------ + + public bool Execute(ClipType clipType, Paths solution, + PolyFillType subjFillType, PolyFillType clipFillType) + { + if (m_ExecuteLocked) return false; + if (m_HasOpenPaths) throw + new ClipperException("Error: PolyTree struct is needed for open path clipping."); + + m_ExecuteLocked = true; + solution.Clear(); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded; + try + { + succeeded = ExecuteInternal(); + //build the return polygons ... + if (succeeded) BuildResult(solution); + } + finally + { + DisposeAllPolyPts(); + m_ExecuteLocked = false; + } + return succeeded; + } + //------------------------------------------------------------------------------ + + public bool Execute(ClipType clipType, PolyTree polytree, + PolyFillType subjFillType, PolyFillType clipFillType) + { + if (m_ExecuteLocked) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded; + try + { + succeeded = ExecuteInternal(); + //build the return polygons ... + if (succeeded) BuildResult2(polytree); + } + finally + { + DisposeAllPolyPts(); + m_ExecuteLocked = false; + } + return succeeded; + } + //------------------------------------------------------------------------------ + + internal void FixHoleLinkage(OutRec outRec) + { + //skip if an outermost polygon or + //already already points to the correct FirstLeft ... + if (outRec.FirstLeft == null || + (outRec.IsHole != outRec.FirstLeft.IsHole && + outRec.FirstLeft.Pts != null)) return; + + OutRec orfl = outRec.FirstLeft; + while (orfl != null && ((orfl.IsHole == outRec.IsHole) || orfl.Pts == null)) + orfl = orfl.FirstLeft; + outRec.FirstLeft = orfl; + } + //------------------------------------------------------------------------------ + + private bool ExecuteInternal() + { + try + { + Reset(); + m_SortedEdges = null; + m_Maxima = null; + + cInt botY, topY; + if (!PopScanbeam(out botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(out topY) || LocalMinimaPending()) + { + ProcessHorizontals(); + m_GhostJoins.Clear(); + if (!ProcessIntersections(topY)) return false; + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + InsertLocalMinimaIntoAEL(botY); + } + + //fix orientations ... + foreach (OutRec outRec in m_PolyOuts) + { + if (outRec.Pts == null || outRec.IsOpen) continue; + if ((outRec.IsHole ^ ReverseSolution) == (Area(outRec) > 0)) + ReversePolyPtLinks(outRec.Pts); + } + + JoinCommonEdges(); + + foreach (OutRec outRec in m_PolyOuts) + { + if (outRec.Pts == null) + continue; + else if (outRec.IsOpen) + FixupOutPolyline(outRec); + else + FixupOutPolygon(outRec); + } + + if (StrictlySimple) DoSimplePolygons(); + return true; + } + //catch { return false; } + finally + { + m_Joins.Clear(); + m_GhostJoins.Clear(); + } + } + //------------------------------------------------------------------------------ + + private void DisposeAllPolyPts(){ + for (int i = 0; i < m_PolyOuts.Count; ++i) DisposeOutRec(i); + m_PolyOuts.Clear(); + } + //------------------------------------------------------------------------------ + + private void AddJoin(OutPt Op1, OutPt Op2, IntPoint OffPt) + { + Join j = new Join(); + j.OutPt1 = Op1; + j.OutPt2 = Op2; + j.OffPt = OffPt; + m_Joins.Add(j); + } + //------------------------------------------------------------------------------ + + private void AddGhostJoin(OutPt Op, IntPoint OffPt) + { + Join j = new Join(); + j.OutPt1 = Op; + j.OffPt = OffPt; + m_GhostJoins.Add(j); + } + //------------------------------------------------------------------------------ + +#if use_xyz + internal void SetZ(ref IntPoint pt, TEdge e1, TEdge e2) + { + if (pt.Z != 0 || ZFillFunction == null) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else ZFillFunction(e1.Bot, e1.Top, e2.Bot, e2.Top, ref pt); + } + //------------------------------------------------------------------------------ +#endif + + private void InsertLocalMinimaIntoAEL(cInt botY) + { + LocalMinima lm; + while (PopLocalMinima(botY, out lm)) + { + TEdge lb = lm.LeftBound; + TEdge rb = lm.RightBound; + + OutPt Op1 = null; + if (lb == null) + { + InsertEdgeIntoAEL(rb, null); + SetWindingCount(rb); + if (IsContributing(rb)) + Op1 = AddOutPt(rb, rb.Bot); + } + else if (rb == null) + { + InsertEdgeIntoAEL(lb, null); + SetWindingCount(lb); + if (IsContributing(lb)) + Op1 = AddOutPt(lb, lb.Bot); + InsertScanbeam(lb.Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, null); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount(lb); + rb.WindCnt = lb.WindCnt; + rb.WindCnt2 = lb.WindCnt2; + if (IsContributing(lb)) + Op1 = AddLocalMinPoly(lb, rb, lb.Bot); + InsertScanbeam(lb.Top.Y); + } + + if (rb != null) + { + if (IsHorizontal(rb)) + { + if (rb.NextInLML != null) + InsertScanbeam(rb.NextInLML.Top.Y); + AddEdgeToSEL(rb); + } + else + InsertScanbeam(rb.Top.Y); + } + + if (lb == null || rb == null) continue; + + //if output polygons share an Edge with a horizontal rb, they'll need joining later ... + if (Op1 != null && IsHorizontal(rb) && + m_GhostJoins.Count > 0 && rb.WindDelta != 0) + { + for (int i = 0; i < m_GhostJoins.Count; i++) + { + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + Join j = m_GhostJoins[i]; + if (HorzSegmentsOverlap(j.OutPt1.Pt.X, j.OffPt.X, rb.Bot.X, rb.Top.X)) + AddJoin(j.OutPt1, Op1, j.OffPt); + } + } + + if (lb.OutIdx >= 0 && lb.PrevInAEL != null && + lb.PrevInAEL.Curr.X == lb.Bot.X && + lb.PrevInAEL.OutIdx >= 0 && + SlopesEqual(lb.PrevInAEL.Curr, lb.PrevInAEL.Top, lb.Curr, lb.Top, m_UseFullRange) && + lb.WindDelta != 0 && lb.PrevInAEL.WindDelta != 0) + { + OutPt Op2 = AddOutPt(lb.PrevInAEL, lb.Bot); + AddJoin(Op1, Op2, lb.Top); + } + + if( lb.NextInAEL != rb ) + { + + if (rb.OutIdx >= 0 && rb.PrevInAEL.OutIdx >= 0 && + SlopesEqual(rb.PrevInAEL.Curr, rb.PrevInAEL.Top, rb.Curr, rb.Top, m_UseFullRange) && + rb.WindDelta != 0 && rb.PrevInAEL.WindDelta != 0) + { + OutPt Op2 = AddOutPt(rb.PrevInAEL, rb.Bot); + AddJoin(Op1, Op2, rb.Top); + } + + TEdge e = lb.NextInAEL; + if (e != null) + while (e != rb) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the right of param2 ABOVE the intersection ... + IntersectEdges(rb, e, lb.Curr); //order important here + e = e.NextInAEL; + } + } + } + } + //------------------------------------------------------------------------------ + + private void InsertEdgeIntoAEL(TEdge edge, TEdge startEdge) + { + if (m_ActiveEdges == null) + { + edge.PrevInAEL = null; + edge.NextInAEL = null; + m_ActiveEdges = edge; + } + else if (startEdge == null && E2InsertsBeforeE1(m_ActiveEdges, edge)) + { + edge.PrevInAEL = null; + edge.NextInAEL = m_ActiveEdges; + m_ActiveEdges.PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if (startEdge == null) startEdge = m_ActiveEdges; + while (startEdge.NextInAEL != null && + !E2InsertsBeforeE1(startEdge.NextInAEL, edge)) + startEdge = startEdge.NextInAEL; + edge.NextInAEL = startEdge.NextInAEL; + if (startEdge.NextInAEL != null) startEdge.NextInAEL.PrevInAEL = edge; + edge.PrevInAEL = startEdge; + startEdge.NextInAEL = edge; + } + } + //---------------------------------------------------------------------- + + private bool E2InsertsBeforeE1(TEdge e1, TEdge e2) + { + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; + } + //------------------------------------------------------------------------------ + + private bool IsEvenOddFillType(TEdge edge) + { + if (edge.PolyTyp == PolyType.ptSubject) + return m_SubjFillType == PolyFillType.pftEvenOdd; + else + return m_ClipFillType == PolyFillType.pftEvenOdd; + } + //------------------------------------------------------------------------------ + + private bool IsEvenOddAltFillType(TEdge edge) + { + if (edge.PolyTyp == PolyType.ptSubject) + return m_ClipFillType == PolyFillType.pftEvenOdd; + else + return m_SubjFillType == PolyFillType.pftEvenOdd; + } + //------------------------------------------------------------------------------ + + private bool IsContributing(TEdge edge) + { + PolyFillType pft, pft2; + if (edge.PolyTyp == PolyType.ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } + else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch (pft) + { + case PolyFillType.pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case PolyFillType.pftNonZero: + if (Math.Abs(edge.WindCnt) != 1) return false; + break; + case PolyFillType.pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //PolyFillType.pftNegative + if (edge.WindCnt != -1) return false; + break; + } + + switch (m_ClipType) + { + case ClipType.ctIntersection: + switch (pft2) + { + case PolyFillType.pftEvenOdd: + case PolyFillType.pftNonZero: + return (edge.WindCnt2 != 0); + case PolyFillType.pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + case ClipType.ctUnion: + switch (pft2) + { + case PolyFillType.pftEvenOdd: + case PolyFillType.pftNonZero: + return (edge.WindCnt2 == 0); + case PolyFillType.pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + case ClipType.ctDifference: + if (edge.PolyTyp == PolyType.ptSubject) + switch (pft2) + { + case PolyFillType.pftEvenOdd: + case PolyFillType.pftNonZero: + return (edge.WindCnt2 == 0); + case PolyFillType.pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch (pft2) + { + case PolyFillType.pftEvenOdd: + case PolyFillType.pftNonZero: + return (edge.WindCnt2 != 0); + case PolyFillType.pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + case ClipType.ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch (pft2) + { + case PolyFillType.pftEvenOdd: + case PolyFillType.pftNonZero: + return (edge.WindCnt2 == 0); + case PolyFillType.pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + } + return true; + } + //------------------------------------------------------------------------------ + + private void SetWindingCount(TEdge edge) + { + TEdge e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e != null && ((e.PolyTyp != edge.PolyTyp) || (e.WindDelta == 0))) e = e.PrevInAEL; + if (e == null) + { + PolyFillType pft; + pft = (edge.PolyTyp == PolyType.ptSubject ? m_SubjFillType : m_ClipFillType); + if (edge.WindDelta == 0) edge.WindCnt = (pft == PolyFillType.pftNegative ? -1 : 1); + else edge.WindCnt = edge.WindDelta; + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ClipType.ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e.WindCnt2; + e = e.NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge e2 = e.PrevInAEL; + while (e2 != null) + { + if (e2.PolyTyp == e.PolyTyp && e2.WindDelta != 0) + Inside = !Inside; + e2 = e2.PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e.WindCnt2; + e = e.NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e.WindCnt * e.WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Math.Abs(e.WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e.WindDelta * edge.WindDelta < 0) edge.WindCnt = e.WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e.WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } + else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e.WindCnt < 0 ? e.WindCnt - 1 : e.WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e.WindDelta * edge.WindDelta < 0) + edge.WindCnt = e.WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e.WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e.WindCnt2; + e = e.NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != edge) + { + if (e.WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e.NextInAEL; + } + } + else + { + //nonZero, Positive or Negative filling ... + while (e != edge) + { + edge.WindCnt2 += e.WindDelta; + e = e.NextInAEL; + } + } + } + //------------------------------------------------------------------------------ + + private void AddEdgeToSEL(TEdge edge) + { + //SEL pointers in PEdge are use to build transient lists of horizontal edges. + //However, since we don't need to worry about processing order, all additions + //are made to the front of the list ... + if (m_SortedEdges == null) + { + m_SortedEdges = edge; + edge.PrevInSEL = null; + edge.NextInSEL = null; + } + else + { + edge.NextInSEL = m_SortedEdges; + edge.PrevInSEL = null; + m_SortedEdges.PrevInSEL = edge; + m_SortedEdges = edge; + } + } + //------------------------------------------------------------------------------ + + internal Boolean PopEdgeFromSEL(out TEdge e) + { + //Pop edge from front of SEL (ie SEL is a FILO list) + e = m_SortedEdges; + if (e == null) return false; + TEdge oldE = e; + m_SortedEdges = e.NextInSEL; + if (m_SortedEdges != null) m_SortedEdges.PrevInSEL = null; + oldE.NextInSEL = null; + oldE.PrevInSEL = null; + return true; + } + //------------------------------------------------------------------------------ + + private void CopyAELToSEL() + { + TEdge e = m_ActiveEdges; + m_SortedEdges = e; + while (e != null) + { + e.PrevInSEL = e.PrevInAEL; + e.NextInSEL = e.NextInAEL; + e = e.NextInAEL; + } + } + //------------------------------------------------------------------------------ + + private void SwapPositionsInSEL(TEdge edge1, TEdge edge2) + { + if (edge1.NextInSEL == null && edge1.PrevInSEL == null) + return; + if (edge2.NextInSEL == null && edge2.PrevInSEL == null) + return; + + if (edge1.NextInSEL == edge2) + { + TEdge next = edge2.NextInSEL; + if (next != null) + next.PrevInSEL = edge1; + TEdge prev = edge1.PrevInSEL; + if (prev != null) + prev.NextInSEL = edge2; + edge2.PrevInSEL = prev; + edge2.NextInSEL = edge1; + edge1.PrevInSEL = edge2; + edge1.NextInSEL = next; + } + else if (edge2.NextInSEL == edge1) + { + TEdge next = edge1.NextInSEL; + if (next != null) + next.PrevInSEL = edge2; + TEdge prev = edge2.PrevInSEL; + if (prev != null) + prev.NextInSEL = edge1; + edge1.PrevInSEL = prev; + edge1.NextInSEL = edge2; + edge2.PrevInSEL = edge1; + edge2.NextInSEL = next; + } + else + { + TEdge next = edge1.NextInSEL; + TEdge prev = edge1.PrevInSEL; + edge1.NextInSEL = edge2.NextInSEL; + if (edge1.NextInSEL != null) + edge1.NextInSEL.PrevInSEL = edge1; + edge1.PrevInSEL = edge2.PrevInSEL; + if (edge1.PrevInSEL != null) + edge1.PrevInSEL.NextInSEL = edge1; + edge2.NextInSEL = next; + if (edge2.NextInSEL != null) + edge2.NextInSEL.PrevInSEL = edge2; + edge2.PrevInSEL = prev; + if (edge2.PrevInSEL != null) + edge2.PrevInSEL.NextInSEL = edge2; + } + + if (edge1.PrevInSEL == null) + m_SortedEdges = edge1; + else if (edge2.PrevInSEL == null) + m_SortedEdges = edge2; + } + //------------------------------------------------------------------------------ + + + private void AddLocalMaxPoly(TEdge e1, TEdge e2, IntPoint pt) + { + AddOutPt(e1, pt); + if (e2.WindDelta == 0) AddOutPt(e2, pt); + if (e1.OutIdx == e2.OutIdx) + { + e1.OutIdx = Unassigned; + e2.OutIdx = Unassigned; + } + else if (e1.OutIdx < e2.OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); + } + //------------------------------------------------------------------------------ + + private OutPt AddLocalMinPoly(TEdge e1, TEdge e2, IntPoint pt) + { + OutPt result; + TEdge e, prevE; + if (IsHorizontal(e2) || (e1.Dx > e2.Dx)) + { + result = AddOutPt(e1, pt); + e2.OutIdx = e1.OutIdx; + e1.Side = EdgeSide.esLeft; + e2.Side = EdgeSide.esRight; + e = e1; + if (e.PrevInAEL == e2) + prevE = e2.PrevInAEL; + else + prevE = e.PrevInAEL; + } + else + { + result = AddOutPt(e2, pt); + e1.OutIdx = e2.OutIdx; + e1.Side = EdgeSide.esRight; + e2.Side = EdgeSide.esLeft; + e = e2; + if (e.PrevInAEL == e1) + prevE = e1.PrevInAEL; + else + prevE = e.PrevInAEL; + } + + if (prevE != null && prevE.OutIdx >= 0 && prevE.Top.Y < pt.Y && e.Top.Y < pt.Y) + { + cInt xPrev = TopX(prevE, pt.Y); + cInt xE = TopX(e, pt.Y); + if ((xPrev == xE) && (e.WindDelta != 0) && (prevE.WindDelta != 0) && + SlopesEqual(new IntPoint(xPrev, pt.Y), prevE.Top, new IntPoint(xE, pt.Y), e.Top, m_UseFullRange)) + { + OutPt outPt = AddOutPt(prevE, pt); + AddJoin(result, outPt, e.Top); + } + } + return result; + } + //------------------------------------------------------------------------------ + + private OutPt AddOutPt(TEdge e, IntPoint pt) + { + if (e.OutIdx < 0) + { + OutRec outRec = CreateOutRec(); + outRec.IsOpen = (e.WindDelta == 0); + OutPt newOp = new OutPt(); + outRec.Pts = newOp; + newOp.Idx = outRec.Idx; + newOp.Pt = pt; + newOp.Next = newOp; + newOp.Prev = newOp; + if (!outRec.IsOpen) + SetHoleState(e, outRec); + e.OutIdx = outRec.Idx; //nb: do this after SetZ ! + return newOp; + } + else + { + OutRec outRec = m_PolyOuts[e.OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt op = outRec.Pts; + bool ToFront = (e.Side == EdgeSide.esLeft); + if (ToFront && pt == op.Pt) return op; + else if (!ToFront && pt == op.Prev.Pt) return op.Prev; + + OutPt newOp = new OutPt(); + newOp.Idx = outRec.Idx; + newOp.Pt = pt; + newOp.Next = op; + newOp.Prev = op.Prev; + newOp.Prev.Next = newOp; + op.Prev = newOp; + if (ToFront) outRec.Pts = newOp; + return newOp; + } + } + //------------------------------------------------------------------------------ + + private OutPt GetLastOutPt(TEdge e) + { + OutRec outRec = m_PolyOuts[e.OutIdx]; + if (e.Side == EdgeSide.esLeft) + return outRec.Pts; + else + return outRec.Pts.Prev; + } + //------------------------------------------------------------------------------ + + internal void SwapPoints(ref IntPoint pt1, ref IntPoint pt2) + { + IntPoint tmp = new IntPoint(pt1); + pt1 = pt2; + pt2 = tmp; + } + //------------------------------------------------------------------------------ + + private bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) + { + if (seg1a > seg1b) Swap(ref seg1a, ref seg1b); + if (seg2a > seg2b) Swap(ref seg2a, ref seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); + } + //------------------------------------------------------------------------------ + + private void SetHoleState(TEdge e, OutRec outRec) + { + TEdge e2 = e.PrevInAEL; + TEdge eTmp = null; + while (e2 != null) + { + if (e2.OutIdx >= 0 && e2.WindDelta != 0) + { + if (eTmp == null) + eTmp = e2; + else if (eTmp.OutIdx == e2.OutIdx) + eTmp = null; //paired + } + e2 = e2.PrevInAEL; + } + + if (eTmp == null) + { + outRec.FirstLeft = null; + outRec.IsHole = false; + } + else + { + outRec.FirstLeft = m_PolyOuts[eTmp.OutIdx]; + outRec.IsHole = !outRec.FirstLeft.IsHole; + } + } + //------------------------------------------------------------------------------ + + private double GetDx(IntPoint pt1, IntPoint pt2) + { + if (pt1.Y == pt2.Y) return horizontal; + else return (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); + } + //--------------------------------------------------------------------------- + + private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2) + { + OutPt p = btmPt1.Prev; + while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Prev; + double dx1p = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); + p = btmPt1.Next; + while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Next; + double dx1n = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); + + p = btmPt2.Prev; + while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Prev; + double dx2p = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); + p = btmPt2.Next; + while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Next; + double dx2n = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); + + if (Math.Max(dx1p, dx1n) == Math.Max(dx2p, dx2n) && + Math.Min(dx1p, dx1n) == Math.Min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); + } + //------------------------------------------------------------------------------ + + private OutPt GetBottomPt(OutPt pp) + { + OutPt dups = null; + OutPt p = pp.Next; + while (p != pp) + { + if (p.Pt.Y > pp.Pt.Y) + { + pp = p; + dups = null; + } + else if (p.Pt.Y == pp.Pt.Y && p.Pt.X <= pp.Pt.X) + { + if (p.Pt.X < pp.Pt.X) + { + dups = null; + pp = p; + } else + { + if (p.Next != pp && p.Prev != pp) dups = p; + } + } + p = p.Next; + } + if (dups != null) + { + //there appears to be at least 2 vertices at bottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups.Next; + while (dups.Pt != pp.Pt) dups = dups.Next; + } + } + return pp; + } + //------------------------------------------------------------------------------ + + private OutRec GetLowermostRec(OutRec outRec1, OutRec outRec2) + { + //work out which polygon fragment has the correct hole state ... + if (outRec1.BottomPt == null) + outRec1.BottomPt = GetBottomPt(outRec1.Pts); + if (outRec2.BottomPt == null) + outRec2.BottomPt = GetBottomPt(outRec2.Pts); + OutPt bPt1 = outRec1.BottomPt; + OutPt bPt2 = outRec2.BottomPt; + if (bPt1.Pt.Y > bPt2.Pt.Y) return outRec1; + else if (bPt1.Pt.Y < bPt2.Pt.Y) return outRec2; + else if (bPt1.Pt.X < bPt2.Pt.X) return outRec1; + else if (bPt1.Pt.X > bPt2.Pt.X) return outRec2; + else if (bPt1.Next == bPt1) return outRec2; + else if (bPt2.Next == bPt2) return outRec1; + else if (FirstIsBottomPt(bPt1, bPt2)) return outRec1; + else return outRec2; + } + //------------------------------------------------------------------------------ + + bool OutRec1RightOfOutRec2(OutRec outRec1, OutRec outRec2) + { + do + { + outRec1 = outRec1.FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1 != null); + return false; + } + //------------------------------------------------------------------------------ + + private OutRec GetOutRec(int idx) + { + OutRec outrec = m_PolyOuts[idx]; + while (outrec != m_PolyOuts[outrec.Idx]) + outrec = m_PolyOuts[outrec.Idx]; + return outrec; + } + //------------------------------------------------------------------------------ + + private void AppendPolygon(TEdge e1, TEdge e2) + { + OutRec outRec1 = m_PolyOuts[e1.OutIdx]; + OutRec outRec2 = m_PolyOuts[e2.OutIdx]; + + OutRec holeStateRec; + if (OutRec1RightOfOutRec2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join E2 poly onto E1 poly and delete pointers to E2 ... + OutPt p1_lft = outRec1.Pts; + OutPt p1_rt = p1_lft.Prev; + OutPt p2_lft = outRec2.Pts; + OutPt p2_rt = p2_lft.Prev; + + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1.Side == EdgeSide.esLeft ) + { + if (e2.Side == EdgeSide.esLeft) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft.Next = p1_lft; + p1_lft.Prev = p2_lft; + p1_rt.Next = p2_rt; + p2_rt.Prev = p1_rt; + outRec1.Pts = p2_rt; + } else + { + //x y z a b c + p2_rt.Next = p1_lft; + p1_lft.Prev = p2_rt; + p2_lft.Prev = p1_rt; + p1_rt.Next = p2_lft; + outRec1.Pts = p2_lft; + } + } else + { + if (e2.Side == EdgeSide.esRight) + { + //a b c z y x + ReversePolyPtLinks( p2_lft ); + p1_rt.Next = p2_rt; + p2_rt.Prev = p1_rt; + p2_lft.Next = p1_lft; + p1_lft.Prev = p2_lft; + } else + { + //a b c x y z + p1_rt.Next = p2_lft; + p2_lft.Prev = p1_rt; + p1_lft.Prev = p2_rt; + p2_rt.Next = p1_lft; + } + } + + outRec1.BottomPt = null; + if (holeStateRec == outRec2) + { + if (outRec2.FirstLeft != outRec1) + outRec1.FirstLeft = outRec2.FirstLeft; + outRec1.IsHole = outRec2.IsHole; + } + outRec2.Pts = null; + outRec2.BottomPt = null; + + outRec2.FirstLeft = outRec1; + + int OKIdx = e1.OutIdx; + int ObsoleteIdx = e2.OutIdx; + + e1.OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2.OutIdx = Unassigned; + + TEdge e = m_ActiveEdges; + while( e != null ) + { + if( e.OutIdx == ObsoleteIdx ) + { + e.OutIdx = OKIdx; + e.Side = e1.Side; + break; + } + e = e.NextInAEL; + } + outRec2.Idx = outRec1.Idx; + } + //------------------------------------------------------------------------------ + + private void ReversePolyPtLinks(OutPt pp) + { + if (pp == null) return; + OutPt pp1; + OutPt pp2; + pp1 = pp; + do + { + pp2 = pp1.Next; + pp1.Next = pp1.Prev; + pp1.Prev = pp2; + pp1 = pp2; + } while (pp1 != pp); + } + //------------------------------------------------------------------------------ + + private static void SwapSides(TEdge edge1, TEdge edge2) + { + EdgeSide side = edge1.Side; + edge1.Side = edge2.Side; + edge2.Side = side; + } + //------------------------------------------------------------------------------ + + private static void SwapPolyIndexes(TEdge edge1, TEdge edge2) + { + int outIdx = edge1.OutIdx; + edge1.OutIdx = edge2.OutIdx; + edge2.OutIdx = outIdx; + } + //------------------------------------------------------------------------------ + + private void IntersectEdges(TEdge e1, TEdge e2, IntPoint pt) + { + //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before + //e2 in AEL except when e1 is being inserted at the intersection point ... + + bool e1Contributing = (e1.OutIdx >= 0); + bool e2Contributing = (e2.OutIdx >= 0); + +#if use_xyz + SetZ(ref pt, e1, e2); +#endif + +#if use_lines + //if either edge is on an OPEN path ... + if (e1.WindDelta == 0 || e2.WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1.WindDelta == 0 && e2.WindDelta == 0) return; + //if intersecting a subj line with a subj poly ... + else if (e1.PolyTyp == e2.PolyTyp && + e1.WindDelta != e2.WindDelta && m_ClipType == ClipType.ctUnion) + { + if (e1.WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, pt); + if (e1Contributing) e1.OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, pt); + if (e2Contributing) e2.OutIdx = Unassigned; + } + } + } + else if (e1.PolyTyp != e2.PolyTyp) + { + if ((e1.WindDelta == 0) && Math.Abs(e2.WindCnt) == 1 && + (m_ClipType != ClipType.ctUnion || e2.WindCnt2 == 0)) + { + AddOutPt(e1, pt); + if (e1Contributing) e1.OutIdx = Unassigned; + } + else if ((e2.WindDelta == 0) && (Math.Abs(e1.WindCnt) == 1) && + (m_ClipType != ClipType.ctUnion || e1.WindCnt2 == 0)) + { + AddOutPt(e2, pt); + if (e2Contributing) e2.OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if (e1.PolyTyp == e2.PolyTyp) + { + if (IsEvenOddFillType(e1)) + { + int oldE1WindCnt = e1.WindCnt; + e1.WindCnt = e2.WindCnt; + e2.WindCnt = oldE1WindCnt; + } + else + { + if (e1.WindCnt + e2.WindDelta == 0) e1.WindCnt = -e1.WindCnt; + else e1.WindCnt += e2.WindDelta; + if (e2.WindCnt - e1.WindDelta == 0) e2.WindCnt = -e2.WindCnt; + else e2.WindCnt -= e1.WindDelta; + } + } + else + { + if (!IsEvenOddFillType(e2)) e1.WindCnt2 += e2.WindDelta; + else e1.WindCnt2 = (e1.WindCnt2 == 0) ? 1 : 0; + if (!IsEvenOddFillType(e1)) e2.WindCnt2 -= e1.WindDelta; + else e2.WindCnt2 = (e2.WindCnt2 == 0) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1.PolyTyp == PolyType.ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } + else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2.PolyTyp == PolyType.ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } + else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + int e1Wc, e2Wc; + switch (e1FillType) + { + case PolyFillType.pftPositive: e1Wc = e1.WindCnt; break; + case PolyFillType.pftNegative: e1Wc = -e1.WindCnt; break; + default: e1Wc = Math.Abs(e1.WindCnt); break; + } + switch (e2FillType) + { + case PolyFillType.pftPositive: e2Wc = e2.WindCnt; break; + case PolyFillType.pftNegative: e2Wc = -e2.WindCnt; break; + default: e2Wc = Math.Abs(e2.WindCnt); break; + } + + if (e1Contributing && e2Contributing) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1.PolyTyp != e2.PolyTyp && m_ClipType != ClipType.ctXor)) + { + AddLocalMaxPoly(e1, e2, pt); + } + else + { + AddOutPt(e1, pt); + AddOutPt(e2, pt); + SwapSides(e1, e2); + SwapPolyIndexes(e1, e2); + } + } + else if (e1Contributing) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, pt); + SwapSides(e1, e2); + SwapPolyIndexes(e1, e2); + } + + } + else if (e2Contributing) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, pt); + SwapSides(e1, e2); + SwapPolyIndexes(e1, e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case PolyFillType.pftPositive: e1Wc2 = e1.WindCnt2; break; + case PolyFillType.pftNegative: e1Wc2 = -e1.WindCnt2; break; + default: e1Wc2 = Math.Abs(e1.WindCnt2); break; + } + switch (e2FillType2) + { + case PolyFillType.pftPositive: e2Wc2 = e2.WindCnt2; break; + case PolyFillType.pftNegative: e2Wc2 = -e2.WindCnt2; break; + default: e2Wc2 = Math.Abs(e2.WindCnt2); break; + } + + if (e1.PolyTyp != e2.PolyTyp) + { + AddLocalMinPoly(e1, e2, pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch (m_ClipType) + { + case ClipType.ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, pt); + break; + case ClipType.ctUnion: + if (e1Wc2 <= 0 && e2Wc2 <= 0) + AddLocalMinPoly(e1, e2, pt); + break; + case ClipType.ctDifference: + if (((e1.PolyTyp == PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1.PolyTyp == PolyType.ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, pt); + break; + case ClipType.ctXor: + AddLocalMinPoly(e1, e2, pt); + break; + } + else + SwapSides(e1, e2); + } + } + //------------------------------------------------------------------------------ + + private void DeleteFromSEL(TEdge e) + { + TEdge SelPrev = e.PrevInSEL; + TEdge SelNext = e.NextInSEL; + if (SelPrev == null && SelNext == null && (e != m_SortedEdges)) + return; //already deleted + if (SelPrev != null) + SelPrev.NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if (SelNext != null) + SelNext.PrevInSEL = SelPrev; + e.NextInSEL = null; + e.PrevInSEL = null; + } + //------------------------------------------------------------------------------ + + private void ProcessHorizontals() + { + TEdge horzEdge; //m_SortedEdges; + while (PopEdgeFromSEL(out horzEdge)) + ProcessHorizontal(horzEdge); + } + //------------------------------------------------------------------------------ + + void GetHorzDirection(TEdge HorzEdge, out Direction Dir, out cInt Left, out cInt Right) + { + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = Direction.dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = Direction.dRightToLeft; + } + } + //------------------------------------------------------------------------ + + private void ProcessHorizontal(TEdge horzEdge) + { + Direction dir; + cInt horzLeft, horzRight; + bool IsOpen = horzEdge.WindDelta == 0; + + GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); + + TEdge eLastHorz = horzEdge, eMaxPair = null; + while (eLastHorz.NextInLML != null && IsHorizontal(eLastHorz.NextInLML)) + eLastHorz = eLastHorz.NextInLML; + if (eLastHorz.NextInLML == null) + eMaxPair = GetMaximaPair(eLastHorz); + + Maxima currMax = m_Maxima; + if (currMax != null) + { + //get the first maxima in range (X) ... + if (dir == Direction.dLeftToRight) + { + while (currMax != null && currMax.X <= horzEdge.Bot.X) + currMax = currMax.Next; + if (currMax != null && currMax.X >= eLastHorz.Top.X) + currMax = null; + } + else + { + while (currMax.Next != null && currMax.Next.X < horzEdge.Bot.X) + currMax = currMax.Next; + if (currMax.X <= eLastHorz.Top.X) currMax = null; + } + } + + OutPt op1 = null; + for (;;) //loop through consec. horizontal edges + { + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge e = GetNextInAEL(horzEdge, dir); + while(e != null) + { + + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (currMax != null) + { + if (dir == Direction.dLeftToRight) + { + while (currMax != null && currMax.X < e.Curr.X) + { + if (horzEdge.OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, new IntPoint(currMax.X, horzEdge.Bot.Y)); + currMax = currMax.Next; + } + } + else + { + while (currMax != null && currMax.X > e.Curr.X) + { + if (horzEdge.OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, new IntPoint(currMax.X, horzEdge.Bot.Y)); + currMax = currMax.Prev; + } + } + }; + + if ((dir == Direction.dLeftToRight && e.Curr.X > horzRight) || + (dir == Direction.dRightToLeft && e.Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML != null && + e.Dx < horzEdge.NextInLML.Dx) break; + + if (horzEdge.OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { +#if use_xyz + if (dir == Direction.dLeftToRight) SetZ(ref e.Curr, horzEdge, e); + else SetZ(ref e.Curr, e, horzEdge); +#endif + + op1 = AddOutPt(horzEdge, e.Curr); + TEdge eNextHorz = m_SortedEdges; + while (eNextHorz != null) + { + if (eNextHorz.OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge.Bot.X, + horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X)) + { + OutPt op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz.Top); + } + eNextHorz = eNextHorz.NextInSEL; + } + AddGhostJoin(op1, horzEdge.Bot); + } + + //OK, so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (horzEdge.OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge.Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + + if(dir == Direction.dLeftToRight) + { + IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y); + IntersectEdges(e, horzEdge, Pt); + } + TEdge eNext = GetNextInAEL(e, dir); + SwapPositionsInAEL(horzEdge, e); + e = eNext; + } //end while(e != null) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (horzEdge.NextInLML == null || !IsHorizontal(horzEdge.NextInLML)) break; + + UpdateEdgeIntoAEL(ref horzEdge); + if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Bot); + GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); + + } //end for (;;) + + if (horzEdge.OutIdx >= 0 && op1 == null) + { + op1 = GetLastOutPt(horzEdge); + TEdge eNextHorz = m_SortedEdges; + while (eNextHorz != null) + { + if (eNextHorz.OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge.Bot.X, + horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X)) + { + OutPt op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz.Top); + } + eNextHorz = eNextHorz.NextInSEL; + } + AddGhostJoin(op1, horzEdge.Top); + } + + if (horzEdge.NextInLML != null) + { + if(horzEdge.OutIdx >= 0) + { + op1 = AddOutPt( horzEdge, horzEdge.Top); + + UpdateEdgeIntoAEL(ref horzEdge); + if (horzEdge.WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge ePrev = horzEdge.PrevInAEL; + TEdge eNext = horzEdge.NextInAEL; + if (ePrev != null && ePrev.Curr.X == horzEdge.Bot.X && + ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta != 0 && + (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && + SlopesEqual(horzEdge, ePrev, m_UseFullRange))) + { + OutPt op2 = AddOutPt(ePrev, horzEdge.Bot); + AddJoin(op1, op2, horzEdge.Top); + } + else if (eNext != null && eNext.Curr.X == horzEdge.Bot.X && + eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta != 0 && + eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && + SlopesEqual(horzEdge, eNext, m_UseFullRange)) + { + OutPt op2 = AddOutPt(eNext, horzEdge.Bot); + AddJoin(op1, op2, horzEdge.Top); + } + } + else + UpdateEdgeIntoAEL(ref horzEdge); + } + else + { + if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Top); + DeleteFromAEL(horzEdge); + } + } + //------------------------------------------------------------------------------ + + private TEdge GetNextInAEL(TEdge e, Direction Direction) + { + return Direction == Direction.dLeftToRight ? e.NextInAEL: e.PrevInAEL; + } + //------------------------------------------------------------------------------ + + private bool IsMinima(TEdge e) + { + return e != null && (e.Prev.NextInLML != e) && (e.Next.NextInLML != e); + } + //------------------------------------------------------------------------------ + + private bool IsMaxima(TEdge e, double Y) + { + return (e != null && e.Top.Y == Y && e.NextInLML == null); + } + //------------------------------------------------------------------------------ + + private bool IsIntermediate(TEdge e, double Y) + { + return (e.Top.Y == Y && e.NextInLML != null); + } + //------------------------------------------------------------------------------ + + internal TEdge GetMaximaPair(TEdge e) + { + if ((e.Next.Top == e.Top) && e.Next.NextInLML == null) + return e.Next; + else if ((e.Prev.Top == e.Top) && e.Prev.NextInLML == null) + return e.Prev; + else + return null; + } + //------------------------------------------------------------------------------ + + internal TEdge GetMaximaPairEx(TEdge e) + { + //as above but returns null if MaxPair isn't in AEL (unless it's horizontal) + TEdge result = GetMaximaPair(e); + if (result == null || result.OutIdx == Skip || + ((result.NextInAEL == result.PrevInAEL) && !IsHorizontal(result))) return null; + return result; + } + //------------------------------------------------------------------------------ + + private bool ProcessIntersections(cInt topY) + { + if( m_ActiveEdges == null ) return true; + try { + BuildIntersectList(topY); + if ( m_IntersectList.Count == 0) return true; + if (m_IntersectList.Count == 1 || FixupIntersectionOrder()) + ProcessIntersectList(); + else + return false; + } + catch { + m_SortedEdges = null; + m_IntersectList.Clear(); + throw new ClipperException("ProcessIntersections error"); + } + m_SortedEdges = null; + return true; + } + //------------------------------------------------------------------------------ + + private void BuildIntersectList(cInt topY) + { + if ( m_ActiveEdges == null ) return; + + //prepare for sorting ... + TEdge e = m_ActiveEdges; + m_SortedEdges = e; + while( e != null ) + { + e.PrevInSEL = e.PrevInAEL; + e.NextInSEL = e.NextInAEL; + e.Curr.X = TopX( e, topY ); + e = e.NextInAEL; + } + + //bubblesort ... + bool isModified = true; + while( isModified && m_SortedEdges != null ) + { + isModified = false; + e = m_SortedEdges; + while( e.NextInSEL != null ) + { + TEdge eNext = e.NextInSEL; + IntPoint pt; + if (e.Curr.X > eNext.Curr.X) + { + IntersectPoint(e, eNext, out pt); + if (pt.Y < topY) + pt = new IntPoint(TopX(e, topY), topY); + IntersectNode newNode = new IntersectNode(); + newNode.Edge1 = e; + newNode.Edge2 = eNext; + newNode.Pt = pt; + m_IntersectList.Add(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e.PrevInSEL != null ) e.PrevInSEL.NextInSEL = null; + else break; + } + m_SortedEdges = null; + } + //------------------------------------------------------------------------------ + + private bool EdgesAdjacent(IntersectNode inode) + { + return (inode.Edge1.NextInSEL == inode.Edge2) || + (inode.Edge1.PrevInSEL == inode.Edge2); + } + //------------------------------------------------------------------------------ + + private static int IntersectNodeSort(IntersectNode node1, IntersectNode node2) + { + //the following typecast is safe because the differences in Pt.Y will + //be limited to the height of the scanbeam. + return (int)(node2.Pt.Y - node1.Pt.Y); + } + //------------------------------------------------------------------------------ + + private bool FixupIntersectionOrder() + { + //pre-condition: intersections are sorted bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + m_IntersectList.Sort(m_IntersectNodeComparer); + + CopyAELToSEL(); + int cnt = m_IntersectList.Count; + for (int i = 0; i < cnt; i++) + { + if (!EdgesAdjacent(m_IntersectList[i])) + { + int j = i + 1; + while (j < cnt && !EdgesAdjacent(m_IntersectList[j])) j++; + if (j == cnt) return false; + + IntersectNode tmp = m_IntersectList[i]; + m_IntersectList[i] = m_IntersectList[j]; + m_IntersectList[j] = tmp; + + } + SwapPositionsInSEL(m_IntersectList[i].Edge1, m_IntersectList[i].Edge2); + } + return true; + } + //------------------------------------------------------------------------------ + + private void ProcessIntersectList() + { + for (int i = 0; i < m_IntersectList.Count; i++) + { + IntersectNode iNode = m_IntersectList[i]; + { + IntersectEdges(iNode.Edge1, iNode.Edge2, iNode.Pt); + SwapPositionsInAEL(iNode.Edge1, iNode.Edge2); + } + } + m_IntersectList.Clear(); + } + //------------------------------------------------------------------------------ + + internal static cInt Round(double value) + { + return value < 0 ? (cInt)(value - 0.5) : (cInt)(value + 0.5); + } + //------------------------------------------------------------------------------ + + private static cInt TopX(TEdge edge, cInt currentY) + { + if (currentY == edge.Top.Y) + return edge.Top.X; + return edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); + } + //------------------------------------------------------------------------------ + + private void IntersectPoint(TEdge edge1, TEdge edge2, out IntPoint ip) + { + ip = new IntPoint(); + double b1, b2; + //nb: with very large coordinate values, it's possible for SlopesEqual() to + //return false but for the edge.Dx value be equal due to double precision rounding. + if (edge1.Dx == edge2.Dx) + { + ip.Y = edge1.Curr.Y; + ip.X = TopX(edge1, ip.Y); + return; + } + + if (edge1.Delta.X == 0) + { + ip.X = edge1.Bot.X; + if (IsHorizontal(edge2)) + { + ip.Y = edge2.Bot.Y; + } + else + { + b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx); + ip.Y = Round(ip.X / edge2.Dx + b2); + } + } + else if (edge2.Delta.X == 0) + { + ip.X = edge2.Bot.X; + if (IsHorizontal(edge1)) + { + ip.Y = edge1.Bot.Y; + } + else + { + b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx); + ip.Y = Round(ip.X / edge1.Dx + b1); + } + } + else + { + b1 = edge1.Bot.X - edge1.Bot.Y * edge1.Dx; + b2 = edge2.Bot.X - edge2.Bot.Y * edge2.Dx; + double q = (b2 - b1) / (edge1.Dx - edge2.Dx); + ip.Y = Round(q); + if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx)) + ip.X = Round(edge1.Dx * q + b1); + else + ip.X = Round(edge2.Dx * q + b2); + } + + if (ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y) + { + if (edge1.Top.Y > edge2.Top.Y) + ip.Y = edge1.Top.Y; + else + ip.Y = edge2.Top.Y; + if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx)) + ip.X = TopX(edge1, ip.Y); + else + ip.X = TopX(edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > edge1.Curr.Y) + { + ip.Y = edge1.Curr.Y; + //better to use the more vertical edge to derive X ... + if (Math.Abs(edge1.Dx) > Math.Abs(edge2.Dx)) + ip.X = TopX(edge2, ip.Y); + else + ip.X = TopX(edge1, ip.Y); + } + } + //------------------------------------------------------------------------------ + + private void ProcessEdgesAtTopOfScanbeam(cInt topY) + { + TEdge e = m_ActiveEdges; + while(e != null) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge eMaxPair = GetMaximaPairEx(e); + IsMaximaEdge = (eMaxPair == null || !IsHorizontal(eMaxPair)); + } + + if(IsMaximaEdge) + { + if (StrictlySimple) InsertMaxima(e.Top.X); + TEdge ePrev = e.PrevInAEL; + DoMaxima(e); + if( ePrev == null) e = m_ActiveEdges; + else e = ePrev.NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(e.NextInLML)) + { + UpdateEdgeIntoAEL(ref e); + if (e.OutIdx >= 0) + AddOutPt(e, e.Bot); + AddEdgeToSEL(e); + } + else + { + e.Curr.X = TopX( e, topY ); + e.Curr.Y = topY; +#if use_xyz + if (e.Top.Y == topY) e.Curr.Z = e.Top.Z; + else if (e.Bot.Y == topY) e.Curr.Z = e.Bot.Z; + else e.Curr.Z = 0; +#endif + } + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... + if (StrictlySimple) + { + TEdge ePrev = e.PrevInAEL; + if ((e.OutIdx >= 0) && (e.WindDelta != 0) && ePrev != null && + (ePrev.OutIdx >= 0) && (ePrev.Curr.X == e.Curr.X) && + (ePrev.WindDelta != 0)) + { + IntPoint ip = new IntPoint(e.Curr); +#if use_xyz + SetZ(ref ip, ePrev, e); +#endif + OutPt op = AddOutPt(ePrev, ip); + OutPt op2 = AddOutPt(e, ip); + AddJoin(op, op2, ip); //StrictlySimple (type-3) join + } + } + + e = e.NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + ProcessHorizontals(); + m_Maxima = null; + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while (e != null) + { + if(IsIntermediate(e, topY)) + { + OutPt op = null; + if( e.OutIdx >= 0 ) + op = AddOutPt(e, e.Top); + UpdateEdgeIntoAEL(ref e); + + //if output polygons share an edge, they'll need joining later ... + TEdge ePrev = e.PrevInAEL; + TEdge eNext = e.NextInAEL; + if (ePrev != null && ePrev.Curr.X == e.Bot.X && + ePrev.Curr.Y == e.Bot.Y && op != null && + ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && + SlopesEqual(e.Curr, e.Top, ePrev.Curr, ePrev.Top, m_UseFullRange) && + (e.WindDelta != 0) && (ePrev.WindDelta != 0)) + { + OutPt op2 = AddOutPt(ePrev, e.Bot); + AddJoin(op, op2, e.Top); + } + else if (eNext != null && eNext.Curr.X == e.Bot.X && + eNext.Curr.Y == e.Bot.Y && op != null && + eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && + SlopesEqual(e.Curr, e.Top, eNext.Curr, eNext.Top, m_UseFullRange) && + (e.WindDelta != 0) && (eNext.WindDelta != 0)) + { + OutPt op2 = AddOutPt(eNext, e.Bot); + AddJoin(op, op2, e.Top); + } + } + e = e.NextInAEL; + } + } + //------------------------------------------------------------------------------ + + private void DoMaxima(TEdge e) + { + TEdge eMaxPair = GetMaximaPairEx(e); + if (eMaxPair == null) + { + if (e.OutIdx >= 0) + AddOutPt(e, e.Top); + DeleteFromAEL(e); + return; + } + + TEdge eNext = e.NextInAEL; + while(eNext != null && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e.Top); + SwapPositionsInAEL(e, eNext); + eNext = e.NextInAEL; + } + + if(e.OutIdx == Unassigned && eMaxPair.OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e.OutIdx >= 0 && eMaxPair.OutIdx >= 0 ) + { + if (e.OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e.Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#if use_lines + else if (e.WindDelta == 0) + { + if (e.OutIdx >= 0) + { + AddOutPt(e, e.Top); + e.OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair.OutIdx >= 0) + { + AddOutPt(eMaxPair, e.Top); + eMaxPair.OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw new ClipperException("DoMaxima error"); + } + //------------------------------------------------------------------------------ + + public static void ReversePaths(Paths polys) + { + foreach (var poly in polys) { poly.Reverse(); } + } + //------------------------------------------------------------------------------ + + public static bool Orientation(Path poly) + { + return Area(poly) >= 0; + } + //------------------------------------------------------------------------------ + + private int PointCount(OutPt pts) + { + if (pts == null) return 0; + int result = 0; + OutPt p = pts; + do + { + result++; + p = p.Next; + } + while (p != pts); + return result; + } + //------------------------------------------------------------------------------ + + private void BuildResult(Paths polyg) + { + polyg.Clear(); + polyg.Capacity = m_PolyOuts.Count; + for (int i = 0; i < m_PolyOuts.Count; i++) + { + OutRec outRec = m_PolyOuts[i]; + if (outRec.Pts == null) continue; + OutPt p = outRec.Pts.Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + Path pg = new Path(cnt); + for (int j = 0; j < cnt; j++) + { + pg.Add(p.Pt); + p = p.Prev; + } + polyg.Add(pg); + } + } + //------------------------------------------------------------------------------ + + private void BuildResult2(PolyTree polytree) + { + polytree.Clear(); + + //add each output polygon/contour to polytree ... + polytree.m_AllPolys.Capacity = m_PolyOuts.Count; + for (int i = 0; i < m_PolyOuts.Count; i++) + { + OutRec outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec.Pts); + if ((outRec.IsOpen && cnt < 2) || + (!outRec.IsOpen && cnt < 3)) continue; + FixHoleLinkage(outRec); + PolyNode pn = new PolyNode(); + polytree.m_AllPolys.Add(pn); + outRec.PolyNode = pn; + pn.m_polygon.Capacity = cnt; + OutPt op = outRec.Pts.Prev; + for (int j = 0; j < cnt; j++) + { + pn.m_polygon.Add(op.Pt); + op = op.Prev; + } + } + + //fixup PolyNode links etc ... + polytree.m_Childs.Capacity = m_PolyOuts.Count; + for (int i = 0; i < m_PolyOuts.Count; i++) + { + OutRec outRec = m_PolyOuts[i]; + if (outRec.PolyNode == null) continue; + else if (outRec.IsOpen) + { + outRec.PolyNode.IsOpen = true; + polytree.AddChild(outRec.PolyNode); + } + else if (outRec.FirstLeft != null && + outRec.FirstLeft.PolyNode != null) + outRec.FirstLeft.PolyNode.AddChild(outRec.PolyNode); + else + polytree.AddChild(outRec.PolyNode); + } + } + //------------------------------------------------------------------------------ + + private void FixupOutPolyline(OutRec outrec) + { + OutPt pp = outrec.Pts; + OutPt lastPP = pp.Prev; + while (pp != lastPP) + { + pp = pp.Next; + if (pp.Pt == pp.Prev.Pt) + { + if (pp == lastPP) lastPP = pp.Prev; + OutPt tmpPP = pp.Prev; + tmpPP.Next = pp.Next; + pp.Next.Prev = tmpPP; + pp = tmpPP; + } + } + if (pp == pp.Prev) outrec.Pts = null; + } + //------------------------------------------------------------------------------ + + private void FixupOutPolygon(OutRec outRec) + { + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt lastOK = null; + outRec.BottomPt = null; + OutPt pp = outRec.Pts; + bool preserveCol = PreserveCollinear || StrictlySimple; + for (;;) + { + if (pp.Prev == pp || pp.Prev == pp.Next) + { + outRec.Pts = null; + return; + } + //test for duplicate points and collinear edges ... + if ((pp.Pt == pp.Next.Pt) || (pp.Pt == pp.Prev.Pt) || + (SlopesEqual(pp.Prev.Pt, pp.Pt, pp.Next.Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp.Prev.Pt, pp.Pt, pp.Next.Pt)))) + { + lastOK = null; + pp.Prev.Next = pp.Next; + pp.Next.Prev = pp.Prev; + pp = pp.Prev; + } + else if (pp == lastOK) break; + else + { + if (lastOK == null) lastOK = pp; + pp = pp.Next; + } + } + outRec.Pts = pp; + } + //------------------------------------------------------------------------------ + + OutPt DupOutPt(OutPt outPt, bool InsertAfter) + { + OutPt result = new OutPt(); + result.Pt = outPt.Pt; + result.Idx = outPt.Idx; + if (InsertAfter) + { + result.Next = outPt.Next; + result.Prev = outPt; + outPt.Next.Prev = result; + outPt.Next = result; + } + else + { + result.Prev = outPt.Prev; + result.Next = outPt; + outPt.Prev.Next = result; + outPt.Prev = result; + } + return result; + } + //------------------------------------------------------------------------------ + + bool GetOverlap(cInt a1, cInt a2, cInt b1, cInt b2, out cInt Left, out cInt Right) + { + if (a1 < a2) + { + if (b1 < b2) {Left = Math.Max(a1,b1); Right = Math.Min(a2,b2);} + else {Left = Math.Max(a1,b2); Right = Math.Min(a2,b1);} + } + else + { + if (b1 < b2) {Left = Math.Max(a2,b1); Right = Math.Min(a1,b2);} + else { Left = Math.Max(a2, b2); Right = Math.Min(a1, b1); } + } + return Left < Right; + } + //------------------------------------------------------------------------------ + + bool JoinHorz(OutPt op1, OutPt op1b, OutPt op2, OutPt op2b, + IntPoint Pt, bool DiscardLeft) + { + Direction Dir1 = (op1.Pt.X > op1b.Pt.X ? + Direction.dRightToLeft : Direction.dLeftToRight); + Direction Dir2 = (op2.Pt.X > op2b.Pt.X ? + Direction.dRightToLeft : Direction.dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == Direction.dLeftToRight) + { + while (op1.Next.Pt.X <= Pt.X && + op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == Pt.Y) + op1 = op1.Next; + if (DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b.Pt != Pt) + { + op1 = op1b; + op1.Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1.Next.Pt.X >= Pt.X && + op1.Next.Pt.X <= op1.Pt.X && op1.Next.Pt.Y == Pt.Y) + op1 = op1.Next; + if (!DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b.Pt != Pt) + { + op1 = op1b; + op1.Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == Direction.dLeftToRight) + { + while (op2.Next.Pt.X <= Pt.X && + op2.Next.Pt.X >= op2.Pt.X && op2.Next.Pt.Y == Pt.Y) + op2 = op2.Next; + if (DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b.Pt != Pt) + { + op2 = op2b; + op2.Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2.Next.Pt.X >= Pt.X && + op2.Next.Pt.X <= op2.Pt.X && op2.Next.Pt.Y == Pt.Y) + op2 = op2.Next; + if (!DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b.Pt != Pt) + { + op2 = op2b; + op2.Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == Direction.dLeftToRight) == DiscardLeft) + { + op1.Prev = op2; + op2.Next = op1; + op1b.Next = op2b; + op2b.Prev = op1b; + } + else + { + op1.Next = op2; + op2.Prev = op1; + op1b.Prev = op2b; + op2b.Next = op1b; + } + return true; + } + //------------------------------------------------------------------------------ + + private bool JoinPoints(Join j, OutRec outRec1, OutRec outRec2) + { + OutPt op1 = j.OutPt1, op1b; + OutPt op2 = j.OutPt2, op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictlySimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j.OutPt1.Pt.Y == j.OffPt.Y); + + if (isHorizontal && (j.OffPt == j.OutPt1.Pt) && (j.OffPt == j.OutPt2.Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j.OutPt1.Next; + while (op1b != op1 && (op1b.Pt == j.OffPt)) + op1b = op1b.Next; + bool reverse1 = (op1b.Pt.Y > j.OffPt.Y); + op2b = j.OutPt2.Next; + while (op2b != op2 && (op2b.Pt == j.OffPt)) + op2b = op2b.Next; + bool reverse2 = (op2b.Pt.Y > j.OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1.Prev = op2; + op2.Next = op1; + op1b.Next = op2b; + op2b.Prev = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1.Next = op2; + op2.Prev = op1; + op1b.Prev = op2b; + op2b.Next = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2) + op1 = op1.Prev; + while (op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2) + op1b = op1b.Next; + if (op1b.Next == op1 || op1b.Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b) + op2 = op2.Prev; + while (op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1) + op2b = op2b.Next; + if (op2b.Next == op2 || op2b.Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, out Left, out Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1.Pt.X >= Left && op1.Pt.X <= Right) + { + Pt = op1.Pt; DiscardLeftSide = (op1.Pt.X > op1b.Pt.X); + } + else if (op2.Pt.X >= Left&& op2.Pt.X <= Right) + { + Pt = op2.Pt; DiscardLeftSide = (op2.Pt.X > op2b.Pt.X); + } + else if (op1b.Pt.X >= Left && op1b.Pt.X <= Right) + { + Pt = op1b.Pt; DiscardLeftSide = op1b.Pt.X > op1.Pt.X; + } + else + { + Pt = op2b.Pt; DiscardLeftSide = (op2b.Pt.X > op2.Pt.X); + } + j.OutPt1 = op1; + j.OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1.Next; + while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Next; + bool Reverse1 = ((op1b.Pt.Y > op1.Pt.Y) || + !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1.Prev; + while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Prev; + if ((op1b.Pt.Y > op1.Pt.Y) || + !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange)) return false; + }; + op2b = op2.Next; + while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Next; + bool Reverse2 = ((op2b.Pt.Y > op2.Pt.Y) || + !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2.Prev; + while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Prev; + if ((op2b.Pt.Y > op2.Pt.Y) || + !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1.Prev = op2; + op2.Next = op1; + op1b.Next = op2b; + op2b.Prev = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1.Next = op2; + op2.Prev = op1; + op1b.Prev = op2b; + op2b.Next = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } + } + } + //---------------------------------------------------------------------- + + public static int PointInPolygon(IntPoint pt, Path path) + { + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + int result = 0, cnt = path.Count; + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for (int i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (d == 0) return -1; + else if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (d == 0) return -1; + else if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; + } + //------------------------------------------------------------------------------ + + //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + private static int PointInPolygon(IntPoint pt, OutPt op) + { + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt startOp = op; + cInt ptx = pt.X, pty = pt.Y; + cInt poly0x = op.Pt.X, poly0y = op.Pt.Y; + do + { + op = op.Next; + cInt poly1x = op.Pt.X, poly1y = op.Pt.Y; + + if (poly1y == pty) + { + if ((poly1x == ptx) || (poly0y == pty && + ((poly1x > ptx) == (poly0x < ptx)))) return -1; + } + if ((poly0y < pty) != (poly1y < pty)) + { + if (poly0x >= ptx) + { + if (poly1x > ptx) result = 1 - result; + else + { + double d = (double)(poly0x - ptx) * (poly1y - pty) - + (double)(poly1x - ptx) * (poly0y - pty); + if (d == 0) return -1; + if ((d > 0) == (poly1y > poly0y)) result = 1 - result; + } + } + else + { + if (poly1x > ptx) + { + double d = (double)(poly0x - ptx) * (poly1y - pty) - + (double)(poly1x - ptx) * (poly0y - pty); + if (d == 0) return -1; + if ((d > 0) == (poly1y > poly0y)) result = 1 - result; + } + } + } + poly0x = poly1x; poly0y = poly1y; + } while (startOp != op); + return result; + } + //------------------------------------------------------------------------------ + + private static bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2) + { + OutPt op = outPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op.Pt, outPt2); + if (res >= 0) return res > 0; + op = op.Next; + } + while (op != outPt1); + return true; + } + //---------------------------------------------------------------------- + + private void FixupFirstLefts1(OutRec OldOutRec, OutRec NewOutRec) + { + foreach (OutRec outRec in m_PolyOuts) + { + OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (outRec.Pts != null && firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec.Pts, NewOutRec.Pts)) + outRec.FirstLeft = NewOutRec; + } + } + } + //---------------------------------------------------------------------- + + private void FixupFirstLefts2(OutRec innerOutRec, OutRec outerOutRec) + { + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including nil) to see if they've become inner to the new inner polygon ... + OutRec orfl = outerOutRec.FirstLeft; + foreach (OutRec outRec in m_PolyOuts) + { + if (outRec.Pts == null || outRec == outerOutRec || outRec == innerOutRec) + continue; + OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (firstLeft != orfl && firstLeft != innerOutRec && firstLeft != outerOutRec) + continue; + if (Poly2ContainsPoly1(outRec.Pts, innerOutRec.Pts)) + outRec.FirstLeft = innerOutRec; + else if (Poly2ContainsPoly1(outRec.Pts, outerOutRec.Pts)) + outRec.FirstLeft = outerOutRec; + else if (outRec.FirstLeft == innerOutRec || outRec.FirstLeft == outerOutRec) + outRec.FirstLeft = orfl; + } + } + //---------------------------------------------------------------------- + + private void FixupFirstLefts3(OutRec OldOutRec, OutRec NewOutRec) + { + //same as FixupFirstLefts1 but doesn't call Poly2ContainsPoly1() + foreach (OutRec outRec in m_PolyOuts) + { + OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (outRec.Pts != null && firstLeft == OldOutRec) + outRec.FirstLeft = NewOutRec; + } + } + //---------------------------------------------------------------------- + + private static OutRec ParseFirstLeft(OutRec FirstLeft) + { + while (FirstLeft != null && FirstLeft.Pts == null) + FirstLeft = FirstLeft.FirstLeft; + return FirstLeft; + } + //------------------------------------------------------------------------------ + + private void JoinCommonEdges() + { + for (int i = 0; i < m_Joins.Count; i++) + { + Join join = m_Joins[i]; + + OutRec outRec1 = GetOutRec(join.OutPt1.Idx); + OutRec outRec2 = GetOutRec(join.OutPt2.Idx); + + if (outRec1.Pts == null || outRec2.Pts == null) continue; + if (outRec1.IsOpen || outRec2.IsOpen) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1.Pts = join.OutPt1; + outRec1.BottomPt = null; + outRec2 = CreateOutRec(); + outRec2.Pts = join.OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(outRec2); + + if (Poly2ContainsPoly1(outRec2.Pts, outRec1.Pts)) + { + //outRec1 contains outRec2 ... + outRec2.IsHole = !outRec1.IsHole; + outRec2.FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2.IsHole ^ ReverseSolution) == (Area(outRec2) > 0)) + ReversePolyPtLinks(outRec2.Pts); + + } + else if (Poly2ContainsPoly1(outRec1.Pts, outRec2.Pts)) + { + //outRec2 contains outRec1 ... + outRec2.IsHole = outRec1.IsHole; + outRec1.IsHole = !outRec2.IsHole; + outRec2.FirstLeft = outRec1.FirstLeft; + outRec1.FirstLeft = outRec2; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1.IsHole ^ ReverseSolution) == (Area(outRec1) > 0)) + ReversePolyPtLinks(outRec1.Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2.IsHole = outRec1.IsHole; + outRec2.FirstLeft = outRec1.FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2.Pts = null; + outRec2.BottomPt = null; + outRec2.Idx = outRec1.Idx; + + outRec1.IsHole = holeStateRec.IsHole; + if (holeStateRec == outRec2) + outRec1.FirstLeft = outRec2.FirstLeft; + outRec2.FirstLeft = outRec1; + + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); + } + } + } + //------------------------------------------------------------------------------ + + private void UpdateOutPtIdxs(OutRec outrec) + { + OutPt op = outrec.Pts; + do + { + op.Idx = outrec.Idx; + op = op.Prev; + } + while(op != outrec.Pts); + } + //------------------------------------------------------------------------------ + + private void DoSimplePolygons() + { + int i = 0; + while (i < m_PolyOuts.Count) + { + OutRec outrec = m_PolyOuts[i++]; + OutPt op = outrec.Pts; + if (op == null || outrec.IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt op2 = op.Next; + while (op2 != outrec.Pts) + { + if ((op.Pt == op2.Pt) && op2.Next != op && op2.Prev != op) + { + //split the polygon into two ... + OutPt op3 = op.Prev; + OutPt op4 = op2.Prev; + op.Prev = op4; + op4.Next = op; + op2.Prev = op3; + op3.Next = op2; + + outrec.Pts = op; + OutRec outrec2 = CreateOutRec(); + outrec2.Pts = op2; + UpdateOutPtIdxs(outrec2); + if (Poly2ContainsPoly1(outrec2.Pts, outrec.Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2.IsHole = !outrec.IsHole; + outrec2.FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec.Pts, outrec2.Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2.IsHole = outrec.IsHole; + outrec.IsHole = !outrec2.IsHole; + outrec2.FirstLeft = outrec.FirstLeft; + outrec.FirstLeft = outrec2; + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2.IsHole = outrec.IsHole; + outrec2.FirstLeft = outrec.FirstLeft; + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the next iteration + } + op2 = op2.Next; + } + op = op.Next; + } + while (op != outrec.Pts); + } + } + //------------------------------------------------------------------------------ + + public static double Area(Path poly) + { + int cnt = (int)poly.Count; + if (cnt < 3) return 0; + double a = 0; + for (int i = 0, j = cnt - 1; i < cnt; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; + } + //------------------------------------------------------------------------------ + + internal double Area(OutRec outRec) + { + return Area(outRec.Pts); + } + //------------------------------------------------------------------------------ + + internal double Area(OutPt op) + { + OutPt opFirst = op; + if (op == null) return 0; + double a = 0; + do { + a = a + (double)(op.Prev.Pt.X + op.Pt.X) * (double)(op.Prev.Pt.Y - op.Pt.Y); + op = op.Next; + } while (op != opFirst); + return a * 0.5; + } + + //------------------------------------------------------------------------------ + // SimplifyPolygon functions ... + // Convert self-intersecting polygons into simple polygons + //------------------------------------------------------------------------------ + + public static Paths SimplifyPolygon(Path poly, + PolyFillType fillType = PolyFillType.pftEvenOdd) + { + Paths result = new Paths(); + Clipper c = new Clipper(); + c.StrictlySimple = true; + c.AddPath(poly, PolyType.ptSubject, true); + c.Execute(ClipType.ctUnion, result, fillType, fillType); + return result; + } + //------------------------------------------------------------------------------ + + public static Paths SimplifyPolygons(Paths polys, + PolyFillType fillType = PolyFillType.pftEvenOdd) + { + Paths result = new Paths(); + Clipper c = new Clipper(); + c.StrictlySimple = true; + c.AddPaths(polys, PolyType.ptSubject, true); + c.Execute(ClipType.ctUnion, result, fillType, fillType); + return result; + } + //------------------------------------------------------------------------------ + + private static double DistanceSqrd(IntPoint pt1, IntPoint pt2) + { + double dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (dx*dx + dy*dy); + } + //------------------------------------------------------------------------------ + + private static double DistanceFromLineSqrd(IntPoint pt, IntPoint ln1, IntPoint ln2) + { + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = ln1.Y - ln2.Y; + double B = ln2.X - ln1.X; + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); + } + //--------------------------------------------------------------------------- + + private static bool SlopesNearCollinear(IntPoint pt1, + IntPoint pt2, IntPoint pt3, double distSqrd) + { + //this function is more accurate when the point that's GEOMETRICALLY + //between the other 2 points is the one that's tested for distance. + //nb: with 'spikes', either pt1 or pt3 is geometrically between the other pts + if (Math.Abs(pt1.X - pt2.X) > Math.Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + } + //------------------------------------------------------------------------------ + + private static bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) + { + double dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((dx * dx) + (dy * dy) <= distSqrd); + } + //------------------------------------------------------------------------------ + + private static OutPt ExcludeOp(OutPt op) + { + OutPt result = op.Prev; + result.Next = op.Next; + op.Next.Prev = result; + result.Idx = 0; + return result; + } + //------------------------------------------------------------------------------ + + public static Path CleanPolygon(Path path, double distance = 1.415) + { + //distance = proximity in units/pixels below which vertices will be stripped. + //Default ~= sqrt(2) so when adjacent vertices or semi-adjacent vertices have + //both x & y coords within 1 unit, then the second vertex will be stripped. + + int cnt = path.Count; + + if (cnt == 0) return new Path(); + + OutPt [] outPts = new OutPt[cnt]; + for (int i = 0; i < cnt; ++i) outPts[i] = new OutPt(); + + for (int i = 0; i < cnt; ++i) + { + outPts[i].Pt = path[i]; + outPts[i].Next = outPts[(i + 1) % cnt]; + outPts[i].Next.Prev = outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt op = outPts[0]; + while (op.Idx == 0 && op.Next != op.Prev) + { + if (PointsAreClose(op.Pt, op.Prev.Pt, distSqrd)) + { + op = ExcludeOp(op); + cnt--; + } + else if (PointsAreClose(op.Prev.Pt, op.Next.Pt, distSqrd)) + { + ExcludeOp(op.Next); + op = ExcludeOp(op); + cnt -= 2; + } + else if (SlopesNearCollinear(op.Prev.Pt, op.Pt, op.Next.Pt, distSqrd)) + { + op = ExcludeOp(op); + cnt--; + } + else + { + op.Idx = 1; + op = op.Next; + } + } + + if (cnt < 3) cnt = 0; + Path result = new Path(cnt); + for (int i = 0; i < cnt; ++i) + { + result.Add(op.Pt); + op = op.Next; + } + outPts = null; + return result; + } + //------------------------------------------------------------------------------ + + public static Paths CleanPolygons(Paths polys, + double distance = 1.415) + { + Paths result = new Paths(polys.Count); + for (int i = 0; i < polys.Count; i++) + result.Add(CleanPolygon(polys[i], distance)); + return result; + } + //------------------------------------------------------------------------------ + + internal static Paths Minkowski(Path pattern, Path path, bool IsSum, bool IsClosed) + { + int delta = (IsClosed ? 1 : 0); + int polyCnt = pattern.Count; + int pathCnt = path.Count; + Paths result = new Paths(pathCnt); + if (IsSum) + for (int i = 0; i < pathCnt; i++) + { + Path p = new Path(polyCnt); + foreach (IntPoint ip in pattern) + p.Add(new IntPoint(path[i].X + ip.X, path[i].Y + ip.Y)); + result.Add(p); + } + else + for (int i = 0; i < pathCnt; i++) + { + Path p = new Path(polyCnt); + foreach (IntPoint ip in pattern) + p.Add(new IntPoint(path[i].X - ip.X, path[i].Y - ip.Y)); + result.Add(p); + } + + Paths quads = new Paths((pathCnt + delta) * (polyCnt + 1)); + for (int i = 0; i < pathCnt - 1 + delta; i++) + for (int j = 0; j < polyCnt; j++) + { + Path quad = new Path(4); + quad.Add(result[i % pathCnt][j % polyCnt]); + quad.Add(result[(i + 1) % pathCnt][j % polyCnt]); + quad.Add(result[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.Add(result[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) quad.Reverse(); + quads.Add(quad); + } + return quads; + } + //------------------------------------------------------------------------------ + + public static Paths MinkowskiSum(Path pattern, Path path, bool pathIsClosed) + { + Paths paths = Minkowski(pattern, path, true, pathIsClosed); + Clipper c = new Clipper(); + c.AddPaths(paths, PolyType.ptSubject, true); + c.Execute(ClipType.ctUnion, paths, PolyFillType.pftNonZero, PolyFillType.pftNonZero); + return paths; + } + //------------------------------------------------------------------------------ + + private static Path TranslatePath(Path path, IntPoint delta) + { + Path outPath = new Path(path.Count); + for (int i = 0; i < path.Count; i++) + outPath.Add(new IntPoint(path[i].X + delta.X, path[i].Y + delta.Y)); + return outPath; + } + //------------------------------------------------------------------------------ + + public static Paths MinkowskiSum(Path pattern, Paths paths, bool pathIsClosed) + { + Paths solution = new Paths(); + Clipper c = new Clipper(); + for (int i = 0; i < paths.Count; ++i) + { + Paths tmp = Minkowski(pattern, paths[i], true, pathIsClosed); + c.AddPaths(tmp, PolyType.ptSubject, true); + if (pathIsClosed) + { + Path path = TranslatePath(paths[i], pattern[0]); + c.AddPath(path, PolyType.ptClip, true); + } + } + c.Execute(ClipType.ctUnion, solution, + PolyFillType.pftNonZero, PolyFillType.pftNonZero); + return solution; + } + //------------------------------------------------------------------------------ + + public static Paths MinkowskiDiff(Path poly1, Path poly2) + { + Paths paths = Minkowski(poly1, poly2, false, true); + Clipper c = new Clipper(); + c.AddPaths(paths, PolyType.ptSubject, true); + c.Execute(ClipType.ctUnion, paths, PolyFillType.pftNonZero, PolyFillType.pftNonZero); + return paths; + } + //------------------------------------------------------------------------------ + + internal enum NodeType { ntAny, ntOpen, ntClosed }; + + public static Paths PolyTreeToPaths(PolyTree polytree) + { + + Paths result = new Paths(); + result.Capacity = polytree.Total; + AddPolyNodeToPaths(polytree, NodeType.ntAny, result); + return result; + } + //------------------------------------------------------------------------------ + + internal static void AddPolyNodeToPaths(PolyNode polynode, NodeType nt, Paths paths) + { + bool match = true; + switch (nt) + { + case NodeType.ntOpen: return; + case NodeType.ntClosed: match = !polynode.IsOpen; break; + default: break; + } + + if (polynode.m_polygon.Count > 0 && match) + paths.Add(polynode.m_polygon); + foreach (PolyNode pn in polynode.Childs) + AddPolyNodeToPaths(pn, nt, paths); + } + //------------------------------------------------------------------------------ + + public static Paths OpenPathsFromPolyTree(PolyTree polytree) + { + Paths result = new Paths(); + result.Capacity = polytree.ChildCount; + for (int i = 0; i < polytree.ChildCount; i++) + if (polytree.Childs[i].IsOpen) + result.Add(polytree.Childs[i].m_polygon); + return result; + } + //------------------------------------------------------------------------------ + + public static Paths ClosedPathsFromPolyTree(PolyTree polytree) + { + Paths result = new Paths(); + result.Capacity = polytree.Total; + AddPolyNodeToPaths(polytree, NodeType.ntClosed, result); + return result; + } + //------------------------------------------------------------------------------ + + } //end Clipper + + public class ClipperOffset + { + private Paths m_destPolys; + private Path m_srcPoly; + private Path m_destPoly; + private List m_normals = new List(); + private double m_delta, m_sinA, m_sin, m_cos; + private double m_miterLim, m_StepsPerRad; + + private IntPoint m_lowest; + private PolyNode m_polyNodes = new PolyNode(); + + public double ArcTolerance { get; set; } + public double MiterLimit { get; set; } + + private const double two_pi = Math.PI * 2; + private const double def_arc_tolerance = 0.25; + + public ClipperOffset( + double miterLimit = 2.0, double arcTolerance = def_arc_tolerance) + { + MiterLimit = miterLimit; + ArcTolerance = arcTolerance; + m_lowest.X = -1; + } + //------------------------------------------------------------------------------ + + public void Clear() + { + m_polyNodes.Childs.Clear(); + m_lowest.X = -1; + } + //------------------------------------------------------------------------------ + + internal static cInt Round(double value) + { + return value < 0 ? (cInt)(value - 0.5) : (cInt)(value + 0.5); + } + //------------------------------------------------------------------------------ + + public void AddPath(Path path, JoinType joinType, EndType endType) + { + int highI = path.Count - 1; + if (highI < 0) return; + PolyNode newNode = new PolyNode(); + newNode.m_jointype = joinType; + newNode.m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == EndType.etClosedLine || endType == EndType.etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode.m_polygon.Capacity = highI + 1; + newNode.m_polygon.Add(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode.m_polygon[j] != path[i]) + { + j++; + newNode.m_polygon.Add(path[i]); + if (path[i].Y > newNode.m_polygon[k].Y || + (path[i].Y == newNode.m_polygon[k].Y && + path[i].X < newNode.m_polygon[k].X)) k = j; + } + if (endType == EndType.etClosedPolygon && j < 2) return; + + m_polyNodes.AddChild(newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != EndType.etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = new IntPoint(m_polyNodes.ChildCount - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X].m_polygon[(int)m_lowest.Y]; + if (newNode.m_polygon[k].Y > ip.Y || + (newNode.m_polygon[k].Y == ip.Y && + newNode.m_polygon[k].X < ip.X)) + m_lowest = new IntPoint(m_polyNodes.ChildCount - 1, k); + } + } + //------------------------------------------------------------------------------ + + public void AddPaths(Paths paths, JoinType joinType, EndType endType) + { + foreach (Path p in paths) + AddPath(p, joinType, endType); + } + //------------------------------------------------------------------------------ + + private void FixOrientations() + { + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Clipper.Orientation(m_polyNodes.Childs[(int)m_lowest.X].m_polygon)) + { + for (int i = 0; i < m_polyNodes.ChildCount; i++) + { + PolyNode node = m_polyNodes.Childs[i]; + if (node.m_endtype == EndType.etClosedPolygon || + (node.m_endtype == EndType.etClosedLine && + Clipper.Orientation(node.m_polygon))) + node.m_polygon.Reverse(); + } + } + else + { + for (int i = 0; i < m_polyNodes.ChildCount; i++) + { + PolyNode node = m_polyNodes.Childs[i]; + if (node.m_endtype == EndType.etClosedLine && + !Clipper.Orientation(node.m_polygon)) + node.m_polygon.Reverse(); + } + } + } + //------------------------------------------------------------------------------ + + internal static DoublePoint GetUnitNormal(IntPoint pt1, IntPoint pt2) + { + double dx = (pt2.X - pt1.X); + double dy = (pt2.Y - pt1.Y); + if ((dx == 0) && (dy == 0)) return new DoublePoint(); + + double f = 1 * 1.0 / Math.Sqrt(dx * dx + dy * dy); + dx *= f; + dy *= f; + + return new DoublePoint(dy, -dx); + } + //------------------------------------------------------------------------------ + + private void DoOffset(double delta) + { + m_destPolys = new Paths(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (ClipperBase.near_zero(delta)) + { + m_destPolys.Capacity = m_polyNodes.ChildCount; + for (int i = 0; i < m_polyNodes.ChildCount; i++) + { + PolyNode node = m_polyNodes.Childs[i]; + if (node.m_endtype == EndType.etClosedPolygon) + m_destPolys.Add(node.m_polygon); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2 / (MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) + y = def_arc_tolerance; + else if (ArcTolerance > Math.Abs(delta) * def_arc_tolerance) + y = Math.Abs(delta) * def_arc_tolerance; + else + y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = Math.PI / Math.Acos(1 - y / Math.Abs(delta)); + m_sin = Math.Sin(two_pi / steps); + m_cos = Math.Cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.Capacity = m_polyNodes.ChildCount * 2; + for (int i = 0; i < m_polyNodes.ChildCount; i++) + { + PolyNode node = m_polyNodes.Childs[i]; + m_srcPoly = node.m_polygon; + + int len = m_srcPoly.Count; + + if (len == 0 || (delta <= 0 && (len < 3 || + node.m_endtype != EndType.etClosedPolygon))) + continue; + + m_destPoly = new Path(); + + if (len == 1) + { + if (node.m_jointype == JoinType.jtRound) + { + double X = 1.0, Y = 0.0; + for (int j = 1; j <= steps; j++) + { + m_destPoly.Add(new IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.Add(new IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.Add(m_destPoly); + continue; + } + + //build m_normals ... + m_normals.Clear(); + m_normals.Capacity = len; + for (int j = 0; j < len - 1; j++) + m_normals.Add(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == EndType.etClosedLine || + node.m_endtype == EndType.etClosedPolygon) + m_normals.Add(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.Add(new DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == EndType.etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; j++) + OffsetPoint(j, ref k, node.m_jointype); + m_destPolys.Add(m_destPoly); + } + else if (node.m_endtype == EndType.etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; j++) + OffsetPoint(j, ref k, node.m_jointype); + m_destPolys.Add(m_destPoly); + m_destPoly = new Path(); + //re-build m_normals ... + DoublePoint n = m_normals[len - 1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = new DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = new DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, ref k, node.m_jointype); + m_destPolys.Add(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, ref k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == EndType.etOpenButt) + { + int j = len - 1; + pt1 = new IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.Add(pt1); + pt1 = new IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.Add(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = new DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == EndType.etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = new DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + + m_normals[0] = new DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) + OffsetPoint(j, ref k, node.m_jointype); + + if (node.m_endtype == EndType.etOpenButt) + { + pt1 = new IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.Add(pt1); + pt1 = new IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.Add(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == EndType.etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.Add(m_destPoly); + } + } + } + //------------------------------------------------------------------------------ + + public void Execute(ref Paths solution, double delta) + { + solution.Clear(); + FixOrientations(); + DoOffset(delta); + //now clean up 'corners' ... + Clipper clpr = new Clipper(); + clpr.AddPaths(m_destPolys, PolyType.ptSubject, true); + if (delta > 0) + { + clpr.Execute(ClipType.ctUnion, solution, + PolyFillType.pftPositive, PolyFillType.pftPositive); + } + else + { + IntRect r = Clipper.GetBounds(m_destPolys); + Path outer = new Path(4); + + outer.Add(new IntPoint(r.left - 10, r.bottom + 10)); + outer.Add(new IntPoint(r.right + 10, r.bottom + 10)); + outer.Add(new IntPoint(r.right + 10, r.top - 10)); + outer.Add(new IntPoint(r.left - 10, r.top - 10)); + + clpr.AddPath(outer, PolyType.ptSubject, true); + clpr.ReverseSolution = true; + clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); + if (solution.Count > 0) solution.RemoveAt(0); + } + } + //------------------------------------------------------------------------------ + + public void Execute(ref PolyTree solution, double delta) + { + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr = new Clipper(); + clpr.AddPaths(m_destPolys, PolyType.ptSubject, true); + if (delta > 0) + { + clpr.Execute(ClipType.ctUnion, solution, + PolyFillType.pftPositive, PolyFillType.pftPositive); + } + else + { + IntRect r = Clipper.GetBounds(m_destPolys); + Path outer = new Path(4); + + outer.Add(new IntPoint(r.left - 10, r.bottom + 10)); + outer.Add(new IntPoint(r.right + 10, r.bottom + 10)); + outer.Add(new IntPoint(r.right + 10, r.top - 10)); + outer.Add(new IntPoint(r.left - 10, r.top - 10)); + + clpr.AddPath(outer, PolyType.ptSubject, true); + clpr.ReverseSolution = true; + clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount == 1 && solution.Childs[0].ChildCount > 0) + { + PolyNode outerNode = solution.Childs[0]; + solution.Childs.Capacity = outerNode.ChildCount; + solution.Childs[0] = outerNode.Childs[0]; + solution.Childs[0].m_Parent = solution; + for (int i = 1; i < outerNode.ChildCount; i++) + solution.AddChild(outerNode.Childs[i]); + } + else + solution.Clear(); + } + } + //------------------------------------------------------------------------------ + + void OffsetPoint(int j, ref int k, JoinType jointype) + { + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + + if (Math.Abs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y); + if (cosA > 0) // angle ==> 0 degrees + { + m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle ==> 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.Add(m_srcPoly[j]); + m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case JoinType.jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case JoinType.jtSquare: DoSquare(j, k); break; + case JoinType.jtRound: DoRound(j, k); break; + } + k = j; + } + //------------------------------------------------------------------------------ + + internal void DoSquare(int j, int k) + { + double dx = Math.Tan(Math.Atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.Add(new IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.Add(new IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); + } + //------------------------------------------------------------------------------ + + internal void DoMiter(int j, int k, double r) + { + double q = m_delta / r; + m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); + } + //------------------------------------------------------------------------------ + + internal void DoRound(int j, int k) + { + double a = Math.Atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = Math.Max((int)Round(m_StepsPerRad * Math.Abs(a)),1); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.Add(new IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.Add(new IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + //------------------------------------------------------------------------------ + } + + class ClipperException : Exception + { + public ClipperException(string description) : base(description){} + } + //------------------------------------------------------------------------------ + +} //end ClipperLib namespace diff --git a/PCUT/DeepNestLib.Core/D3.cs b/PCUT/DeepNestLib.Core/D3.cs new file mode 100644 index 0000000..ea3dae9 --- /dev/null +++ b/PCUT/DeepNestLib.Core/D3.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DeepNestLib +{ + public class D3 + { + + // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of + // the 3D cross product in a quadrant I Cartesian coordinate system (+x is + // right, +y is up). Returns a positive value if ABC is counter-clockwise, + // negative if clockwise, and zero if the points are collinear. + public static double cross(double[] a, double[] b, double[] c) + { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + } + // Computes the upper convex hull per the monotone chain algorithm. + // Assumes points.length >= 3, is sorted by x, unique in y. + // Returns an array of indices into points in left-to-right order. + public static int[] computeUpperHullIndexes(double[][] points) + { + Dictionary indexes = new Dictionary(); + indexes.Add(0, 0); + indexes.Add(1, 1); + var n = points.Count(); + var size = 2; + + for (var i = 2; i < n; ++i) + { + while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size; + + if (!indexes.ContainsKey(size)) + { + indexes.Add(size, -1); + } + indexes[size++] = i; + } + List ret = new List(); + for (int i = 0; i < size; i++) + { + ret.Add(indexes[i]); + } + return ret.ToArray(); + //return indexes.slice(0, size); // remove popped points + } + + public class HullInfoPoint + { + public double x; + public double y; + public int index; + } + public static double[][] polygonHull(double[][] points) + { + int n; + n = points.Count(); + if ((n) < 3) return null; + + + + HullInfoPoint[] sortedPoints = new HullInfoPoint[n]; + double[][] flippedPoints = new double[n][]; + + + + for (int i = 0; i < n; ++i) sortedPoints[i] = new HullInfoPoint { x = points[i][0], y = points[i][1], index = i }; + sortedPoints = sortedPoints.OrderBy(x => x.x).ThenBy(z => z.y).ToArray(); + + for (int i = 0; i < n; ++i) flippedPoints[i] = new double[] { sortedPoints[i].x, -sortedPoints[i].y }; + + var upperIndexes = computeUpperHullIndexes(sortedPoints.Select(z => new double[] { z.x, z.y, z.index }).ToArray()); + var lowerIndexes = computeUpperHullIndexes(flippedPoints); + + + // Construct the hull polygon, removing possible duplicate endpoints. + var skipLeft = lowerIndexes[0] == upperIndexes[0]; + var skipRight = lowerIndexes[lowerIndexes.Length - 1] == upperIndexes[upperIndexes.Length - 1]; + List hull = new List(); + + // Add upper hull in right-to-l order. + // Then add lower hull in left-to-right order. + for (int i = upperIndexes.Length - 1; i >= 0; --i) + hull.Add(points[sortedPoints[upperIndexes[i]].index]); + //for (int i = +skipLeft; i < lowerIndexes.Length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]); + for (int i = skipLeft ? 1 : 0; i < lowerIndexes.Length - (skipRight ? 1 : 0); ++i) hull.Add(points[sortedPoints[lowerIndexes[i]].index]); + + return hull.ToArray(); + } + + } +} diff --git a/PCUT/DeepNestLib.Core/DeepNestLib.Core.csproj b/PCUT/DeepNestLib.Core/DeepNestLib.Core.csproj new file mode 100644 index 0000000..ca16c9e --- /dev/null +++ b/PCUT/DeepNestLib.Core/DeepNestLib.Core.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/PCUT/DeepNestLib.Core/Extensions.cs b/PCUT/DeepNestLib.Core/Extensions.cs new file mode 100644 index 0000000..26b0085 --- /dev/null +++ b/PCUT/DeepNestLib.Core/Extensions.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; + +namespace DeepNestLib +{ + public static class Extensions + { + public static T[] splice(this T[] p, int a, int b) + { + List ret = new List(); + for (int i = 0; i < p.Length; i++) + { + if (i >= a && i < (a + b)) continue; + ret.Add(p[i]); + } + return ret.ToArray(); + } + + public static List> splice(this List> p, int a, int b) + { + List> ret = new List>(); + for (int i = a; i < (a + b); i++) + { + if (i >= a && i < (a + b)) continue; + ret.Add(p[i]); + } + return ret; + } + + public static NFP[] splice(this NFP[] p, int a, int b) + { + List ret = new List(); + for (int i = 0; i < p.Length; i++) + { + if (i >= a && i < (a + b)) continue; + ret.Add(p[i]); + } + + return ret.ToArray(); + } + } +} diff --git a/PCUT/DeepNestLib.Core/GeneticAlgorithm.cs b/PCUT/DeepNestLib.Core/GeneticAlgorithm.cs new file mode 100644 index 0000000..016fc1a --- /dev/null +++ b/PCUT/DeepNestLib.Core/GeneticAlgorithm.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DeepNestLib +{ + public class GeneticAlgorithm + { + SvgNestConfig Config; + public List population; + + public static bool StrictAngles = false; + float[] defaultAngles = new float[] { + 0, + 0, + 90, + 0, + 0, + 270, + 180, + 180, + 180, + 90 + }; + + public GeneticAlgorithm(NFP[] adam, SvgNestConfig config) + { + List ang2 = new List(); + for (int i = 0; i < adam.Length; i++) + { + ang2.Add((i * 90) % 360); + } + defaultAngles = ang2.ToArray(); + Config = config; + + + List angles = new List(); + for (int i = 0; i < adam.Length; i++) + { + if (StrictAngles) + { + angles.Add(defaultAngles[i]); + } + else + { + var angle = (float)Math.Floor(r.NextDouble() * Config.rotations) * (360f / Config.rotations); + angles.Add(angle); + } + + //angles.Add(randomAngle(adam[i])); + } + + + population = new List(); + population.Add(new PopulationItem() { placements = adam.ToList(), Rotation = angles.ToArray() }); + while (population.Count() < config.populationSize) + { + var mutant = this.mutate(population[0]); + population.Add(mutant); + } + } + + public PopulationItem mutate(PopulationItem p) + { + var clone = new PopulationItem(); + + clone.placements = p.placements.ToArray().ToList(); + clone.Rotation = p.Rotation.Clone() as float[]; + for (int i = 0; i < clone.placements.Count(); i++) + { + var rand = r.NextDouble(); + if (rand < 0.01 * Config.mutationRate) + { + var j = i + 1; + if (j < clone.placements.Count) + { + var temp = clone.placements[i]; + clone.placements[i] = clone.placements[j]; + clone.placements[j] = temp; + } + } + rand = r.NextDouble(); + if (rand < 0.01 * Config.mutationRate) + { + clone.Rotation[i] = (float)Math.Floor(r.NextDouble() * Config.rotations) * (360f / Config.rotations); + } + } + + + return clone; + } + Random r = new Random(); + public float[] shuffleArray(float[] array) + { + for (var i = array.Length - 1; i > 0; i--) + { + var j = (int)Math.Floor(r.NextDouble() * (i + 1)); + var temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + return array; + } + + + // returns a random individual from the population, weighted to the front of the list (lower fitness value is more likely to be selected) + public PopulationItem randomWeightedIndividual(PopulationItem exclude = null) + { + //var pop = this.population.slice(0); + var pop = this.population.ToArray(); + + if (exclude != null && Array.IndexOf(pop, exclude) >= 0) + { + pop.splice(Array.IndexOf(pop, exclude), 1); + } + + var rand = r.NextDouble(); + + float lower = 0; + var weight = 1 / (float)pop.Length; + float upper = weight; + + for (var i = 0; i < pop.Length; i++) + { + // if the random number falls between lower and upper bounds, select this individual + if (rand > lower && rand < upper) + { + return pop[i]; + } + lower = upper; + upper += 2 * weight * ((pop.Length - i) / (float)pop.Length); + } + + return pop[0]; + } + + // single point crossover + public PopulationItem[] mate(PopulationItem male, PopulationItem female) + { + var cutpoint = (int)Math.Round(Math.Min(Math.Max(r.NextDouble(), 0.1), 0.9) * (male.placements.Count - 1)); + + var gene1 = new List(male.placements.Take(cutpoint).ToArray()); + var rot1 = new List(male.Rotation.Take(cutpoint).ToArray()); + + var gene2 = new List(female.placements.Take(cutpoint).ToArray()); + var rot2 = new List(female.Rotation.Take(cutpoint).ToArray()); + + int i = 0; + + for (i = 0; i < female.placements.Count; i++) + { + if (!gene1.Any(z => z.id == female.placements[i].id)) + { + gene1.Add(female.placements[i]); + rot1.Add(female.Rotation[i]); + } + } + + for (i = 0; i < male.placements.Count; i++) + { + if (!gene2.Any(z => z.id == male.placements[i].id)) + { + gene2.Add(male.placements[i]); + rot2.Add(male.Rotation[i]); + } + } + + return new[] {new PopulationItem() { + placements= gene1, Rotation= rot1.ToArray()}, + new PopulationItem(){ placements= gene2, Rotation= rot2.ToArray()}}; + } + + public void generation() + { + // Individuals with higher fitness are more likely to be selected for mating + population = population.OrderBy(z => z.fitness).ToList(); + + // fittest individual is preserved in the new generation (elitism) + + List newpopulation = new List(); + newpopulation.Add(this.population[0]); + while (newpopulation.Count() < this.population.Count) + { + var male = randomWeightedIndividual(); + var female = randomWeightedIndividual(male); + + // each mating produces two children + var children = mate(male, female); + + // slightly mutate children + newpopulation.Add(this.mutate(children[0])); + + if (newpopulation.Count < this.population.Count) + { + newpopulation.Add(this.mutate(children[1])); + } + } + + this.population = newpopulation; + } + } +} \ No newline at end of file diff --git a/PCUT/DeepNestLib.Core/GeometryUtil.cs b/PCUT/DeepNestLib.Core/GeometryUtil.cs new file mode 100644 index 0000000..a03e7a8 --- /dev/null +++ b/PCUT/DeepNestLib.Core/GeometryUtil.cs @@ -0,0 +1,1447 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DeepNestLib +{ + public class GeometryUtil + { + // returns true if points are within the given distance + public static bool _withinDistance(SvgPoint p1, SvgPoint p2, double distance) + { + var dx = p1.x - p2.x; + var dy = p1.y - p2.y; + return ((dx * dx + dy * dy) < distance * distance); + } + + // returns an interior NFP for the special case where A is a rectangle + public static NFP[] noFitPolygonRectangle(NFP A, NFP B) + { + var minAx = A[0].x; + var minAy = A[0].y; + var maxAx = A[0].x; + var maxAy = A[0].y; + + for (var i = 1; i < A.Length; i++) + { + if (A[i].x < minAx) + { + minAx = A[i].x; + } + if (A[i].y < minAy) + { + minAy = A[i].y; + } + if (A[i].x > maxAx) + { + maxAx = A[i].x; + } + if (A[i].y > maxAy) + { + maxAy = A[i].y; + } + } + + var minBx = B[0].x; + var minBy = B[0].y; + var maxBx = B[0].x; + var maxBy = B[0].y; + for (int i = 1; i < B.Length; i++) + { + if (B[i].x < minBx) + { + minBx = B[i].x; + } + if (B[i].y < minBy) + { + minBy = B[i].y; + } + if (B[i].x > maxBx) + { + maxBx = B[i].x; + } + if (B[i].y > maxBy) + { + maxBy = B[i].y; + } + } + + if (maxBx - minBx > maxAx - minAx) + { + return null; + } + if (maxBy - minBy > maxAy - minAy) + { + return null; + } + + + var pnts = new NFP[] { new NFP() { Points=new SvgPoint[]{ + + new SvgPoint(minAx - minBx + B[0].x, minAy - minBy + B[0].y), + new SvgPoint(maxAx - maxBx + B[0].x, minAy - minBy + B[0].y), + new SvgPoint( maxAx - maxBx + B[0].x, maxAy - maxBy + B[0].y), + new SvgPoint( minAx - minBx + B[0].x, maxAy - maxBy + B[0].y) + } } }; + return pnts; + } + + + // returns the rectangular bounding box of the given polygon + public static PolygonBounds getPolygonBounds(NFP _polygon) + { + return getPolygonBounds(_polygon.Points); + } + public static PolygonBounds getPolygonBounds(List polygon) + { + return getPolygonBounds(polygon.ToArray()); + } + public static PolygonBounds getPolygonBounds(SvgPoint[] polygon) + { + + if (polygon == null || polygon.Count() < 3) + { + throw new ArgumentException("null"); + } + + var xmin = polygon[0].x; + var xmax = polygon[0].x; + var ymin = polygon[0].y; + var ymax = polygon[0].y; + + for (var i = 1; i < polygon.Length; i++) + { + if (polygon[i].x > xmax) + { + xmax = polygon[i].x; + } + else if (polygon[i].x < xmin) + { + xmin = polygon[i].x; + } + + if (polygon[i].y > ymax) + { + ymax = polygon[i].y; + } + else if (polygon[i].y < ymin) + { + ymin = polygon[i].y; + } + } + + var w = xmax - xmin; + var h = ymax - ymin; + //return new rectanglef(xmin, ymin, xmax - xmin, ymax - ymin); + return new PolygonBounds(xmin, ymin, w, h); + + + } + + public static bool isRectangle(NFP poly, double? tolerance = null) + { + var bb = getPolygonBounds(poly); + if (tolerance == null) + { + tolerance = TOL; + } + + + for (var i = 0; i < poly.Points.Length; i++) + { + if (!_almostEqual(poly.Points[i].x, bb.x) && !_almostEqual(poly.Points[i].x, bb.x + bb.width)) + { + return false; + } + if (!_almostEqual(poly.Points[i].y, bb.y) && !_almostEqual(poly.Points[i].y, bb.y + bb.height)) + { + return false; + } + } + + return true; + } + + public static PolygonWithBounds rotatePolygon(NFP polygon, float angle) + { + + List rotated = new List(); + angle = (float)(angle * Math.PI / 180.0f); + for (var i = 0; i < polygon.Points.Length; i++) + { + var x = polygon.Points[i].x; + var y = polygon.Points[i].y; + var x1 = (float)(x * Math.Cos(angle) - y * Math.Sin(angle)); + var y1 = (float)(x * Math.Sin(angle) + y * Math.Cos(angle)); + + rotated.Add(new SvgPoint(x1, y1)); + } + // reset bounding box + //RectangleF rr = new RectangleF(); + + var ret = new PolygonWithBounds() + { + Points = rotated.ToArray() + }; + var bounds = GeometryUtil.getPolygonBounds(ret); + ret.x = bounds.x; + ret.y = bounds.y; + ret.width = bounds.width; + ret.height = bounds.height; + return ret; + + } + + public class PolygonWithBounds : NFP + { + public double x; + public double y; + public double width; + public double height; + } + public static bool _almostEqual(double a, double b, double? tolerance = null) + { + if (tolerance == null) + { + tolerance = TOL; + } + return Math.Abs(a - b) < tolerance; + } + public static bool _almostEqual(double? a, double? b, double? tolerance = null) + { + return _almostEqual(a.Value, b.Value, tolerance); + } + // returns true if point already exists in the given nfp + public static bool inNfp(SvgPoint p, NFP[] nfp) + { + if (nfp == null || nfp.Length == 0) + { + return false; + } + + for (var i = 0; i < nfp.Length; i++) + { + for (var j = 0; j < nfp[i].length; j++) + { + if (_almostEqual(p.x, nfp[i][j].x) && _almostEqual(p.y, nfp[i][j].y)) + { + return true; + } + } + } + + return false; + } + // normalize vector into a unit vector + public static SvgPoint _normalizeVector(SvgPoint v) + { + if (_almostEqual(v.x * v.x + v.y * v.y, 1)) + { + return v; // given vector was already a unit vector + } + var len = Math.Sqrt(v.x * v.x + v.y * v.y); + var inverse = (float)(1 / len); + + return new SvgPoint(v.x * inverse, v.y * inverse + ); + } + public static double? pointDistance(SvgPoint p, SvgPoint s1, SvgPoint s2, SvgPoint normal, bool infinite = false) + { + normal = _normalizeVector(normal); + + var dir = new SvgPoint(normal.y, -normal.x); + + var pdot = p.x * dir.x + p.y * dir.y; + var s1dot = s1.x * dir.x + s1.y * dir.y; + var s2dot = s2.x * dir.x + s2.y * dir.y; + + var pdotnorm = p.x * normal.x + p.y * normal.y; + var s1dotnorm = s1.x * normal.x + s1.y * normal.y; + var s2dotnorm = s2.x * normal.x + s2.y * normal.y; + + if (!infinite) + { + if (((pdot < s1dot || _almostEqual(pdot, s1dot)) && (pdot < s2dot || _almostEqual(pdot, s2dot))) || ((pdot > s1dot || _almostEqual(pdot, s1dot)) && (pdot > s2dot || _almostEqual(pdot, s2dot)))) + { + return null; // dot doesn't collide with segment, or lies directly on the vertex + } + if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && (pdotnorm > s1dotnorm && pdotnorm > s2dotnorm)) + { + return Math.Min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm); + } + if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) && (pdotnorm < s1dotnorm && pdotnorm < s2dotnorm)) + { + return -Math.Min(s1dotnorm - pdotnorm, s2dotnorm - pdotnorm); + } + } + + return -(pdotnorm - s1dotnorm + (s1dotnorm - s2dotnorm) * (s1dot - pdot) / (s1dot - s2dot)); + } + static double TOL = (float)Math.Pow(10, -9); // Floating point error is likely to be above 1 epsilon + // returns true if p lies on the line segment defined by AB, but not at any endpoints + // may need work! + public static bool _onSegment(SvgPoint A, SvgPoint B, SvgPoint p) + { + + // vertical line + if (_almostEqual(A.x, B.x) && _almostEqual(p.x, A.x)) + { + if (!_almostEqual(p.y, B.y) && !_almostEqual(p.y, A.y) && p.y < Math.Max(B.y, A.y) && p.y > Math.Min(B.y, A.y)) + { + return true; + } + else + { + return false; + } + } + + // horizontal line + if (_almostEqual(A.y, B.y) && _almostEqual(p.y, A.y)) + { + if (!_almostEqual(p.x, B.x) && !_almostEqual(p.x, A.x) && p.x < Math.Max(B.x, A.x) && p.x > Math.Min(B.x, A.x)) + { + return true; + } + else + { + return false; + } + } + + //range check + if ((p.x < A.x && p.x < B.x) || (p.x > A.x && p.x > B.x) || (p.y < A.y && p.y < B.y) || (p.y > A.y && p.y > B.y)) + { + return false; + } + + + // exclude end points + if ((_almostEqual(p.x, A.x) && _almostEqual(p.y, A.y)) || (_almostEqual(p.x, B.x) && _almostEqual(p.y, B.y))) + { + return false; + } + + var cross = (p.y - A.y) * (B.x - A.x) - (p.x - A.x) * (B.y - A.y); + + if (Math.Abs(cross) > TOL) + { + return false; + } + + var dot = (p.x - A.x) * (B.x - A.x) + (p.y - A.y) * (B.y - A.y); + + + + if (dot < 0 || _almostEqual(dot, 0)) + { + return false; + } + + var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y); + + + + if (dot > len2 || _almostEqual(dot, len2)) + { + return false; + } + + return true; + } + + + // project each point of B onto A in the given direction, and return the + public static double? polygonProjectionDistance(NFP A, NFP B, SvgPoint direction) + { + var Boffsetx = B.offsetx ?? 0; + var Boffsety = B.offsety ?? 0; + + var Aoffsetx = A.offsetx ?? 0; + var Aoffsety = A.offsety ?? 0; + + A = A.slice(0); + B = B.slice(0); + + // close the loop for polygons + if (A[0] != A[A.length - 1]) + { + A.push(A[0]); + } + + if (B[0] != B[B.length - 1]) + { + B.push(B[0]); + } + + var edgeA = A; + var edgeB = B; + + double? distance = null; + SvgPoint p, s1, s2; + double? d; + + + for (var i = 0; i < edgeB.length; i++) + { + // the shortest/most negative projection of B onto A + double? minprojection = null; + SvgPoint minp = null; + for (var j = 0; j < edgeA.length - 1; j++) + { + p = new SvgPoint(edgeB[i].x + Boffsetx, edgeB[i].y + Boffsety); + s1 = new SvgPoint(edgeA[j].x + Aoffsetx, edgeA[j].y + Aoffsety); + s2 = new SvgPoint(edgeA[j + 1].x + Aoffsetx, edgeA[j + 1].y + Aoffsety); + + if (Math.Abs((s2.y - s1.y) * direction.x - (s2.x - s1.x) * direction.y) < TOL) + { + continue; + } + + // project point, ignore edge boundaries + d = pointDistance(p, s1, s2, direction); + + if (d != null && (minprojection == null || d < minprojection)) + { + minprojection = d; + minp = p; + } + } + if (minprojection != null && (distance == null || minprojection > distance)) + { + distance = minprojection; + } + } + + return distance; + } + + public static double polygonArea(NFP polygon) + { + double area = 0; + int i, j; + for (i = 0, j = polygon.Points.Length - 1; i < polygon.Points.Length; j = i++) + { + area += (polygon.Points[j].x + polygon.Points[i].x) * (polygon.Points[j].y + - polygon.Points[i].y); + } + return 0.5f * area; + } + + // return true if point is in the polygon, false if outside, and null if exactly on a point or edge + public static bool? pointInPolygon(SvgPoint point, NFP polygon) + { + if (polygon == null || polygon.Points.Length < 3) + { + throw new ArgumentException(); + } + + var inside = false; + //var offsetx = polygon.offsetx || 0; + //var offsety = polygon.offsety || 0; + double offsetx = polygon.offsetx == null ? 0 : polygon.offsetx.Value; + double offsety = polygon.offsety == null ? 0 : polygon.offsety.Value; + + int i, j; + for (i = 0, j = polygon.Points.Count() - 1; i < polygon.Points.Length; j = i++) + { + var xi = polygon.Points[i].x + offsetx; + var yi = polygon.Points[i].y + offsety; + var xj = polygon.Points[j].x + offsetx; + var yj = polygon.Points[j].y + offsety; + + if (_almostEqual(xi, point.x) && _almostEqual(yi, point.y)) + { + + return null; // no result + } + + if (_onSegment(new SvgPoint(xi, yi), new SvgPoint(xj, yj), point)) + { + return null; // exactly on the segment + } + + if (_almostEqual(xi, xj) && _almostEqual(yi, yj)) + { // ignore very small lines + continue; + } + + var intersect = ((yi > point.y) != (yj > point.y)) && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi); + if (intersect) inside = !inside; + } + + return inside; + } + // todo: swap this for a more efficient sweep-line implementation + // returnEdges: if set, return all edges on A that have intersections + + public static bool intersect(NFP A, NFP B) + { + var Aoffsetx = A.offsetx ?? 0; + var Aoffsety = A.offsety ?? 0; + + var Boffsetx = B.offsetx ?? 0; + var Boffsety = B.offsety ?? 0; + + A = A.slice(0); + B = B.slice(0); + + for (var i = 0; i < A.length - 1; i++) + { + for (var j = 0; j < B.length - 1; j++) + { + var a1 = new SvgPoint(A[i].x + Aoffsetx, A[i].y + Aoffsety); + var a2 = new SvgPoint(A[i + 1].x + Aoffsetx, A[i + 1].y + Aoffsety); + var b1 = new SvgPoint(B[j].x + Boffsetx, B[j].y + Boffsety); + var b2 = new SvgPoint(B[j + 1].x + Boffsetx, B[j + 1].y + Boffsety); + + var prevbindex = (j == 0) ? B.length - 1 : j - 1; + var prevaindex = (i == 0) ? A.length - 1 : i - 1; + var nextbindex = (j + 1 == B.length - 1) ? 0 : j + 2; + var nextaindex = (i + 1 == A.length - 1) ? 0 : i + 2; + + // go even further back if we happen to hit on a loop end point + if (B[prevbindex] == B[j] || (_almostEqual(B[prevbindex].x, B[j].x) && _almostEqual(B[prevbindex].y, B[j].y))) + { + prevbindex = (prevbindex == 0) ? B.length - 1 : prevbindex - 1; + } + + if (A[prevaindex] == A[i] || (_almostEqual(A[prevaindex].x, A[i].x) && _almostEqual(A[prevaindex].y, A[i].y))) + { + prevaindex = (prevaindex == 0) ? A.length - 1 : prevaindex - 1; + } + + // go even further forward if we happen to hit on a loop end point + if (B[nextbindex] == B[j + 1] || (_almostEqual(B[nextbindex].x, B[j + 1].x) && _almostEqual(B[nextbindex].y, B[j + 1].y))) + { + nextbindex = (nextbindex == B.length - 1) ? 0 : nextbindex + 1; + } + + if (A[nextaindex] == A[i + 1] || (_almostEqual(A[nextaindex].x, A[i + 1].x) && _almostEqual(A[nextaindex].y, A[i + 1].y))) + { + nextaindex = (nextaindex == A.length - 1) ? 0 : nextaindex + 1; + } + + var a0 = new SvgPoint(A[prevaindex].x + Aoffsetx, A[prevaindex].y + Aoffsety); + var b0 = new SvgPoint(B[prevbindex].x + Boffsetx, B[prevbindex].y + Boffsety); + + var a3 = new SvgPoint(A[nextaindex].x + Aoffsetx, A[nextaindex].y + Aoffsety); + var b3 = new SvgPoint(B[nextbindex].x + Boffsetx, B[nextbindex].y + Boffsety); + + if (_onSegment(a1, a2, b1) || (_almostEqual(a1.x, b1.x) && _almostEqual(a1.y, b1.y))) + { + // if a point is on a segment, it could intersect or it could not. Check via the neighboring points + var b0in = pointInPolygon(b0, A); + var b2in = pointInPolygon(b2, A); + if ((b0in == true && b2in == false) || (b0in == false && b2in == true)) + { + return true; + } + else + { + continue; + } + } + + if (_onSegment(a1, a2, b2) || (_almostEqual(a2.x, b2.x) && _almostEqual(a2.y, b2.y))) + { + // if a point is on a segment, it could intersect or it could not. Check via the neighboring points + var b1in = pointInPolygon(b1, A); + var b3in = pointInPolygon(b3, A); + + if ((b1in == true && b3in == false) || (b1in == false && b3in == true)) + { + return true; + } + else + { + continue; + } + } + + if (_onSegment(b1, b2, a1) || (_almostEqual(a1.x, b2.x) && _almostEqual(a1.y, b2.y))) + { + // if a point is on a segment, it could intersect or it could not. Check via the neighboring points + var a0in = pointInPolygon(a0, B); + var a2in = pointInPolygon(a2, B); + + if ((a0in == true && a2in == false) || (a0in == false && a2in == true)) + { + return true; + } + else + { + continue; + } + } + + if (_onSegment(b1, b2, a2) || (_almostEqual(a2.x, b1.x) && _almostEqual(a2.y, b1.y))) + { + // if a point is on a segment, it could intersect or it could not. Check via the neighboring points + var a1in = pointInPolygon(a1, B); + var a3in = pointInPolygon(a3, B); + + if ((a1in == true && a3in == false) || (a1in == false && a3in == true)) + { + return true; + } + else + { + continue; + } + } + + var p = _lineIntersect(b1, b2, a1, a2); + + if (p != null) + { + return true; + } + } + } + + return false; + } + + public static bool isFinite(object obj) + { + return true; + } + // returns the intersection of AB and EF + // or null if there are no intersections or other numerical error + // if the infinite flag is set, AE and EF describe infinite lines without endpoints, they are finite line segments otherwise + public static SvgPoint _lineIntersect(SvgPoint A, SvgPoint B, SvgPoint E, SvgPoint F, bool infinite = false) + { + double a1, a2, b1, b2, c1, c2, x, y; + + a1 = B.y - A.y; + b1 = A.x - B.x; + c1 = B.x * A.y - A.x * B.y; + a2 = F.y - E.y; + b2 = E.x - F.x; + c2 = F.x * E.y - E.x * F.y; + + var denom = a1 * b2 - a2 * b1; + + x = (b1 * c2 - b2 * c1) / denom; + y = (a2 * c1 - a1 * c2) / denom; + + + if (!isFinite(x) || !isFinite(y)) + { + return null; + } + + // lines are colinear + /*var crossABE = (E.y - A.y) * (B.x - A.x) - (E.x - A.x) * (B.y - A.y); + var crossABF = (F.y - A.y) * (B.x - A.x) - (F.x - A.x) * (B.y - A.y); + if(_almostEqual(crossABE,0) && _almostEqual(crossABF,0)){ + return null; + }*/ + + if (!infinite) + { + // coincident points do not count as intersecting + if (Math.Abs(A.x - B.x) > TOL && ((A.x < B.x) ? x < A.x || x > B.x : x > A.x || x < B.x)) return null; + if (Math.Abs(A.y - B.y) > TOL && ((A.y < B.y) ? y < A.y || y > B.y : y > A.y || y < B.y)) return null; + + if (Math.Abs(E.x - F.x) > TOL && ((E.x < F.x) ? x < E.x || x > F.x : x > E.x || x < F.x)) return null; + if (Math.Abs(E.y - F.y) > TOL && ((E.y < F.y) ? y < E.y || y > F.y : y > E.y || y < F.y)) return null; + } + + return new SvgPoint(x, y); + } + + // searches for an arrangement of A and B such that they do not overlap + // if an NFP is given, only search for startpoints that have not already been traversed in the given NFP + + public static SvgPoint searchStartPoint(NFP A, NFP B, bool inside, NFP[] NFP = null) + { + // clone arrays + A = A.slice(0); + B = B.slice(0); + + // close the loop for polygons + if (A[0] != A[A.length - 1]) + { + A.push(A[0]); + } + + if (B[0] != B[B.length - 1]) + { + B.push(B[0]); + } + + for (var i = 0; i < A.length - 1; i++) + { + if (!A[i].marked) + { + A[i].marked = true; + for (var j = 0; j < B.length; j++) + { + B.offsetx = A[i].x - B[j].x; + B.offsety = A[i].y - B[j].y; + + bool? Binside = null; + for (var k = 0; k < B.length; k++) + { + var inpoly = pointInPolygon(new SvgPoint(B[k].x + B.offsetx.Value, + B[k].y + B.offsety.Value), A); + if (inpoly != null) + { + Binside = inpoly; + break; + } + } + + if (Binside == null) + { // A and B are the same + return null; + } + + var startPoint = new SvgPoint(B.offsetx.Value, B.offsety.Value); + if (((Binside.Value && inside) || (!Binside.Value && !inside)) && + !intersect(A, B) && !inNfp(startPoint, NFP)) + { + return startPoint; + } + + // slide B along vector + var vx = A[i + 1].x - A[i].x; + var vy = A[i + 1].y - A[i].y; + + var d1 = polygonProjectionDistance(A, B, new SvgPoint(vx, vy)); + var d2 = polygonProjectionDistance(B, A, new SvgPoint(-vx, -vy)); + + double? d = null; + + // todo: clean this up + if (d1 == null && d2 == null) + { + // nothin + } + else if (d1 == null) + { + d = d2; + } + else if (d2 == null) + { + d = d1; + } + else + { + d = Math.Min(d1.Value, d2.Value); + } + + // only slide until no longer negative + // todo: clean this up + if (d != null && !_almostEqual(d, 0) && d > 0) + { + + } + else + { + continue; + } + + var vd2 = vx * vx + vy * vy; + + if (d * d < vd2 && !_almostEqual(d * d, vd2)) + { + var vd = (float)Math.Sqrt(vx * vx + vy * vy); + vx *= d.Value / vd; + vy *= d.Value / vd; + } + + B.offsetx += vx; + B.offsety += vy; + + for (var k = 0; k < B.length; k++) + { + var inpoly = pointInPolygon( + new SvgPoint( + B[k].x + B.offsetx.Value, B[k].y + B.offsety.Value), A); + if (inpoly != null) + { + Binside = inpoly; + break; + } + } + startPoint = + new SvgPoint(B.offsetx.Value, B.offsety.Value); + if (((Binside.Value && inside) || (!Binside.Value && !inside)) && + !intersect(A, B) && !inNfp(startPoint, NFP)) + { + return startPoint; + } + } + } + } + + + + return null; + } + + public class TouchingItem + { + public TouchingItem(int _type, int _a, int _b) + { + A = _a; + B = _b; + type = _type; + } + public int A; + public int B; + public int type; + + } + + public static double? segmentDistance(SvgPoint A, SvgPoint B, SvgPoint E, SvgPoint F, SvgPoint direction) + { + var normal = new SvgPoint( + direction.y, + -direction.x + + ); + + var reverse = new SvgPoint( + -direction.x, + -direction.y + ); + + var dotA = A.x * normal.x + A.y * normal.y; + var dotB = B.x * normal.x + B.y * normal.y; + var dotE = E.x * normal.x + E.y * normal.y; + var dotF = F.x * normal.x + F.y * normal.y; + + var crossA = A.x * direction.x + A.y * direction.y; + var crossB = B.x * direction.x + B.y * direction.y; + var crossE = E.x * direction.x + E.y * direction.y; + var crossF = F.x * direction.x + F.y * direction.y; + + var crossABmin = Math.Min(crossA, crossB); + var crossABmax = Math.Max(crossA, crossB); + + var crossEFmax = Math.Max(crossE, crossF); + var crossEFmin = Math.Min(crossE, crossF); + + var ABmin = Math.Min(dotA, dotB); + var ABmax = Math.Max(dotA, dotB); + + var EFmax = Math.Max(dotE, dotF); + var EFmin = Math.Min(dotE, dotF); + + // segments that will merely touch at one point + if (_almostEqual(ABmax, EFmin, TOL) || _almostEqual(ABmin, EFmax, TOL)) + { + return null; + } + // segments miss eachother completely + if (ABmax < EFmin || ABmin > EFmax) + { + return null; + } + + double overlap; + + if ((ABmax > EFmax && ABmin < EFmin) || (EFmax > ABmax && EFmin < ABmin)) + { + overlap = 1; + } + else + { + var minMax = Math.Min(ABmax, EFmax); + var maxMin = Math.Max(ABmin, EFmin); + + var maxMax = Math.Max(ABmax, EFmax); + var minMin = Math.Min(ABmin, EFmin); + + overlap = (minMax - maxMin) / (maxMax - minMin); + } + + var crossABE = (E.y - A.y) * (B.x - A.x) - (E.x - A.x) * (B.y - A.y); + var crossABF = (F.y - A.y) * (B.x - A.x) - (F.x - A.x) * (B.y - A.y); + + // lines are colinear + if (_almostEqual(crossABE, 0) && _almostEqual(crossABF, 0)) + { + + var ABnorm = new SvgPoint(B.y - A.y, A.x - B.x); + var EFnorm = new SvgPoint(F.y - E.y, E.x - F.x); + + var ABnormlength = (float)Math.Sqrt(ABnorm.x * ABnorm.x + ABnorm.y * ABnorm.y); + ABnorm.x /= ABnormlength; + ABnorm.y /= ABnormlength; + + var EFnormlength = (float)Math.Sqrt(EFnorm.x * EFnorm.x + EFnorm.y * EFnorm.y); + EFnorm.x /= EFnormlength; + EFnorm.y /= EFnormlength; + + // segment normals must point in opposite directions + if (Math.Abs(ABnorm.y * EFnorm.x - ABnorm.x * EFnorm.y) < TOL && ABnorm.y * EFnorm.y + ABnorm.x * EFnorm.x < 0) + { + // normal of AB segment must point in same direction as given direction vector + var normdot = ABnorm.y * direction.y + ABnorm.x * direction.x; + // the segments merely slide along eachother + if (_almostEqual(normdot, 0, TOL)) + { + return null; + } + if (normdot < 0) + { + return 0; + } + } + return null; + } + + var distances = new List(); + + // coincident points + if (_almostEqual(dotA, dotE)) + { + distances.Add(crossA - crossE); + } + else if (_almostEqual(dotA, dotF)) + { + distances.Add(crossA - crossF); + } + else if (dotA > EFmin && dotA < EFmax) + { + var d = pointDistance(A, E, F, reverse); + if (d != null && _almostEqual(d, 0)) + { // A currently touches EF, but AB is moving away from EF + var dB = pointDistance(B, E, F, reverse, true); + if (dB < 0 || _almostEqual(dB * overlap, 0)) + { + d = null; + } + } + if (d != null) + { + distances.Add(d.Value); + } + } + + if (_almostEqual(dotB, dotE)) + { + distances.Add(crossB - crossE); + } + else if (_almostEqual(dotB, dotF)) + { + distances.Add(crossB - crossF); + } + else if (dotB > EFmin && dotB < EFmax) + { + var d = pointDistance(B, E, F, reverse); + + if (d != null && _almostEqual(d, 0)) + { // crossA>crossB A currently touches EF, but AB is moving away from EF + var dA = pointDistance(A, E, F, reverse, true); + if (dA < 0 || _almostEqual(dA * overlap, 0)) + { + d = null; + } + } + if (d != null) + { + distances.Add(d.Value); + } + } + + if (dotE > ABmin && dotE < ABmax) + { + var d = pointDistance(E, A, B, direction); + if (d != null && _almostEqual(d, 0)) + { // crossF ABmin && dotF < ABmax) + { + var d = pointDistance(F, A, B, direction); + if (d != null && _almostEqual(d, 0)) + { // && crossE 0 || _almostEqual(d, 0)) + { + distance = d; + } + } + } + } + return distance; + } + public class nVector + { + public SvgPoint start; + public SvgPoint end; + public double x; + public double y; + + + public nVector(double v1, double v2, SvgPoint _start, SvgPoint _end) + { + this.x = v1; + this.y = v2; + this.start = _start; + this.end = _end; + } + } + + // given a static polygon A and a movable polygon B, compute a no fit polygon by orbiting B about A + // if the inside flag is set, B is orbited inside of A rather than outside + // if the searchEdges flag is set, all edges of A are explored for NFPs - multiple + public static NFP[] noFitPolygon(NFP A, NFP B, bool inside, bool searchEdges) + { + if (A == null || A.length < 3 || B == null || B.length < 3) + { + return null; + } + + A.offsetx = 0; + A.offsety = 0; + + int i = 0, j = 0; + + var minA = A[0].y; + var minAindex = 0; + + var maxB = B[0].y; + var maxBindex = 0; + + for (i = 1; i < A.length; i++) + { + A[i].marked = false; + if (A[i].y < minA) + { + minA = A[i].y; + minAindex = i; + } + } + + for (i = 1; i < B.length; i++) + { + B[i].marked = false; + if (B[i].y > maxB) + { + maxB = B[i].y; + maxBindex = i; + } + } + SvgPoint startpoint; + if (!inside) + { + // shift B such that the bottom-most point of B is at the top-most point of A. This guarantees an initial placement with no intersections + startpoint = new SvgPoint( + A[minAindex].x - B[maxBindex].x, + A[minAindex].y - B[maxBindex].y); + } + else + { + // no reliable heuristic for inside + + startpoint = searchStartPoint(A, B, true); + } + + List NFPlist = new List(); + + + + while (startpoint != null) + { + + B.offsetx = startpoint.x; + B.offsety = startpoint.y; + + // maintain a list of touching points/edges + List touching = null; + + nVector prevvector = null; // keep track of previous vector + NFP NFP = new NFP(); + /*var NFP = [{ + x: B[0].x + B.offsetx, + y: B[0].y + B.offsety + + + + + + }];*/ + NFP.push(new SvgPoint(B[0].x + B.offsetx.Value, B[0].y + B.offsety.Value)); + + double referencex = B[0].x + B.offsetx.Value; + double referencey = B[0].y + B.offsety.Value; + var startx = referencex; + var starty = referencey; + var counter = 0; + + while (counter < 10 * (A.length + B.length)) + { // sanity check, prevent infinite loop + touching = new List(); + // find touching vertices/edges + for (i = 0; i < A.length; i++) + { + var nexti = (i == A.length - 1) ? 0 : i + 1; + for (j = 0; j < B.length; j++) + { + var nextj = (j == B.length - 1) ? 0 : j + 1; + if (_almostEqual(A[i].x, B[j].x + B.offsetx) && _almostEqual(A[i].y, B[j].y + B.offsety)) + { + touching.Add(new TouchingItem(0, i, j)); + } + else if (_onSegment(A[i], A[nexti], + new SvgPoint(B[j].x + B.offsetx.Value, B[j].y + B.offsety.Value))) + { + touching.Add(new TouchingItem(1, nexti, j)); + } + else if (_onSegment( + new SvgPoint( + B[j].x + B.offsetx.Value, B[j].y + B.offsety.Value), + new SvgPoint( + B[nextj].x + B.offsetx.Value, B[nextj].y + B.offsety.Value), A[i])) + { + touching.Add(new TouchingItem(2, i, nextj)); + } + } + } + + // generate translation vectors from touching vertices/edges + var vectors = new List(); + for (i = 0; i < touching.Count; i++) + { + var vertexA = A[touching[i].A]; + vertexA.marked = true; + + // adjacent A vertices + var prevAindex = touching[i].A - 1; + var nextAindex = touching[i].A + 1; + + prevAindex = (prevAindex < 0) ? A.length - 1 : prevAindex; // loop + nextAindex = (nextAindex >= A.length) ? 0 : nextAindex; // loop + + var prevA = A[prevAindex]; + var nextA = A[nextAindex]; + + // adjacent B vertices + var vertexB = B[touching[i].B]; + + var prevBindex = touching[i].B - 1; + var nextBindex = touching[i].B + 1; + + prevBindex = (prevBindex < 0) ? B.length - 1 : prevBindex; // loop + nextBindex = (nextBindex >= B.length) ? 0 : nextBindex; // loop + + var prevB = B[prevBindex]; + var nextB = B[nextBindex]; + + if (touching[i].type == 0) + { + + var vA1 = new nVector( + prevA.x - vertexA.x, + prevA.y - vertexA.y, + vertexA, + prevA + ); + + var vA2 = new nVector( + nextA.x - vertexA.x, + nextA.y - vertexA.y, + vertexA, + nextA + ); + + // B vectors need to be inverted + var vB1 = new nVector( + vertexB.x - prevB.x, + vertexB.y - prevB.y, + prevB, + vertexB + ); + + var vB2 = new nVector( + vertexB.x - nextB.x, + vertexB.y - nextB.y, + nextB, + vertexB + ); + + vectors.Add(vA1); + vectors.Add(vA2); + vectors.Add(vB1); + vectors.Add(vB2); + } + else if (touching[i].type == 1) + { + vectors.Add(new nVector( + vertexA.x - (vertexB.x + B.offsetx.Value), + vertexA.y - (vertexB.y + B.offsety.Value), + prevA, + vertexA + )); + + vectors.Add(new nVector( + prevA.x - (vertexB.x + B.offsetx.Value), + prevA.y - (vertexB.y + B.offsety.Value), + vertexA, + prevA + )); + } + else if (touching[i].type == 2) + { + vectors.Add(new nVector( + vertexA.x - (vertexB.x + B.offsetx.Value), + vertexA.y - (vertexB.y + B.offsety.Value), + prevB, + vertexB + )); + + vectors.Add(new nVector( + vertexA.x - (prevB.x + B.offsetx.Value), + vertexA.y - (prevB.y + B.offsety.Value), + vertexB, + prevB + + )); + } + } + + // todo: there should be a faster way to reject vectors that will cause immediate intersection. For now just check them all + + nVector translate = null; + double maxd = 0; + + for (i = 0; i < vectors.Count; i++) + { + if (vectors[i].x == 0 && vectors[i].y == 0) + { + continue; + } + + // if this vector points us back to where we came from, ignore it. + // ie cross product = 0, dot product < 0 + if (prevvector != null && + vectors[i].y * prevvector.y + vectors[i].x * prevvector.x < 0) + { + + // compare magnitude with unit vectors + var vectorlength = (float)Math.Sqrt(vectors[i].x * vectors[i].x + vectors[i].y * vectors[i].y); + var unitv = new SvgPoint(vectors[i].x / vectorlength, vectors[i].y / vectorlength); + + var prevlength = (float)Math.Sqrt(prevvector.x * prevvector.x + prevvector.y * prevvector.y); + var prevunit = new SvgPoint(prevvector.x / prevlength, prevvector.y / prevlength); + + // we need to scale down to unit vectors to normalize vector length. Could also just do a tan here + if (Math.Abs(unitv.y * prevunit.x - unitv.x * prevunit.y) < 0.0001) + { + continue; + } + } + + var d = polygonSlideDistance(A, B, vectors[i], true); + var vecd2 = vectors[i].x * vectors[i].x + vectors[i].y * vectors[i].y; + + if (d == null || d * d > vecd2) + { + var vecd = (float)Math.Sqrt(vectors[i].x * vectors[i].x + vectors[i].y * vectors[i].y); + d = vecd; + } + + if (d != null && d > maxd) + { + maxd = d.Value; + translate = vectors[i]; + } + } + + + if (translate == null || _almostEqual(maxd, 0)) + { + // didn't close the loop, something went wrong here + NFP = null; + break; + } + + translate.start.marked = true; + translate.end.marked = true; + + prevvector = translate; + + // trim + var vlength2 = translate.x * translate.x + translate.y * translate.y; + if (maxd * maxd < vlength2 && !_almostEqual(maxd * maxd, vlength2)) + { + var scale = (float)Math.Sqrt((maxd * maxd) / vlength2); + translate.x *= scale; + translate.y *= scale; + } + + referencex += translate.x; + referencey += translate.y; + + if (_almostEqual(referencex, startx) && _almostEqual(referencey, starty)) + { + // we've made a full loop + break; + } + + // if A and B start on a touching horizontal line, the end point may not be the start point + var looped = false; + if (NFP.length > 0) + { + for (i = 0; i < NFP.length - 1; i++) + { + if (_almostEqual(referencex, NFP[i].x) && _almostEqual(referencey, NFP[i].y)) + { + looped = true; + } + } + } + + if (looped) + { + // we've made a full loop + break; + } + + NFP.push(new SvgPoint( + referencex, referencey + )); + + B.offsetx += translate.x; + B.offsety += translate.y; + + counter++; + } + + if (NFP != null && NFP.length > 0) + { + NFPlist.Add(NFP); + + } + + if (!searchEdges) + { + // only get outer NFP or first inner NFP + break; + } + startpoint = searchStartPoint(A, B, inside, NFPlist.ToArray()); + } + + return NFPlist.ToArray(); + } + public static bool pnpoly((double X, double Y)[] verts, double testx, double testy) + { + int nvert = verts.Length; + int i, j; + bool c = false; + for (i = 0, j = nvert - 1; i < nvert; j = i++) + { + if (((verts[i].Y > testy) != (verts[j].Y > testy)) && + (testx < (verts[j].X - verts[i].X) * (testy - verts[i].Y) / (verts[j].Y - verts[i].Y) + verts[i].X)) + c = !c; + } + return c; + } + + + + } +} diff --git a/PCUT/DeepNestLib.Core/IStringify.cs b/PCUT/DeepNestLib.Core/IStringify.cs new file mode 100644 index 0000000..ad7bd9b --- /dev/null +++ b/PCUT/DeepNestLib.Core/IStringify.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DeepNestLib +{ + public interface IStringify + { + string stringify(); + } +} diff --git a/PCUT/DeepNestLib.Core/NFP.cs b/PCUT/DeepNestLib.Core/NFP.cs new file mode 100644 index 0000000..98ae3fe --- /dev/null +++ b/PCUT/DeepNestLib.Core/NFP.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DeepNestLib +{ + public class NFP : IStringify + { + public int Z; + public bool fitted { get { return sheet != null; } } + public NFP sheet; + public object Tag; + public override string ToString() + { + var str1 = (Points != null) ? Points.Count() + "" : "null"; + return $"nfp: id: {id}; source: {source}; rotation: {rotation}; points: {str1}"; + } + public NFP() + { + Points = new SvgPoint[] { }; + } + + public string Name { get; set; } + public void AddPoint(SvgPoint point) + { + var list = Points.ToList(); + list.Add(point); + Points = list.ToArray(); + } + + #region gdi section + public bool isBin; + + #endregion + public void reverse() + { + Points = Points.Reverse().ToArray(); + } + + public StringBuilder GetXml() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(""); + sb.AppendLine(""); + sb.AppendLine(""); + foreach (var item in Points) + { + sb.AppendLine($""); + } + sb.AppendLine(""); + if (children != null) + foreach (var item in children) + { + sb.AppendLine(""); + foreach (var citem in item.Points) + { + sb.AppendLine($""); + } + sb.AppendLine(""); + } + + sb.AppendLine(""); + + return sb; + } + public double x { get; set; } + public double y { get; set; } + + public double WidthCalculated + { + get + { + var maxx = Points.Max(z => z.x); + var minx = Points.Min(z => z.x); + + return maxx - minx; + } + } + + public double HeightCalculated + { + get + { + var maxy = Points.Max(z => z.y); + var miny = Points.Min(z => z.y); + return maxy - miny; + } + } + + public SvgPoint this[int ind] + { + get + { + return Points[ind]; + } + } + + public List children; + + + + + public int Length + { + get + { + return Points.Length; + } + } + + //public float? width; + //public float? height; + public int length + { + get + { + return Points.Length; + } + } + + public int Id; + public int id + { + get + { + return Id; + } + set + { + Id = value; + } + } + + public double? offsetx; + public double? offsety; + public int? source = null; + public float Rotation; + + public float rotation + { + get + { + return Rotation; + } + set + { + Rotation = value; + } + } + public SvgPoint[] Points; + public float Area + { + get + { + float ret = 0; + if (Points.Length < 3) return 0; + List pp = new List(); + pp.AddRange(Points); + pp.Add(Points[0]); + for (int i = 1; i < pp.Count; i++) + { + var s0 = pp[i - 1]; + var s1 = pp[i]; + ret += (float)(s0.x * s1.y - s0.y * s1.x); + } + return (float)Math.Abs(ret / 2); + } + } + + public void push(SvgPoint svgPoint) + { + List points = new List(); + if (Points == null) + { + Points = new SvgPoint[] { }; + } + points.AddRange(Points); + points.Add(svgPoint); + Points = points.ToArray(); + + } + + public NFP slice(int v) + { + var ret = new NFP(); + List pp = new List(); + for (int i = v; i < length; i++) + { + pp.Add(new SvgPoint(this[i].x, this[i].y)); + + } + ret.Points = pp.ToArray(); + return ret; + } + + public string stringify() + { + throw new NotImplementedException(); + } + + public static NFP Create(params SvgPoint[] points) + { + if (points == null || points.Length == 0) + return new NFP(); + else + return new NFP + { + Points = points + }; + } + } +} diff --git a/PCUT/DeepNestLib.Core/NfpKey.cs b/PCUT/DeepNestLib.Core/NfpKey.cs new file mode 100644 index 0000000..e6b0814 --- /dev/null +++ b/PCUT/DeepNestLib.Core/NfpKey.cs @@ -0,0 +1,24 @@ +namespace DeepNestLib +{ + public class NfpKey : IStringify + { + + public NFP A; + public NFP B; + public float ARotation { get; set; } + public float BRotation { get; set; } + public bool Inside { get; set; } + + public int AIndex { get; set; } + public int BIndex { get; set; } + public object Asource; + public object Bsource; + + + public string stringify() + { + return $"A:{AIndex} B:{BIndex} inside:{Inside} Arotation:{ARotation} Brotation:{BRotation}"; + } + } +} + diff --git a/PCUT/DeepNestLib.Core/PlacementItem.cs b/PCUT/DeepNestLib.Core/PlacementItem.cs new file mode 100644 index 0000000..2bf93f7 --- /dev/null +++ b/PCUT/DeepNestLib.Core/PlacementItem.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace DeepNestLib +{ + public class PlacementItem + { + public double? mergedLength; + public object mergedSegments; + public List> nfp; + public int id; + public NFP hull; + public NFP hullsheet; + + public float rotation; + public double x; + public double y; + public int source; + } +} + diff --git a/PCUT/DeepNestLib.Core/PlacementTypeEnum.cs b/PCUT/DeepNestLib.Core/PlacementTypeEnum.cs new file mode 100644 index 0000000..b0ee253 --- /dev/null +++ b/PCUT/DeepNestLib.Core/PlacementTypeEnum.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DeepNestLib +{ + public enum PlacementTypeEnum + { + box, gravity, squeeze + } +} diff --git a/PCUT/DeepNestLib.Core/PolygonBounds.cs b/PCUT/DeepNestLib.Core/PolygonBounds.cs new file mode 100644 index 0000000..6d995d7 --- /dev/null +++ b/PCUT/DeepNestLib.Core/PolygonBounds.cs @@ -0,0 +1,17 @@ +namespace DeepNestLib +{ + public class PolygonBounds + { + public double x; + public double y; + public double width; + public double height; + public PolygonBounds(double _x, double _y, double _w, double _h) + { + x = _x; + y = _y; + width = _w; + height = _h; + } + } +} diff --git a/PCUT/DeepNestLib.Core/PopulationItem.cs b/PCUT/DeepNestLib.Core/PopulationItem.cs new file mode 100644 index 0000000..ea520e8 --- /dev/null +++ b/PCUT/DeepNestLib.Core/PopulationItem.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DeepNestLib +{ + public class PopulationItem + { + public object processing = null; + + public double? fitness; + + public float[] Rotation; + public List placements; + + public NFP[] paths; + public double area; + } +} diff --git a/PCUT/DeepNestLib.Core/RectangleSheet.cs b/PCUT/DeepNestLib.Core/RectangleSheet.cs new file mode 100644 index 0000000..4e33f53 --- /dev/null +++ b/PCUT/DeepNestLib.Core/RectangleSheet.cs @@ -0,0 +1,15 @@ +namespace DeepNestLib +{ + public class RectangleSheet : Sheet + { + public void Rebuild() + { + Points = new SvgPoint[] { }; + AddPoint(new SvgPoint(x, y)); + AddPoint(new SvgPoint(x + Width, y)); + AddPoint(new SvgPoint(x + Width, y + Height)); + AddPoint(new SvgPoint(x, y + Height)); + } + } +} + diff --git a/PCUT/DeepNestLib.Core/Sheet.cs b/PCUT/DeepNestLib.Core/Sheet.cs new file mode 100644 index 0000000..2467f17 --- /dev/null +++ b/PCUT/DeepNestLib.Core/Sheet.cs @@ -0,0 +1,9 @@ +namespace DeepNestLib +{ + public class Sheet : NFP + { + public double Width; + public double Height; + } +} + diff --git a/PCUT/DeepNestLib.Core/SheetPlacement.cs b/PCUT/DeepNestLib.Core/SheetPlacement.cs new file mode 100644 index 0000000..683523d --- /dev/null +++ b/PCUT/DeepNestLib.Core/SheetPlacement.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace DeepNestLib +{ + public class SheetPlacement + { + public double? fitness; + + public float[] Rotation; + public List[] placements; + + public NFP[] paths; + public double area; + public double mergedLength; + public int index; + } +} + diff --git a/PCUT/DeepNestLib.Core/SheetPlacementItem.cs b/PCUT/DeepNestLib.Core/SheetPlacementItem.cs new file mode 100644 index 0000000..398f27e --- /dev/null +++ b/PCUT/DeepNestLib.Core/SheetPlacementItem.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DeepNestLib +{ + public class SheetPlacementItem + { + public int sheetId; + public int sheetSource; + + public List sheetplacements = new List(); + public List placements = new List(); + } +} diff --git a/PCUT/DeepNestLib.Core/Simplify.cs b/PCUT/DeepNestLib.Core/Simplify.cs new file mode 100644 index 0000000..a39fa96 --- /dev/null +++ b/PCUT/DeepNestLib.Core/Simplify.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DeepNestLib +{ + public class Simplify + { + + // to suit your point format, run search/replace for '.x' and '.y'; + // for 3D version, see 3d branch (configurability would draw significant performance overhead) + + // square distance between 2 points + public static double getSqDist(SvgPoint p1, SvgPoint p2) + { + + var dx = p1.x - p2.x; + var dy = p1.y - p2.y; + + return dx * dx + dy * dy; + } + + // square distance from a point to a segment + public static double getSqSegDist(SvgPoint p, SvgPoint p1, SvgPoint p2) + { + + var x = p1.x; + var y = p1.y; + var dx = p2.x - x; + var dy = p2.y - y; + + if (dx != 0 || dy != 0) + { + + var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy); + + if (t > 1) + { + x = p2.x; + y = p2.y; + + } + else if (t > 0) + { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return dx * dx + dy * dy; + } + // rest of the code doesn't care about point format + + // basic distance-based simplification + public static NFP simplifyRadialDist(NFP points, double? sqTolerance) + { + + var prevPoint = points[0]; + var newPoints = new NFP(); + newPoints.AddPoint(prevPoint); + + SvgPoint point = null; + int i = 1; + for (var len = points.length; i < len; i++) + { + point = points[i]; + + if (point.marked || getSqDist(point, prevPoint) > sqTolerance) + { + newPoints.AddPoint(point); + prevPoint = point; + } + } + + if (prevPoint != point) newPoints.AddPoint(point); + return newPoints; + } + + + public static void simplifyDPStep(NFP points, int first, int last, double? sqTolerance, NFP simplified) + { + var maxSqDist = sqTolerance; + var index = -1; + var marked = false; + for (var i = first + 1; i < last; i++) + { + var sqDist = getSqSegDist(points[i], points[first], points[last]); + + if (sqDist > maxSqDist) + { + index = i; + maxSqDist = sqDist; + } + /*if(points[i].marked && maxSqDist <= sqTolerance){ + index = i; + marked = true; + }*/ + } + + /*if(!points[index] && maxSqDist > sqTolerance){ + console.log('shit shit shit'); + }*/ + + if (maxSqDist > sqTolerance || marked) + { + if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified); + simplified.push(points[index]); + if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified); + } + } + + // simplification using Ramer-Douglas-Peucker algorithm + public static NFP simplifyDouglasPeucker(NFP points, double? sqTolerance) + { + var last = points.length - 1; + + var simplified = new NFP(); + simplified.AddPoint(points[0]); + simplifyDPStep(points, 0, last, sqTolerance, simplified); + simplified.push(points[last]); + + return simplified; + } + + // both algorithms combined for awesome performance + public static NFP simplify(NFP points, double? tolerance, bool highestQuality) + { + + if (points.length <= 2) return points; + + var sqTolerance = (tolerance != null) ? (tolerance * tolerance) : 1; + + points = highestQuality ? points : simplifyRadialDist(points, sqTolerance); + points = simplifyDouglasPeucker(points, sqTolerance); + + return points; + } + } +} diff --git a/PCUT/DeepNestLib.Core/SvgNestConfig.cs b/PCUT/DeepNestLib.Core/SvgNestConfig.cs new file mode 100644 index 0000000..54cdf4e --- /dev/null +++ b/PCUT/DeepNestLib.Core/SvgNestConfig.cs @@ -0,0 +1,25 @@ +namespace DeepNestLib +{ + public class SvgNestConfig + { + public PlacementTypeEnum placementType = PlacementTypeEnum.box; + public double curveTolerance = 0.72; + public double scale = 25; + public double clipperScale = 10000000; + public bool exploreConcave = false; + public int mutationRate = 10; + public int populationSize = 10; + public int rotations = 4; + public double spacing = 10; + public double sheetSpacing = 0; + public bool useHoles = false; + public double timeRatio = 0.5; + public bool mergeLines = false; + public bool simplify; + + #region port features (don't exist in the original DeepNest project) + public bool clipByHull = false; + public bool clipByRects = true; //clip by AABB + MinRect + #endregion + } +} diff --git a/PCUT/DeepNestLib.Core/SvgPoint.cs b/PCUT/DeepNestLib.Core/SvgPoint.cs new file mode 100644 index 0000000..ed7c1d0 --- /dev/null +++ b/PCUT/DeepNestLib.Core/SvgPoint.cs @@ -0,0 +1,33 @@ +namespace DeepNestLib +{ + public class SvgPoint + { + public bool exact = true; + public override string ToString() + { + return "x: " + x + "; y: " + y; + } + public int id; + public SvgPoint(double _x, double _y) + { + x = _x; + y = _y; + } + internal SvgPoint(SvgPoint point) + { + this.exact = point.exact; + this.id = point.id; + this.marked = point.marked; + this.x = point.x; + this.y = point.y; + } + public bool marked; + public double x; + public double y; + public SvgPoint Clone() + { + return new SvgPoint(this); + } + } +} + diff --git a/PCUT/DeepNestLib.Core/_Clipper.cs b/PCUT/DeepNestLib.Core/_Clipper.cs new file mode 100644 index 0000000..02d28a2 --- /dev/null +++ b/PCUT/DeepNestLib.Core/_Clipper.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace DeepNestLib +{ + public class _Clipper + { + public static ClipperLib.IntPoint[] ScaleUpPaths(SvgPoint[] points, double scale = 1) + { + var result = new ClipperLib.IntPoint[points.Length]; + + Parallel.For(0, points.Length, i => result[i] = new ClipperLib.IntPoint((long)Math.Round((decimal)points[i].x * (decimal)scale), (long)Math.Round((decimal)points[i].y * (decimal)scale))); + + return result.ToArray(); + } // 2 secs + public static ClipperLib.IntPoint[] ScaleUpPaths(NFP p, double scale = 1) + { + List ret = new List(); + + for (int i = 0; i < p.Points.Count(); i++) + { + //p.Points[i] = new SvgNestPort.SvgPoint((float)Math.Round(p.Points[i].x * scale), (float)Math.Round(p.Points[i].y * scale)); + ret.Add(new ClipperLib.IntPoint( + (long)Math.Round((decimal)p.Points[i].x * (decimal)scale), + (long)Math.Round((decimal)p.Points[i].y * (decimal)scale) + )); + + } + return ret.ToArray(); + } + /*public static IntPoint[] ScaleUpPath(IntPoint[] p, double scale = 1) + { + for (int i = 0; i < p.Length; i++) + { + + //p[i] = new IntPoint(p[i].X * scale, p[i].Y * scale); + p[i] = new IntPoint( + (long)Math.Round((decimal)p[i].X * (decimal)scale), + (long)Math.Round((decimal)p[i].Y * (decimal)scale)); + } + return p.ToArray(); + } + public static void ScaleUpPaths(List> p, double scale = 1) + { + for (int i = 0; i < p.Count; i++) + { + for (int j = 0; j < p[i].Count; j++) + { + p[i][j] = new IntPoint(p[i][j].X * scale, p[i][j].Y * scale); + + } + } + + + }*/ + } +} + diff --git a/PCUT/Http.Core/Constants/HttpConstants.cs b/PCUT/Http.Core/Constants/HttpConstants.cs new file mode 100644 index 0000000..0871f69 --- /dev/null +++ b/PCUT/Http.Core/Constants/HttpConstants.cs @@ -0,0 +1,45 @@ +namespace Http.Core.Constants +{ + public static class HttpConstants + { + public static class ClientNames + { + public const string AuthClient = "LoginClient"; + public const string ApiClient = "ApiClient"; + } + + public static class Auth + { + public const string BaseUrl = "http://14.225.255.245:3003"; + + public const string Login = "api/auth/login"; + public const string Refresh = "api/auth/refresh"; + + } + + public static class Api + { + public const string BaseUrl = "http://14.225.255.245:3003"; + + public const string LogOut = "api/auth/logout"; + public const string Profile = "api/auth/profile"; + + public const string Users = "api/users"; + public const string UserById = "api/users/{0}"; + public const string UserUnlockDevice = "api/users/{0}/device/unlock"; + + public const string Categories = "api/categories"; + public const string CategoryById = "api/categories/{0}"; + + public const string Upload = "api/files/upload"; + public const string Download = "api/files/{0}"; + + public const string CdrFiles = "api/cdr-files"; + public const string CdrFileById = "api/cdr-files/{0}"; + + public const string Metadata = "api/metadata"; + public const string MetadataById = "api/metadata/{0}"; + + } + } +} diff --git a/PCUT/Http.Core/Contexts/UserContext.cs b/PCUT/Http.Core/Contexts/UserContext.cs new file mode 100644 index 0000000..781f52f --- /dev/null +++ b/PCUT/Http.Core/Contexts/UserContext.cs @@ -0,0 +1,75 @@ +using Microsoft.IdentityModel.JsonWebTokens; +using PCUT.Entities; +using System; + +namespace Http.Core.Contexts +{ + public class UserContext : IDisposable + { + private UserContext() { } + private static readonly object _instanceLock = new object(); + private static UserContext _instance; + public static UserContext Instance + { + get + { + if (_instance == null) + { + lock (_instanceLock) + { + if (_instance == null) + _instance = new UserContext(); + } + } + return _instance; + } + } + + public UserProfile Profile { get; private set; } + public string AccessToken { get; private set; } + public string RefreshToken { get; private set; } + public string DeviceId { get; private set; } + public DateTime? ExpiredTime { get; private set; } + + public bool IsPermittedEdit => Profile.Role == "admin" || (Profile.IsPermittedEdit ?? false); + + public void SetDeviceId(string deviceId) + { + if (DeviceId == null) + { + DeviceId = deviceId; + } + } + + private readonly object _updateLock = new object(); + internal void SetValue(string accessToken, string refreshToken, string userName = null) + { + lock(_updateLock) + { + AccessToken = accessToken; + RefreshToken = refreshToken; + try + { + var token = new JsonWebToken(AccessToken); + ExpiredTime = token.ValidTo != DateTime.MinValue ? token.ValidTo : (DateTime?)null; + } + catch { } + } + } + + internal void SetProfile(UserProfile profile) + { + Profile = profile; + } + + public void Dispose() + { + AccessToken = null; + RefreshToken = null; + DeviceId = null; + ExpiredTime = null; + Profile = null; + _instance = null; + } + } +} diff --git a/PCUT/Http.Core/Extensions/CategoryExtensions.cs b/PCUT/Http.Core/Extensions/CategoryExtensions.cs new file mode 100644 index 0000000..1d3a778 --- /dev/null +++ b/PCUT/Http.Core/Extensions/CategoryExtensions.cs @@ -0,0 +1,37 @@ +using Http.Core.Contexts; +using PCUT.Entities; +using PCUT.Entities.ApiResponse; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using static Http.Core.Constants.HttpConstants; + +namespace Http.Core.Extensions +{ + public static class CategoryExtensions + { + public static async Task> GetCategoriesAsync(this HttpClient client, bool filterByRole = false) + { + try + { + var response = await client.GetAsync(Api.Categories); + if (response.IsSuccessStatusCode) + { + var apiResponse = await response.DeserializeObjectAsync>>(); + var permittedCategories = apiResponse.Data; + if (filterByRole && UserContext.Instance?.Profile?.Role != "admin") + permittedCategories = permittedCategories.Where(x => UserContext.Instance.Profile.Permissions.Contains(x.Id)); + + return permittedCategories; + } + } + catch + { + } + return Enumerable.Empty(); + } + } +} diff --git a/PCUT/Http.Core/Extensions/HttpExtensions.cs b/PCUT/Http.Core/Extensions/HttpExtensions.cs new file mode 100644 index 0000000..a2a69c8 --- /dev/null +++ b/PCUT/Http.Core/Extensions/HttpExtensions.cs @@ -0,0 +1,179 @@ +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using Http.Core.Contexts; +using Http.Core.Models; +using static Http.Core.Constants.HttpConstants; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using PCUT.Entities.ApiResponse; +using PCUT.Entities; + +namespace Http.Core.Extensions +{ + public static class HttpExtensions + { + public static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + public static readonly JsonSerializerSettings IgnoreNullJsonSerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore + }; + + public static readonly JsonSerializer DefaultSerializer = JsonSerializer.Create(JsonSerializerSettings); + + public static string FormatRoute(this string path, params object[] routeParams) + { + if (string.IsNullOrEmpty(path)) + return null; + return string.Format(path, routeParams); + } + + public static Task PostAsJsonAsync(this HttpClient client, string requestUri, object content = null, bool ignoreNull = false) + { + var jsonContent = content != null ? JsonConvert.SerializeObject(content, ignoreNull ? IgnoreNullJsonSerializerSettings : JsonSerializerSettings) : null; + var httpContent = jsonContent != null ? new StringContent(jsonContent, Encoding.UTF8, "application/json") : null; + return client.PostAsync(requestUri, httpContent); + } + + public static Task PutAsJsonAsync(this HttpClient client, string requestUri, object content = null, bool ignoreNull = false) + { + var jsonContent = content != null ? JsonConvert.SerializeObject(content, ignoreNull ? IgnoreNullJsonSerializerSettings : JsonSerializerSettings) : null; + var httpContent = jsonContent != null ? new StringContent(jsonContent, Encoding.UTF8, "application/json") : null; + return client.PutAsync(requestUri, httpContent); + } + + public static Task UploadAsync(this HttpClient client, string requestUri, Stream stream, string name, string filename) + { + var streamContent = new StreamContent(stream); + var httpContent = new MultipartFormDataContent + { + { streamContent, name, filename } + }; + return client.PostAsync(requestUri, httpContent); + } + + public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream outputStream) + { + HttpResponseMessage response = null; + var isSuccess = true; + try + { + response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + await response.Content.CopyToAsync(outputStream); + } + catch + { + isSuccess = false; + } + finally + { + response?.Dispose(); + } + return isSuccess; + } + + public static async Task LoginAsync(this HttpClient client, LoginRequest loginRequest, string username) + { + var response = await client.PostAsJsonAsync(Auth.Login, loginRequest); + if (response.IsSuccessStatusCode) + { + var userCredential = await response.DeserializeObjectAsync(); + UserContext.Instance.SetValue(userCredential.AccessToken, userCredential.RefreshToken, username); + return new LoginResult { Success = true }; + } + else + { + var responseContent = await response.DeserializeObjectAsync(ensureSuccess: false); + return new LoginResult { Success = false, Message = responseContent.ErrorMessage }; + } + } + + public static async Task LogOutAsync(this HttpClient client) + { + var response = await client.PostAsync(Api.LogOut, null); + if (response.IsSuccessStatusCode) + { + return true; + } + return false; + } + + public static async Task RefreshTokenAsync(this HttpClient client) + { + var response = await client.PostAsJsonAsync(Auth.Refresh, new RefreshTokenRequest()); + if (response.IsSuccessStatusCode) + { + var userCredential = await response.DeserializeObjectAsync(); + UserContext.Instance.SetValue(userCredential.AccessToken, userCredential.RefreshToken, userCredential.Username); + return true; + } + return false; + } + + public static async Task LoadUserProfileAsync(this HttpClient client) + { + var response = await client.GetAsync(Api.Profile); + if (response.IsSuccessStatusCode) + { + var content = await response.DeserializeObjectAsync>(); + UserContext.Instance.SetProfile(content.Data); + } + } + + public static async Task DeserializeObjectAsync(this HttpResponseMessage response, bool ensureSuccess = true) + { + if (ensureSuccess) + response.EnsureSuccessStatusCode(); + + if (response.StatusCode == System.Net.HttpStatusCode.NoContent) + return default; + + var content = response.Content; + if (content.Headers.ContentLength <= 0 || !content.Headers.ContentType.MediaType.Contains("application/json")) + return default; + + var contentStream = await response.Content.ReadAsStreamAsync(); + using (var reader = new StreamReader(contentStream)) + using (var jsonReader = new JsonTextReader(reader)) + { + return DefaultSerializer.Deserialize(jsonReader); + } + } + + public static StringBuilder AppendFilter(this StringBuilder builder, string key, string op, string value) + { + if (builder.Length > 0) + builder.Append(','); + return builder.Append('\"') + .Append(key).Append(':') + .Append(op).Append(':') + .Append(value) + .Append('\"'); + } + + public static StringBuilder AppendFilter(this StringBuilder builder, string key, string value) + { + return builder.AppendFilter(key, "eq", value); + } + + public static StringBuilder BuildFilter(this StringBuilder builder) + { + return builder.Insert(0, '[').Append(']'); + } + + public class LoginResult + { + public bool Success { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } = string.Empty; + } + } +} diff --git a/PCUT/Http.Core/Handlers/AuthenticationHandler.cs b/PCUT/Http.Core/Handlers/AuthenticationHandler.cs new file mode 100644 index 0000000..e065fad --- /dev/null +++ b/PCUT/Http.Core/Handlers/AuthenticationHandler.cs @@ -0,0 +1,68 @@ +using Http.Core.Constants; +using Http.Core.Contexts; +using Http.Core.Extensions; +using Http.Core.Models; +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace Http.Core.Handlers +{ + public class AuthenticationHandler : DelegatingHandler + { + public static readonly TimeSpan _requiredRefreshTime = TimeSpan.FromMinutes(5); + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var shouldContinue = await AppendTokenAsync(request); + if (!shouldContinue) + return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); + return await base.SendAsync(request, cancellationToken); + } + + private static readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim(1, 1); + private async Task AppendTokenAsync(HttpRequestMessage request) + { + if (request.Headers.Authorization == null) + { + if (UserContext.Instance.AccessToken == null) + return false; + + var expiredTime = UserContext.Instance.ExpiredTime; + if (expiredTime != null || expiredTime - DateTime.UtcNow < _requiredRefreshTime) + { + await _refreshSemaphore.WaitAsync(); + bool refreshSuccess; + { + expiredTime = UserContext.Instance.ExpiredTime; + if (expiredTime != null || expiredTime - DateTime.UtcNow < _requiredRefreshTime) + refreshSuccess = await RefreshTokenAsync(); + else + refreshSuccess = true; // Some other thread already refreshed token + } + _refreshSemaphore.Release(); + if (!refreshSuccess) + return false; + } + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", UserContext.Instance.AccessToken); + return true; + } + return true; + } + + private async Task RefreshTokenAsync() + { + try + { + using (var client = HttpClientFactory.CreateClient(HttpConstants.ClientNames.AuthClient)) + { + return await client.RefreshTokenAsync(); + } + } + catch { } + return false; + } + } +} diff --git a/PCUT/Http.Core/Handlers/DeviceAuthHandler.cs b/PCUT/Http.Core/Handlers/DeviceAuthHandler.cs new file mode 100644 index 0000000..bee7eec --- /dev/null +++ b/PCUT/Http.Core/Handlers/DeviceAuthHandler.cs @@ -0,0 +1,19 @@ +using Http.Core.Contexts; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Http.Core.Handlers +{ + public class DeviceAuthHandler : DelegatingHandler + { + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + request.Headers.Add("x-device-id", $"{UserContext.Instance.DeviceId}"); + return base.SendAsync(request, cancellationToken); + } + } +} diff --git a/PCUT/Http.Core/Http.Core.csproj b/PCUT/Http.Core/Http.Core.csproj new file mode 100644 index 0000000..c5f0ed0 --- /dev/null +++ b/PCUT/Http.Core/Http.Core.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/PCUT/Http.Core/HttpClientFactory.cs b/PCUT/Http.Core/HttpClientFactory.cs new file mode 100644 index 0000000..79fd01a --- /dev/null +++ b/PCUT/Http.Core/HttpClientFactory.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; + +namespace Http.Core +{ + public class HttpClientFactory : IDisposable + { + private HttpClientFactory() { } + private static readonly object _instanceLock = new object(); + private static HttpClientFactory _instance; + public static HttpClientFactory Instance + { + get + { + if (_instance == null) + { + lock (_instanceLock) + { + if (_instance == null) + _instance = new HttpClientFactory(); + } + } + return _instance; + } + } + + public void Dispose() + { + foreach (var config in _configs) + { + config.Value.Dispose(); + } + _configs.Clear(); + } + + private readonly ConcurrentDictionary _configs = new ConcurrentDictionary(); + + public delegate void ConfigureBuilder(HttpClientBuilder builder); + public static HttpClientBuilder AddHttpClient(string name) + { + var builder = new HttpClientBuilder(); + _ = Instance._configs.TryAdd(name, builder.Config); + return builder; + } + + public static HttpClient CreateClient(string name) + { + if (!Instance._configs.ContainsKey(name)) + _ = Instance._configs.TryAdd(name, new HttpClientConfig()); + var config = Instance._configs[name]; + + var client = new HttpClient(config.Handler, false); + config.ConfigureClient?.Invoke(client); + return client; + } + + public delegate void ConfigureClient(HttpClient client); + public delegate DelegatingHandler HandlerFactory(); + public class HttpClientBuilder + { + internal HttpClientConfig Config { get; } = new HttpClientConfig(); + + public HttpClientBuilder AddConfigureClient(ConfigureClient configureClient) + { + Config.ConfigureClient += configureClient; + return this; + } + + public HttpClientBuilder AddHttpMessageHandler(HandlerFactory configureHandler) + { + Config.HandlerFactories.Add(configureHandler); + return this; + } + } + + internal class HttpClientConfig : IDisposable + { + internal ConfigureClient ConfigureClient { get; set; } + internal ICollection HandlerFactories { get; private set; } + + internal HttpClientConfig() + { + HandlerFactories = new List(); + } + + private readonly object _handlerLock = new object(); + private HttpMessageHandler _handler; + internal HttpMessageHandler Handler + { + get + { + if (_handler == null) + { + lock (_handlerLock) + { + if (_handler == null) + _handler = BuildHandler(); + } + } + return _handler; + } + } + + private HttpMessageHandler BuildHandler() + { + if (HandlerFactories == null || !HandlerFactories.Any()) + return new HttpClientHandler(); + + DelegatingHandler handler, intermediate; + handler = intermediate = HandlerFactories.First().Invoke(); + foreach (var handlerFactory in HandlerFactories.Skip(1)) + { + intermediate.InnerHandler = handlerFactory(); + intermediate = intermediate.InnerHandler as DelegatingHandler; + } + intermediate.InnerHandler = new HttpClientHandler(); + return handler; + } + + public void Dispose() + { + ConfigureClient = null; + HandlerFactories?.Clear(); + HandlerFactories = null; + _handler?.Dispose(); + } + } + } +} diff --git a/PCUT/Http.Core/Models/LoginRequest.cs b/PCUT/Http.Core/Models/LoginRequest.cs new file mode 100644 index 0000000..8c77070 --- /dev/null +++ b/PCUT/Http.Core/Models/LoginRequest.cs @@ -0,0 +1,14 @@ +namespace Http.Core.Models +{ + public class LoginRequest + { + public string Username { get; set; } + public string Password { get; set; } + + public LoginRequest(string username, string password) + { + Username = username; + Password = password; + } + } +} diff --git a/PCUT/Http.Core/Models/RefreshTokenRequest.cs b/PCUT/Http.Core/Models/RefreshTokenRequest.cs new file mode 100644 index 0000000..ce3eb50 --- /dev/null +++ b/PCUT/Http.Core/Models/RefreshTokenRequest.cs @@ -0,0 +1,11 @@ +using Http.Core.Contexts; +using Newtonsoft.Json; + +namespace Http.Core.Models +{ + internal class RefreshTokenRequest + { + [JsonProperty("refresh_token")] + public string RefreshToken => UserContext.Instance.RefreshToken; + } +} diff --git a/PCUT/Http.Core/PathBuilder.cs b/PCUT/Http.Core/PathBuilder.cs new file mode 100644 index 0000000..502b04c --- /dev/null +++ b/PCUT/Http.Core/PathBuilder.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Http.Core +{ + public class PathBuilder + { + private readonly string _uriTemplate; + private readonly List _routeParams; + + private PathBuilder(string uriTemplate) + { + _uriTemplate = uriTemplate; + _routeParams = new List(); + } + + public static PathBuilder FromRouteTemplate(string urlTemplate) + { + return new PathBuilder(urlTemplate); + } + + public PathBuilder AddRouteParams(params string[] routeParams) + { + _routeParams.AddRange(routeParams); + return this; + } + + public QueryBuilder ToQuery() + { + return new QueryBuilder(_uriTemplate, _routeParams); + } + + public QueryBuilder AddQuery(string key, string value) + { + return ToQuery().AddQuery(key, value); + } + + public string Build() + { + if (!_routeParams.Any()) + return _uriTemplate; + + return new StringBuilder() + .AppendFormat(_uriTemplate, _routeParams.ToArray()) + .ToString(); + } + + public class QueryBuilder + { + private readonly string _uriTemplate; + private readonly List _routeParams; + private readonly IDictionary _queryParams; + + internal QueryBuilder(string uriTemplate, List routeParams) + { + _uriTemplate = uriTemplate; + _routeParams = routeParams; + _queryParams = new Dictionary(); + } + + public QueryBuilder AddQuery(string key, string value) + { + _queryParams[key] = Uri.EscapeDataString(value); + return this; + } + + public string Build() + { + var builder = new StringBuilder(); + + if (_routeParams.Any()) + builder.AppendFormat(_uriTemplate, _routeParams.ToArray()); + else + builder.Append(_uriTemplate); + + if (_queryParams.Any()) + { + builder.Append("?"); + foreach (var keyValuePair in _queryParams) + { + builder.Append(keyValuePair.Key) + .Append('=') + .Append(keyValuePair.Value) + .Append('&'); + } + builder.Remove(builder.Length - 1, 1); // remove last & character + } + return builder.ToString(); + } + } + } +} diff --git a/PCUT/MinkowskiCpp/Minkowski.cpp b/PCUT/MinkowskiCpp/Minkowski.cpp new file mode 100644 index 0000000..1b7252c --- /dev/null +++ b/PCUT/MinkowskiCpp/Minkowski.cpp @@ -0,0 +1,337 @@ +#include "pch.h" +#include "Minkowski.h" +#include + +#undef min +#undef max + +typedef boost::polygon::point_data point; +typedef boost::polygon::polygon_set_data polygon_set; +typedef boost::polygon::polygon_with_holes_data polygon; +typedef std::pair edge; + +using namespace MinkowskiCpp; +using namespace Platform; +using namespace boost::polygon::operators; +using namespace std; + +Minkowski::Minkowski() +{ +} + +void convolve_two_segments(std::vector& figure, const edge& a, const edge& b) { + using namespace boost::polygon; + figure.clear(); + figure.push_back(point(a.first)); + figure.push_back(point(a.first)); + figure.push_back(point(a.second)); + figure.push_back(point(a.second)); + convolve(figure[0], b.second); + convolve(figure[1], b.first); + convolve(figure[2], b.first); + convolve(figure[3], b.second); +} + +template +void convolve_two_point_sequences(polygon_set& result, itrT1 ab, itrT1 ae, itrT2 bb, itrT2 be) { + using namespace boost::polygon; + if (ab == ae || bb == be) + return; + point first_a = *ab; + point prev_a = *ab; + std::vector vec; + polygon poly; + ++ab; + for (; ab != ae; ++ab) { + point first_b = *bb; + point prev_b = *bb; + itrT2 tmpb = bb; + ++tmpb; + for (; tmpb != be; ++tmpb) { + convolve_two_segments(vec, std::make_pair(prev_b, *tmpb), std::make_pair(prev_a, *ab)); + set_points(poly, vec.begin(), vec.end()); + result.insert(poly); + prev_b = *tmpb; + } + prev_a = *ab; + } +} + +template +void convolve_point_sequence_with_polygons(polygon_set& result, itrT b, itrT e, const std::vector& polygons) { + using namespace boost::polygon; + for (std::size_t i = 0; i < polygons.size(); ++i) { + convolve_two_point_sequences(result, b, e, begin_points(polygons[i]), end_points(polygons[i])); + for (polygon_with_holes_traits::iterator_holes_type itrh = begin_holes(polygons[i]); + itrh != end_holes(polygons[i]); ++itrh) { + convolve_two_point_sequences(result, b, e, begin_points(*itrh), end_points(*itrh)); + } + } +} + +void convolve_two_polygon_sets(polygon_set& result, const polygon_set& a, const polygon_set& b) { + using namespace boost::polygon; + result.clear(); + std::vector a_polygons; + std::vector b_polygons; + a.get(a_polygons); + b.get(b_polygons); + for (std::size_t ai = 0; ai < a_polygons.size(); ++ai) { + convolve_point_sequence_with_polygons(result, begin_points(a_polygons[ai]), + end_points(a_polygons[ai]), b_polygons); + for (polygon_with_holes_traits::iterator_holes_type itrh = begin_holes(a_polygons[ai]); + itrh != end_holes(a_polygons[ai]); ++itrh) { + convolve_point_sequence_with_polygons(result, begin_points(*itrh), + end_points(*itrh), b_polygons); + } + for (std::size_t bi = 0; bi < b_polygons.size(); ++bi) { + polygon tmp_poly = a_polygons[ai]; + result.insert(convolve(tmp_poly, *(begin_points(b_polygons[bi])))); + tmp_poly = b_polygons[bi]; + result.insert(convolve(tmp_poly, *(begin_points(a_polygons[ai])))); + } + } +} + +double inputscale; + +using namespace boost::polygon; + +vector Avec; +vector Bvec; +vector> chlds; +vector> retpnts; +vector> retholes; + + +void MinkowskiCpp::Minkowski::getResults(Platform::WriteOnlyArray^ data, Platform::WriteOnlyArray^ holesData) +{ + int index = 0; + for (int i = 0; i < retpnts.size(); i++) { + for (int j = 0; j < retpnts[i].size(); j++) { + data[index] = retpnts[i][j]; + index++; + } + } + + index = 0; + for (int i = 0; i < retholes.size(); i++) { + for (int j = 0; j < retholes[i].size(); j++) { + holesData[index] = retholes[i][j]; + index++; + } + } +} + +void MinkowskiCpp::Minkowski::getSizes1(Platform::WriteOnlyArray^ sizes) +{ + sizes[0] = retpnts.size(); + sizes[1] = retholes.size(); +} + +void MinkowskiCpp::Minkowski::getSizes2(Platform::WriteOnlyArray^ sizes1, Platform::WriteOnlyArray^ sizes2) +{ + for (int i = 0; i < retpnts.size(); i++) { + sizes1[i] = retpnts[i].size(); + } + for (int i = 0; i < retholes.size(); i++) { + sizes2[i] = retholes[i].size(); + } +} + +void MinkowskiCpp::Minkowski::setData(int cntA, + const Platform::Array^ pntsA, + int holesCnt, + const Platform::Array^ holesSizes, + const Platform::Array^ holesPoints, + int cntB, + const Platform::Array^ pntsB) +{ + Avec.clear(); + Bvec.clear(); + chlds.clear(); + + retpnts.clear(); + retholes.clear(); + + for (int i = 0; i < cntA; i++) { + Avec.push_back(pntsA[i]); + } + for (int i = 0; i < cntB; i++) { + Bvec.push_back(pntsB[i]); + } + + int index = 0; + for (int i = 0; i < holesCnt; i++) { + chlds.push_back(vector()); + for (int j = 0; j < holesSizes[i]; j++) { + chlds.back().push_back(holesPoints[index]); + index++; + } + } +} + + +void MinkowskiCpp::Minkowski::calculateNFP() +{ + + polygon_set a, b, c; + std::vector polys; + std::vector pts; + + + + // get maximum bounds for scaling factor + unsigned int len = Avec.size() / 2; + double Amaxx = 0; + double Aminx = 0; + double Amaxy = 0; + double Aminy = 0; + for (unsigned int i = 0; i < len; i++) { + double x1 = Avec[i * 2]; + double y1 = Avec[i * 2 + 1]; + Amaxx = (std::max)(Amaxx, (double)x1); + Aminx = (std::min)(Aminx, (double)x1); + Amaxy = (std::max)(Amaxy, (double)y1); + Aminy = (std::min)(Aminy, (double)y1); + } + + len = Bvec.size() / 2; + double Bmaxx = 0; + double Bminx = 0; + double Bmaxy = 0; + double Bminy = 0; + for (unsigned int i = 0; i < len * 2; i += 2) { + double x1 = Bvec[i]; + double y1 = Bvec[i + 1]; + Bmaxx = (std::max)(Bmaxx, (double)x1); + Bminx = (std::min)(Bminx, (double)x1); + Bmaxy = (std::max)(Bmaxy, (double)y1); + Bminy = (std::min)(Bminy, (double)y1); + } + + double Cmaxx = Amaxx + Bmaxx; + double Cminx = Aminx + Bminx; + double Cmaxy = Amaxy + Bmaxy; + double Cminy = Aminy + Bminy; + + double maxxAbs = (std::max)(Cmaxx, std::fabs(Cminx)); + double maxyAbs = (std::max)(Cmaxy, std::fabs(Cminy)); + + double maxda = (std::max)(maxxAbs, maxyAbs); + int maxi = std::numeric_limits::max(); + + if (maxda < 1) { + maxda = 1; + } + + // why 0.1? dunno. it doesn't screw up with 0.1 + inputscale = (0.1f * (double)(maxi)) / maxda; + + //double scale = 1000; + len = Avec.size() / 2; + + for (unsigned int i = 0; i < len; i++) { + double x1 = Avec[i * 2]; + double y1 = Avec[i * 2 + 1]; + int x = (int)(inputscale * (double)x1); + int y = (int)(inputscale * (double)y1); + + pts.push_back(point(x, y)); + } + + polygon poly; + boost::polygon::set_points(poly, pts.begin(), pts.end()); + a += poly; + + // subtract holes from a here... + + len = chlds.size(); + + for (unsigned int i = 0; i < len; i++) { + vector hole = chlds[i]; + pts.clear(); + unsigned int hlen = hole.size() / 2; + for (unsigned int j = 0; j < hlen; j++) { + double x1 = hole[j * 2]; + double y1 = hole[j * 2 + 1]; + int x = (int)(inputscale * (double)x1); + int y = (int)(inputscale * (double)y1); + pts.push_back(point(x, y)); + } + boost::polygon::set_points(poly, pts.begin(), pts.end()); + a -= poly; + } + + //and then load points B + pts.clear(); + len = Bvec.size() / 2; + + //javascript nfps are referenced with respect to the first point + double xshift = 0; + double yshift = 0; + + for (unsigned int i = 0; i < len; i++) { + double x1 = Bvec[i * 2]; + double y1 = Bvec[i * 2 + 1]; + + int x = -(int)(inputscale * (double)x1); + int y = -(int)(inputscale * (double)y1); + pts.push_back(point(x, y)); + + if (i == 0) { + xshift = (double)x1; + yshift = (double)y1; + } + } + + boost::polygon::set_points(poly, pts.begin(), pts.end()); + b += poly; + + polys.clear(); + + convolve_two_polygon_sets(c, a, b); + c.get(polys); + + for (unsigned int i = 0; i < polys.size(); ++i) { + + vector pointlist = vector(); + + int j = 0; + + for (polygon_traits::iterator_type itr = polys[i].begin(); itr != polys[i].end(); ++itr) { + + // std::cout << (double)(*itr).get(boost::polygon::HORIZONTAL) / inputscale << std::endl; + double x1 = ((double)(*itr).get(boost::polygon::HORIZONTAL)) / inputscale + xshift; + double y1 = ((double)(*itr).get(boost::polygon::VERTICAL)) / inputscale + yshift; + + pointlist.push_back(x1); + pointlist.push_back(y1); + + j++; + } + retpnts.push_back(pointlist); + // holes + + int k = 0; + for (polygon_with_holes_traits::iterator_holes_type itrh = begin_holes(polys[i]); itrh != end_holes(polys[i]); ++itrh) { + vector child = vector(); + + + int z = 0; + for (polygon_traits::iterator_type itr2 = (*itrh).begin(); itr2 != (*itrh).end(); ++itr2) { + + double x1 = ((double)(*itr2).get(boost::polygon::HORIZONTAL)) / inputscale + xshift; + double y1 = ((double)(*itr2).get(boost::polygon::VERTICAL)) / inputscale + yshift; + + child.push_back(x1); + child.push_back(y1); + + z++; + } + retholes.push_back(child); + + k++; + } + } +} \ No newline at end of file diff --git a/PCUT/MinkowskiCpp/Minkowski.h b/PCUT/MinkowskiCpp/Minkowski.h new file mode 100644 index 0000000..ce87054 --- /dev/null +++ b/PCUT/MinkowskiCpp/Minkowski.h @@ -0,0 +1,17 @@ +#pragma once + +namespace MinkowskiCpp +{ + public ref class Minkowski sealed + { + public: + static void setData(int cntA, const Platform::Array^ pntsA, int holesCnt, const Platform::Array^ holesSizes, const Platform::Array^ holesPoints, int cntB, const Platform::Array^ pntsB); + static void getSizes1(Platform::WriteOnlyArray^ sizes); + static void getSizes2(Platform::WriteOnlyArray^ sizes1, Platform::WriteOnlyArray^ sizes2); + static void getResults(Platform::WriteOnlyArray^ data, Platform::WriteOnlyArray^ holesData); + static void calculateNFP(); + private: + Minkowski(); + static Minkowski^ Instance; + }; +} diff --git a/PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj b/PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj new file mode 100644 index 0000000..ef27d78 --- /dev/null +++ b/PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj @@ -0,0 +1,451 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {71998b3c-e08f-4324-a087-2edeff1e3256} + WindowsRuntimeComponent + MinkowskiCpp + en-US + 14.0 + true + Windows Store + 10.0.22621.0 + 10.0.17763.0 + 10.0 + + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + true + v143 + + + DynamicLibrary + false + true + v143 + + + DynamicLibrary + false + true + v143 + + + DynamicLibrary + false + true + v143 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + + Use + _WINRT_DLL;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + Use + _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + Use + _WINRT_DLL;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + Use + _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + Use + _WINRT_DLL;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + Use + _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + Use + _WINRT_DLL;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + Use + _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + $(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\boost_1_84_0 + + + Console + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj.filters b/PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj.filters new file mode 100644 index 0000000..d9ab6ce --- /dev/null +++ b/PCUT/MinkowskiCpp/MinkowskiCpp.vcxproj.filters @@ -0,0 +1,171 @@ + + + + + ab1c52c7-026a-42f8-9a9e-7fdceed91912 + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config.hpp new file mode 100644 index 0000000..f00a980 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config.hpp @@ -0,0 +1,67 @@ +// Boost config.hpp configuration header file ------------------------------// + +// (C) Copyright John Maddock 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for most recent version. + +// Boost config.hpp policy and rationale documentation has been moved to +// http://www.boost.org/libs/config +// +// CAUTION: This file is intended to be completely stable - +// DO NOT MODIFY THIS FILE! +// + +#ifndef BOOST_CONFIG_HPP +#define BOOST_CONFIG_HPP + +// if we don't have a user config, then use the default location: +#if !defined(BOOST_USER_CONFIG) && !defined(BOOST_NO_USER_CONFIG) +# define BOOST_USER_CONFIG +#if 0 +// For dependency trackers: +# include +#endif +#endif +// include it first: +#ifdef BOOST_USER_CONFIG +# include BOOST_USER_CONFIG +#endif + +// if we don't have a compiler config set, try and find one: +#if !defined(BOOST_COMPILER_CONFIG) && !defined(BOOST_NO_COMPILER_CONFIG) && !defined(BOOST_NO_CONFIG) +# include +#endif +// if we have a compiler config, include it now: +#ifdef BOOST_COMPILER_CONFIG +# include BOOST_COMPILER_CONFIG +#endif + +// if we don't have a std library config set, try and find one: +#if !defined(BOOST_STDLIB_CONFIG) && !defined(BOOST_NO_STDLIB_CONFIG) && !defined(BOOST_NO_CONFIG) && defined(__cplusplus) +# include +#endif +// if we have a std library config, include it now: +#ifdef BOOST_STDLIB_CONFIG +# include BOOST_STDLIB_CONFIG +#endif + +// if we don't have a platform config set, try and find one: +#if !defined(BOOST_PLATFORM_CONFIG) && !defined(BOOST_NO_PLATFORM_CONFIG) && !defined(BOOST_NO_CONFIG) +# include +#endif +// if we have a platform config, include it now: +#ifdef BOOST_PLATFORM_CONFIG +# include BOOST_PLATFORM_CONFIG +#endif + +// get config suffix code: +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#endif // BOOST_CONFIG_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_prefix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_prefix.hpp new file mode 100644 index 0000000..3a0e5ae --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_prefix.hpp @@ -0,0 +1,27 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// for C++ Builder the following options effect the ABI: +// +// -b (on or off - effect emum sizes) +// -Vx (on or off - empty members) +// -Ve (on or off - empty base classes) +// -aX (alignment - 5 options). +// -pX (Calling convention - 4 options) +// -VmX (member pointer size and layout - 5 options) +// -VC (on or off, changes name mangling) +// -Vl (on or off, changes struct layout). + +// In addition the following warnings are sufficiently annoying (and +// unfixable) to have them turned off by default: +// +// 8027 - functions containing [for|while] loops are not expanded inline +// 8026 - functions taking class by value arguments are not expanded inline + +#pragma nopushoptwarn +# pragma option push -a8 -Vx- -Ve- -b- -pc -Vmv -VC- -Vl- -w-8027 -w-8026 + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_suffix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_suffix.hpp new file mode 100644 index 0000000..940535f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/borland_suffix.hpp @@ -0,0 +1,12 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +# pragma option pop +#pragma nopushoptwarn + + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_prefix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_prefix.hpp new file mode 100644 index 0000000..97f06cd --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_prefix.hpp @@ -0,0 +1,22 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// +// Boost binaries are built with the compiler's default ABI settings, +// if the user changes their default alignment in the VS IDE then their +// code will no longer be binary compatible with the bjam built binaries +// unless this header is included to force Boost code into a consistent ABI. +// +// Note that inclusion of this header is only necessary for libraries with +// separate source, header only libraries DO NOT need this as long as all +// translation units are built with the same options. +// +#if defined(_M_X64) +# pragma pack(push,16) +#else +# pragma pack(push,8) +#endif + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_suffix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_suffix.hpp new file mode 100644 index 0000000..a64d783 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi/msvc_suffix.hpp @@ -0,0 +1,8 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma pack(pop) + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_prefix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_prefix.hpp new file mode 100644 index 0000000..bcdc26d --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_prefix.hpp @@ -0,0 +1,25 @@ +// abi_prefix header -------------------------------------------------------// + +// (c) Copyright John Maddock 2003 + +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +#ifndef BOOST_CONFIG_ABI_PREFIX_HPP +# define BOOST_CONFIG_ABI_PREFIX_HPP +#else +# error double inclusion of header boost/config/abi_prefix.hpp is an error +#endif + +#include + +// this must occur after all other includes and before any code appears: +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if defined( BOOST_BORLANDC ) +#pragma nopushoptwarn +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_suffix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_suffix.hpp new file mode 100644 index 0000000..a1eb78d --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/abi_suffix.hpp @@ -0,0 +1,25 @@ +// abi_sufffix header -------------------------------------------------------// + +// (c) Copyright John Maddock 2003 + +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +// This header should be #included AFTER code that was preceded by a #include +// . + +#ifndef BOOST_CONFIG_ABI_PREFIX_HPP +# error Header boost/config/abi_suffix.hpp must only be used after boost/config/abi_prefix.hpp +#else +# undef BOOST_CONFIG_ABI_PREFIX_HPP +#endif + +// the suffix header occurs after all of our code: +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#if defined( BOOST_BORLANDC ) +#pragma nopushoptwarn +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx03.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx03.hpp new file mode 100644 index 0000000..c914aa8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx03.hpp @@ -0,0 +1,211 @@ +// This file was automatically generated on Fri Oct 13 19:09:38 2023 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-21. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#include + +#ifdef BOOST_NO_ADL_BARRIER +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_ADL_BARRIER." +#endif +#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP." +#endif +#ifdef BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS." +#endif +#ifdef BOOST_NO_COMPLETE_VALUE_INITIALIZATION +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_COMPLETE_VALUE_INITIALIZATION." +#endif +#ifdef BOOST_NO_CTYPE_FUNCTIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_CTYPE_FUNCTIONS." +#endif +#ifdef BOOST_NO_CV_SPECIALIZATIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_CV_SPECIALIZATIONS." +#endif +#ifdef BOOST_NO_CV_VOID_SPECIALIZATIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_CV_VOID_SPECIALIZATIONS." +#endif +#ifdef BOOST_NO_CWCHAR +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_CWCHAR." +#endif +#ifdef BOOST_NO_CWCTYPE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_CWCTYPE." +#endif +#ifdef BOOST_NO_DEPENDENT_NESTED_DERIVATIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_DEPENDENT_NESTED_DERIVATIONS." +#endif +#ifdef BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS." +#endif +#ifdef BOOST_NO_EXCEPTIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_EXCEPTIONS." +#endif +#ifdef BOOST_NO_EXCEPTION_STD_NAMESPACE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_EXCEPTION_STD_NAMESPACE." +#endif +#ifdef BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS." +#endif +#ifdef BOOST_NO_FENV_H +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_FENV_H." +#endif +#ifdef BOOST_NO_FUNCTION_TEMPLATE_ORDERING +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_FUNCTION_TEMPLATE_ORDERING." +#endif +#ifdef BOOST_NO_FUNCTION_TYPE_SPECIALIZATIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_FUNCTION_TYPE_SPECIALIZATIONS." +#endif +#ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_INCLASS_MEMBER_INITIALIZATION." +#endif +#ifdef BOOST_NO_INTEGRAL_INT64_T +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_INTEGRAL_INT64_T." +#endif +#ifdef BOOST_NO_INTRINSIC_WCHAR_T +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_INTRINSIC_WCHAR_T." +#endif +#ifdef BOOST_NO_IOSFWD +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_IOSFWD." +#endif +#ifdef BOOST_NO_IOSTREAM +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_IOSTREAM." +#endif +#ifdef BOOST_NO_IS_ABSTRACT +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_IS_ABSTRACT." +#endif +#ifdef BOOST_NO_LIMITS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_LIMITS." +#endif +#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS." +#endif +#ifdef BOOST_NO_LONG_LONG +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_LONG_LONG." +#endif +#ifdef BOOST_NO_LONG_LONG_NUMERIC_LIMITS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_LONG_LONG_NUMERIC_LIMITS." +#endif +#ifdef BOOST_NO_MEMBER_FUNCTION_SPECIALIZATIONS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_MEMBER_FUNCTION_SPECIALIZATIONS." +#endif +#ifdef BOOST_NO_MEMBER_TEMPLATES +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_MEMBER_TEMPLATES." +#endif +#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_MEMBER_TEMPLATE_FRIENDS." +#endif +#ifdef BOOST_NO_MEMBER_TEMPLATE_KEYWORD +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_MEMBER_TEMPLATE_KEYWORD." +#endif +#ifdef BOOST_NO_NESTED_FRIENDSHIP +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_NESTED_FRIENDSHIP." +#endif +#ifdef BOOST_NO_OPERATORS_IN_NAMESPACE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_OPERATORS_IN_NAMESPACE." +#endif +#ifdef BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS." +#endif +#ifdef BOOST_NO_POINTER_TO_MEMBER_CONST +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_POINTER_TO_MEMBER_CONST." +#endif +#ifdef BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS." +#endif +#ifdef BOOST_NO_PRIVATE_IN_AGGREGATE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_PRIVATE_IN_AGGREGATE." +#endif +#ifdef BOOST_NO_RESTRICT_REFERENCES +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_RESTRICT_REFERENCES." +#endif +#ifdef BOOST_NO_RTTI +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_RTTI." +#endif +#ifdef BOOST_NO_SFINAE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_SFINAE." +#endif +#ifdef BOOST_NO_SFINAE_EXPR +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_SFINAE_EXPR." +#endif +#ifdef BOOST_NO_STDC_NAMESPACE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STDC_NAMESPACE." +#endif +#ifdef BOOST_NO_STD_ALLOCATOR +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_ALLOCATOR." +#endif +#ifdef BOOST_NO_STD_DISTANCE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_DISTANCE." +#endif +#ifdef BOOST_NO_STD_ITERATOR +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_ITERATOR." +#endif +#ifdef BOOST_NO_STD_ITERATOR_TRAITS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_ITERATOR_TRAITS." +#endif +#ifdef BOOST_NO_STD_LOCALE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_LOCALE." +#endif +#ifdef BOOST_NO_STD_MESSAGES +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_MESSAGES." +#endif +#ifdef BOOST_NO_STD_MIN_MAX +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_MIN_MAX." +#endif +#ifdef BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN." +#endif +#ifdef BOOST_NO_STD_TYPEINFO +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_TYPEINFO." +#endif +#ifdef BOOST_NO_STD_USE_FACET +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_USE_FACET." +#endif +#ifdef BOOST_NO_STD_WSTREAMBUF +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_WSTREAMBUF." +#endif +#ifdef BOOST_NO_STD_WSTRING +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STD_WSTRING." +#endif +#ifdef BOOST_NO_STRINGSTREAM +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_STRINGSTREAM." +#endif +#ifdef BOOST_NO_TEMPLATED_IOSTREAMS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_TEMPLATED_IOSTREAMS." +#endif +#ifdef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS." +#endif +#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION." +#endif +#ifdef BOOST_NO_TEMPLATE_TEMPLATES +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_TEMPLATE_TEMPLATES." +#endif +#ifdef BOOST_NO_TWO_PHASE_NAME_LOOKUP +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_TWO_PHASE_NAME_LOOKUP." +#endif +#ifdef BOOST_NO_TYPEID +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_TYPEID." +#endif +#ifdef BOOST_NO_TYPENAME_WITH_CTOR +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_TYPENAME_WITH_CTOR." +#endif +#ifdef BOOST_NO_UNREACHABLE_RETURN_DETECTION +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_UNREACHABLE_RETURN_DETECTION." +#endif +#ifdef BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE." +#endif +#ifdef BOOST_NO_USING_TEMPLATE +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_USING_TEMPLATE." +#endif +#ifdef BOOST_NO_VOID_RETURNS +# error "Your compiler appears not to be fully C++03 compliant. Detected via defect macro BOOST_NO_VOID_RETURNS." +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx11.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx11.hpp new file mode 100644 index 0000000..addd06a --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx11.hpp @@ -0,0 +1,212 @@ +// This file was automatically generated on Fri Oct 13 19:09:38 2023 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-21. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#include +#include + +#ifdef BOOST_NO_CXX11_ADDRESSOF +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_ADDRESSOF." +#endif +#ifdef BOOST_NO_CXX11_ALIGNAS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_ALIGNAS." +#endif +#ifdef BOOST_NO_CXX11_ALIGNOF +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_ALIGNOF." +#endif +#ifdef BOOST_NO_CXX11_ALLOCATOR +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_ALLOCATOR." +#endif +#ifdef BOOST_NO_CXX11_AUTO_DECLARATIONS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_AUTO_DECLARATIONS." +#endif +#ifdef BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS." +#endif +#ifdef BOOST_NO_CXX11_CHAR16_T +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_CHAR16_T." +#endif +#ifdef BOOST_NO_CXX11_CHAR32_T +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_CHAR32_T." +#endif +#ifdef BOOST_NO_CXX11_CONSTEXPR +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_CONSTEXPR." +#endif +#ifdef BOOST_NO_CXX11_DECLTYPE +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_DECLTYPE." +#endif +#ifdef BOOST_NO_CXX11_DECLTYPE_N3276 +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_DECLTYPE_N3276." +#endif +#ifdef BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_DEFAULTED_FUNCTIONS." +#endif +#ifdef BOOST_NO_CXX11_DEFAULTED_MOVES +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_DEFAULTED_MOVES." +#endif +#ifdef BOOST_NO_CXX11_DELETED_FUNCTIONS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_DELETED_FUNCTIONS." +#endif +#ifdef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS." +#endif +#ifdef BOOST_NO_CXX11_EXTERN_TEMPLATE +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_EXTERN_TEMPLATE." +#endif +#ifdef BOOST_NO_CXX11_FINAL +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_FINAL." +#endif +#ifdef BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS." +#endif +#ifdef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS." +#endif +#ifdef BOOST_NO_CXX11_HDR_ARRAY +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_ARRAY." +#endif +#ifdef BOOST_NO_CXX11_HDR_ATOMIC +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_ATOMIC." +#endif +#ifdef BOOST_NO_CXX11_HDR_CHRONO +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_CHRONO." +#endif +#ifdef BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_CONDITION_VARIABLE." +#endif +#ifdef BOOST_NO_CXX11_HDR_EXCEPTION +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_EXCEPTION." +#endif +#ifdef BOOST_NO_CXX11_HDR_FORWARD_LIST +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_FORWARD_LIST." +#endif +#ifdef BOOST_NO_CXX11_HDR_FUNCTIONAL +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_FUNCTIONAL." +#endif +#ifdef BOOST_NO_CXX11_HDR_FUTURE +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_FUTURE." +#endif +#ifdef BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_INITIALIZER_LIST." +#endif +#ifdef BOOST_NO_CXX11_HDR_MUTEX +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_MUTEX." +#endif +#ifdef BOOST_NO_CXX11_HDR_RANDOM +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_RANDOM." +#endif +#ifdef BOOST_NO_CXX11_HDR_RATIO +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_RATIO." +#endif +#ifdef BOOST_NO_CXX11_HDR_REGEX +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_REGEX." +#endif +#ifdef BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_SYSTEM_ERROR." +#endif +#ifdef BOOST_NO_CXX11_HDR_THREAD +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_THREAD." +#endif +#ifdef BOOST_NO_CXX11_HDR_TUPLE +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_TUPLE." +#endif +#ifdef BOOST_NO_CXX11_HDR_TYPEINDEX +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_TYPEINDEX." +#endif +#ifdef BOOST_NO_CXX11_HDR_TYPE_TRAITS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_TYPE_TRAITS." +#endif +#ifdef BOOST_NO_CXX11_HDR_UNORDERED_MAP +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_UNORDERED_MAP." +#endif +#ifdef BOOST_NO_CXX11_HDR_UNORDERED_SET +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_HDR_UNORDERED_SET." +#endif +#ifdef BOOST_NO_CXX11_INLINE_NAMESPACES +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_INLINE_NAMESPACES." +#endif +#ifdef BOOST_NO_CXX11_LAMBDAS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_LAMBDAS." +#endif +#ifdef BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS." +#endif +#ifdef BOOST_NO_CXX11_NOEXCEPT +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_NOEXCEPT." +#endif +#ifdef BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS." +#endif +#ifdef BOOST_NO_CXX11_NULLPTR +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_NULLPTR." +#endif +#ifdef BOOST_NO_CXX11_NUMERIC_LIMITS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_NUMERIC_LIMITS." +#endif +#ifdef BOOST_NO_CXX11_OVERRIDE +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_OVERRIDE." +#endif +#ifdef BOOST_NO_CXX11_POINTER_TRAITS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_POINTER_TRAITS." +#endif +#ifdef BOOST_NO_CXX11_RANGE_BASED_FOR +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_RANGE_BASED_FOR." +#endif +#ifdef BOOST_NO_CXX11_RAW_LITERALS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_RAW_LITERALS." +#endif +#ifdef BOOST_NO_CXX11_REF_QUALIFIERS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_REF_QUALIFIERS." +#endif +#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_RVALUE_REFERENCES." +#endif +#ifdef BOOST_NO_CXX11_SCOPED_ENUMS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_SCOPED_ENUMS." +#endif +#ifdef BOOST_NO_CXX11_SFINAE_EXPR +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_SFINAE_EXPR." +#endif +#ifdef BOOST_NO_CXX11_SMART_PTR +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_SMART_PTR." +#endif +#ifdef BOOST_NO_CXX11_STATIC_ASSERT +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_STATIC_ASSERT." +#endif +#ifdef BOOST_NO_CXX11_STD_ALIGN +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_STD_ALIGN." +#endif +#ifdef BOOST_NO_CXX11_TEMPLATE_ALIASES +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_TEMPLATE_ALIASES." +#endif +#ifdef BOOST_NO_CXX11_THREAD_LOCAL +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_THREAD_LOCAL." +#endif +#ifdef BOOST_NO_CXX11_TRAILING_RESULT_TYPES +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_TRAILING_RESULT_TYPES." +#endif +#ifdef BOOST_NO_CXX11_UNICODE_LITERALS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_UNICODE_LITERALS." +#endif +#ifdef BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX." +#endif +#ifdef BOOST_NO_CXX11_UNRESTRICTED_UNION +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_UNRESTRICTED_UNION." +#endif +#ifdef BOOST_NO_CXX11_USER_DEFINED_LITERALS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_USER_DEFINED_LITERALS." +#endif +#ifdef BOOST_NO_CXX11_VARIADIC_MACROS +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_VARIADIC_MACROS." +#endif +#ifdef BOOST_NO_CXX11_VARIADIC_TEMPLATES +# error "Your compiler appears not to be fully C++11 compliant. Detected via defect macro BOOST_NO_CXX11_VARIADIC_TEMPLATES." +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx14.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx14.hpp new file mode 100644 index 0000000..061aba3 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx14.hpp @@ -0,0 +1,47 @@ +// This file was automatically generated on Fri Oct 13 19:09:38 2023 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-21. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#include +#include + +#ifdef BOOST_NO_CXX14_AGGREGATE_NSDMI +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_AGGREGATE_NSDMI." +#endif +#ifdef BOOST_NO_CXX14_BINARY_LITERALS +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_BINARY_LITERALS." +#endif +#ifdef BOOST_NO_CXX14_CONSTEXPR +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_CONSTEXPR." +#endif +#ifdef BOOST_NO_CXX14_DECLTYPE_AUTO +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_DECLTYPE_AUTO." +#endif +#ifdef BOOST_NO_CXX14_DIGIT_SEPARATORS +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_DIGIT_SEPARATORS." +#endif +#ifdef BOOST_NO_CXX14_GENERIC_LAMBDAS +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_GENERIC_LAMBDAS." +#endif +#ifdef BOOST_NO_CXX14_HDR_SHARED_MUTEX +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_HDR_SHARED_MUTEX." +#endif +#ifdef BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES." +#endif +#ifdef BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION." +#endif +#ifdef BOOST_NO_CXX14_STD_EXCHANGE +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_STD_EXCHANGE." +#endif +#ifdef BOOST_NO_CXX14_VARIABLE_TEMPLATES +# error "Your compiler appears not to be fully C++14 compliant. Detected via defect macro BOOST_NO_CXX14_VARIABLE_TEMPLATES." +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx17.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx17.hpp new file mode 100644 index 0000000..ffa1ae9 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx17.hpp @@ -0,0 +1,62 @@ +// This file was automatically generated on Fri Oct 13 19:09:38 2023 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-21. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#include +#include + +#ifdef BOOST_NO_CXX17_DEDUCTION_GUIDES +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_DEDUCTION_GUIDES." +#endif +#ifdef BOOST_NO_CXX17_FOLD_EXPRESSIONS +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_FOLD_EXPRESSIONS." +#endif +#ifdef BOOST_NO_CXX17_HDR_ANY +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_ANY." +#endif +#ifdef BOOST_NO_CXX17_HDR_CHARCONV +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_CHARCONV." +#endif +#ifdef BOOST_NO_CXX17_HDR_EXECUTION +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_EXECUTION." +#endif +#ifdef BOOST_NO_CXX17_HDR_FILESYSTEM +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_FILESYSTEM." +#endif +#ifdef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_MEMORY_RESOURCE." +#endif +#ifdef BOOST_NO_CXX17_HDR_OPTIONAL +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_OPTIONAL." +#endif +#ifdef BOOST_NO_CXX17_HDR_STRING_VIEW +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_STRING_VIEW." +#endif +#ifdef BOOST_NO_CXX17_HDR_VARIANT +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_HDR_VARIANT." +#endif +#ifdef BOOST_NO_CXX17_IF_CONSTEXPR +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_IF_CONSTEXPR." +#endif +#ifdef BOOST_NO_CXX17_INLINE_VARIABLES +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_INLINE_VARIABLES." +#endif +#ifdef BOOST_NO_CXX17_ITERATOR_TRAITS +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_ITERATOR_TRAITS." +#endif +#ifdef BOOST_NO_CXX17_STD_APPLY +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_STD_APPLY." +#endif +#ifdef BOOST_NO_CXX17_STD_INVOKE +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_STD_INVOKE." +#endif +#ifdef BOOST_NO_CXX17_STRUCTURED_BINDINGS +# error "Your compiler appears not to be fully C++17 compliant. Detected via defect macro BOOST_NO_CXX17_STRUCTURED_BINDINGS." +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx20.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx20.hpp new file mode 100644 index 0000000..71a7415 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx20.hpp @@ -0,0 +1,59 @@ +// This file was automatically generated on Fri Oct 13 19:09:38 2023 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-21. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#include +#include + +#ifdef BOOST_NO_CXX20_HDR_BARRIER +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_BARRIER." +#endif +#ifdef BOOST_NO_CXX20_HDR_BIT +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_BIT." +#endif +#ifdef BOOST_NO_CXX20_HDR_COMPARE +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_COMPARE." +#endif +#ifdef BOOST_NO_CXX20_HDR_CONCEPTS +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_CONCEPTS." +#endif +#ifdef BOOST_NO_CXX20_HDR_COROUTINE +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_COROUTINE." +#endif +#ifdef BOOST_NO_CXX20_HDR_FORMAT +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_FORMAT." +#endif +#ifdef BOOST_NO_CXX20_HDR_LATCH +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_LATCH." +#endif +#ifdef BOOST_NO_CXX20_HDR_NUMBERS +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_NUMBERS." +#endif +#ifdef BOOST_NO_CXX20_HDR_RANGES +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_RANGES." +#endif +#ifdef BOOST_NO_CXX20_HDR_SEMAPHORE +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_SEMAPHORE." +#endif +#ifdef BOOST_NO_CXX20_HDR_SOURCE_LOCATION +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_SOURCE_LOCATION." +#endif +#ifdef BOOST_NO_CXX20_HDR_SPAN +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_SPAN." +#endif +#ifdef BOOST_NO_CXX20_HDR_STOP_TOKEN +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_STOP_TOKEN." +#endif +#ifdef BOOST_NO_CXX20_HDR_SYNCSTREAM +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_SYNCSTREAM." +#endif +#ifdef BOOST_NO_CXX20_HDR_VERSION +# error "Your compiler appears not to be fully C++20 compliant. Detected via defect macro BOOST_NO_CXX20_HDR_VERSION." +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx23.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx23.hpp new file mode 100644 index 0000000..feb4457 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx23.hpp @@ -0,0 +1,41 @@ +// This file was automatically generated on Fri Oct 13 19:09:38 2023 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-21. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#include +#include + +#ifdef BOOST_NO_CXX23_HDR_EXPECTED +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_EXPECTED." +#endif +#ifdef BOOST_NO_CXX23_HDR_FLAT_MAP +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_FLAT_MAP." +#endif +#ifdef BOOST_NO_CXX23_HDR_FLAT_SET +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_FLAT_SET." +#endif +#ifdef BOOST_NO_CXX23_HDR_GENERATOR +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_GENERATOR." +#endif +#ifdef BOOST_NO_CXX23_HDR_MDSPAN +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_MDSPAN." +#endif +#ifdef BOOST_NO_CXX23_HDR_PRINT +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_PRINT." +#endif +#ifdef BOOST_NO_CXX23_HDR_SPANSTREAM +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_SPANSTREAM." +#endif +#ifdef BOOST_NO_CXX23_HDR_STACKTRACE +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_STACKTRACE." +#endif +#ifdef BOOST_NO_CXX23_HDR_STDFLOAT +# error "Your compiler appears not to be fully C++23 compliant. Detected via defect macro BOOST_NO_CXX23_HDR_STDFLOAT." +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx98.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx98.hpp new file mode 100644 index 0000000..aa745d4 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/assert_cxx98.hpp @@ -0,0 +1,23 @@ +// This file was automatically generated on Wed Mar 3 08:46:11 2021 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-4. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#include +#include + +#ifdef BOOST_NO_CXX98_BINDERS +# error "Your compiler appears not to be fully C++98 compliant. Detected via defect macro BOOST_NO_CXX98_BINDERS." +#endif +#ifdef BOOST_NO_CXX98_FUNCTION_BASE +# error "Your compiler appears not to be fully C++98 compliant. Detected via defect macro BOOST_NO_CXX98_FUNCTION_BASE." +#endif +#ifdef BOOST_NO_CXX98_RANDOM_SHUFFLE +# error "Your compiler appears not to be fully C++98 compliant. Detected via defect macro BOOST_NO_CXX98_RANDOM_SHUFFLE." +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/auto_link.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/auto_link.hpp new file mode 100644 index 0000000..64dee1e --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/auto_link.hpp @@ -0,0 +1,525 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + /* + * LOCATION: see http://www.boost.org for most recent version. + * FILE auto_link.hpp + * VERSION see + * DESCRIPTION: Automatic library inclusion for Borland/Microsoft compilers. + */ + +/************************************************************************* + +USAGE: +~~~~~~ + +Before including this header you must define one or more of define the following macros: + +BOOST_LIB_NAME: Required: A string containing the basename of the library, + for example boost_regex. +BOOST_LIB_TOOLSET: Optional: the base name of the toolset. +BOOST_DYN_LINK: Optional: when set link to dll rather than static library. +BOOST_LIB_DIAGNOSTIC: Optional: when set the header will print out the name + of the library selected (useful for debugging). +BOOST_AUTO_LINK_NOMANGLE: Specifies that we should link to BOOST_LIB_NAME.lib, + rather than a mangled-name version. +BOOST_AUTO_LINK_TAGGED: Specifies that we link to libraries built with the --layout=tagged option. + This is essentially the same as the default name-mangled version, but without + the compiler name and version, or the Boost version. Just the build options. +BOOST_AUTO_LINK_SYSTEM: Specifies that we link to libraries built with the --layout=system option. + This is essentially the same as the non-name-mangled version, but with + the prefix to differentiate static and dll builds + +These macros will be undef'ed at the end of the header, further this header +has no include guards - so be sure to include it only once from your library! + +Algorithm: +~~~~~~~~~~ + +Libraries for Borland and Microsoft compilers are automatically +selected here, the name of the lib is selected according to the following +formula: + +BOOST_LIB_PREFIX + + BOOST_LIB_NAME + + "_" + + BOOST_LIB_TOOLSET + + BOOST_LIB_THREAD_OPT + + BOOST_LIB_RT_OPT + + BOOST_LIB_ARCH_AND_MODEL_OPT + "-" + + BOOST_LIB_VERSION + + BOOST_LIB_SUFFIX + +These are defined as: + +BOOST_LIB_PREFIX: "lib" for static libraries otherwise "". + +BOOST_LIB_NAME: The base name of the lib ( for example boost_regex). + +BOOST_LIB_TOOLSET: The compiler toolset name (vc6, vc7, bcb5 etc). + +BOOST_LIB_THREAD_OPT: "-mt" for multithread builds, otherwise nothing. + +BOOST_LIB_RT_OPT: A suffix that indicates the runtime library used, + contains one or more of the following letters after + a hyphen: + + s static runtime (dynamic if not present). + g debug/diagnostic runtime (release if not present). + y Python debug/diagnostic runtime (release if not present). + d debug build (release if not present). + p STLport build. + n STLport build without its IOStreams. + +BOOST_LIB_ARCH_AND_MODEL_OPT: The architecture and address model + (-x32 or -x64 for x86/32 and x86/64 respectively) + +BOOST_LIB_VERSION: The Boost version, in the form x_y, for Boost version x.y. + +BOOST_LIB_SUFFIX: Static/import libraries extension (".lib", ".a") for the compiler. + +***************************************************************************/ + +#ifdef __cplusplus +# ifndef BOOST_CONFIG_HPP +# include +# endif +#elif defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__EDG_VERSION__) +// +// C language compatability (no, honestly) +// +# define BOOST_MSVC _MSC_VER +# define BOOST_STRINGIZE(X) BOOST_DO_STRINGIZE(X) +# define BOOST_DO_STRINGIZE(X) #X +#endif +// +// Only include what follows for known and supported compilers: +// +#if defined(BOOST_MSVC) \ + || defined(BOOST_EMBTC_WINDOWS) \ + || defined(BOOST_BORLANDC) \ + || (defined(__MWERKS__) && defined(_WIN32) && (__MWERKS__ >= 0x3000)) \ + || (defined(__ICL) && defined(_MSC_EXTENSIONS) && (_MSC_VER >= 1200)) \ + || (defined(BOOST_CLANG) && defined(BOOST_WINDOWS) && defined(_MSC_VER) && (__clang_major__ >= 4)) + +#ifndef BOOST_VERSION_HPP +# include +#endif + +#ifndef BOOST_LIB_NAME +# error "Macro BOOST_LIB_NAME not set (internal error)" +#endif + +// +// error check: +// +#if defined(__MSVC_RUNTIME_CHECKS) && !defined(_DEBUG) +# pragma message("Using the /RTC option without specifying a debug runtime will lead to linker errors") +# pragma message("Hint: go to the code generation options and switch to one of the debugging runtimes") +# error "Incompatible build options" +#endif +// +// select toolset if not defined already: +// +#ifndef BOOST_LIB_TOOLSET +# if defined(BOOST_MSVC) && (BOOST_MSVC < 1200) + // Note: no compilers before 1200 are supported +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1300) + +# ifdef UNDER_CE + // eVC4: +# define BOOST_LIB_TOOLSET "evc4" +# else + // vc6: +# define BOOST_LIB_TOOLSET "vc6" +# endif + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1310) + + // vc7: +# define BOOST_LIB_TOOLSET "vc7" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1400) + + // vc71: +# define BOOST_LIB_TOOLSET "vc71" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1500) + + // vc80: +# define BOOST_LIB_TOOLSET "vc80" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1600) + + // vc90: +# define BOOST_LIB_TOOLSET "vc90" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1700) + + // vc10: +# define BOOST_LIB_TOOLSET "vc100" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1800) + + // vc11: +# define BOOST_LIB_TOOLSET "vc110" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1900) + + // vc12: +# define BOOST_LIB_TOOLSET "vc120" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1910) + + // vc14: +# define BOOST_LIB_TOOLSET "vc140" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1920) + + // vc14.1: +# define BOOST_LIB_TOOLSET "vc141" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1930) + + // vc14.2: +# define BOOST_LIB_TOOLSET "vc142" + +# elif defined(BOOST_MSVC) + + // vc14.3: +# define BOOST_LIB_TOOLSET "vc143" + +# elif defined(BOOST_EMBTC_WINDOWS) + + // Embarcadero Clang based compilers: +# define BOOST_LIB_TOOLSET "embtc" + +# elif defined(BOOST_BORLANDC) + + // CBuilder 6: +# define BOOST_LIB_TOOLSET "bcb" + +# elif defined(__ICL) + + // Intel C++, no version number: +# define BOOST_LIB_TOOLSET "iw" + +# elif defined(__MWERKS__) && (__MWERKS__ <= 0x31FF ) + + // Metrowerks CodeWarrior 8.x +# define BOOST_LIB_TOOLSET "cw8" + +# elif defined(__MWERKS__) && (__MWERKS__ <= 0x32FF ) + + // Metrowerks CodeWarrior 9.x +# define BOOST_LIB_TOOLSET "cw9" + +# elif defined(BOOST_CLANG) && defined(BOOST_WINDOWS) && defined(_MSC_VER) && (__clang_major__ >= 4) + + // Clang on Windows +# define BOOST_LIB_TOOLSET "clangw" BOOST_STRINGIZE(__clang_major__) + +# endif +#endif // BOOST_LIB_TOOLSET + +// +// select thread opt: +// +#if defined(_MT) || defined(__MT__) +# define BOOST_LIB_THREAD_OPT "-mt" +#else +# define BOOST_LIB_THREAD_OPT +#endif + +#if defined(_MSC_VER) || defined(__MWERKS__) + +# ifdef _DLL + +# if (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) && (defined(_STLP_OWN_IOSTREAMS) || defined(__STL_OWN_IOSTREAMS)) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydp" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-gdp" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-gdp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-p" +# endif + +# elif defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydpn" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-gdpn" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-gdpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-pn" +# endif + +# else + +# if defined(_DEBUG) && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gyd" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-gd" +# else +# define BOOST_LIB_RT_OPT +# endif + +# endif + +# else + +# if (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) && (defined(_STLP_OWN_IOSTREAMS) || defined(__STL_OWN_IOSTREAMS)) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydp" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-sgdp" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-sgdp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-sp" +# endif + +# elif defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydpn" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-sgdpn" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-sgdpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-spn" +# endif + +# else + +# if defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgyd" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-sgd" +# else +# define BOOST_LIB_RT_OPT "-s" +# endif + +# endif + +# endif + +#elif defined(BOOST_EMBTC_WINDOWS) + +# ifdef _RTLDLL + +# if defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-d" +# else +# define BOOST_LIB_RT_OPT +# endif + +# else + +# if defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-sd" +# else +# define BOOST_LIB_RT_OPT "-s" +# endif + +# endif + +#elif defined(BOOST_BORLANDC) + +// +// figure out whether we want the debug builds or not: +// +#if BOOST_BORLANDC > 0x561 +#pragma defineonoption BOOST_BORLAND_DEBUG -v +#endif +// +// sanity check: +// +#if defined(__STL_DEBUG) || defined(_STLP_DEBUG) +#error "Pre-built versions of the Boost libraries are not provided in STLport-debug form" +#endif + +# ifdef _RTLDLL + +# if defined(BOOST_BORLAND_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-yd" +# elif defined(BOOST_BORLAND_DEBUG) +# define BOOST_LIB_RT_OPT "-d" +# elif defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-y" +# else +# define BOOST_LIB_RT_OPT +# endif + +# else + +# if defined(BOOST_BORLAND_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-syd" +# elif defined(BOOST_BORLAND_DEBUG) +# define BOOST_LIB_RT_OPT "-sd" +# elif defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sy" +# else +# define BOOST_LIB_RT_OPT "-s" +# endif + +# endif + +#endif + +// +// BOOST_LIB_ARCH_AND_MODEL_OPT +// + +#if defined( _M_IX86 ) +# define BOOST_LIB_ARCH_AND_MODEL_OPT "-x32" +#elif defined( _M_X64 ) +# define BOOST_LIB_ARCH_AND_MODEL_OPT "-x64" +#elif defined( _M_ARM ) +# define BOOST_LIB_ARCH_AND_MODEL_OPT "-a32" +#elif defined( _M_ARM64 ) +# define BOOST_LIB_ARCH_AND_MODEL_OPT "-a64" +#endif + +// +// select linkage opt: +// +#if (defined(_DLL) || defined(_RTLDLL)) && defined(BOOST_DYN_LINK) +# define BOOST_LIB_PREFIX +#elif defined(BOOST_DYN_LINK) +# error "Mixing a dll boost library with a static runtime is a really bad idea..." +#else +# define BOOST_LIB_PREFIX "lib" +#endif + +// +// now include the lib: +// +#if defined(BOOST_LIB_NAME) \ + && defined(BOOST_LIB_PREFIX) \ + && defined(BOOST_LIB_TOOLSET) \ + && defined(BOOST_LIB_THREAD_OPT) \ + && defined(BOOST_LIB_RT_OPT) \ + && defined(BOOST_LIB_ARCH_AND_MODEL_OPT) \ + && defined(BOOST_LIB_VERSION) + +#if defined(BOOST_EMBTC_WIN64) +# define BOOST_LIB_SUFFIX ".a" +#else +# define BOOST_LIB_SUFFIX ".lib" +#endif + +#ifdef BOOST_AUTO_LINK_NOMANGLE +# pragma comment(lib, BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_SUFFIX) +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_SUFFIX) +# endif +#elif defined(BOOST_AUTO_LINK_TAGGED) +# pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT BOOST_LIB_ARCH_AND_MODEL_OPT BOOST_LIB_SUFFIX) +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT BOOST_LIB_ARCH_AND_MODEL_OPT BOOST_LIB_SUFFIX) +# endif +#elif defined(BOOST_AUTO_LINK_SYSTEM) +# pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_SUFFIX) +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_SUFFIX) +# endif +#elif defined(BOOST_LIB_BUILDID) +# pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT BOOST_LIB_ARCH_AND_MODEL_OPT "-" BOOST_LIB_VERSION "-" BOOST_STRINGIZE(BOOST_LIB_BUILDID) BOOST_LIB_SUFFIX) +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT BOOST_LIB_ARCH_AND_MODEL_OPT "-" BOOST_LIB_VERSION "-" BOOST_STRINGIZE(BOOST_LIB_BUILDID) BOOST_LIB_SUFFIX) +# endif +#else +# pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT BOOST_LIB_ARCH_AND_MODEL_OPT "-" BOOST_LIB_VERSION BOOST_LIB_SUFFIX) +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT BOOST_LIB_ARCH_AND_MODEL_OPT "-" BOOST_LIB_VERSION BOOST_LIB_SUFFIX) +# endif +#endif + +#else +# error "some required macros where not defined (internal logic error)." +#endif + + +#endif // _MSC_VER || __BORLANDC__ + +// +// finally undef any macros we may have set: +// +#ifdef BOOST_LIB_PREFIX +# undef BOOST_LIB_PREFIX +#endif +#if defined(BOOST_LIB_NAME) +# undef BOOST_LIB_NAME +#endif +// Don't undef this one: it can be set by the user and should be the +// same for all libraries: +//#if defined(BOOST_LIB_TOOLSET) +//# undef BOOST_LIB_TOOLSET +//#endif +#if defined(BOOST_LIB_THREAD_OPT) +# undef BOOST_LIB_THREAD_OPT +#endif +#if defined(BOOST_LIB_RT_OPT) +# undef BOOST_LIB_RT_OPT +#endif +#if defined(BOOST_LIB_ARCH_AND_MODEL_OPT) +# undef BOOST_LIB_ARCH_AND_MODEL_OPT +#endif +#if defined(BOOST_LIB_LINK_OPT) +# undef BOOST_LIB_LINK_OPT +#endif +#if defined(BOOST_LIB_DEBUG_OPT) +# undef BOOST_LIB_DEBUG_OPT +#endif +#if defined(BOOST_DYN_LINK) +# undef BOOST_DYN_LINK +#endif +#if defined(BOOST_LIB_SUFFIX) +# undef BOOST_LIB_SUFFIX +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/borland.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/borland.hpp new file mode 100644 index 0000000..567636c --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/borland.hpp @@ -0,0 +1,339 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright David Abrahams 2002 - 2003. +// (C) Copyright Aleksey Gurtovoy 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Borland C++ compiler setup: + +// +// versions check: +// we don't support Borland prior to version 5.4: +#if __BORLANDC__ < 0x540 +# error "Compiler not supported or configured - please reconfigure" +#endif + +// last known compiler version: +#if (__BORLANDC__ > 0x613) +//# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +//# else +//# pragma message( "boost: Unknown compiler version - please run the configure tests and report the results") +//# endif +#elif (__BORLANDC__ == 0x600) +# error "CBuilderX preview compiler is no longer supported" +#endif + +// +// Support macros to help with standard library detection +#if (__BORLANDC__ < 0x560) || defined(_USE_OLD_RW_STL) +# define BOOST_BCB_WITH_ROGUE_WAVE +#elif __BORLANDC__ < 0x570 +# define BOOST_BCB_WITH_STLPORT +#else +# define BOOST_BCB_WITH_DINKUMWARE +#endif + +// +// Version 5.0 and below: +# if __BORLANDC__ <= 0x0550 +// Borland C++Builder 4 and 5: +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +# if __BORLANDC__ == 0x0550 +// Borland C++Builder 5, command-line compiler 5.5: +# define BOOST_NO_OPERATORS_IN_NAMESPACE +# endif +// Variadic macros do not exist for C++ Builder versions 5 and below +#define BOOST_NO_CXX11_VARIADIC_MACROS +# endif + +// Version 5.51 and below: +#if (__BORLANDC__ <= 0x551) +# define BOOST_NO_CV_SPECIALIZATIONS +# define BOOST_NO_CV_VOID_SPECIALIZATIONS +# define BOOST_NO_DEDUCED_TYPENAME +// workaround for missing WCHAR_MAX/WCHAR_MIN: +#ifdef __cplusplus +#include +#include +#else +#include +#include +#endif // __cplusplus +#ifndef WCHAR_MAX +# define WCHAR_MAX 0xffff +#endif +#ifndef WCHAR_MIN +# define WCHAR_MIN 0 +#endif +#endif + +// Borland C++ Builder 6 and below: +#if (__BORLANDC__ <= 0x564) + +# if defined(NDEBUG) && defined(__cplusplus) + // fix broken so that Boost.test works: +# include +# undef strcmp +# endif + // fix broken errno declaration: +# include +# ifndef errno +# define errno errno +# endif + +#endif + +// +// new bug in 5.61: +#if (__BORLANDC__ >= 0x561) && (__BORLANDC__ <= 0x580) + // this seems to be needed by the command line compiler, but not the IDE: +# define BOOST_NO_MEMBER_FUNCTION_SPECIALIZATIONS +#endif + +// Borland C++ Builder 2006 Update 2 and below: +#if (__BORLANDC__ <= 0x582) +# define BOOST_NO_SFINAE +# define BOOST_BCB_PARTIAL_SPECIALIZATION_BUG +# define BOOST_NO_TEMPLATE_TEMPLATES + +# define BOOST_NO_PRIVATE_IN_AGGREGATE + +# ifdef _WIN32 +# define BOOST_NO_SWPRINTF +# elif defined(linux) || defined(__linux__) || defined(__linux) + // we should really be able to do without this + // but the wcs* functions aren't imported into std:: +# define BOOST_NO_STDC_NAMESPACE + // _CPPUNWIND doesn't get automatically set for some reason: +# pragma defineonoption BOOST_CPPUNWIND -x +# endif +#endif + +#if (__BORLANDC__ <= 0x613) // Beman has asked Alisdair for more info + // we shouldn't really need this - but too many things choke + // without it, this needs more investigation: +# define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +# define BOOST_NO_IS_ABSTRACT +# define BOOST_NO_FUNCTION_TYPE_SPECIALIZATIONS +# define BOOST_NO_USING_TEMPLATE +# define BOOST_SP_NO_SP_CONVERTIBLE + +// Temporary workaround +#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS +#endif + +// Borland C++ Builder 2008 and below: +# define BOOST_NO_INTEGRAL_INT64_T +# define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +# define BOOST_NO_DEPENDENT_NESTED_DERIVATIONS +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +# define BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE +# define BOOST_NO_NESTED_FRIENDSHIP +# define BOOST_NO_TYPENAME_WITH_CTOR +#if (__BORLANDC__ < 0x600) +# define BOOST_ILLEGAL_CV_REFERENCES +#endif + +// +// Positive Feature detection +// +// Borland C++ Builder 2008 and below: +#if (__BORLANDC__ >= 0x599) +# pragma defineonoption BOOST_CODEGEAR_0X_SUPPORT -Ax +#endif +// +// C++0x Macros: +// +#if !defined( BOOST_CODEGEAR_0X_SUPPORT ) || (__BORLANDC__ < 0x610) +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +# define BOOST_NO_CXX11_DECLTYPE +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +# define BOOST_NO_CXX11_EXTERN_TEMPLATE +# define BOOST_NO_CXX11_RVALUE_REFERENCES +# define BOOST_NO_CXX11_SCOPED_ENUMS +# define BOOST_NO_CXX11_STATIC_ASSERT +#else +# define BOOST_HAS_ALIGNOF +# define BOOST_HAS_CHAR16_T +# define BOOST_HAS_CHAR32_T +# define BOOST_HAS_DECLTYPE +# define BOOST_HAS_EXPLICIT_CONVERSION_OPS +# define BOOST_HAS_REF_QUALIFIER +# define BOOST_HAS_RVALUE_REFS +# define BOOST_HAS_STATIC_ASSERT +#endif + +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DEFAULTED_MOVES +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS // UTF-8 still not supported +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +#if __BORLANDC__ >= 0x590 +# define BOOST_HAS_TR1_HASH + +# define BOOST_HAS_MACRO_USE_FACET +#endif + +// +// Post 0x561 we have long long and stdint.h: +#if __BORLANDC__ >= 0x561 +# ifndef __NO_LONG_LONG +# define BOOST_HAS_LONG_LONG +# else +# define BOOST_NO_LONG_LONG +# endif + // On non-Win32 platforms let the platform config figure this out: +# ifdef _WIN32 +# define BOOST_HAS_STDINT_H +# endif +#endif + +// Borland C++Builder 6 defaults to using STLPort. If _USE_OLD_RW_STL is +// defined, then we have 0x560 or greater with the Rogue Wave implementation +// which presumably has the std::DBL_MAX bug. +#if defined( BOOST_BCB_WITH_ROGUE_WAVE ) +// is partly broken, some macros define symbols that are really in +// namespace std, so you end up having to use illegal constructs like +// std::DBL_MAX, as a fix we'll just include float.h and have done with: +#include +#endif +// +// __int64: +// +#if (__BORLANDC__ >= 0x530) && !defined(__STRICT_ANSI__) +# define BOOST_HAS_MS_INT64 +#endif +// +// check for exception handling support: +// +#if !defined(_CPPUNWIND) && !defined(BOOST_CPPUNWIND) && !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif +// +// all versions have a : +// +#ifndef __STRICT_ANSI__ +# define BOOST_HAS_DIRENT_H +#endif +// +// all versions support __declspec: +// +#if defined(__STRICT_ANSI__) +// config/platform/win32.hpp will define BOOST_SYMBOL_EXPORT, etc., unless already defined +# define BOOST_SYMBOL_EXPORT +#endif +// +// ABI fixing headers: +// +#if __BORLANDC__ != 0x600 // not implemented for version 6 compiler yet +#ifndef BOOST_ABI_PREFIX +# define BOOST_ABI_PREFIX "boost/config/abi/borland_prefix.hpp" +#endif +#ifndef BOOST_ABI_SUFFIX +# define BOOST_ABI_SUFFIX "boost/config/abi/borland_suffix.hpp" +#endif +#endif +// +// Disable Win32 support in ANSI mode: +// +#if __BORLANDC__ < 0x600 +# pragma defineonoption BOOST_DISABLE_WIN32 -A +#elif defined(__STRICT_ANSI__) +# define BOOST_DISABLE_WIN32 +#endif +// +// MSVC compatibility mode does some nasty things: +// TODO: look up if this doesn't apply to the whole 12xx range +// +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +# define BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +# define BOOST_NO_VOID_RETURNS +#endif + +// Borland did not implement value-initialization completely, as I reported +// in 2007, Borland Report 51854, "Value-initialization: POD struct should be +// zero-initialized", http://qc.embarcadero.com/wc/qcmain.aspx?d=51854 +// See also: http://www.boost.org/libs/utility/value_init.htm#compiler_issues +// (Niels Dekker, LKEB, April 2010) +#define BOOST_NO_COMPLETE_VALUE_INITIALIZATION + +#define BOOST_BORLANDC __BORLANDC__ +#define BOOST_COMPILER "Classic Borland C++ version " BOOST_STRINGIZE(__BORLANDC__) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang.hpp new file mode 100644 index 0000000..1eeed31 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang.hpp @@ -0,0 +1,366 @@ +// (C) Copyright Douglas Gregor 2010 +// +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Clang compiler setup. + +#define BOOST_HAS_PRAGMA_ONCE + +// Detecting `-fms-extension` compiler flag assuming that _MSC_VER defined when that flag is used. +#if defined (_MSC_VER) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 4)) +# define BOOST_HAS_PRAGMA_DETECT_MISMATCH +#endif + +// When compiling with clang before __has_extension was defined, +// even if one writes 'defined(__has_extension) && __has_extension(xxx)', +// clang reports a compiler error. So the only workaround found is: + +#ifndef __has_extension +#define __has_extension __has_feature +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif + +#if !__has_feature(cxx_exceptions) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + +#if !__has_feature(cxx_rtti) && !defined(BOOST_NO_RTTI) +# define BOOST_NO_RTTI +#endif + +#if !__has_feature(cxx_rtti) && !defined(BOOST_NO_TYPEID) +# define BOOST_NO_TYPEID +#endif + +#if !__has_feature(cxx_thread_local) +# define BOOST_NO_CXX11_THREAD_LOCAL +#endif + +#ifdef __is_identifier +#if !__is_identifier(__int64) && !defined(__GNUC__) +# define BOOST_HAS_MS_INT64 +#endif +#endif + +#if __has_include() +# define BOOST_HAS_STDINT_H +#endif + +#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) +#if (__clang_major__ >= 4) && defined(__has_include) +#if __has_include() +# define BOOST_HAS_FLOAT128 +#endif +#endif +#endif + + +#define BOOST_HAS_NRVO + +// Branch prediction hints +#if !defined (__c2__) && defined(__has_builtin) +#if __has_builtin(__builtin_expect) +#define BOOST_LIKELY(x) __builtin_expect(x, 1) +#define BOOST_UNLIKELY(x) __builtin_expect(x, 0) +#endif +#endif + +// Clang supports "long long" in all compilation modes. +#define BOOST_HAS_LONG_LONG + +// +// We disable this if the compiler is really nvcc with C++03 as it +// doesn't actually support __int128 as of CUDA_VERSION=7500 +// even though it defines __SIZEOF_INT128__. +// See https://svn.boost.org/trac/boost/ticket/10418 +// https://svn.boost.org/trac/boost/ticket/11852 +// Only re-enable this for nvcc if you're absolutely sure +// of the circumstances under which it's supported. +// Similarly __SIZEOF_INT128__ is defined when targetting msvc +// compatibility even though the required support functions are absent. +// +#if defined(__CUDACC__) +# if defined(BOOST_GCC_CXX11) +# define BOOST_NVCC_CXX11 +# else +# define BOOST_NVCC_CXX03 +# endif +#endif + +#if defined(__SIZEOF_INT128__) && !defined(BOOST_NVCC_CXX03) && !defined(_MSC_VER) +# define BOOST_HAS_INT128 +#endif + + +// +// Dynamic shared object (DSO) and dynamic-link library (DLL) support +// +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) +# define BOOST_HAS_DECLSPEC +# define BOOST_SYMBOL_EXPORT __attribute__((__dllexport__)) +# define BOOST_SYMBOL_IMPORT __attribute__((__dllimport__)) +#else +# define BOOST_SYMBOL_EXPORT __attribute__((__visibility__("default"))) +# define BOOST_SYMBOL_VISIBLE __attribute__((__visibility__("default"))) +# define BOOST_SYMBOL_IMPORT +#endif + +// +// The BOOST_FALLTHROUGH macro can be used to annotate implicit fall-through +// between switch labels. +// +#if __cplusplus >= 201103L && defined(__has_warning) +# if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +# define BOOST_FALLTHROUGH [[clang::fallthrough]] +# endif +#endif + +#if !__has_feature(cxx_auto_type) +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#endif + +// +// Currently clang on Windows using VC++ RTL does not support C++11's char16_t or char32_t +// +#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || !(defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(__GNUC__) +#define BOOST_HAS_EXPM1 +#define BOOST_HAS_LOG1P +#endif + +#if !__has_feature(cxx_constexpr) +# define BOOST_NO_CXX11_CONSTEXPR +#endif + +#if !__has_feature(cxx_decltype) +# define BOOST_NO_CXX11_DECLTYPE +#endif + +#if !__has_feature(cxx_decltype_incomplete_return_types) +# define BOOST_NO_CXX11_DECLTYPE_N3276 +#endif + +#if !__has_feature(cxx_defaulted_functions) +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#endif + +#if !__has_feature(cxx_deleted_functions) +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +#endif + +#if !__has_feature(cxx_explicit_conversions) +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#endif + +#if !__has_feature(cxx_default_function_template_args) +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#endif + +#if !__has_feature(cxx_generalized_initializers) +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#endif + +#if !__has_feature(cxx_lambdas) +# define BOOST_NO_CXX11_LAMBDAS +#endif + +#if !__has_feature(cxx_local_type_template_args) +# define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#endif + +#if !__has_feature(cxx_noexcept) +# define BOOST_NO_CXX11_NOEXCEPT +#endif + +#if !__has_feature(cxx_nullptr) +# define BOOST_NO_CXX11_NULLPTR +#endif + +#if !__has_feature(cxx_range_for) +# define BOOST_NO_CXX11_RANGE_BASED_FOR +#endif + +#if !__has_feature(cxx_raw_string_literals) +# define BOOST_NO_CXX11_RAW_LITERALS +#endif + +#if !__has_feature(cxx_reference_qualified_functions) +# define BOOST_NO_CXX11_REF_QUALIFIERS +#endif + +#if !__has_feature(cxx_generalized_initializers) +# define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#endif + +#if !__has_feature(cxx_rvalue_references) +# define BOOST_NO_CXX11_RVALUE_REFERENCES +#endif + +#if !__has_feature(cxx_strong_enums) +# define BOOST_NO_CXX11_SCOPED_ENUMS +#endif + +#if !__has_feature(cxx_static_assert) +# define BOOST_NO_CXX11_STATIC_ASSERT +#endif + +#if !__has_feature(cxx_alias_templates) +# define BOOST_NO_CXX11_TEMPLATE_ALIASES +#endif + +#if !__has_feature(cxx_unicode_literals) +# define BOOST_NO_CXX11_UNICODE_LITERALS +#endif + +#if !__has_feature(cxx_variadic_templates) +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif + +#if !__has_feature(cxx_user_literals) +# define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#endif + +#if !__has_feature(cxx_alignas) +# define BOOST_NO_CXX11_ALIGNAS +#endif + +#if !__has_feature(cxx_alignof) +# define BOOST_NO_CXX11_ALIGNOF +#endif + +#if !__has_feature(cxx_trailing_return) +# define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#endif + +#if !__has_feature(cxx_inline_namespaces) +# define BOOST_NO_CXX11_INLINE_NAMESPACES +#endif + +#if !__has_feature(cxx_override_control) +# define BOOST_NO_CXX11_FINAL +# define BOOST_NO_CXX11_OVERRIDE +#endif + +#if !__has_feature(cxx_unrestricted_unions) +# define BOOST_NO_CXX11_UNRESTRICTED_UNION +#endif + +#if !(__has_feature(__cxx_binary_literals__) || __has_extension(__cxx_binary_literals__)) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif + +#if !__has_feature(__cxx_decltype_auto__) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif + +#if !__has_feature(__cxx_aggregate_nsdmi__) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif + +#if !__has_feature(__cxx_init_captures__) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif + +#if !__has_feature(__cxx_generic_lambdas__) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif + +// clang < 3.5 has a defect with dependent type, like following. +// +// template +// constexpr typename enable_if >::type foo(T &) +// { } // error: no return statement in constexpr function +// +// This issue also affects C++11 mode, but C++11 constexpr requires return stmt. +// Therefore we don't care such case. +// +// Note that we can't check Clang version directly as the numbering system changes depending who's +// creating the Clang release (see https://github.com/boostorg/config/pull/39#issuecomment-59927873) +// so instead verify that we have a feature that was introduced at the same time as working C++14 +// constexpr (generic lambda's in this case): +// +#if !__has_feature(__cxx_generic_lambdas__) || !__has_feature(__cxx_relaxed_constexpr__) +# define BOOST_NO_CXX14_CONSTEXPR +#endif + +#if !__has_feature(__cxx_return_type_deduction__) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif + +#if !__has_feature(__cxx_variable_templates__) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif + +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +// Clang 3.9+ in c++1z +#if !__has_cpp_attribute(fallthrough) || __cplusplus < 201406L +# define BOOST_NO_CXX17_INLINE_VARIABLES +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif + +#if __cplusplus < 201103L +#define BOOST_NO_CXX11_SFINAE_EXPR +#endif + +#if __cplusplus < 201400 +// All versions with __cplusplus above this value seem to support this: +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif + +// Unreachable code markup +#if defined(__has_builtin) +#if __has_builtin(__builtin_unreachable) +#define BOOST_UNREACHABLE_RETURN(x) __builtin_unreachable(); +#endif +#endif + +// Deprecated symbol markup +#if __has_attribute(deprecated) +#define BOOST_DEPRECATED(msg) __attribute__((deprecated(msg))) +#endif + +#if (__clang_major__ == 3) && (__clang_minor__ == 0) +// Apparently a clang bug: +# define BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS +#endif + +// Clang has supported the 'unused' attribute since the first release. +#define BOOST_ATTRIBUTE_UNUSED __attribute__((__unused__)) + +// Type aliasing hint. +#if __has_attribute(__may_alias__) +# define BOOST_MAY_ALIAS __attribute__((__may_alias__)) +#endif + +#ifndef BOOST_COMPILER +# define BOOST_COMPILER "Clang version " __clang_version__ +#endif + +// Macro used to identify the Clang compiler. +#define BOOST_CLANG 1 + +// BOOST_CLANG_VERSION +#include diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang_version.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang_version.hpp new file mode 100644 index 0000000..a61de13 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/clang_version.hpp @@ -0,0 +1,89 @@ +// Copyright 2021 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt) + +#if !defined(__apple_build_version__) + +# define BOOST_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__ % 100) + +#else +# define BOOST_CLANG_REPORTED_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__ % 100) + +// https://en.wikipedia.org/wiki/Xcode#Toolchain_versions + +# if BOOST_CLANG_REPORTED_VERSION >= 150000 +# define BOOST_CLANG_VERSION 160000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 140003 +# define BOOST_CLANG_VERSION 150000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 140000 +# define BOOST_CLANG_VERSION 140000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 130100 +# define BOOST_CLANG_VERSION 130000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 130000 +# define BOOST_CLANG_VERSION 120000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 120005 +# define BOOST_CLANG_VERSION 110100 + +# elif BOOST_CLANG_REPORTED_VERSION >= 120000 +# define BOOST_CLANG_VERSION 100000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 110003 +# define BOOST_CLANG_VERSION 90000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 110000 +# define BOOST_CLANG_VERSION 80000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 100001 +# define BOOST_CLANG_VERSION 70000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 100000 +# define BOOST_CLANG_VERSION 60001 + +# elif BOOST_CLANG_REPORTED_VERSION >= 90100 +# define BOOST_CLANG_VERSION 50002 + +# elif BOOST_CLANG_REPORTED_VERSION >= 90000 +# define BOOST_CLANG_VERSION 40000 + +# elif BOOST_CLANG_REPORTED_VERSION >= 80000 +# define BOOST_CLANG_VERSION 30900 + +# elif BOOST_CLANG_REPORTED_VERSION >= 70300 +# define BOOST_CLANG_VERSION 30800 + +# elif BOOST_CLANG_REPORTED_VERSION >= 70000 +# define BOOST_CLANG_VERSION 30700 + +# elif BOOST_CLANG_REPORTED_VERSION >= 60100 +# define BOOST_CLANG_VERSION 30600 + +# elif BOOST_CLANG_REPORTED_VERSION >= 60000 +# define BOOST_CLANG_VERSION 30500 + +# elif BOOST_CLANG_REPORTED_VERSION >= 50100 +# define BOOST_CLANG_VERSION 30400 + +# elif BOOST_CLANG_REPORTED_VERSION >= 50000 +# define BOOST_CLANG_VERSION 30300 + +# elif BOOST_CLANG_REPORTED_VERSION >= 40200 +# define BOOST_CLANG_VERSION 30200 + +# elif BOOST_CLANG_REPORTED_VERSION >= 30100 +# define BOOST_CLANG_VERSION 30100 + +# elif BOOST_CLANG_REPORTED_VERSION >= 20100 +# define BOOST_CLANG_VERSION 30000 + +# else +# define BOOST_CLANG_VERSION 20900 + +# endif + +# undef BOOST_CLANG_REPORTED_VERSION +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/codegear.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/codegear.hpp new file mode 100644 index 0000000..4d3f42a --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/codegear.hpp @@ -0,0 +1,385 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright David Abrahams 2002 - 2003. +// (C) Copyright Aleksey Gurtovoy 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// CodeGear C++ compiler setup: + +// +// versions check: +// last known and checked version is 0x740 +#if (__CODEGEARC__ > 0x740) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# else +# pragma message( "boost: Unknown compiler version - please run the configure tests and report the results") +# endif +#endif + +#ifdef __clang__ // Clang enhanced Windows compiler + +# include "clang.hpp" +# define BOOST_NO_CXX11_THREAD_LOCAL +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR + +// This bug has been reported to Embarcadero + +#if defined(BOOST_HAS_INT128) +#undef BOOST_HAS_INT128 +#endif +#if defined(BOOST_HAS_FLOAT128) +#undef BOOST_HAS_FLOAT128 +#endif + +// The clang-based compilers can not do 128 atomic exchanges + +#define BOOST_ATOMIC_NO_CMPXCHG16B + +// 32 functions are missing from the current RTL in cwchar, so it really can not be used even if it exists + +# define BOOST_NO_CWCHAR + +# ifndef __MT__ /* If compiling in single-threaded mode, assume there is no CXX11_HDR_ATOMIC */ +# define BOOST_NO_CXX11_HDR_ATOMIC +# endif + +/* temporarily disable this until we can link against fegetround fesetround feholdexcept */ + +#define BOOST_NO_FENV_H + +/* Reported this bug to Embarcadero with the latest C++ Builder Rio release */ + +#define BOOST_NO_CXX11_HDR_EXCEPTION + +// +// check for exception handling support: +// +#if !defined(_CPPUNWIND) && !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + +/* + +// On non-Win32 platforms let the platform config figure this out: +#ifdef _WIN32 +# define BOOST_HAS_STDINT_H +#endif + +// +// __int64: +// +#if !defined(__STRICT_ANSI__) +# define BOOST_HAS_MS_INT64 +#endif +// +// all versions have a : +// +#if !defined(__STRICT_ANSI__) +# define BOOST_HAS_DIRENT_H +#endif +// +// Disable Win32 support in ANSI mode: +// +# pragma defineonoption BOOST_DISABLE_WIN32 -A +// +// MSVC compatibility mode does some nasty things: +// TODO: look up if this doesn't apply to the whole 12xx range +// +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +# define BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +# define BOOST_NO_VOID_RETURNS +#endif +// + +*/ + +// Specific settings for Embarcadero drivers +# define BOOST_EMBTC __CODEGEARC__ +# define BOOST_EMBTC_FULL_VER ((__clang_major__ << 16) | \ + (__clang_minor__ << 8) | \ + __clang_patchlevel__ ) + +// Detecting which Embarcadero driver is being used +#if defined(BOOST_EMBTC) +# if defined(_WIN64) +# define BOOST_EMBTC_WIN64 1 +# define BOOST_EMBTC_WINDOWS 1 +# ifndef BOOST_USE_WINDOWS_H +# define BOOST_USE_WINDOWS_H +# endif +# elif defined(_WIN32) +# define BOOST_EMBTC_WIN32C 1 +# define BOOST_EMBTC_WINDOWS 1 +# ifndef BOOST_USE_WINDOWS_H +# define BOOST_USE_WINDOWS_H +# endif +# elif defined(__APPLE__) && defined(__arm__) +# define BOOST_EMBTC_IOSARM 1 +# define BOOST_EMBTC_IOS 1 +# elif defined(__APPLE__) && defined(__aarch64__) +# define BOOST_EMBTC_IOSARM64 1 +# define BOOST_EMBTC_IOS 1 +# elif defined(__ANDROID__) && defined(__arm__) +# define BOOST_EMBTC_AARM 1 +# define BOOST_EMBTC_ANDROID 1 +# elif +# if defined(BOOST_ASSERT_CONFIG) +# error "Unknown Embarcadero driver" +# else +# warning "Unknown Embarcadero driver" +# endif /* defined(BOOST_ASSERT_CONFIG) */ +# endif +#endif /* defined(BOOST_EMBTC) */ + +#if defined(BOOST_EMBTC_WINDOWS) + +#if !defined(_chdir) +#define _chdir(x) chdir(x) +#endif + +#if !defined(_dup2) +#define _dup2(x,y) dup2(x,y) +#endif + +#endif + +# undef BOOST_COMPILER +# define BOOST_COMPILER "Embarcadero-Clang C++ version " BOOST_STRINGIZE(__CODEGEARC__) " clang: " __clang_version__ +// # define __CODEGEARC_CLANG__ __CODEGEARC__ +// # define __EMBARCADERO_CLANG__ __CODEGEARC__ +// # define __BORLANDC_CLANG__ __BORLANDC__ + +#else // #if !defined(__clang__) + +# define BOOST_CODEGEARC __CODEGEARC__ +# define BOOST_BORLANDC __BORLANDC__ + +#if !defined( BOOST_WITH_CODEGEAR_WARNINGS ) +// these warnings occur frequently in optimized template code +# pragma warn -8004 // var assigned value, but never used +# pragma warn -8008 // condition always true/false +# pragma warn -8066 // dead code can never execute +# pragma warn -8104 // static members with ctors not threadsafe +# pragma warn -8105 // reference member in class without ctors +#endif + +// CodeGear C++ Builder 2009 +#if (__CODEGEARC__ <= 0x613) +# define BOOST_NO_INTEGRAL_INT64_T +# define BOOST_NO_DEPENDENT_NESTED_DERIVATIONS +# define BOOST_NO_PRIVATE_IN_AGGREGATE +# define BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE + // we shouldn't really need this - but too many things choke + // without it, this needs more investigation: +# define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +# define BOOST_SP_NO_SP_CONVERTIBLE +#endif + +// CodeGear C++ Builder 2010 +#if (__CODEGEARC__ <= 0x621) +# define BOOST_NO_TYPENAME_WITH_CTOR // Cannot use typename keyword when making temporaries of a dependant type +# define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +# define BOOST_NO_NESTED_FRIENDSHIP // TC1 gives nested classes access rights as any other member +# define BOOST_NO_USING_TEMPLATE +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +// Temporary hack, until specific MPL preprocessed headers are generated +# define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS + +// CodeGear has not yet completely implemented value-initialization, for +// example for array types, as I reported in 2010: Embarcadero Report 83751, +// "Value-initialization: arrays should have each element value-initialized", +// http://qc.embarcadero.com/wc/qcmain.aspx?d=83751 +// Last checked version: Embarcadero C++ 6.21 +// See also: http://www.boost.org/libs/utility/value_init.htm#compiler_issues +// (Niels Dekker, LKEB, April 2010) +# define BOOST_NO_COMPLETE_VALUE_INITIALIZATION + +# if defined(NDEBUG) && defined(__cplusplus) + // fix broken so that Boost.test works: +# include +# undef strcmp +# endif + // fix broken errno declaration: +# include +# ifndef errno +# define errno errno +# endif + +#endif + +// Reportedly, #pragma once is supported since C++ Builder 2010 +#if (__CODEGEARC__ >= 0x620) +# define BOOST_HAS_PRAGMA_ONCE +#endif + +#define BOOST_NO_FENV_H + +// +// C++0x macros: +// +#if (__CODEGEARC__ <= 0x620) +#define BOOST_NO_CXX11_STATIC_ASSERT +#else +#define BOOST_HAS_STATIC_ASSERT +#endif +#define BOOST_HAS_CHAR16_T +#define BOOST_HAS_CHAR32_T +#define BOOST_HAS_LONG_LONG +// #define BOOST_HAS_ALIGNOF +#define BOOST_HAS_DECLTYPE +#define BOOST_HAS_EXPLICIT_CONVERSION_OPS +// #define BOOST_HAS_RVALUE_REFS +#define BOOST_HAS_SCOPED_ENUM +// #define BOOST_HAS_STATIC_ASSERT +#define BOOST_HAS_STD_TYPE_TRAITS + +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXTERN_TEMPLATE +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif + +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif + +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif + +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +// +// TR1 macros: +// +#define BOOST_HAS_TR1_HASH +#define BOOST_HAS_TR1_TYPE_TRAITS +#define BOOST_HAS_TR1_UNORDERED_MAP +#define BOOST_HAS_TR1_UNORDERED_SET + +#define BOOST_HAS_MACRO_USE_FACET + +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST + +// On non-Win32 platforms let the platform config figure this out: +#ifdef _WIN32 +# define BOOST_HAS_STDINT_H +#endif + +// +// __int64: +// +#if !defined(__STRICT_ANSI__) +# define BOOST_HAS_MS_INT64 +#endif +// +// check for exception handling support: +// +#if !defined(_CPPUNWIND) && !defined(BOOST_CPPUNWIND) && !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif +// +// all versions have a : +// +#if !defined(__STRICT_ANSI__) +# define BOOST_HAS_DIRENT_H +#endif +// +// all versions support __declspec: +// +#if defined(__STRICT_ANSI__) +// config/platform/win32.hpp will define BOOST_SYMBOL_EXPORT, etc., unless already defined +# define BOOST_SYMBOL_EXPORT +#endif +// +// ABI fixing headers: +// +#ifndef BOOST_ABI_PREFIX +# define BOOST_ABI_PREFIX "boost/config/abi/borland_prefix.hpp" +#endif +#ifndef BOOST_ABI_SUFFIX +# define BOOST_ABI_SUFFIX "boost/config/abi/borland_suffix.hpp" +#endif +// +// Disable Win32 support in ANSI mode: +// +# pragma defineonoption BOOST_DISABLE_WIN32 -A +// +// MSVC compatibility mode does some nasty things: +// TODO: look up if this doesn't apply to the whole 12xx range +// +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +# define BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +# define BOOST_NO_VOID_RETURNS +#endif + +#define BOOST_COMPILER "CodeGear C++ version " BOOST_STRINGIZE(__CODEGEARC__) + +#endif // #if !defined(__clang__) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/comeau.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/comeau.hpp new file mode 100644 index 0000000..ca80fac --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/comeau.hpp @@ -0,0 +1,59 @@ +// (C) Copyright John Maddock 2001. +// (C) Copyright Douglas Gregor 2001. +// (C) Copyright Peter Dimov 2001. +// (C) Copyright Aleksey Gurtovoy 2003. +// (C) Copyright Beman Dawes 2003. +// (C) Copyright Jens Maurer 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Comeau C++ compiler setup: + +#include + +#if (__COMO_VERSION__ <= 4245) + +# if defined(_MSC_VER) && _MSC_VER <= 1300 +# if _MSC_VER > 100 + // only set this in non-strict mode: +# define BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +# endif +# endif + +// Void returns don't work when emulating VC 6 (Peter Dimov) +// TODO: look up if this doesn't apply to the whole 12xx range +# if defined(_MSC_VER) && (_MSC_VER < 1300) +# define BOOST_NO_VOID_RETURNS +# endif + +#endif // version 4245 + +// +// enable __int64 support in VC emulation mode +// +# if defined(_MSC_VER) && (_MSC_VER >= 1200) +# define BOOST_HAS_MS_INT64 +# endif + +#define BOOST_COMPILER "Comeau compiler version " BOOST_STRINGIZE(__COMO_VERSION__) + +// +// versions check: +// we don't know Comeau prior to version 4245: +#if __COMO_VERSION__ < 4245 +# error "Compiler not configured - please reconfigure" +#endif +// +// last known and checked version is 4245: +#if (__COMO_VERSION__ > 4245) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/common_edg.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/common_edg.hpp new file mode 100644 index 0000000..dc04989 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/common_edg.hpp @@ -0,0 +1,183 @@ +// (C) Copyright John Maddock 2001 - 2002. +// (C) Copyright Jens Maurer 2001. +// (C) Copyright David Abrahams 2002. +// (C) Copyright Aleksey Gurtovoy 2002. +// (C) Copyright Markus Schoepflin 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// +// Options common to all edg based compilers. +// +// This is included from within the individual compiler mini-configs. + +#ifndef __EDG_VERSION__ +# error This file requires that __EDG_VERSION__ be defined. +#endif + +#if (__EDG_VERSION__ <= 238) +# define BOOST_NO_INTEGRAL_INT64_T +# define BOOST_NO_SFINAE +#endif + +#if (__EDG_VERSION__ <= 240) +# define BOOST_NO_VOID_RETURNS +#endif + +#if (__EDG_VERSION__ <= 241) && !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +# define BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +#endif + +#if (__EDG_VERSION__ <= 244) && !defined(BOOST_NO_TEMPLATE_TEMPLATES) +# define BOOST_NO_TEMPLATE_TEMPLATES +#endif + +#if (__EDG_VERSION__ < 300) && !defined(BOOST_NO_IS_ABSTRACT) +# define BOOST_NO_IS_ABSTRACT +#endif + +#if (__EDG_VERSION__ <= 303) && !defined(BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL) +# define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +#endif + +// See also kai.hpp which checks a Kai-specific symbol for EH +# if !defined(__KCC) && !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +# endif + +# if !defined(__NO_LONG_LONG) +# define BOOST_HAS_LONG_LONG +# else +# define BOOST_NO_LONG_LONG +# endif + +// Not sure what version was the first to support #pragma once, but +// different EDG-based compilers (e.g. Intel) supported it for ages. +// Add a proper version check if it causes problems. +#define BOOST_HAS_PRAGMA_ONCE + +// +// C++0x features +// +// See above for BOOST_NO_LONG_LONG +// +#if (__EDG_VERSION__ < 310) +# define BOOST_NO_CXX11_EXTERN_TEMPLATE +#endif +#if (__EDG_VERSION__ <= 310) +// No support for initializer lists +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#endif +#if (__EDG_VERSION__ < 400) +# define BOOST_NO_CXX11_VARIADIC_MACROS +#endif + +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +//__cpp_decltype 200707 possibly? +#define BOOST_NO_CXX11_DECLTYPE +#define BOOST_NO_CXX11_DECLTYPE_N3276 + +#if !defined(__cpp_unicode_characters) || (__cpp_unicode_characters < 200704) +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +#endif +#if !defined(__cpp_unicode_literals) || (__cpp_unicode_literals < 200710) +# define BOOST_NO_CXX11_UNICODE_LITERALS +#endif +#if !defined(__cpp_user_defined_literals) || (__cpp_user_defined_literals < 200809) +# define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#endif +#if !defined(__cpp_variadic_templates) || (__cpp_variadic_templates < 200704) +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 200907) +# define BOOST_NO_CXX11_CONSTEXPR +#endif +#if !defined(__cpp_lambdas) || (__cpp_lambdas < 200907) +# define BOOST_NO_CXX11_LAMBDAS +#endif +#if !defined(__cpp_range_based_for) || (__cpp_range_based_for < 200710) +# define BOOST_NO_CXX11_RANGE_BASED_FOR +#endif +#if !defined(__cpp_raw_strings) || (__cpp_raw_strings < 200610) +# define BOOST_NO_CXX11_RAW_LITERALS +#endif + + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif + +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +#ifdef c_plusplus +// EDG has "long long" in non-strict mode +// However, some libraries have insufficient "long long" support +// #define BOOST_HAS_LONG_LONG +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/compaq_cxx.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/compaq_cxx.hpp new file mode 100644 index 0000000..4d6b8ab --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/compaq_cxx.hpp @@ -0,0 +1,19 @@ +// (C) Copyright John Maddock 2001 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Tru64 C++ compiler setup (now HP): + +#define BOOST_COMPILER "HP Tru64 C++ " BOOST_STRINGIZE(__DECCXX_VER) + +#include + +// +// versions check: +// Nothing to do here? + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/cray.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/cray.hpp new file mode 100644 index 0000000..e40fd05 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/cray.hpp @@ -0,0 +1,446 @@ +// Copyright 2011 John Maddock +// Copyright 2013, 2017-2018 Cray, Inc. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Cray C++ compiler setup. +// +// There are a few parameters that affect the macros defined in this file: +// +// - What version of CCE (Cray Compiling Environment) are we running? This +// comes from the '_RELEASE_MAJOR', '_RELEASE_MINOR', and +// '_RELEASE_PATCHLEVEL' macros. +// - What C++ standards conformance level are we using (e.g. '-h +// std=c++14')? This comes from the '__cplusplus' macro. +// - Are we using GCC extensions ('-h gnu' or '-h nognu')? If we have '-h +// gnu' then CCE emulates GCC, and the macros '__GNUC__', +// '__GNUC_MINOR__', and '__GNUC_PATCHLEVEL__' are defined. +// +// This file is organized as follows: +// +// - Verify that the combination of parameters listed above is supported. +// If we have an unsupported combination, we abort with '#error'. +// - Establish baseline values for all Boost macros. +// - Apply changes to the baseline macros based on compiler version. These +// changes are cummulative so each version section only describes the +// changes since the previous version. +// - Within each version section, we may also apply changes based on +// other parameters (i.e. C++ standards conformance level and GCC +// extensions). +// +// To test changes to this file: +// +// ``` +// module load cce/8.6.5 # Pick the version you want to test. +// cd boost/libs/config/test/all +// b2 -j 8 toolset=cray cxxstd=03 cxxstd=11 cxxstd=14 cxxstd-dialect=gnu linkflags=-lrt +// ``` +// Note: Using 'cxxstd-dialect=iso' is not supported at this time (the +// tests run, but many tests fail). +// +// Note: 'linkflags=-lrt' is needed in Cray Linux Environment. Otherwise +// you get an 'undefined reference to clock_gettime' error. +// +// Note: If a test '*_fail.cpp' file compiles, but fails to run, then it is +// reported as a defect. However, this is not actually a defect. This is an +// area where the test system is somewhat broken. Tests that are failing +// because of this problem are noted in the comments. +// +// Pay attention to the macro definitions for the macros you wish to +// modify. For example, only macros categorized as compiler macros should +// appear in this file; platform macros should not appear in this file. +// Also, some macros have to be defined to specific values; it is not +// always enough to define or undefine a macro. +// +// Macro definitions are available in the source code at: +// +// `boost/libs/config/doc/html/boost_config/boost_macro_reference.html` +// +// Macro definitions are also available online at: +// +// http://www.boost.org/doc/libs/master/libs/config/doc/html/boost_config/boost_macro_reference.html +// +// Typically, if you enable a feature, and the tests pass, then you have +// nothing to worry about. However, it's sometimes hard to figure out if a +// disabled feature needs to stay disabled. To get a list of disabled +// features, run 'b2' in 'boost/libs/config/checks'. These are the macros +// you should pay attention to (in addition to macros that cause test +// failures). + +//// +//// Front matter +//// + +// In a developer build of the Cray compiler (i.e. a compiler built by a +// Cray employee), the release patch level is reported as "x". This gives +// versions that look like e.g. "8.6.x". +// +// To accomplish this, the the Cray compiler preprocessor inserts: +// +// #define _RELEASE_PATCHLEVEL x +// +// If we are using a developer build of the compiler, we want to use the +// configuration macros for the most recent patch level of the release. To +// accomplish this, we'll pretend that _RELEASE_PATCHLEVEL is 99. +// +// However, it's difficult to detect if _RELEASE_PATCHLEVEL is x. We must +// consider that the x will be expanded if x is defined as a macro +// elsewhere. For example, imagine if someone put "-D x=3" on the command +// line, and _RELEASE_PATCHLEVEL is x. Then _RELEASE_PATCHLEVEL would +// expand to 3, and we could not distinguish it from an actual +// _RELEASE_PATCHLEVEL of 3. This problem only affects developer builds; in +// production builds, _RELEASE_PATCHLEVEL is always an integer. +// +// IMPORTANT: In developer builds, if x is defined as a macro, you will get +// an incorrect configuration. The behavior in this case is undefined. +// +// Even if x is not defined, we have to use some trickery to detect if +// _RELEASE_PATCHLEVEL is x. First we define BOOST_CRAY_x to some arbitrary +// magic value, 9867657. Then we use BOOST_CRAY_APPEND to append the +// expanded value of _RELEASE_PATCHLEVEL to the string "BOOST_CRAY_". +// +// - If _RELEASE_PATCHLEVEL is undefined, we get "BOOST_CRAY_". +// - If _RELEASE_PATCHLEVEL is 5, we get "BOOST_CRAY_5". +// - If _RELEASE_PATCHLEVEL is x (and x is not defined) we get +// "BOOST_CRAY_x": +// +// Then we check if BOOST_CRAY_x is equal to the output of +// BOOST_CRAY_APPEND. In other words, the output of BOOST_CRAY_APPEND is +// treated as a macro name, and expanded again. If we can safely assume +// that BOOST_CRAY_ is not a macro defined as our magic number, and +// BOOST_CRAY_5 is not a macro defined as our magic number, then the only +// way the equality test can pass is if _RELEASE_PATCHLEVEL expands to x. +// +// So, that is how we detect if we are using a developer build of the Cray +// compiler. + +#define BOOST_CRAY_x 9867657 // Arbitrary number +#define BOOST_CRAY_APPEND(MACRO) BOOST_CRAY_APPEND_INTERNAL(MACRO) +#define BOOST_CRAY_APPEND_INTERNAL(MACRO) BOOST_CRAY_##MACRO + +#if BOOST_CRAY_x == BOOST_CRAY_APPEND(_RELEASE_PATCHLEVEL) + + // This is a developer build. + // + // - _RELEASE_PATCHLEVEL is defined as x, and x is not defined as a macro. + + // Pretend _RELEASE_PATCHLEVEL is 99, so we get the configuration for the + // most recent patch level in this release. + + #define BOOST_CRAY_VERSION (_RELEASE_MAJOR * 10000 + _RELEASE_MINOR * 100 + 99) + +#else + + // This is a production build. + // + // _RELEASE_PATCHLEVEL is not defined as x, or x is defined as a macro. + + #define BOOST_CRAY_VERSION (_RELEASE_MAJOR * 10000 + _RELEASE_MINOR * 100 + _RELEASE_PATCHLEVEL) + +#endif // BOOST_CRAY_x == BOOST_CRAY_APPEND(_RELEASE_PATCHLEVEL) + +#undef BOOST_CRAY_APPEND_INTERNAL +#undef BOOST_CRAY_APPEND +#undef BOOST_CRAY_x + + +#ifdef __GNUC__ +# define BOOST_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +#ifndef BOOST_COMPILER +# define BOOST_COMPILER "Cray C++ version " BOOST_STRINGIZE(_RELEASE_MAJOR) "." BOOST_STRINGIZE(_RELEASE_MINOR) "." BOOST_STRINGIZE(_RELEASE_PATCHLEVEL) +#endif + +// Since the Cray compiler defines '__GNUC__', we have to emulate some +// additional GCC macros in order to make everything work. +// +// FIXME: Perhaps Cray should fix the compiler to define these additional +// macros for GCC emulation? + +#if __cplusplus >= 201103L && defined(__GNUC__) && !defined(__GXX_EXPERIMENTAL_CXX0X__) +# define __GXX_EXPERIMENTAL_CXX0X__ 1 +#endif + +//// +//// Parameter validation +//// + +// FIXME: Do we really need to support compilers before 8.5? Do they pass +// the Boost.Config tests? + +#if BOOST_CRAY_VERSION < 80000 +# error "Boost is not configured for Cray compilers prior to version 8, please try the configure script." +#endif + +// We only support recent EDG based compilers. + +#ifndef __EDG__ +# error "Unsupported Cray compiler, please try running the configure script." +#endif + +//// +//// Baseline values +//// + +#include + +#define BOOST_HAS_NRVO +#define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CHAR16_T +#define BOOST_NO_CXX11_CHAR32_T +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DECLTYPE +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_VARIADIC_MACROS +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_UNRESTRICTED_UNION +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_TWO_PHASE_NAME_LOOKUP + +//#define BOOST_BCB_PARTIAL_SPECIALIZATION_BUG +#define BOOST_MATH_DISABLE_STD_FPCLASSIFY +//#define BOOST_HAS_FPCLASSIFY + +#define BOOST_SP_USE_PTHREADS +#define BOOST_AC_USE_PTHREADS + +// +// Everything that follows is working around what are thought to be +// compiler shortcomings. Revist all of these regularly. +// + +//#define BOOST_USE_ENUM_STATIC_ASSERT +//#define BOOST_BUGGY_INTEGRAL_CONSTANT_EXPRESSIONS //(this may be implied by the previous #define + +// These constants should be provided by the compiler. + +#ifndef __ATOMIC_RELAXED +#define __ATOMIC_RELAXED 0 +#define __ATOMIC_CONSUME 1 +#define __ATOMIC_ACQUIRE 2 +#define __ATOMIC_RELEASE 3 +#define __ATOMIC_ACQ_REL 4 +#define __ATOMIC_SEQ_CST 5 +#endif + +//// +//// Version changes +//// + +// +// 8.5.0 +// + +#if BOOST_CRAY_VERSION >= 80500 + +#if __cplusplus >= 201103L + +#undef BOOST_HAS_NRVO +#undef BOOST_NO_COMPLETE_VALUE_INITIALIZATION +#undef BOOST_NO_CXX11_AUTO_DECLARATIONS +#undef BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#undef BOOST_NO_CXX11_CHAR16_T +#undef BOOST_NO_CXX11_CHAR32_T +#undef BOOST_NO_CXX11_CONSTEXPR +#undef BOOST_NO_CXX11_DECLTYPE +#undef BOOST_NO_CXX11_DECLTYPE_N3276 +#undef BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#undef BOOST_NO_CXX11_DELETED_FUNCTIONS +#undef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#undef BOOST_NO_CXX11_FINAL +#undef BOOST_NO_CXX11_OVERRIDE +#undef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#undef BOOST_NO_CXX11_LAMBDAS +#undef BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#undef BOOST_NO_CXX11_NOEXCEPT +#undef BOOST_NO_CXX11_NULLPTR +#undef BOOST_NO_CXX11_RANGE_BASED_FOR +#undef BOOST_NO_CXX11_RAW_LITERALS +#undef BOOST_NO_CXX11_REF_QUALIFIERS +#undef BOOST_NO_CXX11_RVALUE_REFERENCES +#undef BOOST_NO_CXX11_SCOPED_ENUMS +#undef BOOST_NO_CXX11_SFINAE_EXPR +#undef BOOST_NO_CXX11_STATIC_ASSERT +#undef BOOST_NO_CXX11_TEMPLATE_ALIASES +#undef BOOST_NO_CXX11_THREAD_LOCAL +#undef BOOST_NO_CXX11_UNICODE_LITERALS +#undef BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#undef BOOST_NO_CXX11_USER_DEFINED_LITERALS +#undef BOOST_NO_CXX11_VARIADIC_MACROS +#undef BOOST_NO_CXX11_VARIADIC_TEMPLATES +#undef BOOST_NO_CXX11_UNRESTRICTED_UNION +#undef BOOST_NO_SFINAE_EXPR +#undef BOOST_NO_TWO_PHASE_NAME_LOOKUP +#undef BOOST_MATH_DISABLE_STD_FPCLASSIFY +#undef BOOST_SP_USE_PTHREADS +#undef BOOST_AC_USE_PTHREADS + +#define BOOST_HAS_VARIADIC_TMPL +#define BOOST_HAS_UNISTD_H +#define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG +#define BOOST_HAS_TR1_COMPLEX_OVERLOADS +#define BOOST_HAS_STDINT_H +#define BOOST_HAS_STATIC_ASSERT +#define BOOST_HAS_SIGACTION +#define BOOST_HAS_SCHED_YIELD +#define BOOST_HAS_RVALUE_REFS +#define BOOST_HAS_PTHREADS +#define BOOST_HAS_PTHREAD_YIELD +#define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +#define BOOST_HAS_PARTIAL_STD_ALLOCATOR +#define BOOST_HAS_NRVO +#define BOOST_HAS_NL_TYPES_H +#define BOOST_HAS_NANOSLEEP +#define BOOST_NO_CXX11_SMART_PTR +#define BOOST_NO_CXX11_HDR_FUNCTIONAL +#define BOOST_NO_CXX14_CONSTEXPR +#define BOOST_HAS_LONG_LONG +#define BOOST_HAS_FLOAT128 + +#if __cplusplus < 201402L +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#endif // __cplusplus < 201402L + +#endif // __cplusplus >= 201103L + +#endif // BOOST_CRAY_VERSION >= 80500 + +// +// 8.6.4 +// (versions prior to 8.6.5 do not define _RELEASE_PATCHLEVEL) +// + +#if BOOST_CRAY_VERSION >= 80600 + +#if __cplusplus >= 199711L +#define BOOST_HAS_FLOAT128 +#define BOOST_HAS_PTHREAD_YIELD // This is a platform macro, but it improves test results. +#define BOOST_NO_COMPLETE_VALUE_INITIALIZATION // This is correct. Test compiles, but fails to run. +#undef BOOST_NO_CXX11_CHAR16_T +#undef BOOST_NO_CXX11_CHAR32_T +#undef BOOST_NO_CXX11_INLINE_NAMESPACES +#undef BOOST_NO_CXX11_FINAL +#undef BOOST_NO_CXX11_OVERRIDE +#undef BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS +#undef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_SFINAE_EXPR // This is correct, even though '*_fail.cpp' test fails. +#undef BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#undef BOOST_NO_CXX11_VARIADIC_MACROS +#undef BOOST_NO_CXX11_VARIADIC_TEMPLATES +// 'BOOST_NO_DEDUCED_TYPENAME' test is broken. The test files are enabled / +// disabled with an '#ifdef BOOST_DEDUCED_TYPENAME'. However, +// 'boost/libs/config/include/boost/config/detail/suffix.hpp' ensures that +// 'BOOST_DEDUCED_TYPENAME' is always defined (the value it is defined as +// depends on 'BOOST_NO_DEDUCED_TYPENAME'). So, modifying +// 'BOOST_NO_DEDUCED_TYPENAME' has no effect on which tests are run. +// +// The 'no_ded_typename_pass.cpp' test should always compile and run +// successfully, because 'BOOST_DEDUCED_TYPENAME' must always have an +// appropriate value (it's not just something that you turn on or off). +// Therefore, if you wish to test changes to 'BOOST_NO_DEDUCED_TYPENAME', +// you have to modify 'no_ded_typename_pass.cpp' to unconditionally include +// 'boost_no_ded_typename.ipp'. +#undef BOOST_NO_DEDUCED_TYPENAME // This is correct. Test is broken. +#undef BOOST_NO_SFINAE_EXPR +#undef BOOST_NO_TWO_PHASE_NAME_LOOKUP +#endif // __cplusplus >= 199711L + +#if __cplusplus >= 201103L +#undef BOOST_NO_CXX11_ALIGNAS +#undef BOOST_NO_CXX11_ALIGNOF +#undef BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_HDR_ATOMIC +#undef BOOST_NO_CXX11_HDR_FUNCTIONAL +#define BOOST_NO_CXX11_HDR_REGEX // This is correct. Test compiles, but fails to run. +#undef BOOST_NO_CXX11_SFINAE_EXPR +#undef BOOST_NO_CXX11_SMART_PTR +#undef BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201402L +#undef BOOST_NO_CXX14_CONSTEXPR +#define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif // __cplusplus == 201402L + +#endif // BOOST_CRAY_VERSION >= 80600 + +// +// 8.6.5 +// (no change from 8.6.4) +// + +// +// 8.7.0 +// + +#if BOOST_CRAY_VERSION >= 80700 + +#if __cplusplus >= 199711L +#endif // __cplusplus >= 199711L + +#if __cplusplus >= 201103L +#undef BOOST_NO_CXX11_HDR_ATOMIC +#undef BOOST_NO_CXX11_HDR_REGEX +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201402L +#endif // __cplusplus == 201402L + +#endif // BOOST_CRAY_VERSION >= 80700 + +// +// Next release +// + +#if BOOST_CRAY_VERSION > 80799 + +#if __cplusplus >= 199711L +#endif // __cplusplus >= 199711L + +#if __cplusplus >= 201103L +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201402L +#endif // __cplusplus == 201402L + +#endif // BOOST_CRAY_VERSION > 80799 + +//// +//// Remove temporary macros +//// + +// I've commented out some '#undef' statements to signify that we purposely +// want to keep certain macros. + +//#undef __GXX_EXPERIMENTAL_CXX0X__ +//#undef BOOST_COMPILER +#undef BOOST_GCC_VERSION +#undef BOOST_CRAY_VERSION diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/diab.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/diab.hpp new file mode 100644 index 0000000..943db83 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/diab.hpp @@ -0,0 +1,26 @@ +// (C) Copyright Brian Kuhl 2016. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Check this is a recent EDG based compiler, otherwise we don't support it here: + + +#ifndef __EDG_VERSION__ +# error "Unknown Diab compiler version - please run the configure tests and report the results" +#endif + +#include "boost/config/compiler/common_edg.hpp" + +#define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#define BOOST_BUGGY_INTEGRAL_CONSTANT_EXPRESSIONS + +#define BOOST_MPL_CFG_NO_HAS_XXX_TEMPLATE +#define BOOST_LOG_NO_MEMBER_TEMPLATE_FRIENDS +#define BOOST_REGEX_NO_EXTERNAL_TEMPLATES + +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_HDR_CODECVT +#define BOOST_NO_CXX11_NUMERIC_LIMITS + +#define BOOST_COMPILER "Wind River Diab " BOOST_STRINGIZE(__VERSION_NUMBER__) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/digitalmars.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/digitalmars.hpp new file mode 100644 index 0000000..bb56ff6 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/digitalmars.hpp @@ -0,0 +1,143 @@ +// Copyright (C) Christof Meerwald 2003 +// Copyright (C) Dan Watkins 2003 +// +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Digital Mars C++ compiler setup: +#define BOOST_COMPILER __DMC_VERSION_STRING__ + +#define BOOST_HAS_LONG_LONG +#define BOOST_HAS_PRAGMA_ONCE + +#if !defined(BOOST_STRICT_CONFIG) +#define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +#define BOOST_NO_OPERATORS_IN_NAMESPACE +#define BOOST_NO_UNREACHABLE_RETURN_DETECTION +#define BOOST_NO_SFINAE +#define BOOST_NO_USING_TEMPLATE +#define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +#endif + +// +// has macros: +#define BOOST_HAS_DIRENT_H +#define BOOST_HAS_STDINT_H +#define BOOST_HAS_WINTHREADS + +#if (__DMC__ >= 0x847) +#define BOOST_HAS_EXPM1 +#define BOOST_HAS_LOG1P +#endif + +// +// Is this really the best way to detect whether the std lib is in namespace std? +// +#ifdef __cplusplus +#include +#endif +#if !defined(__STL_IMPORT_VENDOR_CSTD) && !defined(_STLP_IMPORT_VENDOR_CSTD) +# define BOOST_NO_STDC_NAMESPACE +#endif + + +// check for exception handling support: +#if !defined(_CPPUNWIND) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + +// +// C++0x features +// +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CHAR16_T +#define BOOST_NO_CXX11_CHAR32_T +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DECLTYPE +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#define BOOST_NO_CXX11_EXTERN_TEMPLATE +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +#if (__DMC__ <= 0x840) +#error "Compiler not supported or configured - please reconfigure" +#endif +// +// last known and checked version is ...: +#if (__DMC__ > 0x848) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc.hpp new file mode 100644 index 0000000..2f1fe55 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc.hpp @@ -0,0 +1,383 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Darin Adler 2001 - 2002. +// (C) Copyright Jens Maurer 2001 - 2002. +// (C) Copyright Beman Dawes 2001 - 2003. +// (C) Copyright Douglas Gregor 2002. +// (C) Copyright David Abrahams 2002 - 2003. +// (C) Copyright Synge Todo 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// GNU C++ compiler setup. + +// +// Define BOOST_GCC so we know this is "real" GCC and not some pretender: +// +#define BOOST_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if !defined(__CUDACC__) +#define BOOST_GCC BOOST_GCC_VERSION +#endif + +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +# define BOOST_GCC_CXX11 +#endif + +#if __GNUC__ == 3 +# if defined (__PATHSCALE__) +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +# define BOOST_NO_IS_ABSTRACT +# endif + +# if __GNUC_MINOR__ < 4 +# define BOOST_NO_IS_ABSTRACT +# endif +# define BOOST_NO_CXX11_EXTERN_TEMPLATE +#endif +#if __GNUC__ < 4 +// +// All problems to gcc-3.x and earlier here: +// +#define BOOST_NO_TWO_PHASE_NAME_LOOKUP +# ifdef __OPEN64__ +# define BOOST_NO_IS_ABSTRACT +# endif +#endif + +// GCC prior to 3.4 had #pragma once too but it didn't work well with filesystem links +#if BOOST_GCC_VERSION >= 30400 +#define BOOST_HAS_PRAGMA_ONCE +#endif + +#if BOOST_GCC_VERSION < 40400 +// Previous versions of GCC did not completely implement value-initialization: +// GCC Bug 30111, "Value-initialization of POD base class doesn't initialize +// members", reported by Jonathan Wakely in 2006, +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30111 (fixed for GCC 4.4) +// GCC Bug 33916, "Default constructor fails to initialize array members", +// reported by Michael Elizabeth Chastain in 2007, +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33916 (fixed for GCC 4.2.4) +// See also: http://www.boost.org/libs/utility/value_init.htm#compiler_issues +#define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +#endif + +#if !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + + +// +// Threading support: Turn this on unconditionally here (except for +// those platforms where we can know for sure). It will get turned off again +// later if no threading API is detected. +// +#if !defined(__MINGW32__) && !defined(linux) && !defined(__linux) && !defined(__linux__) +# define BOOST_HAS_THREADS +#endif + +// +// gcc has "long long" +// Except on Darwin with standard compliance enabled (-pedantic) +// Apple gcc helpfully defines this macro we can query +// +#if !defined(__DARWIN_NO_LONG_LONG) +# define BOOST_HAS_LONG_LONG +#endif + +// +// gcc implements the named return value optimization since version 3.1 +// +#define BOOST_HAS_NRVO + +// Branch prediction hints +#define BOOST_LIKELY(x) __builtin_expect(x, 1) +#define BOOST_UNLIKELY(x) __builtin_expect(x, 0) + +// +// Dynamic shared object (DSO) and dynamic-link library (DLL) support +// +#if __GNUC__ >= 4 +# if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) + // All Win32 development environments, including 64-bit Windows and MinGW, define + // _WIN32 or one of its variant spellings. Note that Cygwin is a POSIX environment, + // so does not define _WIN32 or its variants, but still supports dllexport/dllimport. +# define BOOST_HAS_DECLSPEC +# define BOOST_SYMBOL_EXPORT __attribute__((__dllexport__)) +# define BOOST_SYMBOL_IMPORT __attribute__((__dllimport__)) +# else +# define BOOST_SYMBOL_EXPORT __attribute__((__visibility__("default"))) +# define BOOST_SYMBOL_IMPORT +# endif +# define BOOST_SYMBOL_VISIBLE __attribute__((__visibility__("default"))) +#else +// config/platform/win32.hpp will define BOOST_SYMBOL_EXPORT, etc., unless already defined +# define BOOST_SYMBOL_EXPORT +#endif + +// +// RTTI and typeinfo detection is possible post gcc-4.3: +// +#if BOOST_GCC_VERSION > 40300 +# ifndef __GXX_RTTI +# ifndef BOOST_NO_TYPEID +# define BOOST_NO_TYPEID +# endif +# ifndef BOOST_NO_RTTI +# define BOOST_NO_RTTI +# endif +# endif +#endif + +// +// Recent GCC versions have __int128 when in 64-bit mode. +// +// We disable this if the compiler is really nvcc with C++03 as it +// doesn't actually support __int128 as of CUDA_VERSION=7500 +// even though it defines __SIZEOF_INT128__. +// See https://svn.boost.org/trac/boost/ticket/8048 +// https://svn.boost.org/trac/boost/ticket/11852 +// Only re-enable this for nvcc if you're absolutely sure +// of the circumstances under which it's supported: +// +#if defined(__CUDACC__) +# if defined(BOOST_GCC_CXX11) +# define BOOST_NVCC_CXX11 +# else +# define BOOST_NVCC_CXX03 +# endif +#endif + +#if defined(__SIZEOF_INT128__) && !defined(BOOST_NVCC_CXX03) +# define BOOST_HAS_INT128 +#endif +// +// Recent GCC versions have a __float128 native type, we need to +// include a std lib header to detect this - not ideal, but we'll +// be including later anyway when we select the std lib. +// +// Nevertheless, as of CUDA 7.5, using __float128 with the host +// compiler in pre-C++11 mode is still not supported. +// See https://svn.boost.org/trac/boost/ticket/11852 +// +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) && !defined(BOOST_NVCC_CXX03) +# define BOOST_HAS_FLOAT128 +#endif + +// C++0x features in 4.3.n and later +// +#if (BOOST_GCC_VERSION >= 40300) && defined(BOOST_GCC_CXX11) +// C++0x features are only enabled when -std=c++0x or -std=gnu++0x are +// passed on the command line, which in turn defines +// __GXX_EXPERIMENTAL_CXX0X__. +# define BOOST_HAS_DECLTYPE +# define BOOST_HAS_RVALUE_REFS +# define BOOST_HAS_STATIC_ASSERT +# define BOOST_HAS_VARIADIC_TMPL +#else +# define BOOST_NO_CXX11_DECLTYPE +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +# define BOOST_NO_CXX11_RVALUE_REFERENCES +# define BOOST_NO_CXX11_STATIC_ASSERT +#endif + +// C++0x features in 4.4.n and later +// +#if (BOOST_GCC_VERSION < 40400) || !defined(BOOST_GCC_CXX11) +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +# define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +# define BOOST_NO_CXX11_INLINE_NAMESPACES +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif + +#if BOOST_GCC_VERSION < 40500 +# define BOOST_NO_SFINAE_EXPR +#endif + +// GCC 4.5 forbids declaration of defaulted functions in private or protected sections +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 5) || !defined(BOOST_GCC_CXX11) +# define BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS +#endif + +// C++0x features in 4.5.0 and later +// +#if (BOOST_GCC_VERSION < 40500) || !defined(BOOST_GCC_CXX11) +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +# define BOOST_NO_CXX11_LAMBDAS +# define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +# define BOOST_NO_CXX11_RAW_LITERALS +# define BOOST_NO_CXX11_UNICODE_LITERALS +# define BOOST_NO_CXX11_ALIGNOF +#endif + +// C++0x features in 4.5.1 and later +// +#if (BOOST_GCC_VERSION < 40501) || !defined(BOOST_GCC_CXX11) +// scoped enums have a serious bug in 4.4.0, so define BOOST_NO_CXX11_SCOPED_ENUMS before 4.5.1 +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38064 +# define BOOST_NO_CXX11_SCOPED_ENUMS +#endif + +// C++0x features in 4.6.n and later +// +#if (BOOST_GCC_VERSION < 40600) || !defined(BOOST_GCC_CXX11) +#define BOOST_NO_CXX11_DEFAULTED_MOVES +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#endif + +// C++0x features in 4.7.n and later +// +#if (BOOST_GCC_VERSION < 40700) || !defined(BOOST_GCC_CXX11) +// Note that while constexpr is partly supported in gcc-4.6 it's a +// pre-std version with several bugs: +# define BOOST_NO_CXX11_CONSTEXPR +# define BOOST_NO_CXX11_FINAL +# define BOOST_NO_CXX11_TEMPLATE_ALIASES +# define BOOST_NO_CXX11_USER_DEFINED_LITERALS +# define BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS +# define BOOST_NO_CXX11_OVERRIDE +#endif + +// C++0x features in 4.8.n and later +// +#if (BOOST_GCC_VERSION < 40800) || !defined(BOOST_GCC_CXX11) +# define BOOST_NO_CXX11_THREAD_LOCAL +# define BOOST_NO_CXX11_SFINAE_EXPR +#endif + +// C++0x features in 4.8.1 and later +// +#if (BOOST_GCC_VERSION < 40801) || !defined(BOOST_GCC_CXX11) +# define BOOST_NO_CXX11_DECLTYPE_N3276 +# define BOOST_NO_CXX11_REF_QUALIFIERS +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif + +// C++0x features in 4.9.n and later +// +#if (BOOST_GCC_VERSION < 40900) || !defined(BOOST_GCC_CXX11) +// Although alignas support is added in gcc 4.8, it does not accept +// dependent constant expressions as an argument until gcc 4.9. +# define BOOST_NO_CXX11_ALIGNAS +#endif + +// C++0x features in 5.1 and later +// +#if (BOOST_GCC_VERSION < 50100) || !defined(BOOST_GCC_CXX11) +# define BOOST_NO_CXX11_UNRESTRICTED_UNION +#endif + +// C++14 features in 4.9.0 and later +// +#if (BOOST_GCC_VERSION < 40900) || (__cplusplus < 201300) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +# define BOOST_NO_CXX14_DECLTYPE_AUTO +# if !((BOOST_GCC_VERSION >= 40801) && (BOOST_GCC_VERSION < 40900) && defined(BOOST_GCC_CXX11)) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +# endif +#endif + + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if (BOOST_GCC_VERSION < 50200) || !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +#if __GNUC__ >= 7 +# define BOOST_FALLTHROUGH __attribute__((fallthrough)) +#endif + +#if (__GNUC__ < 11) && defined(__MINGW32__) && !defined(__MINGW64__) +// thread_local was broken on mingw for all 32bit compiler releases prior to 11.x, see +// https://sourceforge.net/p/mingw-w64/bugs/527/ +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83562 +// Not setting this causes program termination on thread exit. +#define BOOST_NO_CXX11_THREAD_LOCAL +#endif + +// +// Unused attribute: +#if __GNUC__ >= 4 +# define BOOST_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#endif + +// Type aliasing hint. Supported since gcc 3.3. +#define BOOST_MAY_ALIAS __attribute__((__may_alias__)) + +// Unreachable code markup +#if BOOST_GCC_VERSION >= 40500 +#define BOOST_UNREACHABLE_RETURN(x) __builtin_unreachable(); +#endif + +// Deprecated symbol markup +#if BOOST_GCC_VERSION >= 40500 +#define BOOST_DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define BOOST_DEPRECATED(msg) __attribute__((deprecated)) +#endif + +#ifndef BOOST_COMPILER +# define BOOST_COMPILER "GNU C++ version " __VERSION__ +#endif + +// ConceptGCC compiler: +// http://www.generic-programming.org/software/ConceptGCC/ +#ifdef __GXX_CONCEPTS__ +# define BOOST_HAS_CONCEPTS +# define BOOST_COMPILER "ConceptGCC version " __VERSION__ +#endif + +// versions check: +// we don't know gcc prior to version 3.30: +#if (BOOST_GCC_VERSION < 30300) +# error "Compiler not configured - please reconfigure" +#endif +// +// last known and checked version is 8.1: +#if (BOOST_GCC_VERSION > 80100) +# if defined(BOOST_ASSERT_CONFIG) +# error "Boost.Config is older than your compiler - please check for an updated Boost release." +# else +// we don't emit warnings here anymore since there are no defect macros defined for +// gcc post 3.4, so any failures are gcc regressions... +//# warning "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc_xml.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc_xml.hpp new file mode 100644 index 0000000..75cac44 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/gcc_xml.hpp @@ -0,0 +1,114 @@ +// (C) Copyright John Maddock 2006. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// GCC-XML C++ compiler setup: + +# if !defined(__GCCXML_GNUC__) || ((__GCCXML_GNUC__ <= 3) && (__GCCXML_GNUC_MINOR__ <= 3)) +# define BOOST_NO_IS_ABSTRACT +# endif + +// +// Threading support: Turn this on unconditionally here (except for +// those platforms where we can know for sure). It will get turned off again +// later if no threading API is detected. +// +#if !defined(__MINGW32__) && !defined(_MSC_VER) && !defined(linux) && !defined(__linux) && !defined(__linux__) +# define BOOST_HAS_THREADS +#endif + +// +// gcc has "long long" +// +#define BOOST_HAS_LONG_LONG + +// C++0x features: +// +# define BOOST_NO_CXX11_CONSTEXPR +# define BOOST_NO_CXX11_NULLPTR +# define BOOST_NO_CXX11_TEMPLATE_ALIASES +# define BOOST_NO_CXX11_DECLTYPE +# define BOOST_NO_CXX11_DECLTYPE_N3276 +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +# define BOOST_NO_CXX11_RVALUE_REFERENCES +# define BOOST_NO_CXX11_STATIC_ASSERT +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +# define BOOST_NO_CXX11_VARIADIC_MACROS +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_SCOPED_ENUMS +# define BOOST_NO_SFINAE_EXPR +# define BOOST_NO_CXX11_SFINAE_EXPR +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +# define BOOST_NO_CXX11_LAMBDAS +# define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +# define BOOST_NO_CXX11_RANGE_BASED_FOR +# define BOOST_NO_CXX11_RAW_LITERALS +# define BOOST_NO_CXX11_UNICODE_LITERALS +# define BOOST_NO_CXX11_NOEXCEPT +# define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +# define BOOST_NO_CXX11_USER_DEFINED_LITERALS +# define BOOST_NO_CXX11_ALIGNAS +# define BOOST_NO_CXX11_ALIGNOF +# define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +# define BOOST_NO_CXX11_INLINE_NAMESPACES +# define BOOST_NO_CXX11_REF_QUALIFIERS +# define BOOST_NO_CXX11_FINAL +# define BOOST_NO_CXX11_OVERRIDE +# define BOOST_NO_CXX11_THREAD_LOCAL +# define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +#define BOOST_COMPILER "GCC-XML C++ version " __GCCXML__ + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/greenhills.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/greenhills.hpp new file mode 100644 index 0000000..39112c2 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/greenhills.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Greenhills C++ compiler setup: + +#define BOOST_COMPILER "Greenhills C++ version " BOOST_STRINGIZE(__ghs) + +#include + +// +// versions check: +// we don't support Greenhills prior to version 0: +#if __ghs < 0 +# error "Compiler not supported or configured - please reconfigure" +#endif +// +// last known and checked version is 0: +#if (__ghs > 0) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/hp_acc.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/hp_acc.hpp new file mode 100644 index 0000000..2563632 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/hp_acc.hpp @@ -0,0 +1,149 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001 - 2003. +// (C) Copyright Aleksey Gurtovoy 2002. +// (C) Copyright David Abrahams 2002 - 2003. +// (C) Copyright Toon Knapen 2003. +// (C) Copyright Boris Gubenko 2006 - 2007. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// HP aCC C++ compiler setup: + +#if defined(__EDG__) +#include +#endif + +#if (__HP_aCC <= 33100) +# define BOOST_NO_INTEGRAL_INT64_T +# define BOOST_NO_OPERATORS_IN_NAMESPACE +# if !defined(_NAMESPACE_STD) +# define BOOST_NO_STD_LOCALE +# define BOOST_NO_STRINGSTREAM +# endif +#endif + +#if (__HP_aCC <= 33300) +// member templates are sufficiently broken that we disable them for now +# define BOOST_NO_MEMBER_TEMPLATES +# define BOOST_NO_DEPENDENT_NESTED_DERIVATIONS +# define BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE +#endif + +#if (__HP_aCC <= 38000) +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#endif + +#if (__HP_aCC > 50000) && (__HP_aCC < 60000) +# define BOOST_NO_UNREACHABLE_RETURN_DETECTION +# define BOOST_NO_TEMPLATE_TEMPLATES +# define BOOST_NO_SWPRINTF +# define BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS +# define BOOST_NO_IS_ABSTRACT +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +#endif + +// optional features rather than defects: +#if (__HP_aCC >= 33900) +# define BOOST_HAS_LONG_LONG +# define BOOST_HAS_PARTIAL_STD_ALLOCATOR +#endif + +#if (__HP_aCC >= 50000 ) && (__HP_aCC <= 53800 ) || (__HP_aCC < 31300 ) +# define BOOST_NO_MEMBER_TEMPLATE_KEYWORD +#endif + +// This macro should not be defined when compiling in strict ansi +// mode, but, currently, we don't have the ability to determine +// what standard mode we are compiling with. Some future version +// of aCC6 compiler will provide predefined macros reflecting the +// compilation options, including the standard mode. +#if (__HP_aCC >= 60000) || ((__HP_aCC > 38000) && defined(__hpxstd98)) +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#endif + +#define BOOST_COMPILER "HP aCC version " BOOST_STRINGIZE(__HP_aCC) + +// +// versions check: +// we don't support HP aCC prior to version 33000: +#if __HP_aCC < 33000 +# error "Compiler not supported or configured - please reconfigure" +#endif + +// +// Extended checks for supporting aCC on PA-RISC +#if __HP_aCC > 30000 && __HP_aCC < 50000 +# if __HP_aCC < 38000 + // versions prior to version A.03.80 not supported +# error "Compiler version not supported - version A.03.80 or higher is required" +# elif !defined(__hpxstd98) + // must compile using the option +hpxstd98 with version A.03.80 and above +# error "Compiler option '+hpxstd98' is required for proper support" +# endif //PA-RISC +#endif + +// +// C++0x features +// +// See boost\config\suffix.hpp for BOOST_NO_LONG_LONG +// +#if !defined(__EDG__) + +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CHAR16_T +#define BOOST_NO_CXX11_CHAR32_T +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DECLTYPE +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#define BOOST_NO_CXX11_EXTERN_TEMPLATE +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +/* + See https://forums13.itrc.hp.com/service/forums/questionanswer.do?threadId=1443331 and + https://forums13.itrc.hp.com/service/forums/questionanswer.do?threadId=1443436 +*/ + +#if (__HP_aCC < 62500) || !defined(HP_CXX0x_SOURCE) + #define BOOST_NO_CXX11_VARIADIC_MACROS +#endif + +#endif + +// +// last known and checked version for HP-UX/ia64 is 61300 +// last known and checked version for PA-RISC is 38000 +#if ((__HP_aCC > 61300) || ((__HP_aCC > 38000) && defined(__hpxstd98))) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/intel.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/intel.hpp new file mode 100644 index 0000000..6a34397 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/intel.hpp @@ -0,0 +1,577 @@ +// (C) Copyright John Maddock 2001-8. +// (C) Copyright Peter Dimov 2001. +// (C) Copyright Jens Maurer 2001. +// (C) Copyright David Abrahams 2002 - 2003. +// (C) Copyright Aleksey Gurtovoy 2002 - 2003. +// (C) Copyright Guillaume Melquiond 2002 - 2003. +// (C) Copyright Beman Dawes 2003. +// (C) Copyright Martin Wille 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Intel compiler setup: + +#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500) && (defined(_MSC_VER) || defined(__GNUC__)) + +#ifdef _MSC_VER + +#include + +#undef BOOST_MSVC +#undef BOOST_MSVC_FULL_VER + +#if (__INTEL_COMPILER >= 1500) && (_MSC_VER >= 1900) +// +// These appear to be supported, even though VC++ may not support them: +// +#define BOOST_HAS_EXPM1 +#define BOOST_HAS_LOG1P +#undef BOOST_NO_CXX14_BINARY_LITERALS +// This one may be a little risky to enable?? +#undef BOOST_NO_SFINAE_EXPR + +#endif + +#if (__INTEL_COMPILER <= 1600) && !defined(BOOST_NO_CXX14_VARIABLE_TEMPLATES) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +#else // defined(_MSC_VER) + +#include + +#undef BOOST_GCC_VERSION +#undef BOOST_GCC_CXX11 +#undef BOOST_GCC +#undef BOOST_FALLTHROUGH + +// Broken in all versions up to 17 (newer versions not tested) +#if (__INTEL_COMPILER <= 1700) && !defined(BOOST_NO_CXX14_CONSTEXPR) +# define BOOST_NO_CXX14_CONSTEXPR +#endif + +#if (__INTEL_COMPILER >= 1800) && (__cplusplus >= 201703) +# define BOOST_FALLTHROUGH [[fallthrough]] +#endif + +#endif // defined(_MSC_VER) + +#undef BOOST_COMPILER + +#if defined(__INTEL_COMPILER) +#if __INTEL_COMPILER == 9999 +# define BOOST_INTEL_CXX_VERSION 1200 // Intel bug in 12.1. +#else +# define BOOST_INTEL_CXX_VERSION __INTEL_COMPILER +#endif +#elif defined(__ICL) +# define BOOST_INTEL_CXX_VERSION __ICL +#elif defined(__ICC) +# define BOOST_INTEL_CXX_VERSION __ICC +#elif defined(__ECC) +# define BOOST_INTEL_CXX_VERSION __ECC +#endif + +// Flags determined by comparing output of 'icpc -dM -E' with and without '-std=c++0x' +#if (!(defined(_WIN32) || defined(_WIN64)) && defined(__STDC_HOSTED__) && (__STDC_HOSTED__ && (BOOST_INTEL_CXX_VERSION <= 1200))) || defined(__GXX_EXPERIMENTAL_CPP0X__) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_INTEL_STDCXX0X +#endif +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +# define BOOST_INTEL_STDCXX0X +#endif + +#ifdef __GNUC__ +# define BOOST_INTEL_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +#if !defined(BOOST_COMPILER) +# if defined(BOOST_INTEL_STDCXX0X) +# define BOOST_COMPILER "Intel C++ C++0x mode version " BOOST_STRINGIZE(BOOST_INTEL_CXX_VERSION) +# else +# define BOOST_COMPILER "Intel C++ version " BOOST_STRINGIZE(BOOST_INTEL_CXX_VERSION) +# endif +#endif + +#define BOOST_INTEL BOOST_INTEL_CXX_VERSION + +#if defined(_WIN32) || defined(_WIN64) +# define BOOST_INTEL_WIN BOOST_INTEL +#else +# define BOOST_INTEL_LINUX BOOST_INTEL +#endif + +#else // defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500) && (defined(_MSC_VER) || defined(__GNUC__)) + +#include + +#if defined(__INTEL_COMPILER) +#if __INTEL_COMPILER == 9999 +# define BOOST_INTEL_CXX_VERSION 1200 // Intel bug in 12.1. +#else +# define BOOST_INTEL_CXX_VERSION __INTEL_COMPILER +#endif +#elif defined(__ICL) +# define BOOST_INTEL_CXX_VERSION __ICL +#elif defined(__ICC) +# define BOOST_INTEL_CXX_VERSION __ICC +#elif defined(__ECC) +# define BOOST_INTEL_CXX_VERSION __ECC +#endif + +// Flags determined by comparing output of 'icpc -dM -E' with and without '-std=c++0x' +#if (!(defined(_WIN32) || defined(_WIN64)) && defined(__STDC_HOSTED__) && (__STDC_HOSTED__ && (BOOST_INTEL_CXX_VERSION <= 1200))) || defined(__GXX_EXPERIMENTAL_CPP0X__) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_INTEL_STDCXX0X +#endif +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +# define BOOST_INTEL_STDCXX0X +#endif + +#ifdef __GNUC__ +# define BOOST_INTEL_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +#if !defined(BOOST_COMPILER) +# if defined(BOOST_INTEL_STDCXX0X) +# define BOOST_COMPILER "Intel C++ C++0x mode version " BOOST_STRINGIZE(BOOST_INTEL_CXX_VERSION) +# else +# define BOOST_COMPILER "Intel C++ version " BOOST_STRINGIZE(BOOST_INTEL_CXX_VERSION) +# endif +#endif + +#define BOOST_INTEL BOOST_INTEL_CXX_VERSION + +#if defined(_WIN32) || defined(_WIN64) +# define BOOST_INTEL_WIN BOOST_INTEL +#else +# define BOOST_INTEL_LINUX BOOST_INTEL +#endif + +#if (BOOST_INTEL_CXX_VERSION <= 600) + +# if defined(_MSC_VER) && (_MSC_VER <= 1300) // added check for <= VC 7 (Peter Dimov) + +// Boost libraries assume strong standard conformance unless otherwise +// indicated by a config macro. As configured by Intel, the EDG front-end +// requires certain compiler options be set to achieve that strong conformance. +// Particularly /Qoption,c,--arg_dep_lookup (reported by Kirk Klobe & Thomas Witt) +// and /Zc:wchar_t,forScope. See boost-root/tools/build/intel-win32-tools.jam for +// details as they apply to particular versions of the compiler. When the +// compiler does not predefine a macro indicating if an option has been set, +// this config file simply assumes the option has been set. +// Thus BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP will not be defined, even if +// the compiler option is not enabled. + +# define BOOST_NO_SWPRINTF +# endif + +// Void returns, 64 bit integrals don't work when emulating VC 6 (Peter Dimov) + +# if defined(_MSC_VER) && (_MSC_VER <= 1200) +# define BOOST_NO_VOID_RETURNS +# define BOOST_NO_INTEGRAL_INT64_T +# endif + +#endif + +#if (BOOST_INTEL_CXX_VERSION <= 710) && defined(_WIN32) +# define BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS +#endif + +// See http://aspn.activestate.com/ASPN/Mail/Message/boost/1614864 +#if BOOST_INTEL_CXX_VERSION < 600 +# define BOOST_NO_INTRINSIC_WCHAR_T +#else +// We should test the macro _WCHAR_T_DEFINED to check if the compiler +// supports wchar_t natively. *BUT* there is a problem here: the standard +// headers define this macro if they typedef wchar_t. Anyway, we're lucky +// because they define it without a value, while Intel C++ defines it +// to 1. So we can check its value to see if the macro was defined natively +// or not. +// Under UNIX, the situation is exactly the same, but the macro _WCHAR_T +// is used instead. +# if ((_WCHAR_T_DEFINED + 0) == 0) && ((_WCHAR_T + 0) == 0) +# define BOOST_NO_INTRINSIC_WCHAR_T +# endif +#endif + +#if defined(__GNUC__) && !defined(BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL) +// +// Figure out when Intel is emulating this gcc bug +// (All Intel versions prior to 9.0.26, and versions +// later than that if they are set up to emulate gcc 3.2 +// or earlier): +// +# if ((__GNUC__ == 3) && (__GNUC_MINOR__ <= 2)) || (BOOST_INTEL < 900) || (__INTEL_COMPILER_BUILD_DATE < 20050912) +# define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +# endif +#endif +#if (defined(__GNUC__) && (__GNUC__ < 4)) || (defined(_WIN32) && (BOOST_INTEL_CXX_VERSION <= 1200)) || (BOOST_INTEL_CXX_VERSION <= 1200) +// GCC or VC emulation: +#define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#endif +// +// Verify that we have actually got BOOST_NO_INTRINSIC_WCHAR_T +// set correctly, if we don't do this now, we will get errors later +// in type_traits code among other things, getting this correct +// for the Intel compiler is actually remarkably fragile and tricky: +// +#ifdef __cplusplus +#if defined(BOOST_NO_INTRINSIC_WCHAR_T) +#include +template< typename T > struct assert_no_intrinsic_wchar_t; +template<> struct assert_no_intrinsic_wchar_t { typedef void type; }; +// if you see an error here then you need to unset BOOST_NO_INTRINSIC_WCHAR_T +// where it is defined above: +typedef assert_no_intrinsic_wchar_t::type assert_no_intrinsic_wchar_t_; +#else +template< typename T > struct assert_intrinsic_wchar_t; +template<> struct assert_intrinsic_wchar_t {}; +// if you see an error here then define BOOST_NO_INTRINSIC_WCHAR_T on the command line: +template<> struct assert_intrinsic_wchar_t {}; +#endif +#endif + +#if defined(_MSC_VER) && (_MSC_VER+0 >= 1000) +# if _MSC_VER >= 1200 +# define BOOST_HAS_MS_INT64 +# endif +# define BOOST_NO_SWPRINTF +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#elif defined(_WIN32) +# define BOOST_DISABLE_WIN32 +#endif + +// I checked version 6.0 build 020312Z, it implements the NRVO. +// Correct this as you find out which version of the compiler +// implemented the NRVO first. (Daniel Frey) +#if (BOOST_INTEL_CXX_VERSION >= 600) +# define BOOST_HAS_NRVO +#endif + +// Branch prediction hints +// I'm not sure 8.0 was the first version to support these builtins, +// update the condition if the version is not accurate. (Andrey Semashev) +#if defined(__GNUC__) && BOOST_INTEL_CXX_VERSION >= 800 +#define BOOST_LIKELY(x) __builtin_expect(x, 1) +#define BOOST_UNLIKELY(x) __builtin_expect(x, 0) +#endif + +// RTTI +// __RTTI is the EDG macro +// __INTEL_RTTI__ is the Intel macro +// __GXX_RTTI is the g++ macro +// _CPPRTTI is the MSVC++ macro +#if !defined(__RTTI) && !defined(__INTEL_RTTI__) && !defined(__GXX_RTTI) && !defined(_CPPRTTI) + +#if !defined(BOOST_NO_RTTI) +# define BOOST_NO_RTTI +#endif + +// in MS mode, static typeid works even when RTTI is off +#if !defined(_MSC_VER) && !defined(BOOST_NO_TYPEID) +# define BOOST_NO_TYPEID +#endif + +#endif + +// +// versions check: +// we don't support Intel prior to version 6.0: +#if BOOST_INTEL_CXX_VERSION < 600 +# error "Compiler not supported or configured - please reconfigure" +#endif + +// Intel on MacOS requires +#if defined(__APPLE__) && defined(__INTEL_COMPILER) +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#endif + +// Intel on Altix Itanium +#if defined(__itanium__) && defined(__INTEL_COMPILER) +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#endif + +// +// An attempt to value-initialize a pointer-to-member may trigger an +// internal error on Intel <= 11.1 (last checked version), as was +// reported by John Maddock, Intel support issue 589832, May 2010. +// Moreover, according to test results from Huang-Vista-x86_32_intel, +// intel-vc9-win-11.1 may leave a non-POD array uninitialized, in some +// cases when it should be value-initialized. +// (Niels Dekker, LKEB, May 2010) +// Apparently Intel 12.1 (compiler version number 9999 !!) has the same issue (compiler regression). +#if defined(__INTEL_COMPILER) +# if (__INTEL_COMPILER <= 1110) || (__INTEL_COMPILER == 9999) || (defined(_WIN32) && (__INTEL_COMPILER < 1600)) +# define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +# endif +#endif + +// +// Dynamic shared object (DSO) and dynamic-link library (DLL) support +// +#if defined(__GNUC__) && (__GNUC__ >= 4) +# define BOOST_SYMBOL_EXPORT __attribute__((visibility("default"))) +# define BOOST_SYMBOL_IMPORT +# define BOOST_SYMBOL_VISIBLE __attribute__((visibility("default"))) +#endif + +// Type aliasing hint +#if defined(__GNUC__) && (BOOST_INTEL_CXX_VERSION >= 1300) +# define BOOST_MAY_ALIAS __attribute__((__may_alias__)) +#endif + +// +// C++0x features +// For each feature we need to check both the Intel compiler version, +// and the version of MSVC or GCC that we are emulating. +// See http://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler/ +// for a list of which features were implemented in which Intel releases. +// +#if defined(BOOST_INTEL_STDCXX0X) +// BOOST_NO_CXX11_CONSTEXPR: +#if (BOOST_INTEL_CXX_VERSION >= 1500) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40600)) && !defined(_MSC_VER) +// Available in earlier Intel versions, but fail our tests: +# undef BOOST_NO_CXX11_CONSTEXPR +#endif +// BOOST_NO_CXX11_NULLPTR: +#if (BOOST_INTEL_CXX_VERSION >= 1210) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40600)) && (!defined(_MSC_VER) || (_MSC_VER >= 1600)) +# undef BOOST_NO_CXX11_NULLPTR +#endif +// BOOST_NO_CXX11_TEMPLATE_ALIASES +#if (BOOST_INTEL_CXX_VERSION >= 1210) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40700)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_TEMPLATE_ALIASES +#endif + +// BOOST_NO_CXX11_DECLTYPE +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40300)) && (!defined(_MSC_VER) || (_MSC_VER >= 1600)) +# undef BOOST_NO_CXX11_DECLTYPE +#endif + +// BOOST_NO_CXX11_DECLTYPE_N3276 +#if (BOOST_INTEL_CXX_VERSION >= 1500) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40800)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_DECLTYPE_N3276 +#endif + +// BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40300)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#endif + +// BOOST_NO_CXX11_RVALUE_REFERENCES +#if (BOOST_INTEL_CXX_VERSION >= 1300) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40300)) && (!defined(_MSC_VER) || (_MSC_VER >= 1600)) +// This is available from earlier Intel versions, but breaks Filesystem and other libraries: +# undef BOOST_NO_CXX11_RVALUE_REFERENCES +#endif + +// BOOST_NO_CXX11_STATIC_ASSERT +#if (BOOST_INTEL_CXX_VERSION >= 1110) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40300)) && (!defined(_MSC_VER) || (_MSC_VER >= 1600)) +# undef BOOST_NO_CXX11_STATIC_ASSERT +#endif + +// BOOST_NO_CXX11_VARIADIC_TEMPLATES +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif + +// BOOST_NO_CXX11_VARIADIC_MACROS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40200)) && (!defined(_MSC_VER) || (_MSC_VER >= 1400)) +# undef BOOST_NO_CXX11_VARIADIC_MACROS +#endif + +// BOOST_NO_CXX11_AUTO_DECLARATIONS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_VER >= 1600)) +# undef BOOST_NO_CXX11_AUTO_DECLARATIONS +#endif + +// BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_VER >= 1600)) +# undef BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#endif + +// BOOST_NO_CXX11_CHAR16_T +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_VER >= 9999)) +# undef BOOST_NO_CXX11_CHAR16_T +#endif + +// BOOST_NO_CXX11_CHAR32_T +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_VER >= 9999)) +# undef BOOST_NO_CXX11_CHAR32_T +#endif + +// BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#endif + +// BOOST_NO_CXX11_DELETED_FUNCTIONS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_DELETED_FUNCTIONS +#endif + +// BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_VER >= 1700)) +# undef BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#endif + +// BOOST_NO_CXX11_SCOPED_ENUMS +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40501)) && (!defined(_MSC_VER) || (_MSC_VER >= 1700)) +// This is available but broken in earlier Intel releases. +# undef BOOST_NO_CXX11_SCOPED_ENUMS +#endif + +// BOOST_NO_SFINAE_EXPR +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40500)) && (!defined(_MSC_VER) || (_MSC_VER >= 9999)) +# undef BOOST_NO_SFINAE_EXPR +#endif + +// BOOST_NO_CXX11_SFINAE_EXPR +#if (BOOST_INTEL_CXX_VERSION >= 1500) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40800)) && !defined(_MSC_VER) +# undef BOOST_NO_CXX11_SFINAE_EXPR +#endif + +// BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#if (BOOST_INTEL_CXX_VERSION >= 1500) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40500)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +// This is available in earlier Intel releases, but breaks Multiprecision: +# undef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#endif + +// BOOST_NO_CXX11_LAMBDAS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40500)) && (!defined(_MSC_VER) || (_MSC_VER >= 1600)) +# undef BOOST_NO_CXX11_LAMBDAS +#endif + +// BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40500)) +# undef BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#endif + +// BOOST_NO_CXX11_RANGE_BASED_FOR +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40600)) && (!defined(_MSC_VER) || (_MSC_VER >= 1700)) +# undef BOOST_NO_CXX11_RANGE_BASED_FOR +#endif + +// BOOST_NO_CXX11_RAW_LITERALS +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40500)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_RAW_LITERALS +#endif + +// BOOST_NO_CXX11_UNICODE_LITERALS +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40500)) && (!defined(_MSC_VER) || (_MSC_VER >= 9999)) +# undef BOOST_NO_CXX11_UNICODE_LITERALS +#endif + +// BOOST_NO_CXX11_NOEXCEPT +#if (BOOST_INTEL_CXX_VERSION >= 1500) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40600)) && (!defined(_MSC_VER) || (_MSC_VER >= 9999)) +// Available in earlier Intel release, but generates errors when used with +// conditional exception specifications, for example in multiprecision: +# undef BOOST_NO_CXX11_NOEXCEPT +#endif + +// BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40600)) && (!defined(_MSC_VER) || (_MSC_VER >= 9999)) +# undef BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#endif + +// BOOST_NO_CXX11_USER_DEFINED_LITERALS +#if (BOOST_INTEL_CXX_VERSION >= 1500) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40700)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 190021730)) +# undef BOOST_NO_CXX11_USER_DEFINED_LITERALS +#endif + +// BOOST_NO_CXX11_ALIGNAS +#if (BOOST_INTEL_CXX_VERSION >= 1500) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40800)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 190021730)) +# undef BOOST_NO_CXX11_ALIGNAS +# undef BOOST_NO_CXX11_ALIGNOF +#endif + +// BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#if (BOOST_INTEL_CXX_VERSION >= 1200) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 180020827)) +# undef BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#endif + +// BOOST_NO_CXX11_INLINE_NAMESPACES +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40400)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 190021730)) +# undef BOOST_NO_CXX11_INLINE_NAMESPACES +#endif + +// BOOST_NO_CXX11_REF_QUALIFIERS +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40800)) && (!defined(_MSC_VER) || (_MSC_FULL_VER >= 190021730)) +# undef BOOST_NO_CXX11_REF_QUALIFIERS +#endif + +// BOOST_NO_CXX11_FINAL +// BOOST_NO_CXX11_OVERRIDE +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 40700)) && (!defined(_MSC_VER) || (_MSC_VER >= 1700)) +# undef BOOST_NO_CXX11_FINAL +# undef BOOST_NO_CXX11_OVERRIDE +#endif + +// BOOST_NO_CXX11_UNRESTRICTED_UNION +#if (BOOST_INTEL_CXX_VERSION >= 1400) && (!defined(BOOST_INTEL_GCC_VERSION) || (BOOST_INTEL_GCC_VERSION >= 50100)) && (!defined(_MSC_VER)) +# undef BOOST_NO_CXX11_UNRESTRICTED_UNION +#endif + +#endif // defined(BOOST_INTEL_STDCXX0X) + +// +// Broken in all versions up to 15: +#define BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS + +#if defined(BOOST_INTEL_STDCXX0X) && (BOOST_INTEL_CXX_VERSION <= 1310) +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#endif + +#if defined(BOOST_INTEL_STDCXX0X) && (BOOST_INTEL_CXX_VERSION == 1400) +// A regression in Intel's compiler means that seems to be broken in this release as well as : +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_TUPLE +#endif + +#if (BOOST_INTEL_CXX_VERSION < 1200) +// +// fenv.h appears not to work with Intel prior to 12.0: +// +# define BOOST_NO_FENV_H +#endif + +// Intel 13.10 fails to access defaulted functions of a base class declared in private or protected sections, +// producing the following errors: +// error #453: protected function "..." (declared at ...") is not accessible through a "..." pointer or object +#if (BOOST_INTEL_CXX_VERSION <= 1310) +# define BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +# define BOOST_HAS_STDINT_H +#endif + +#if defined(__CUDACC__) +# if defined(BOOST_GCC_CXX11) +# define BOOST_NVCC_CXX11 +# else +# define BOOST_NVCC_CXX03 +# endif +#endif + +#if defined(__LP64__) && defined(__GNUC__) && (BOOST_INTEL_CXX_VERSION >= 1310) && !defined(BOOST_NVCC_CXX03) +# define BOOST_HAS_INT128 +#endif + +#endif // defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500) && (defined(_MSC_VER) || defined(__GNUC__)) +// +// last known and checked version: +#if (BOOST_INTEL_CXX_VERSION > 1700) +# if defined(BOOST_ASSERT_CONFIG) +# error "Boost.Config is older than your compiler - please check for an updated Boost release." +# elif defined(_MSC_VER) +// +// We don't emit this warning any more, since we have so few +// defect macros set anyway (just the one). +// +//# pragma message("boost: Unknown compiler version - please run the configure tests and report the results") +# endif +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/kai.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/kai.hpp new file mode 100644 index 0000000..0b22ec1 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/kai.hpp @@ -0,0 +1,33 @@ +// (C) Copyright John Maddock 2001. +// (C) Copyright David Abrahams 2002. +// (C) Copyright Aleksey Gurtovoy 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Kai C++ compiler setup: + +#include + +# if (__KCC_VERSION <= 4001) || !defined(BOOST_STRICT_CONFIG) + // at least on Sun, the contents of is not in namespace std +# define BOOST_NO_STDC_NAMESPACE +# endif + +// see also common_edg.hpp which needs a special check for __KCC +# if !defined(_EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +# endif + +// +// last known and checked version is 4001: +#if (__KCC_VERSION > 4001) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/metrowerks.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/metrowerks.hpp new file mode 100644 index 0000000..448ab67 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/metrowerks.hpp @@ -0,0 +1,198 @@ +// (C) Copyright John Maddock 2001. +// (C) Copyright Darin Adler 2001. +// (C) Copyright Peter Dimov 2001. +// (C) Copyright David Abrahams 2001 - 2002. +// (C) Copyright Beman Dawes 2001 - 2003. +// (C) Copyright Stefan Slapeta 2004. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Metrowerks C++ compiler setup: + +// locale support is disabled when linking with the dynamic runtime +# ifdef _MSL_NO_LOCALE +# define BOOST_NO_STD_LOCALE +# endif + +# if __MWERKS__ <= 0x2301 // 5.3 +# define BOOST_NO_FUNCTION_TEMPLATE_ORDERING +# define BOOST_NO_POINTER_TO_MEMBER_CONST +# define BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS +# define BOOST_NO_MEMBER_TEMPLATE_KEYWORD +# endif + +# if __MWERKS__ <= 0x2401 // 6.2 +//# define BOOST_NO_FUNCTION_TEMPLATE_ORDERING +# endif + +# if(__MWERKS__ <= 0x2407) // 7.x +# define BOOST_NO_MEMBER_FUNCTION_SPECIALIZATIONS +# define BOOST_NO_UNREACHABLE_RETURN_DETECTION +# endif + +# if(__MWERKS__ <= 0x3003) // 8.x +# define BOOST_NO_SFINAE +# endif + +// the "|| !defined(BOOST_STRICT_CONFIG)" part should apply to the last +// tested version *only*: +# if(__MWERKS__ <= 0x3207) || !defined(BOOST_STRICT_CONFIG) // 9.6 +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +# define BOOST_NO_IS_ABSTRACT +# endif + +#if !__option(wchar_type) +# define BOOST_NO_INTRINSIC_WCHAR_T +#endif + +#if !__option(exceptions) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + +#if (__INTEL__ && _WIN32) || (__POWERPC__ && macintosh) +# if __MWERKS__ == 0x3000 +# define BOOST_COMPILER_VERSION 8.0 +# elif __MWERKS__ == 0x3001 +# define BOOST_COMPILER_VERSION 8.1 +# elif __MWERKS__ == 0x3002 +# define BOOST_COMPILER_VERSION 8.2 +# elif __MWERKS__ == 0x3003 +# define BOOST_COMPILER_VERSION 8.3 +# elif __MWERKS__ == 0x3200 +# define BOOST_COMPILER_VERSION 9.0 +# elif __MWERKS__ == 0x3201 +# define BOOST_COMPILER_VERSION 9.1 +# elif __MWERKS__ == 0x3202 +# define BOOST_COMPILER_VERSION 9.2 +# elif __MWERKS__ == 0x3204 +# define BOOST_COMPILER_VERSION 9.3 +# elif __MWERKS__ == 0x3205 +# define BOOST_COMPILER_VERSION 9.4 +# elif __MWERKS__ == 0x3206 +# define BOOST_COMPILER_VERSION 9.5 +# elif __MWERKS__ == 0x3207 +# define BOOST_COMPILER_VERSION 9.6 +# else +# define BOOST_COMPILER_VERSION __MWERKS__ +# endif +#else +# define BOOST_COMPILER_VERSION __MWERKS__ +#endif + +// +// C++0x features +// +// See boost\config\suffix.hpp for BOOST_NO_LONG_LONG +// +#if __MWERKS__ > 0x3206 && __option(rvalue_refs) +# define BOOST_HAS_RVALUE_REFS +#else +# define BOOST_NO_CXX11_RVALUE_REFERENCES +#endif +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CHAR16_T +#define BOOST_NO_CXX11_CHAR32_T +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DECLTYPE +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#define BOOST_NO_CXX11_EXTERN_TEMPLATE +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_VARIADIC_MACROS +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +#define BOOST_COMPILER "Metrowerks CodeWarrior C++ version " BOOST_STRINGIZE(BOOST_COMPILER_VERSION) + +// +// versions check: +// we don't support Metrowerks prior to version 5.3: +#if __MWERKS__ < 0x2301 +# error "Compiler not supported or configured - please reconfigure" +#endif +// +// last known and checked version: +#if (__MWERKS__ > 0x3205) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + + + + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/mpw.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/mpw.hpp new file mode 100644 index 0000000..8433f37 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/mpw.hpp @@ -0,0 +1,140 @@ +// (C) Copyright John Maddock 2001 - 2002. +// (C) Copyright Aleksey Gurtovoy 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// MPW C++ compilers setup: + +# if defined(__SC__) +# define BOOST_COMPILER "MPW SCpp version " BOOST_STRINGIZE(__SC__) +# elif defined(__MRC__) +# define BOOST_COMPILER "MPW MrCpp version " BOOST_STRINGIZE(__MRC__) +# else +# error "Using MPW compiler configuration by mistake. Please update." +# endif + +// +// MPW 8.90: +// +#if (MPW_CPLUS <= 0x890) || !defined(BOOST_STRICT_CONFIG) +# define BOOST_NO_CV_SPECIALIZATIONS +# define BOOST_NO_DEPENDENT_NESTED_DERIVATIONS +# define BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS +# define BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# define BOOST_NO_INTRINSIC_WCHAR_T +# define BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +# define BOOST_NO_USING_TEMPLATE + +# define BOOST_NO_CWCHAR +# define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS + +# define BOOST_NO_STD_ALLOCATOR /* actually a bug with const reference overloading */ + +#endif + +// +// C++0x features +// +// See boost\config\suffix.hpp for BOOST_NO_LONG_LONG +// +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CHAR16_T +#define BOOST_NO_CXX11_CHAR32_T +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DECLTYPE +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#define BOOST_NO_CXX11_EXTERN_TEMPLATE +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_VARIADIC_MACROS +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +// +// versions check: +// we don't support MPW prior to version 8.9: +#if MPW_CPLUS < 0x890 +# error "Compiler not supported or configured - please reconfigure" +#endif +// +// last known and checked version is 0x890: +#if (MPW_CPLUS > 0x890) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/nvcc.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/nvcc.hpp new file mode 100644 index 0000000..419dd72 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/nvcc.hpp @@ -0,0 +1,61 @@ +// (C) Copyright Eric Jourdanneau, Joel Falcou 2010 +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// NVIDIA CUDA C++ compiler setup + +#ifndef BOOST_COMPILER +# define BOOST_COMPILER "NVIDIA CUDA C++ Compiler" +#endif + +#if defined(__CUDACC_VER_MAJOR__) && defined(__CUDACC_VER_MINOR__) && defined(__CUDACC_VER_BUILD__) +# define BOOST_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 1000000 + __CUDACC_VER_MINOR__ * 10000 + __CUDACC_VER_BUILD__) +#else +// We don't really know what the CUDA version is, but it's definitely before 7.5: +# define BOOST_CUDA_VERSION 7000000 +#endif + +// NVIDIA Specific support +// BOOST_GPU_ENABLED : Flag a function or a method as being enabled on the host and device +#define BOOST_GPU_ENABLED __host__ __device__ + +#if !defined(__clang__) || defined(__NVCC__) +// A bug in version 7.0 of CUDA prevents use of variadic templates in some occasions +// https://svn.boost.org/trac/boost/ticket/11897 +// This is fixed in 7.5. As the following version macro was introduced in 7.5 an existance +// check is enough to detect versions < 7.5 +#if BOOST_CUDA_VERSION < 7050000 +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif +// The same bug is back again in 8.0: +#if (BOOST_CUDA_VERSION > 8000000) && (BOOST_CUDA_VERSION < 8010000) +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif +// CUDA (8.0) has no constexpr support in msvc mode: +#if defined(_MSC_VER) && (BOOST_CUDA_VERSION < 9000000) +# define BOOST_NO_CXX11_CONSTEXPR +#endif + +#endif + +#ifdef __CUDACC__ +// +// When compiing .cu files, there's a bunch of stuff that doesn't work with msvc: +// +#if defined(_MSC_VER) +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +# define BOOST_NO_CXX11_UNICODE_LITERALS +#endif +// +// And this one effects the NVCC front end, +// See https://svn.boost.org/trac/boost/ticket/13049 +// +#if (BOOST_CUDA_VERSION >= 8000000) && (BOOST_CUDA_VERSION < 8010000) +# define BOOST_NO_CXX11_NOEXCEPT +#endif + +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pathscale.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pathscale.hpp new file mode 100644 index 0000000..5348cf7 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pathscale.hpp @@ -0,0 +1,138 @@ +// (C) Copyright Bryce Lelbach 2011 + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// PathScale EKOPath C++ Compiler + +#ifndef BOOST_COMPILER +# define BOOST_COMPILER "PathScale EKOPath C++ Compiler version " __PATHSCALE__ +#endif + +#if __PATHCC__ >= 6 +// PathCC is based on clang, and supports the __has_*() builtins used +// to detect features in clang.hpp. Since the clang toolset is much +// better maintained, it is more convenient to reuse its definitions. +# include "boost/config/compiler/clang.hpp" +#elif __PATHCC__ >= 4 +# define BOOST_MSVC6_MEMBER_TEMPLATES +# define BOOST_HAS_UNISTD_H +# define BOOST_HAS_STDINT_H +# define BOOST_HAS_SIGACTION +# define BOOST_HAS_SCHED_YIELD +# define BOOST_HAS_THREADS +# define BOOST_HAS_PTHREADS +# define BOOST_HAS_PTHREAD_YIELD +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# define BOOST_HAS_PARTIAL_STD_ALLOCATOR +# define BOOST_HAS_NRVO +# define BOOST_HAS_NL_TYPES_H +# define BOOST_HAS_NANOSLEEP +# define BOOST_HAS_LONG_LONG +# define BOOST_HAS_LOG1P +# define BOOST_HAS_GETTIMEOFDAY +# define BOOST_HAS_EXPM1 +# define BOOST_HAS_DIRENT_H +# define BOOST_HAS_CLOCK_GETTIME +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +# define BOOST_NO_CXX11_UNICODE_LITERALS +# define BOOST_NO_CXX11_TEMPLATE_ALIASES +# define BOOST_NO_CXX11_STATIC_ASSERT +# define BOOST_NO_SFINAE_EXPR +# define BOOST_NO_CXX11_SFINAE_EXPR +# define BOOST_NO_CXX11_SCOPED_ENUMS +# define BOOST_NO_CXX11_RVALUE_REFERENCES +# define BOOST_NO_CXX11_RANGE_BASED_FOR +# define BOOST_NO_CXX11_RAW_LITERALS +# define BOOST_NO_CXX11_NULLPTR +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_NOEXCEPT +# define BOOST_NO_CXX11_LAMBDAS +# define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +# define BOOST_NO_CXX11_DECLTYPE +# define BOOST_NO_CXX11_DECLTYPE_N3276 +# define BOOST_NO_CXX11_CONSTEXPR +# define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +# define BOOST_NO_CXX11_CHAR32_T +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_USER_DEFINED_LITERALS +# define BOOST_NO_CXX11_ALIGNAS +# define BOOST_NO_CXX11_ALIGNOF +# define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +# define BOOST_NO_CXX11_INLINE_NAMESPACES +# define BOOST_NO_CXX11_REF_QUALIFIERS +# define BOOST_NO_CXX11_FINAL +# define BOOST_NO_CXX11_OVERRIDE +# define BOOST_NO_CXX11_THREAD_LOCAL +# define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pgi.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pgi.hpp new file mode 100644 index 0000000..4e909d8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/pgi.hpp @@ -0,0 +1,23 @@ +// (C) Copyright Noel Belcourt 2007. +// Copyright 2017, NVIDIA CORPORATION. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// PGI C++ compiler setup: + +#define BOOST_COMPILER_VERSION __PGIC__##__PGIC_MINOR__ +#define BOOST_COMPILER "PGI compiler version " BOOST_STRINGIZE(BOOST_COMPILER_VERSION) + +// PGI is mostly GNU compatible. So start with that. +#include + +// Now adjust for things that are different. + +// __float128 is a typedef, not a distinct type. +#undef BOOST_HAS_FLOAT128 + +// __int128 is not supported. +#undef BOOST_HAS_INT128 diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sgi_mipspro.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sgi_mipspro.hpp new file mode 100644 index 0000000..54433c9 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sgi_mipspro.hpp @@ -0,0 +1,29 @@ +// (C) Copyright John Maddock 2001 - 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// SGI C++ compiler setup: + +#define BOOST_COMPILER "SGI Irix compiler version " BOOST_STRINGIZE(_COMPILER_VERSION) + +#include + +// +// Threading support: +// Turn this on unconditionally here, it will get turned off again later +// if no threading API is detected. +// +#define BOOST_HAS_THREADS +#define BOOST_NO_TWO_PHASE_NAME_LOOKUP + +#undef BOOST_NO_SWPRINTF +#undef BOOST_DEDUCED_TYPENAME + +// +// version check: +// probably nothing to do here? + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sunpro_cc.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sunpro_cc.hpp new file mode 100644 index 0000000..490dc76 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/sunpro_cc.hpp @@ -0,0 +1,222 @@ +// (C) Copyright John Maddock 2001. +// (C) Copyright Jens Maurer 2001 - 2003. +// (C) Copyright Peter Dimov 2002. +// (C) Copyright Aleksey Gurtovoy 2002 - 2003. +// (C) Copyright David Abrahams 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Sun C++ compiler setup: + +# if __SUNPRO_CC <= 0x500 +# define BOOST_NO_MEMBER_TEMPLATES +# define BOOST_NO_FUNCTION_TEMPLATE_ORDERING +# endif + +# if (__SUNPRO_CC <= 0x520) + // + // Sunpro 5.2 and earler: + // + // although sunpro 5.2 supports the syntax for + // inline initialization it often gets the value + // wrong, especially where the value is computed + // from other constants (J Maddock 6th May 2001) +# define BOOST_NO_INCLASS_MEMBER_INITIALIZATION + + // Although sunpro 5.2 supports the syntax for + // partial specialization, it often seems to + // bind to the wrong specialization. Better + // to disable it until suppport becomes more stable + // (J Maddock 6th May 2001). +# define BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +# endif + +# if (__SUNPRO_CC <= 0x530) + // Requesting debug info (-g) with Boost.Python results + // in an internal compiler error for "static const" + // initialized in-class. + // >> Assertion: (../links/dbg_cstabs.cc, line 611) + // while processing ../test.cpp at line 0. + // (Jens Maurer according to Gottfried Ganssauge 04 Mar 2002) +# define BOOST_NO_INCLASS_MEMBER_INITIALIZATION + + // SunPro 5.3 has better support for partial specialization, + // but breaks when compiling std::less > + // (Jens Maurer 4 Nov 2001). + + // std::less specialization fixed as reported by George + // Heintzelman; partial specialization re-enabled + // (Peter Dimov 17 Jan 2002) + +//# define BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + + // integral constant expressions with 64 bit numbers fail +# define BOOST_NO_INTEGRAL_INT64_T +# endif + +# if (__SUNPRO_CC < 0x570) +# define BOOST_NO_TEMPLATE_TEMPLATES + // see http://lists.boost.org/MailArchives/boost/msg47184.php + // and http://lists.boost.org/MailArchives/boost/msg47220.php +# define BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# define BOOST_NO_SFINAE +# define BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS +# endif +# if (__SUNPRO_CC <= 0x580) +# define BOOST_NO_IS_ABSTRACT +# endif + +# if (__SUNPRO_CC <= 0x5100) + // Sun 5.10 may not correctly value-initialize objects of + // some user defined types, as was reported in April 2010 + // (CR 6947016), and confirmed by Steve Clamage. + // (Niels Dekker, LKEB, May 2010). +# define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +# endif + +// +// Dynamic shared object (DSO) and dynamic-link library (DLL) support +// +#if __SUNPRO_CC > 0x500 +# define BOOST_SYMBOL_EXPORT __global +# define BOOST_SYMBOL_IMPORT __global +# define BOOST_SYMBOL_VISIBLE __global +#endif + +// Deprecated symbol markup +// Oracle Studio 12.4 supports deprecated attribute with a message; this is the first release that supports the attribute. +#if (__SUNPRO_CC >= 0x5130) +#define BOOST_DEPRECATED(msg) __attribute__((deprecated(msg))) +#endif + +#if (__SUNPRO_CC < 0x5130) +// C++03 features in 12.4: +#define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_ADL_BARRIER +#define BOOST_NO_CXX11_VARIADIC_MACROS +#endif + +#if (__SUNPRO_CC < 0x5130) || (__cplusplus < 201100) +// C++11 only featuires in 12.4: +#define BOOST_NO_CXX11_AUTO_DECLARATIONS +#define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#define BOOST_NO_CXX11_CHAR16_T +#define BOOST_NO_CXX11_CHAR32_T +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_DECLTYPE +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#define BOOST_NO_CXX11_EXTERN_TEMPLATE +#define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_RVALUE_REFERENCES +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_UNRESTRICTED_UNION +#endif + +#if (__SUNPRO_CC < 0x5140) || (__cplusplus < 201103) +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_THREAD_LOCAL +#endif + +#define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +// +// C++0x features +// +# define BOOST_HAS_LONG_LONG + +#define BOOST_NO_CXX11_SFINAE_EXPR + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) || (__cplusplus < 201402L) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +// Turn on threading support for Solaris 12. +// Ticket #11972 +#if (__SUNPRO_CC >= 0x5140) && defined(__SunOS_5_12) && !defined(BOOST_HAS_THREADS) +# define BOOST_HAS_THREADS +#endif + +// +// Version +// + +#define BOOST_COMPILER "Sun compiler version " BOOST_STRINGIZE(__SUNPRO_CC) + +// +// versions check: +// we don't support sunpro prior to version 4: +#if __SUNPRO_CC < 0x400 +#error "Compiler not supported or configured - please reconfigure" +#endif +// +// last known and checked version: +#if (__SUNPRO_CC > 0x5150) +# if defined(BOOST_ASSERT_CONFIG) +# error "Boost.Config is older than your compiler - please check for an updated Boost release." +# endif +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/vacpp.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/vacpp.hpp new file mode 100644 index 0000000..9cfa1ad --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/vacpp.hpp @@ -0,0 +1,186 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Toon Knapen 2001 - 2003. +// (C) Copyright Lie-Quan Lee 2001. +// (C) Copyright Markus Schoepflin 2002 - 2003. +// (C) Copyright Beman Dawes 2002 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Visual Age (IBM) C++ compiler setup: + +#if __IBMCPP__ <= 501 +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +# define BOOST_NO_MEMBER_FUNCTION_SPECIALIZATIONS +#endif + +#if (__IBMCPP__ <= 502) +// Actually the compiler supports inclass member initialization but it +// requires a definition for the class member and it doesn't recognize +// it as an integral constant expression when used as a template argument. +# define BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# define BOOST_NO_INTEGRAL_INT64_T +# define BOOST_NO_MEMBER_TEMPLATE_KEYWORD +#endif + +#if (__IBMCPP__ <= 600) || !defined(BOOST_STRICT_CONFIG) +# define BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS +#endif + +#if (__IBMCPP__ <= 1110) +// XL C++ V11.1 and earlier versions may not always value-initialize +// a temporary object T(), when T is a non-POD aggregate class type. +// Michael Wong (IBM Canada Ltd) has confirmed this issue and gave it +// high priority. -- Niels Dekker (LKEB), May 2010. +# define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +#endif + +// +// On AIX thread support seems to be indicated by _THREAD_SAFE: +// +#ifdef _THREAD_SAFE +# define BOOST_HAS_THREADS +#endif + +#define BOOST_COMPILER "IBM Visual Age version " BOOST_STRINGIZE(__IBMCPP__) + +// +// versions check: +// we don't support Visual age prior to version 5: +#if __IBMCPP__ < 500 +#error "Compiler not supported or configured - please reconfigure" +#endif +// +// last known and checked version is 1210: +#if (__IBMCPP__ > 1210) +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + +// Some versions of the compiler have issues with default arguments on partial specializations +#if __IBMCPP__ <= 1010 +#define BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS +#endif + +// Type aliasing hint. Supported since XL C++ 13.1 +#if (__IBMCPP__ >= 1310) +# define BOOST_MAY_ALIAS __attribute__((__may_alias__)) +#endif + +// +// C++0x features +// +// See boost\config\suffix.hpp for BOOST_NO_LONG_LONG +// +#if ! __IBMCPP_AUTO_TYPEDEDUCTION +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#endif +#if ! __IBMCPP_UTF_LITERAL__ +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +#endif +#if ! __IBMCPP_CONSTEXPR +# define BOOST_NO_CXX11_CONSTEXPR +#endif +#if ! __IBMCPP_DECLTYPE +# define BOOST_NO_CXX11_DECLTYPE +#else +# define BOOST_HAS_DECLTYPE +#endif +#define BOOST_NO_CXX11_DECLTYPE_N3276 +#define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#define BOOST_NO_CXX11_DELETED_FUNCTIONS +#if ! __IBMCPP_EXPLICIT_CONVERSION_OPERATORS +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#endif +#if ! __IBMCPP_EXTERN_TEMPLATE +# define BOOST_NO_CXX11_EXTERN_TEMPLATE +#endif +#if ! __IBMCPP_VARIADIC_TEMPLATES +// not enabled separately at this time +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#endif +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#if ! __IBMCPP_RVALUE_REFERENCES +# define BOOST_NO_CXX11_RVALUE_REFERENCES +#endif +#if ! __IBMCPP_SCOPED_ENUM +# define BOOST_NO_CXX11_SCOPED_ENUMS +#endif +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#if ! __IBMCPP_STATIC_ASSERT +# define BOOST_NO_CXX11_STATIC_ASSERT +#endif +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_UNICODE_LITERALS +#if ! __IBMCPP_VARIADIC_TEMPLATES +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif +#if ! __C99_MACRO_WITH_VA_ARGS +# define BOOST_NO_CXX11_VARIADIC_MACROS +#endif +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#define BOOST_NO_CXX11_INLINE_NAMESPACES +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_UNRESTRICTED_UNION + +// C++ 14: +#if !defined(__cpp_aggregate_nsdmi) || (__cpp_aggregate_nsdmi < 201304) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif +#if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif +#if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) +# define BOOST_NO_CXX14_CONSTEXPR +#endif +#if !defined(__cpp_decltype_auto) || (__cpp_decltype_auto < 201304) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif +#if (__cplusplus < 201304) // There's no SD6 check for this.... +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif +#if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif +#if !defined(__cpp_init_captures) || (__cpp_init_captures < 201304) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif +#if !defined(__cpp_return_type_deduction) || (__cpp_return_type_deduction < 201304) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif +#if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +// C++17 +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif +#if !defined(__cpp_inline_variables) || (__cpp_inline_variables < 201606) +# define BOOST_NO_CXX17_INLINE_VARIABLES +#endif +#if !defined(__cpp_fold_expressions) || (__cpp_fold_expressions < 201603) +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/visualc.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/visualc.hpp new file mode 100644 index 0000000..c0ada09 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/visualc.hpp @@ -0,0 +1,395 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Darin Adler 2001 - 2002. +// (C) Copyright Peter Dimov 2001. +// (C) Copyright Aleksey Gurtovoy 2002. +// (C) Copyright David Abrahams 2002 - 2003. +// (C) Copyright Beman Dawes 2002 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. +// +// Microsoft Visual C++ compiler setup: +// +// We need to be careful with the checks in this file, as contrary +// to popular belief there are versions with _MSC_VER with the final +// digit non-zero (mainly the MIPS cross compiler). +// +// So we either test _MSC_VER >= XXXX or else _MSC_VER < XXXX. +// No other comparisons (==, >, or <=) are safe. +// + +#define BOOST_MSVC _MSC_VER + +// +// Helper macro BOOST_MSVC_FULL_VER for use in Boost code: +// +#if _MSC_FULL_VER > 100000000 +# define BOOST_MSVC_FULL_VER _MSC_FULL_VER +#else +# define BOOST_MSVC_FULL_VER (_MSC_FULL_VER * 10) +#endif + +// Attempt to suppress VC6 warnings about the length of decorated names (obsolete): +#pragma warning( disable : 4503 ) // warning: decorated name length exceeded + +#define BOOST_HAS_PRAGMA_ONCE + +// +// versions check: +// we don't support Visual C++ prior to version 7.1: +#if _MSC_VER < 1310 +# error "Compiler not supported or configured - please reconfigure" +#endif + +// VS2005 (VC8) docs: __assume has been in Visual C++ for multiple releases +#define BOOST_UNREACHABLE_RETURN(x) __assume(0); + +#if _MSC_FULL_VER < 180020827 +# define BOOST_NO_FENV_H +#endif + +#if _MSC_VER < 1400 +// although a conforming signature for swprint exists in VC7.1 +// it appears not to actually work: +# define BOOST_NO_SWPRINTF +// Our extern template tests also fail for this compiler: +# define BOOST_NO_CXX11_EXTERN_TEMPLATE +// Variadic macros do not exist for VC7.1 and lower +# define BOOST_NO_CXX11_VARIADIC_MACROS +# define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#endif + +#if _MSC_VER < 1500 // 140X == VC++ 8.0 +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +#endif + +#if _MSC_VER < 1600 // 150X == VC++ 9.0 + // A bug in VC9: +# define BOOST_NO_ADL_BARRIER +#endif + + +#ifndef _NATIVE_WCHAR_T_DEFINED +# define BOOST_NO_INTRINSIC_WCHAR_T +#endif + +// +// check for exception handling support: +#if !defined(_CPPUNWIND) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + +// +// __int64 support: +// +#define BOOST_HAS_MS_INT64 +#if defined(_MSC_EXTENSIONS) || (_MSC_VER >= 1400) +# define BOOST_HAS_LONG_LONG +#else +# define BOOST_NO_LONG_LONG +#endif +#if (_MSC_VER >= 1400) && !defined(_DEBUG) +# define BOOST_HAS_NRVO +#endif +#if _MSC_VER >= 1600 // 160X == VC++ 10.0 +# define BOOST_HAS_PRAGMA_DETECT_MISMATCH +#endif +// +// disable Win32 API's if compiler extensions are +// turned off: +// +#if !defined(_MSC_EXTENSIONS) && !defined(BOOST_DISABLE_WIN32) +# define BOOST_DISABLE_WIN32 +#endif +#if !defined(_CPPRTTI) && !defined(BOOST_NO_RTTI) +# define BOOST_NO_RTTI +#endif + +// Deprecated symbol markup +#if (_MSC_VER >= 1400) +#define BOOST_DEPRECATED(msg) __declspec(deprecated(msg)) +#else +// MSVC 7.1 only supports the attribute without a message +#define BOOST_DEPRECATED(msg) __declspec(deprecated) +#endif + +// +// TR1 features: +// +#if (_MSC_VER >= 1700) && defined(_HAS_CXX17) && (_HAS_CXX17 > 0) +// # define BOOST_HAS_TR1_HASH // don't know if this is true yet. +// # define BOOST_HAS_TR1_TYPE_TRAITS // don't know if this is true yet. +# define BOOST_HAS_TR1_UNORDERED_MAP +# define BOOST_HAS_TR1_UNORDERED_SET +#endif + +// +// C++0x features +// +// See above for BOOST_NO_LONG_LONG + +// C++ features supported by VC++ 10 (aka 2010) +// +#if _MSC_VER < 1600 +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +# define BOOST_NO_CXX11_LAMBDAS +# define BOOST_NO_CXX11_RVALUE_REFERENCES +# define BOOST_NO_CXX11_STATIC_ASSERT +# define BOOST_NO_CXX11_NULLPTR +# define BOOST_NO_CXX11_DECLTYPE +#endif // _MSC_VER < 1600 + +#if _MSC_VER >= 1600 +# define BOOST_HAS_STDINT_H +#endif + +// C++11 features supported by VC++ 11 (aka 2012) +// +#if _MSC_VER < 1700 +# define BOOST_NO_CXX11_FINAL +# define BOOST_NO_CXX11_RANGE_BASED_FOR +# define BOOST_NO_CXX11_SCOPED_ENUMS +# define BOOST_NO_CXX11_OVERRIDE +#endif // _MSC_VER < 1700 + +// C++11 features supported by VC++ 12 (aka 2013). +// +#if _MSC_FULL_VER < 180020827 +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +# define BOOST_NO_CXX11_RAW_LITERALS +# define BOOST_NO_CXX11_TEMPLATE_ALIASES +# define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +# define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +# define BOOST_NO_CXX11_DECLTYPE_N3276 +#endif + +#if _MSC_FULL_VER >= 180020827 +#define BOOST_HAS_EXPM1 +#define BOOST_HAS_LOG1P +#endif + +// C++11 features supported by VC++ 14 (aka 2015) +// +#if (_MSC_FULL_VER < 190023026) +# define BOOST_NO_CXX11_NOEXCEPT +# define BOOST_NO_CXX11_DEFAULTED_MOVES +# define BOOST_NO_CXX11_REF_QUALIFIERS +# define BOOST_NO_CXX11_USER_DEFINED_LITERALS +# define BOOST_NO_CXX11_ALIGNAS +# define BOOST_NO_CXX11_ALIGNOF +# define BOOST_NO_CXX11_INLINE_NAMESPACES +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +# define BOOST_NO_CXX11_UNICODE_LITERALS +# define BOOST_NO_CXX14_DECLTYPE_AUTO +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +# define BOOST_NO_CXX14_BINARY_LITERALS +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +# define BOOST_NO_CXX11_THREAD_LOCAL +# define BOOST_NO_CXX11_UNRESTRICTED_UNION +#endif +// C++11 features supported by VC++ 14 update 3 (aka 2015) +// +#if (_MSC_FULL_VER < 190024210) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +# define BOOST_NO_SFINAE_EXPR +# define BOOST_NO_CXX11_CONSTEXPR +#endif + +// C++14 features supported by VC++ 14.1 (Visual Studio 2017) +// +#if (_MSC_VER < 1910) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif + +// C++17 features supported by VC++ 14.1 (Visual Studio 2017) Update 3 +// +#if (_MSC_VER < 1911) || (_MSVC_LANG < 201703) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +# define BOOST_NO_CXX17_IF_CONSTEXPR +// Let the defaults handle these now: +//# define BOOST_NO_CXX17_HDR_OPTIONAL +//# define BOOST_NO_CXX17_HDR_STRING_VIEW +#endif + +// MSVC including version 14 has not yet completely +// implemented value-initialization, as is reported: +// "VC++ does not value-initialize members of derived classes without +// user-declared constructor", reported in 2009 by Sylvester Hesp: +// https://connect.microsoft.com/VisualStudio/feedback/details/484295 +// "Presence of copy constructor breaks member class initialization", +// reported in 2009 by Alex Vakulenko: +// https://connect.microsoft.com/VisualStudio/feedback/details/499606 +// "Value-initialization in new-expression", reported in 2005 by +// Pavel Kuznetsov (MetaCommunications Engineering): +// https://connect.microsoft.com/VisualStudio/feedback/details/100744 +// Reported again by John Maddock in 2015 for VC14: +// https://connect.microsoft.com/VisualStudio/feedback/details/1582233/c-subobjects-still-not-value-initialized-correctly +// See also: http://www.boost.org/libs/utility/value_init.htm#compiler_issues +// (Niels Dekker, LKEB, May 2010) +// Still present in VC15.5, Dec 2017. +#define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +// +// C++ 11: +// +// This is supported with /permissive- for 15.5 onwards, unfortunately we appear to have no way to tell +// if this is in effect or not, in any case nothing in Boost is currently using this, so we'll just go +// on defining it for now: +// +#if (_MSC_FULL_VER < 193030705) || (_MSVC_LANG < 202004) +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +#endif + +#if (_MSC_VER < 1912) || (_MSVC_LANG < 201402) +// Supported from msvc-15.5 onwards: +#define BOOST_NO_CXX11_SFINAE_EXPR +#endif +#if (_MSC_VER < 1915) || (_MSVC_LANG < 201402) +// C++ 14: +// Still gives internal compiler error for msvc-15.5: +# define BOOST_NO_CXX14_CONSTEXPR +#endif +// C++ 17: +#if (_MSC_VER < 1912) || (_MSVC_LANG < 201703) +#define BOOST_NO_CXX17_INLINE_VARIABLES +#define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif + +// +// Things that don't work in clr mode: +// +#ifdef _M_CEE +#ifndef BOOST_NO_CXX11_THREAD_LOCAL +# define BOOST_NO_CXX11_THREAD_LOCAL +#endif +#if !defined(BOOST_NO_SFINAE_EXPR) && !defined(_MSVC_LANG) +# define BOOST_NO_SFINAE_EXPR +#endif +#ifndef BOOST_NO_CXX11_REF_QUALIFIERS +# define BOOST_NO_CXX11_REF_QUALIFIERS +#endif +#endif +#ifdef _M_CEE_PURE +#ifndef BOOST_NO_CXX11_CONSTEXPR +# define BOOST_NO_CXX11_CONSTEXPR +#endif +#endif + +// +// prefix and suffix headers: +// +#ifndef BOOST_ABI_PREFIX +# define BOOST_ABI_PREFIX "boost/config/abi/msvc_prefix.hpp" +#endif +#ifndef BOOST_ABI_SUFFIX +# define BOOST_ABI_SUFFIX "boost/config/abi/msvc_suffix.hpp" +#endif + +// +// Approximate compiler conformance version +// +#ifdef _MSVC_LANG +# define BOOST_CXX_VERSION _MSVC_LANG +#elif defined(_HAS_CXX17) +# define BOOST_CXX_VERSION 201703L +#elif BOOST_MSVC >= 1916 +# define BOOST_CXX_VERSION 201402L +#endif + +#if BOOST_CXX_VERSION >= 201703L +# define BOOST_ATTRIBUTE_UNUSED [[maybe_unused]] +#endif + +#ifndef BOOST_COMPILER +// TODO: +// these things are mostly bogus. 1200 means version 12.0 of the compiler. The +// artificial versions assigned to them only refer to the versions of some IDE +// these compilers have been shipped with, and even that is not all of it. Some +// were shipped with freely downloadable SDKs, others as crosscompilers in eVC. +// IOW, you can't use these 'versions' in any sensible way. Sorry. +# if defined(UNDER_CE) +# if _MSC_VER < 1400 + // Note: I'm not aware of any CE compiler with version 13xx +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown EVC++ compiler version - please run the configure tests and report the results" +# else +# pragma message("boost: Unknown EVC++ compiler version - please run the configure tests and report the results") +# endif +# elif _MSC_VER < 1500 +# define BOOST_COMPILER_VERSION evc8 +# elif _MSC_VER < 1600 +# define BOOST_COMPILER_VERSION evc9 +# elif _MSC_VER < 1700 +# define BOOST_COMPILER_VERSION evc10 +# elif _MSC_VER < 1800 +# define BOOST_COMPILER_VERSION evc11 +# elif _MSC_VER < 1900 +# define BOOST_COMPILER_VERSION evc12 +# elif _MSC_VER < 2000 +# define BOOST_COMPILER_VERSION evc14 +# else +# if defined(BOOST_ASSERT_CONFIG) +# error "boost: Unknown EVC++ compiler version - please run the configure tests and report the results" +# else +# pragma message("boost: Unknown EVC++ compiler version - please run the configure tests and report the results") +# endif +# endif +# else +# if _MSC_VER < 1200 + // Note: Versions up to 10.0 aren't supported. +# define BOOST_COMPILER_VERSION 5.0 +# elif _MSC_VER < 1300 +# define BOOST_COMPILER_VERSION 6.0 +# elif _MSC_VER < 1310 +# define BOOST_COMPILER_VERSION 7.0 +# elif _MSC_VER < 1400 +# define BOOST_COMPILER_VERSION 7.1 +# elif _MSC_VER < 1500 +# define BOOST_COMPILER_VERSION 8.0 +# elif _MSC_VER < 1600 +# define BOOST_COMPILER_VERSION 9.0 +# elif _MSC_VER < 1700 +# define BOOST_COMPILER_VERSION 10.0 +# elif _MSC_VER < 1800 +# define BOOST_COMPILER_VERSION 11.0 +# elif _MSC_VER < 1900 +# define BOOST_COMPILER_VERSION 12.0 +# elif _MSC_VER < 1910 +# define BOOST_COMPILER_VERSION 14.0 +# elif _MSC_VER < 1920 +# define BOOST_COMPILER_VERSION 14.1 +# elif _MSC_VER < 1930 +# define BOOST_COMPILER_VERSION 14.2 +# elif _MSC_VER < 1940 +# define BOOST_COMPILER_VERSION 14.3 +# else +# define BOOST_COMPILER_VERSION _MSC_VER +# endif +# endif + +# define BOOST_COMPILER "Microsoft Visual C++ version " BOOST_STRINGIZE(BOOST_COMPILER_VERSION) +#endif + +#include + +// +// last known and checked version is 19.3x (VS2022): +#if (_MSC_VER >= 1940) +# if defined(BOOST_ASSERT_CONFIG) +# error "Boost.Config is older than your current compiler version." +# elif !defined(BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE) + // + // Disabled as of March 2018 - the pace of VS releases is hard to keep up with + // and in any case, we have relatively few defect macros defined now. + // BOOST_PRAGMA_MESSAGE("Info: Boost.Config is older than your compiler version - probably nothing bad will happen - but you may wish to look for an updated Boost version. Define BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE to suppress this message.") +# endif +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp.hpp new file mode 100644 index 0000000..99b8b24 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp.hpp @@ -0,0 +1,299 @@ +// (C) Copyright Douglas Gregor 2010 +// +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// compiler setup for IBM XL C/C++ for Linux (Little Endian) based on clang. + +#define BOOST_HAS_PRAGMA_ONCE + +// Detecting `-fms-extension` compiler flag assuming that _MSC_VER defined when that flag is used. +#if defined (_MSC_VER) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 4)) +# define BOOST_HAS_PRAGMA_DETECT_MISMATCH +#endif + +// When compiling with clang before __has_extension was defined, +// even if one writes 'defined(__has_extension) && __has_extension(xxx)', +// clang reports a compiler error. So the only workaround found is: + +#ifndef __has_extension +#define __has_extension __has_feature +#endif + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif + +#if !__has_feature(cxx_exceptions) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + +#if !__has_feature(cxx_rtti) && !defined(BOOST_NO_RTTI) +# define BOOST_NO_RTTI +#endif + +#if !__has_feature(cxx_rtti) && !defined(BOOST_NO_TYPEID) +# define BOOST_NO_TYPEID +#endif + +#if defined(__int64) && !defined(__GNUC__) +# define BOOST_HAS_MS_INT64 +#endif + +#define BOOST_HAS_NRVO + +// Branch prediction hints +#if defined(__has_builtin) +#if __has_builtin(__builtin_expect) +#define BOOST_LIKELY(x) __builtin_expect(x, 1) +#define BOOST_UNLIKELY(x) __builtin_expect(x, 0) +#endif +#endif + +// Clang supports "long long" in all compilation modes. +#define BOOST_HAS_LONG_LONG + +// +// Dynamic shared object (DSO) and dynamic-link library (DLL) support +// +#if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) +# define BOOST_SYMBOL_EXPORT __attribute__((__visibility__("default"))) +# define BOOST_SYMBOL_IMPORT +# define BOOST_SYMBOL_VISIBLE __attribute__((__visibility__("default"))) +#endif + +// +// The BOOST_FALLTHROUGH macro can be used to annotate implicit fall-through +// between switch labels. +// +#if __cplusplus >= 201103L && defined(__has_warning) +# if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +# define BOOST_FALLTHROUGH [[clang::fallthrough]] +# endif +#endif + +#if !__has_feature(cxx_auto_type) +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +#endif + +// +// Currently clang on Windows using VC++ RTL does not support C++11's char16_t or char32_t +// +#if defined(_MSC_VER) || !(defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +#endif + +#if !__has_feature(cxx_constexpr) +# define BOOST_NO_CXX11_CONSTEXPR +#endif + +#if !__has_feature(cxx_decltype) +# define BOOST_NO_CXX11_DECLTYPE +#endif + +#if !__has_feature(cxx_decltype_incomplete_return_types) +# define BOOST_NO_CXX11_DECLTYPE_N3276 +#endif + +#if !__has_feature(cxx_defaulted_functions) +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +#endif + +#if !__has_feature(cxx_deleted_functions) +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +#endif + +#if !__has_feature(cxx_explicit_conversions) +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#endif + +#if !__has_feature(cxx_default_function_template_args) +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#endif + +#if !__has_feature(cxx_generalized_initializers) +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +#endif + +#if !__has_feature(cxx_lambdas) +# define BOOST_NO_CXX11_LAMBDAS +#endif + +#if !__has_feature(cxx_local_type_template_args) +# define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +#endif + +#if !__has_feature(cxx_noexcept) +# define BOOST_NO_CXX11_NOEXCEPT +#endif + +#if !__has_feature(cxx_nullptr) +# define BOOST_NO_CXX11_NULLPTR +#endif + +#if !__has_feature(cxx_range_for) +# define BOOST_NO_CXX11_RANGE_BASED_FOR +#endif + +#if !__has_feature(cxx_raw_string_literals) +# define BOOST_NO_CXX11_RAW_LITERALS +#endif + +#if !__has_feature(cxx_reference_qualified_functions) +# define BOOST_NO_CXX11_REF_QUALIFIERS +#endif + +#if !__has_feature(cxx_generalized_initializers) +# define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#endif + +#if !__has_feature(cxx_rvalue_references) +# define BOOST_NO_CXX11_RVALUE_REFERENCES +#endif + +#if !__has_feature(cxx_strong_enums) +# define BOOST_NO_CXX11_SCOPED_ENUMS +#endif + +#if !__has_feature(cxx_static_assert) +# define BOOST_NO_CXX11_STATIC_ASSERT +#endif + +#if !__has_feature(cxx_alias_templates) +# define BOOST_NO_CXX11_TEMPLATE_ALIASES +#endif + +#if !__has_feature(cxx_unicode_literals) +# define BOOST_NO_CXX11_UNICODE_LITERALS +#endif + +#if !__has_feature(cxx_variadic_templates) +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#endif + +#if !__has_feature(cxx_user_literals) +# define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#endif + +#if !__has_feature(cxx_alignas) +# define BOOST_NO_CXX11_ALIGNAS +#endif + +#if !__has_feature(cxx_alignof) +# define BOOST_NO_CXX11_ALIGNOF +#endif + +#if !__has_feature(cxx_trailing_return) +# define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#endif + +#if !__has_feature(cxx_inline_namespaces) +# define BOOST_NO_CXX11_INLINE_NAMESPACES +#endif + +#if !__has_feature(cxx_override_control) +# define BOOST_NO_CXX11_FINAL +# define BOOST_NO_CXX11_OVERRIDE +#endif + +#if !__has_feature(cxx_unrestricted_unions) +# define BOOST_NO_CXX11_UNRESTRICTED_UNION +#endif + +#if !(__has_feature(__cxx_binary_literals__) || __has_extension(__cxx_binary_literals__)) +# define BOOST_NO_CXX14_BINARY_LITERALS +#endif + +#if !__has_feature(__cxx_decltype_auto__) +# define BOOST_NO_CXX14_DECLTYPE_AUTO +#endif + +#if !__has_feature(__cxx_aggregate_nsdmi__) +# define BOOST_NO_CXX14_AGGREGATE_NSDMI +#endif + +#if !__has_feature(__cxx_init_captures__) +# define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#endif + +#if !__has_feature(__cxx_generic_lambdas__) +# define BOOST_NO_CXX14_GENERIC_LAMBDAS +#endif + +// clang < 3.5 has a defect with dependent type, like following. +// +// template +// constexpr typename enable_if >::type foo(T &) +// { } // error: no return statement in constexpr function +// +// This issue also affects C++11 mode, but C++11 constexpr requires return stmt. +// Therefore we don't care such case. +// +// Note that we can't check Clang version directly as the numbering system changes depending who's +// creating the Clang release (see https://github.com/boostorg/config/pull/39#issuecomment-59927873) +// so instead verify that we have a feature that was introduced at the same time as working C++14 +// constexpr (generic lambda's in this case): +// +#if !__has_feature(__cxx_generic_lambdas__) || !__has_feature(__cxx_relaxed_constexpr__) +# define BOOST_NO_CXX14_CONSTEXPR +#endif + +#if !__has_feature(__cxx_return_type_deduction__) +# define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#endif + +#if !__has_feature(__cxx_variable_templates__) +# define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#endif + +#if !defined(__cpp_structured_bindings) || (__cpp_structured_bindings < 201606) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif + +#if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606) +# define BOOST_NO_CXX17_IF_CONSTEXPR +#endif + +// Clang 3.9+ in c++1z +#if !__has_cpp_attribute(fallthrough) || __cplusplus < 201406L +# define BOOST_NO_CXX17_INLINE_VARIABLES +# define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#endif + +#if !__has_feature(cxx_thread_local) +# define BOOST_NO_CXX11_THREAD_LOCAL +#endif + +#if __cplusplus < 201400 +// All versions with __cplusplus above this value seem to support this: +# define BOOST_NO_CXX14_DIGIT_SEPARATORS +#endif + +// Deprecated symbol markup +#if __has_attribute(deprecated) +#define BOOST_DEPRECATED(msg) __attribute__((deprecated(msg))) +#endif + +// Unused attribute: +#if defined(__GNUC__) && (__GNUC__ >= 4) +# define BOOST_ATTRIBUTE_UNUSED __attribute__((unused)) +#endif + +// Type aliasing hint. +#if __has_attribute(__may_alias__) +# define BOOST_MAY_ALIAS __attribute__((__may_alias__)) +#endif + +#ifndef BOOST_COMPILER +# define BOOST_COMPILER "Clang version " __clang_version__ +#endif + +// Macro used to identify the Clang compiler. +#define BOOST_CLANG 1 + +#define BOOST_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp_zos.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp_zos.hpp new file mode 100644 index 0000000..9a177f1 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/compiler/xlcpp_zos.hpp @@ -0,0 +1,173 @@ +// Copyright (c) 2017 Dynatrace +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +// See http://www.boost.org for most recent version. + +// Compiler setup for IBM z/OS XL C/C++ compiler. + +// Oldest compiler version currently supported is 2.1 (V2R1) +#if !defined(__IBMCPP__) || !defined(__COMPILER_VER__) || __COMPILER_VER__ < 0x42010000 +# error "Compiler not supported or configured - please reconfigure" +#endif + +#if __COMPILER_VER__ > 0x42010000 +# if defined(BOOST_ASSERT_CONFIG) +# error "Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + +#define BOOST_COMPILER "IBM z/OS XL C/C++ version " BOOST_STRINGIZE(__COMPILER_VER__) +#define BOOST_XLCPP_ZOS __COMPILER_VER__ + +// ------------------------------------- + +#include // For __UU, __C99, __TR1, ... + +#if !defined(__IBMCPP_DEFAULTED_AND_DELETED_FUNCTIONS) +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +# define BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS +#endif + +// ------------------------------------- + +#if defined(__UU) || defined(__C99) || defined(__TR1) +# define BOOST_HAS_LOG1P +# define BOOST_HAS_EXPM1 +#endif + +#if defined(__C99) || defined(__TR1) +# define BOOST_HAS_STDINT_H +#else +# define BOOST_NO_FENV_H +#endif + +// ------------------------------------- + +#define BOOST_HAS_NRVO + +#if !defined(__RTTI_ALL__) +# define BOOST_NO_RTTI +#endif + +#if !defined(_CPPUNWIND) && !defined(__EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + +#if defined(_LONG_LONG) || defined(__IBMCPP_C99_LONG_LONG) || defined(__LL) +# define BOOST_HAS_LONG_LONG +#else +# define BOOST_NO_LONG_LONG +#endif + +#if defined(_LONG_LONG) || defined(__IBMCPP_C99_LONG_LONG) || defined(__LL) || defined(_LP64) +# define BOOST_HAS_MS_INT64 +#endif + +#define BOOST_NO_SFINAE_EXPR +#define BOOST_NO_CXX11_SFINAE_EXPR + +#if defined(__IBMCPP_VARIADIC_TEMPLATES) +# define BOOST_HAS_VARIADIC_TMPL +#else +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +#endif + +#if defined(__IBMCPP_STATIC_ASSERT) +# define BOOST_HAS_STATIC_ASSERT +#else +# define BOOST_NO_CXX11_STATIC_ASSERT +#endif + +#if defined(__IBMCPP_RVALUE_REFERENCES) +# define BOOST_HAS_RVALUE_REFS +#else +# define BOOST_NO_CXX11_RVALUE_REFERENCES +#endif + +#if !defined(__IBMCPP_SCOPED_ENUM) +# define BOOST_NO_CXX11_SCOPED_ENUMS +#endif + +#define BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS +#define BOOST_NO_CXX11_TEMPLATE_ALIASES +#define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS + +#if !defined(__IBMCPP_EXPLICIT_CONVERSION_OPERATORS) +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +#endif + +#if !defined(__IBMCPP_DECLTYPE) +# define BOOST_NO_CXX11_DECLTYPE +#else +# define BOOST_HAS_DECLTYPE +#endif +#define BOOST_NO_CXX11_DECLTYPE_N3276 + +#if !defined(__IBMCPP_INLINE_NAMESPACE) +# define BOOST_NO_CXX11_INLINE_NAMESPACES +#endif + +#if !defined(__IBMCPP_AUTO_TYPEDEDUCTION) +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_TRAILING_RESULT_TYPES +#endif + +#if !defined(__IBM_CHAR32_T__) +# define BOOST_NO_CXX11_CHAR32_T +#endif +#if !defined(__IBM_CHAR16_T__) +# define BOOST_NO_CXX11_CHAR16_T +#endif + +#if !defined(__IBMCPP_CONSTEXPR) +# define BOOST_NO_CXX11_CONSTEXPR +#endif + +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#define BOOST_NO_CXX11_UNICODE_LITERALS +#define BOOST_NO_CXX11_RAW_LITERALS +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_LAMBDAS +#define BOOST_NO_CXX11_USER_DEFINED_LITERALS +#define BOOST_NO_CXX11_THREAD_LOCAL +#define BOOST_NO_CXX11_REF_QUALIFIERS +#define BOOST_NO_CXX11_FINAL +#define BOOST_NO_CXX11_OVERRIDE +#define BOOST_NO_CXX11_ALIGNAS +#define BOOST_NO_CXX11_ALIGNOF +#define BOOST_NO_CXX11_UNRESTRICTED_UNION +#define BOOST_NO_CXX14_VARIABLE_TEMPLATES +#define BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION +#define BOOST_NO_CXX14_AGGREGATE_NSDMI +#define BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES +#define BOOST_NO_CXX14_GENERIC_LAMBDAS +#define BOOST_NO_CXX14_DIGIT_SEPARATORS +#define BOOST_NO_CXX14_DECLTYPE_AUTO +#define BOOST_NO_CXX14_CONSTEXPR +#define BOOST_NO_CXX14_BINARY_LITERALS +#define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#define BOOST_NO_CXX17_INLINE_VARIABLES +#define BOOST_NO_CXX17_FOLD_EXPRESSIONS +#define BOOST_NO_CXX17_IF_CONSTEXPR + +// ------------------------------------- + +#if defined(__IBM_ATTRIBUTES) +# define BOOST_FORCEINLINE inline __attribute__ ((__always_inline__)) +# define BOOST_NOINLINE __attribute__ ((__noinline__)) +# define BOOST_MAY_ALIAS __attribute__((__may_alias__)) +// No BOOST_ALIGNMENT - explicit alignment support is broken (V2R1). +#endif + +extern "builtin" long __builtin_expect(long, long); + +#define BOOST_LIKELY(x) __builtin_expect((x) && true, 1) +#define BOOST_UNLIKELY(x) __builtin_expect((x) && true, 0) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/cxx_composite.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/cxx_composite.hpp new file mode 100644 index 0000000..9c2c01e --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/cxx_composite.hpp @@ -0,0 +1,217 @@ +// This file was automatically generated on Fri Oct 13 19:09:38 2023 +// by libs/config/tools/generate.cpp +// Copyright John Maddock 2002-21. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for the most recent version.// +// Revision $Id$ +// + +#if defined(BOOST_NO_ADL_BARRIER)\ + || defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)\ + || defined(BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS)\ + || defined(BOOST_NO_COMPLETE_VALUE_INITIALIZATION)\ + || defined(BOOST_NO_CTYPE_FUNCTIONS)\ + || defined(BOOST_NO_CV_SPECIALIZATIONS)\ + || defined(BOOST_NO_CV_VOID_SPECIALIZATIONS)\ + || defined(BOOST_NO_CWCHAR)\ + || defined(BOOST_NO_CWCTYPE)\ + || defined(BOOST_NO_DEPENDENT_NESTED_DERIVATIONS)\ + || defined(BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS)\ + || defined(BOOST_NO_EXCEPTIONS)\ + || defined(BOOST_NO_EXCEPTION_STD_NAMESPACE)\ + || defined(BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS)\ + || defined(BOOST_NO_FENV_H)\ + || defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)\ + || defined(BOOST_NO_FUNCTION_TYPE_SPECIALIZATIONS)\ + || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)\ + || defined(BOOST_NO_INTEGRAL_INT64_T)\ + || defined(BOOST_NO_INTRINSIC_WCHAR_T)\ + || defined(BOOST_NO_IOSFWD)\ + || defined(BOOST_NO_IOSTREAM)\ + || defined(BOOST_NO_IS_ABSTRACT)\ + || defined(BOOST_NO_LIMITS)\ + || defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS)\ + || defined(BOOST_NO_LONG_LONG)\ + || defined(BOOST_NO_LONG_LONG_NUMERIC_LIMITS)\ + || defined(BOOST_NO_MEMBER_FUNCTION_SPECIALIZATIONS)\ + || defined(BOOST_NO_MEMBER_TEMPLATES)\ + || defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)\ + || defined(BOOST_NO_MEMBER_TEMPLATE_KEYWORD)\ + || defined(BOOST_NO_NESTED_FRIENDSHIP)\ + || defined(BOOST_NO_OPERATORS_IN_NAMESPACE)\ + || defined(BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS)\ + || defined(BOOST_NO_POINTER_TO_MEMBER_CONST)\ + || defined(BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS)\ + || defined(BOOST_NO_PRIVATE_IN_AGGREGATE)\ + || defined(BOOST_NO_RESTRICT_REFERENCES)\ + || defined(BOOST_NO_RTTI)\ + || defined(BOOST_NO_SFINAE)\ + || defined(BOOST_NO_SFINAE_EXPR)\ + || defined(BOOST_NO_STDC_NAMESPACE)\ + || defined(BOOST_NO_STD_ALLOCATOR)\ + || defined(BOOST_NO_STD_DISTANCE)\ + || defined(BOOST_NO_STD_ITERATOR)\ + || defined(BOOST_NO_STD_ITERATOR_TRAITS)\ + || defined(BOOST_NO_STD_LOCALE)\ + || defined(BOOST_NO_STD_MESSAGES)\ + || defined(BOOST_NO_STD_MIN_MAX)\ + || defined(BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN)\ + || defined(BOOST_NO_STD_TYPEINFO)\ + || defined(BOOST_NO_STD_USE_FACET)\ + || defined(BOOST_NO_STD_WSTREAMBUF)\ + || defined(BOOST_NO_STD_WSTRING)\ + || defined(BOOST_NO_STRINGSTREAM)\ + || defined(BOOST_NO_TEMPLATED_IOSTREAMS)\ + || defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\ + || defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)\ + || defined(BOOST_NO_TEMPLATE_TEMPLATES)\ + || defined(BOOST_NO_TWO_PHASE_NAME_LOOKUP)\ + || defined(BOOST_NO_TYPEID)\ + || defined(BOOST_NO_TYPENAME_WITH_CTOR)\ + || defined(BOOST_NO_UNREACHABLE_RETURN_DETECTION)\ + || defined(BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE)\ + || defined(BOOST_NO_USING_TEMPLATE)\ + || defined(BOOST_NO_VOID_RETURNS) +# define BOOST_NO_CXX03 +#endif + +#if defined(BOOST_NO_CXX03)\ + || defined(BOOST_NO_CXX11_ADDRESSOF)\ + || defined(BOOST_NO_CXX11_ALIGNAS)\ + || defined(BOOST_NO_CXX11_ALIGNOF)\ + || defined(BOOST_NO_CXX11_ALLOCATOR)\ + || defined(BOOST_NO_CXX11_AUTO_DECLARATIONS)\ + || defined(BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS)\ + || defined(BOOST_NO_CXX11_CHAR16_T)\ + || defined(BOOST_NO_CXX11_CHAR32_T)\ + || defined(BOOST_NO_CXX11_CONSTEXPR)\ + || defined(BOOST_NO_CXX11_DECLTYPE)\ + || defined(BOOST_NO_CXX11_DECLTYPE_N3276)\ + || defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)\ + || defined(BOOST_NO_CXX11_DEFAULTED_MOVES)\ + || defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)\ + || defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS)\ + || defined(BOOST_NO_CXX11_EXTERN_TEMPLATE)\ + || defined(BOOST_NO_CXX11_FINAL)\ + || defined(BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS)\ + || defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS)\ + || defined(BOOST_NO_CXX11_HDR_ARRAY)\ + || defined(BOOST_NO_CXX11_HDR_ATOMIC)\ + || defined(BOOST_NO_CXX11_HDR_CHRONO)\ + || defined(BOOST_NO_CXX11_HDR_CONDITION_VARIABLE)\ + || defined(BOOST_NO_CXX11_HDR_EXCEPTION)\ + || defined(BOOST_NO_CXX11_HDR_FORWARD_LIST)\ + || defined(BOOST_NO_CXX11_HDR_FUNCTIONAL)\ + || defined(BOOST_NO_CXX11_HDR_FUTURE)\ + || defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)\ + || defined(BOOST_NO_CXX11_HDR_MUTEX)\ + || defined(BOOST_NO_CXX11_HDR_RANDOM)\ + || defined(BOOST_NO_CXX11_HDR_RATIO)\ + || defined(BOOST_NO_CXX11_HDR_REGEX)\ + || defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR)\ + || defined(BOOST_NO_CXX11_HDR_THREAD)\ + || defined(BOOST_NO_CXX11_HDR_TUPLE)\ + || defined(BOOST_NO_CXX11_HDR_TYPEINDEX)\ + || defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS)\ + || defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP)\ + || defined(BOOST_NO_CXX11_HDR_UNORDERED_SET)\ + || defined(BOOST_NO_CXX11_INLINE_NAMESPACES)\ + || defined(BOOST_NO_CXX11_LAMBDAS)\ + || defined(BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS)\ + || defined(BOOST_NO_CXX11_NOEXCEPT)\ + || defined(BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS)\ + || defined(BOOST_NO_CXX11_NULLPTR)\ + || defined(BOOST_NO_CXX11_NUMERIC_LIMITS)\ + || defined(BOOST_NO_CXX11_OVERRIDE)\ + || defined(BOOST_NO_CXX11_POINTER_TRAITS)\ + || defined(BOOST_NO_CXX11_RANGE_BASED_FOR)\ + || defined(BOOST_NO_CXX11_RAW_LITERALS)\ + || defined(BOOST_NO_CXX11_REF_QUALIFIERS)\ + || defined(BOOST_NO_CXX11_RVALUE_REFERENCES)\ + || defined(BOOST_NO_CXX11_SCOPED_ENUMS)\ + || defined(BOOST_NO_CXX11_SFINAE_EXPR)\ + || defined(BOOST_NO_CXX11_SMART_PTR)\ + || defined(BOOST_NO_CXX11_STATIC_ASSERT)\ + || defined(BOOST_NO_CXX11_STD_ALIGN)\ + || defined(BOOST_NO_CXX11_TEMPLATE_ALIASES)\ + || defined(BOOST_NO_CXX11_THREAD_LOCAL)\ + || defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)\ + || defined(BOOST_NO_CXX11_UNICODE_LITERALS)\ + || defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)\ + || defined(BOOST_NO_CXX11_UNRESTRICTED_UNION)\ + || defined(BOOST_NO_CXX11_USER_DEFINED_LITERALS)\ + || defined(BOOST_NO_CXX11_VARIADIC_MACROS)\ + || defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +# define BOOST_NO_CXX11 +#endif + +#if defined(BOOST_NO_CXX11)\ + || defined(BOOST_NO_CXX14_AGGREGATE_NSDMI)\ + || defined(BOOST_NO_CXX14_BINARY_LITERALS)\ + || defined(BOOST_NO_CXX14_CONSTEXPR)\ + || defined(BOOST_NO_CXX14_DECLTYPE_AUTO)\ + || defined(BOOST_NO_CXX14_DIGIT_SEPARATORS)\ + || defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)\ + || defined(BOOST_NO_CXX14_HDR_SHARED_MUTEX)\ + || defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES)\ + || defined(BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION)\ + || defined(BOOST_NO_CXX14_STD_EXCHANGE)\ + || defined(BOOST_NO_CXX14_VARIABLE_TEMPLATES) +# define BOOST_NO_CXX14 +#endif + +#if defined(BOOST_NO_CXX14)\ + || defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)\ + || defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS)\ + || defined(BOOST_NO_CXX17_HDR_ANY)\ + || defined(BOOST_NO_CXX17_HDR_CHARCONV)\ + || defined(BOOST_NO_CXX17_HDR_EXECUTION)\ + || defined(BOOST_NO_CXX17_HDR_FILESYSTEM)\ + || defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE)\ + || defined(BOOST_NO_CXX17_HDR_OPTIONAL)\ + || defined(BOOST_NO_CXX17_HDR_STRING_VIEW)\ + || defined(BOOST_NO_CXX17_HDR_VARIANT)\ + || defined(BOOST_NO_CXX17_IF_CONSTEXPR)\ + || defined(BOOST_NO_CXX17_INLINE_VARIABLES)\ + || defined(BOOST_NO_CXX17_ITERATOR_TRAITS)\ + || defined(BOOST_NO_CXX17_STD_APPLY)\ + || defined(BOOST_NO_CXX17_STD_INVOKE)\ + || defined(BOOST_NO_CXX17_STRUCTURED_BINDINGS) +# define BOOST_NO_CXX17 +#endif + +#if defined(BOOST_NO_CXX17)\ + || defined(BOOST_NO_CXX20_HDR_BARRIER)\ + || defined(BOOST_NO_CXX20_HDR_BIT)\ + || defined(BOOST_NO_CXX20_HDR_COMPARE)\ + || defined(BOOST_NO_CXX20_HDR_CONCEPTS)\ + || defined(BOOST_NO_CXX20_HDR_COROUTINE)\ + || defined(BOOST_NO_CXX20_HDR_FORMAT)\ + || defined(BOOST_NO_CXX20_HDR_LATCH)\ + || defined(BOOST_NO_CXX20_HDR_NUMBERS)\ + || defined(BOOST_NO_CXX20_HDR_RANGES)\ + || defined(BOOST_NO_CXX20_HDR_SEMAPHORE)\ + || defined(BOOST_NO_CXX20_HDR_SOURCE_LOCATION)\ + || defined(BOOST_NO_CXX20_HDR_SPAN)\ + || defined(BOOST_NO_CXX20_HDR_STOP_TOKEN)\ + || defined(BOOST_NO_CXX20_HDR_SYNCSTREAM)\ + || defined(BOOST_NO_CXX20_HDR_VERSION) +# define BOOST_NO_CXX20 +#endif + +#if defined(BOOST_NO_CXX20)\ + || defined(BOOST_NO_CXX23_HDR_EXPECTED)\ + || defined(BOOST_NO_CXX23_HDR_FLAT_MAP)\ + || defined(BOOST_NO_CXX23_HDR_FLAT_SET)\ + || defined(BOOST_NO_CXX23_HDR_GENERATOR)\ + || defined(BOOST_NO_CXX23_HDR_MDSPAN)\ + || defined(BOOST_NO_CXX23_HDR_PRINT)\ + || defined(BOOST_NO_CXX23_HDR_SPANSTREAM)\ + || defined(BOOST_NO_CXX23_HDR_STACKTRACE)\ + || defined(BOOST_NO_CXX23_HDR_STDFLOAT) +# define BOOST_NO_CXX23 +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/posix_features.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/posix_features.hpp new file mode 100644 index 0000000..d129547 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/posix_features.hpp @@ -0,0 +1,95 @@ +// (C) Copyright John Maddock 2001 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// See http://www.boost.org for most recent version. + +// All POSIX feature tests go in this file, +// Note that we test _POSIX_C_SOURCE and _XOPEN_SOURCE as well +// _POSIX_VERSION and _XOPEN_VERSION: on some systems POSIX API's +// may be present but none-functional unless _POSIX_C_SOURCE and +// _XOPEN_SOURCE have been defined to the right value (it's up +// to the user to do this *before* including any header, although +// in most cases the compiler will do this for you). + +# if defined(BOOST_HAS_UNISTD_H) +# include + + // XOpen has , but is this the correct version check? +# if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 3) +# define BOOST_HAS_NL_TYPES_H +# endif + + // POSIX version 6 requires +# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200100) +# define BOOST_HAS_STDINT_H +# endif + + // POSIX version 2 requires +# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 199009L) +# define BOOST_HAS_DIRENT_H +# endif + + // POSIX version 3 requires to have sigaction: +# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 199506L) +# define BOOST_HAS_SIGACTION +# endif + // POSIX defines _POSIX_THREADS > 0 for pthread support, + // however some platforms define _POSIX_THREADS without + // a value, hence the (_POSIX_THREADS+0 >= 0) check. + // Strictly speaking this may catch platforms with a + // non-functioning stub , but such occurrences should + // occur very rarely if at all. +# if defined(_POSIX_THREADS) && (_POSIX_THREADS+0 >= 0) && !defined(BOOST_HAS_WINTHREADS) && !defined(BOOST_HAS_MPTASKS) +# define BOOST_HAS_PTHREADS +# endif + + // BOOST_HAS_NANOSLEEP: + // This is predicated on _POSIX_TIMERS or _XOPEN_REALTIME: +# if (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0 >= 0)) \ + || (defined(_XOPEN_REALTIME) && (_XOPEN_REALTIME+0 >= 0)) +# define BOOST_HAS_NANOSLEEP +# endif + + // BOOST_HAS_CLOCK_GETTIME: + // This is predicated on _POSIX_TIMERS (also on _XOPEN_REALTIME + // but at least one platform - linux - defines that flag without + // defining clock_gettime): +# if (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0 >= 0)) +# define BOOST_HAS_CLOCK_GETTIME +# endif + + // BOOST_HAS_SCHED_YIELD: + // This is predicated on _POSIX_PRIORITY_SCHEDULING or + // on _POSIX_THREAD_PRIORITY_SCHEDULING or on _XOPEN_REALTIME. +# if defined(_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING+0 > 0)\ + || (defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING+0 > 0))\ + || (defined(_XOPEN_REALTIME) && (_XOPEN_REALTIME+0 >= 0)) +# define BOOST_HAS_SCHED_YIELD +# endif + + // BOOST_HAS_GETTIMEOFDAY: + // BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE: + // These are predicated on _XOPEN_VERSION, and appears to be first released + // in issue 4, version 2 (_XOPEN_VERSION > 500). + // Likewise for the functions log1p and expm1. +# if defined(_XOPEN_VERSION) && (_XOPEN_VERSION+0 >= 500) +# define BOOST_HAS_GETTIMEOFDAY +# if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE+0 >= 500) +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# endif +# ifndef BOOST_HAS_LOG1P +# define BOOST_HAS_LOG1P +# endif +# ifndef BOOST_HAS_EXPM1 +# define BOOST_HAS_EXPM1 +# endif +# endif + +# endif + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_compiler_config.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_compiler_config.hpp new file mode 100644 index 0000000..c3d99e1 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_compiler_config.hpp @@ -0,0 +1,157 @@ +// Boost compiler configuration selection header file + +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Martin Wille 2003. +// (C) Copyright Guillaume Melquiond 2003. +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/ for most recent version. + +// locate which compiler we are using and define +// BOOST_COMPILER_CONFIG as needed: + +#if defined __CUDACC__ +// NVIDIA CUDA C++ compiler for GPU +# include "boost/config/compiler/nvcc.hpp" + +#endif + +#if defined(__GCCXML__) +// GCC-XML emulates other compilers, it has to appear first here! +# define BOOST_COMPILER_CONFIG "boost/config/compiler/gcc_xml.hpp" + +#elif defined(_CRAYC) +// EDG based Cray compiler: +# define BOOST_COMPILER_CONFIG "boost/config/compiler/cray.hpp" + +#elif defined __COMO__ +// Comeau C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/comeau.hpp" + +#elif defined(__PATHSCALE__) && (__PATHCC__ >= 4) +// PathScale EKOPath compiler (has to come before clang and gcc) +# define BOOST_COMPILER_CONFIG "boost/config/compiler/pathscale.hpp" + +#elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) +// Intel +# define BOOST_COMPILER_CONFIG "boost/config/compiler/intel.hpp" + +#elif defined __clang__ && !defined(__ibmxl__) && !defined(__CODEGEARC__) +// Clang C++ emulates GCC, so it has to appear early. +# define BOOST_COMPILER_CONFIG "boost/config/compiler/clang.hpp" + +#elif defined __DMC__ +// Digital Mars C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/digitalmars.hpp" + +#elif defined __DCC__ +// Wind River Diab C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/diab.hpp" + +#elif defined(__PGI) +// Portland Group Inc. +# define BOOST_COMPILER_CONFIG "boost/config/compiler/pgi.hpp" + +# elif defined(__GNUC__) && !defined(__ibmxl__) +// GNU C++: +# define BOOST_COMPILER_CONFIG "boost/config/compiler/gcc.hpp" + +#elif defined __KCC +// Kai C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/kai.hpp" + +#elif defined __sgi +// SGI MIPSpro C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/sgi_mipspro.hpp" + +#elif defined __DECCXX +// Compaq Tru64 Unix cxx +# define BOOST_COMPILER_CONFIG "boost/config/compiler/compaq_cxx.hpp" + +#elif defined __ghs +// Greenhills C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/greenhills.hpp" + +#elif defined __CODEGEARC__ +// CodeGear - must be checked for before Borland +# define BOOST_COMPILER_CONFIG "boost/config/compiler/codegear.hpp" + +#elif defined __BORLANDC__ +// Borland +# define BOOST_COMPILER_CONFIG "boost/config/compiler/borland.hpp" + +#elif defined __MWERKS__ +// Metrowerks CodeWarrior +# define BOOST_COMPILER_CONFIG "boost/config/compiler/metrowerks.hpp" + +#elif defined __SUNPRO_CC +// Sun Workshop Compiler C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/sunpro_cc.hpp" + +#elif defined __HP_aCC +// HP aCC +# define BOOST_COMPILER_CONFIG "boost/config/compiler/hp_acc.hpp" + +#elif defined(__MRC__) || defined(__SC__) +// MPW MrCpp or SCpp +# define BOOST_COMPILER_CONFIG "boost/config/compiler/mpw.hpp" + +#elif defined(__IBMCPP__) && defined(__COMPILER_VER__) && defined(__MVS__) +// IBM z/OS XL C/C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/xlcpp_zos.hpp" + +#elif defined(__ibmxl__) +// IBM XL C/C++ for Linux (Little Endian) +# define BOOST_COMPILER_CONFIG "boost/config/compiler/xlcpp.hpp" + +#elif defined(__IBMCPP__) +// IBM Visual Age or IBM XL C/C++ for Linux (Big Endian) +# define BOOST_COMPILER_CONFIG "boost/config/compiler/vacpp.hpp" + +#elif defined _MSC_VER +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for +// example) also #define _MSC_VER +# define BOOST_COMPILER_CONFIG "boost/config/compiler/visualc.hpp" + +#elif defined (BOOST_ASSERT_CONFIG) +// this must come last - generate an error if we don't +// recognise the compiler: +# error "Unknown compiler - please configure (http://www.boost.org/libs/config/config.htm#configuring) and report the results to the main boost mailing list (http://www.boost.org/more/mailing_lists.htm#main)" + +#endif + +#if 0 +// +// This section allows dependency scanners to find all the headers we *might* include: +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_platform_config.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_platform_config.hpp new file mode 100644 index 0000000..dbff74a --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_platform_config.hpp @@ -0,0 +1,147 @@ +// Boost compiler configuration selection header file + +// (C) Copyright John Maddock 2001 - 2002. +// (C) Copyright Jens Maurer 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// locate which platform we are on and define BOOST_PLATFORM_CONFIG as needed. +// Note that we define the headers to include using "header_name" not +// in order to prevent macro expansion within the header +// name (for example "linux" is a macro on linux systems). + +#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) +// linux, also other platforms (Hurd etc) that use GLIBC, should these really have their own config headers though? +# define BOOST_PLATFORM_CONFIG "boost/config/platform/linux.hpp" + +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +// BSD: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/bsd.hpp" + +#elif defined(sun) || defined(__sun) +// solaris: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/solaris.hpp" + +#elif defined(__sgi) +// SGI Irix: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/irix.hpp" + +#elif defined(__hpux) +// hp unix: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/hpux.hpp" + +#elif defined(__CYGWIN__) +// cygwin is not win32: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/cygwin.hpp" + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +// win32: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/win32.hpp" + +#elif defined(__HAIKU__) +// Haiku +# define BOOST_PLATFORM_CONFIG "boost/config/platform/haiku.hpp" + +#elif defined(__BEOS__) +// BeOS +# define BOOST_PLATFORM_CONFIG "boost/config/platform/beos.hpp" + +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +// MacOS +# define BOOST_PLATFORM_CONFIG "boost/config/platform/macos.hpp" + +#elif defined(__TOS_MVS__) +// IBM z/OS +# define BOOST_PLATFORM_CONFIG "boost/config/platform/zos.hpp" + +#elif defined(__IBMCPP__) || defined(_AIX) +// IBM AIX +# define BOOST_PLATFORM_CONFIG "boost/config/platform/aix.hpp" + +#elif defined(__amigaos__) +// AmigaOS +# define BOOST_PLATFORM_CONFIG "boost/config/platform/amigaos.hpp" + +#elif defined(__QNXNTO__) +// QNX: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/qnxnto.hpp" + +#elif defined(__VXWORKS__) +// vxWorks: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/vxworks.hpp" + +#elif defined(__SYMBIAN32__) +// Symbian: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/symbian.hpp" + +#elif defined(_CRAYC) +// Cray: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/cray.hpp" + +#elif defined(__VMS) +// VMS: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/vms.hpp" + +#elif defined(__CloudABI__) +// Nuxi CloudABI: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/cloudabi.hpp" + +#elif defined (__wasm__) +// Web assembly: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/wasm.hpp" + +#else + +# if defined(unix) \ + || defined(__unix) \ + || defined(_XOPEN_SOURCE) \ + || defined(_POSIX_SOURCE) + + // generic unix platform: + +# ifndef BOOST_HAS_UNISTD_H +# define BOOST_HAS_UNISTD_H +# endif + +# include + +# endif + +# if defined (BOOST_ASSERT_CONFIG) + // this must come last - generate an error if we don't + // recognise the platform: +# error "Unknown platform - please configure and report the results to boost.org" +# endif + +#endif + +#if 0 +// +// This section allows dependency scanners to find all the files we *might* include: +// +# include "boost/config/platform/linux.hpp" +# include "boost/config/platform/bsd.hpp" +# include "boost/config/platform/solaris.hpp" +# include "boost/config/platform/irix.hpp" +# include "boost/config/platform/hpux.hpp" +# include "boost/config/platform/cygwin.hpp" +# include "boost/config/platform/win32.hpp" +# include "boost/config/platform/beos.hpp" +# include "boost/config/platform/macos.hpp" +# include "boost/config/platform/zos.hpp" +# include "boost/config/platform/aix.hpp" +# include "boost/config/platform/amigaos.hpp" +# include "boost/config/platform/qnxnto.hpp" +# include "boost/config/platform/vxworks.hpp" +# include "boost/config/platform/symbian.hpp" +# include "boost/config/platform/cray.hpp" +# include "boost/config/platform/vms.hpp" +# include + + + +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_stdlib_config.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_stdlib_config.hpp new file mode 100644 index 0000000..1a09dda --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/select_stdlib_config.hpp @@ -0,0 +1,121 @@ +// Boost compiler configuration selection header file + +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001 - 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// See http://www.boost.org for most recent version. + +// locate which std lib we are using and define BOOST_STDLIB_CONFIG as needed: + +// First, check if __has_include is available and include can be located, +// otherwise include to determine if some version of STLport is in use as the std lib +// (do not rely on this header being included since users can short-circuit this header +// if they know whose std lib they are using.) +#if defined(__cplusplus) && defined(__has_include) +# if __has_include() +// It should be safe to include `` when it is present without checking +// the actual C++ language version as it consists solely of macro definitions. +// [version.syn] p1: The header supplies implementation-dependent +// information about the C++ standard library (e.g., version number and release date). +# include +# else +# include +# endif +#elif defined(__cplusplus) +# include +#else +# include +#endif + +#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) +// STLPort library; this _must_ come first, otherwise since +// STLport typically sits on top of some other library, we +// can end up detecting that first rather than STLport: +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/stlport.hpp" + +#else + +// If our std lib was not some version of STLport, and has not otherwise +// been detected, then include as it is about +// the smallest of the std lib headers that includes real C++ stuff. +// Some std libs do not include their C++-related macros in +// so this additional include makes sure we get those definitions. +// Note: do not rely on this header being included since users can short-circuit this +// #include if they know whose std lib they are using. +#if !defined(__LIBCOMO__) && !defined(__STD_RWCOMPILER_H__) && !defined(_RWSTD_VER)\ + && !defined(_LIBCPP_VERSION) && !defined(__GLIBCPP__) && !defined(__GLIBCXX__)\ + && !defined(__STL_CONFIG_H) && !defined(__MSL_CPP__) && !defined(__IBMCPP__)\ + && !defined(MSIPL_COMPILE_H) && !defined(_YVALS) && !defined(_CPPLIB_VER) +#include +#endif + +#if defined(__LIBCOMO__) +// Comeau STL: +#define BOOST_STDLIB_CONFIG "boost/config/stdlib/libcomo.hpp" + +#elif defined(__STD_RWCOMPILER_H__) || defined(_RWSTD_VER) +// Rogue Wave library: +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/roguewave.hpp" + +#elif defined(_LIBCPP_VERSION) +// libc++ +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/libcpp.hpp" + +#elif defined(__GLIBCPP__) || defined(__GLIBCXX__) +// GNU libstdc++ 3 +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/libstdcpp3.hpp" + +#elif defined(__STL_CONFIG_H) +// generic SGI STL +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/sgi.hpp" + +#elif defined(__MSL_CPP__) +// MSL standard lib: +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/msl.hpp" + +#elif defined(__IBMCPP__) && defined(__COMPILER_VER__) && defined(__MVS__) +// IBM z/OS XL C/C++ +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/xlcpp_zos.hpp" + +#elif defined(__IBMCPP__) +// take the default VACPP std lib +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/vacpp.hpp" + +#elif defined(MSIPL_COMPILE_H) +// Modena C++ standard library +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/modena.hpp" + +#elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER) +// Dinkumware Library (this has to appear after any possible replacement libraries): +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/dinkumware.hpp" + +#elif defined (BOOST_ASSERT_CONFIG) +// this must come last - generate an error if we don't +// recognise the library: +# error "Unknown standard library - please configure and report the results to boost.org" + +#endif + +#endif + +#if 0 +// +// This section allows dependency scanners to find all the files we *might* include: +// +# include "boost/config/stdlib/stlport.hpp" +# include "boost/config/stdlib/libcomo.hpp" +# include "boost/config/stdlib/roguewave.hpp" +# include "boost/config/stdlib/libcpp.hpp" +# include "boost/config/stdlib/libstdcpp3.hpp" +# include "boost/config/stdlib/sgi.hpp" +# include "boost/config/stdlib/msl.hpp" +# include "boost/config/stdlib/xlcpp_zos.hpp" +# include "boost/config/stdlib/vacpp.hpp" +# include "boost/config/stdlib/modena.hpp" +# include "boost/config/stdlib/dinkumware.hpp" +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/suffix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/suffix.hpp new file mode 100644 index 0000000..2650510 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/detail/suffix.hpp @@ -0,0 +1,1334 @@ +// Boost config.hpp configuration header file ------------------------------// +// boostinspect:ndprecated_macros -- tell the inspect tool to ignore this file + +// Copyright (c) 2001-2003 John Maddock +// Copyright (c) 2001 Darin Adler +// Copyright (c) 2001 Peter Dimov +// Copyright (c) 2002 Bill Kempf +// Copyright (c) 2002 Jens Maurer +// Copyright (c) 2002-2003 David Abrahams +// Copyright (c) 2003 Gennaro Prota +// Copyright (c) 2003 Eric Friedman +// Copyright (c) 2010 Eric Jourdanneau, Joel Falcou +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/ for most recent version. + +// Boost config.hpp policy and rationale documentation has been moved to +// http://www.boost.org/libs/config/ +// +// This file is intended to be stable, and relatively unchanging. +// It should contain boilerplate code only - no compiler specific +// code unless it is unavoidable - no changes unless unavoidable. + +#ifndef BOOST_CONFIG_SUFFIX_HPP +#define BOOST_CONFIG_SUFFIX_HPP + +#if defined(__GNUC__) && (__GNUC__ >= 4) +// +// Some GCC-4.x versions issue warnings even when __extension__ is used, +// so use this as a workaround: +// +#pragma GCC system_header +#endif + +// +// ensure that visibility macros are always defined, thus simplifying use +// +#ifndef BOOST_SYMBOL_EXPORT +# define BOOST_SYMBOL_EXPORT +#endif +#ifndef BOOST_SYMBOL_IMPORT +# define BOOST_SYMBOL_IMPORT +#endif +#ifndef BOOST_SYMBOL_VISIBLE +# define BOOST_SYMBOL_VISIBLE +#endif + +// +// disable explicitly enforced visibility +// +#if defined(BOOST_DISABLE_EXPLICIT_SYMBOL_VISIBILITY) + +#undef BOOST_SYMBOL_EXPORT +#define BOOST_SYMBOL_EXPORT + +#undef BOOST_SYMBOL_IMPORT +#define BOOST_SYMBOL_IMPORT + +#undef BOOST_SYMBOL_VISIBLE +#define BOOST_SYMBOL_VISIBLE + +#endif + +// +// look for long long by looking for the appropriate macros in . +// Note that we use limits.h rather than climits for maximal portability, +// remember that since these just declare a bunch of macros, there should be +// no namespace issues from this. +// +#if !defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_LONG_LONG) \ + && !defined(BOOST_MSVC) && !defined(BOOST_BORLANDC) +# include +# if (defined(ULLONG_MAX) || defined(ULONG_LONG_MAX) || defined(ULONGLONG_MAX)) +# define BOOST_HAS_LONG_LONG +# else +# define BOOST_NO_LONG_LONG +# endif +#endif + +// GCC 3.x will clean up all of those nasty macro definitions that +// BOOST_NO_CTYPE_FUNCTIONS is intended to help work around, so undefine +// it under GCC 3.x. +#if defined(__GNUC__) && (__GNUC__ >= 3) && defined(BOOST_NO_CTYPE_FUNCTIONS) +# undef BOOST_NO_CTYPE_FUNCTIONS +#endif + +// +// Assume any extensions are in namespace std:: unless stated otherwise: +// +# ifndef BOOST_STD_EXTENSION_NAMESPACE +# define BOOST_STD_EXTENSION_NAMESPACE std +# endif + +// +// If cv-qualified specializations are not allowed, then neither are cv-void ones: +// +# if defined(BOOST_NO_CV_SPECIALIZATIONS) \ + && !defined(BOOST_NO_CV_VOID_SPECIALIZATIONS) +# define BOOST_NO_CV_VOID_SPECIALIZATIONS +# endif + +// +// If there is no numeric_limits template, then it can't have any compile time +// constants either! +// +# if defined(BOOST_NO_LIMITS) \ + && !defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) +# define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +# define BOOST_NO_LONG_LONG_NUMERIC_LIMITS +# endif + +// +// if there is no long long then there is no specialisation +// for numeric_limits either: +// +#if !defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_LONG_LONG_NUMERIC_LIMITS) +# define BOOST_NO_LONG_LONG_NUMERIC_LIMITS +#endif + +// +// if there is no __int64 then there is no specialisation +// for numeric_limits<__int64> either: +// +#if !defined(BOOST_HAS_MS_INT64) && !defined(BOOST_NO_MS_INT64_NUMERIC_LIMITS) +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +#endif + +// +// if member templates are supported then so is the +// VC6 subset of member templates: +// +# if !defined(BOOST_NO_MEMBER_TEMPLATES) \ + && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) +# define BOOST_MSVC6_MEMBER_TEMPLATES +# endif + +// +// Without partial specialization, can't test for partial specialisation bugs: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) +# define BOOST_BCB_PARTIAL_SPECIALIZATION_BUG +# endif + +// +// Without partial specialization, we can't have array-type partial specialisations: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS) +# define BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS +# endif + +// +// Without partial specialization, std::iterator_traits can't work: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_NO_STD_ITERATOR_TRAITS) +# define BOOST_NO_STD_ITERATOR_TRAITS +# endif + +// +// Without partial specialization, partial +// specialization with default args won't work either: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS) +# define BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS +# endif + +// +// Without member template support, we can't have template constructors +// in the standard library either: +// +# if defined(BOOST_NO_MEMBER_TEMPLATES) \ + && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) \ + && !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS) +# define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +# endif + +// +// Without member template support, we can't have a conforming +// std::allocator template either: +// +# if defined(BOOST_NO_MEMBER_TEMPLATES) \ + && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) \ + && !defined(BOOST_NO_STD_ALLOCATOR) +# define BOOST_NO_STD_ALLOCATOR +# endif + +// +// without ADL support then using declarations will break ADL as well: +// +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) && !defined(BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL) +# define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +#endif + +// +// Without typeid support we have no dynamic RTTI either: +// +#if defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI) +# define BOOST_NO_RTTI +#endif + +// +// If we have a standard allocator, then we have a partial one as well: +// +#if !defined(BOOST_NO_STD_ALLOCATOR) +# define BOOST_HAS_PARTIAL_STD_ALLOCATOR +#endif + +// +// We can't have a working std::use_facet if there is no std::locale: +// +# if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_USE_FACET) +# define BOOST_NO_STD_USE_FACET +# endif + +// +// We can't have a std::messages facet if there is no std::locale: +// +# if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_MESSAGES) +# define BOOST_NO_STD_MESSAGES +# endif + +// +// We can't have a working std::wstreambuf if there is no std::locale: +// +# if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_WSTREAMBUF) +# define BOOST_NO_STD_WSTREAMBUF +# endif + +// +// We can't have a if there is no : +// +# if defined(BOOST_NO_CWCHAR) && !defined(BOOST_NO_CWCTYPE) +# define BOOST_NO_CWCTYPE +# endif + +// +// We can't have a swprintf if there is no : +// +# if defined(BOOST_NO_CWCHAR) && !defined(BOOST_NO_SWPRINTF) +# define BOOST_NO_SWPRINTF +# endif + +// +// If Win32 support is turned off, then we must turn off +// threading support also, unless there is some other +// thread API enabled: +// +#if defined(BOOST_DISABLE_WIN32) && defined(_WIN32) \ + && !defined(BOOST_DISABLE_THREADS) && !defined(BOOST_HAS_PTHREADS) +# define BOOST_DISABLE_THREADS +#endif + +// +// Turn on threading support if the compiler thinks that it's in +// multithreaded mode. We put this here because there are only a +// limited number of macros that identify this (if there's any missing +// from here then add to the appropriate compiler section): +// +#if (defined(__MT__) || defined(_MT) || defined(_REENTRANT) \ + || defined(_PTHREADS) || defined(__APPLE__) || defined(__DragonFly__)) \ + && !defined(BOOST_HAS_THREADS) +# define BOOST_HAS_THREADS +#endif + +// +// Turn threading support off if BOOST_DISABLE_THREADS is defined: +// +#if defined(BOOST_DISABLE_THREADS) && defined(BOOST_HAS_THREADS) +# undef BOOST_HAS_THREADS +#endif + +// +// Turn threading support off if we don't recognise the threading API: +// +#if defined(BOOST_HAS_THREADS) && !defined(BOOST_HAS_PTHREADS)\ + && !defined(BOOST_HAS_WINTHREADS) && !defined(BOOST_HAS_BETHREADS)\ + && !defined(BOOST_HAS_MPTASKS) +# undef BOOST_HAS_THREADS +#endif + +// +// Turn threading detail macros off if we don't (want to) use threading +// +#ifndef BOOST_HAS_THREADS +# undef BOOST_HAS_PTHREADS +# undef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# undef BOOST_HAS_PTHREAD_YIELD +# undef BOOST_HAS_PTHREAD_DELAY_NP +# undef BOOST_HAS_WINTHREADS +# undef BOOST_HAS_BETHREADS +# undef BOOST_HAS_MPTASKS +#endif + +// +// If the compiler claims to be C99 conformant, then it had better +// have a : +// +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901) +# define BOOST_HAS_STDINT_H +# ifndef BOOST_HAS_LOG1P +# define BOOST_HAS_LOG1P +# endif +# ifndef BOOST_HAS_EXPM1 +# define BOOST_HAS_EXPM1 +# endif +# endif + +// +// Define BOOST_NO_SLIST and BOOST_NO_HASH if required. +// Note that this is for backwards compatibility only. +// +# if !defined(BOOST_HAS_SLIST) && !defined(BOOST_NO_SLIST) +# define BOOST_NO_SLIST +# endif + +# if !defined(BOOST_HAS_HASH) && !defined(BOOST_NO_HASH) +# define BOOST_NO_HASH +# endif + +// +// Set BOOST_SLIST_HEADER if not set already: +// +#if defined(BOOST_HAS_SLIST) && !defined(BOOST_SLIST_HEADER) +# define BOOST_SLIST_HEADER +#endif + +// +// Set BOOST_HASH_SET_HEADER if not set already: +// +#if defined(BOOST_HAS_HASH) && !defined(BOOST_HASH_SET_HEADER) +# define BOOST_HASH_SET_HEADER +#endif + +// +// Set BOOST_HASH_MAP_HEADER if not set already: +// +#if defined(BOOST_HAS_HASH) && !defined(BOOST_HASH_MAP_HEADER) +# define BOOST_HASH_MAP_HEADER +#endif + +// BOOST_HAS_ABI_HEADERS +// This macro gets set if we have headers that fix the ABI, +// and prevent ODR violations when linking to external libraries: +#if defined(BOOST_ABI_PREFIX) && defined(BOOST_ABI_SUFFIX) && !defined(BOOST_HAS_ABI_HEADERS) +# define BOOST_HAS_ABI_HEADERS +#endif + +#if defined(BOOST_HAS_ABI_HEADERS) && defined(BOOST_DISABLE_ABI_HEADERS) +# undef BOOST_HAS_ABI_HEADERS +#endif + +// BOOST_NO_STDC_NAMESPACE workaround --------------------------------------// +// Because std::size_t usage is so common, even in boost headers which do not +// otherwise use the C library, the workaround is included here so +// that ugly workaround code need not appear in many other boost headers. +// NOTE WELL: This is a workaround for non-conforming compilers; +// must still be #included in the usual places so that inclusion +// works as expected with standard conforming compilers. The resulting +// double inclusion of is harmless. + +# if defined(BOOST_NO_STDC_NAMESPACE) && defined(__cplusplus) +# include + namespace std { using ::ptrdiff_t; using ::size_t; } +# endif + +// Workaround for the unfortunate min/max macros defined by some platform headers + +#define BOOST_PREVENT_MACRO_SUBSTITUTION + +#ifndef BOOST_USING_STD_MIN +# define BOOST_USING_STD_MIN() using std::min +#endif + +#ifndef BOOST_USING_STD_MAX +# define BOOST_USING_STD_MAX() using std::max +#endif + +// BOOST_NO_STD_MIN_MAX workaround -----------------------------------------// + +# if defined(BOOST_NO_STD_MIN_MAX) && defined(__cplusplus) + +namespace std { + template + inline const _Tp& min BOOST_PREVENT_MACRO_SUBSTITUTION (const _Tp& __a, const _Tp& __b) { + return __b < __a ? __b : __a; + } + template + inline const _Tp& max BOOST_PREVENT_MACRO_SUBSTITUTION (const _Tp& __a, const _Tp& __b) { + return __a < __b ? __b : __a; + } +} + +# endif + +// BOOST_STATIC_CONSTANT workaround --------------------------------------- // +// On compilers which don't allow in-class initialization of static integral +// constant members, we must use enums as a workaround if we want the constants +// to be available at compile-time. This macro gives us a convenient way to +// declare such constants. + +# ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# define BOOST_STATIC_CONSTANT(type, assignment) enum { assignment } +# else +# define BOOST_STATIC_CONSTANT(type, assignment) static const type assignment +# endif + +// BOOST_USE_FACET / HAS_FACET workaround ----------------------------------// +// When the standard library does not have a conforming std::use_facet there +// are various workarounds available, but they differ from library to library. +// The same problem occurs with has_facet. +// These macros provide a consistent way to access a locale's facets. +// Usage: +// replace +// std::use_facet(loc); +// with +// BOOST_USE_FACET(Type, loc); +// Note do not add a std:: prefix to the front of BOOST_USE_FACET! +// Use for BOOST_HAS_FACET is analogous. + +#if defined(BOOST_NO_STD_USE_FACET) +# ifdef BOOST_HAS_TWO_ARG_USE_FACET +# define BOOST_USE_FACET(Type, loc) std::use_facet(loc, static_cast(0)) +# define BOOST_HAS_FACET(Type, loc) std::has_facet(loc, static_cast(0)) +# elif defined(BOOST_HAS_MACRO_USE_FACET) +# define BOOST_USE_FACET(Type, loc) std::_USE(loc, Type) +# define BOOST_HAS_FACET(Type, loc) std::_HAS(loc, Type) +# elif defined(BOOST_HAS_STLP_USE_FACET) +# define BOOST_USE_FACET(Type, loc) (*std::_Use_facet(loc)) +# define BOOST_HAS_FACET(Type, loc) std::has_facet< Type >(loc) +# endif +#else +# define BOOST_USE_FACET(Type, loc) std::use_facet< Type >(loc) +# define BOOST_HAS_FACET(Type, loc) std::has_facet< Type >(loc) +#endif + +// BOOST_NESTED_TEMPLATE workaround ------------------------------------------// +// Member templates are supported by some compilers even though they can't use +// the A::template member syntax, as a workaround replace: +// +// typedef typename A::template rebind binder; +// +// with: +// +// typedef typename A::BOOST_NESTED_TEMPLATE rebind binder; + +#ifndef BOOST_NO_MEMBER_TEMPLATE_KEYWORD +# define BOOST_NESTED_TEMPLATE template +#else +# define BOOST_NESTED_TEMPLATE +#endif + +// BOOST_UNREACHABLE_RETURN(x) workaround -------------------------------------// +// Normally evaluates to nothing, unless BOOST_NO_UNREACHABLE_RETURN_DETECTION +// is defined, in which case it evaluates to return x; Use when you have a return +// statement that can never be reached. + +#ifndef BOOST_UNREACHABLE_RETURN +# ifdef BOOST_NO_UNREACHABLE_RETURN_DETECTION +# define BOOST_UNREACHABLE_RETURN(x) return x; +# else +# define BOOST_UNREACHABLE_RETURN(x) +# endif +#endif + +// BOOST_DEDUCED_TYPENAME workaround ------------------------------------------// +// +// Some compilers don't support the use of `typename' for dependent +// types in deduced contexts, e.g. +// +// template void f(T, typename T::type); +// ^^^^^^^^ +// Replace these declarations with: +// +// template void f(T, BOOST_DEDUCED_TYPENAME T::type); + +#ifndef BOOST_NO_DEDUCED_TYPENAME +# define BOOST_DEDUCED_TYPENAME typename +#else +# define BOOST_DEDUCED_TYPENAME +#endif + +#ifndef BOOST_NO_TYPENAME_WITH_CTOR +# define BOOST_CTOR_TYPENAME typename +#else +# define BOOST_CTOR_TYPENAME +#endif + +// +// If we're on a CUDA device (note DEVICE not HOST, irrespective of compiler) then disable __int128 and __float128 support if present: +// +#if defined(__CUDA_ARCH__) && defined(BOOST_HAS_FLOAT128) +# undef BOOST_HAS_FLOAT128 +#endif +#if defined(__CUDA_ARCH__) && defined(BOOST_HAS_INT128) +# undef BOOST_HAS_INT128 +#endif + +// long long workaround ------------------------------------------// +// On gcc (and maybe other compilers?) long long is alway supported +// but it's use may generate either warnings (with -ansi), or errors +// (with -pedantic -ansi) unless it's use is prefixed by __extension__ +// +#if defined(BOOST_HAS_LONG_LONG) && defined(__cplusplus) +namespace boost{ +# ifdef __GNUC__ + __extension__ typedef long long long_long_type; + __extension__ typedef unsigned long long ulong_long_type; +# else + typedef long long long_long_type; + typedef unsigned long long ulong_long_type; +# endif +} +#endif +// same again for __int128: +#if defined(BOOST_HAS_INT128) && defined(__cplusplus) +namespace boost{ +# ifdef __GNUC__ + __extension__ typedef __int128 int128_type; + __extension__ typedef unsigned __int128 uint128_type; +# else + typedef __int128 int128_type; + typedef unsigned __int128 uint128_type; +# endif +} +#endif +// same again for __float128: +#if defined(BOOST_HAS_FLOAT128) && defined(__cplusplus) +namespace boost { +# ifdef __GNUC__ + __extension__ typedef __float128 float128_type; +# else + typedef __float128 float128_type; +# endif +} +#endif + +// BOOST_[APPEND_]EXPLICIT_TEMPLATE_[NON_]TYPE macros --------------------------// + +// These macros are obsolete. Port away and remove. + +# define BOOST_EXPLICIT_TEMPLATE_TYPE(t) +# define BOOST_EXPLICIT_TEMPLATE_TYPE_SPEC(t) +# define BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v) +# define BOOST_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) + +# define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(t) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE_SPEC(t) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(t, v) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) + +// When BOOST_NO_STD_TYPEINFO is defined, we can just import +// the global definition into std namespace, +// see https://svn.boost.org/trac10/ticket/4115 +#if defined(BOOST_NO_STD_TYPEINFO) && defined(__cplusplus) && defined(BOOST_MSVC) +#include +namespace std{ using ::type_info; } +// Since we do now have typeinfo, undef the macro: +#undef BOOST_NO_STD_TYPEINFO +#endif + +// ---------------------------------------------------------------------------// + +// Helper macro BOOST_STRINGIZE: +// Helper macro BOOST_JOIN: + +#include + +// +// Set some default values for compiler/library/platform names. +// These are for debugging config setup only: +// +# ifndef BOOST_COMPILER +# define BOOST_COMPILER "Unknown ISO C++ Compiler" +# endif +# ifndef BOOST_STDLIB +# define BOOST_STDLIB "Unknown ISO standard library" +# endif +# ifndef BOOST_PLATFORM +# if defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) \ + || defined(_POSIX_SOURCE) +# define BOOST_PLATFORM "Generic Unix" +# else +# define BOOST_PLATFORM "Unknown" +# endif +# endif + +// +// Set some default values GPU support +// +# ifndef BOOST_GPU_ENABLED +# define BOOST_GPU_ENABLED +# endif + +// BOOST_RESTRICT ---------------------------------------------// +// Macro to use in place of 'restrict' keyword variants +#if !defined(BOOST_RESTRICT) +# if defined(_MSC_VER) +# define BOOST_RESTRICT __restrict +# if !defined(BOOST_NO_RESTRICT_REFERENCES) && (_MSC_FULL_VER < 190023026) +# define BOOST_NO_RESTRICT_REFERENCES +# endif +# elif defined(__GNUC__) && __GNUC__ > 3 + // Clang also defines __GNUC__ (as 4) +# define BOOST_RESTRICT __restrict__ +# else +# define BOOST_RESTRICT +# if !defined(BOOST_NO_RESTRICT_REFERENCES) +# define BOOST_NO_RESTRICT_REFERENCES +# endif +# endif +#endif + +// BOOST_MAY_ALIAS -----------------------------------------------// +// The macro expands to an attribute to mark a type that is allowed to alias other types. +// The macro is defined in the compiler-specific headers. +#if !defined(BOOST_MAY_ALIAS) +# define BOOST_NO_MAY_ALIAS +# define BOOST_MAY_ALIAS +#endif + +// BOOST_FORCEINLINE ---------------------------------------------// +// Macro to use in place of 'inline' to force a function to be inline +#if !defined(BOOST_FORCEINLINE) +# if defined(_MSC_VER) +# define BOOST_FORCEINLINE __forceinline +# elif defined(__GNUC__) && __GNUC__ > 3 + // Clang also defines __GNUC__ (as 4) +# define BOOST_FORCEINLINE inline __attribute__ ((__always_inline__)) +# else +# define BOOST_FORCEINLINE inline +# endif +#endif + +// BOOST_NOINLINE ---------------------------------------------// +// Macro to use in place of 'inline' to prevent a function to be inlined +#if !defined(BOOST_NOINLINE) +# if defined(_MSC_VER) +# define BOOST_NOINLINE __declspec(noinline) +# elif defined(__GNUC__) && __GNUC__ > 3 + // Clang also defines __GNUC__ (as 4) +# if defined(__CUDACC__) + // nvcc doesn't always parse __noinline__, + // see: https://svn.boost.org/trac/boost/ticket/9392 +# define BOOST_NOINLINE __attribute__ ((noinline)) +# elif defined(__HIP__) + // See https://github.com/boostorg/config/issues/392 +# define BOOST_NOINLINE __attribute__ ((noinline)) +# else +# define BOOST_NOINLINE __attribute__ ((__noinline__)) +# endif +# else +# define BOOST_NOINLINE +# endif +#endif + +// BOOST_NORETURN ---------------------------------------------// +// Macro to use before a function declaration/definition to designate +// the function as not returning normally (i.e. with a return statement +// or by leaving the function scope, if the function return type is void). +#if !defined(BOOST_NORETURN) +# if defined(_MSC_VER) +# define BOOST_NORETURN __declspec(noreturn) +# elif defined(__GNUC__) || defined(__CODEGEARC__) && defined(__clang__) +# define BOOST_NORETURN __attribute__ ((__noreturn__)) +# elif defined(__has_attribute) && defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x5130) +# if __has_attribute(noreturn) +# define BOOST_NORETURN [[noreturn]] +# endif +# elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(noreturn) +# define BOOST_NORETURN [[noreturn]] +# endif +# endif +#endif + +#if !defined(BOOST_NORETURN) +# define BOOST_NO_NORETURN +# define BOOST_NORETURN +#endif + +// BOOST_DEPRECATED -------------------------------------------// +// The macro can be used to mark deprecated symbols, such as functions, objects and types. +// Any code that uses these symbols will produce warnings, possibly with a message specified +// as an argument. The warnings can be suppressed by defining BOOST_ALLOW_DEPRECATED_SYMBOLS +// or BOOST_ALLOW_DEPRECATED. +#if !defined(BOOST_DEPRECATED) && __cplusplus >= 201402 +#define BOOST_DEPRECATED(msg) [[deprecated(msg)]] +#endif + +#if defined(BOOST_ALLOW_DEPRECATED_SYMBOLS) || defined(BOOST_ALLOW_DEPRECATED) +#undef BOOST_DEPRECATED +#endif + +#if !defined(BOOST_DEPRECATED) +#define BOOST_DEPRECATED(msg) +#endif + +// Branch prediction hints +// These macros are intended to wrap conditional expressions that yield true or false +// +// if (BOOST_LIKELY(var == 10)) +// { +// // the most probable code here +// } +// +#if !defined(BOOST_LIKELY) +# define BOOST_LIKELY(x) x +#endif +#if !defined(BOOST_UNLIKELY) +# define BOOST_UNLIKELY(x) x +#endif + +#if !defined(BOOST_NO_CXX11_OVERRIDE) +# define BOOST_OVERRIDE override +#else +# define BOOST_OVERRIDE +#endif + +// Type and data alignment specification +// +#if !defined(BOOST_ALIGNMENT) +# if !defined(BOOST_NO_CXX11_ALIGNAS) +# define BOOST_ALIGNMENT(x) alignas(x) +# elif defined(_MSC_VER) +# define BOOST_ALIGNMENT(x) __declspec(align(x)) +# elif defined(__GNUC__) +# define BOOST_ALIGNMENT(x) __attribute__ ((__aligned__(x))) +# else +# define BOOST_NO_ALIGNMENT +# define BOOST_ALIGNMENT(x) +# endif +#endif + +// Lack of non-public defaulted functions is implied by the lack of any defaulted functions +#if !defined(BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS) && defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) +# define BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS +#endif + +// Lack of defaulted moves is implied by the lack of either rvalue references or any defaulted functions +#if !defined(BOOST_NO_CXX11_DEFAULTED_MOVES) && (defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || defined(BOOST_NO_CXX11_RVALUE_REFERENCES)) +# define BOOST_NO_CXX11_DEFAULTED_MOVES +#endif + +// Defaulted and deleted function declaration helpers +// These macros are intended to be inside a class definition. +// BOOST_DEFAULTED_FUNCTION accepts the function declaration and its +// body, which will be used if the compiler doesn't support defaulted functions. +// BOOST_DELETED_FUNCTION only accepts the function declaration. It +// will expand to a private function declaration, if the compiler doesn't support +// deleted functions. Because of this it is recommended to use BOOST_DELETED_FUNCTION +// in the end of the class definition. +// +// class my_class +// { +// public: +// // Default-constructible +// BOOST_DEFAULTED_FUNCTION(my_class(), {}) +// // Copying prohibited +// BOOST_DELETED_FUNCTION(my_class(my_class const&)) +// BOOST_DELETED_FUNCTION(my_class& operator= (my_class const&)) +// }; +// +#if !(defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || defined(BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS)) +# define BOOST_DEFAULTED_FUNCTION(fun, body) fun = default; +#else +# define BOOST_DEFAULTED_FUNCTION(fun, body) fun body +#endif + +#if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) +# define BOOST_DELETED_FUNCTION(fun) fun = delete; +#else +# define BOOST_DELETED_FUNCTION(fun) private: fun; +#endif + +// +// Set BOOST_NO_DECLTYPE_N3276 when BOOST_NO_DECLTYPE is defined +// +#if defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_CXX11_DECLTYPE_N3276) +#define BOOST_NO_CXX11_DECLTYPE_N3276 BOOST_NO_CXX11_DECLTYPE +#endif + +// -------------------- Deprecated macros for 1.50 --------------------------- +// These will go away in a future release + +// Use BOOST_NO_CXX11_HDR_UNORDERED_SET or BOOST_NO_CXX11_HDR_UNORDERED_MAP +// instead of BOOST_NO_STD_UNORDERED +#if defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) || defined (BOOST_NO_CXX11_HDR_UNORDERED_SET) +# ifndef BOOST_NO_CXX11_STD_UNORDERED +# define BOOST_NO_CXX11_STD_UNORDERED +# endif +#endif + +// Use BOOST_NO_CXX11_HDR_INITIALIZER_LIST instead of BOOST_NO_INITIALIZER_LISTS +#if defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_INITIALIZER_LISTS) +# define BOOST_NO_INITIALIZER_LISTS +#endif + +// Use BOOST_NO_CXX11_HDR_ARRAY instead of BOOST_NO_0X_HDR_ARRAY +#if defined(BOOST_NO_CXX11_HDR_ARRAY) && !defined(BOOST_NO_0X_HDR_ARRAY) +# define BOOST_NO_0X_HDR_ARRAY +#endif +// Use BOOST_NO_CXX11_HDR_CHRONO instead of BOOST_NO_0X_HDR_CHRONO +#if defined(BOOST_NO_CXX11_HDR_CHRONO) && !defined(BOOST_NO_0X_HDR_CHRONO) +# define BOOST_NO_0X_HDR_CHRONO +#endif +// Use BOOST_NO_CXX11_HDR_CODECVT instead of BOOST_NO_0X_HDR_CODECVT +#if defined(BOOST_NO_CXX11_HDR_CODECVT) && !defined(BOOST_NO_0X_HDR_CODECVT) +# define BOOST_NO_0X_HDR_CODECVT +#endif +// Use BOOST_NO_CXX11_HDR_CONDITION_VARIABLE instead of BOOST_NO_0X_HDR_CONDITION_VARIABLE +#if defined(BOOST_NO_CXX11_HDR_CONDITION_VARIABLE) && !defined(BOOST_NO_0X_HDR_CONDITION_VARIABLE) +# define BOOST_NO_0X_HDR_CONDITION_VARIABLE +#endif +// Use BOOST_NO_CXX11_HDR_FORWARD_LIST instead of BOOST_NO_0X_HDR_FORWARD_LIST +#if defined(BOOST_NO_CXX11_HDR_FORWARD_LIST) && !defined(BOOST_NO_0X_HDR_FORWARD_LIST) +# define BOOST_NO_0X_HDR_FORWARD_LIST +#endif +// Use BOOST_NO_CXX11_HDR_FUTURE instead of BOOST_NO_0X_HDR_FUTURE +#if defined(BOOST_NO_CXX11_HDR_FUTURE) && !defined(BOOST_NO_0X_HDR_FUTURE) +# define BOOST_NO_0X_HDR_FUTURE +#endif + +// Use BOOST_NO_CXX11_HDR_INITIALIZER_LIST +// instead of BOOST_NO_0X_HDR_INITIALIZER_LIST or BOOST_NO_INITIALIZER_LISTS +#ifdef BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# ifndef BOOST_NO_0X_HDR_INITIALIZER_LIST +# define BOOST_NO_0X_HDR_INITIALIZER_LIST +# endif +# ifndef BOOST_NO_INITIALIZER_LISTS +# define BOOST_NO_INITIALIZER_LISTS +# endif +#endif + +// Use BOOST_NO_CXX11_HDR_MUTEX instead of BOOST_NO_0X_HDR_MUTEX +#if defined(BOOST_NO_CXX11_HDR_MUTEX) && !defined(BOOST_NO_0X_HDR_MUTEX) +# define BOOST_NO_0X_HDR_MUTEX +#endif +// Use BOOST_NO_CXX11_HDR_RANDOM instead of BOOST_NO_0X_HDR_RANDOM +#if defined(BOOST_NO_CXX11_HDR_RANDOM) && !defined(BOOST_NO_0X_HDR_RANDOM) +# define BOOST_NO_0X_HDR_RANDOM +#endif +// Use BOOST_NO_CXX11_HDR_RATIO instead of BOOST_NO_0X_HDR_RATIO +#if defined(BOOST_NO_CXX11_HDR_RATIO) && !defined(BOOST_NO_0X_HDR_RATIO) +# define BOOST_NO_0X_HDR_RATIO +#endif +// Use BOOST_NO_CXX11_HDR_REGEX instead of BOOST_NO_0X_HDR_REGEX +#if defined(BOOST_NO_CXX11_HDR_REGEX) && !defined(BOOST_NO_0X_HDR_REGEX) +# define BOOST_NO_0X_HDR_REGEX +#endif +// Use BOOST_NO_CXX11_HDR_SYSTEM_ERROR instead of BOOST_NO_0X_HDR_SYSTEM_ERROR +#if defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR) && !defined(BOOST_NO_0X_HDR_SYSTEM_ERROR) +# define BOOST_NO_0X_HDR_SYSTEM_ERROR +#endif +// Use BOOST_NO_CXX11_HDR_THREAD instead of BOOST_NO_0X_HDR_THREAD +#if defined(BOOST_NO_CXX11_HDR_THREAD) && !defined(BOOST_NO_0X_HDR_THREAD) +# define BOOST_NO_0X_HDR_THREAD +#endif +// Use BOOST_NO_CXX11_HDR_TUPLE instead of BOOST_NO_0X_HDR_TUPLE +#if defined(BOOST_NO_CXX11_HDR_TUPLE) && !defined(BOOST_NO_0X_HDR_TUPLE) +# define BOOST_NO_0X_HDR_TUPLE +#endif +// Use BOOST_NO_CXX11_HDR_TYPE_TRAITS instead of BOOST_NO_0X_HDR_TYPE_TRAITS +#if defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) && !defined(BOOST_NO_0X_HDR_TYPE_TRAITS) +# define BOOST_NO_0X_HDR_TYPE_TRAITS +#endif +// Use BOOST_NO_CXX11_HDR_TYPEINDEX instead of BOOST_NO_0X_HDR_TYPEINDEX +#if defined(BOOST_NO_CXX11_HDR_TYPEINDEX) && !defined(BOOST_NO_0X_HDR_TYPEINDEX) +# define BOOST_NO_0X_HDR_TYPEINDEX +#endif +// Use BOOST_NO_CXX11_HDR_UNORDERED_MAP instead of BOOST_NO_0X_HDR_UNORDERED_MAP +#if defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) && !defined(BOOST_NO_0X_HDR_UNORDERED_MAP) +# define BOOST_NO_0X_HDR_UNORDERED_MAP +#endif +// Use BOOST_NO_CXX11_HDR_UNORDERED_SET instead of BOOST_NO_0X_HDR_UNORDERED_SET +#if defined(BOOST_NO_CXX11_HDR_UNORDERED_SET) && !defined(BOOST_NO_0X_HDR_UNORDERED_SET) +# define BOOST_NO_0X_HDR_UNORDERED_SET +#endif + +// ------------------ End of deprecated macros for 1.50 --------------------------- + +// -------------------- Deprecated macros for 1.51 --------------------------- +// These will go away in a future release + +// Use BOOST_NO_CXX11_AUTO_DECLARATIONS instead of BOOST_NO_AUTO_DECLARATIONS +#if defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) && !defined(BOOST_NO_AUTO_DECLARATIONS) +# define BOOST_NO_AUTO_DECLARATIONS +#endif +// Use BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS instead of BOOST_NO_AUTO_MULTIDECLARATIONS +#if defined(BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS) && !defined(BOOST_NO_AUTO_MULTIDECLARATIONS) +# define BOOST_NO_AUTO_MULTIDECLARATIONS +#endif +// Use BOOST_NO_CXX11_CHAR16_T instead of BOOST_NO_CHAR16_T +#if defined(BOOST_NO_CXX11_CHAR16_T) && !defined(BOOST_NO_CHAR16_T) +# define BOOST_NO_CHAR16_T +#endif +// Use BOOST_NO_CXX11_CHAR32_T instead of BOOST_NO_CHAR32_T +#if defined(BOOST_NO_CXX11_CHAR32_T) && !defined(BOOST_NO_CHAR32_T) +# define BOOST_NO_CHAR32_T +#endif +// Use BOOST_NO_CXX11_TEMPLATE_ALIASES instead of BOOST_NO_TEMPLATE_ALIASES +#if defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) && !defined(BOOST_NO_TEMPLATE_ALIASES) +# define BOOST_NO_TEMPLATE_ALIASES +#endif +// Use BOOST_NO_CXX11_CONSTEXPR instead of BOOST_NO_CONSTEXPR +#if defined(BOOST_NO_CXX11_CONSTEXPR) && !defined(BOOST_NO_CONSTEXPR) +# define BOOST_NO_CONSTEXPR +#endif +// Use BOOST_NO_CXX11_DECLTYPE_N3276 instead of BOOST_NO_DECLTYPE_N3276 +#if defined(BOOST_NO_CXX11_DECLTYPE_N3276) && !defined(BOOST_NO_DECLTYPE_N3276) +# define BOOST_NO_DECLTYPE_N3276 +#endif +// Use BOOST_NO_CXX11_DECLTYPE instead of BOOST_NO_DECLTYPE +#if defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_DECLTYPE) +# define BOOST_NO_DECLTYPE +#endif +// Use BOOST_NO_CXX11_DEFAULTED_FUNCTIONS instead of BOOST_NO_DEFAULTED_FUNCTIONS +#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) && !defined(BOOST_NO_DEFAULTED_FUNCTIONS) +# define BOOST_NO_DEFAULTED_FUNCTIONS +#endif +// Use BOOST_NO_CXX11_DELETED_FUNCTIONS instead of BOOST_NO_DELETED_FUNCTIONS +#if defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) && !defined(BOOST_NO_DELETED_FUNCTIONS) +# define BOOST_NO_DELETED_FUNCTIONS +#endif +// Use BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS instead of BOOST_NO_EXPLICIT_CONVERSION_OPERATORS +#if defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) && !defined(BOOST_NO_EXPLICIT_CONVERSION_OPERATORS) +# define BOOST_NO_EXPLICIT_CONVERSION_OPERATORS +#endif +// Use BOOST_NO_CXX11_EXTERN_TEMPLATE instead of BOOST_NO_EXTERN_TEMPLATE +#if defined(BOOST_NO_CXX11_EXTERN_TEMPLATE) && !defined(BOOST_NO_EXTERN_TEMPLATE) +# define BOOST_NO_EXTERN_TEMPLATE +#endif +// Use BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS instead of BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS +#if defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) && !defined(BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) +# define BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS +#endif +// Use BOOST_NO_CXX11_LAMBDAS instead of BOOST_NO_LAMBDAS +#if defined(BOOST_NO_CXX11_LAMBDAS) && !defined(BOOST_NO_LAMBDAS) +# define BOOST_NO_LAMBDAS +#endif +// Use BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS instead of BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS +#if defined(BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS) && !defined(BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS) +# define BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS +#endif +// Use BOOST_NO_CXX11_NOEXCEPT instead of BOOST_NO_NOEXCEPT +#if defined(BOOST_NO_CXX11_NOEXCEPT) && !defined(BOOST_NO_NOEXCEPT) +# define BOOST_NO_NOEXCEPT +#endif +// Use BOOST_NO_CXX11_NULLPTR instead of BOOST_NO_NULLPTR +#if defined(BOOST_NO_CXX11_NULLPTR) && !defined(BOOST_NO_NULLPTR) +# define BOOST_NO_NULLPTR +#endif +// Use BOOST_NO_CXX11_RAW_LITERALS instead of BOOST_NO_RAW_LITERALS +#if defined(BOOST_NO_CXX11_RAW_LITERALS) && !defined(BOOST_NO_RAW_LITERALS) +# define BOOST_NO_RAW_LITERALS +#endif +// Use BOOST_NO_CXX11_RVALUE_REFERENCES instead of BOOST_NO_RVALUE_REFERENCES +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_RVALUE_REFERENCES) +# define BOOST_NO_RVALUE_REFERENCES +#endif +// Use BOOST_NO_CXX11_SCOPED_ENUMS instead of BOOST_NO_SCOPED_ENUMS +#if defined(BOOST_NO_CXX11_SCOPED_ENUMS) && !defined(BOOST_NO_SCOPED_ENUMS) +# define BOOST_NO_SCOPED_ENUMS +#endif +// Use BOOST_NO_CXX11_STATIC_ASSERT instead of BOOST_NO_STATIC_ASSERT +#if defined(BOOST_NO_CXX11_STATIC_ASSERT) && !defined(BOOST_NO_STATIC_ASSERT) +# define BOOST_NO_STATIC_ASSERT +#endif +// Use BOOST_NO_CXX11_STD_UNORDERED instead of BOOST_NO_STD_UNORDERED +#if defined(BOOST_NO_CXX11_STD_UNORDERED) && !defined(BOOST_NO_STD_UNORDERED) +# define BOOST_NO_STD_UNORDERED +#endif +// Use BOOST_NO_CXX11_UNICODE_LITERALS instead of BOOST_NO_UNICODE_LITERALS +#if defined(BOOST_NO_CXX11_UNICODE_LITERALS) && !defined(BOOST_NO_UNICODE_LITERALS) +# define BOOST_NO_UNICODE_LITERALS +#endif +// Use BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX instead of BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX +#if defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !defined(BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX) +# define BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX +#endif +// Use BOOST_NO_CXX11_VARIADIC_TEMPLATES instead of BOOST_NO_VARIADIC_TEMPLATES +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_NO_VARIADIC_TEMPLATES) +# define BOOST_NO_VARIADIC_TEMPLATES +#endif +// Use BOOST_NO_CXX11_VARIADIC_MACROS instead of BOOST_NO_VARIADIC_MACROS +#if defined(BOOST_NO_CXX11_VARIADIC_MACROS) && !defined(BOOST_NO_VARIADIC_MACROS) +# define BOOST_NO_VARIADIC_MACROS +#endif +// Use BOOST_NO_CXX11_NUMERIC_LIMITS instead of BOOST_NO_NUMERIC_LIMITS_LOWEST +#if defined(BOOST_NO_CXX11_NUMERIC_LIMITS) && !defined(BOOST_NO_NUMERIC_LIMITS_LOWEST) +# define BOOST_NO_NUMERIC_LIMITS_LOWEST +#endif +// ------------------ End of deprecated macros for 1.51 --------------------------- + + +// +// Helper macro for marking types and methods final +// +#if !defined(BOOST_NO_CXX11_FINAL) +# define BOOST_FINAL final +#else +# define BOOST_FINAL +#endif + +// +// Helper macros BOOST_NOEXCEPT, BOOST_NOEXCEPT_IF, BOOST_NOEXCEPT_EXPR +// These aid the transition to C++11 while still supporting C++03 compilers +// +#ifdef BOOST_NO_CXX11_NOEXCEPT +# define BOOST_NOEXCEPT +# define BOOST_NOEXCEPT_OR_NOTHROW throw() +# define BOOST_NOEXCEPT_IF(Predicate) +# define BOOST_NOEXCEPT_EXPR(Expression) false +#else +# define BOOST_NOEXCEPT noexcept +# define BOOST_NOEXCEPT_OR_NOTHROW noexcept +# define BOOST_NOEXCEPT_IF(Predicate) noexcept((Predicate)) +# define BOOST_NOEXCEPT_EXPR(Expression) noexcept((Expression)) +#endif +// +// Helper macro BOOST_FALLTHROUGH +// Fallback definition of BOOST_FALLTHROUGH macro used to mark intended +// fall-through between case labels in a switch statement. We use a definition +// that requires a semicolon after it to avoid at least one type of misuse even +// on unsupported compilers. +// +#ifndef BOOST_FALLTHROUGH +# define BOOST_FALLTHROUGH ((void)0) +#endif + +// +// constexpr workarounds +// +#if defined(BOOST_NO_CXX11_CONSTEXPR) +#define BOOST_CONSTEXPR +#define BOOST_CONSTEXPR_OR_CONST const +#else +#define BOOST_CONSTEXPR constexpr +#define BOOST_CONSTEXPR_OR_CONST constexpr +#endif +#if defined(BOOST_NO_CXX14_CONSTEXPR) +#define BOOST_CXX14_CONSTEXPR +#else +#define BOOST_CXX14_CONSTEXPR constexpr +#endif +#if !defined(BOOST_NO_CXX17_STRUCTURED_BINDINGS) && defined(BOOST_NO_CXX11_HDR_TUPLE) +# define BOOST_NO_CXX17_STRUCTURED_BINDINGS +#endif + +// +// C++17 inline variables +// +#if !defined(BOOST_NO_CXX17_INLINE_VARIABLES) +#define BOOST_INLINE_VARIABLE inline +#else +#define BOOST_INLINE_VARIABLE +#endif +// +// C++17 if constexpr +// +#if !defined(BOOST_NO_CXX17_IF_CONSTEXPR) +# define BOOST_IF_CONSTEXPR if constexpr +#else +# define BOOST_IF_CONSTEXPR if +#endif + +#define BOOST_INLINE_CONSTEXPR BOOST_INLINE_VARIABLE BOOST_CONSTEXPR_OR_CONST + +// +// Unused variable/typedef workarounds: +// +#ifndef BOOST_ATTRIBUTE_UNUSED +# if defined(__has_attribute) && defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x5130) +# if __has_attribute(maybe_unused) +# define BOOST_ATTRIBUTE_UNUSED [[maybe_unused]] +# endif +# elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(maybe_unused) +# define BOOST_ATTRIBUTE_UNUSED [[maybe_unused]] +# endif +# endif +#endif + +#ifndef BOOST_ATTRIBUTE_UNUSED +# define BOOST_ATTRIBUTE_UNUSED +#endif + +// +// [[nodiscard]]: +// +#if defined(__has_attribute) && defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x5130) +#if __has_attribute(nodiscard) +# define BOOST_ATTRIBUTE_NODISCARD [[nodiscard]] +#endif +#if __has_attribute(no_unique_address) +# define BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS [[no_unique_address]] +#endif +#elif defined(__has_cpp_attribute) +// clang-6 accepts [[nodiscard]] with -std=c++14, but warns about it -pedantic +#if __has_cpp_attribute(nodiscard) && !(defined(__clang__) && (__cplusplus < 201703L)) && !(defined(__GNUC__) && (__cplusplus < 201100)) +# define BOOST_ATTRIBUTE_NODISCARD [[nodiscard]] +#endif +#if __has_cpp_attribute(no_unique_address) && !(defined(__GNUC__) && (__cplusplus < 201100)) +# define BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS [[no_unique_address]] +#endif +#endif +#ifndef BOOST_ATTRIBUTE_NODISCARD +# define BOOST_ATTRIBUTE_NODISCARD +#endif +#ifndef BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS +# define BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS +#endif + +#define BOOST_STATIC_CONSTEXPR static BOOST_CONSTEXPR_OR_CONST + +#if !defined(BOOST_NO_CXX11_NULLPTR) +# define BOOST_NULLPTR nullptr +#else +# define BOOST_NULLPTR 0 +#endif + +// +// Set BOOST_HAS_STATIC_ASSERT when BOOST_NO_CXX11_STATIC_ASSERT is not defined +// +#if !defined(BOOST_NO_CXX11_STATIC_ASSERT) && !defined(BOOST_HAS_STATIC_ASSERT) +# define BOOST_HAS_STATIC_ASSERT +#endif + +// +// Set BOOST_HAS_RVALUE_REFS when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined +// +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_HAS_RVALUE_REFS) +#define BOOST_HAS_RVALUE_REFS +#endif + +// +// Set BOOST_HAS_VARIADIC_TMPL when BOOST_NO_CXX11_VARIADIC_TEMPLATES is not defined +// +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_HAS_VARIADIC_TMPL) +#define BOOST_HAS_VARIADIC_TMPL +#endif +// +// Set BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS when +// BOOST_NO_CXX11_VARIADIC_TEMPLATES is set: +// +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS) +# define BOOST_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS +#endif + +// This is a catch all case for obsolete compilers / std libs: +#if !defined(_YVALS) && !defined(_CPPLIB_VER) // msvc std lib already configured +#if (!defined(__has_include) || (__cplusplus < 201700)) +# define BOOST_NO_CXX17_HDR_OPTIONAL +# define BOOST_NO_CXX17_HDR_STRING_VIEW +# define BOOST_NO_CXX17_HDR_VARIANT +# define BOOST_NO_CXX17_HDR_ANY +# define BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +# define BOOST_NO_CXX17_HDR_CHARCONV +# define BOOST_NO_CXX17_HDR_EXECUTION +# define BOOST_NO_CXX17_HDR_FILESYSTEM +#else +#if !__has_include() +# define BOOST_NO_CXX17_HDR_OPTIONAL +#endif +#if !__has_include() +# define BOOST_NO_CXX17_HDR_STRING_VIEW +#endif +#if !__has_include() +# define BOOST_NO_CXX17_HDR_VARIANT +#endif +#if !__has_include() +# define BOOST_NO_CXX17_HDR_ANY +#endif +#if !__has_include() +# define BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#endif +#if !__has_include() +# define BOOST_NO_CXX17_HDR_CHARCONV +#endif +#if !__has_include() +# define BOOST_NO_CXX17_HDR_EXECUTION +#endif +#if !__has_include() +# define BOOST_NO_CXX17_HDR_FILESYSTEM +#endif +#endif +#endif +// +// Define the std level that the compiler claims to support: +// +#ifndef BOOST_CXX_VERSION +# define BOOST_CXX_VERSION __cplusplus +#endif + +#if (!defined(__has_include) || (BOOST_CXX_VERSION < 201704)) +# define BOOST_NO_CXX20_HDR_BARRIER +# define BOOST_NO_CXX20_HDR_FORMAT +# define BOOST_NO_CXX20_HDR_SOURCE_LOCATION +# define BOOST_NO_CXX20_HDR_BIT +# define BOOST_NO_CXX20_HDR_LATCH +# define BOOST_NO_CXX20_HDR_SPAN +# define BOOST_NO_CXX20_HDR_COMPARE +# define BOOST_NO_CXX20_HDR_NUMBERS +# define BOOST_NO_CXX20_HDR_STOP_TOKEN +# define BOOST_NO_CXX20_HDR_CONCEPTS +# define BOOST_NO_CXX20_HDR_RANGES +# define BOOST_NO_CXX20_HDR_SYNCSTREAM +# define BOOST_NO_CXX20_HDR_COROUTINE +# define BOOST_NO_CXX20_HDR_SEMAPHORE +#else +#if (!__has_include() || !defined(__cpp_lib_barrier) || (__cpp_lib_barrier < 201907L)) && !defined(BOOST_NO_CXX20_HDR_BARRIER) +# define BOOST_NO_CXX20_HDR_BARRIER +#endif +#if (!__has_include() || !defined(__cpp_lib_format) || (__cpp_lib_format < 201907L)) && !defined(BOOST_NO_CXX20_HDR_FORMAT) +# define BOOST_NO_CXX20_HDR_FORMAT +#endif +#if (!__has_include() || !defined(__cpp_lib_source_location) || (__cpp_lib_source_location < 201907L)) && !defined(BOOST_NO_CXX20_HDR_SOURCE_LOCATION) +# define BOOST_NO_CXX20_HDR_SOURCE_LOCATION +#endif +#if (!__has_include() || !defined(__cpp_lib_bit_cast) || (__cpp_lib_bit_cast < 201806L) || !defined(__cpp_lib_bitops) || (__cpp_lib_bitops < 201907L) || !defined(__cpp_lib_endian) || (__cpp_lib_endian < 201907L)) && !defined(BOOST_NO_CXX20_HDR_BIT) +# define BOOST_NO_CXX20_HDR_BIT +#endif +#if (!__has_include() || !defined(__cpp_lib_latch) || (__cpp_lib_latch < 201907L)) && !defined(BOOST_NO_CXX20_HDR_LATCH) +# define BOOST_NO_CXX20_HDR_LATCH +#endif +#if (!__has_include() || !defined(__cpp_lib_span) || (__cpp_lib_span < 202002L)) && !defined(BOOST_NO_CXX20_HDR_SPAN) +# define BOOST_NO_CXX20_HDR_SPAN +#endif +#if (!__has_include() || !defined(__cpp_lib_three_way_comparison) || (__cpp_lib_three_way_comparison < 201907L)) && !defined(BOOST_NO_CXX20_HDR_COMPARE) +# define BOOST_NO_CXX20_HDR_COMPARE +#endif +#if (!__has_include() || !defined(__cpp_lib_math_constants) || (__cpp_lib_math_constants < 201907L)) && !defined(BOOST_NO_CXX20_HDR_NUMBERS) +# define BOOST_NO_CXX20_HDR_NUMBERS +#endif +#if (!__has_include() || !defined(__cpp_lib_jthread) || (__cpp_lib_jthread < 201911L)) && !defined(BOOST_NO_CXX20_HDR_STOP_TOKEN) +# define BOOST_NO_CXX20_HDR_STOP_TOKEN +#endif +#if (!__has_include() || !defined(__cpp_lib_concepts) || (__cpp_lib_concepts < 202002L)) && !defined(_YVALS) && !defined(_CPPLIB_VER) && !defined(BOOST_NO_CXX20_HDR_CONCEPTS) +# define BOOST_NO_CXX20_HDR_CONCEPTS +#endif +#if (!__has_include() || !defined(__cpp_lib_ranges) || (__cpp_lib_ranges < 201911L)) && !defined(BOOST_NO_CXX20_HDR_RANGES) +# define BOOST_NO_CXX20_HDR_RANGES +#endif +#if (!__has_include() || !defined(__cpp_lib_syncbuf) || (__cpp_lib_syncbuf < 201803L)) && !defined(BOOST_NO_CXX20_HDR_SYNCSTREAM) +# define BOOST_NO_CXX20_HDR_SYNCSTREAM +#endif +#if (!__has_include() || !defined(__cpp_lib_coroutine) || (__cpp_lib_coroutine < 201902L)) && !defined(BOOST_NO_CXX20_HDR_COROUTINE) +# define BOOST_NO_CXX20_HDR_COROUTINE +#endif +#if (!__has_include() || !defined(__cpp_lib_semaphore) || (__cpp_lib_semaphore < 201907L)) && !defined(BOOST_NO_CXX20_HDR_SEMAPHORE) +# define BOOST_NO_CXX20_HDR_SEMAPHORE +#endif +#endif + +#if (!defined(__has_include) || (BOOST_CXX_VERSION < 202003L)) +# define BOOST_NO_CXX23_HDR_EXPECTED +# define BOOST_NO_CXX23_HDR_FLAT_MAP +# define BOOST_NO_CXX23_HDR_FLAT_SET +# define BOOST_NO_CXX23_HDR_GENERATOR +# define BOOST_NO_CXX23_HDR_MDSPAN +# define BOOST_NO_CXX23_HDR_PRINT +# define BOOST_NO_CXX23_HDR_SPANSTREAM +# define BOOST_NO_CXX23_HDR_STACKTRACE +# define BOOST_NO_CXX23_HDR_STDFLOAT +#else +#if (!__has_include() || !defined(__cpp_lib_expected) || (__cpp_lib_expected < 202211L)) && !defined(BOOST_NO_CXX23_HDR_EXPECTED) +# define BOOST_NO_CXX23_HDR_EXPECTED +#endif +#if (!__has_include() || !defined(__cpp_lib_flat_map) || (__cpp_lib_flat_map < 202207L)) && !defined(BOOST_NO_CXX23_HDR_FLAT_MAP) +# define BOOST_NO_CXX23_HDR_FLAT_MAP +#endif +#if (!__has_include() || !defined(__cpp_lib_flat_set) || (__cpp_lib_flat_set < 202207L)) && !defined(BOOST_NO_CXX23_HDR_FLAT_SET) +# define BOOST_NO_CXX23_HDR_FLAT_SET +#endif +#if (!__has_include() || !defined(__cpp_lib_generator) || (__cpp_lib_generator < 202207L)) && !defined(BOOST_NO_CXX23_HDR_GENERATOR) +# define BOOST_NO_CXX23_HDR_GENERATOR +#endif +#if (!__has_include() || !defined(__cpp_lib_mdspan) || (__cpp_lib_mdspan < 202207L)) && !defined(BOOST_NO_CXX23_HDR_MDSPAN) +# define BOOST_NO_CXX23_HDR_MDSPAN +#endif +#if (!__has_include() || !defined(__cpp_lib_print) || (__cpp_lib_print < 202207L)) && !defined(BOOST_NO_CXX23_HDR_PRINT) +# define BOOST_NO_CXX23_HDR_PRINT +#endif +#if (!__has_include() || !defined(__cpp_lib_spanstream) || (__cpp_lib_spanstream < 202106L)) && !defined(BOOST_NO_CXX23_HDR_SPANSTREAM) +# define BOOST_NO_CXX23_HDR_SPANSTREAM +#endif +#if (!__has_include() || !defined(__cpp_lib_stacktrace) || (__cpp_lib_stacktrace < 202011L)) && !defined(BOOST_NO_CXX23_HDR_STACKTRACE) +# define BOOST_NO_CXX23_HDR_STACKTRACE +#endif +#if !__has_include() && !defined(BOOST_NO_CXX23_HDR_STDFLOAT) +# define BOOST_NO_CXX23_HDR_STDFLOAT +#endif +#endif + +#if defined(__cplusplus) && defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX20_HDR_VERSION +#else +// For convenience, this is always included: +# include +#endif +#else +# define BOOST_NO_CXX20_HDR_VERSION +#endif + +#if defined(BOOST_MSVC) +#if (BOOST_MSVC < 1914) || (_MSVC_LANG < 201703) +# define BOOST_NO_CXX17_DEDUCTION_GUIDES +#endif +#elif !defined(__cpp_deduction_guides) || (__cpp_deduction_guides < 201606) +# define BOOST_NO_CXX17_DEDUCTION_GUIDES +#endif + +// +// Define composite agregate macros: +// +#include + +// +// Finish off with checks for macros that are depricated / no longer supported, +// if any of these are set then it's very likely that much of Boost will no +// longer work. So stop with a #error for now, but give the user a chance +// to continue at their own risk if they really want to: +// +#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_CONFIG_ALLOW_DEPRECATED) +# error "You are using a compiler which lacks features which are now a minimum requirement in order to use Boost, define BOOST_CONFIG_ALLOW_DEPRECATED if you want to continue at your own risk!!!" +#endif + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/header_deprecated.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/header_deprecated.hpp new file mode 100644 index 0000000..120b4b3 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/header_deprecated.hpp @@ -0,0 +1,26 @@ +#ifndef BOOST_CONFIG_HEADER_DEPRECATED_HPP_INCLUDED +#define BOOST_CONFIG_HEADER_DEPRECATED_HPP_INCLUDED + +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +// BOOST_HEADER_DEPRECATED("") +// +// Expands to the equivalent of +// BOOST_PRAGMA_MESSAGE("This header is deprecated. Use instead.") +// +// Note that this header is C compatible. + +#include + +#if defined(BOOST_ALLOW_DEPRECATED_HEADERS) || defined(BOOST_ALLOW_DEPRECATED) +# define BOOST_HEADER_DEPRECATED(a) +#else +# define BOOST_HEADER_DEPRECATED(a) BOOST_PRAGMA_MESSAGE("This header is deprecated. Use " a " instead.") +#endif + +#endif // BOOST_CONFIG_HEADER_DEPRECATED_HPP_INCLUDED diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/helper_macros.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/helper_macros.hpp new file mode 100644 index 0000000..3e79526 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/helper_macros.hpp @@ -0,0 +1,37 @@ +#ifndef BOOST_CONFIG_HELPER_MACROS_HPP_INCLUDED +#define BOOST_CONFIG_HELPER_MACROS_HPP_INCLUDED + +// Copyright 2001 John Maddock. +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +// BOOST_STRINGIZE(X) +// BOOST_JOIN(X, Y) +// +// Note that this header is C compatible. + +// +// Helper macro BOOST_STRINGIZE: +// Converts the parameter X to a string after macro replacement +// on X has been performed. +// +#define BOOST_STRINGIZE(X) BOOST_DO_STRINGIZE(X) +#define BOOST_DO_STRINGIZE(X) #X + +// +// Helper macro BOOST_JOIN: +// The following piece of macro magic joins the two +// arguments together, even when one of the arguments is +// itself a macro (see 16.3.1 in C++ standard). The key +// is that macro expansion of macro arguments does not +// occur in BOOST_DO_JOIN2 but does in BOOST_DO_JOIN. +// +#define BOOST_JOIN(X, Y) BOOST_DO_JOIN(X, Y) +#define BOOST_DO_JOIN(X, Y) BOOST_DO_JOIN2(X,Y) +#define BOOST_DO_JOIN2(X, Y) X##Y + +#endif // BOOST_CONFIG_HELPER_MACROS_HPP_INCLUDED diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/cmath.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/cmath.hpp new file mode 100644 index 0000000..d8268d8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/cmath.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2008. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// The aim of this header is just to include but to do +// so in a way that does not result in recursive inclusion of +// the Boost TR1 components if boost/tr1/tr1/cmath is in the +// include search path. We have to do this to avoid circular +// dependencies: +// + +#ifndef BOOST_CONFIG_CMATH +# define BOOST_CONFIG_CMATH + +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_CONFIG_NO_CMATH_RECURSION +# endif + +# include + +# ifdef BOOST_CONFIG_NO_CMATH_RECURSION +# undef BOOST_TR1_NO_RECURSION +# undef BOOST_CONFIG_NO_CMATH_RECURSION +# endif + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/complex.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/complex.hpp new file mode 100644 index 0000000..ca20092 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/complex.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// The aim of this header is just to include but to do +// so in a way that does not result in recursive inclusion of +// the Boost TR1 components if boost/tr1/tr1/complex is in the +// include search path. We have to do this to avoid circular +// dependencies: +// + +#ifndef BOOST_CONFIG_COMPLEX +# define BOOST_CONFIG_COMPLEX + +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_CONFIG_NO_COMPLEX_RECURSION +# endif + +# include + +# ifdef BOOST_CONFIG_NO_COMPLEX_RECURSION +# undef BOOST_TR1_NO_RECURSION +# undef BOOST_CONFIG_NO_COMPLEX_RECURSION +# endif + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/functional.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/functional.hpp new file mode 100644 index 0000000..e395efc --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/functional.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// The aim of this header is just to include but to do +// so in a way that does not result in recursive inclusion of +// the Boost TR1 components if boost/tr1/tr1/functional is in the +// include search path. We have to do this to avoid circular +// dependencies: +// + +#ifndef BOOST_CONFIG_FUNCTIONAL +# define BOOST_CONFIG_FUNCTIONAL + +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_CONFIG_NO_FUNCTIONAL_RECURSION +# endif + +# include + +# ifdef BOOST_CONFIG_NO_FUNCTIONAL_RECURSION +# undef BOOST_TR1_NO_RECURSION +# undef BOOST_CONFIG_NO_FUNCTIONAL_RECURSION +# endif + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/memory.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/memory.hpp new file mode 100644 index 0000000..2b5d208 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/memory.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// The aim of this header is just to include but to do +// so in a way that does not result in recursive inclusion of +// the Boost TR1 components if boost/tr1/tr1/memory is in the +// include search path. We have to do this to avoid circular +// dependencies: +// + +#ifndef BOOST_CONFIG_MEMORY +# define BOOST_CONFIG_MEMORY + +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_CONFIG_NO_MEMORY_RECURSION +# endif + +# include + +# ifdef BOOST_CONFIG_NO_MEMORY_RECURSION +# undef BOOST_TR1_NO_RECURSION +# undef BOOST_CONFIG_NO_MEMORY_RECURSION +# endif + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/utility.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/utility.hpp new file mode 100644 index 0000000..dea8f11 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/no_tr1/utility.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// The aim of this header is just to include but to do +// so in a way that does not result in recursive inclusion of +// the Boost TR1 components if boost/tr1/tr1/utility is in the +// include search path. We have to do this to avoid circular +// dependencies: +// + +#ifndef BOOST_CONFIG_UTILITY +# define BOOST_CONFIG_UTILITY + +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_CONFIG_NO_UTILITY_RECURSION +# endif + +# include + +# ifdef BOOST_CONFIG_NO_UTILITY_RECURSION +# undef BOOST_TR1_NO_RECURSION +# undef BOOST_CONFIG_NO_UTILITY_RECURSION +# endif + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/aix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/aix.hpp new file mode 100644 index 0000000..a48e232 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/aix.hpp @@ -0,0 +1,33 @@ +// (C) Copyright John Maddock 2001 - 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// IBM/Aix specific config options: + +#define BOOST_PLATFORM "IBM Aix" + +#define BOOST_HAS_UNISTD_H +#define BOOST_HAS_NL_TYPES_H +#define BOOST_HAS_NANOSLEEP +#define BOOST_HAS_CLOCK_GETTIME + +// This needs support in "boost/cstdint.hpp" exactly like FreeBSD. +// This platform has header named which includes all +// the things needed. +#define BOOST_HAS_STDINT_H + +// Threading API's: +#define BOOST_HAS_PTHREADS +#define BOOST_HAS_PTHREAD_DELAY_NP +#define BOOST_HAS_SCHED_YIELD +//#define BOOST_HAS_PTHREAD_YIELD + +// boilerplate code: +#include + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/amigaos.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/amigaos.hpp new file mode 100644 index 0000000..34bcf41 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/amigaos.hpp @@ -0,0 +1,15 @@ +// (C) Copyright John Maddock 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +#define BOOST_PLATFORM "AmigaOS" + +#define BOOST_DISABLE_THREADS +#define BOOST_NO_CWCHAR +#define BOOST_NO_STD_WSTRING +#define BOOST_NO_INTRINSIC_WCHAR_T + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/beos.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/beos.hpp new file mode 100644 index 0000000..6158c1c --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/beos.hpp @@ -0,0 +1,26 @@ +// (C) Copyright John Maddock 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// BeOS specific config options: + +#define BOOST_PLATFORM "BeOS" + +#define BOOST_NO_CWCHAR +#define BOOST_NO_CWCTYPE +#define BOOST_HAS_UNISTD_H + +#define BOOST_HAS_BETHREADS + +#ifndef BOOST_DISABLE_THREADS +# define BOOST_HAS_THREADS +#endif + +// boilerplate code: +#include + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/bsd.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/bsd.hpp new file mode 100644 index 0000000..ccc7eb0 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/bsd.hpp @@ -0,0 +1,83 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Darin Adler 2001. +// (C) Copyright Douglas Gregor 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// generic BSD config options: + +#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) +#error "This platform is not BSD" +#endif + +#ifdef __FreeBSD__ +#define BOOST_PLATFORM "FreeBSD " BOOST_STRINGIZE(__FreeBSD__) +#elif defined(__NetBSD__) +#define BOOST_PLATFORM "NetBSD " BOOST_STRINGIZE(__NetBSD__) +#elif defined(__OpenBSD__) +#define BOOST_PLATFORM "OpenBSD " BOOST_STRINGIZE(__OpenBSD__) +#elif defined(__DragonFly__) +#define BOOST_PLATFORM "DragonFly " BOOST_STRINGIZE(__DragonFly__) +#endif + +// +// is this the correct version check? +// FreeBSD has but does not +// advertise the fact in : +// +#if (defined(__FreeBSD__) && (__FreeBSD__ >= 3)) \ + || defined(__OpenBSD__) || defined(__DragonFly__) +# define BOOST_HAS_NL_TYPES_H +#endif + +// +// FreeBSD 3.x has pthreads support, but defines _POSIX_THREADS in +// and not in +// +#if (defined(__FreeBSD__) && (__FreeBSD__ <= 3))\ + || defined(__OpenBSD__) || defined(__DragonFly__) +# define BOOST_HAS_PTHREADS +#endif + +// +// No wide character support in the BSD header files: +// +#if defined(__NetBSD__) +#define __NetBSD_GCC__ (__GNUC__ * 1000000 \ + + __GNUC_MINOR__ * 1000 \ + + __GNUC_PATCHLEVEL__) +// XXX - the following is required until c++config.h +// defines _GLIBCXX_HAVE_SWPRINTF and friends +// or the preprocessor conditionals are removed +// from the cwchar header. +#define _GLIBCXX_HAVE_SWPRINTF 1 +#endif + +#if !((defined(__FreeBSD__) && (__FreeBSD__ >= 5)) \ + || (defined(__NetBSD_GCC__) && (__NetBSD_GCC__ >= 2095003)) \ + || defined(__OpenBSD__) || defined(__DragonFly__)) +# define BOOST_NO_CWCHAR +#endif +// +// The BSD has macros only, no functions: +// +#if !defined(__OpenBSD__) || defined(__DragonFly__) +# define BOOST_NO_CTYPE_FUNCTIONS +#endif + +// +// thread API's not auto detected: +// +#define BOOST_HAS_SCHED_YIELD +#define BOOST_HAS_NANOSLEEP +#define BOOST_HAS_GETTIMEOFDAY +#define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +#define BOOST_HAS_SIGACTION +#define BOOST_HAS_CLOCK_GETTIME + +// boilerplate code: +#define BOOST_HAS_UNISTD_H +#include diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cloudabi.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cloudabi.hpp new file mode 100644 index 0000000..bed7b63 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cloudabi.hpp @@ -0,0 +1,18 @@ +// Copyright Nuxi, https://nuxi.nl/ 2015. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_PLATFORM "CloudABI" + +#define BOOST_HAS_DIRENT_H +#define BOOST_HAS_STDINT_H +#define BOOST_HAS_UNISTD_H + +#define BOOST_HAS_CLOCK_GETTIME +#define BOOST_HAS_EXPM1 +#define BOOST_HAS_GETTIMEOFDAY +#define BOOST_HAS_LOG1P +#define BOOST_HAS_NANOSLEEP +#define BOOST_HAS_PTHREADS +#define BOOST_HAS_SCHED_YIELD diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cray.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cray.hpp new file mode 100644 index 0000000..103e9c0 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cray.hpp @@ -0,0 +1,18 @@ +// (C) Copyright John Maddock 2011. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// See http://www.boost.org for most recent version. + +// SGI Irix specific config options: + +#define BOOST_PLATFORM "Cray" + +// boilerplate code: +#define BOOST_HAS_UNISTD_H +#include + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cygwin.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cygwin.hpp new file mode 100644 index 0000000..d0052d8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/cygwin.hpp @@ -0,0 +1,71 @@ +// (C) Copyright John Maddock 2001 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// cygwin specific config options: + +#define BOOST_PLATFORM "Cygwin" +#define BOOST_HAS_DIRENT_H +#define BOOST_HAS_LOG1P +#define BOOST_HAS_EXPM1 + +// +// Threading API: +// See if we have POSIX threads, if we do use them, otherwise +// revert to native Win threads. +#define BOOST_HAS_UNISTD_H +#include +#if defined(_POSIX_THREADS) && (_POSIX_THREADS+0 >= 0) && !defined(BOOST_HAS_WINTHREADS) +# define BOOST_HAS_PTHREADS +# define BOOST_HAS_SCHED_YIELD +# define BOOST_HAS_GETTIMEOFDAY +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +//# define BOOST_HAS_SIGACTION +#else +# if !defined(BOOST_HAS_WINTHREADS) +# define BOOST_HAS_WINTHREADS +# endif +# define BOOST_HAS_FTIME +#endif + +// +// find out if we have a stdint.h, there should be a better way to do this: +// +#include +#ifdef _STDINT_H +#define BOOST_HAS_STDINT_H +#endif +#if __GNUC__ > 5 && !defined(BOOST_HAS_STDINT_H) +# define BOOST_HAS_STDINT_H +#endif + +#include +#if (CYGWIN_VERSION_API_MAJOR == 0 && CYGWIN_VERSION_API_MINOR < 231) +/// Cygwin has no fenv.h +#define BOOST_NO_FENV_H +#endif + +// Cygwin has it's own which breaks unless the correct compiler flags are used: +#ifndef BOOST_NO_CXX14_HDR_SHARED_MUTEX +#include +#if !(__XSI_VISIBLE >= 500 || __POSIX_VISIBLE >= 200112) +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#endif + +// boilerplate code: +#include + +// +// Cygwin lies about XSI conformance, there is no nl_types.h: +// +#ifdef BOOST_HAS_NL_TYPES_H +# undef BOOST_HAS_NL_TYPES_H +#endif + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/haiku.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/haiku.hpp new file mode 100644 index 0000000..04244c5 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/haiku.hpp @@ -0,0 +1,31 @@ +// (C) Copyright Jessica Hamilton 2014. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Haiku specific config options: + +#define BOOST_PLATFORM "Haiku" + +#define BOOST_HAS_UNISTD_H +#define BOOST_HAS_STDINT_H + +#ifndef BOOST_DISABLE_THREADS +# define BOOST_HAS_THREADS +#endif + +#define BOOST_NO_CXX11_HDR_TYPE_TRAITS +#define BOOST_NO_CXX11_ATOMIC_SMART_PTR +#define BOOST_NO_CXX11_STATIC_ASSERT +#define BOOST_NO_CXX11_VARIADIC_MACROS + +// +// thread API's not auto detected: +// +#define BOOST_HAS_SCHED_YIELD +#define BOOST_HAS_GETTIMEOFDAY + +// boilerplate code: +#include diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/hpux.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/hpux.hpp new file mode 100644 index 0000000..222622e --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/hpux.hpp @@ -0,0 +1,87 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001 - 2003. +// (C) Copyright David Abrahams 2002. +// (C) Copyright Toon Knapen 2003. +// (C) Copyright Boris Gubenko 2006 - 2007. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// hpux specific config options: + +#define BOOST_PLATFORM "HP-UX" + +// In principle, HP-UX has a nice under the name +// However, it has the following problem: +// Use of UINT32_C(0) results in "0u l" for the preprocessed source +// (verifyable with gcc 2.95.3) +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__HP_aCC) +# define BOOST_HAS_STDINT_H +#endif + +#if !(defined(__HP_aCC) || !defined(_INCLUDE__STDC_A1_SOURCE)) +# define BOOST_NO_SWPRINTF +#endif +#if defined(__HP_aCC) && !defined(_INCLUDE__STDC_A1_SOURCE) +# define BOOST_NO_CWCTYPE +#endif + +#if defined(__GNUC__) +# if (__GNUC__ < 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ < 3)) + // GNU C on HP-UX does not support threads (checked up to gcc 3.3) +# define BOOST_DISABLE_THREADS +# elif !defined(BOOST_DISABLE_THREADS) + // threads supported from gcc-3.3 onwards: +# define BOOST_HAS_THREADS +# define BOOST_HAS_PTHREADS +# endif +#elif defined(__HP_aCC) && !defined(BOOST_DISABLE_THREADS) +# define BOOST_HAS_PTHREADS +#endif + +// boilerplate code: +#define BOOST_HAS_UNISTD_H +#include + +// the following are always available: +#ifndef BOOST_HAS_GETTIMEOFDAY +# define BOOST_HAS_GETTIMEOFDAY +#endif +#ifndef BOOST_HAS_SCHED_YIELD +# define BOOST_HAS_SCHED_YIELD +#endif +#ifndef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +#endif +#ifndef BOOST_HAS_NL_TYPES_H +# define BOOST_HAS_NL_TYPES_H +#endif +#ifndef BOOST_HAS_NANOSLEEP +# define BOOST_HAS_NANOSLEEP +#endif +#ifndef BOOST_HAS_GETTIMEOFDAY +# define BOOST_HAS_GETTIMEOFDAY +#endif +#ifndef BOOST_HAS_DIRENT_H +# define BOOST_HAS_DIRENT_H +#endif +#ifndef BOOST_HAS_CLOCK_GETTIME +# define BOOST_HAS_CLOCK_GETTIME +#endif +#ifndef BOOST_HAS_SIGACTION +# define BOOST_HAS_SIGACTION +#endif +#ifndef BOOST_HAS_NRVO +# ifndef __parisc +# define BOOST_HAS_NRVO +# endif +#endif +#ifndef BOOST_HAS_LOG1P +# define BOOST_HAS_LOG1P +#endif +#ifndef BOOST_HAS_EXPM1 +# define BOOST_HAS_EXPM1 +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/irix.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/irix.hpp new file mode 100644 index 0000000..0acb651 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/irix.hpp @@ -0,0 +1,31 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// See http://www.boost.org for most recent version. + +// SGI Irix specific config options: + +#define BOOST_PLATFORM "SGI Irix" + +#define BOOST_NO_SWPRINTF +// +// these are not auto detected by POSIX feature tests: +// +#define BOOST_HAS_GETTIMEOFDAY +#define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE + +#ifdef __GNUC__ + // GNU C on IRIX does not support threads (checked up to gcc 3.3) +# define BOOST_DISABLE_THREADS +#endif + +// boilerplate code: +#define BOOST_HAS_UNISTD_H +#include + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/linux.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/linux.hpp new file mode 100644 index 0000000..c4eef8f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/linux.hpp @@ -0,0 +1,106 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// linux specific config options: + +#define BOOST_PLATFORM "linux" + +// make sure we have __GLIBC_PREREQ if available at all +#ifdef __cplusplus +#include +#else +#include +#endif + +// +// added to glibc 2.1.1 +// We can only test for 2.1 though: +// +#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))) + // defines int64_t unconditionally, but defines + // int64_t only if __GNUC__. Thus, assume a fully usable + // only when using GCC. Update 2017: this appears not to be the case for + // recent glibc releases, see bug report: https://svn.boost.org/trac/boost/ticket/13045 +# if defined(__GNUC__) || ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 5))) +# define BOOST_HAS_STDINT_H +# endif +#endif + +#if defined(__LIBCOMO__) + // + // como on linux doesn't have std:: c functions: + // NOTE: versions of libcomo prior to beta28 have octal version numbering, + // e.g. version 25 is 21 (dec) + // +# if __LIBCOMO_VERSION__ <= 20 +# define BOOST_NO_STDC_NAMESPACE +# endif + +# if __LIBCOMO_VERSION__ <= 21 +# define BOOST_NO_SWPRINTF +# endif + +#endif + +// +// If glibc is past version 2 then we definitely have +// gettimeofday, earlier versions may or may not have it: +// +#if defined(__GLIBC__) && (__GLIBC__ >= 2) +# define BOOST_HAS_GETTIMEOFDAY +#endif + +#ifdef __USE_POSIX199309 +# define BOOST_HAS_NANOSLEEP +#endif + +#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) +// __GLIBC_PREREQ is available since 2.1.2 + + // swprintf is available since glibc 2.2.0 +# if !__GLIBC_PREREQ(2,2) || (!defined(__USE_ISOC99) && !defined(__USE_UNIX98)) +# define BOOST_NO_SWPRINTF +# endif +#else +# define BOOST_NO_SWPRINTF +#endif + +// boilerplate code: +#define BOOST_HAS_UNISTD_H +#include +#if defined(__USE_GNU) && !defined(__ANDROID__) && !defined(ANDROID) +#define BOOST_HAS_PTHREAD_YIELD +#endif + +#ifndef __GNUC__ +// +// if the compiler is not gcc we still need to be able to parse +// the GNU system headers, some of which (mainly ) +// use GNU specific extensions: +// +# ifndef __extension__ +# define __extension__ +# endif +# ifndef __const__ +# define __const__ const +# endif +# ifndef __volatile__ +# define __volatile__ volatile +# endif +# ifndef __signed__ +# define __signed__ signed +# endif +# ifndef __typeof__ +# define __typeof__ typeof +# endif +# ifndef __inline__ +# define __inline__ inline +# endif +#endif + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/macos.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/macos.hpp new file mode 100644 index 0000000..ed7dc15 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/macos.hpp @@ -0,0 +1,87 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Darin Adler 2001 - 2002. +// (C) Copyright Bill Kempf 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Mac OS specific config options: + +#define BOOST_PLATFORM "Mac OS" + +#if __MACH__ && !defined(_MSL_USING_MSL_C) + +// Using the Mac OS X system BSD-style C library. + +# ifndef BOOST_HAS_UNISTD_H +# define BOOST_HAS_UNISTD_H +# endif +// +// Begin by including our boilerplate code for POSIX +// feature detection, this is safe even when using +// the MSL as Metrowerks supply their own +// to replace the platform-native BSD one. G++ users +// should also always be able to do this on MaxOS X. +// +# include +# ifndef BOOST_HAS_STDINT_H +# define BOOST_HAS_STDINT_H +# endif + +// +// BSD runtime has pthreads, sigaction, sched_yield and gettimeofday, +// of these only pthreads are advertised in , so set the +// other options explicitly: +// +# define BOOST_HAS_SCHED_YIELD +# define BOOST_HAS_GETTIMEOFDAY +# define BOOST_HAS_SIGACTION + +# if (__GNUC__ < 3) && !defined( __APPLE_CC__) + +// GCC strange "ignore std" mode works better if you pretend everything +// is in the std namespace, for the most part. + +# define BOOST_NO_STDC_NAMESPACE +# endif + +# if (__GNUC__ >= 4) + +// Both gcc and intel require these. +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# define BOOST_HAS_NANOSLEEP + +# endif + +#else + +// Using the MSL C library. + +// We will eventually support threads in non-Carbon builds, but we do +// not support this yet. +# if ( defined(TARGET_API_MAC_CARBON) && TARGET_API_MAC_CARBON ) || ( defined(TARGET_CARBON) && TARGET_CARBON ) + +# if !defined(BOOST_HAS_PTHREADS) +// MPTasks support is deprecated/removed from Boost: +//# define BOOST_HAS_MPTASKS +# elif ( __dest_os == __mac_os_x ) +// We are doing a Carbon/Mach-O/MSL build which has pthreads, but only the +// gettimeofday and no posix. +# define BOOST_HAS_GETTIMEOFDAY +# endif + +#ifdef BOOST_HAS_PTHREADS +# define BOOST_HAS_THREADS +#endif + +// The remote call manager depends on this. +# define BOOST_BIND_ENABLE_PASCAL + +# endif + +#endif + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/qnxnto.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/qnxnto.hpp new file mode 100644 index 0000000..d0298cb --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/qnxnto.hpp @@ -0,0 +1,31 @@ +// (C) Copyright Jim Douglas 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// QNX specific config options: + +#define BOOST_PLATFORM "QNX" + +#define BOOST_HAS_UNISTD_H +#include + +// QNX claims XOpen version 5 compatibility, but doesn't have an nl_types.h +// or log1p and expm1: +#undef BOOST_HAS_NL_TYPES_H +#undef BOOST_HAS_LOG1P +#undef BOOST_HAS_EXPM1 + +#define BOOST_HAS_PTHREADS +#define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE + +#define BOOST_HAS_GETTIMEOFDAY +#define BOOST_HAS_CLOCK_GETTIME +#define BOOST_HAS_NANOSLEEP + + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/solaris.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/solaris.hpp new file mode 100644 index 0000000..51ffe67 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/solaris.hpp @@ -0,0 +1,31 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// sun specific config options: + +#define BOOST_PLATFORM "Sun Solaris" + +#define BOOST_HAS_GETTIMEOFDAY + +// boilerplate code: +#define BOOST_HAS_UNISTD_H +#include + +// +// pthreads don't actually work with gcc unless _PTHREADS is defined: +// +#if defined(__GNUC__) && defined(_POSIX_THREADS) && !defined(_PTHREADS) +# undef BOOST_HAS_PTHREADS +#endif + +#define BOOST_HAS_STDINT_H +#define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +#define BOOST_HAS_LOG1P +#define BOOST_HAS_EXPM1 + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/symbian.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/symbian.hpp new file mode 100644 index 0000000..f814d00 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/symbian.hpp @@ -0,0 +1,97 @@ +// (C) Copyright Yuriy Krasnoschek 2009. +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// symbian specific config options: + + +#define BOOST_PLATFORM "Symbian" +#define BOOST_SYMBIAN 1 + + +#if defined(__S60_3X__) +// Open C / C++ plugin was introdused in this SDK, earlier versions don't have CRT / STL +# define BOOST_S60_3rd_EDITION_FP2_OR_LATER_SDK +// make sure we have __GLIBC_PREREQ if available at all +#ifdef __cplusplus +#include +#else +#include +#endif// boilerplate code: +# define BOOST_HAS_UNISTD_H +# include +// S60 SDK defines _POSIX_VERSION as POSIX.1 +# ifndef BOOST_HAS_STDINT_H +# define BOOST_HAS_STDINT_H +# endif +# ifndef BOOST_HAS_GETTIMEOFDAY +# define BOOST_HAS_GETTIMEOFDAY +# endif +# ifndef BOOST_HAS_DIRENT_H +# define BOOST_HAS_DIRENT_H +# endif +# ifndef BOOST_HAS_SIGACTION +# define BOOST_HAS_SIGACTION +# endif +# ifndef BOOST_HAS_PTHREADS +# define BOOST_HAS_PTHREADS +# endif +# ifndef BOOST_HAS_NANOSLEEP +# define BOOST_HAS_NANOSLEEP +# endif +# ifndef BOOST_HAS_SCHED_YIELD +# define BOOST_HAS_SCHED_YIELD +# endif +# ifndef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# endif +# ifndef BOOST_HAS_LOG1P +# define BOOST_HAS_LOG1P +# endif +# ifndef BOOST_HAS_EXPM1 +# define BOOST_HAS_EXPM1 +# endif +# ifndef BOOST_POSIX_API +# define BOOST_POSIX_API +# endif +// endianess support +# include +// Symbian SDK provides _BYTE_ORDER instead of __BYTE_ORDER +# ifndef __LITTLE_ENDIAN +# ifdef _LITTLE_ENDIAN +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# else +# define __LITTLE_ENDIAN 1234 +# endif +# endif +# ifndef __BIG_ENDIAN +# ifdef _BIG_ENDIAN +# define __BIG_ENDIAN _BIG_ENDIAN +# else +# define __BIG_ENDIAN 4321 +# endif +# endif +# ifndef __BYTE_ORDER +# define __BYTE_ORDER __LITTLE_ENDIAN // Symbian is LE +# endif +// Known limitations +# define BOOST_ASIO_DISABLE_SERIAL_PORT +# define BOOST_DATE_TIME_NO_LOCALE +# define BOOST_NO_STD_WSTRING +# define BOOST_EXCEPTION_DISABLE +# define BOOST_NO_EXCEPTIONS + +#else // TODO: More platform support e.g. UIQ +# error "Unsuppoted Symbian SDK" +#endif + +#if defined(__WINSCW__) && !defined(BOOST_DISABLE_WIN32) +# define BOOST_DISABLE_WIN32 // winscw defines WIN32 macro +#endif + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vms.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vms.hpp new file mode 100644 index 0000000..f70efcf --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vms.hpp @@ -0,0 +1,25 @@ +// (C) Copyright Artyom Beilis 2010. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_CONFIG_PLATFORM_VMS_HPP +#define BOOST_CONFIG_PLATFORM_VMS_HPP + +#define BOOST_PLATFORM "OpenVMS" + +#undef BOOST_HAS_STDINT_H +#define BOOST_HAS_UNISTD_H +#define BOOST_HAS_NL_TYPES_H +#define BOOST_HAS_GETTIMEOFDAY +#define BOOST_HAS_DIRENT_H +#define BOOST_HAS_PTHREADS +#define BOOST_HAS_NANOSLEEP +#define BOOST_HAS_CLOCK_GETTIME +#define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +#define BOOST_HAS_LOG1P +#define BOOST_HAS_EXPM1 +#define BOOST_HAS_THREADS +#undef BOOST_HAS_SCHED_YIELD + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vxworks.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vxworks.hpp new file mode 100644 index 0000000..0564b94 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/vxworks.hpp @@ -0,0 +1,422 @@ +// (C) Copyright Dustin Spicuzza 2009. +// Adapted to vxWorks 6.9 by Peter Brockamp 2012. +// Updated for VxWorks 7 by Brian Kuhl 2016 +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Old versions of vxWorks (namely everything below 6.x) are +// absolutely unable to use boost. Old STLs and compilers +// like (GCC 2.96) . Do not even think of getting this to work, +// a miserable failure will be guaranteed! +// +// VxWorks supports C++ linkage in the kernel with +// DKMs (Downloadable Kernel Modules). But, until recently +// the kernel used a C89 library with no +// wide character support and no guarantee of ANSI C. +// Regardless of the C library the same Dinkum +// STL library is used in both contexts. +// +// Similarly the Dinkum abridged STL that supports the loosely specified +// embedded C++ standard has not been tested and is unlikely to work +// on anything but the simplest library. +// ==================================================================== +// +// Some important information regarding the usage of POSIX semaphores: +// ------------------------------------------------------------------- +// +// VxWorks as a real time operating system handles threads somewhat +// different from what "normal" OSes do, regarding their scheduling! +// This could lead to a scenario called "priority inversion" when using +// semaphores, see http://en.wikipedia.org/wiki/Priority_inversion. +// +// Now, VxWorks POSIX-semaphores for DKM's default to the usage of +// priority inverting semaphores, which is fine. On the other hand, +// for RTP's it defaults to using non priority inverting semaphores, +// which could easily pose a serious problem for a real time process. +// +// To change the default properties for POSIX-semaphores in VxWorks 7 +// enable core > CORE_USER Menu > DEFAULT_PTHREAD_PRIO_INHERIT +// +// In VxWorks 6.x so as to integrate with boost. +// - Edit the file +// installDir/vxworks-6.x/target/usr/src/posix/pthreadLib.c +// - Around line 917 there should be the definition of the default +// mutex attributes: +// +// LOCAL pthread_mutexattr_t defaultMutexAttr = +// { +// PTHREAD_INITIALIZED_OBJ, PTHREAD_PRIO_NONE, 0, +// PTHREAD_MUTEX_DEFAULT +// }; +// +// Here, replace PTHREAD_PRIO_NONE by PTHREAD_PRIO_INHERIT. +// - Around line 1236 there should be a definition for the function +// pthread_mutexattr_init(). A couple of lines below you should +// find a block of code like this: +// +// pAttr->mutexAttrStatus = PTHREAD_INITIALIZED_OBJ; +// pAttr->mutexAttrProtocol = PTHREAD_PRIO_NONE; +// pAttr->mutexAttrPrioceiling = 0; +// pAttr->mutexAttrType = PTHREAD_MUTEX_DEFAULT; +// +// Here again, replace PTHREAD_PRIO_NONE by PTHREAD_PRIO_INHERIT. +// - Finally, rebuild your VSB. This will rebuild the libraries +// with the changed properties. That's it! Now, using boost should +// no longer cause any problems with task deadlocks! +// +// ==================================================================== + +// Block out all versions before vxWorks 6.x, as these don't work: +// Include header with the vxWorks version information and query them +#include +#if !defined(_WRS_VXWORKS_MAJOR) || (_WRS_VXWORKS_MAJOR < 6) +# error "The vxWorks version you're using is so badly outdated,\ + it doesn't work at all with boost, sorry, no chance!" +#endif + +// Handle versions above 5.X but below 6.9 +#if (_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR < 9) +// TODO: Starting from what version does vxWorks work with boost? +// We can't reasonably insert a #warning "" as a user hint here, +// as this will show up with every file including some boost header, +// badly bugging the user... So for the time being we just leave it. +#endif + +// vxWorks specific config options: +// -------------------------------- +#define BOOST_PLATFORM "vxWorks" + + +// Generally available headers: +#define BOOST_HAS_UNISTD_H +#define BOOST_HAS_STDINT_H +#define BOOST_HAS_DIRENT_H +//#define BOOST_HAS_SLIST + +// vxWorks does not have installed an iconv-library by default, +// so unfortunately no Unicode support from scratch is available! +// Thus, instead it is suggested to switch to ICU, as this seems +// to be the most complete and portable option... +#ifndef BOOST_LOCALE_WITH_ICU + #define BOOST_LOCALE_WITH_ICU +#endif + +// Generally available functionality: +#define BOOST_HAS_THREADS +#define BOOST_HAS_NANOSLEEP +#define BOOST_HAS_GETTIMEOFDAY +#define BOOST_HAS_CLOCK_GETTIME +#define BOOST_HAS_MACRO_USE_FACET + +// Generally available threading API's: +#define BOOST_HAS_PTHREADS +#define BOOST_HAS_SCHED_YIELD +#define BOOST_HAS_SIGACTION + +// Functionality available for RTPs only: +#ifdef __RTP__ +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# define BOOST_HAS_LOG1P +# define BOOST_HAS_EXPM1 +#endif + +// Functionality available for DKMs only: +#ifdef _WRS_KERNEL + // Luckily, at the moment there seems to be none! +#endif + +// These #defines allow detail/posix_features to work, since vxWorks doesn't +// #define them itself for DKMs (for RTPs on the contrary it does): +#ifdef _WRS_KERNEL +# ifndef _POSIX_TIMERS +# define _POSIX_TIMERS 1 +# endif +# ifndef _POSIX_THREADS +# define _POSIX_THREADS 1 +# endif +// no sysconf( _SC_PAGESIZE) in kernel +# define BOOST_THREAD_USES_GETPAGESIZE +#endif + +#if (_WRS_VXWORKS_MAJOR < 7) +// vxWorks-around: #defines CLOCKS_PER_SEC as sysClkRateGet() but +// miserably fails to #include the required to make +// sysClkRateGet() available! So we manually include it here. +# ifdef __RTP__ +# include +# include +# endif + +// vxWorks-around: In the macros INT32_C(), UINT32_C(), INT64_C() and +// UINT64_C() are defined erroneously, yielding not a signed/ +// unsigned long/long long type, but a signed/unsigned int/long +// type. Eventually this leads to compile errors in ratio_fwd.hpp, +// when trying to define several constants which do not fit into a +// long type! We correct them here by redefining. + +# include + +// Special behaviour for DKMs: + +// Some macro-magic to do the job +# define VX_JOIN(X, Y) VX_DO_JOIN(X, Y) +# define VX_DO_JOIN(X, Y) VX_DO_JOIN2(X, Y) +# define VX_DO_JOIN2(X, Y) X##Y + +// Correctly setup the macros +# undef INT32_C +# undef UINT32_C +# undef INT64_C +# undef UINT64_C +# define INT32_C(x) VX_JOIN(x, L) +# define UINT32_C(x) VX_JOIN(x, UL) +# define INT64_C(x) VX_JOIN(x, LL) +# define UINT64_C(x) VX_JOIN(x, ULL) + +// #include Libraries required for the following function adaption +# include +#endif // _WRS_VXWORKS_MAJOR < 7 + +#include +#include + +#if defined(_WRS_KERNEL) && (_CPPLIB_VER < 700) + // recent kernels use Dinkum clib v7.00+ + // with widechar but older kernels + // do not have the -header, + // but apparently they do have an intrinsic wchar_t meanwhile! +# define BOOST_NO_CWCHAR + + // Lots of wide-functions and -headers are unavailable for DKMs as well: +# define BOOST_NO_CWCTYPE +# define BOOST_NO_SWPRINTF +# define BOOST_NO_STD_WSTRING +# define BOOST_NO_STD_WSTREAMBUF +#endif + + +// Use C-linkage for the following helper functions +#ifdef __cplusplus +extern "C" { +#endif + +// vxWorks-around: The required functions getrlimit() and getrlimit() are missing. +// But we have the similar functions getprlimit() and setprlimit(), +// which may serve the purpose. +// Problem: The vxWorks-documentation regarding these functions +// doesn't deserve its name! It isn't documented what the first two +// parameters idtype and id mean, so we must fall back to an educated +// guess - null, argh... :-/ + +// TODO: getprlimit() and setprlimit() do exist for RTPs only, for whatever reason. +// Thus for DKMs there would have to be another implementation. +#if defined ( __RTP__) && (_WRS_VXWORKS_MAJOR < 7) + inline int getrlimit(int resource, struct rlimit *rlp){ + return getprlimit(0, 0, resource, rlp); + } + + inline int setrlimit(int resource, const struct rlimit *rlp){ + return setprlimit(0, 0, resource, const_cast(rlp)); + } +#endif + +// vxWorks has ftruncate() only, so we do simulate truncate(): +inline int truncate(const char *p, off_t l){ + int fd = open(p, O_WRONLY); + if (fd == -1){ + errno = EACCES; + return -1; + } + if (ftruncate(fd, l) == -1){ + close(fd); + errno = EACCES; + return -1; + } + return close(fd); +} + +#ifdef __GNUC__ +# define ___unused __attribute__((unused)) +#else +# define ___unused +#endif + +// Fake symlink handling by dummy functions: +inline int symlink(const char* path1 ___unused, const char* path2 ___unused){ + // vxWorks has no symlinks -> always return an error! + errno = EACCES; + return -1; +} + +inline ssize_t readlink(const char* path1 ___unused, char* path2 ___unused, size_t size ___unused){ + // vxWorks has no symlinks -> always return an error! + errno = EACCES; + return -1; +} + +#if (_WRS_VXWORKS_MAJOR < 7) + +inline int gettimeofday(struct timeval *tv, void * /*tzv*/) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + return 0; +} +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +/* + * moved to os/utils/unix/freind_h/times.h in VxWorks 7 + * to avoid conflict with MPL operator times + */ +#if (_WRS_VXWORKS_MAJOR < 7) +# ifdef __cplusplus + +// vxWorks provides neither struct tms nor function times()! +// We implement an empty dummy-function, simply setting the user +// and system time to the half of thew actual system ticks-value +// and the child user and system time to 0. +// Rather ugly but at least it suppresses compiler errors... +// Unfortunately, this of course *does* have an severe impact on +// dependant libraries, actually this is chrono only! Here it will +// not be possible to correctly use user and system times! But +// as vxWorks is lacking the ability to calculate user and system +// process times there seems to be no other possible solution. +struct tms{ + clock_t tms_utime; // User CPU time + clock_t tms_stime; // System CPU time + clock_t tms_cutime; // User CPU time of terminated child processes + clock_t tms_cstime; // System CPU time of terminated child processes +}; + + + inline clock_t times(struct tms *t){ + struct timespec ts; + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + clock_t ticks(static_cast(static_cast(ts.tv_sec) * CLOCKS_PER_SEC + + static_cast(ts.tv_nsec) * CLOCKS_PER_SEC / 1000000.0)); + t->tms_utime = ticks/2U; + t->tms_stime = ticks/2U; + t->tms_cutime = 0; // vxWorks is lacking the concept of a child process! + t->tms_cstime = 0; // -> Set the wait times for childs to 0 + return ticks; +} + + +namespace std { + using ::times; +} +# endif // __cplusplus +#endif // _WRS_VXWORKS_MAJOR < 7 + + +#ifdef __cplusplus +extern "C" void bzero (void *, size_t); // FD_ZERO uses bzero() but doesn't include strings.h + +// Put the selfmade functions into the std-namespace, just in case +namespace std { +# ifdef __RTP__ + using ::getrlimit; + using ::setrlimit; +# endif + using ::truncate; + using ::symlink; + using ::readlink; +# if (_WRS_VXWORKS_MAJOR < 7) + using ::gettimeofday; +# endif +} +#endif // __cplusplus + +// Some more macro-magic: +// vxWorks-around: Some functions are not present or broken in vxWorks +// but may be patched to life via helper macros... + +// Include signal.h which might contain a typo to be corrected here +#include + +#if (_WRS_VXWORKS_MAJOR < 7) +# define getpagesize() sysconf(_SC_PAGESIZE) // getpagesize is deprecated anyway! +inline int lstat(p, b) { return stat(p, b); } // lstat() == stat(), as vxWorks has no symlinks! +#endif + +#ifndef S_ISSOCK +# define S_ISSOCK(mode) ((mode & S_IFMT) == S_IFSOCK) // Is file a socket? +#endif +#ifndef FPE_FLTINV +# define FPE_FLTINV (FPE_FLTSUB+1) // vxWorks has no FPE_FLTINV, so define one as a dummy +#endif +#if !defined(BUS_ADRALN) && defined(BUS_ADRALNR) +# define BUS_ADRALN BUS_ADRALNR // Correct a supposed typo in vxWorks' +#endif +typedef int locale_t; // locale_t is a POSIX-extension, currently not present in vxWorks! + +// #include boilerplate code: +#include + +// vxWorks lies about XSI conformance, there is no nl_types.h: +#undef BOOST_HAS_NL_TYPES_H + +// vxWorks 7 adds C++11 support +// however it is optional, and does not match exactly the support determined +// by examining the Dinkum STL version and GCC version (or ICC and DCC) +#if !( defined( _WRS_CONFIG_LANG_LIB_CPLUS_CPLUS_USER_2011) || defined(_WRS_CONFIG_LIBCPLUS_STD)) +# define BOOST_NO_CXX11_ADDRESSOF // C11 addressof operator on memory location +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_NUMERIC_LIMITS // max_digits10 in test/../print_helper.hpp +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_STD_ALIGN + + +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST //serialization/test/test_list.cpp +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM //math/../test_data.hpp +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +#else +# ifndef BOOST_SYSTEM_NO_DEPRECATED +# define BOOST_SYSTEM_NO_DEPRECATED // workaround link error in spirit +# endif +#endif + + +// NONE is used in enums in lamda and other libraries +#undef NONE +// restrict is an iostreams class +#undef restrict +// affects some typeof tests +#undef V7 + +// use fake poll() from Unix layer in ASIO to get full functionality +// most libraries will use select() but this define allows 'iostream' functionality +// which is based on poll() only +#if (_WRS_VXWORKS_MAJOR > 6) +# ifndef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR +# define BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR +# endif +#else +# define BOOST_ASIO_DISABLE_SERIAL_PORT +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/wasm.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/wasm.hpp new file mode 100644 index 0000000..682b848 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/wasm.hpp @@ -0,0 +1,23 @@ +// (C) Copyright John Maddock 2020. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// WASM specific config options: + +#define BOOST_PLATFORM "Wasm" + +#ifdef __has_include +#if __has_include() +# define BOOST_HAS_UNISTD_H +#endif +#endif + +// boilerplate code: +#include +// +// fenv lacks the C++11 macros: +// +#define BOOST_NO_FENV_H diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/win32.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/win32.hpp new file mode 100644 index 0000000..450158f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/win32.hpp @@ -0,0 +1,90 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Bill Kempf 2001. +// (C) Copyright Aleksey Gurtovoy 2003. +// (C) Copyright Rene Rivera 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Win32 specific config options: + +#define BOOST_PLATFORM "Win32" + +// Get the information about the MinGW runtime, i.e. __MINGW32_*VERSION. +#if defined(__MINGW32__) +# include <_mingw.h> +#endif + +#if defined(__GNUC__) && !defined(BOOST_NO_SWPRINTF) +# define BOOST_NO_SWPRINTF +#endif + +// Default defines for BOOST_SYMBOL_EXPORT and BOOST_SYMBOL_IMPORT +// If a compiler doesn't support __declspec(dllexport)/__declspec(dllimport), +// its boost/config/compiler/ file must define BOOST_SYMBOL_EXPORT and +// BOOST_SYMBOL_IMPORT +#ifndef BOOST_SYMBOL_EXPORT +# define BOOST_HAS_DECLSPEC +# define BOOST_SYMBOL_EXPORT __declspec(dllexport) +# define BOOST_SYMBOL_IMPORT __declspec(dllimport) +#endif + +#if defined(__MINGW32__) && ((__MINGW32_MAJOR_VERSION > 2) || ((__MINGW32_MAJOR_VERSION == 2) && (__MINGW32_MINOR_VERSION >= 0))) +# define BOOST_HAS_STDINT_H +# ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS +# endif +# define BOOST_HAS_DIRENT_H +# define BOOST_HAS_UNISTD_H +#endif + +#if defined(__MINGW32__) && (__GNUC__ >= 4) +// Mingw has these functions but there are persistent problems +// with calls to these crashing, so disable for now: +//# define BOOST_HAS_EXPM1 +//# define BOOST_HAS_LOG1P +# define BOOST_HAS_GETTIMEOFDAY +#endif +// +// Win32 will normally be using native Win32 threads, +// but there is a pthread library avaliable as an option, +// we used to disable this when BOOST_DISABLE_WIN32 was +// defined but no longer - this should allow some +// files to be compiled in strict mode - while maintaining +// a consistent setting of BOOST_HAS_THREADS across +// all translation units (needed for shared_ptr etc). +// + +#ifndef BOOST_HAS_PTHREADS +# define BOOST_HAS_WINTHREADS +#endif + +// +// WinCE configuration: +// +#if defined(_WIN32_WCE) || defined(UNDER_CE) +# define BOOST_NO_ANSI_APIS +// Windows CE does not have a conforming signature for swprintf +# define BOOST_NO_SWPRINTF +#else +# define BOOST_HAS_GETSYSTEMTIMEASFILETIME +# define BOOST_HAS_THREADEX +# define BOOST_HAS_GETSYSTEMTIMEASFILETIME +#endif + +// +// Windows Runtime +// +#if defined(WINAPI_FAMILY) && \ + (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) +# define BOOST_NO_ANSI_APIS +#endif + +#ifndef BOOST_DISABLE_WIN32 +// WEK: Added +#define BOOST_HAS_FTIME +#define BOOST_WINDOWS 1 + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/zos.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/zos.hpp new file mode 100644 index 0000000..fa77999 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/platform/zos.hpp @@ -0,0 +1,32 @@ +// Copyright (c) 2017 Dynatrace +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +// See http://www.boost.org for most recent version. + +// Platform setup for IBM z/OS. + +#define BOOST_PLATFORM "IBM z/OS" + +#include // For __UU, __C99, __TR1, ... + +#if defined(__UU) +# define BOOST_HAS_GETTIMEOFDAY +#endif + +#if defined(_OPEN_THREADS) || defined(__SUSV3_THR) +# define BOOST_HAS_PTHREADS +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# define BOOST_HAS_THREADS +#endif + +#if defined(__SUSV3) || defined(__SUSV3_THR) +# define BOOST_HAS_SCHED_YIELD +#endif + +#define BOOST_HAS_SIGACTION +#define BOOST_HAS_UNISTD_H +#define BOOST_HAS_DIRENT_H +#define BOOST_HAS_NL_TYPES_H diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/pragma_message.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/pragma_message.hpp new file mode 100644 index 0000000..b2c5ff2 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/pragma_message.hpp @@ -0,0 +1,31 @@ +#ifndef BOOST_CONFIG_PRAGMA_MESSAGE_HPP_INCLUDED +#define BOOST_CONFIG_PRAGMA_MESSAGE_HPP_INCLUDED + +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +// BOOST_PRAGMA_MESSAGE("message") +// +// Expands to the equivalent of #pragma message("message") +// +// Note that this header is C compatible. + +#include + +#if defined(BOOST_DISABLE_PRAGMA_MESSAGE) +# define BOOST_PRAGMA_MESSAGE(x) +#elif defined(__INTEL_COMPILER) +# define BOOST_PRAGMA_MESSAGE(x) __pragma(message(__FILE__ "(" BOOST_STRINGIZE(__LINE__) "): note: " x)) +#elif defined(__GNUC__) +# define BOOST_PRAGMA_MESSAGE(x) _Pragma(BOOST_STRINGIZE(message(x))) +#elif defined(_MSC_VER) +# define BOOST_PRAGMA_MESSAGE(x) __pragma(message(__FILE__ "(" BOOST_STRINGIZE(__LINE__) "): note: " x)) +#else +# define BOOST_PRAGMA_MESSAGE(x) +#endif + +#endif // BOOST_CONFIG_PRAGMA_MESSAGE_HPP_INCLUDED diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/requires_threads.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/requires_threads.hpp new file mode 100644 index 0000000..c23a2ce --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/requires_threads.hpp @@ -0,0 +1,92 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_CONFIG_REQUIRES_THREADS_HPP +#define BOOST_CONFIG_REQUIRES_THREADS_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif + +#if defined(BOOST_DISABLE_THREADS) + +// +// special case to handle versions of gcc which don't currently support threads: +// +#if defined(__GNUC__) && ((__GNUC__ < 3) || (__GNUC_MINOR__ <= 3) || !defined(BOOST_STRICT_CONFIG)) +// +// this is checked up to gcc 3.3: +// +#if defined(__sgi) || defined(__hpux) +# error "Multi-threaded programs are not supported by gcc on HPUX or Irix (last checked with gcc 3.3)" +#endif + +#endif + +# error "Threading support unavaliable: it has been explicitly disabled with BOOST_DISABLE_THREADS" + +#elif !defined(BOOST_HAS_THREADS) + +# if defined __COMO__ +// Comeau C++ +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -D_MT (Windows) or -D_REENTRANT (Unix)" + +#elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) +// Intel +#ifdef _WIN32 +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: either /MT /MTd /MD or /MDd" +#else +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -openmp" +#endif + +# elif defined __GNUC__ +// GNU C++: +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)" + +#elif defined __sgi +// SGI MIPSpro C++ +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -D_SGI_MP_SOURCE" + +#elif defined __DECCXX +// Compaq Tru64 Unix cxx +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -pthread" + +#elif defined BOOST_BORLANDC +// Borland +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -tWM" + +#elif defined __MWERKS__ +// Metrowerks CodeWarrior +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: either -runtime sm, -runtime smd, -runtime dm, or -runtime dmd" + +#elif defined __SUNPRO_CC +// Sun Workshop Compiler C++ +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -mt" + +#elif defined __HP_aCC +// HP aCC +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: -mt" + +#elif defined(__IBMCPP__) +// IBM Visual Age +# error "Compiler threading support is not turned on. Please compile the code with the xlC_r compiler" + +#elif defined _MSC_VER +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for +// example) also #define _MSC_VER +# error "Compiler threading support is not turned on. Please set the correct command line options for threading: either /MT /MTd /MD or /MDd" + +#else + +# error "Compiler threading support is not turned on. Please consult your compiler's documentation for the appropriate options to use" + +#endif // compilers + +#endif // BOOST_HAS_THREADS + +#endif // BOOST_CONFIG_REQUIRES_THREADS_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/dinkumware.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/dinkumware.hpp new file mode 100644 index 0000000..46ffe09 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/dinkumware.hpp @@ -0,0 +1,324 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001. +// (C) Copyright Peter Dimov 2001. +// (C) Copyright David Abrahams 2002. +// (C) Copyright Guillaume Melquiond 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Dinkumware standard library config: + +#if !defined(_YVALS) && !defined(_CPPLIB_VER) +#include +#if !defined(_YVALS) && !defined(_CPPLIB_VER) +#error This is not the Dinkumware lib! +#endif +#endif + + +#if defined(_CPPLIB_VER) && (_CPPLIB_VER >= 306) + // full dinkumware 3.06 and above + // fully conforming provided the compiler supports it: +# if !(defined(_GLOBAL_USING) && (_GLOBAL_USING+0 > 0)) && !defined(BOOST_BORLANDC) && !defined(_STD) && !(defined(__ICC) && (__ICC >= 700)) // can be defined in yvals.h +# define BOOST_NO_STDC_NAMESPACE +# endif +# if !(defined(_HAS_MEMBER_TEMPLATES_REBIND) && (_HAS_MEMBER_TEMPLATES_REBIND+0 > 0)) && !(defined(_MSC_VER) && (_MSC_VER > 1300)) && defined(BOOST_MSVC) +# define BOOST_NO_STD_ALLOCATOR +# endif +# define BOOST_HAS_PARTIAL_STD_ALLOCATOR +# if defined(BOOST_MSVC) && (BOOST_MSVC < 1300) + // if this lib version is set up for vc6 then there is no std::use_facet: +# define BOOST_NO_STD_USE_FACET +# define BOOST_HAS_TWO_ARG_USE_FACET + // C lib functions aren't in namespace std either: +# define BOOST_NO_STDC_NAMESPACE + // and nor is +# define BOOST_NO_EXCEPTION_STD_NAMESPACE +# endif +// There's no numeric_limits support unless _LONGLONG is defined: +# if !defined(_LONGLONG) && (_CPPLIB_VER <= 310) +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +# endif +// 3.06 appears to have (non-sgi versions of) & , +// and no at all +#else +# define BOOST_MSVC_STD_ITERATOR 1 +# define BOOST_NO_STD_ITERATOR +# define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +# define BOOST_NO_STD_ALLOCATOR +# define BOOST_NO_STDC_NAMESPACE +# define BOOST_NO_STD_USE_FACET +# define BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN +# define BOOST_HAS_MACRO_USE_FACET +# ifndef _CPPLIB_VER + // Updated Dinkum library defines this, and provides + // its own min and max definitions, as does MTA version. +# ifndef __MTA__ +# define BOOST_NO_STD_MIN_MAX +# endif +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +# endif +#endif + +// +// std extension namespace is stdext for vc7.1 and later, +// the same applies to other compilers that sit on top +// of vc7.1 (Intel and Comeau): +// +#if defined(_MSC_VER) && (_MSC_VER >= 1310) && !defined(BOOST_BORLANDC) +# define BOOST_STD_EXTENSION_NAMESPACE stdext +#endif + + +#if (defined(_MSC_VER) && (_MSC_VER <= 1300) && !defined(BOOST_BORLANDC)) || !defined(_CPPLIB_VER) || (_CPPLIB_VER < 306) + // if we're using a dinkum lib that's + // been configured for VC6/7 then there is + // no iterator traits (true even for icl) +# define BOOST_NO_STD_ITERATOR_TRAITS +#endif + +#if defined(__ICL) && (__ICL < 800) && defined(_CPPLIB_VER) && (_CPPLIB_VER <= 310) +// Intel C++ chokes over any non-trivial use of +// this may be an overly restrictive define, but regex fails without it: +# define BOOST_NO_STD_LOCALE +#endif + +#if ((defined(BOOST_MSVC) && BOOST_MSVC >= 1400) || (defined(__clang__) && defined(_MSC_VER))) && (_MSC_VER < 1800) +// Fix for VC++ 8.0 on up ( I do not have a previous version to test ) +// or clang-cl. If exceptions are off you must manually include the +// header before including the header. Admittedly +// trying to use Boost libraries or the standard C++ libraries without +// exception support is not suggested but currently clang-cl ( v 3.4 ) +// does not support exceptions and must be compiled with exceptions off. +#if !_HAS_EXCEPTIONS +#include +#endif +#include +#if !_HAS_EXCEPTIONS +# define BOOST_NO_STD_TYPEINFO +#endif +#endif +#if defined(__ghs__) && !_HAS_NAMESPACE +# define BOOST_NO_STD_TYPEINFO +#endif + +// C++0x headers implemented in 520 (as shipped by Microsoft) +// +#if !defined(_CPPLIB_VER) || _CPPLIB_VER < 520 +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_SMART_PTR +#endif + +#if ((!defined(_HAS_TR1_IMPORTS) || (_HAS_TR1_IMPORTS+0 == 0)) && !defined(BOOST_NO_CXX11_HDR_TUPLE)) \ + && (!defined(_CPPLIB_VER) || _CPPLIB_VER < 610) +# define BOOST_NO_CXX11_HDR_TUPLE +#endif + +// C++0x headers implemented in 540 (as shipped by Microsoft) +// +#if !defined(_CPPLIB_VER) || _CPPLIB_VER < 540 +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_HDR_EXCEPTION +#endif + +// C++0x headers implemented in 610 (as shipped by Microsoft) +// +#if !defined(_CPPLIB_VER) || _CPPLIB_VER < 610 +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_ALLOCATOR +// 540 has std::align but it is not a conforming implementation +# define BOOST_NO_CXX11_STD_ALIGN +#endif + +// Before 650 std::pointer_traits has a broken rebind template +#if !defined(_CPPLIB_VER) || _CPPLIB_VER < 650 +# define BOOST_NO_CXX11_POINTER_TRAITS +#elif defined(BOOST_MSVC) && BOOST_MSVC < 1910 +# define BOOST_NO_CXX11_POINTER_TRAITS +#endif + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif (__cplusplus < 201402) && !defined(_MSC_VER) +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#elif !defined(_CPPLIB_VER) || (_CPPLIB_VER < 650) +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +#if !defined(_CPPLIB_VER) || (_CPPLIB_VER < 650) +# define BOOST_NO_CXX14_STD_EXCHANGE +#endif + +// C++17 features +#if !defined(_CPPLIB_VER) || (_CPPLIB_VER < 650) \ + || ((!defined(BOOST_MSVC) || (BOOST_MSVC < 1910))) && (!defined(__clang__) || !defined(_MSC_VER) || (_MSC_VER < 1929))\ + || !defined(_HAS_CXX17) || (_HAS_CXX17 == 0) +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_ITERATOR_TRAITS +# define BOOST_NO_CXX17_HDR_STRING_VIEW +# define BOOST_NO_CXX17_HDR_OPTIONAL +# define BOOST_NO_CXX17_HDR_VARIANT +# define BOOST_NO_CXX17_HDR_ANY +# define BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +# define BOOST_NO_CXX17_HDR_CHARCONV +# define BOOST_NO_CXX17_HDR_EXECUTION +# define BOOST_NO_CXX17_HDR_FILESYSTEM +#endif +#if !defined(_CPPLIB_VER) || (_CPPLIB_VER < 650) || !defined(_HAS_CXX17) || (_HAS_CXX17 == 0) || !defined(_MSVC_STL_UPDATE) || (_MSVC_STL_UPDATE < 201709) +# define BOOST_NO_CXX17_STD_INVOKE +#endif + +// C++20 features which aren't configured in suffix.hpp correctly: +#if !defined(_MSVC_STL_UPDATE) || (_MSVC_STL_UPDATE < 202008L) || !defined(_HAS_CXX20) || (_HAS_CXX20 == 0) +# define BOOST_NO_CXX20_HDR_CONCEPTS +#endif + +#if !(!defined(_CPPLIB_VER) || (_CPPLIB_VER < 650) || !defined(BOOST_MSVC) || (BOOST_MSVC < 1912) || !defined(_HAS_CXX17) || (_HAS_CXX17 == 0)) +// Deprecated std::iterator: +# define BOOST_NO_STD_ITERATOR +#endif + +#if defined(BOOST_INTEL) && (BOOST_INTEL <= 1400) +// Intel's compiler can't handle this header yet: +# define BOOST_NO_CXX11_HDR_ATOMIC +#endif + + +// 520..610 have std::addressof, but it doesn't support functions +// +#if !defined(_CPPLIB_VER) || _CPPLIB_VER < 650 +# define BOOST_NO_CXX11_ADDRESSOF +#endif + +// Bug specific to VC14, +// See https://connect.microsoft.com/VisualStudio/feedback/details/1348277/link-error-when-using-std-codecvt-utf8-utf16-char16-t +// and discussion here: http://blogs.msdn.com/b/vcblog/archive/2014/11/12/visual-studio-2015-preview-now-available.aspx?PageIndex=2 +#if defined(_CPPLIB_VER) && (_CPPLIB_VER == 650) && (!defined(_MSVC_STL_VERSION) || (_MSVC_STL_VERSION < 142)) +# define BOOST_NO_CXX11_HDR_CODECVT +#endif + +#if (_MSVC_LANG > 201700) && !defined(BOOST_NO_CXX11_HDR_CODECVT) +// +// is deprected as of C++17, and by default MSVC emits hard errors +// if you try to use it, so mark it as unavailable: +// +# define BOOST_NO_CXX11_HDR_CODECVT +#endif + +#if defined(_CPPLIB_VER) && (_CPPLIB_VER >= 650) +// If _HAS_AUTO_PTR_ETC is defined to 0, std::auto_ptr and std::random_shuffle are not available. +// See https://www.visualstudio.com/en-us/news/vs2015-vs.aspx#C++ +// and http://blogs.msdn.com/b/vcblog/archive/2015/06/19/c-11-14-17-features-in-vs-2015-rtm.aspx +# if defined(_HAS_AUTO_PTR_ETC) && (_HAS_AUTO_PTR_ETC == 0) +# define BOOST_NO_AUTO_PTR +# define BOOST_NO_CXX98_RANDOM_SHUFFLE +# define BOOST_NO_CXX98_FUNCTION_BASE +# define BOOST_NO_CXX98_BINDERS +# elif defined(_HAS_DEPRECATED_ADAPTOR_TYPEDEFS) && (_HAS_DEPRECATED_ADAPTOR_TYPEDEFS == 0) +# define BOOST_NO_CXX98_BINDERS +# endif +#endif +// +// Things deprecated in C++20: +// +#if defined(_HAS_CXX20) +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +#endif + + +// +// Things not supported by the CLR: +#ifdef _M_CEE +#ifndef BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_MUTEX +#endif +#ifndef BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_HDR_ATOMIC +#endif +#ifndef BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_FUTURE +#endif +#ifndef BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +#endif +#ifndef BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_THREAD +#endif +#ifndef BOOST_NO_CXX14_HDR_SHARED_MUTEX +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#ifndef BOOST_NO_CXX14_STD_EXCHANGE +# define BOOST_NO_CXX14_STD_EXCHANGE +#endif +#ifndef BOOST_NO_FENV_H +# define BOOST_NO_FENV_H +#endif +#endif + +#ifdef _CPPLIB_VER +# define BOOST_DINKUMWARE_STDLIB _CPPLIB_VER +#else +# define BOOST_DINKUMWARE_STDLIB 1 +#endif + +// BOOST_MSSTL_VERSION: as _MSVC_STL_VERSION, but for earlier releases as well + +#if defined(_MSVC_STL_VERSION) // VS2017 (14.1) and above +# define BOOST_MSSTL_VERSION _MSVC_STL_VERSION + +#elif defined(_CPPLIB_VER) && _CPPLIB_VER >= 650 // VS2015 (14.0) +# define BOOST_MSSTL_VERSION 140 + +#elif defined(_CPPLIB_VER) && _CPPLIB_VER >= 610 // VS2013 (12.0) +# define BOOST_MSSTL_VERSION 120 + +#elif defined(_CPPLIB_VER) && _CPPLIB_VER >= 540 // VS2012 (11.0) +# define BOOST_MSSTL_VERSION 110 + +#elif defined(_CPPLIB_VER) && _CPPLIB_VER >= 520 // VS2010 (10.0) +# define BOOST_MSSTL_VERSION 100 + +#elif defined(_CPPLIB_VER) && _CPPLIB_VER >= 505 // VS2008SP1 (9.0) +# define BOOST_MSSTL_VERSION 91 + +#elif defined(_CPPLIB_VER) && _CPPLIB_VER >= 503 // VS2008 (also 9.0) +# define BOOST_MSSTL_VERSION 90 + +#elif defined(_CPPLIB_VER) && _CPPLIB_VER >= 405 // VS2005 (8.0) +# define BOOST_MSSTL_VERSION 80 + +#endif + +// + +#ifdef _CPPLIB_VER +# define BOOST_STDLIB "Dinkumware standard library version " BOOST_STRINGIZE(_CPPLIB_VER) +#else +# define BOOST_STDLIB "Dinkumware standard library version 1.x" +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcomo.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcomo.hpp new file mode 100644 index 0000000..6a8a161 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcomo.hpp @@ -0,0 +1,93 @@ +// (C) Copyright John Maddock 2002 - 2003. +// (C) Copyright Jens Maurer 2002 - 2003. +// (C) Copyright Beman Dawes 2002 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Comeau STL: + +#if !defined(__LIBCOMO__) +# include +# if !defined(__LIBCOMO__) +# error "This is not the Comeau STL!" +# endif +#endif + +// +// std::streambuf is non-standard +// NOTE: versions of libcomo prior to beta28 have octal version numbering, +// e.g. version 25 is 21 (dec) +#if __LIBCOMO_VERSION__ <= 22 +# define BOOST_NO_STD_WSTREAMBUF +#endif + +#if (__LIBCOMO_VERSION__ <= 31) && defined(_WIN32) +#define BOOST_NO_SWPRINTF +#endif + +#if __LIBCOMO_VERSION__ >= 31 +# define BOOST_HAS_HASH +# define BOOST_HAS_SLIST +#endif + +// C++0x headers not yet implemented +// +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_EXCEPTION +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#else +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +# define BOOST_NO_CXX14_STD_EXCHANGE + +// C++17 features +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_ITERATOR_TRAITS + +// +// Intrinsic type_traits support. +// The SGI STL has it's own __type_traits class, which +// has intrinsic compiler support with SGI's compilers. +// Whatever map SGI style type traits to boost equivalents: +// +#define BOOST_HAS_SGI_TYPE_TRAITS + +#define BOOST_STDLIB "Comeau standard library " BOOST_STRINGIZE(__LIBCOMO_VERSION__) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcpp.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcpp.hpp new file mode 100644 index 0000000..0e9f244 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libcpp.hpp @@ -0,0 +1,180 @@ +// (C) Copyright Christopher Jefferson 2011. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// config for libc++ +// Might need more in here later. + +#if !defined(_LIBCPP_VERSION) +# include +# if !defined(_LIBCPP_VERSION) +# error "This is not libc++!" +# endif +#endif + +#define BOOST_STDLIB "libc++ version " BOOST_STRINGIZE(_LIBCPP_VERSION) + +#define BOOST_HAS_THREADS + +#ifdef _LIBCPP_HAS_NO_VARIADICS +# define BOOST_NO_CXX11_HDR_TUPLE +#endif + +// BOOST_NO_CXX11_ALLOCATOR should imply no support for the C++11 +// allocator model. The C++11 allocator model requires a conforming +// std::allocator_traits which is only possible with C++11 template +// aliases since members rebind_alloc and rebind_traits require it. +#if defined(_LIBCPP_HAS_NO_TEMPLATE_ALIASES) +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +#endif + +#if __cplusplus < 201103 +// +// These two appear to be somewhat useable in C++03 mode, there may be others... +// +//# define BOOST_NO_CXX11_HDR_ARRAY +//# define BOOST_NO_CXX11_HDR_FORWARD_LIST + +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_EXCEPTION +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_FUTURE +#elif _LIBCPP_VERSION < 3700 +// +// These appear to be unusable/incomplete so far: +// +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_FUTURE +#endif + + +#if _LIBCPP_VERSION < 3700 +// libc++ uses a non-standard messages_base +#define BOOST_NO_STD_MESSAGES +#endif + +// C++14 features +#if (_LIBCPP_VERSION < 3700) || (__cplusplus <= 201402L) +# define BOOST_NO_CXX14_STD_EXCHANGE +#endif + +// C++17 features +#if (_LIBCPP_VERSION < 4000) || (__cplusplus <= 201402L) +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_HDR_OPTIONAL +# define BOOST_NO_CXX17_HDR_STRING_VIEW +# define BOOST_NO_CXX17_HDR_VARIANT +#endif +#if (_LIBCPP_VERSION > 4000) && (__cplusplus > 201402L) && !defined(_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR) +# define BOOST_NO_AUTO_PTR +#endif +#if (_LIBCPP_VERSION > 4000) && (__cplusplus > 201402L) && !defined(_LIBCPP_ENABLE_CXX17_REMOVED_RANDOM_SHUFFLE) +# define BOOST_NO_CXX98_RANDOM_SHUFFLE +#endif +#if (_LIBCPP_VERSION > 4000) && (__cplusplus > 201402L) && !defined(_LIBCPP_ENABLE_CXX17_REMOVED_BINDERS) +# define BOOST_NO_CXX98_BINDERS +#endif + +#if defined(__cplusplus) && defined(__has_include) +#if __has_include() +#include + +#if !defined(__cpp_lib_execution) || (__cpp_lib_execution < 201603L) +# define BOOST_NO_CXX17_HDR_EXECUTION +#endif +#if !defined(__cpp_lib_invoke) || (__cpp_lib_invoke < 201411L) +#define BOOST_NO_CXX17_STD_INVOKE +#endif + +#if(_LIBCPP_VERSION < 9000) +// as_writable_bytes is missing. +# define BOOST_NO_CXX20_HDR_SPAN +#endif + +#else +#define BOOST_NO_CXX17_STD_INVOKE // Invoke support is incomplete (no invoke_result) +#define BOOST_NO_CXX17_HDR_EXECUTION +#endif +#else +#define BOOST_NO_CXX17_STD_INVOKE // Invoke support is incomplete (no invoke_result) +#define BOOST_NO_CXX17_HDR_EXECUTION +#endif + +#if _LIBCPP_VERSION < 10000 // What's the correct version check here? +#define BOOST_NO_CXX17_ITERATOR_TRAITS +#endif + +#if (_LIBCPP_VERSION <= 1101) && !defined(BOOST_NO_CXX11_THREAD_LOCAL) +// This is a bit of a sledgehammer, because really it's just libc++abi that has no +// support for thread_local, leading to linker errors such as +// "undefined reference to `__cxa_thread_atexit'". It is fixed in the +// most recent releases of libc++abi though... +# define BOOST_NO_CXX11_THREAD_LOCAL +#endif + +#if defined(__linux__) && (_LIBCPP_VERSION < 6000) && !defined(BOOST_NO_CXX11_THREAD_LOCAL) +// After libc++-dev is installed on Trusty, clang++-libc++ almost works, +// except uses of `thread_local` fail with undefined reference to +// `__cxa_thread_atexit`. +// +// clang's libc++abi provides an implementation by deferring to the glibc +// implementation, which may or may not be available (it is not on Trusty). +// clang 4's libc++abi will provide an implementation if one is not in glibc +// though, so thread local support should work with clang 4 and above as long +// as libc++abi is linked in. +# define BOOST_NO_CXX11_THREAD_LOCAL +#endif + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus <= 201103 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +#if !defined(BOOST_NO_CXX14_HDR_SHARED_MUTEX) && (_LIBCPP_VERSION < 5000) +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +#if _LIBCPP_VERSION >= 15000 +// +// Unary function is now deprecated in C++11 and later: +// +#if __cplusplus >= 201103L +#define BOOST_NO_CXX98_FUNCTION_BASE +#endif +#endif + +// --- end --- diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libstdcpp3.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libstdcpp3.hpp new file mode 100644 index 0000000..ad70936 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/libstdcpp3.hpp @@ -0,0 +1,482 @@ +// (C) Copyright John Maddock 2001. +// (C) Copyright Jens Maurer 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// config for libstdc++ v3 +// not much to go in here: + +#define BOOST_GNU_STDLIB 1 + +#ifdef __GLIBCXX__ +#define BOOST_STDLIB "GNU libstdc++ version " BOOST_STRINGIZE(__GLIBCXX__) +#else +#define BOOST_STDLIB "GNU libstdc++ version " BOOST_STRINGIZE(__GLIBCPP__) +#endif + +#if !defined(_GLIBCPP_USE_WCHAR_T) && !defined(_GLIBCXX_USE_WCHAR_T) +# define BOOST_NO_CWCHAR +# define BOOST_NO_CWCTYPE +# define BOOST_NO_STD_WSTRING +# define BOOST_NO_STD_WSTREAMBUF +#endif + +#if defined(__osf__) && !defined(_REENTRANT) \ + && ( defined(_GLIBCXX_HAVE_GTHR_DEFAULT) || defined(_GLIBCPP_HAVE_GTHR_DEFAULT) ) +// GCC 3 on Tru64 forces the definition of _REENTRANT when any std lib header +// file is included, therefore for consistency we define it here as well. +# define _REENTRANT +#endif + +#ifdef __GLIBCXX__ // gcc 3.4 and greater: +# if defined(_GLIBCXX_HAVE_GTHR_DEFAULT) \ + || defined(_GLIBCXX__PTHREADS) \ + || defined(_GLIBCXX_HAS_GTHREADS) \ + || defined(_WIN32) \ + || defined(_AIX) \ + || defined(__HAIKU__) + // + // If the std lib has thread support turned on, then turn it on in Boost + // as well. We do this because some gcc-3.4 std lib headers define _REENTANT + // while others do not... + // +# define BOOST_HAS_THREADS +# else +# define BOOST_DISABLE_THREADS +# endif +#elif defined(__GLIBCPP__) \ + && !defined(_GLIBCPP_HAVE_GTHR_DEFAULT) \ + && !defined(_GLIBCPP__PTHREADS) + // disable thread support if the std lib was built single threaded: +# define BOOST_DISABLE_THREADS +#endif + +#if (defined(linux) || defined(__linux) || defined(__linux__)) && defined(__arm__) && defined(_GLIBCPP_HAVE_GTHR_DEFAULT) +// linux on arm apparently doesn't define _REENTRANT +// so just turn on threading support whenever the std lib is thread safe: +# define BOOST_HAS_THREADS +#endif + +#if !defined(_GLIBCPP_USE_LONG_LONG) \ + && !defined(_GLIBCXX_USE_LONG_LONG)\ + && defined(BOOST_HAS_LONG_LONG) +// May have been set by compiler/*.hpp, but "long long" without library +// support is useless. +# undef BOOST_HAS_LONG_LONG +#endif + +// Apple doesn't seem to reliably defined a *unix* macro +#if !defined(CYGWIN) && ( defined(__unix__) \ + || defined(__unix) \ + || defined(unix) \ + || defined(__APPLE__) \ + || defined(__APPLE) \ + || defined(APPLE)) +# include +#endif + +#ifndef __VXWORKS__ // VxWorks uses Dinkum, not GNU STL with GCC +#if defined(__GLIBCXX__) || (defined(__GLIBCPP__) && __GLIBCPP__>=20020514) // GCC >= 3.1.0 +# define BOOST_STD_EXTENSION_NAMESPACE __gnu_cxx +# define BOOST_HAS_SLIST +# define BOOST_HAS_HASH +# define BOOST_SLIST_HEADER +# if !defined(__GNUC__) || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define BOOST_HASH_SET_HEADER +# define BOOST_HASH_MAP_HEADER +# else +# define BOOST_HASH_SET_HEADER +# define BOOST_HASH_MAP_HEADER +# endif +#endif +#endif + +#if defined(__has_include) +#if defined(BOOST_HAS_HASH) +#if !__has_include(BOOST_HASH_SET_HEADER) || (__GNUC__ >= 10) +#undef BOOST_HAS_HASH +#undef BOOST_HAS_SET_HEADER +#undef BOOST_HAS_MAP_HEADER +#endif +#if !__has_include(BOOST_SLIST_HEADER) +#undef BOOST_HAS_SLIST +#undef BOOST_HAS_SLIST_HEADER +#endif +#endif +#endif + +// +// Decide whether we have C++11 support turned on: +// +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103) +# define BOOST_LIBSTDCXX11 +#endif + +// +// Decide which version of libstdc++ we have, normally +// libstdc++ C++0x support is detected via __GNUC__, __GNUC_MINOR__, and possibly +// __GNUC_PATCHLEVEL__ at the suggestion of Jonathan Wakely, one of the libstdc++ +// developers. He also commented: +// +// "I'm not sure how useful __GLIBCXX__ is for your purposes, for instance in +// GCC 4.2.4 it is set to 20080519 but in GCC 4.3.0 it is set to 20080305. +// Although 4.3.0 was released earlier than 4.2.4, it has better C++0x support +// than any release in the 4.2 series." +// +// Another resource for understanding libstdc++ features is: +// http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#manual.intro.status.standard.200x +// +// However, using the GCC version number fails when the compiler is clang since this +// only ever claims to emulate GCC-4.2, see https://svn.boost.org/trac/boost/ticket/7473 +// for a long discussion on this issue. What we can do though is use clang's __has_include +// to detect the presence of a C++11 header that was introduced with a specific GCC release. +// We still have to be careful though as many such headers were buggy and/or incomplete when +// first introduced, so we only check for headers that were fully featured from day 1, and then +// use that to infer the underlying GCC version: +// +#ifdef __clang__ + +#ifdef _GLIBCXX_RELEASE +# define BOOST_LIBSTDCXX_VERSION (_GLIBCXX_RELEASE * 10000 + 100) +#else +// +// We figure out which gcc version issued this std lib +// by checking which headers are available: +// +#if __has_include() +# define BOOST_LIBSTDCXX_VERSION 120100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 110100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 100100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 90100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 80100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 70100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 60100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 50100 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 40900 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 40800 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 40700 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 40600 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 40500 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 40400 +#elif __has_include() +# define BOOST_LIBSTDCXX_VERSION 40300 +#endif +#endif +// +// If BOOST_HAS_FLOAT128 is set, now that we know the std lib is libstdc++3, check to see if the std lib is +// configured to support this type. If not disable it: +// +#if defined(BOOST_HAS_FLOAT128) && !defined(_GLIBCXX_USE_FLOAT128) +# undef BOOST_HAS_FLOAT128 +#endif + +#if (BOOST_LIBSTDCXX_VERSION >= 100000) && defined(BOOST_HAS_HASH) +// +// hash_set/hash_map deprecated and have terminal bugs: +// +#undef BOOST_HAS_HASH +#undef BOOST_HAS_SET_HEADER +#undef BOOST_HAS_MAP_HEADER +#endif + + +#if (BOOST_LIBSTDCXX_VERSION >= 100000) && defined(BOOST_HAS_HASH) +// +// hash_set/hash_map deprecated and have terminal bugs: +// +#undef BOOST_HAS_HASH +#undef BOOST_HAS_SET_HEADER +#undef BOOST_HAS_MAP_HEADER +#endif + + +#if (BOOST_LIBSTDCXX_VERSION < 50100) +// libstdc++ does not define this function as it's deprecated in C++11, but clang still looks for it, +// defining it here is a terrible cludge, but should get things working: +extern "C" char *gets (char *__s); +#endif +// +// clang is unable to parse some GCC headers, add those workarounds here: +// +#if BOOST_LIBSTDCXX_VERSION < 50000 +# define BOOST_NO_CXX11_HDR_REGEX +#endif +// +// GCC 4.7.x has no __cxa_thread_atexit which +// thread_local objects require for cleanup: +// +#if BOOST_LIBSTDCXX_VERSION < 40800 +# define BOOST_NO_CXX11_THREAD_LOCAL +#endif +// +// Early clang versions can handle , not exactly sure which versions +// but certainly up to clang-3.8 and gcc-4.6: +// +#if (__clang_major__ < 5) +# if BOOST_LIBSTDCXX_VERSION < 40800 +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_CHRONO +# endif +#endif + +// +// GCC 4.8 and 9 add working versions of and respectively. +// However, we have no test for these as the headers were present but broken +// in early GCC versions. +// +#endif + +#if defined(__SUNPRO_CC) && (__SUNPRO_CC >= 0x5130) && (__cplusplus >= 201103L) +// +// Oracle Solaris compiler uses it's own verison of libstdc++ but doesn't +// set __GNUC__ +// +#if __SUNPRO_CC >= 0x5140 +#define BOOST_LIBSTDCXX_VERSION 50100 +#else +#define BOOST_LIBSTDCXX_VERSION 40800 +#endif +#endif + +#if !defined(BOOST_LIBSTDCXX_VERSION) +# define BOOST_LIBSTDCXX_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +// std::auto_ptr isn't provided with _GLIBCXX_DEPRECATED=0 (GCC 4.5 and earlier) +// or _GLIBCXX_USE_DEPRECATED=0 (GCC 4.6 and later). +#if defined(BOOST_LIBSTDCXX11) +# if BOOST_LIBSTDCXX_VERSION < 40600 +# if !_GLIBCXX_DEPRECATED +# define BOOST_NO_AUTO_PTR +# endif +# elif !defined(_GLIBCXX_USE_DEPRECATED) || !_GLIBCXX_USE_DEPRECATED +# define BOOST_NO_AUTO_PTR +# define BOOST_NO_CXX98_BINDERS +# endif +#endif + +// C++0x headers in GCC 4.3.0 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 40300) || !defined(BOOST_LIBSTDCXX11) +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +#endif + +// C++0x headers in GCC 4.4.0 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 40400) || !defined(BOOST_LIBSTDCXX11) +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_EXCEPTION +#else +# define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG +# define BOOST_HAS_TR1_COMPLEX_OVERLOADS +#endif + +// C++0x features in GCC 4.5.0 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 40500) || !defined(BOOST_LIBSTDCXX11) +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_RANDOM +#endif + +// C++0x features in GCC 4.6.0 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 40600) || !defined(BOOST_LIBSTDCXX11) +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX17_ITERATOR_TRAITS +#endif + +// C++0x features in GCC 4.7.0 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 40700) || !defined(BOOST_LIBSTDCXX11) +// Note that although existed prior to 4.7, "steady_clock" is spelled "monotonic_clock" +// so 4.7.0 is the first truly conforming one. +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +#endif +// C++0x features in GCC 4.8.0 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 40800) || !defined(BOOST_LIBSTDCXX11) +// Note that although existed prior to gcc 4.8 it was largely unimplemented for many types: +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_HDR_THREAD +#endif +// C++0x features in GCC 4.9.0 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 40900) || !defined(BOOST_LIBSTDCXX11) +// Although is present and compilable against, the actual implementation is not functional +// even for the simplest patterns such as "\d" or "[0-9]". This is the case at least in gcc up to 4.8, inclusively. +# define BOOST_NO_CXX11_HDR_REGEX +#endif +#if (BOOST_LIBSTDCXX_VERSION < 40900) || (__cplusplus <= 201103) +# define BOOST_NO_CXX14_STD_EXCHANGE +#endif + +// +// C++0x features in GCC 5.1 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 50100) || !defined(BOOST_LIBSTDCXX11) +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_STD_ALIGN +#endif + +// +// C++17 features in GCC 7.1 and later +// +#if (BOOST_LIBSTDCXX_VERSION < 70100) || (__cplusplus <= 201402L) +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_HDR_OPTIONAL +# define BOOST_NO_CXX17_HDR_STRING_VIEW +# define BOOST_NO_CXX17_HDR_VARIANT +#endif + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus <= 201103 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +// +// has a dependency to Intel's thread building blocks: +// unless these are installed seperately, including leads +// to inscrutable errors inside libstdc++'s own headers. +// +#if (BOOST_LIBSTDCXX_VERSION < 100100) +#if !__has_include() +#define BOOST_NO_CXX17_HDR_EXECUTION +#endif +#endif +#elif __cplusplus < 201402 || (BOOST_LIBSTDCXX_VERSION < 40900) || !defined(BOOST_LIBSTDCXX11) +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +#if BOOST_LIBSTDCXX_VERSION < 100100 +// +// The header may be present but is incomplete: +// +# define BOOST_NO_CXX17_HDR_CHARCONV +#endif + +#if BOOST_LIBSTDCXX_VERSION < 110000 +// +// Header may be present but lacks std::bit_cast: +// +#define BOOST_NO_CXX20_HDR_BIT +#endif + +#if BOOST_LIBSTDCXX_VERSION >= 120000 +// +// Unary function is now deprecated in C++11 and later: +// +#if __cplusplus >= 201103L +#define BOOST_NO_CXX98_FUNCTION_BASE +#endif +#endif + +#ifndef __cpp_impl_coroutine +# define BOOST_NO_CXX20_HDR_COROUTINE +#endif + +// +// These next defines are mostly for older clang versions with a newer libstdc++ : +// +#if !defined(__cpp_lib_concepts) +#if !defined(BOOST_NO_CXX20_HDR_COMPARE) +# define BOOST_NO_CXX20_HDR_COMPARE +#endif +#if !defined(BOOST_NO_CXX20_HDR_CONCEPTS) +# define BOOST_NO_CXX20_HDR_CONCEPTS +#endif +#if !defined(BOOST_NO_CXX20_HDR_SPAN) +# define BOOST_NO_CXX20_HDR_SPAN +#endif +#if !defined(BOOST_NO_CXX20_HDR_RANGES) +# define BOOST_NO_CXX20_HDR_RANGES +#endif +#endif + +#if defined(__clang__) +#if (__clang_major__ < 11) && !defined(BOOST_NO_CXX20_HDR_RANGES) +# define BOOST_NO_CXX20_HDR_RANGES +#endif +#if (__clang_major__ < 10) && (BOOST_LIBSTDCXX_VERSION >= 110000) && !defined(BOOST_NO_CXX11_HDR_CHRONO) +// Old clang can't parse : +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +#endif +#endif + +#if defined(__clang__) && (BOOST_LIBSTDCXX_VERSION < 40300) && !defined(BOOST_NO_CXX11_NULLPTR) +# define BOOST_NO_CXX11_NULLPTR +#endif +#if defined(__clang__) && (BOOST_LIBSTDCXX_VERSION < 40300) && defined(BOOST_HAS_INT128) && defined(__APPLE_CC__) +#undef BOOST_HAS_INT128 +#endif + +// +// Headers not present on Solaris with the Oracle compiler: +#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5140) +#define BOOST_NO_CXX11_HDR_FUTURE +#define BOOST_NO_CXX11_HDR_FORWARD_LIST +#define BOOST_NO_CXX11_HDR_ATOMIC +// shared_ptr is present, but is not convertible to bool +// which causes all kinds of problems especially in Boost.Thread +// but probably elsewhere as well. +#define BOOST_NO_CXX11_SMART_PTR +#endif + +#if (!defined(_GLIBCXX_HAS_GTHREADS) || !defined(_GLIBCXX_USE_C99_STDINT_TR1)) + // Headers not always available: +# ifndef BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# endif +# ifndef BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_MUTEX +# endif +# ifndef BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_THREAD +# endif +# ifndef BOOST_NO_CXX14_HDR_SHARED_MUTEX +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +# endif +#endif + +#if (!defined(_GTHREAD_USE_MUTEX_TIMEDLOCK) || (_GTHREAD_USE_MUTEX_TIMEDLOCK == 0)) && !defined(BOOST_NO_CXX11_HDR_MUTEX) && (__GNUC__ < 6) +// Timed mutexes are not always available: +# define BOOST_NO_CXX11_HDR_MUTEX +#endif + +// --- end --- diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/modena.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/modena.hpp new file mode 100644 index 0000000..31a26c8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/modena.hpp @@ -0,0 +1,79 @@ +// (C) Copyright Jens Maurer 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Modena C++ standard library (comes with KAI C++) + +#if !defined(MSIPL_COMPILE_H) +# include +# if !defined(__MSIPL_COMPILE_H) +# error "This is not the Modena C++ library!" +# endif +#endif + +#ifndef MSIPL_NL_TYPES +#define BOOST_NO_STD_MESSAGES +#endif + +#ifndef MSIPL_WCHART +#define BOOST_NO_STD_WSTRING +#endif + +// C++0x headers not yet implemented +// +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX11_HDR_EXCEPTION + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#else +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +# define BOOST_NO_CXX14_STD_EXCHANGE + +// C++17 features +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_ITERATOR_TRAITS + +#define BOOST_STDLIB "Modena C++ standard library" + + + + + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/msl.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/msl.hpp new file mode 100644 index 0000000..f2f8259 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/msl.hpp @@ -0,0 +1,98 @@ +// (C) Copyright John Maddock 2001. +// (C) Copyright Darin Adler 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Metrowerks standard library: + +#ifndef __MSL_CPP__ +# include +# ifndef __MSL_CPP__ +# error This is not the MSL standard library! +# endif +#endif + +#if __MSL_CPP__ >= 0x6000 // Pro 6 +# define BOOST_HAS_HASH +# define BOOST_STD_EXTENSION_NAMESPACE Metrowerks +#endif +#define BOOST_HAS_SLIST + +#if __MSL_CPP__ < 0x6209 +# define BOOST_NO_STD_MESSAGES +#endif + +// check C lib version for +#include + +#if defined(__MSL__) && (__MSL__ >= 0x5000) +# define BOOST_HAS_STDINT_H +# if !defined(__PALMOS_TRAPS__) +# define BOOST_HAS_UNISTD_H +# endif + // boilerplate code: +# include +#endif + +#if defined(_MWMT) || _MSL_THREADSAFE +# define BOOST_HAS_THREADS +#endif + +#ifdef _MSL_NO_EXPLICIT_FUNC_TEMPLATE_ARG +# define BOOST_NO_STD_USE_FACET +# define BOOST_HAS_TWO_ARG_USE_FACET +#endif + +// C++0x headers not yet implemented +// +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX11_HDR_EXCEPTION + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#else +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +# define BOOST_NO_CXX14_STD_EXCHANGE + +// C++17 features +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_ITERATOR_TRAITS + +#define BOOST_STDLIB "Metrowerks Standard Library version " BOOST_STRINGIZE(__MSL_CPP__) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/roguewave.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/roguewave.hpp new file mode 100644 index 0000000..03a6576 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/roguewave.hpp @@ -0,0 +1,208 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001. +// (C) Copyright David Abrahams 2003. +// (C) Copyright Boris Gubenko 2007. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Rogue Wave std lib: + +#define BOOST_RW_STDLIB 1 + +#if !defined(__STD_RWCOMPILER_H__) && !defined(_RWSTD_VER) +# include +# if !defined(__STD_RWCOMPILER_H__) && !defined(_RWSTD_VER) +# error This is not the Rogue Wave standard library +# endif +#endif +// +// figure out a consistent version number: +// +#ifndef _RWSTD_VER +# define BOOST_RWSTD_VER 0x010000 +#elif _RWSTD_VER < 0x010000 +# define BOOST_RWSTD_VER (_RWSTD_VER << 8) +#else +# define BOOST_RWSTD_VER _RWSTD_VER +#endif + +#ifndef _RWSTD_VER +# define BOOST_STDLIB "Rogue Wave standard library version (Unknown version)" +#elif _RWSTD_VER < 0x04010200 + # define BOOST_STDLIB "Rogue Wave standard library version " BOOST_STRINGIZE(_RWSTD_VER) +#else +# ifdef _RWSTD_VER_STR +# define BOOST_STDLIB "Apache STDCXX standard library version " _RWSTD_VER_STR +# else +# define BOOST_STDLIB "Apache STDCXX standard library version " BOOST_STRINGIZE(_RWSTD_VER) +# endif +#endif + +// +// Prior to version 2.2.0 the primary template for std::numeric_limits +// does not have compile time constants, even though specializations of that +// template do: +// +#if BOOST_RWSTD_VER < 0x020200 +# define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +#endif + +// Sun CC 5.5 patch 113817-07 adds long long specialization, but does not change the +// library version number (http://sunsolve6.sun.com/search/document.do?assetkey=1-21-113817): +#if BOOST_RWSTD_VER <= 0x020101 && (!defined(__SUNPRO_CC) || (__SUNPRO_CC < 0x550)) +# define BOOST_NO_LONG_LONG_NUMERIC_LIMITS +# endif + +// +// Borland version of numeric_limits lacks __int64 specialisation: +// +#ifdef BOOST_BORLANDC +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +#endif + +// +// No std::iterator if it can't figure out default template args: +// +#if defined(_RWSTD_NO_SIMPLE_DEFAULT_TEMPLATES) || defined(RWSTD_NO_SIMPLE_DEFAULT_TEMPLATES) || (BOOST_RWSTD_VER < 0x020000) +# define BOOST_NO_STD_ITERATOR +#endif + +// +// No iterator traits without partial specialization: +// +#if defined(_RWSTD_NO_CLASS_PARTIAL_SPEC) || defined(RWSTD_NO_CLASS_PARTIAL_SPEC) +# define BOOST_NO_STD_ITERATOR_TRAITS +#endif + +// +// Prior to version 2.0, std::auto_ptr was buggy, and there were no +// new-style iostreams, and no conformant std::allocator: +// +#if (BOOST_RWSTD_VER < 0x020000) +# define BOOST_NO_AUTO_PTR +# define BOOST_NO_STRINGSTREAM +# define BOOST_NO_STD_ALLOCATOR +# define BOOST_NO_STD_LOCALE +#endif + +// +// No template iterator constructors without member template support: +// +#if defined(RWSTD_NO_MEMBER_TEMPLATES) || defined(_RWSTD_NO_MEMBER_TEMPLATES) +# define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +#endif + +// +// RW defines _RWSTD_ALLOCATOR if the allocator is conformant and in use +// (the or _HPACC_ part is a hack - the library seems to define _RWSTD_ALLOCATOR +// on HP aCC systems even though the allocator is in fact broken): +// +#if !defined(_RWSTD_ALLOCATOR) || (defined(__HP_aCC) && __HP_aCC <= 33100) +# define BOOST_NO_STD_ALLOCATOR +#endif + +// +// If we have a std::locale, we still may not have std::use_facet: +// +#if defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE) && !defined(BOOST_NO_STD_LOCALE) +# define BOOST_NO_STD_USE_FACET +# define BOOST_HAS_TWO_ARG_USE_FACET +#endif + +// +// There's no std::distance prior to version 2, or without +// partial specialization support: +// +#if (BOOST_RWSTD_VER < 0x020000) || defined(_RWSTD_NO_CLASS_PARTIAL_SPEC) + #define BOOST_NO_STD_DISTANCE +#endif + +// +// Some versions of the rogue wave library don't have assignable +// OutputIterators: +// +#if BOOST_RWSTD_VER < 0x020100 +# define BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN +#endif + +// +// Disable BOOST_HAS_LONG_LONG when the library has no support for it. +// +#if !defined(_RWSTD_LONG_LONG) && defined(BOOST_HAS_LONG_LONG) +# undef BOOST_HAS_LONG_LONG +#endif + +// +// check that on HP-UX, the proper RW library is used +// +#if defined(__HP_aCC) && !defined(_HP_NAMESPACE_STD) +# error "Boost requires Standard RW library. Please compile and link with -AA" +#endif + +// +// Define macros specific to RW V2.2 on HP-UX +// +#if defined(__HP_aCC) && (BOOST_RWSTD_VER == 0x02020100) +# ifndef __HP_TC1_MAKE_PAIR +# define __HP_TC1_MAKE_PAIR +# endif +# ifndef _HP_INSTANTIATE_STD2_VL +# define _HP_INSTANTIATE_STD2_VL +# endif +#endif + +#if _RWSTD_VER < 0x05000000 +# define BOOST_NO_CXX11_HDR_ARRAY +#endif +// type_traits header is incomplete: +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +// +// C++0x headers not yet implemented +// +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX11_HDR_EXCEPTION + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#else +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +# define BOOST_NO_CXX14_STD_EXCHANGE + +// C++17 features +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_ITERATOR_TRAITS diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/sgi.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/sgi.hpp new file mode 100644 index 0000000..c49957c --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/sgi.hpp @@ -0,0 +1,168 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Darin Adler 2001. +// (C) Copyright Jens Maurer 2001 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// generic SGI STL: + +#if !defined(__STL_CONFIG_H) +# include +# if !defined(__STL_CONFIG_H) +# error "This is not the SGI STL!" +# endif +#endif + +// +// No std::iterator traits without partial specialisation: +// +#if !defined(__STL_CLASS_PARTIAL_SPECIALIZATION) +# define BOOST_NO_STD_ITERATOR_TRAITS +#endif + +// +// No std::stringstream with gcc < 3 +// +#if defined(__GNUC__) && (__GNUC__ < 3) && \ + ((__GNUC_MINOR__ < 95) || (__GNUC_MINOR__ == 96)) && \ + !defined(__STL_USE_NEW_IOSTREAMS) || \ + defined(__APPLE_CC__) + // Note that we only set this for GNU C++ prior to 2.95 since the + // latest patches for that release do contain a minimal + // If you are running a 2.95 release prior to 2.95.3 then this will need + // setting, but there is no way to detect that automatically (other + // than by running the configure script). + // Also, the unofficial GNU C++ 2.96 included in RedHat 7.1 doesn't + // have . +# define BOOST_NO_STRINGSTREAM +#endif + +// Apple doesn't seem to reliably defined a *unix* macro +#if !defined(CYGWIN) && ( defined(__unix__) \ + || defined(__unix) \ + || defined(unix) \ + || defined(__APPLE__) \ + || defined(__APPLE) \ + || defined(APPLE)) +# include +#endif + + +// +// Assume no std::locale without own iostreams (this may be an +// incorrect assumption in some cases): +// +#if !defined(__SGI_STL_OWN_IOSTREAMS) && !defined(__STL_USE_NEW_IOSTREAMS) +# define BOOST_NO_STD_LOCALE +#endif + +// +// Original native SGI streams have non-standard std::messages facet: +// +#if defined(__sgi) && (_COMPILER_VERSION <= 650) && !defined(__SGI_STL_OWN_IOSTREAMS) +# define BOOST_NO_STD_LOCALE +#endif + +// +// SGI's new iostreams have missing "const" in messages<>::open +// +#if defined(__sgi) && (_COMPILER_VERSION <= 740) && defined(__STL_USE_NEW_IOSTREAMS) +# define BOOST_NO_STD_MESSAGES +#endif + +// +// No template iterator constructors, or std::allocator +// without member templates: +// +#if !defined(__STL_MEMBER_TEMPLATES) +# define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +# define BOOST_NO_STD_ALLOCATOR +#endif + +// +// We always have SGI style hash_set, hash_map, and slist: +// +#define BOOST_HAS_HASH +#define BOOST_HAS_SLIST + +// +// If this is GNU libstdc++2, then no and no std::wstring: +// +#if (defined(__GNUC__) && (__GNUC__ < 3)) +# include +# if defined(__BASTRING__) +# define BOOST_NO_LIMITS +// Note: will provide compile-time constants +# undef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +# define BOOST_NO_STD_WSTRING +# endif +#endif + +// +// There is no standard iterator unless we have namespace support: +// +#if !defined(__STL_USE_NAMESPACES) +# define BOOST_NO_STD_ITERATOR +#endif + +// +// Intrinsic type_traits support. +// The SGI STL has it's own __type_traits class, which +// has intrinsic compiler support with SGI's compilers. +// Whatever map SGI style type traits to boost equivalents: +// +#define BOOST_HAS_SGI_TYPE_TRAITS + +// C++0x headers not yet implemented +// +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX11_HDR_EXCEPTION + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#else +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +# define BOOST_NO_CXX14_STD_EXCHANGE + +// C++17 features +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_ITERATOR_TRAITS + +#define BOOST_STDLIB "SGI standard library" diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/stlport.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/stlport.hpp new file mode 100644 index 0000000..38bc763 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/stlport.hpp @@ -0,0 +1,258 @@ +// (C) Copyright John Maddock 2001 - 2002. +// (C) Copyright Darin Adler 2001. +// (C) Copyright Jens Maurer 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// STLPort standard library config: + +#if !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION) +# include +# if !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION) +# error "This is not STLPort!" +# endif +#endif + +// Apple doesn't seem to reliably defined a *unix* macro +#if !defined(CYGWIN) && ( defined(__unix__) \ + || defined(__unix) \ + || defined(unix) \ + || defined(__APPLE__) \ + || defined(__APPLE) \ + || defined(APPLE)) +# include +#endif + +// +// __STL_STATIC_CONST_INIT_BUG implies BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +// for versions prior to 4.1(beta) +// +#if (defined(__STL_STATIC_CONST_INIT_BUG) || defined(_STLP_STATIC_CONST_INIT_BUG)) && (__SGI_STL_PORT <= 0x400) +# define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +#endif + +// +// If STLport thinks that there is no partial specialisation, then there is no +// std::iterator traits: +// +#if !(defined(_STLP_CLASS_PARTIAL_SPECIALIZATION) || defined(__STL_CLASS_PARTIAL_SPECIALIZATION)) +# define BOOST_NO_STD_ITERATOR_TRAITS +#endif + +// +// No new style iostreams on GCC without STLport's iostreams enabled: +// +#if (defined(__GNUC__) && (__GNUC__ < 3)) && !(defined(__SGI_STL_OWN_IOSTREAMS) || defined(_STLP_OWN_IOSTREAMS)) +# define BOOST_NO_STRINGSTREAM +#endif + +// +// No new iostreams implies no std::locale, and no std::stringstream: +// +#if defined(__STL_NO_IOSTREAMS) || defined(__STL_NO_NEW_IOSTREAMS) || defined(_STLP_NO_IOSTREAMS) || defined(_STLP_NO_NEW_IOSTREAMS) +# define BOOST_NO_STD_LOCALE +# define BOOST_NO_STRINGSTREAM +#endif + +// +// If the streams are not native, and we have a "using ::x" compiler bug +// then the io stream facets are not available in namespace std:: +// +#ifdef _STLPORT_VERSION +# if !(_STLPORT_VERSION >= 0x500) && !defined(_STLP_OWN_IOSTREAMS) && defined(_STLP_USE_NAMESPACES) && defined(BOOST_NO_USING_TEMPLATE) && !defined(BOOST_BORLANDC) +# define BOOST_NO_STD_LOCALE +# endif +#else +# if !defined(__SGI_STL_OWN_IOSTREAMS) && defined(__STL_USE_NAMESPACES) && defined(BOOST_NO_USING_TEMPLATE) && !defined(BOOST_BORLANDC) +# define BOOST_NO_STD_LOCALE +# endif +#endif + +#if defined(_STLPORT_VERSION) && (_STLPORT_VERSION >= 0x520) +# define BOOST_HAS_TR1_UNORDERED_SET +# define BOOST_HAS_TR1_UNORDERED_MAP +#endif +// +// Without member template support enabled, their are no template +// iterate constructors, and no std::allocator: +// +#if !(defined(__STL_MEMBER_TEMPLATES) || defined(_STLP_MEMBER_TEMPLATES)) +# define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +# define BOOST_NO_STD_ALLOCATOR +#endif +// +// however we always have at least a partial allocator: +// +#define BOOST_HAS_PARTIAL_STD_ALLOCATOR + +#if !defined(_STLP_MEMBER_TEMPLATE_CLASSES) || defined(_STLP_DONT_SUPPORT_REBIND_MEMBER_TEMPLATE) +# define BOOST_NO_STD_ALLOCATOR +#endif + +#if defined(_STLP_NO_MEMBER_TEMPLATE_KEYWORD) && defined(BOOST_MSVC) && (BOOST_MSVC <= 1300) +# define BOOST_NO_STD_ALLOCATOR +#endif + +// +// If STLport thinks there is no wchar_t at all, then we have to disable +// the support for the relevant specilazations of std:: templates. +// +#if !defined(_STLP_HAS_WCHAR_T) && !defined(_STLP_WCHAR_T_IS_USHORT) +# ifndef BOOST_NO_STD_WSTRING +# define BOOST_NO_STD_WSTRING +# endif +# ifndef BOOST_NO_STD_WSTREAMBUF +# define BOOST_NO_STD_WSTREAMBUF +# endif +#endif + +// +// We always have SGI style hash_set, hash_map, and slist: +// +#ifndef _STLP_NO_EXTENSIONS +#define BOOST_HAS_HASH +#define BOOST_HAS_SLIST +#endif + +// +// STLport does a good job of importing names into namespace std::, +// but doesn't always get them all, define BOOST_NO_STDC_NAMESPACE, since our +// workaround does not conflict with STLports: +// +// +// Harold Howe says: +// Borland switched to STLport in BCB6. Defining BOOST_NO_STDC_NAMESPACE with +// BCB6 does cause problems. If we detect C++ Builder, then don't define +// BOOST_NO_STDC_NAMESPACE +// +#if !defined(BOOST_BORLANDC) && !defined(__DMC__) +// +// If STLport is using it's own namespace, and the real names are in +// the global namespace, then we duplicate STLport's using declarations +// (by defining BOOST_NO_STDC_NAMESPACE), we do this because STLport doesn't +// necessarily import all the names we need into namespace std:: +// +# if (defined(__STL_IMPORT_VENDOR_CSTD) \ + || defined(__STL_USE_OWN_NAMESPACE) \ + || defined(_STLP_IMPORT_VENDOR_CSTD) \ + || defined(_STLP_USE_OWN_NAMESPACE)) \ + && (defined(__STL_VENDOR_GLOBAL_CSTD) || defined (_STLP_VENDOR_GLOBAL_CSTD)) +# define BOOST_NO_STDC_NAMESPACE +# define BOOST_NO_EXCEPTION_STD_NAMESPACE +# endif +#elif defined(BOOST_BORLANDC) && BOOST_BORLANDC < 0x560 +// STLport doesn't import std::abs correctly: +#include +namespace std { using ::abs; } +// and strcmp/strcpy don't get imported either ('cos they are macros) +#include +#ifdef strcpy +# undef strcpy +#endif +#ifdef strcmp +# undef strcmp +#endif +#ifdef _STLP_VENDOR_CSTD +namespace std{ using _STLP_VENDOR_CSTD::strcmp; using _STLP_VENDOR_CSTD::strcpy; } +#endif +#endif + +// +// std::use_facet may be non-standard, uses a class instead: +// +#if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) || defined(_STLP_NO_EXPLICIT_FUNCTION_TMPL_ARGS) +# define BOOST_NO_STD_USE_FACET +# define BOOST_HAS_STLP_USE_FACET +#endif + +// +// If STLport thinks there are no wide functions, etc. is not working; but +// only if BOOST_NO_STDC_NAMESPACE is not defined (if it is then we do the import +// into std:: ourselves). +// +#if defined(_STLP_NO_NATIVE_WIDE_FUNCTIONS) && !defined(BOOST_NO_STDC_NAMESPACE) +# define BOOST_NO_CWCHAR +# define BOOST_NO_CWCTYPE +#endif + +// +// If STLport for some reason was configured so that it thinks that wchar_t +// is not an intrinsic type, then we have to disable the support for it as +// well (we would be missing required specializations otherwise). +// +#if !defined( _STLP_HAS_WCHAR_T) || defined(_STLP_WCHAR_T_IS_USHORT) +# undef BOOST_NO_INTRINSIC_WCHAR_T +# define BOOST_NO_INTRINSIC_WCHAR_T +#endif + +// +// Borland ships a version of STLport with C++ Builder 6 that lacks +// hashtables and the like: +// +#if defined(BOOST_BORLANDC) && (BOOST_BORLANDC == 0x560) +# undef BOOST_HAS_HASH +#endif + +// +// gcc-2.95.3/STLPort does not like the using declarations we use to get ADL with std::min/max +// +#if defined(__GNUC__) && (__GNUC__ < 3) +# include // for std::min and std::max +# define BOOST_USING_STD_MIN() ((void)0) +# define BOOST_USING_STD_MAX() ((void)0) +namespace boost { using std::min; using std::max; } +#endif + +// C++0x headers not yet implemented +// +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX11_HDR_EXCEPTION + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#else +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +# define BOOST_NO_CXX14_STD_EXCHANGE + +// C++17 features +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_ITERATOR_TRAITS + +#define BOOST_STDLIB "STLPort standard library version " BOOST_STRINGIZE(__SGI_STL_PORT) diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/vacpp.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/vacpp.hpp new file mode 100644 index 0000000..b14dd65 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/vacpp.hpp @@ -0,0 +1,74 @@ +// (C) Copyright John Maddock 2001 - 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +#if __IBMCPP__ <= 501 +# define BOOST_NO_STD_ALLOCATOR +#endif + +#define BOOST_HAS_MACRO_USE_FACET +#define BOOST_NO_STD_MESSAGES + +// Apple doesn't seem to reliably defined a *unix* macro +#if !defined(CYGWIN) && ( defined(__unix__) \ + || defined(__unix) \ + || defined(unix) \ + || defined(__APPLE__) \ + || defined(__APPLE) \ + || defined(APPLE)) +# include +#endif + +// C++0x headers not yet implemented +// +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_POINTER_TRAITS +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +# define BOOST_NO_CXX11_SMART_PTR +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_HDR_ATOMIC +# define BOOST_NO_CXX11_STD_ALIGN +# define BOOST_NO_CXX11_ADDRESSOF +# define BOOST_NO_CXX11_HDR_EXCEPTION + +#if defined(__has_include) +#if !__has_include() +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#elif __cplusplus < 201402 +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif +#else +# define BOOST_NO_CXX14_HDR_SHARED_MUTEX +#endif + +// C++14 features +# define BOOST_NO_CXX14_STD_EXCHANGE + +// C++17 features +# define BOOST_NO_CXX17_STD_APPLY +# define BOOST_NO_CXX17_STD_INVOKE +# define BOOST_NO_CXX17_ITERATOR_TRAITS + +#define BOOST_STDLIB "Visual Age default standard library" diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/xlcpp_zos.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/xlcpp_zos.hpp new file mode 100644 index 0000000..a5e02fd --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/stdlib/xlcpp_zos.hpp @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Dynatrace +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +// See http://www.boost.org for most recent version. + +// Standard library setup for IBM z/OS XL C/C++ compiler. + +// Oldest library version currently supported is 2.1 (V2R1) +#if __TARGET_LIB__ < 0x42010000 +# error "Library version not supported or configured - please reconfigure" +#endif + +#if __TARGET_LIB__ > 0x42010000 +# if defined(BOOST_ASSERT_CONFIG) +# error "Unknown library version - please run the configure tests and report the results" +# endif +#endif + +#define BOOST_STDLIB "IBM z/OS XL C/C++ standard library" + +#define BOOST_HAS_MACRO_USE_FACET + +#define BOOST_NO_CXX11_HDR_TYPE_TRAITS +#define BOOST_NO_CXX11_HDR_INITIALIZER_LIST + +#define BOOST_NO_CXX11_ADDRESSOF +#define BOOST_NO_CXX11_SMART_PTR +#define BOOST_NO_CXX11_ATOMIC_SMART_PTR +#define BOOST_NO_CXX11_NUMERIC_LIMITS +#define BOOST_NO_CXX11_ALLOCATOR +#define BOOST_NO_CXX11_POINTER_TRAITS +#define BOOST_NO_CXX11_HDR_FUNCTIONAL +#define BOOST_NO_CXX11_HDR_UNORDERED_SET +#define BOOST_NO_CXX11_HDR_UNORDERED_MAP +#define BOOST_NO_CXX11_HDR_TYPEINDEX +#define BOOST_NO_CXX11_HDR_TUPLE +#define BOOST_NO_CXX11_HDR_THREAD +#define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +#define BOOST_NO_CXX11_HDR_REGEX +#define BOOST_NO_CXX11_HDR_RATIO +#define BOOST_NO_CXX11_HDR_RANDOM +#define BOOST_NO_CXX11_HDR_MUTEX +#define BOOST_NO_CXX11_HDR_FUTURE +#define BOOST_NO_CXX11_HDR_FORWARD_LIST +#define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +#define BOOST_NO_CXX11_HDR_CODECVT +#define BOOST_NO_CXX11_HDR_CHRONO +#define BOOST_NO_CXX11_HDR_ATOMIC +#define BOOST_NO_CXX11_HDR_ARRAY +#define BOOST_NO_CXX11_HDR_EXCEPTION +#define BOOST_NO_CXX11_STD_ALIGN + +#define BOOST_NO_CXX14_STD_EXCHANGE +#define BOOST_NO_CXX14_HDR_SHARED_MUTEX + +#define BOOST_NO_CXX17_STD_INVOKE +#define BOOST_NO_CXX17_STD_APPLY +#define BOOST_NO_CXX17_ITERATOR_TRAITS diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/user.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/user.hpp new file mode 100644 index 0000000..8160fca --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/user.hpp @@ -0,0 +1,133 @@ +// boost/config/user.hpp ---------------------------------------------------// + +// (C) Copyright John Maddock 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Do not check in modified versions of this file, +// This file may be customized by the end user, but not by boost. + +// +// Use this file to define a site and compiler specific +// configuration policy: +// + +// define this to locate a compiler config file: +// #define BOOST_COMPILER_CONFIG + +// define this to locate a stdlib config file: +// #define BOOST_STDLIB_CONFIG + +// define this to locate a platform config file: +// #define BOOST_PLATFORM_CONFIG + +// define this to disable compiler config, +// use if your compiler config has nothing to set: +// #define BOOST_NO_COMPILER_CONFIG + +// define this to disable stdlib config, +// use if your stdlib config has nothing to set: +// #define BOOST_NO_STDLIB_CONFIG + +// define this to disable platform config, +// use if your platform config has nothing to set: +// #define BOOST_NO_PLATFORM_CONFIG + +// define this to disable all config options, +// excluding the user config. Use if your +// setup is fully ISO compliant, and has no +// useful extensions, or for autoconf generated +// setups: +// #define BOOST_NO_CONFIG + +// define this to make the config "optimistic" +// about unknown compiler versions. Normally +// unknown compiler versions are assumed to have +// all the defects of the last known version, however +// setting this flag, causes the config to assume +// that unknown compiler versions are fully conformant +// with the standard: +// #define BOOST_STRICT_CONFIG + +// define this to cause the config to halt compilation +// with an #error if it encounters anything unknown -- +// either an unknown compiler version or an unknown +// compiler/platform/library: +// #define BOOST_ASSERT_CONFIG + + +// define if you want to disable threading support, even +// when available: +// #define BOOST_DISABLE_THREADS + +// define when you want to disable Win32 specific features +// even when available: +// #define BOOST_DISABLE_WIN32 + +// BOOST_DISABLE_ABI_HEADERS: Stops boost headers from including any +// prefix/suffix headers that normally control things like struct +// packing and alignment. +// #define BOOST_DISABLE_ABI_HEADERS + +// BOOST_ABI_PREFIX: A prefix header to include in place of whatever +// boost.config would normally select, any replacement should set up +// struct packing and alignment options as required. +// #define BOOST_ABI_PREFIX my-header-name + +// BOOST_ABI_SUFFIX: A suffix header to include in place of whatever +// boost.config would normally select, any replacement should undo +// the effects of the prefix header. +// #define BOOST_ABI_SUFFIX my-header-name + +// BOOST_ALL_DYN_LINK: Forces all libraries that have separate source, +// to be linked as dll's rather than static libraries on Microsoft Windows +// (this macro is used to turn on __declspec(dllimport) modifiers, so that +// the compiler knows which symbols to look for in a dll rather than in a +// static library). Note that there may be some libraries that can only +// be linked in one way (statically or dynamically), in these cases this +// macro has no effect. +// #define BOOST_ALL_DYN_LINK + +// BOOST_WHATEVER_DYN_LINK: Forces library "whatever" to be linked as a dll +// rather than a static library on Microsoft Windows: replace the WHATEVER +// part of the macro name with the name of the library that you want to +// dynamically link to, for example use BOOST_DATE_TIME_DYN_LINK or +// BOOST_REGEX_DYN_LINK etc (this macro is used to turn on __declspec(dllimport) +// modifiers, so that the compiler knows which symbols to look for in a dll +// rather than in a static library). +// Note that there may be some libraries that can only +// be linked in one way (statically or dynamically), +// in these cases this macro is unsupported. +// #define BOOST_WHATEVER_DYN_LINK + +// BOOST_ALL_NO_LIB: Tells the config system not to automatically select +// which libraries to link against. +// Normally if a compiler supports #pragma lib, then the correct library +// build variant will be automatically selected and linked against, +// simply by the act of including one of that library's headers. +// This macro turns that feature off. +// #define BOOST_ALL_NO_LIB + +// BOOST_WHATEVER_NO_LIB: Tells the config system not to automatically +// select which library to link against for library "whatever", +// replace WHATEVER in the macro name with the name of the library; +// for example BOOST_DATE_TIME_NO_LIB or BOOST_REGEX_NO_LIB. +// Normally if a compiler supports #pragma lib, then the correct library +// build variant will be automatically selected and linked against, simply +// by the act of including one of that library's headers. This macro turns +// that feature off. +// #define BOOST_WHATEVER_NO_LIB + +// BOOST_LIB_BUILDID: Set to the same value as the value passed to Boost.Build's +// --buildid command line option. For example if you built using: +// +// bjam address-model=64 --buildid=amd64 +// +// then compile your code with: +// +// -DBOOST_LIB_BUILDID = amd64 +// +// to ensure the correct libraries are selected at link time. +// #define BOOST_LIB_BUILDID amd64 + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/warning_disable.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/warning_disable.hpp new file mode 100644 index 0000000..fea8e82 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/warning_disable.hpp @@ -0,0 +1,47 @@ +// Copyright John Maddock 2008 +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// This file exists to turn off some overly-pedantic warning emitted +// by certain compilers. You should include this header only in: +// +// * A test case, before any other headers, or, +// * A library source file before any other headers. +// +// IT SHOULD NOT BE INCLUDED BY ANY BOOST HEADER. +// +// YOU SHOULD NOT INCLUDE IT IF YOU CAN REASONABLY FIX THE WARNING. +// +// The only warnings disabled here are those that are: +// +// * Quite unreasonably pedantic. +// * Generally only emitted by a single compiler. +// * Can't easily be fixed: for example if the vendors own std lib +// code emits these warnings! +// +// Note that THIS HEADER MUST NOT INCLUDE ANY OTHER HEADERS: +// not even std library ones! Doing so may turn the warning +// off too late to be of any use. For example the VC++ C4996 +// warning can be emitted from if that header is included +// before or by this one :-( +// + +#ifndef BOOST_CONFIG_WARNING_DISABLE_HPP +#define BOOST_CONFIG_WARNING_DISABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + // Error 'function': was declared deprecated + // http://msdn2.microsoft.com/en-us/library/ttcz0bys(VS.80).aspx + // This error is emitted when you use some perfectly conforming + // std lib functions in a perfectly correct way, and also by + // some of Microsoft's own std lib code ! +# pragma warning(disable:4996) +#endif +#if defined(__INTEL_COMPILER) || defined(__ICL) + // As above: gives warning when a "deprecated" + // std library function is encountered. +# pragma warning(disable:1786) +#endif + +#endif // BOOST_CONFIG_WARNING_DISABLE_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/workaround.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/workaround.hpp new file mode 100644 index 0000000..688f963 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/config/workaround.hpp @@ -0,0 +1,305 @@ +// Copyright David Abrahams 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#ifndef BOOST_CONFIG_WORKAROUND_HPP +#define BOOST_CONFIG_WORKAROUND_HPP + +// Compiler/library version workaround macro +// +// Usage: +// +// #if BOOST_WORKAROUND(BOOST_MSVC, < 1300) +// // workaround for eVC4 and VC6 +// ... // workaround code here +// #endif +// +// When BOOST_STRICT_CONFIG is defined, expands to 0. Otherwise, the +// first argument must be undefined or expand to a numeric +// value. The above expands to: +// +// (BOOST_MSVC) != 0 && (BOOST_MSVC) < 1300 +// +// When used for workarounds that apply to the latest known version +// and all earlier versions of a compiler, the following convention +// should be observed: +// +// #if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1301)) +// +// The version number in this case corresponds to the last version in +// which the workaround was known to have been required. When +// BOOST_DETECT_OUTDATED_WORKAROUNDS is not the defined, the macro +// BOOST_TESTED_AT(x) expands to "!= 0", which effectively activates +// the workaround for any version of the compiler. When +// BOOST_DETECT_OUTDATED_WORKAROUNDS is defined, a compiler warning or +// error will be issued if the compiler version exceeds the argument +// to BOOST_TESTED_AT(). This can be used to locate workarounds which +// may be obsoleted by newer versions. + +#ifndef BOOST_STRICT_CONFIG + +#include + +#ifndef __BORLANDC__ +#define __BORLANDC___WORKAROUND_GUARD 1 +#else +#define __BORLANDC___WORKAROUND_GUARD 0 +#endif +#ifndef __CODEGEARC__ +#define __CODEGEARC___WORKAROUND_GUARD 1 +#else +#define __CODEGEARC___WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_BORLANDC +#define BOOST_BORLANDC_WORKAROUND_GUARD 1 +#else +#define BOOST_BORLANDC_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_CODEGEARC +#define BOOST_CODEGEARC_WORKAROUND_GUARD 1 +#else +#define BOOST_CODEGEARC_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_EMBTC +#define BOOST_EMBTC_WORKAROUND_GUARD 1 +#else +#define BOOST_EMBTC_WORKAROUND_GUARD 0 +#endif +#ifndef _MSC_VER +#define _MSC_VER_WORKAROUND_GUARD 1 +#else +#define _MSC_VER_WORKAROUND_GUARD 0 +#endif +#ifndef _MSC_FULL_VER +#define _MSC_FULL_VER_WORKAROUND_GUARD 1 +#else +#define _MSC_FULL_VER_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_MSVC +#define BOOST_MSVC_WORKAROUND_GUARD 1 +#else +#define BOOST_MSVC_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_MSVC_FULL_VER +#define BOOST_MSVC_FULL_VER_WORKAROUND_GUARD 1 +#else +#define BOOST_MSVC_FULL_VER_WORKAROUND_GUARD 0 +#endif +#ifndef __GNUC__ +#define __GNUC___WORKAROUND_GUARD 1 +#else +#define __GNUC___WORKAROUND_GUARD 0 +#endif +#ifndef __GNUC_MINOR__ +#define __GNUC_MINOR___WORKAROUND_GUARD 1 +#else +#define __GNUC_MINOR___WORKAROUND_GUARD 0 +#endif +#ifndef __GNUC_PATCHLEVEL__ +#define __GNUC_PATCHLEVEL___WORKAROUND_GUARD 1 +#else +#define __GNUC_PATCHLEVEL___WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_GCC +#define BOOST_GCC_WORKAROUND_GUARD 1 +#define BOOST_GCC_VERSION_WORKAROUND_GUARD 1 +#else +#define BOOST_GCC_WORKAROUND_GUARD 0 +#define BOOST_GCC_VERSION_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_XLCPP_ZOS +#define BOOST_XLCPP_ZOS_WORKAROUND_GUARD 1 +#else +#define BOOST_XLCPP_ZOS_WORKAROUND_GUARD 0 +#endif +#ifndef __IBMCPP__ +#define __IBMCPP___WORKAROUND_GUARD 1 +#else +#define __IBMCPP___WORKAROUND_GUARD 0 +#endif +#ifndef __SUNPRO_CC +#define __SUNPRO_CC_WORKAROUND_GUARD 1 +#else +#define __SUNPRO_CC_WORKAROUND_GUARD 0 +#endif +#ifndef __DECCXX_VER +#define __DECCXX_VER_WORKAROUND_GUARD 1 +#else +#define __DECCXX_VER_WORKAROUND_GUARD 0 +#endif +#ifndef __MWERKS__ +#define __MWERKS___WORKAROUND_GUARD 1 +#else +#define __MWERKS___WORKAROUND_GUARD 0 +#endif +#ifndef __EDG__ +#define __EDG___WORKAROUND_GUARD 1 +#else +#define __EDG___WORKAROUND_GUARD 0 +#endif +#ifndef __EDG_VERSION__ +#define __EDG_VERSION___WORKAROUND_GUARD 1 +#else +#define __EDG_VERSION___WORKAROUND_GUARD 0 +#endif +#ifndef __HP_aCC +#define __HP_aCC_WORKAROUND_GUARD 1 +#else +#define __HP_aCC_WORKAROUND_GUARD 0 +#endif +#ifndef __hpxstd98 +#define __hpxstd98_WORKAROUND_GUARD 1 +#else +#define __hpxstd98_WORKAROUND_GUARD 0 +#endif +#ifndef _CRAYC +#define _CRAYC_WORKAROUND_GUARD 1 +#else +#define _CRAYC_WORKAROUND_GUARD 0 +#endif +#ifndef __DMC__ +#define __DMC___WORKAROUND_GUARD 1 +#else +#define __DMC___WORKAROUND_GUARD 0 +#endif +#ifndef MPW_CPLUS +#define MPW_CPLUS_WORKAROUND_GUARD 1 +#else +#define MPW_CPLUS_WORKAROUND_GUARD 0 +#endif +#ifndef __COMO__ +#define __COMO___WORKAROUND_GUARD 1 +#else +#define __COMO___WORKAROUND_GUARD 0 +#endif +#ifndef __COMO_VERSION__ +#define __COMO_VERSION___WORKAROUND_GUARD 1 +#else +#define __COMO_VERSION___WORKAROUND_GUARD 0 +#endif +#ifndef __INTEL_COMPILER +#define __INTEL_COMPILER_WORKAROUND_GUARD 1 +#else +#define __INTEL_COMPILER_WORKAROUND_GUARD 0 +#endif +#ifndef __ICL +#define __ICL_WORKAROUND_GUARD 1 +#else +#define __ICL_WORKAROUND_GUARD 0 +#endif +#ifndef _COMPILER_VERSION +#define _COMPILER_VERSION_WORKAROUND_GUARD 1 +#else +#define _COMPILER_VERSION_WORKAROUND_GUARD 0 +#endif +#ifndef __clang_major__ +#define __clang_major___WORKAROUND_GUARD 1 +#else +#define __clang_major___WORKAROUND_GUARD 0 +#endif + +#ifndef _RWSTD_VER +#define _RWSTD_VER_WORKAROUND_GUARD 1 +#else +#define _RWSTD_VER_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_RWSTD_VER +#define BOOST_RWSTD_VER_WORKAROUND_GUARD 1 +#else +#define BOOST_RWSTD_VER_WORKAROUND_GUARD 0 +#endif +#ifndef __GLIBCPP__ +#define __GLIBCPP___WORKAROUND_GUARD 1 +#else +#define __GLIBCPP___WORKAROUND_GUARD 0 +#endif +#ifndef _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC +#define _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC_WORKAROUND_GUARD 1 +#else +#define _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC_WORKAROUND_GUARD 0 +#endif +#ifndef __SGI_STL_PORT +#define __SGI_STL_PORT_WORKAROUND_GUARD 1 +#else +#define __SGI_STL_PORT_WORKAROUND_GUARD 0 +#endif +#ifndef _STLPORT_VERSION +#define _STLPORT_VERSION_WORKAROUND_GUARD 1 +#else +#define _STLPORT_VERSION_WORKAROUND_GUARD 0 +#endif +#ifndef __LIBCOMO_VERSION__ +#define __LIBCOMO_VERSION___WORKAROUND_GUARD 1 +#else +#define __LIBCOMO_VERSION___WORKAROUND_GUARD 0 +#endif +#ifndef _CPPLIB_VER +#define _CPPLIB_VER_WORKAROUND_GUARD 1 +#else +#define _CPPLIB_VER_WORKAROUND_GUARD 0 +#endif + +#ifndef BOOST_INTEL_CXX_VERSION +#define BOOST_INTEL_CXX_VERSION_WORKAROUND_GUARD 1 +#else +#define BOOST_INTEL_CXX_VERSION_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_INTEL_WIN +#define BOOST_INTEL_WIN_WORKAROUND_GUARD 1 +#else +#define BOOST_INTEL_WIN_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_DINKUMWARE_STDLIB +#define BOOST_DINKUMWARE_STDLIB_WORKAROUND_GUARD 1 +#else +#define BOOST_DINKUMWARE_STDLIB_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_INTEL +#define BOOST_INTEL_WORKAROUND_GUARD 1 +#else +#define BOOST_INTEL_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_CLANG_VERSION +#define BOOST_CLANG_VERSION_WORKAROUND_GUARD 1 +#else +#define BOOST_CLANG_VERSION_WORKAROUND_GUARD 0 +#endif + +// Always define to zero, if it's used it'll be defined my MPL: +#define BOOST_MPL_CFG_GCC_WORKAROUND_GUARD 0 + +#define BOOST_WORKAROUND(symbol, test) \ + ((symbol ## _WORKAROUND_GUARD + 0 == 0) && \ + (symbol != 0) && (1 % (( (symbol test) ) + 1))) +// ^ ^ ^ ^ +// The extra level of parenthesis nesting above, along with the +// BOOST_OPEN_PAREN indirection below, is required to satisfy the +// broken preprocessor in MWCW 8.3 and earlier. +// +// The basic mechanism works as follows: +// (symbol test) + 1 => if (symbol test) then 2 else 1 +// 1 % ((symbol test) + 1) => if (symbol test) then 1 else 0 +// +// The complication with % is for cooperation with BOOST_TESTED_AT(). +// When "test" is BOOST_TESTED_AT(x) and +// BOOST_DETECT_OUTDATED_WORKAROUNDS is #defined, +// +// symbol test => if (symbol <= x) then 1 else -1 +// (symbol test) + 1 => if (symbol <= x) then 2 else 0 +// 1 % ((symbol test) + 1) => if (symbol <= x) then 1 else divide-by-zero +// + +#ifdef BOOST_DETECT_OUTDATED_WORKAROUNDS +# define BOOST_OPEN_PAREN ( +# define BOOST_TESTED_AT(value) > value) ?(-1): BOOST_OPEN_PAREN 1 +#else +# define BOOST_TESTED_AT(value) != ((value)-(value)) +#endif + +#else + +#define BOOST_WORKAROUND(symbol, test) 0 + +#endif + +#endif // BOOST_CONFIG_WORKAROUND_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op.hpp new file mode 100644 index 0000000..0c674a2 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op.hpp @@ -0,0 +1,442 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_BOOLEAN_OP_HPP +#define BOOST_POLYGON_BOOLEAN_OP_HPP +namespace boost { namespace polygon{ +namespace boolean_op { + + //BooleanOp is the generic boolean operation scanline algorithm that provides + //all the simple boolean set operations on manhattan data. By templatizing + //the intersection count of the input and algorithm internals it is extensible + //to multi-layer scans, properties and other advanced scanline operations above + //and beyond simple booleans. + //T must cast to int + template + class BooleanOp { + public: + typedef std::map ScanData; + typedef std::pair ElementType; + protected: + ScanData scanData_; + typename ScanData::iterator nextItr_; + T nullT_; + public: + inline BooleanOp () : scanData_(), nextItr_(), nullT_() { nextItr_ = scanData_.end(); nullT_ = 0; } + inline BooleanOp (T nullT) : scanData_(), nextItr_(), nullT_(nullT) { nextItr_ = scanData_.end(); } + inline BooleanOp (const BooleanOp& that) : scanData_(that.scanData_), nextItr_(), + nullT_(that.nullT_) { nextItr_ = scanData_.begin(); } + inline BooleanOp& operator=(const BooleanOp& that); + + //moves scanline forward + inline void advanceScan() { nextItr_ = scanData_.begin(); } + + //proceses the given interval and T data + //appends output edges to cT + template + inline void processInterval(cT& outputContainer, interval_data ivl, T deltaCount); + + private: + inline typename ScanData::iterator lookup_(Unit pos){ + if(nextItr_ != scanData_.end() && nextItr_->first >= pos) { + return nextItr_; + } + return nextItr_ = scanData_.lower_bound(pos); + } + inline typename ScanData::iterator insert_(Unit pos, T count){ + return nextItr_ = scanData_.insert(nextItr_, ElementType(pos, count)); + } + template + inline void evaluateInterval_(cT& outputContainer, interval_data ivl, T beforeCount, T afterCount); + }; + + class BinaryAnd { + public: + inline BinaryAnd() {} + inline bool operator()(int a, int b) { return (a > 0) & (b > 0); } + }; + class BinaryOr { + public: + inline BinaryOr() {} + inline bool operator()(int a, int b) { return (a > 0) | (b > 0); } + }; + class BinaryNot { + public: + inline BinaryNot() {} + inline bool operator()(int a, int b) { return (a > 0) & !(b > 0); } + }; + class BinaryXor { + public: + inline BinaryXor() {} + inline bool operator()(int a, int b) { return (a > 0) ^ (b > 0); } + }; + + //BinaryCount is an array of two deltaCounts coming from two different layers + //of scan event data. It is the merged count of the two suitable for consumption + //as the template argument of the BooleanOp algorithm because BinaryCount casts to int. + //T is a binary functor object that evaluates the array of counts and returns a logical + //result of some operation on those values. + //BinaryCount supports many of the operators that work with int, particularly the + //binary operators, but cannot support less than or increment. + template + class BinaryCount { + public: + inline BinaryCount() +#ifndef BOOST_POLYGON_MSVC + : counts_() +#endif + { counts_[0] = counts_[1] = 0; } + // constructs from two integers + inline BinaryCount(int countL, int countR) +#ifndef BOOST_POLYGON_MSVC + : counts_() +#endif + { counts_[0] = countL, counts_[1] = countR; } + inline BinaryCount& operator=(int count) { counts_[0] = count, counts_[1] = count; return *this; } + inline BinaryCount& operator=(const BinaryCount& that); + inline BinaryCount(const BinaryCount& that) +#ifndef BOOST_POLYGON_MSVC + : counts_() +#endif + { *this = that; } + inline bool operator==(const BinaryCount& that) const; + inline bool operator!=(const BinaryCount& that) const { return !((*this) == that);} + inline BinaryCount& operator+=(const BinaryCount& that); + inline BinaryCount& operator-=(const BinaryCount& that); + inline BinaryCount operator+(const BinaryCount& that) const; + inline BinaryCount operator-(const BinaryCount& that) const; + inline BinaryCount operator-() const; + inline int& operator[](bool index) { return counts_[index]; } + + //cast to int operator evaluates data using T binary functor + inline operator int() const { return T()(counts_[0], counts_[1]); } + private: + int counts_[2]; + }; + + class UnaryCount { + public: + inline UnaryCount() : count_(0) {} + // constructs from two integers + inline explicit UnaryCount(int count) : count_(count) {} + inline UnaryCount& operator=(int count) { count_ = count; return *this; } + inline UnaryCount& operator=(const UnaryCount& that) { count_ = that.count_; return *this; } + inline UnaryCount(const UnaryCount& that) : count_(that.count_) {} + inline bool operator==(const UnaryCount& that) const { return count_ == that.count_; } + inline bool operator!=(const UnaryCount& that) const { return !((*this) == that);} + inline UnaryCount& operator+=(const UnaryCount& that) { count_ += that.count_; return *this; } + inline UnaryCount& operator-=(const UnaryCount& that) { count_ -= that.count_; return *this; } + inline UnaryCount operator+(const UnaryCount& that) const { UnaryCount tmp(*this); tmp += that; return tmp; } + inline UnaryCount operator-(const UnaryCount& that) const { UnaryCount tmp(*this); tmp -= that; return tmp; } + inline UnaryCount operator-() const { UnaryCount tmp; return tmp - *this; } + + //cast to int operator evaluates data using T binary functor + inline operator int() const { return count_ % 2; } + private: + int count_; + }; + + template + inline BooleanOp& BooleanOp::operator=(const BooleanOp& that) { + scanData_ = that.scanData_; + nextItr_ = scanData_.begin(); + nullT_ = that.nullT_; + return *this; + } + + //appends output edges to cT + template + template + inline void BooleanOp::processInterval(cT& outputContainer, interval_data ivl, T deltaCount) { + typename ScanData::iterator lowItr = lookup_(ivl.low()); + typename ScanData::iterator highItr = lookup_(ivl.high()); + //add interval to scan data if it is past the end + if(lowItr == scanData_.end()) { + lowItr = insert_(ivl.low(), deltaCount); + highItr = insert_(ivl.high(), nullT_); + evaluateInterval_(outputContainer, ivl, nullT_, deltaCount); + return; + } + //ensure that highItr points to the end of the ivl + if(highItr == scanData_.end() || (*highItr).first > ivl.high()) { + T value = nullT_; + if(highItr != scanData_.begin()) { + --highItr; + value = highItr->second; + } + nextItr_ = highItr; + highItr = insert_(ivl.high(), value); + } + //split the low interval if needed + if(lowItr->first > ivl.low()) { + if(lowItr != scanData_.begin()) { + --lowItr; + nextItr_ = lowItr; + lowItr = insert_(ivl.low(), lowItr->second); + } else { + nextItr_ = lowItr; + lowItr = insert_(ivl.low(), nullT_); + } + } + //process scan data intersecting interval + for(typename ScanData::iterator itr = lowItr; itr != highItr; ){ + T beforeCount = itr->second; + T afterCount = itr->second += deltaCount; + Unit low = itr->first; + ++itr; + Unit high = itr->first; + evaluateInterval_(outputContainer, interval_data(low, high), beforeCount, afterCount); + } + //merge the bottom interval with the one below if they have the same count + if(lowItr != scanData_.begin()){ + typename ScanData::iterator belowLowItr = lowItr; + --belowLowItr; + if(belowLowItr->second == lowItr->second) { + scanData_.erase(lowItr); + } + } + //merge the top interval with the one above if they have the same count + if(highItr != scanData_.begin()) { + typename ScanData::iterator beforeHighItr = highItr; + --beforeHighItr; + if(beforeHighItr->second == highItr->second) { + scanData_.erase(highItr); + highItr = beforeHighItr; + ++highItr; + } + } + nextItr_ = highItr; + } + + template + template + inline void BooleanOp::evaluateInterval_(cT& outputContainer, interval_data ivl, + T beforeCount, T afterCount) { + bool before = (int)beforeCount > 0; + bool after = (int)afterCount > 0; + int value = (!before & after) - (before & !after); + if(value) { + outputContainer.insert(outputContainer.end(), std::pair, int>(ivl, value)); + } + } + + template + inline BinaryCount& BinaryCount::operator=(const BinaryCount& that) { + counts_[0] = that.counts_[0]; + counts_[1] = that.counts_[1]; + return *this; + } + template + inline bool BinaryCount::operator==(const BinaryCount& that) const { + return counts_[0] == that.counts_[0] && + counts_[1] == that.counts_[1]; + } + template + inline BinaryCount& BinaryCount::operator+=(const BinaryCount& that) { + counts_[0] += that.counts_[0]; + counts_[1] += that.counts_[1]; + return *this; + } + template + inline BinaryCount& BinaryCount::operator-=(const BinaryCount& that) { + counts_[0] += that.counts_[0]; + counts_[1] += that.counts_[1]; + return *this; + } + template + inline BinaryCount BinaryCount::operator+(const BinaryCount& that) const { + BinaryCount retVal(*this); + retVal += that; + return retVal; + } + template + inline BinaryCount BinaryCount::operator-(const BinaryCount& that) const { + BinaryCount retVal(*this); + retVal -= that; + return retVal; + } + template + inline BinaryCount BinaryCount::operator-() const { + return BinaryCount() - *this; + } + + + template + inline void applyBooleanBinaryOp(std::vector > >& output, + //const std::vector > >& input1, + //const std::vector > >& input2, + iterator_type_1 itr1, iterator_type_1 itr1_end, + iterator_type_2 itr2, iterator_type_2 itr2_end, + T defaultCount) { + BooleanOp boolean(defaultCount); + //typename std::vector > >::const_iterator itr1 = input1.begin(); + //typename std::vector > >::const_iterator itr2 = input2.begin(); + std::vector, int> > container; + //output.reserve((std::max)(input1.size(), input2.size())); + + //consider eliminating dependecy on limits with bool flag for initial state + Unit UnitMax = (std::numeric_limits::max)(); + Unit prevCoord = UnitMax; + Unit prevPosition = UnitMax; + T count(defaultCount); + //define the starting point + if(itr1 != itr1_end) { + prevCoord = (*itr1).first; + prevPosition = (*itr1).second.first; + count[0] += (*itr1).second.second; + } + if(itr2 != itr2_end) { + if((*itr2).first < prevCoord || + ((*itr2).first == prevCoord && (*itr2).second.first < prevPosition)) { + prevCoord = (*itr2).first; + prevPosition = (*itr2).second.first; + count = defaultCount; + count[1] += (*itr2).second.second; + ++itr2; + } else if((*itr2).first == prevCoord && (*itr2).second.first == prevPosition) { + count[1] += (*itr2).second.second; + ++itr2; + if(itr1 != itr1_end) ++itr1; + } else { + if(itr1 != itr1_end) ++itr1; + } + } else { + if(itr1 != itr1_end) ++itr1; + } + + while(itr1 != itr1_end || itr2 != itr2_end) { + Unit curCoord = UnitMax; + Unit curPosition = UnitMax; + T curCount(defaultCount); + if(itr1 != itr1_end) { + curCoord = (*itr1).first; + curPosition = (*itr1).second.first; + curCount[0] += (*itr1).second.second; + } + if(itr2 != itr2_end) { + if((*itr2).first < curCoord || + ((*itr2).first == curCoord && (*itr2).second.first < curPosition)) { + curCoord = (*itr2).first; + curPosition = (*itr2).second.first; + curCount = defaultCount; + curCount[1] += (*itr2).second.second; + ++itr2; + } else if((*itr2).first == curCoord && (*itr2).second.first == curPosition) { + curCount[1] += (*itr2).second.second; + ++itr2; + if(itr1 != itr1_end) ++itr1; + } else { + if(itr1 != itr1_end) ++itr1; + } + } else { + ++itr1; + } + + if(prevCoord != curCoord) { + boolean.advanceScan(); + prevCoord = curCoord; + prevPosition = curPosition; + count = curCount; + continue; + } + if(curPosition != prevPosition && count != defaultCount) { + interval_data ivl(prevPosition, curPosition); + container.clear(); + boolean.processInterval(container, ivl, count); + for(std::size_t i = 0; i < container.size(); ++i) { + std::pair, int>& element = container[i]; + if(!output.empty() && output.back().first == prevCoord && + output.back().second.first == element.first.low() && + output.back().second.second == element.second * -1) { + output.pop_back(); + } else { + output.push_back(std::pair >(prevCoord, std::pair(element.first.low(), + element.second))); + } + output.push_back(std::pair >(prevCoord, std::pair(element.first.high(), + element.second * -1))); + } + } + prevPosition = curPosition; + count += curCount; + } + } + + template + inline void applyBooleanBinaryOp(std::vector > >& inputOutput, + const std::vector > >& input2, + T defaultCount) { + std::vector > > output; + applyBooleanBinaryOp(output, inputOutput, input2, defaultCount); + if(output.size() < inputOutput.size() / 2) { + inputOutput = std::vector > >(); + } else { + inputOutput.clear(); + } + inputOutput.insert(inputOutput.end(), output.begin(), output.end()); + } + + template + struct default_arg_workaround { + template + static inline void applyBooleanOr(std::vector > >& input) { + BooleanOp booleanOr; + std::vector, int> > container; + std::vector > > output; + output.reserve(input.size()); + //consider eliminating dependecy on limits with bool flag for initial state + Unit UnitMax = (std::numeric_limits::max)(); + Unit prevPos = UnitMax; + Unit prevY = UnitMax; + int count = 0; + for(typename std::vector > >::iterator itr = input.begin(); + itr != input.end(); ++itr) { + Unit pos = (*itr).first; + Unit y = (*itr).second.first; + if(pos != prevPos) { + booleanOr.advanceScan(); + prevPos = pos; + prevY = y; + count = (*itr).second.second; + continue; + } + if(y != prevY && count != 0) { + interval_data ivl(prevY, y); + container.clear(); + booleanOr.processInterval(container, ivl, count_type(count)); + for(std::size_t i = 0; i < container.size(); ++i) { + std::pair, int>& element = container[i]; + if(!output.empty() && output.back().first == prevPos && + output.back().second.first == element.first.low() && + output.back().second.second == element.second * -1) { + output.pop_back(); + } else { + output.push_back(std::pair >(prevPos, std::pair(element.first.low(), + element.second))); + } + output.push_back(std::pair >(prevPos, std::pair(element.first.high(), + element.second * -1))); + } + } + prevY = y; + count += (*itr).second.second; + } + if(output.size() < input.size() / 2) { + input = std::vector > >(); + } else { + input.clear(); + } + input.insert(input.end(), output.begin(), output.end()); + } + }; + +} + +} + +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op_45.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op_45.hpp new file mode 100644 index 0000000..0d65501 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/boolean_op_45.hpp @@ -0,0 +1,1398 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_BOOLEAN_OP_45_HPP +#define BOOST_POLYGON_BOOLEAN_OP_45_HPP +namespace boost { namespace polygon{ + + template + struct boolean_op_45 { + typedef point_data Point; + typedef typename coordinate_traits::manhattan_area_type LongUnit; + + class Count2 { + public: + inline Count2() +#ifndef BOOST_POLYGON_MSVC + : counts() +#endif + { counts[0] = counts[1] = 0; } + //inline Count2(int count) { counts[0] = counts[1] = count; } + inline Count2(int count1, int count2) +#ifndef BOOST_POLYGON_MSVC + : counts() +#endif + { counts[0] = count1; counts[1] = count2; } + inline Count2(const Count2& count) +#ifndef BOOST_POLYGON_MSVC + : counts() +#endif + { counts[0] = count.counts[0]; counts[1] = count.counts[1]; } + inline bool operator==(const Count2& count) const { return counts[0] == count.counts[0] && counts[1] == count.counts[1]; } + inline bool operator!=(const Count2& count) const { return !((*this) == count); } + inline Count2& operator=(int count) { counts[0] = counts[1] = count; return *this; } + inline Count2& operator=(const Count2& count) { counts[0] = count.counts[0]; counts[1] = count.counts[1]; return *this; } + inline int& operator[](bool index) { return counts[index]; } + inline int operator[](bool index) const {return counts[index]; } + inline Count2& operator+=(const Count2& count){ + counts[0] += count[0]; + counts[1] += count[1]; + return *this; + } + inline Count2& operator-=(const Count2& count){ + counts[0] -= count[0]; + counts[1] -= count[1]; + return *this; + } + inline Count2 operator+(const Count2& count) const { + return Count2(*this)+=count; + } + inline Count2 operator-(const Count2& count) const { + return Count2(*this)-=count; + } + inline Count2 invert() const { + return Count2(-counts[0], -counts[1]); + } + private: + int counts[2]; + }; + + class Count1 { + public: + inline Count1() : count_(0) { } + inline Count1(int count) : count_(count) { } + inline Count1(const Count1& count) : count_(count.count_) { } + inline bool operator==(const Count1& count) const { return count_ == count.count_; } + inline bool operator!=(const Count1& count) const { return !((*this) == count); } + inline Count1& operator=(int count) { count_ = count; return *this; } + inline Count1& operator=(const Count1& count) { count_ = count.count_; return *this; } + inline Count1& operator+=(const Count1& count){ + count_ += count.count_; + return *this; + } + inline Count1& operator-=(const Count1& count){ + count_ -= count.count_; + return *this; + } + inline Count1 operator+(const Count1& count) const { + return Count1(*this)+=count; + } + inline Count1 operator-(const Count1& count) const { + return Count1(*this)-=count; + } + inline Count1 invert() const { + return Count1(-count_); + } + int count_; + }; + + // inline std::ostream& operator<< (std::ostream& o, const Count2& c) { + // o << c[0] << " " << c[1]; + // return o; + // } + + template + class Scan45ElementT { + public: + Unit x; + Unit y; + int rise; //-1, 0, +1 + mutable CountType count; + inline Scan45ElementT() : x(), y(), rise(), count() {} + inline Scan45ElementT(Unit xIn, Unit yIn, int riseIn, CountType countIn = CountType()) : + x(xIn), y(yIn), rise(riseIn), count(countIn) {} + inline Scan45ElementT(const Scan45ElementT& that) : + x(that.x), y(that.y), rise(that.rise), count(that.count) {} + inline Scan45ElementT& operator=(const Scan45ElementT& that) { + x = that.x; y = that.y; rise = that.rise; count = that.count; + return *this; + } + inline Unit evalAtX(Unit xIn) const { + return y + rise * (xIn - x); + } + + inline bool cross(Point& crossPoint, const Scan45ElementT& edge, Unit currentX) const { + Unit y1 = evalAtX(currentX); + Unit y2 = edge.evalAtX(currentX); + int rise1 = rise; + int rise2 = edge.rise; + if(rise > edge.rise){ + if(y1 > y2) return false; + } else if(rise < edge.rise){ + if(y2 > y1) return false; + std::swap(y1, y2); + std::swap(rise1, rise2); + } else { return false; } + if(rise1 == 1) { + if(rise2 == 0) { + crossPoint = Point(currentX + y2 - y1, y2); + } else { + //rise2 == -1 + Unit delta = (y2 - y1)/2; + crossPoint = Point(currentX + delta, y1 + delta); + } + } else { + //rise1 == 0 and rise2 == -1 + crossPoint = Point(currentX + y2 - y1, y1); + } + return true; + } + }; + + typedef Scan45ElementT Scan45Element; + + // inline std::ostream& operator<< (std::ostream& o, const Scan45Element& c) { + // o << c.x << " " << c.y << " " << c.rise << " " << c.count; + // return o; + // } + + class lessScan45ElementRise { + public: + typedef Scan45Element first_argument_type; + typedef Scan45Element second_argument_type; + typedef bool result_type; + inline lessScan45ElementRise() {} //default constructor is only constructor + inline bool operator () (Scan45Element elm1, Scan45Element elm2) const { + return elm1.rise < elm2.rise; + } + }; + + template + class lessScan45Element { + private: + Unit *x_; //x value at which to apply comparison + int *justBefore_; + public: + inline lessScan45Element() : x_(0), justBefore_(0) {} + inline lessScan45Element(Unit *x, int *justBefore) : x_(x), justBefore_(justBefore) {} + inline lessScan45Element(const lessScan45Element& that) : x_(that.x_), justBefore_(that.justBefore_) {} + inline lessScan45Element& operator=(const lessScan45Element& that) { x_ = that.x_; justBefore_ = that.justBefore_; return *this; } + inline bool operator () (const Scan45ElementT& elm1, + const Scan45ElementT& elm2) const { + Unit y1 = elm1.evalAtX(*x_); + Unit y2 = elm2.evalAtX(*x_); + if(y1 < y2) return true; + if(y1 == y2) { + //if justBefore is true we invert the result of the comparison of slopes + if(*justBefore_) { + return elm1.rise > elm2.rise; + } else { + return elm1.rise < elm2.rise; + } + } + return false; + } + }; + + template + class Scan45CountT { + public: + inline Scan45CountT() : counts() {} //counts[0] = counts[1] = counts[2] = counts[3] = 0; } + inline Scan45CountT(CountType count) : counts() { counts[0] = counts[1] = counts[2] = counts[3] = count; } + inline Scan45CountT(const CountType& count1, const CountType& count2, const CountType& count3, + const CountType& count4) : counts() { + counts[0] = count1; + counts[1] = count2; + counts[2] = count3; + counts[3] = count4; + } + inline Scan45CountT(const Scan45CountT& count) : counts() { + (*this) = count; + } + inline bool operator==(const Scan45CountT& count) const { + for(unsigned int i = 0; i < 4; ++i) { + if(counts[i] != count.counts[i]) return false; + } + return true; + } + inline bool operator!=(const Scan45CountT& count) const { return !((*this) == count); } + inline Scan45CountT& operator=(CountType count) { + counts[0] = counts[1] = counts[2] = counts[3] = count; return *this; } + inline Scan45CountT& operator=(const Scan45CountT& count) { + for(unsigned int i = 0; i < 4; ++i) { + counts[i] = count.counts[i]; + } + return *this; + } + inline CountType& operator[](int index) { return counts[index]; } + inline CountType operator[](int index) const {return counts[index]; } + inline Scan45CountT& operator+=(const Scan45CountT& count){ + for(unsigned int i = 0; i < 4; ++i) { + counts[i] += count.counts[i]; + } + return *this; + } + inline Scan45CountT& operator-=(const Scan45CountT& count){ + for(unsigned int i = 0; i < 4; ++i) { + counts[i] -= count.counts[i]; + } + return *this; + } + inline Scan45CountT operator+(const Scan45CountT& count) const { + return Scan45CountT(*this)+=count; + } + inline Scan45CountT operator-(const Scan45CountT& count) const { + return Scan45CountT(*this)-=count; + } + inline Scan45CountT invert() const { + return Scan45CountT(CountType())-=(*this); + } + inline Scan45CountT& operator+=(const Scan45ElementT& element){ + counts[element.rise+1] += element.count; return *this; + } + private: + CountType counts[4]; + }; + + typedef Scan45CountT Scan45Count; + + // inline std::ostream& operator<< (std::ostream& o, const Scan45Count& c) { + // o << c[0] << ", " << c[1] << ", "; + // o << c[2] << ", " << c[3]; + // return o; + // } + + + // inline std::ostream& operator<< (std::ostream& o, const Scan45Vertex& c) { + // o << c.first << ": " << c.second; + // return o; + // } + + + //vetex45 is sortable + template + class Vertex45T { + public: + Point pt; + int rise; // 1, 0 or -1 + ct count; //dxdydTheta + inline Vertex45T() : pt(), rise(), count() {} + inline Vertex45T(const Point& point, int riseIn, ct countIn) : pt(point), rise(riseIn), count(countIn) {} + inline Vertex45T(const Vertex45T& vertex) : pt(vertex.pt), rise(vertex.rise), count(vertex.count) {} + inline Vertex45T& operator=(const Vertex45T& vertex){ + pt = vertex.pt; rise = vertex.rise; count = vertex.count; return *this; } + inline bool operator==(const Vertex45T& vertex) const { + return pt == vertex.pt && rise == vertex.rise && count == vertex.count; } + inline bool operator!=(const Vertex45T& vertex) const { return !((*this) == vertex); } + inline bool operator<(const Vertex45T& vertex) const { + if(pt.x() < vertex.pt.x()) return true; + if(pt.x() == vertex.pt.x()) { + if(pt.y() < vertex.pt.y()) return true; + if(pt.y() == vertex.pt.y()) { return rise < vertex.rise; } + } + return false; + } + inline bool operator>(const Vertex45T& vertex) const { return vertex < (*this); } + inline bool operator<=(const Vertex45T& vertex) const { return !((*this) > vertex); } + inline bool operator>=(const Vertex45T& vertex) const { return !((*this) < vertex); } + inline Unit evalAtX(Unit xIn) const { return pt.y() + rise * (xIn - pt.x()); } + }; + + typedef Vertex45T Vertex45; + + // inline std::ostream& operator<< (std::ostream& o, const Vertex45& c) { + // o << c.pt << " " << c.rise << " " << c.count; + // return o; + // } + + //when scanning Vertex45 for polygon formation we need a scanline comparator functor + class lessVertex45 { + private: + Unit *x_; //x value at which to apply comparison + int *justBefore_; + public: + inline lessVertex45() : x_(0), justBefore_() {} + + inline lessVertex45(Unit *x, int *justBefore) : x_(x), justBefore_(justBefore) {} + + inline lessVertex45(const lessVertex45& that) : x_(that.x_), justBefore_(that.justBefore_) {} + + inline lessVertex45& operator=(const lessVertex45& that) { x_ = that.x_; justBefore_ = that.justBefore_; return *this; } + + template + inline bool operator () (const Vertex45T& elm1, const Vertex45T& elm2) const { + Unit y1 = elm1.evalAtX(*x_); + Unit y2 = elm2.evalAtX(*x_); + if(y1 < y2) return true; + if(y1 == y2) { + //if justBefore is true we invert the result of the comparison of slopes + if(*justBefore_) { + return elm1.rise > elm2.rise; + } else { + return elm1.rise < elm2.rise; + } + } + return false; + } + }; + + // 0 right to left + // 1 upper right to lower left + // 2 high to low + // 3 upper left to lower right + // 4 left to right + // 5 lower left to upper right + // 6 low to high + // 7 lower right to upper left + static inline int classifyEdge45(const Point& prevPt, const Point& nextPt) { + if(prevPt.x() == nextPt.x()) { + //2 or 6 + return predicated_value(prevPt.y() < nextPt.y(), 6, 2); + } + if(prevPt.y() == nextPt.y()) { + //0 or 4 + return predicated_value(prevPt.x() < nextPt.x(), 4, 0); + } + if(prevPt.x() < nextPt.x()) { + //3 or 5 + return predicated_value(prevPt.y() < nextPt.y(), 5, 3); + } + //prevPt.x() > nextPt.y() + //1 or 7 + return predicated_value(prevPt.y() < nextPt.y(), 7, 1); + } + + template + static int applyLogic(CountType count1, CountType count2){ + bool l1 = applyLogic(count1); + bool l2 = applyLogic(count2); + if(l1 && !l2) + return -1; //was true before and became false like a trailing edge + if(!l1 && l2) + return 1; //was false before and became true like a leading edge + return 0; //no change in logic between the two counts + } + template + static bool applyLogic(Count2 count) { +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op == 0) { //apply or + return count[0] > 0 || count[1] > 0; + } else if(op == 1) { //apply and + return count[0] > 0 && count[1] > 0; + } else if(op == 2) { //apply not + return count[0] > 0 && !(count[1] > 0); + } else if(op == 3) { //apply xor + return (count[0] > 0) ^ (count[1] > 0); + } else + return false; +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + } + + template + struct boolean_op_45_output_functor { + template + void operator()(cT& output, const Count2& count1, const Count2& count2, + const Point& pt, int rise, direction_1d end) { + int edgeType = applyLogic(count1, count2); + if(edgeType) { + int multiplier = end == LOW ? -1 : 1; + //std::cout << "cross logic: " << edgeType << "\n"; + output.insert(output.end(), Vertex45(pt, rise, edgeType * multiplier)); + //std::cout << "write out: " << crossPoint << " " << Point(eraseItrs[i]->x, eraseItrs[i]->y) << "\n"; + } + } + }; + + template + static bool applyLogic(Count1 count) { +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op == 0) { //apply or + return count.count_ > 0; + } else if(op == 1) { //apply and + return count.count_ > 1; + } else if(op == 3) { //apply xor + return (count.count_ % 2) != 0; + } else + return false; +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + } + + template + struct unary_op_45_output_functor { + template + void operator()(cT& output, const Count1& count1, const Count1& count2, + const Point& pt, int rise, direction_1d end) { + int edgeType = applyLogic(count1, count2); + if(edgeType) { + int multiplier = end == LOW ? -1 : 1; + //std::cout << "cross logic: " << edgeType << "\n"; + output.insert(output.end(), Vertex45(pt, rise, edgeType * multiplier)); + //std::cout << "write out: " << crossPoint << " " << Point(eraseItrs[i]->x, eraseItrs[i]->y) << "\n"; + } + } + }; + + class lessScan45Vertex { + public: + inline lessScan45Vertex() {} //default constructor is only constructor + template + inline bool operator () (const Scan45Vertex& v1, const Scan45Vertex& v2) const { + return (v1.first.x() < v2.first.x()) || (v1.first.x() == v2.first.x() && v1.first.y() < v2.first.y()); + } + }; + template + static inline void sortScan45Vector(S45V& vec) { + polygon_sort(vec.begin(), vec.end(), lessScan45Vertex()); + } + + template + class Scan45 { + public: + typedef Scan45CountT Scan45Count; + typedef std::pair Scan45Vertex; + + //index is the index into the vertex + static inline Scan45Element getElement(const Scan45Vertex& vertex, int index) { + return Scan45Element(vertex.first.x(), vertex.first.y(), index - 1, vertex.second[index]); + } + + class lessScan45Point { + public: + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + inline lessScan45Point() {} //default constructor is only constructor + inline bool operator () (const Point& v1, const Point& v2) const { + return (v1.x() < v2.x()) || (v1.x() == v2.x() && v1.y() < v2.y()); + } + }; + + typedef std::vector Scan45Vector; + + //definitions + typedef std::set, lessScan45Element > Scan45Data; + typedef typename Scan45Data::iterator iterator; + typedef typename Scan45Data::const_iterator const_iterator; + typedef std::set CrossQueue; + + //data + Scan45Data scanData_; + CrossQueue crossQueue_; + Scan45Vector crossVector_; + Unit x_; + int justBefore_; + public: + inline Scan45() : scanData_(), crossQueue_(), crossVector_(), + x_((std::numeric_limits::min)()), justBefore_(false) { + lessScan45Element lessElm(&x_, &justBefore_); + scanData_ = std::set, lessScan45Element >(lessElm); + } + inline Scan45(const Scan45& that) : scanData_(), crossQueue_(), crossVector_(), + x_((std::numeric_limits::min)()), justBefore_(false) { + (*this) = that; } + inline Scan45& operator=(const Scan45& that) { + x_ = that.x_; + justBefore_ = that.justBefore_; + crossQueue_ = that.crossQueue_; + crossVector_ = that.crossVector_; + lessScan45Element lessElm(&x_, &justBefore_); + scanData_ = std::set, lessScan45Element >(lessElm); + for(const_iterator itr = that.scanData_.begin(); itr != that.scanData_.end(); ++itr){ + scanData_.insert(scanData_.end(), *itr); + } + return *this; + } + + //cT is an output container of Vertex45 + //iT is an iterator over Scan45Vertex elements + template + void scan(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "1\n"; + while(inputBegin != inputEnd) { + //std::cout << "2\n"; + //std::cout << "x_ = " << x_ << "\n"; + //std::cout << "scan line size: " << scanData_.size() << "\n"; + //for(iterator iter = scanData_.begin(); + // iter != scanData_.end(); ++iter) { + // std::cout << "scan element\n"; + // std::cout << *iter << " " << iter->evalAtX(x_) << "\n"; + // } + // std::cout << "cross queue size: " << crossQueue_.size() << "\n"; + // std::cout << "cross vector size: " << crossVector_.size() << "\n"; + //for(CrossQueue::iterator cqitr = crossQueue_.begin(); cqitr != crossQueue_.end(); ++cqitr) { + // std::cout << *cqitr << " "; + //} std::cout << "\n"; + Unit nextX = (*inputBegin).first.x(); + if(!crossVector_.empty() && crossVector_[0].first.x() < nextX) nextX = crossVector_[0].first.x(); + if(nextX != x_) { + //std::cout << "3\n"; + //we need to move to the next scanline stop + //we need to process end events then cross events + //process end events + if(!crossQueue_.empty() && + (*crossQueue_.begin()).x() < nextX) { + //std::cout << "4\n"; + nextX = (std::min)(nextX, (*crossQueue_.begin()).x()); + } + //std::cout << "6\n"; + justBefore_ = true; + x_ = nextX; + advance_(output); + justBefore_ = false; + if(!crossVector_.empty() && + nextX == (*inputBegin).first.x()) { + inputBegin = mergeCross_(inputBegin, inputEnd); + } + processEvent_(output, crossVector_.begin(), crossVector_.end()); + crossVector_.clear(); + } else { + //std::cout << "7\n"; + //our scanline has progressed to the event that is next in the queue + inputBegin = processEvent_(output, inputBegin, inputEnd); + } + } + //std::cout << "done scanning\n"; + } + + private: + //functions + + template + inline void advance_(cT& output) { + //process all cross points on the cross queue at the current x_ + //std::cout << "advance_\n"; + std::vector eraseVec; + while(!crossQueue_.empty() && + (*crossQueue_.begin()).x() == x_){ + //std::cout << "loop\n"; + //pop point off the cross queue + Point crossPoint = *(crossQueue_.begin()); + //std::cout << crossPoint << "\n"; + //for(iterator iter = scanData_.begin(); + // iter != scanData_.end(); ++iter) { + // std::cout << "scan element\n"; + // std::cout << *iter << " " << iter->evalAtX(x_) << "\n"; + //} + crossQueue_.erase(crossQueue_.begin()); + Scan45Vertex vertex(crossPoint, Scan45Count()); + iterator lowIter = lookUp_(vertex.first.y()); + //std::cout << "searching at: " << vertex.first.y() << "\n"; + //if(lowIter == scanData_.end()) std::cout << "could not find\n"; + //else std::cout << "found: " << *lowIter << "\n"; + if(lowIter == scanData_.end() || + lowIter->evalAtX(x_) != vertex.first.y()) { + // std::cout << "skipping\n"; + //there weren't any edges at this potential cross point + continue; + } + CountType countBelow; + iterator searchDownItr = lowIter; + while(searchDownItr != scanData_.begin() + && searchDownItr->evalAtX(x_) == vertex.first.y()) { + //get count from below + --searchDownItr; + countBelow = searchDownItr->count; + } + //std::cout << "Below Count: " << countBelow << "\n"; + Scan45Count count(countBelow); + std::size_t numEdges = 0; + iterator eraseItrs[3]; + while(lowIter != scanData_.end() && + lowIter->evalAtX(x_) == vertex.first.y()) { + for(int index = lowIter->rise +1; index >= 0; --index) + count[index] = lowIter->count; + //std::cout << count << "\n"; + eraseItrs[numEdges] = lowIter; + ++numEdges; + ++lowIter; + } + if(numEdges == 1) { + //look for the next crossing point and continue + //std::cout << "found only one edge\n"; + findCross_(eraseItrs[0]); + continue; + } + //before we erase the elements we need to decide if they should be written out + CountType currentCount = countBelow; + for(std::size_t i = 0; i < numEdges; ++i) { + output_functor f; + f(output, currentCount, eraseItrs[i]->count, crossPoint, eraseItrs[i]->rise, LOW); + currentCount = eraseItrs[i]->count; + } + //schedule erase of the elements + for(std::size_t i = 0; i < numEdges; ++i) { + eraseVec.push_back(eraseItrs[i]); + } + + //take the derivative wrt theta of the count at the crossing point + vertex.second[2] = count[2] - countBelow; + vertex.second[1] = count[1] - count[2]; + vertex.second[0] = count[0] - count[1]; + //add the point, deriviative pair into the cross vector + //std::cout << "LOOK HERE!\n"; + //std::cout << count << "\n"; + //std::cout << vertex << "\n"; + crossVector_.push_back(vertex); + } + //erase crossing elements + std::vector searchVec; + for(std::size_t i = 0; i < eraseVec.size(); ++i) { + if(eraseVec[i] != scanData_.begin()) { + iterator searchItr = eraseVec[i]; + --searchItr; + if(searchVec.empty() || + searchVec.back() != searchItr) + searchVec.push_back(searchItr); + } + scanData_.erase(eraseVec[i]); + } + for(std::size_t i = 0; i < searchVec.size(); ++i) { + findCross_(searchVec[i]); + } + } + + template + inline iT mergeCross_(iT inputBegin, iT inputEnd) { + Scan45Vector vec; + swap(vec, crossVector_); + iT mergeEnd = inputBegin; + std::size_t mergeCount = 0; + while(mergeEnd != inputEnd && + (*mergeEnd).first.x() == x_) { + ++mergeCount; + ++mergeEnd; + } + crossVector_.reserve((std::max)(vec.capacity(), vec.size() + mergeCount)); + for(std::size_t i = 0; i < vec.size(); ++i){ + while(inputBegin != mergeEnd && + (*inputBegin).first.y() < vec[i].first.y()) { + crossVector_.push_back(*inputBegin); + ++inputBegin; + } + crossVector_.push_back(vec[i]); + } + while(inputBegin != mergeEnd){ + crossVector_.push_back(*inputBegin); + ++inputBegin; + } + return inputBegin; + } + + template + inline iT processEvent_(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "processEvent_\n"; + CountType verticalCount = CountType(); + Point prevPoint; + iterator prevIter = scanData_.end(); + while(inputBegin != inputEnd && + (*inputBegin).first.x() == x_) { + //std::cout << (*inputBegin) << "\n"; + //std::cout << "loop\n"; + Scan45Vertex vertex = *inputBegin; + //std::cout << vertex.first << "\n"; + //if vertical count propigating up fake a null event at the next element + if(verticalCount != CountType() && (prevIter != scanData_.end() && + prevIter->evalAtX(x_) < vertex.first.y())) { + //std::cout << "faking null event\n"; + vertex = Scan45Vertex(Point(x_, prevIter->evalAtX(x_)), Scan45Count()); + } else { + ++inputBegin; + //std::cout << "after increment\n"; + //accumulate overlapping changes in Scan45Count + while(inputBegin != inputEnd && + (*inputBegin).first.x() == x_ && + (*inputBegin).first.y() == vertex.first.y()) { + //std::cout << "accumulate\n"; + vertex.second += (*inputBegin).second; + ++inputBegin; + } + } + //std::cout << vertex.second << "\n"; + //integrate vertex + CountType currentCount = verticalCount;// + vertex.second[0]; + for(unsigned int i = 0; i < 3; ++i) { + vertex.second[i] = currentCount += vertex.second[i]; + } + //std::cout << vertex.second << "\n"; + //vertex represents the change in state at this point + + //get counts at current vertex + CountType countBelow; + iterator lowIter = lookUp_(vertex.first.y()); + if(lowIter != scanData_.begin()) { + //get count from below + --lowIter; + countBelow = lowIter->count; + ++lowIter; + } + //std::cout << "Count Below: " << countBelow[0] << " " << countBelow[1] << "\n"; + //std::cout << "vertical count: " << verticalCount[0] << " " << verticalCount[1] << "\n"; + Scan45Count countAt(countBelow - verticalCount); + //check if the vertical edge should be written out + if(verticalCount != CountType()) { + output_functor f; + f(output, countBelow - verticalCount, countBelow, prevPoint, 2, HIGH); + f(output, countBelow - verticalCount, countBelow, vertex.first, 2, LOW); + } + currentCount = countBelow - verticalCount; + while(lowIter != scanData_.end() && + lowIter->evalAtX(x_) == vertex.first.y()) { + for(unsigned int i = lowIter->rise + 1; i < 3; ++i) { + countAt[i] = lowIter->count; + } + Point lp(lowIter->x, lowIter->y); + if(lp != vertex.first) { + output_functor f; + f(output, currentCount, lowIter->count, vertex.first, lowIter->rise, LOW); + } + currentCount = lowIter->count; + iterator nextIter = lowIter; + ++nextIter; + //std::cout << "erase\n"; + scanData_.erase(lowIter); + if(nextIter != scanData_.end()) + findCross_(nextIter); + lowIter = nextIter; + } + verticalCount += vertex.second[3]; + prevPoint = vertex.first; + //std::cout << "new vertical count: " << verticalCount[0] << " " << verticalCount[1] << "\n"; + prevIter = lowIter; + //count represents the current state at this point + //std::cout << vertex.second << "\n"; + //std::cout << countAt << "\n"; + //std::cout << "ADD\n"; + vertex.second += countAt; + //std::cout << vertex.second << "\n"; + + //add elements to the scanline + for(int i = 0; i < 3; ++i) { + if(vertex.second[i] != countBelow) { + //std::cout << "insert: " << vertex.first.x() << " " << vertex.first.y() << " " << i-1 << + // " " << vertex.second[i][0] << " " << vertex.second[i][1] << "\n"; + iterator insertIter = scanData_.insert(scanData_.end(), + Scan45ElementT(vertex.first.x(), + vertex.first.y(), + i - 1, vertex.second[i])); + findCross_(insertIter); + output_functor f; + f(output, countBelow, vertex.second[i], vertex.first, i - 1, HIGH); + } + countBelow = vertex.second[i]; + } + } + //std::cout << "end processEvent\n"; + return inputBegin; + } + + //iter1 is horizontal + inline void scheduleCross0_(iterator iter1, iterator iter2) { + //std::cout << "0, "; + Unit y1 = iter1->evalAtX(x_); + Unit y2 = iter2->evalAtX(x_); + LongUnit delta = local_abs(LongUnit(y1) - LongUnit(y2)); + if(delta + static_cast(x_) <= (std::numeric_limits::max)()) + crossQueue_.insert(crossQueue_.end(), Point(x_ + static_cast(delta), y1)); + //std::cout << Point(x_ + delta, y1); + } + + //neither iter is horizontal + inline void scheduleCross1_(iterator iter1, iterator iter2) { + //std::cout << "1, "; + Unit y1 = iter1->evalAtX(x_); + Unit y2 = iter2->evalAtX(x_); + //std::cout << y1 << " " << y2 << ": "; + //note that half the delta cannot exceed the positive inter range + LongUnit delta = y1; + delta -= y2; + Unit UnitMax = (std::numeric_limits::max)(); + if((delta & 1) == 1) { + //delta is odd, division by 2 will result in integer trunctaion + if(delta == 1) { + //the cross point is not on the integer grid and cannot be represented + //we must throw an exception + std::string msg = "GTL 45 Boolean error, precision insufficient to represent edge intersection coordinate value."; + throw(msg); + } else { + //note that result of this subtraction is always positive because itr1 is above itr2 in scanline + LongUnit halfDelta2 = (LongUnit)((((LongUnit)y1) - y2)/2); + //note that halfDelta2 has been truncated + if(halfDelta2 + x_ <= UnitMax && halfDelta2 + y2 <= UnitMax) { + crossQueue_.insert(crossQueue_.end(), Point(x_+static_cast(halfDelta2), y2+static_cast(halfDelta2))); + crossQueue_.insert(crossQueue_.end(), Point(x_+static_cast(halfDelta2), y2+static_cast(halfDelta2)+1)); + } + } + } else { + LongUnit halfDelta = (LongUnit)((((LongUnit)y1) - y2)/2); + if(halfDelta + x_ <= UnitMax && halfDelta + y2 <= UnitMax) + crossQueue_.insert(crossQueue_.end(), Point(x_+static_cast(halfDelta), y2+static_cast(halfDelta))); + //std::cout << Point(x_+halfDelta, y2+halfDelta); + } + } + + inline void findCross_(iterator iter) { + //std::cout << "find cross "; + iterator iteratorBelow = iter; + iterator iteratorAbove = iter; + if(iter != scanData_.begin() && iter->rise < 1) { + --iteratorBelow; + if(iter->rise == 0){ + if(iteratorBelow->rise == 1) { + scheduleCross0_(iter, iteratorBelow); + } + } else { + //iter->rise == -1 + if(iteratorBelow->rise == 1) { + scheduleCross1_(iter, iteratorBelow); + } else if(iteratorBelow->rise == 0) { + scheduleCross0_(iteratorBelow, iter); + } + } + } + ++iteratorAbove; + if(iteratorAbove != scanData_.end() && iter->rise > -1) { + if(iter->rise == 0) { + if(iteratorAbove->rise == -1) { + scheduleCross0_(iter, iteratorAbove); + } + } else { + //iter->rise == 1 + if(iteratorAbove->rise == -1) { + scheduleCross1_(iteratorAbove, iter); + } else if(iteratorAbove->rise == 0) { + scheduleCross0_(iteratorAbove, iter); + } + } + } + //std::cout << "\n"; + } + + inline iterator lookUp_(Unit y){ + //if just before then we need to look from 1 not -1 + return scanData_.lower_bound(Scan45ElementT(x_, y, -1+2*justBefore_)); + } + }; + + //template + //static inline void print45Data(const std::set, + // lessScan45Element >& data) { + // typename std::set, lessScan45Element >::const_iterator iter; + // for(iter = data.begin(); iter != data.end(); ++iter) { + // std::cout << iter->x << " " << iter->y << " " << iter->rise << "\n"; + // } + //} + + template + static inline bool testScan45Data(streamtype& stdcout) { + Unit x = 0; + int justBefore = false; + lessScan45Element lessElm(&x, &justBefore); + std::set, lessScan45Element > testData(lessElm); + //Unit size = testData.size(); + typedef std::set, lessScan45Element > Scan45Data; + typename Scan45Data::iterator itr10 = testData.insert(testData.end(), Scan45Element(0, 10, 1)); + typename Scan45Data::iterator itr20 = testData.insert(testData.end(), Scan45Element(0, 20, 1)); + typename Scan45Data::iterator itr30 = testData.insert(testData.end(), Scan45Element(0, 30, -1)); + typename Scan45Data::iterator itr40 = testData.insert(testData.end(), Scan45Element(0, 40, -1)); + typename Scan45Data::iterator itrA = testData.lower_bound(Scan45Element(0, 29, -1)); + typename Scan45Data::iterator itr1 = testData.lower_bound(Scan45Element(0, 10, -1)); + x = 4; + //now at 14 24 26 36 + typename Scan45Data::iterator itrB = testData.lower_bound(Scan45Element(4, 29, -1)); + typename Scan45Data::iterator itr2 = testData.lower_bound(Scan45Element(4, 14, -1)); + if(itr1 != itr2) stdcout << "test1 failed\n"; + if(itrA == itrB) stdcout << "test2 failed\n"; + //remove crossing elements + testData.erase(itr20); + testData.erase(itr30); + x = 5; + itr20 = testData.insert(testData.end(), Scan45Element(0, 20, 1)); + itr30 = testData.insert(testData.end(), Scan45Element(0, 30, -1)); + //now at 15 25 25 35 + typename Scan45Data::iterator itr = testData.begin(); + if(itr != itr10) stdcout << "test3 failed\n"; + ++itr; + if(itr != itr30) stdcout << "test4 failed\n"; + ++itr; + if(itr != itr20) stdcout << "test5 failed\n"; + ++itr; + if(itr != itr40) stdcout << "test6 failed\n"; + stdcout << "done testing Scan45Data\n"; + return true; + } + + template + static inline bool testScan45Rect(stream_type& stdcout) { + stdcout << "testing Scan45Rect\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,0), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + vertices.push_back(Scan45Vertex(Point(0,10), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(10,0), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(10,10), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + // result size == 8 + // result == 0 0 0 1 + // result == 0 0 2 1 + // result == 0 10 2 -1 + // result == 0 10 0 -1 + // result == 10 0 0 -1 + // result == 10 0 2 -1 + // result == 10 10 2 1 + // result == 10 10 0 1 + std::vector reference; + reference.push_back(Vertex45(Point(0, 0), 0, 1)); + reference.push_back(Vertex45(Point(0, 0), 2, 1)); + reference.push_back(Vertex45(Point(0, 10), 2, -1)); + reference.push_back(Vertex45(Point(0, 10), 0, -1)); + reference.push_back(Vertex45(Point(10, 0), 0, -1)); + reference.push_back(Vertex45(Point(10, 0), 2, -1)); + reference.push_back(Vertex45(Point(10, 10), 2, 1)); + reference.push_back(Vertex45(Point(10, 10), 0, 1)); + if(result != reference) { + stdcout << "result size == " << result.size() << "\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + //std::cout << "result == " << result[i]<< "\n"; + } + stdcout << "reference size == " << reference.size() << "\n"; + for(std::size_t i = 0; i < reference.size(); ++i) { + //std::cout << "reference == " << reference[i]<< "\n"; + } + return false; + } + stdcout << "done testing Scan45Rect\n"; + return true; + } + + template + static inline bool testScan45P1(stream_type& stdcout) { + stdcout << "testing Scan45P1\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,0), Scan45Count(Count2(0, 0), Count2(0, 0), count, count))); + vertices.push_back(Scan45Vertex(Point(0,10), Scan45Count(Count2(0, 0), Count2(0, 0), ncount, ncount))); + vertices.push_back(Scan45Vertex(Point(10,10), Scan45Count(Count2(0, 0), Count2(0, 0), ncount, ncount))); + vertices.push_back(Scan45Vertex(Point(10,20), Scan45Count(Count2(0, 0), Count2(0, 0), count, count))); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + // result size == 8 + // result == 0 0 1 1 + // result == 0 0 2 1 + // result == 0 10 2 -1 + // result == 0 10 1 -1 + // result == 10 10 1 -1 + // result == 10 10 2 -1 + // result == 10 20 2 1 + // result == 10 20 1 1 + std::vector reference; + reference.push_back(Vertex45(Point(0, 0), 1, 1)); + reference.push_back(Vertex45(Point(0, 0), 2, 1)); + reference.push_back(Vertex45(Point(0, 10), 2, -1)); + reference.push_back(Vertex45(Point(0, 10), 1, -1)); + reference.push_back(Vertex45(Point(10, 10), 1, -1)); + reference.push_back(Vertex45(Point(10, 10), 2, -1)); + reference.push_back(Vertex45(Point(10, 20), 2, 1)); + reference.push_back(Vertex45(Point(10, 20), 1, 1)); + if(result != reference) { + stdcout << "result size == " << result.size() << "\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + //std::cout << "result == " << result[i]<< "\n"; + } + stdcout << "reference size == " << reference.size() << "\n"; + for(std::size_t i = 0; i < reference.size(); ++i) { + //std::cout << "reference == " << reference[i]<< "\n"; + } + return false; + } + stdcout << "done testing Scan45P1\n"; + return true; + } + + template + static inline bool testScan45P2(stream_type& stdcout) { + stdcout << "testing Scan45P2\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,0), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(10,0), Scan45Count(Count2(0, 0), ncount, count, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(10,10), Scan45Count(Count2(0, 0), ncount, count, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(20,10), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + // result size == 8 + // result == 0 0 0 1 + // result == 0 0 1 -1 + // result == 10 0 0 -1 + // result == 10 0 1 1 + // result == 10 10 1 1 + // result == 10 10 0 -1 + // result == 20 10 1 -1 + // result == 20 10 0 1 + std::vector reference; + reference.push_back(Vertex45(Point(0, 0), 0, 1)); + reference.push_back(Vertex45(Point(0, 0), 1, -1)); + reference.push_back(Vertex45(Point(10, 0), 0, -1)); + reference.push_back(Vertex45(Point(10, 0), 1, 1)); + reference.push_back(Vertex45(Point(10, 10), 1, 1)); + reference.push_back(Vertex45(Point(10, 10), 0, -1)); + reference.push_back(Vertex45(Point(20, 10), 1, -1)); + reference.push_back(Vertex45(Point(20, 10), 0, 1)); + if(result != reference) { + stdcout << "result size == " << result.size() << "\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + //stdcout << "result == " << result[i]<< "\n"; + } + stdcout << "reference size == " << reference.size() << "\n"; + for(std::size_t i = 0; i < reference.size(); ++i) { + //stdcout << "reference == " << reference[i]<< "\n"; + } + return false; + } + stdcout << "done testing Scan45P2\n"; + return true; + } + + template + static inline bool testScan45And(streamtype& stdcout) { + stdcout << "testing Scan45And\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,0), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + vertices.push_back(Scan45Vertex(Point(0,10), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(10,0), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(10,10), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + count = Count2(0, 1); + ncount = count.invert(); + vertices.push_back(Scan45Vertex(Point(2,2), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + vertices.push_back(Scan45Vertex(Point(2,12), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(12,2), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(12,12), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + sortScan45Vector(vertices); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + //result size == 8 + //result == 2 2 0 1 + //result == 2 2 2 1 + //result == 2 10 2 -1 + //result == 2 10 0 -1 + //result == 10 2 0 -1 + //result == 10 2 2 -1 + //result == 10 10 2 1 + //result == 10 10 0 1 + std::vector reference; + reference.push_back(Vertex45(Point(2, 2), 0, 1)); + reference.push_back(Vertex45(Point(2, 2), 2, 1)); + reference.push_back(Vertex45(Point(2, 10), 2, -1)); + reference.push_back(Vertex45(Point(2, 10), 0, -1)); + reference.push_back(Vertex45(Point(10, 2), 0, -1)); + reference.push_back(Vertex45(Point(10, 2), 2, -1)); + reference.push_back(Vertex45(Point(10, 10), 2, 1)); + reference.push_back(Vertex45(Point(10, 10), 0, 1)); + if(result != reference) { + stdcout << "result size == " << result.size() << "\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + //stdcout << "result == " << result[i]<< "\n"; + } + stdcout << "reference size == " << reference.size() << "\n"; + for(std::size_t i = 0; i < reference.size(); ++i) { + //stdcout << "reference == " << reference[i]<< "\n"; + } + return false; + } + stdcout << "done testing Scan45And\n"; + return true; + } + + template + static inline bool testScan45Star1(stream_type& stdcout) { + stdcout << "testing Scan45Star1\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,8), Scan45Count(count, Count2(0, 0), ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,0), Scan45Count(ncount, Count2(0, 0), Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(8,16), Scan45Count(Count2(0, 0), Count2(0, 0), count, count))); + count = Count2(0, 1); + ncount = count.invert(); + vertices.push_back(Scan45Vertex(Point(12,8), Scan45Count(count, Count2(0, 0), ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(4,0), Scan45Count(Count2(0, 0), Count2(0, 0), count, count))); + vertices.push_back(Scan45Vertex(Point(4,16), Scan45Count(ncount, Count2(0, 0), Count2(0, 0), ncount))); + sortScan45Vector(vertices); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + // result size == 24 + // result == 0 8 -1 1 + // result == 0 8 1 -1 + // result == 4 0 1 1 + // result == 4 0 2 1 + // result == 4 4 2 -1 + // result == 4 4 -1 -1 + // result == 4 12 1 1 + // result == 4 12 2 1 + // result == 4 16 2 -1 + // result == 4 16 -1 -1 + // result == 6 2 1 -1 + // result == 6 14 -1 1 + // result == 6 2 -1 1 + // result == 6 14 1 -1 + // result == 8 0 -1 -1 + // result == 8 0 2 -1 + // result == 8 4 2 1 + // result == 8 4 1 1 + // result == 8 12 -1 -1 + // result == 8 12 2 -1 + // result == 8 16 2 1 + // result == 8 16 1 1 + // result == 12 8 1 -1 + // result == 12 8 -1 1 + if(result.size() != 24) { + //stdcout << "result size == " << result.size() << "\n"; + //stdcout << "reference size == " << 24 << "\n"; + return false; + } + stdcout << "done testing Scan45Star1\n"; + return true; + } + + template + static inline bool testScan45Star2(stream_type& stdcout) { + stdcout << "testing Scan45Star2\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,4), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,4), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,12), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + count = Count2(0, 1); + ncount = count.invert(); + vertices.push_back(Scan45Vertex(Point(0,8), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,8), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,0), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + sortScan45Vector(vertices); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + // result size == 24 + // result == 0 4 0 1 + // result == 0 4 1 -1 + // result == 0 8 -1 1 + // result == 0 8 0 -1 + // result == 2 6 1 1 + // result == 2 6 -1 -1 + // result == 4 4 0 -1 + // result == 4 8 0 1 + // result == 4 4 -1 1 + // result == 4 8 1 -1 + // result == 8 0 -1 -1 + // result == 8 0 1 1 + // result == 8 12 1 1 + // result == 8 12 -1 -1 + // result == 12 4 1 -1 + // result == 12 8 -1 1 + // result == 12 4 0 1 + // result == 12 8 0 -1 + // result == 14 6 -1 -1 + // result == 14 6 1 1 + // result == 16 4 0 -1 + // result == 16 4 -1 1 + // result == 16 8 1 -1 + // result == 16 8 0 1 + if(result.size() != 24) { + //std::cout << "result size == " << result.size() << "\n"; + //std::cout << "reference size == " << 24 << "\n"; + return false; + } + stdcout << "done testing Scan45Star2\n"; + return true; + } + + template + static inline bool testScan45Star3(stream_type& stdcout) { + stdcout << "testing Scan45Star3\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,8), Scan45Count(count, Count2(0, 0), ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,0), Scan45Count(ncount, Count2(0, 0), Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(8,16), Scan45Count(Count2(0, 0), Count2(0, 0), count, count))); + + vertices.push_back(Scan45Vertex(Point(6,0), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + vertices.push_back(Scan45Vertex(Point(6,14), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(12,0), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(12,14), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + count = Count2(0, 1); + ncount = count.invert(); + vertices.push_back(Scan45Vertex(Point(12,8), Scan45Count(count, Count2(0, 0), ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(4,0), Scan45Count(Count2(0, 0), Count2(0, 0), count, count))); + vertices.push_back(Scan45Vertex(Point(4,16), Scan45Count(ncount, Count2(0, 0), Count2(0, 0), ncount))); + sortScan45Vector(vertices); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + // result size == 28 + // result == 0 8 -1 1 + // result == 0 8 1 -1 + // result == 4 0 1 1 + // result == 4 0 2 1 + // result == 4 4 2 -1 + // result == 4 4 -1 -1 + // result == 4 12 1 1 + // result == 4 12 2 1 + // result == 4 16 2 -1 + // result == 4 16 -1 -1 + // result == 6 2 1 -1 + // result == 6 14 -1 1 + // result == 6 0 0 1 + // result == 6 0 2 1 + // result == 6 2 2 -1 + // result == 6 14 1 -1 + // result == 8 0 0 -1 + // result == 8 0 0 1 + // result == 8 14 0 -1 + // result == 8 14 2 -1 + // result == 8 16 2 1 + // result == 8 16 1 1 + // result == 12 0 0 -1 + // result == 12 0 2 -1 + // result == 12 8 2 1 + // result == 12 8 2 -1 + // result == 12 14 2 1 + // result == 12 14 0 1 + if(result.size() != 28) { + //std::cout << "result size == " << result.size() << "\n"; + //std::cout << "reference size == " << 28 << "\n"; + return false; + } + + stdcout << "done testing Scan45Star3\n"; + return true; + } + + + template + static inline bool testScan45Star4(stream_type& stdcout) { + stdcout << "testing Scan45Star4\n"; + Scan45 > scan45; + std::vector result; + typedef std::pair Scan45Vertex; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,4), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,4), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,12), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + + vertices.push_back(Scan45Vertex(Point(0,6), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + vertices.push_back(Scan45Vertex(Point(0,12), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(16,6), Scan45Count(Count2(0, 0), ncount, Count2(0, 0), ncount))); + vertices.push_back(Scan45Vertex(Point(16,12), Scan45Count(Count2(0, 0), count, Count2(0, 0), count))); + count = Count2(0, 1); + ncount = count.invert(); + vertices.push_back(Scan45Vertex(Point(0,8), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,8), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,0), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + sortScan45Vector(vertices); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + stdcout << "done scanning\n"; + // result size == 28 + // result == 0 4 0 1 + // result == 0 4 1 -1 + // result == 0 6 0 1 + // result == 0 6 2 1 + // result == 0 8 2 -1 + // result == 0 8 2 1 + // result == 0 12 2 -1 + // result == 0 12 0 -1 + // result == 2 6 1 1 + // result == 2 6 0 -1 + // result == 4 4 0 -1 + // result == 4 4 -1 1 + // result == 8 12 0 1 + // result == 8 0 -1 -1 + // result == 8 0 1 1 + // result == 8 12 0 -1 + // result == 12 4 1 -1 + // result == 12 4 0 1 + // result == 14 6 -1 -1 + // result == 14 6 0 1 + // result == 16 4 0 -1 + // result == 16 4 -1 1 + // result == 16 6 0 -1 + // result == 16 6 2 -1 + // result == 16 8 2 1 + // result == 16 8 2 -1 + // result == 16 12 2 1 + // result == 16 12 0 1 + if(result.size() != 28) { + //stdcout << "result size == " << result.size() << "\n"; + //stdcout << "reference size == " << 28 << "\n"; + return false; + } + + stdcout << "done testing Scan45Star4\n"; + return true; + } + + template + static inline bool testScan45(stream_type& stdcout) { + if(!testScan45Rect(stdcout)) return false; + if(!testScan45P1(stdcout)) return false; + if(!testScan45P2(stdcout)) return false; + if(!testScan45And(stdcout)) return false; + if(!testScan45Star1(stdcout)) return false; + if(!testScan45Star2(stdcout)) return false; + if(!testScan45Star3(stdcout)) return false; + if(!testScan45Star4(stdcout)) return false; + return true; + } + + }; + +} + +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_compact_to_points.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_compact_to_points.hpp new file mode 100644 index 0000000..e0f61d3 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_compact_to_points.hpp @@ -0,0 +1,74 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_ITERATOR_COMPACT_TO_POINTS_HPP +#define BOOST_POLYGON_ITERATOR_COMPACT_TO_POINTS_HPP +namespace boost { namespace polygon{ +template +class iterator_compact_to_points { +private: + iterator_type iter_; + iterator_type iter_end_; + point_type pt_; + typename point_traits::coordinate_type firstX_; + orientation_2d orient_; +public: + typedef std::forward_iterator_tag iterator_category; + typedef point_type value_type; + typedef std::ptrdiff_t difference_type; + typedef const point_type* pointer; //immutable + typedef const point_type& reference; //immutable + + inline iterator_compact_to_points() : iter_(), iter_end_(), pt_(), firstX_(), orient_() {} + inline iterator_compact_to_points(iterator_type iter, iterator_type iter_end) : + iter_(iter), iter_end_(iter_end), pt_(), firstX_(), orient_(HORIZONTAL) { + if(iter_ != iter_end_) { + firstX_ = *iter_; + x(pt_, firstX_); + ++iter_; + if(iter_ != iter_end_) { + y(pt_, *iter_); + } + } + } + //use bitwise copy and assign provided by the compiler + inline iterator_compact_to_points& operator++() { + iterator_type prev_iter = iter_; + ++iter_; + if(iter_ == iter_end_) { + if(x(pt_) != firstX_) { + iter_ = prev_iter; + x(pt_, firstX_); + } + } else { + set(pt_, orient_, *iter_); + orient_.turn_90(); + } + return *this; + } + inline const iterator_compact_to_points operator++(int) { + iterator_compact_to_points tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator_compact_to_points& that) const { + if (iter_ == iter_end_) { + return iter_ == that.iter_; + } + return (iter_ == that.iter_) && (x(pt_) == x(that.pt_)); + } + inline bool operator!=(const iterator_compact_to_points& that) const { + if (iter_ == iter_end_) { + return iter_ != that.iter_; + } + return (iter_ != that.iter_) || (x(pt_) != x(that.pt_)); + } + inline reference operator*() const { return pt_; } +}; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_geometry_to_set.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_geometry_to_set.hpp new file mode 100644 index 0000000..6ad371b --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_geometry_to_set.hpp @@ -0,0 +1,314 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_ITERATOR_GEOMETRY_TO_SET_HPP +#define BOOST_POLYGON_ITERATOR_GEOMETRY_TO_SET_HPP +namespace boost { namespace polygon{ +template +class iterator_geometry_to_set {}; + +template +class iterator_geometry_to_set { +public: + typedef typename rectangle_traits::coordinate_type coordinate_type; + typedef std::forward_iterator_tag iterator_category; + typedef std::pair > value_type; + typedef std::ptrdiff_t difference_type; + typedef const value_type* pointer; //immutable + typedef const value_type& reference; //immutable +private: + rectangle_data rectangle_; + mutable value_type vertex_; + unsigned int corner_; + orientation_2d orient_; + bool is_hole_; +public: + iterator_geometry_to_set() : rectangle_(), vertex_(), corner_(4), orient_(), is_hole_() {} + iterator_geometry_to_set(const rectangle_type& rectangle, direction_1d dir, + orientation_2d orient = HORIZONTAL, bool is_hole = false, bool = false, direction_1d = CLOCKWISE) : + rectangle_(), vertex_(), corner_(0), orient_(orient), is_hole_(is_hole) { + assign(rectangle_, rectangle); + if(dir == HIGH) corner_ = 4; + } + inline iterator_geometry_to_set& operator++() { + ++corner_; + return *this; + } + inline const iterator_geometry_to_set operator++(int) { + iterator_geometry_to_set tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator_geometry_to_set& that) const { + return corner_ == that.corner_; + } + inline bool operator!=(const iterator_geometry_to_set& that) const { + return !(*this == that); + } + inline reference operator*() const { + if(corner_ == 0) { + vertex_.first = get(get(rectangle_, orient_.get_perpendicular()), LOW); + vertex_.second.first = get(get(rectangle_, orient_), LOW); + vertex_.second.second = 1; + if(is_hole_) vertex_.second.second *= -1; + } else if(corner_ == 1) { + vertex_.second.first = get(get(rectangle_, orient_), HIGH); + vertex_.second.second = -1; + if(is_hole_) vertex_.second.second *= -1; + } else if(corner_ == 2) { + vertex_.first = get(get(rectangle_, orient_.get_perpendicular()), HIGH); + vertex_.second.first = get(get(rectangle_, orient_), LOW); + } else { + vertex_.second.first = get(get(rectangle_, orient_), HIGH); + vertex_.second.second = 1; + if(is_hole_) vertex_.second.second *= -1; + } + return vertex_; + } +}; + +template +class iterator_geometry_to_set { +public: + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef std::forward_iterator_tag iterator_category; + typedef std::pair > value_type; + typedef std::ptrdiff_t difference_type; + typedef const value_type* pointer; //immutable + typedef const value_type& reference; //immutable + typedef typename polygon_traits::iterator_type coord_iterator_type; +private: + value_type vertex_; + typename polygon_traits::iterator_type itrb, itre; + bool last_vertex_; + bool is_hole_; + int multiplier_; + point_data first_pt, second_pt, pts[3]; + bool use_wrap; + orientation_2d orient_; + int polygon_index; +public: + iterator_geometry_to_set() : vertex_(), itrb(), itre(), last_vertex_(), is_hole_(), multiplier_(), first_pt(), second_pt(), pts(), use_wrap(), orient_(), polygon_index(-1) {} + iterator_geometry_to_set(const polygon_type& polygon, direction_1d dir, orientation_2d orient = HORIZONTAL, bool is_hole = false, bool winding_override = false, direction_1d w = CLOCKWISE) : + vertex_(), itrb(), itre(), last_vertex_(), + is_hole_(is_hole), multiplier_(), first_pt(), second_pt(), pts(), use_wrap(), + orient_(orient), polygon_index(0) { + itrb = begin_points(polygon); + itre = end_points(polygon); + use_wrap = false; + if(itrb == itre || dir == HIGH || ::boost::polygon::size(polygon) < 4) { + polygon_index = -1; + } else { + direction_1d wdir = w; + if(!winding_override) + wdir = winding(polygon); + multiplier_ = wdir == LOW ? -1 : 1; + if(is_hole_) multiplier_ *= -1; + first_pt = pts[0] = *itrb; + ++itrb; + second_pt = pts[1] = *itrb; + ++itrb; + pts[2] = *itrb; + evaluate_(); + } + } + iterator_geometry_to_set(const iterator_geometry_to_set& that) : + vertex_(), itrb(), itre(), last_vertex_(), is_hole_(), multiplier_(), first_pt(), + second_pt(), pts(), use_wrap(), orient_(), polygon_index(-1) { + vertex_ = that.vertex_; + itrb = that.itrb; + itre = that.itre; + last_vertex_ = that.last_vertex_; + is_hole_ = that.is_hole_; + multiplier_ = that.multiplier_; + first_pt = that.first_pt; + second_pt = that.second_pt; + pts[0] = that.pts[0]; + pts[1] = that.pts[1]; + pts[2] = that.pts[2]; + use_wrap = that.use_wrap; + orient_ = that.orient_; + polygon_index = that.polygon_index; + } + inline iterator_geometry_to_set& operator++() { + ++polygon_index; + if(itrb == itre) { + if(first_pt == pts[1]) polygon_index = -1; + else { + pts[0] = pts[1]; + pts[1] = pts[2]; + if(first_pt == pts[2]) { + pts[2] = second_pt; + } else { + pts[2] = first_pt; + } + } + } else { + ++itrb; + pts[0] = pts[1]; + pts[1] = pts[2]; + if(itrb == itre) { + if(first_pt == pts[2]) { + pts[2] = second_pt; + } else { + pts[2] = first_pt; + } + } else { + pts[2] = *itrb; + } + } + evaluate_(); + return *this; + } + inline const iterator_geometry_to_set operator++(int) { + iterator_geometry_to_set tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator_geometry_to_set& that) const { + return polygon_index == that.polygon_index; + } + inline bool operator!=(const iterator_geometry_to_set& that) const { + return !(*this == that); + } + inline reference operator*() const { + return vertex_; + } + + inline void evaluate_() { + vertex_.first = pts[1].get(orient_.get_perpendicular()); + vertex_.second.first =pts[1].get(orient_); + if(pts[1] == pts[2]) { + vertex_.second.second = 0; + } else if(pts[0].get(HORIZONTAL) != pts[1].get(HORIZONTAL)) { + vertex_.second.second = -1; + } else if(pts[0].get(VERTICAL) != pts[1].get(VERTICAL)) { + vertex_.second.second = 1; + } else { + vertex_.second.second = 0; + } + vertex_.second.second *= multiplier_; + } +}; + +template +class iterator_geometry_to_set { +public: + typedef typename polygon_90_traits::coordinate_type coordinate_type; + typedef std::forward_iterator_tag iterator_category; + typedef std::pair > value_type; + typedef std::ptrdiff_t difference_type; + typedef const value_type* pointer; //immutable + typedef const value_type& reference; //immutable +private: + iterator_geometry_to_set itrb, itre; + iterator_geometry_to_set::hole_type> itrhib, itrhie; + typename polygon_with_holes_traits::iterator_holes_type itrhb, itrhe; + orientation_2d orient_; + bool is_hole_; + bool started_holes; +public: + iterator_geometry_to_set() : itrb(), itre(), itrhib(), itrhie(), itrhb(), itrhe(), orient_(), is_hole_(), started_holes() {} + iterator_geometry_to_set(const polygon_with_holes_type& polygon, direction_1d dir, + orientation_2d orient = HORIZONTAL, bool is_hole = false, bool = false, direction_1d = CLOCKWISE) : + itrb(), itre(), itrhib(), itrhie(), itrhb(), itrhe(), orient_(orient), is_hole_(is_hole), started_holes() { + itre = iterator_geometry_to_set(polygon, HIGH, orient, is_hole_); + itrhe = end_holes(polygon); + if(dir == HIGH) { + itrb = itre; + itrhb = itrhe; + started_holes = true; + } else { + itrb = iterator_geometry_to_set(polygon, LOW, orient, is_hole_); + itrhb = begin_holes(polygon); + started_holes = false; + } + } + iterator_geometry_to_set(const iterator_geometry_to_set& that) : + itrb(), itre(), itrhib(), itrhie(), itrhb(), itrhe(), orient_(), is_hole_(), started_holes() { + itrb = that.itrb; + itre = that.itre; + if(that.itrhib != that.itrhie) { + itrhib = that.itrhib; + itrhie = that.itrhie; + } + itrhb = that.itrhb; + itrhe = that.itrhe; + orient_ = that.orient_; + is_hole_ = that.is_hole_; + started_holes = that.started_holes; + } + inline iterator_geometry_to_set& operator++() { + //this code can be folded with flow control factoring + if(itrb == itre) { + if(itrhib == itrhie) { + if(itrhb != itrhe) { + itrhib = iterator_geometry_to_set::hole_type>(*itrhb, LOW, orient_, !is_hole_); + itrhie = iterator_geometry_to_set::hole_type>(*itrhb, HIGH, orient_, !is_hole_); + ++itrhb; + } else { + //in this case we have no holes so we just need the iterhib == itrhie, which + //is always true if they were default initialized in the initial case or + //both point to end of the previous hole processed + //no need to explicitly reset them, and it causes an stl debug assertion to use + //the default constructed iterator this way + //itrhib = itrhie = iterator_geometry_to_set::hole_type>(); + } + } else { + ++itrhib; + if(itrhib == itrhie) { + if(itrhb != itrhe) { + itrhib = iterator_geometry_to_set::hole_type>(*itrhb, LOW, orient_, !is_hole_); + itrhie = iterator_geometry_to_set::hole_type>(*itrhb, HIGH, orient_, !is_hole_); + ++itrhb; + } else { + //this is the same case as above + //itrhib = itrhie = iterator_geometry_to_set::hole_type>(); + } + } + } + } else { + ++itrb; + if(itrb == itre) { + if(itrhb != itrhe) { + itrhib = iterator_geometry_to_set::hole_type>(*itrhb, LOW, orient_, !is_hole_); + itrhie = iterator_geometry_to_set::hole_type>(*itrhb, HIGH, orient_, !is_hole_); + ++itrhb; + } + } + } + return *this; + } + inline const iterator_geometry_to_set operator++(int) { + iterator_geometry_to_set tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator_geometry_to_set& that) const { + return itrb == that.itrb && itrhb == that.itrhb && itrhib == that.itrhib; + } + inline bool operator!=(const iterator_geometry_to_set& that) const { + return !(*this == that); + } + inline reference operator*() const { + if(itrb != itre) return *itrb; + return *itrhib; + } +}; + + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_points_to_compact.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_points_to_compact.hpp new file mode 100644 index 0000000..25ddb15 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/iterator_points_to_compact.hpp @@ -0,0 +1,60 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_ITERATOR_POINTS_TO_COMPACT_HPP +#define BOOST_POLYGON_ITERATOR_POINTS_TO_COMPACT_HPP +namespace boost { namespace polygon{ +template +class iterator_points_to_compact { +private: + iT iter_, iterEnd_; + orientation_2d orient_; + mutable typename point_traits::coordinate_type coord_; +public: + typedef typename point_traits::coordinate_type coordinate_type; + typedef std::forward_iterator_tag iterator_category; + typedef coordinate_type value_type; + typedef std::ptrdiff_t difference_type; + typedef const coordinate_type* pointer; //immutable + typedef const coordinate_type& reference; //immutable + + inline iterator_points_to_compact() : iter_(), iterEnd_(), orient_(), coord_() {} + inline iterator_points_to_compact(iT iter, iT iterEnd) : + iter_(iter), iterEnd_(iterEnd), orient_(HORIZONTAL), coord_() {} + inline iterator_points_to_compact(const iterator_points_to_compact& that) : + iter_(that.iter_), iterEnd_(that.iterEnd_), orient_(that.orient_), coord_(that.coord_) {} + //use bitwise copy and assign provided by the compiler + inline iterator_points_to_compact& operator++() { + //iT tmp = iter_; + ++iter_; + //iT tmp2 = iter_; + orient_.turn_90(); + //while(tmp2 != iterEnd_ && get(*tmp2, orient_) == get(*tmp, orient_)) { + // iter_ = tmp2; + // ++tmp2; + //} + return *this; + } + inline const iterator_points_to_compact operator++(int) { + iT tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator_points_to_compact& that) const { + return (iter_ == that.iter_); + } + inline bool operator!=(const iterator_points_to_compact& that) const { + return (iter_ != that.iter_); + } + inline reference operator*() const { coord_ = get(*iter_, orient_); + return coord_; + } +}; +} +} +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/max_cover.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/max_cover.hpp new file mode 100644 index 0000000..f01eac8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/max_cover.hpp @@ -0,0 +1,281 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_MAX_COVER_HPP +#define BOOST_POLYGON_MAX_COVER_HPP +namespace boost { namespace polygon{ + + template + struct MaxCover { + typedef interval_data Interval; + typedef rectangle_data Rectangle; + + class Node { + private: + std::vector children_; + std::set tracedPaths_; + public: + Rectangle rect; + Node() : children_(), tracedPaths_(), rect() {} + Node(const Rectangle rectIn) : children_(), tracedPaths_(), rect(rectIn) {} + typedef typename std::vector::iterator iterator; + inline iterator begin() { return children_.begin(); } + inline iterator end() { return children_.end(); } + inline void add(Node* child) { children_.push_back(child); } + inline bool tracedPath(const Interval& ivl) const { + return tracedPaths_.find(ivl) != tracedPaths_.end(); + } + inline void addPath(const Interval& ivl) { + tracedPaths_.insert(tracedPaths_.end(), ivl); + } + }; + + typedef std::pair, Node* > EdgeAssociation; + + class lessEdgeAssociation { + public: + typedef const EdgeAssociation& first_argument_type; + typedef const EdgeAssociation& second_argument_type; + typedef bool result_type; + inline lessEdgeAssociation() {} + inline bool operator () (const EdgeAssociation& elem1, const EdgeAssociation& elem2) const { + if(elem1.first.first < elem2.first.first) return true; + if(elem1.first.first > elem2.first.first) return false; + return elem1.first.second < elem2.first.second; + } + }; + + template + static inline void getMaxCover(cT& outputContainer, Node* node, orientation_2d orient) { + Interval rectIvl = node->rect.get(orient); + if(node->tracedPath(rectIvl)) { + return; + } + node->addPath(rectIvl); + if(node->begin() == node->end()) { + //std::cout << "WRITE OUT 3: " << node->rect << std::endl; + outputContainer.push_back(copy_construct(node->rect)); + return; + } + bool writeOut = true; + for(typename Node::iterator itr = node->begin(); itr != node->end(); ++itr) { + getMaxCover(outputContainer, *itr, orient, node->rect); //get rectangles down path + Interval nodeIvl = (*itr)->rect.get(orient); + if(contains(nodeIvl, rectIvl, true)) writeOut = false; + } + if(writeOut) { + //std::cout << "WRITE OUT 2: " << node->rect << std::endl; + outputContainer.push_back(copy_construct(node->rect)); + } + } + + struct stack_element { + inline stack_element() : + node(), rect(), itr() {} + inline stack_element(Node* n, + const Rectangle& r, + typename Node::iterator i) : + node(n), rect(r), itr(i) {} + Node* node; + Rectangle rect; + typename Node::iterator itr; + }; + + template + static inline void getMaxCover(cT& outputContainer, Node* node, orientation_2d orient, + Rectangle rect) { + //std::cout << "New Root\n"; + std::vector stack; + typename Node::iterator itr = node->begin(); + do { + //std::cout << "LOOP\n"; + //std::cout << node->rect << std::endl; + Interval rectIvl = rect.get(orient); + Interval nodeIvl = node->rect.get(orient); + bool iresult = intersect(rectIvl, nodeIvl, false); + bool tresult = !node->tracedPath(rectIvl); + //std::cout << (itr != node->end()) << " " << iresult << " " << tresult << std::endl; + Rectangle nextRect1 = Rectangle(rectIvl, rectIvl); + Unit low = rect.get(orient.get_perpendicular()).low(); + Unit high = node->rect.get(orient.get_perpendicular()).high(); + nextRect1.set(orient.get_perpendicular(), Interval(low, high)); + if(iresult && tresult) { + node->addPath(rectIvl); + bool writeOut = true; + //check further visibility beyond this node + for(typename Node::iterator itr2 = node->begin(); itr2 != node->end(); ++itr2) { + Interval nodeIvl3 = (*itr2)->rect.get(orient); + //if a child of this node can contain the interval then we can extend through + if(contains(nodeIvl3, rectIvl, true)) writeOut = false; + //std::cout << "child " << (*itr2)->rect << std::endl; + } + Rectangle nextRect2 = Rectangle(rectIvl, rectIvl); + Unit low2 = rect.get(orient.get_perpendicular()).low(); + Unit high2 = node->rect.get(orient.get_perpendicular()).high(); + nextRect2.set(orient.get_perpendicular(), Interval(low2, high2)); + if(writeOut) { + //std::cout << "write out " << nextRect << std::endl; + outputContainer.push_back(copy_construct(nextRect2)); + } else { + //std::cout << "suppress " << nextRect << std::endl; + } + } + if(itr != node->end() && iresult && tresult) { + //std::cout << "recurse into child\n"; + stack.push_back(stack_element(node, rect, itr)); + rect = nextRect1; + node = *itr; + itr = node->begin(); + } else { + if(!stack.empty()) { + //std::cout << "recurse out of child\n"; + node = stack.back().node; + rect = stack.back().rect; + itr = stack.back().itr; + stack.pop_back(); + } else { + //std::cout << "empty stack\n"; + //if there were no children of the root node +// Rectangle nextRect = Rectangle(rectIvl, rectIvl); +// Unit low = rect.get(orient.get_perpendicular()).low(); +// Unit high = node->rect.get(orient.get_perpendicular()).high(); +// nextRect.set(orient.get_perpendicular(), Interval(low, high)); +// outputContainer.push_back(copy_construct(nextRect)); + } + //std::cout << "increment " << (itr != node->end()) << std::endl; + if(itr != node->end()) { + ++itr; + if(itr != node->end()) { + //std::cout << "recurse into next child.\n"; + stack.push_back(stack_element(node, rect, itr)); + Interval rectIvl2 = rect.get(orient); + Interval nodeIvl2 = node->rect.get(orient); + /*bool iresult =*/ intersect(rectIvl2, nodeIvl2, false); + Rectangle nextRect2 = Rectangle(rectIvl2, rectIvl2); + Unit low2 = rect.get(orient.get_perpendicular()).low(); + Unit high2 = node->rect.get(orient.get_perpendicular()).high(); + nextRect2.set(orient.get_perpendicular(), Interval(low2, high2)); + rect = nextRect2; + //std::cout << "rect for next child" << rect << std::endl; + node = *itr; + itr = node->begin(); + } + } + } + } while(!stack.empty() || itr != node->end()); + } + + /* Function recursive version of getMaxCover + Because the code is so much simpler than the loop algorithm I retain it for clarity + + template + static inline void getMaxCover(cT& outputContainer, Node* node, orientation_2d orient, + const Rectangle& rect) { + Interval rectIvl = rect.get(orient); + Interval nodeIvl = node->rect.get(orient); + if(!intersect(rectIvl, nodeIvl, false)) { + return; + } + if(node->tracedPath(rectIvl)) { + return; + } + node->addPath(rectIvl); + Rectangle nextRect(rectIvl, rectIvl); + Unit low = rect.get(orient.get_perpendicular()).low(); + Unit high = node->rect.get(orient.get_perpendicular()).high(); + nextRect.set(orient.get_perpendicular(), Interval(low, high)); + bool writeOut = true; + rectIvl = nextRect.get(orient); + for(typename Node::iterator itr = node->begin(); itr != node->end(); ++itr) { + nodeIvl = (*itr)->rect.get(orient); + if(contains(nodeIvl, rectIvl, true)) writeOut = false; + } + if(writeOut) { + outputContainer.push_back(copy_construct(nextRect)); + } + for(typename Node::iterator itr = node->begin(); itr != node->end(); ++itr) { + getMaxCover(outputContainer, *itr, orient, nextRect); + } + } + */ + + //iterator range is assummed to be in topological order meaning all node's trailing + //edges are in sorted order + template + static inline void computeDag(iT beginNode, iT endNode, orientation_2d orient, + std::size_t size) { + std::vector leadingEdges; + leadingEdges.reserve(size); + for(iT iter = beginNode; iter != endNode; ++iter) { + Node* nodep = &(*iter); + Unit leading = nodep->rect.get(orient.get_perpendicular()).low(); + Interval rectIvl = nodep->rect.get(orient); + leadingEdges.push_back(EdgeAssociation(std::pair(leading, rectIvl), nodep)); + } + polygon_sort(leadingEdges.begin(), leadingEdges.end(), lessEdgeAssociation()); + typename std::vector::iterator leadingBegin = leadingEdges.begin(); + iT trailingBegin = beginNode; + while(leadingBegin != leadingEdges.end()) { + EdgeAssociation& leadingSegment = (*leadingBegin); + Unit trailing = (*trailingBegin).rect.get(orient.get_perpendicular()).high(); + Interval ivl = (*trailingBegin).rect.get(orient); + std::pair trailingSegment(trailing, ivl); + if(leadingSegment.first.first < trailingSegment.first) { + ++leadingBegin; + continue; + } + if(leadingSegment.first.first > trailingSegment.first) { + ++trailingBegin; + continue; + } + if(leadingSegment.first.second.high() <= trailingSegment.second.low()) { + ++leadingBegin; + continue; + } + if(trailingSegment.second.high() <= leadingSegment.first.second.low()) { + ++trailingBegin; + continue; + } + //leading segment intersects trailing segment + (*trailingBegin).add((*leadingBegin).second); + if(leadingSegment.first.second.high() > trailingSegment.second.high()) { + ++trailingBegin; + continue; + } + if(trailingSegment.second.high() > leadingSegment.first.second.high()) { + ++leadingBegin; + continue; + } + ++leadingBegin; + ++trailingBegin; + } + } + + template + static inline void getMaxCover(cT& outputContainer, + const std::vector& rects, orientation_2d orient) { + if(rects.empty()) return; + std::vector nodes; + { + if(rects.size() == 1) { + outputContainer.push_back(copy_construct(rects[0])); + return; + } + nodes.reserve(rects.size()); + for(std::size_t i = 0; i < rects.size(); ++i) { nodes.push_back(Node(rects[i])); } + } + computeDag(nodes.begin(), nodes.end(), orient, nodes.size()); + for(std::size_t i = 0; i < nodes.size(); ++i) { + getMaxCover(outputContainer, &(nodes[i]), orient); + } + } + + }; +} +} + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/minkowski.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/minkowski.hpp new file mode 100644 index 0000000..ce34947 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/minkowski.hpp @@ -0,0 +1,131 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +namespace boost { namespace polygon { namespace detail { + +template +struct minkowski_offset { + typedef point_data point; + typedef polygon_set_data polygon_set; + typedef polygon_with_holes_data polygon; + typedef std::pair edge; + + static void convolve_two_segments(std::vector& figure, const edge& a, const edge& b) { + figure.clear(); + figure.push_back(point(a.first)); + figure.push_back(point(a.first)); + figure.push_back(point(a.second)); + figure.push_back(point(a.second)); + convolve(figure[0], b.second); + convolve(figure[1], b.first); + convolve(figure[2], b.first); + convolve(figure[3], b.second); + } + + template + static void convolve_two_point_sequences(polygon_set& result, itrT1 ab, itrT1 ae, itrT2 bb, itrT2 be) { + if(ab == ae || bb == be) + return; + point first_a = *ab; + point prev_a = *ab; + std::vector vec; + polygon poly; + ++ab; + for( ; ab != ae; ++ab) { + point first_b = *bb; + point prev_b = *bb; + itrT2 tmpb = bb; + ++tmpb; + for( ; tmpb != be; ++tmpb) { + convolve_two_segments(vec, std::make_pair(prev_b, *tmpb), std::make_pair(prev_a, *ab)); + set_points(poly, vec.begin(), vec.end()); + result.insert(poly); + prev_b = *tmpb; + } + prev_a = *ab; + } + } + + template + static void convolve_point_sequence_with_polygons(polygon_set& result, itrT b, itrT e, const std::vector& polygons) { + for(std::size_t i = 0; i < polygons.size(); ++i) { + convolve_two_point_sequences(result, b, e, begin_points(polygons[i]), end_points(polygons[i])); + for(typename polygon_with_holes_traits::iterator_holes_type itrh = begin_holes(polygons[i]); + itrh != end_holes(polygons[i]); ++itrh) { + convolve_two_point_sequences(result, b, e, begin_points(*itrh), end_points(*itrh)); + } + } + } + + static void convolve_two_polygon_sets(polygon_set& result, const polygon_set& a, const polygon_set& b) { + result.clear(); + std::vector a_polygons; + std::vector b_polygons; + a.get(a_polygons); + b.get(b_polygons); + for(std::size_t ai = 0; ai < a_polygons.size(); ++ai) { + convolve_point_sequence_with_polygons(result, begin_points(a_polygons[ai]), + end_points(a_polygons[ai]), b_polygons); + for(typename polygon_with_holes_traits::iterator_holes_type itrh = begin_holes(a_polygons[ai]); + itrh != end_holes(a_polygons[ai]); ++itrh) { + convolve_point_sequence_with_polygons(result, begin_points(*itrh), + end_points(*itrh), b_polygons); + } + for(std::size_t bi = 0; bi < b_polygons.size(); ++bi) { + polygon tmp_poly = a_polygons[ai]; + result.insert(convolve(tmp_poly, *(begin_points(b_polygons[bi])))); + tmp_poly = b_polygons[bi]; + result.insert(convolve(tmp_poly, *(begin_points(a_polygons[ai])))); + } + } + } +}; + +} + template + inline polygon_set_data& + polygon_set_data::resize(coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments) { + using namespace ::boost::polygon::operators; + if(!corner_fill_arc) { + if(resizing < 0) + return shrink(-resizing); + if(resizing > 0) + return bloat(resizing); + return *this; + } + if(resizing == 0) return *this; + if(empty()) return *this; + if(num_circle_segments < 3) num_circle_segments = 4; + rectangle_data rect; + extents(rect); + if(resizing < 0) { + ::boost::polygon::bloat(rect, 10); + (*this) = rect - (*this); //invert + } + //make_arc(std::vector >& return_points, + //point_data< double> start, point_data< double> end, + //point_data< double> center, double r, unsigned int num_circle_segments) + std::vector > circle; + point_data center(0.0, 0.0), start(0.0, (double)resizing); + make_arc(circle, start, start, center, std::abs((double)resizing), + num_circle_segments); + polygon_data poly; + set_points(poly, circle.begin(), circle.end()); + polygon_set_data offset_set; + offset_set += poly; + polygon_set_data result; + detail::minkowski_offset::convolve_two_polygon_sets + (result, *this, offset_set); + if(resizing < 0) { + result = result & rect;//eliminate overhang + result = result ^ rect;//invert + } + *this = result; + return *this; + } + +}} diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_formation.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_formation.hpp new file mode 100644 index 0000000..7034986 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_formation.hpp @@ -0,0 +1,2238 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_FORMATION_HPP +#define BOOST_POLYGON_POLYGON_45_FORMATION_HPP +namespace boost { namespace polygon{ + + template + struct PolyLineByConcept {}; + + template + class PolyLine45PolygonData; + template + class PolyLine45HoleData; + + //polygon45formation algorithm + template + struct polygon_45_formation : public boolean_op_45 { + typedef point_data Point; + typedef polygon_45_data Polygon45; + typedef polygon_45_with_holes_data Polygon45WithHoles; + typedef typename boolean_op_45::Vertex45 Vertex45; + typedef typename boolean_op_45::lessVertex45 lessVertex45; + typedef typename boolean_op_45::Count2 Count2; + typedef typename boolean_op_45::Scan45Count Scan45Count; + typedef std::pair Scan45Vertex; + typedef typename boolean_op_45::template + Scan45::template boolean_op_45_output_functor<0> > Scan45; + + class PolyLine45 { + public: + typedef typename std::list::const_iterator iterator; + + // default constructor of point does not initialize x and y + inline PolyLine45() : points() {} //do nothing default constructor + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline PolyLine45& set(iT inputBegin, iT inputEnd) { + points.clear(); //just in case there was some old data there + while(inputBegin != inputEnd) { + points.insert(points.end(), *inputBegin); + ++inputBegin; + } + return *this; + } + + // copy constructor (since we have dynamic memory) + inline PolyLine45(const PolyLine45& that) : points(that.points) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline PolyLine45& operator=(const PolyLine45& that) { + points = that.points; + return *this; + } + + // get begin iterator, returns a pointer to a const Unit + inline iterator begin() const { return points.begin(); } + + // get end iterator, returns a pointer to a const Unit + inline iterator end() const { return points.end(); } + + inline std::size_t size() const { return points.size(); } + + //public data member + std::list points; + }; + + class ActiveTail45 { + private: + //data + PolyLine45* tailp_; + ActiveTail45 *otherTailp_; + std::list holesList_; + bool head_; + public: + + /** + * @brief iterator over coordinates of the figure + */ + typedef typename PolyLine45::iterator iterator; + + /** + * @brief iterator over holes contained within the figure + */ + typedef typename std::list::const_iterator iteratorHoles; + + //default constructor + inline ActiveTail45() : tailp_(0), otherTailp_(0), holesList_(), head_(0) {} + + //constructor + inline ActiveTail45(const Vertex45& vertex, ActiveTail45* otherTailp = 0) : + tailp_(0), otherTailp_(0), holesList_(), head_(0) { + tailp_ = new PolyLine45; + tailp_->points.push_back(vertex.pt); + bool headArray[4] = {false, true, true, true}; + bool inverted = vertex.count == -1; + head_ = headArray[vertex.rise+1] ^ inverted; + otherTailp_ = otherTailp; + } + + inline ActiveTail45(Point point, ActiveTail45* otherTailp, bool head = true) : + tailp_(0), otherTailp_(0), holesList_(), head_(0) { + tailp_ = new PolyLine45; + tailp_->points.push_back(point); + head_ = head; + otherTailp_ = otherTailp; + + } + inline ActiveTail45(ActiveTail45* otherTailp) : + tailp_(0), otherTailp_(0), holesList_(), head_(0) { + tailp_ = otherTailp->tailp_; + otherTailp_ = otherTailp; + } + + //copy constructor + inline ActiveTail45(const ActiveTail45& that) : + tailp_(0), otherTailp_(0), holesList_(), head_(0) { (*this) = that; } + + //destructor + inline ~ActiveTail45() { + destroyContents(); + } + + //assignment operator + inline ActiveTail45& operator=(const ActiveTail45& that) { + tailp_ = new PolyLine45(*(that.tailp_)); + head_ = that.head_; + otherTailp_ = that.otherTailp_; + holesList_ = that.holesList_; + return *this; + } + + //equivalence operator + inline bool operator==(const ActiveTail45& b) const { + return tailp_ == b.tailp_ && head_ == b.head_; + } + + /** + * @brief get the pointer to the polyline that this is an active tail of + */ + inline PolyLine45* getTail() const { return tailp_; } + + /** + * @brief get the pointer to the polyline at the other end of the chain + */ + inline PolyLine45* getOtherTail() const { return otherTailp_->tailp_; } + + /** + * @brief get the pointer to the activetail at the other end of the chain + */ + inline ActiveTail45* getOtherActiveTail() const { return otherTailp_; } + + /** + * @brief test if another active tail is the other end of the chain + */ + inline bool isOtherTail(const ActiveTail45& b) const { return &b == otherTailp_; } + + /** + * @brief update this end of chain pointer to new polyline + */ + inline ActiveTail45& updateTail(PolyLine45* newTail) { tailp_ = newTail; return *this; } + + inline bool join(ActiveTail45* tail) { + if(tail == otherTailp_) { + //std::cout << "joining to other tail!\n"; + return false; + } + if(tail->head_ == head_) { + //std::cout << "joining head to head!\n"; + return false; + } + if(!tailp_) { + //std::cout << "joining empty tail!\n"; + return false; + } + if(!(otherTailp_->head_)) { + otherTailp_->copyHoles(*tail); + otherTailp_->copyHoles(*this); + } else { + tail->otherTailp_->copyHoles(*this); + tail->otherTailp_->copyHoles(*tail); + } + PolyLine45* tail1 = tailp_; + PolyLine45* tail2 = tail->tailp_; + if(head_) std::swap(tail1, tail2); + tail1->points.splice(tail1->points.end(), tail2->points); + delete tail2; + otherTailp_->tailp_ = tail1; + tail->otherTailp_->tailp_ = tail1; + otherTailp_->otherTailp_ = tail->otherTailp_; + tail->otherTailp_->otherTailp_ = otherTailp_; + tailp_ = 0; + tail->tailp_ = 0; + tail->otherTailp_ = 0; + otherTailp_ = 0; + return true; + } + + /** + * @brief associate a hole to this active tail by the specified policy + */ + inline ActiveTail45* addHole(ActiveTail45* hole) { + holesList_.push_back(hole); + copyHoles(*hole); + copyHoles(*(hole->otherTailp_)); + return this; + } + + /** + * @brief get the list of holes + */ + inline const std::list& getHoles() const { return holesList_; } + + /** + * @brief copy holes from that to this + */ + inline void copyHoles(ActiveTail45& that) { holesList_.splice(holesList_.end(), that.holesList_); } + + /** + * @brief find out if solid to right + */ + inline bool solidToRight() const { return !head_; } + inline bool solidToLeft() const { return head_; } + + /** + * @brief get vertex + */ + inline Point getPoint() const { + if(head_) return tailp_->points.front(); + return tailp_->points.back(); + } + + /** + * @brief add a coordinate to the polygon at this active tail end, properly handle degenerate edges by removing redundant coordinate + */ + inline void pushPoint(Point point) { + if(head_) { + //if(tailp_->points.size() < 2) { + // tailp_->points.push_front(point); + // return; + //} + typename std::list::iterator iter = tailp_->points.begin(); + if(iter == tailp_->points.end()) { + tailp_->points.push_front(point); + return; + } + Unit firstY = (*iter).y(); + Unit firstX = (*iter).x(); + ++iter; + if(iter == tailp_->points.end()) { + tailp_->points.push_front(point); + return; + } + if((iter->y() == point.y() && firstY == point.y()) || + (iter->x() == point.x() && firstX == point.x())){ + --iter; + *iter = point; + } else { + tailp_->points.push_front(point); + } + return; + } + //if(tailp_->points.size() < 2) { + // tailp_->points.push_back(point); + // return; + //} + typename std::list::reverse_iterator iter = tailp_->points.rbegin(); + if(iter == tailp_->points.rend()) { + tailp_->points.push_back(point); + return; + } + Unit firstY = (*iter).y(); + Unit firstX = (*iter).x(); + ++iter; + if(iter == tailp_->points.rend()) { + tailp_->points.push_back(point); + return; + } + if((iter->y() == point.y() && firstY == point.y()) || + (iter->x() == point.x() && firstX == point.x())){ + --iter; + *iter = point; + } else { + tailp_->points.push_back(point); + } + } + + /** + * @brief joins the two chains that the two active tail tails are ends of + * checks for closure of figure and writes out polygons appropriately + * returns a handle to a hole if one is closed + */ + + template + static inline ActiveTail45* joinChains(Point point, ActiveTail45* at1, ActiveTail45* at2, bool solid, + cT& output) { + if(at1->otherTailp_ == at2) { + //if(at2->otherTailp_ != at1) std::cout << "half closed error\n"; + //we are closing a figure + at1->pushPoint(point); + at2->pushPoint(point); + if(solid) { + //we are closing a solid figure, write to output + //std::cout << "test1\n"; + at1->copyHoles(*(at1->otherTailp_)); + //std::cout << "test2\n"; + //Polygon45WithHolesImpl poly(polyData); + //std::cout << poly << "\n"; + //std::cout << "test3\n"; + typedef typename cT::value_type pType; + output.push_back(pType()); + typedef typename geometry_concept::type cType; + typename PolyLineByConcept::type polyData(at1); + assign(output.back(), polyData); + //std::cout << "test4\n"; + //std::cout << "delete " << at1->otherTailp_ << "\n"; + //at1->print(); + //at1->otherTailp_->print(); + delete at1->otherTailp_; + //at1->print(); + //at1->otherTailp_->print(); + //std::cout << "test5\n"; + //std::cout << "delete " << at1 << "\n"; + delete at1; + //std::cout << "test6\n"; + return 0; + } else { + //we are closing a hole, return the tail end active tail of the figure + return at1; + } + } + //we are not closing a figure + at1->pushPoint(point); + at1->join(at2); + delete at1; + delete at2; + return 0; + } + + inline void destroyContents() { + if(otherTailp_) { + //std::cout << "delete p " << tailp_ << "\n"; + if(tailp_) delete tailp_; + tailp_ = 0; + otherTailp_->otherTailp_ = 0; + otherTailp_->tailp_ = 0; + otherTailp_ = 0; + } + for(typename std::list::iterator itr = holesList_.begin(); itr != holesList_.end(); ++itr) { + //std::cout << "delete p " << (*itr) << "\n"; + if(*itr) { + if((*itr)->otherTailp_) { + delete (*itr)->otherTailp_; + (*itr)->otherTailp_ = 0; + } + delete (*itr); + } + (*itr) = 0; + } + holesList_.clear(); + } + +// inline void print() { +// std::cout << this << " " << tailp_ << " " << otherTailp_ << " " << holesList_.size() << " " << head_ << "\n"; +// } + + static inline std::pair createActiveTail45sAsPair(Point point, bool solid, + ActiveTail45* phole, bool fractureHoles) { + ActiveTail45* at1 = 0; + ActiveTail45* at2 = 0; + if(phole && fractureHoles) { + //std::cout << "adding hole\n"; + at1 = phole; + //assert solid == false, we should be creating a corner with solid below and to the left if there was a hole + at2 = at1->getOtherActiveTail(); + at2->pushPoint(point); + at1->pushPoint(point); + } else { + at1 = new ActiveTail45(point, at2, solid); + at2 = new ActiveTail45(at1); + at1->otherTailp_ = at2; + at2->head_ = !solid; + if(phole) + at2->addHole(phole); //assert fractureHoles == false + } + return std::pair(at1, at2); + } + + }; + + template + class Vertex45CountT { + public: + typedef ct count_type; + inline Vertex45CountT() +#ifndef BOOST_POLYGON_MSVC + : counts() +#endif + { counts[0] = counts[1] = counts[2] = counts[3] = 0; } + //inline Vertex45CountT(ct count) { counts[0] = counts[1] = counts[2] = counts[3] = count; } + inline Vertex45CountT(const ct& count1, const ct& count2, const ct& count3, + const ct& count4) +#ifndef BOOST_POLYGON_MSVC + : counts() +#endif + { + counts[0] = count1; + counts[1] = count2; + counts[2] = count3; + counts[3] = count4; + } + inline Vertex45CountT(const Vertex45& vertex) +#ifndef BOOST_POLYGON_MSVC + : counts() +#endif + { + counts[0] = counts[1] = counts[2] = counts[3] = 0; + (*this) += vertex; + } + inline Vertex45CountT(const Vertex45CountT& count) +#ifndef BOOST_POLYGON_MSVC + : counts() +#endif + { + (*this) = count; + } + inline bool operator==(const Vertex45CountT& count) const { + for(unsigned int i = 0; i < 4; ++i) { + if(counts[i] != count.counts[i]) return false; + } + return true; + } + inline bool operator!=(const Vertex45CountT& count) const { return !((*this) == count); } + inline Vertex45CountT& operator=(ct count) { + counts[0] = counts[1] = counts[2] = counts[3] = count; return *this; } + inline Vertex45CountT& operator=(const Vertex45CountT& count) { + for(unsigned int i = 0; i < 4; ++i) { + counts[i] = count.counts[i]; + } + return *this; + } + inline ct& operator[](int index) { return counts[index]; } + inline ct operator[](int index) const {return counts[index]; } + inline Vertex45CountT& operator+=(const Vertex45CountT& count){ + for(unsigned int i = 0; i < 4; ++i) { + counts[i] += count.counts[i]; + } + return *this; + } + inline Vertex45CountT& operator-=(const Vertex45CountT& count){ + for(unsigned int i = 0; i < 4; ++i) { + counts[i] -= count.counts[i]; + } + return *this; + } + inline Vertex45CountT operator+(const Vertex45CountT& count) const { + return Vertex45CountT(*this)+=count; + } + inline Vertex45CountT operator-(const Vertex45CountT& count) const { + return Vertex45CountT(*this)-=count; + } + inline Vertex45CountT invert() const { + return Vertex45CountT()-=(*this); + } + inline Vertex45CountT& operator+=(const Vertex45& element){ + counts[element.rise+1] += element.count; return *this; + } + inline bool is_45() const { + return counts[0] != 0 || counts[2] != 0; + } + private: + ct counts[4]; + }; + + typedef Vertex45CountT Vertex45Count; + +// inline std::ostream& operator<< (std::ostream& o, const Vertex45Count& c) { +// o << c[0] << ", " << c[1] << ", "; +// o << c[2] << ", " << c[3]; +// return o; +// } + + template + class Vertex45CompactT { + public: + Point pt; + ct count; + typedef typename boolean_op_45::template Vertex45T Vertex45T; + inline Vertex45CompactT() : pt(), count() {} + inline Vertex45CompactT(const Point& point, int riseIn, int countIn) : pt(point), count() { + count[riseIn+1] = countIn; + } + template + inline Vertex45CompactT(const typename boolean_op_45::template Vertex45T& vertex) : pt(vertex.pt), count() { + count[vertex.rise+1] = vertex.count; + } + inline Vertex45CompactT(const Vertex45CompactT& vertex) : pt(vertex.pt), count(vertex.count) {} + inline Vertex45CompactT& operator=(const Vertex45CompactT& vertex){ + pt = vertex.pt; count = vertex.count; return *this; } + inline bool operator==(const Vertex45CompactT& vertex) const { + return pt == vertex.pt && count == vertex.count; } + inline bool operator!=(const Vertex45CompactT& vertex) const { return !((*this) == vertex); } + inline bool operator<(const Vertex45CompactT& vertex) const { + if(pt.x() < vertex.pt.x()) return true; + if(pt.x() == vertex.pt.x()) { + return pt.y() < vertex.pt.y(); + } + return false; + } + inline bool operator>(const Vertex45CompactT& vertex) const { return vertex < (*this); } + inline bool operator<=(const Vertex45CompactT& vertex) const { return !((*this) > vertex); } + inline bool operator>=(const Vertex45CompactT& vertex) const { return !((*this) < vertex); } + inline bool haveVertex45(int index) const { return count[index]; } + inline Vertex45T operator[](int index) const { + return Vertex45T(pt, index-1, count[index]); } + }; + + typedef Vertex45CompactT Vertex45Compact; + +// inline std::ostream& operator<< (std::ostream& o, const Vertex45Compact& c) { +// o << c.pt << ", " << c.count; +// return o; +// } + + class Polygon45Formation { + private: + //definitions + typedef std::map Polygon45FormationData; + typedef typename Polygon45FormationData::iterator iterator; + typedef typename Polygon45FormationData::const_iterator const_iterator; + + //data + Polygon45FormationData scanData_; + Unit x_; + int justBefore_; + int fractureHoles_; + public: + inline Polygon45Formation() : scanData_(), x_((std::numeric_limits::min)()), justBefore_(false), fractureHoles_(0) { + lessVertex45 lessElm(&x_, &justBefore_); + scanData_ = Polygon45FormationData(lessElm); + } + inline Polygon45Formation(bool fractureHoles) : scanData_(), x_((std::numeric_limits::min)()), justBefore_(false), fractureHoles_(fractureHoles) { + lessVertex45 lessElm(&x_, &justBefore_); + scanData_ = Polygon45FormationData(lessElm); + } + inline Polygon45Formation(const Polygon45Formation& that) : + scanData_(), x_((std::numeric_limits::min)()), justBefore_(false), fractureHoles_(0) { (*this) = that; } + inline Polygon45Formation& operator=(const Polygon45Formation& that) { + x_ = that.x_; + justBefore_ = that.justBefore_; + fractureHoles_ = that.fractureHoles_; + lessVertex45 lessElm(&x_, &justBefore_); + scanData_ = Polygon45FormationData(lessElm); + for(const_iterator itr = that.scanData_.begin(); itr != that.scanData_.end(); ++itr){ + scanData_.insert(scanData_.end(), *itr); + } + return *this; + } + + //cT is an output container of Polygon45 or Polygon45WithHoles + //iT is an iterator over Vertex45 elements + //inputBegin - inputEnd is a range of sorted iT that represents + //one or more scanline stops worth of data + template + void scan(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "1\n"; + while(inputBegin != inputEnd) { + //std::cout << "2\n"; + x_ = (*inputBegin).pt.x(); + //std::cout << "SCAN FORMATION " << x_ << "\n"; + //std::cout << "x_ = " << x_ << "\n"; + //std::cout << "scan line size: " << scanData_.size() << "\n"; + inputBegin = processEvent_(output, inputBegin, inputEnd); + } + } + + private: + //functions + template + inline std::pair processPoint_(cT& output, cT2& elements, Point point, + Vertex45Count& counts, ActiveTail45** tails, Vertex45Count& incoming) { + //std::cout << point << "\n"; + //std::cout << counts[0] << " "; + //std::cout << counts[1] << " "; + //std::cout << counts[2] << " "; + //std::cout << counts[3] << "\n"; + //std::cout << incoming[0] << " "; + //std::cout << incoming[1] << " "; + //std::cout << incoming[2] << " "; + //std::cout << incoming[3] << "\n"; + //join any closing solid corners + ActiveTail45* returnValue = 0; + int returnCount = 0; + for(int i = 0; i < 3; ++i) { + //std::cout << i << "\n"; + if(counts[i] == -1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < 4; ++j) { + //std::cout << j << "\n"; + if(counts[j]) { + if(counts[j] == 1) { + //std::cout << "case1: " << i << " " << j << "\n"; + //if a figure is closed it will be written out by this function to output + ActiveTail45::joinChains(point, tails[i], tails[j], true, output); + counts[i] = 0; + counts[j] = 0; + tails[i] = 0; + tails[j] = 0; + } + break; + } + } + } + } + //find any pairs of incoming edges that need to create pair for leading solid + //std::cout << "checking case2\n"; + for(int i = 0; i < 3; ++i) { + //std::cout << i << "\n"; + if(incoming[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < 4; ++j) { + //std::cout << j << "\n"; + if(incoming[j]) { + if(incoming[j] == -1) { + //std::cout << "case2: " << i << " " << j << "\n"; + //std::cout << "creating active tail pair\n"; + std::pair tailPair = + ActiveTail45::createActiveTail45sAsPair(point, true, 0, fractureHoles_ != 0); + //tailPair.first->print(); + //tailPair.second->print(); + if(j == 3) { + //vertical active tail becomes return value + returnValue = tailPair.first; + returnCount = 1; + } else { + Vertex45 vertex(point, i -1, incoming[i]); + //std::cout << "new element " << j-1 << " " << -1 << "\n"; + elements.push_back(std::pair(Vertex45(point, j -1, -1), tailPair.first)); + } + //std::cout << "new element " << i-1 << " " << 1 << "\n"; + elements.push_back(std::pair(Vertex45(point, i -1, 1), tailPair.second)); + incoming[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + } + + //find any active tail that needs to pass through to an incoming edge + //we expect to find no more than two pass through + + //find pass through with solid on top + //std::cout << "checking case 3\n"; + for(int i = 0; i < 4; ++i) { + //std::cout << i << "\n"; + if(counts[i] != 0) { + if(counts[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = 3; j >= 0; --j) { + if(incoming[j] != 0) { + if(incoming[j] == 1) { + //std::cout << "case3: " << i << " " << j << "\n"; + //tails[i]->print(); + //pass through solid on top + tails[i]->pushPoint(point); + //std::cout << "after push\n"; + if(j == 3) { + returnValue = tails[i]; + returnCount = -1; + } else { + elements.push_back(std::pair(Vertex45(point, j -1, incoming[j]), tails[i])); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + //std::cout << "checking case 4\n"; + //find pass through with solid on bottom + for(int i = 3; i >= 0; --i) { + if(counts[i] != 0) { + if(counts[i] == -1) { + for(int j = 0; j < 4; ++j) { + if(incoming[j] != 0) { + if(incoming[j] == -1) { + //std::cout << "case4: " << i << " " << j << "\n"; + //pass through solid on bottom + tails[i]->pushPoint(point); + if(j == 3) { + returnValue = tails[i]; + returnCount = 1; + } else { + //std::cout << "new element " << j-1 << " " << incoming[j] << "\n"; + elements.push_back(std::pair(Vertex45(point, j -1, incoming[j]), tails[i])); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + + //find the end of a hole or the beginning of a hole + + //find end of a hole + for(int i = 0; i < 3; ++i) { + if(counts[i] != 0) { + for(int j = i+1; j < 4; ++j) { + if(counts[j] != 0) { + //std::cout << "case5: " << i << " " << j << "\n"; + //we are ending a hole and may potentially close a figure and have to handle the hole + returnValue = ActiveTail45::joinChains(point, tails[i], tails[j], false, output); + tails[i] = 0; + tails[j] = 0; + counts[i] = 0; + counts[j] = 0; + break; + } + } + break; + } + } + //find beginning of a hole + for(int i = 0; i < 3; ++i) { + if(incoming[i] != 0) { + for(int j = i+1; j < 4; ++j) { + if(incoming[j] != 0) { + //std::cout << "case6: " << i << " " << j << "\n"; + //we are beginning a empty space + ActiveTail45* holep = 0; + if(counts[3] == 0) holep = tails[3]; + std::pair tailPair = + ActiveTail45::createActiveTail45sAsPair(point, false, holep, fractureHoles_ != 0); + if(j == 3) { + returnValue = tailPair.first; + returnCount = -1; + } else { + //std::cout << "new element " << j-1 << " " << incoming[j] << "\n"; + elements.push_back(std::pair(Vertex45(point, j -1, incoming[j]), tailPair.first)); + } + //std::cout << "new element " << i-1 << " " << incoming[i] << "\n"; + elements.push_back(std::pair(Vertex45(point, i -1, incoming[i]), tailPair.second)); + incoming[i] = 0; + incoming[j] = 0; + break; + } + } + break; + } + } + //assert that tails, counts and incoming are all null + return std::pair(returnCount, returnValue); + } + + template + inline iT processEvent_(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "processEvent_\n"; + justBefore_ = true; + //collect up all elements from the tree that are at the y + //values of events in the input queue + //create vector of new elements to add into tree + ActiveTail45* verticalTail = 0; + int verticalCount = 0; + iT currentIter = inputBegin; + std::vector elementIters; + std::vector > elements; + while(currentIter != inputEnd && currentIter->pt.x() == x_) { + //std::cout << "loop\n"; + Unit currentY = (*currentIter).pt.y(); + iterator iter = lookUp_(currentY); + //int counts[4] = {0, 0, 0, 0}; + Vertex45Count counts; + ActiveTail45* tails[4] = {0, 0, 0, verticalTail}; + //std::cout << "finding elements in tree\n"; + while(iter != scanData_.end() && + iter->first.evalAtX(x_) == currentY) { + //std::cout << "loop2\n"; + elementIters.push_back(iter); + int index = iter->first.rise + 1; + //std::cout << index << " " << iter->first.count << "\n"; + counts[index] = iter->first.count; + tails[index] = iter->second; + ++iter; + } + //int incoming[4] = {0, 0, 0, 0}; + Vertex45Count incoming; + //std::cout << "aggregating\n"; + do { + //std::cout << "loop3\n"; + Vertex45Compact currentVertex(*currentIter); + incoming += currentVertex.count; + ++currentIter; + } while(currentIter != inputEnd && currentIter->pt.y() == currentY && + currentIter->pt.x() == x_); + //now counts and tails have the data from the left and + //incoming has the data from the right at this point + //cancel out any end points + //std::cout << counts[0] << " "; + //std::cout << counts[1] << " "; + //std::cout << counts[2] << " "; + //std::cout << counts[3] << "\n"; + //std::cout << incoming[0] << " "; + //std::cout << incoming[1] << " "; + //std::cout << incoming[2] << " "; + //std::cout << incoming[3] << "\n"; + if(verticalTail) { + counts[3] = -verticalCount; + } + incoming[3] *= -1; + for(unsigned int i = 0; i < 4; ++i) incoming[i] += counts[i]; + //std::cout << "calling processPoint_\n"; + std::pair result = processPoint_(output, elements, Point(x_, currentY), counts, tails, incoming); + verticalCount = result.first; + verticalTail = result.second; + //if(verticalTail) std::cout << "have vertical tail\n"; + //std::cout << "verticalCount: " << verticalCount << "\n"; + if(verticalTail && !verticalCount) { + //we got a hole out of the point we just processed + //iter is still at the next y element above the current y value in the tree + //std::cout << "checking whether ot handle hole\n"; + if(currentIter == inputEnd || + currentIter->pt.x() != x_ || + currentIter->pt.y() >= iter->first.evalAtX(x_)) { + //std::cout << "handle hole here\n"; + if(fractureHoles_) { + //std::cout << "fracture hole here\n"; + //we need to handle the hole now and not at the next input vertex + ActiveTail45* at = iter->second; + Point point(x_, iter->first.evalAtX(x_)); + verticalTail->getOtherActiveTail()->pushPoint(point); + iter->second = verticalTail->getOtherActiveTail(); + at->pushPoint(point); + verticalTail->join(at); + delete at; + delete verticalTail; + verticalTail = 0; + } else { + //std::cout << "push hole onto list\n"; + iter->second->addHole(verticalTail); + verticalTail = 0; + } + } + } + } + //std::cout << "erasing\n"; + //erase all elements from the tree + for(typename std::vector::iterator iter = elementIters.begin(); + iter != elementIters.end(); ++iter) { + //std::cout << "erasing loop\n"; + scanData_.erase(*iter); + } + //switch comparison tie breaking policy + justBefore_ = false; + //add new elements into tree + //std::cout << "inserting\n"; + for(typename std::vector >::iterator iter = elements.begin(); + iter != elements.end(); ++iter) { + //std::cout << "inserting loop\n"; + scanData_.insert(scanData_.end(), *iter); + } + //std::cout << "end processEvent\n"; + return currentIter; + } + + inline iterator lookUp_(Unit y){ + //if just before then we need to look from 1 not -1 + return scanData_.lower_bound(Vertex45(Point(x_, y), -1+2*justBefore_, 0)); + } + + }; + + template + static inline bool testPolygon45FormationRect(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(true); + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 2, -1)); + data.push_back(Vertex45(Point(10, 10), 2, 1)); + data.push_back(Vertex45(Point(10, 10), 0, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygon45FormationP1(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(true); + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 1, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 1, -1)); + data.push_back(Vertex45(Point(10, 10), 1, -1)); + data.push_back(Vertex45(Point(10, 10), 2, -1)); + data.push_back(Vertex45(Point(10, 20), 2, 1)); + data.push_back(Vertex45(Point(10, 20), 1, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + //polygon45set class + + template + static inline bool testPolygon45FormationP2(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(true); + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 1, -1)); + data.push_back(Vertex45(Point(10, 0), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 1, 1)); + data.push_back(Vertex45(Point(10, 10), 1, 1)); + data.push_back(Vertex45(Point(10, 10), 0, -1)); + data.push_back(Vertex45(Point(20, 10), 1, -1)); + data.push_back(Vertex45(Point(20, 10), 0, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + //polygon45set class + + template + static inline bool testPolygon45FormationStar1(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(true); + std::vector polys; + std::vector data; + // result == 0 8 -1 1 + data.push_back(Vertex45(Point(0, 8), -1, 1)); + // result == 0 8 1 -1 + data.push_back(Vertex45(Point(0, 8), 1, -1)); + // result == 4 0 1 1 + data.push_back(Vertex45(Point(4, 0), 1, 1)); + // result == 4 0 2 1 + data.push_back(Vertex45(Point(4, 0), 2, 1)); + // result == 4 4 2 -1 + data.push_back(Vertex45(Point(4, 4), 2, -1)); + // result == 4 4 -1 -1 + data.push_back(Vertex45(Point(4, 4), -1, -1)); + // result == 4 12 1 1 + data.push_back(Vertex45(Point(4, 12), 1, 1)); + // result == 4 12 2 1 + data.push_back(Vertex45(Point(4, 12), 2, 1)); + // result == 4 16 2 -1 + data.push_back(Vertex45(Point(4, 16), 2, 1)); + // result == 4 16 -1 -1 + data.push_back(Vertex45(Point(4, 16), -1, -1)); + // result == 6 2 1 -1 + data.push_back(Vertex45(Point(6, 2), 1, -1)); + // result == 6 14 -1 1 + data.push_back(Vertex45(Point(6, 14), -1, 1)); + // result == 6 2 -1 1 + data.push_back(Vertex45(Point(6, 2), -1, 1)); + // result == 6 14 1 -1 + data.push_back(Vertex45(Point(6, 14), 1, -1)); + // result == 8 0 -1 -1 + data.push_back(Vertex45(Point(8, 0), -1, -1)); + // result == 8 0 2 -1 + data.push_back(Vertex45(Point(8, 0), 2, -1)); + // result == 8 4 2 1 + data.push_back(Vertex45(Point(8, 4), 2, 1)); + // result == 8 4 1 1 + data.push_back(Vertex45(Point(8, 4), 1, 1)); + // result == 8 12 -1 -1 + data.push_back(Vertex45(Point(8, 12), -1, -1)); + // result == 8 12 2 -1 + data.push_back(Vertex45(Point(8, 12), 2, -1)); + // result == 8 16 2 1 + data.push_back(Vertex45(Point(8, 16), 2, 1)); + // result == 8 16 1 1 + data.push_back(Vertex45(Point(8, 16), 1, 1)); + // result == 12 8 1 -1 + data.push_back(Vertex45(Point(12, 8), 1, -1)); + // result == 12 8 -1 1 + data.push_back(Vertex45(Point(12, 8), -1, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygon45FormationStar2(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(true); + std::vector polys; + Scan45 scan45; + std::vector result; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,4), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,4), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,12), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + count = Count2(0, 1); + ncount = count.invert(); + vertices.push_back(Scan45Vertex(Point(0,8), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,8), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,0), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + sortScan45Vector(vertices); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + + polygon_sort(result.begin(), result.end()); + pf.scan(polys, result.begin(), result.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygon45FormationStarHole1(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(true); + std::vector polys; + std::vector data; + // result == 0 8 -1 1 + data.push_back(Vertex45(Point(0, 8), -1, 1)); + // result == 0 8 1 -1 + data.push_back(Vertex45(Point(0, 8), 1, -1)); + // result == 4 0 1 1 + data.push_back(Vertex45(Point(4, 0), 1, 1)); + // result == 4 0 2 1 + data.push_back(Vertex45(Point(4, 0), 2, 1)); + // result == 4 4 2 -1 + data.push_back(Vertex45(Point(4, 4), 2, -1)); + // result == 4 4 -1 -1 + data.push_back(Vertex45(Point(4, 4), -1, -1)); + // result == 4 12 1 1 + data.push_back(Vertex45(Point(4, 12), 1, 1)); + // result == 4 12 2 1 + data.push_back(Vertex45(Point(4, 12), 2, 1)); + // result == 4 16 2 -1 + data.push_back(Vertex45(Point(4, 16), 2, 1)); + // result == 4 16 -1 -1 + data.push_back(Vertex45(Point(4, 16), -1, -1)); + // result == 6 2 1 -1 + data.push_back(Vertex45(Point(6, 2), 1, -1)); + // result == 6 14 -1 1 + data.push_back(Vertex45(Point(6, 14), -1, 1)); + // result == 6 2 -1 1 + data.push_back(Vertex45(Point(6, 2), -1, 1)); + // result == 6 14 1 -1 + data.push_back(Vertex45(Point(6, 14), 1, -1)); + // result == 8 0 -1 -1 + data.push_back(Vertex45(Point(8, 0), -1, -1)); + // result == 8 0 2 -1 + data.push_back(Vertex45(Point(8, 0), 2, -1)); + // result == 8 4 2 1 + data.push_back(Vertex45(Point(8, 4), 2, 1)); + // result == 8 4 1 1 + data.push_back(Vertex45(Point(8, 4), 1, 1)); + // result == 8 12 -1 -1 + data.push_back(Vertex45(Point(8, 12), -1, -1)); + // result == 8 12 2 -1 + data.push_back(Vertex45(Point(8, 12), 2, -1)); + // result == 8 16 2 1 + data.push_back(Vertex45(Point(8, 16), 2, 1)); + // result == 8 16 1 1 + data.push_back(Vertex45(Point(8, 16), 1, 1)); + // result == 12 8 1 -1 + data.push_back(Vertex45(Point(12, 8), 1, -1)); + // result == 12 8 -1 1 + data.push_back(Vertex45(Point(12, 8), -1, 1)); + + data.push_back(Vertex45(Point(6, 4), 1, -1)); + data.push_back(Vertex45(Point(6, 4), 2, -1)); + data.push_back(Vertex45(Point(6, 8), -1, 1)); + data.push_back(Vertex45(Point(6, 8), 2, 1)); + data.push_back(Vertex45(Point(8, 6), -1, -1)); + data.push_back(Vertex45(Point(8, 6), 1, 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygon45FormationStarHole2(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(false); + std::vector polys; + std::vector data; + // result == 0 8 -1 1 + data.push_back(Vertex45(Point(0, 8), -1, 1)); + // result == 0 8 1 -1 + data.push_back(Vertex45(Point(0, 8), 1, -1)); + // result == 4 0 1 1 + data.push_back(Vertex45(Point(4, 0), 1, 1)); + // result == 4 0 2 1 + data.push_back(Vertex45(Point(4, 0), 2, 1)); + // result == 4 4 2 -1 + data.push_back(Vertex45(Point(4, 4), 2, -1)); + // result == 4 4 -1 -1 + data.push_back(Vertex45(Point(4, 4), -1, -1)); + // result == 4 12 1 1 + data.push_back(Vertex45(Point(4, 12), 1, 1)); + // result == 4 12 2 1 + data.push_back(Vertex45(Point(4, 12), 2, 1)); + // result == 4 16 2 -1 + data.push_back(Vertex45(Point(4, 16), 2, 1)); + // result == 4 16 -1 -1 + data.push_back(Vertex45(Point(4, 16), -1, -1)); + // result == 6 2 1 -1 + data.push_back(Vertex45(Point(6, 2), 1, -1)); + // result == 6 14 -1 1 + data.push_back(Vertex45(Point(6, 14), -1, 1)); + // result == 6 2 -1 1 + data.push_back(Vertex45(Point(6, 2), -1, 1)); + // result == 6 14 1 -1 + data.push_back(Vertex45(Point(6, 14), 1, -1)); + // result == 8 0 -1 -1 + data.push_back(Vertex45(Point(8, 0), -1, -1)); + // result == 8 0 2 -1 + data.push_back(Vertex45(Point(8, 0), 2, -1)); + // result == 8 4 2 1 + data.push_back(Vertex45(Point(8, 4), 2, 1)); + // result == 8 4 1 1 + data.push_back(Vertex45(Point(8, 4), 1, 1)); + // result == 8 12 -1 -1 + data.push_back(Vertex45(Point(8, 12), -1, -1)); + // result == 8 12 2 -1 + data.push_back(Vertex45(Point(8, 12), 2, -1)); + // result == 8 16 2 1 + data.push_back(Vertex45(Point(8, 16), 2, 1)); + // result == 8 16 1 1 + data.push_back(Vertex45(Point(8, 16), 1, 1)); + // result == 12 8 1 -1 + data.push_back(Vertex45(Point(12, 8), 1, -1)); + // result == 12 8 -1 1 + data.push_back(Vertex45(Point(12, 8), -1, 1)); + + data.push_back(Vertex45(Point(6, 4), 1, -1)); + data.push_back(Vertex45(Point(6, 4), 2, -1)); + data.push_back(Vertex45(Point(6, 12), -1, 1)); + data.push_back(Vertex45(Point(6, 12), 2, 1)); + data.push_back(Vertex45(Point(10, 8), -1, -1)); + data.push_back(Vertex45(Point(10, 8), 1, 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygon45Formation(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + Polygon45Formation pf(false); + std::vector polys; + std::vector data; + + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 100), 2, -1)); + data.push_back(Vertex45(Point(0, 100), 0, -1)); + data.push_back(Vertex45(Point(100, 0), 0, -1)); + data.push_back(Vertex45(Point(100, 0), 2, -1)); + data.push_back(Vertex45(Point(100, 100), 2, 1)); + data.push_back(Vertex45(Point(100, 100), 0, 1)); + + data.push_back(Vertex45(Point(2, 2), 0, -1)); + data.push_back(Vertex45(Point(2, 2), 2, -1)); + data.push_back(Vertex45(Point(2, 10), 2, 1)); + data.push_back(Vertex45(Point(2, 10), 0, 1)); + data.push_back(Vertex45(Point(10, 2), 0, 1)); + data.push_back(Vertex45(Point(10, 2), 2, 1)); + data.push_back(Vertex45(Point(10, 10), 2, -1)); + data.push_back(Vertex45(Point(10, 10), 0, -1)); + + data.push_back(Vertex45(Point(2, 12), 0, -1)); + data.push_back(Vertex45(Point(2, 12), 2, -1)); + data.push_back(Vertex45(Point(2, 22), 2, 1)); + data.push_back(Vertex45(Point(2, 22), 0, 1)); + data.push_back(Vertex45(Point(10, 12), 0, 1)); + data.push_back(Vertex45(Point(10, 12), 2, 1)); + data.push_back(Vertex45(Point(10, 22), 2, -1)); + data.push_back(Vertex45(Point(10, 22), 0, -1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + + class Polygon45Tiling { + private: + //definitions + typedef std::map Polygon45FormationData; + typedef typename Polygon45FormationData::iterator iterator; + typedef typename Polygon45FormationData::const_iterator const_iterator; + + //data + Polygon45FormationData scanData_; + Unit x_; + int justBefore_; + public: + inline Polygon45Tiling() : scanData_(), x_((std::numeric_limits::min)()), justBefore_(false) { + lessVertex45 lessElm(&x_, &justBefore_); + scanData_ = Polygon45FormationData(lessElm); + } + inline Polygon45Tiling(const Polygon45Tiling& that) : + scanData_(), x_((std::numeric_limits::min)()), justBefore_(false) { (*this) = that; } + inline Polygon45Tiling& operator=(const Polygon45Tiling& that) { + x_ = that.x_; + justBefore_ = that.justBefore_; + lessVertex45 lessElm(&x_, &justBefore_); + scanData_ = Polygon45FormationData(lessElm); + for(const_iterator itr = that.scanData_.begin(); itr != that.scanData_.end(); ++itr){ + scanData_.insert(scanData_.end(), *itr); + } + return *this; + } + + //cT is an output container of Polygon45 or Polygon45WithHoles + //iT is an iterator over Vertex45 elements + //inputBegin - inputEnd is a range of sorted iT that represents + //one or more scanline stops worth of data + template + void scan(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "1\n"; + while(inputBegin != inputEnd) { + //std::cout << "2\n"; + x_ = (*inputBegin).pt.x(); + //std::cout << "SCAN FORMATION " << x_ << "\n"; + //std::cout << "x_ = " << x_ << "\n"; + //std::cout << "scan line size: " << scanData_.size() << "\n"; + inputBegin = processEvent_(output, inputBegin, inputEnd); + } + } + + private: + //functions + + inline void getVerticalPair_(std::pair& verticalPair, + iterator previter) { + ActiveTail45* iterTail = (*previter).second; + Point prevPoint(x_, previter->first.evalAtX(x_)); + iterTail->pushPoint(prevPoint); + std::pair tailPair = + ActiveTail45::createActiveTail45sAsPair(prevPoint, true, 0, false); + verticalPair.first = iterTail; + verticalPair.second = tailPair.first; + (*previter).second = tailPair.second; + } + + template + inline std::pair processPoint_(cT& output, cT2& elements, + std::pair& verticalPair, + iterator previter, Point point, + Vertex45Count& counts, ActiveTail45** tails, Vertex45Count& incoming) { + //std::cout << point << "\n"; + //std::cout << counts[0] << " "; + //std::cout << counts[1] << " "; + //std::cout << counts[2] << " "; + //std::cout << counts[3] << "\n"; + //std::cout << incoming[0] << " "; + //std::cout << incoming[1] << " "; + //std::cout << incoming[2] << " "; + //std::cout << incoming[3] << "\n"; + //join any closing solid corners + ActiveTail45* returnValue = 0; + std::pair verticalPairOut; + verticalPairOut.first = 0; + verticalPairOut.second = 0; + int returnCount = 0; + for(int i = 0; i < 3; ++i) { + //std::cout << i << "\n"; + if(counts[i] == -1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < 4; ++j) { + //std::cout << j << "\n"; + if(counts[j]) { + if(counts[j] == 1) { + //std::cout << "case1: " << i << " " << j << "\n"; + //if a figure is closed it will be written out by this function to output + ActiveTail45::joinChains(point, tails[i], tails[j], true, output); + counts[i] = 0; + counts[j] = 0; + tails[i] = 0; + tails[j] = 0; + } + break; + } + } + } + } + //find any pairs of incoming edges that need to create pair for leading solid + //std::cout << "checking case2\n"; + for(int i = 0; i < 3; ++i) { + //std::cout << i << "\n"; + if(incoming[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < 4; ++j) { + //std::cout << j << "\n"; + if(incoming[j]) { + if(incoming[j] == -1) { + //std::cout << "case2: " << i << " " << j << "\n"; + //std::cout << "creating active tail pair\n"; + std::pair tailPair = + ActiveTail45::createActiveTail45sAsPair(point, true, 0, false); + //tailPair.first->print(); + //tailPair.second->print(); + if(j == 3) { + //vertical active tail becomes return value + returnValue = tailPair.first; + returnCount = 1; + } else { + Vertex45 vertex(point, i -1, incoming[i]); + //std::cout << "new element " << j-1 << " " << -1 << "\n"; + elements.push_back(std::pair(Vertex45(point, j -1, -1), tailPair.first)); + } + //std::cout << "new element " << i-1 << " " << 1 << "\n"; + elements.push_back(std::pair(Vertex45(point, i -1, 1), tailPair.second)); + incoming[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + } + + //find any active tail that needs to pass through to an incoming edge + //we expect to find no more than two pass through + + //find pass through with solid on top + //std::cout << "checking case 3\n"; + for(int i = 0; i < 4; ++i) { + //std::cout << i << "\n"; + if(counts[i] != 0) { + if(counts[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = 3; j >= 0; --j) { + if(incoming[j] != 0) { + if(incoming[j] == 1) { + //std::cout << "case3: " << i << " " << j << "\n"; + //tails[i]->print(); + //pass through solid on top + if(i != 3) + tails[i]->pushPoint(point); + //std::cout << "after push\n"; + if(j == 3) { + returnValue = tails[i]; + returnCount = -1; + } else { + verticalPairOut.first = tails[i]; + std::pair tailPair = + ActiveTail45::createActiveTail45sAsPair(point, true, 0, false); + verticalPairOut.second = tailPair.first; + elements.push_back(std::pair(Vertex45(point, j -1, incoming[j]), + tailPair.second)); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + //std::cout << "checking case 4\n"; + //find pass through with solid on bottom + for(int i = 3; i >= 0; --i) { + if(counts[i] != 0) { + if(counts[i] == -1) { + for(int j = 0; j < 4; ++j) { + if(incoming[j] != 0) { + if(incoming[j] == -1) { + //std::cout << "case4: " << i << " " << j << "\n"; + //pass through solid on bottom + if(i == 3) { + //std::cout << "new element " << j-1 << " " << incoming[j] << "\n"; + if(j == 3) { + returnValue = tails[i]; + returnCount = 1; + } else { + tails[i]->pushPoint(point); + elements.push_back(std::pair(Vertex45(point, j -1, incoming[j]), tails[i])); + } + } else if(j == 3) { + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + ActiveTail45::joinChains(point, tails[i], verticalPair.first, true, output); + returnValue = verticalPair.second; + returnCount = 1; + } else { + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + ActiveTail45::joinChains(point, tails[i], verticalPair.first, true, output); + verticalPair.second->pushPoint(point); + elements.push_back(std::pair(Vertex45(point, j -1, incoming[j]), + verticalPair.second)); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + + //find the end of a hole or the beginning of a hole + + //find end of a hole + for(int i = 0; i < 3; ++i) { + if(counts[i] != 0) { + for(int j = i+1; j < 4; ++j) { + if(counts[j] != 0) { + //std::cout << "case5: " << i << " " << j << "\n"; + //we are ending a hole and may potentially close a figure and have to handle the hole + tails[i]->pushPoint(point); + verticalPairOut.first = tails[i]; + if(j == 3) { + verticalPairOut.second = tails[j]; + } else { + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + ActiveTail45::joinChains(point, tails[j], verticalPair.first, true, output); + verticalPairOut.second = verticalPair.second; + } + tails[i] = 0; + tails[j] = 0; + counts[i] = 0; + counts[j] = 0; + break; + } + } + break; + } + } + //find beginning of a hole + for(int i = 0; i < 3; ++i) { + if(incoming[i] != 0) { + for(int j = i+1; j < 4; ++j) { + if(incoming[j] != 0) { + //std::cout << "case6: " << i << " " << j << "\n"; + //we are beginning a empty space + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + verticalPair.second->pushPoint(point); + if(j == 3) { + returnValue = verticalPair.first; + returnCount = -1; + } else { + std::pair tailPair = + ActiveTail45::createActiveTail45sAsPair(point, true, 0, false); + //std::cout << "new element " << j-1 << " " << incoming[j] << "\n"; + elements.push_back(std::pair(Vertex45(point, j -1, incoming[j]), tailPair.second)); + verticalPairOut.second = tailPair.first; + verticalPairOut.first = verticalPair.first; + } + //std::cout << "new element " << i-1 << " " << incoming[i] << "\n"; + elements.push_back(std::pair(Vertex45(point, i -1, incoming[i]), verticalPair.second)); + incoming[i] = 0; + incoming[j] = 0; + break; + } + } + break; + } + } + verticalPair = verticalPairOut; + //assert that verticalPair is either both null, or neither null + //assert that returnValue is null if verticalPair is not null + //assert that tails, counts and incoming are all null + return std::pair(returnCount, returnValue); + } + + template + inline iT processEvent_(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "processEvent_\n"; + justBefore_ = true; + //collect up all elements from the tree that are at the y + //values of events in the input queue + //create vector of new elements to add into tree + ActiveTail45* verticalTail = 0; + std::pair verticalPair; + verticalPair.first = 0; + verticalPair.second = 0; + int verticalCount = 0; + iT currentIter = inputBegin; + std::vector elementIters; + std::vector > elements; + while(currentIter != inputEnd && currentIter->pt.x() == x_) { + //std::cout << "loop\n"; + Unit currentY = (*currentIter).pt.y(); + iterator iter = lookUp_(currentY); + //int counts[4] = {0, 0, 0, 0}; + Vertex45Count counts; + ActiveTail45* tails[4] = {0, 0, 0, verticalTail}; + //std::cout << "finding elements in tree\n"; + iterator previter = iter; + if(previter != scanData_.end() && + previter->first.evalAtX(x_) >= currentY && + previter != scanData_.begin()) + --previter; + while(iter != scanData_.end() && + iter->first.evalAtX(x_) == currentY) { + //std::cout << "loop2\n"; + elementIters.push_back(iter); + int index = iter->first.rise + 1; + //std::cout << index << " " << iter->first.count << "\n"; + counts[index] = iter->first.count; + tails[index] = iter->second; + ++iter; + } + //int incoming[4] = {0, 0, 0, 0}; + Vertex45Count incoming; + //std::cout << "aggregating\n"; + do { + //std::cout << "loop3\n"; + Vertex45Compact currentVertex(*currentIter); + incoming += currentVertex.count; + ++currentIter; + } while(currentIter != inputEnd && currentIter->pt.y() == currentY && + currentIter->pt.x() == x_); + //now counts and tails have the data from the left and + //incoming has the data from the right at this point + //cancel out any end points + //std::cout << counts[0] << " "; + //std::cout << counts[1] << " "; + //std::cout << counts[2] << " "; + //std::cout << counts[3] << "\n"; + //std::cout << incoming[0] << " "; + //std::cout << incoming[1] << " "; + //std::cout << incoming[2] << " "; + //std::cout << incoming[3] << "\n"; + if(verticalTail) { + counts[3] = -verticalCount; + } + incoming[3] *= -1; + for(unsigned int i = 0; i < 4; ++i) incoming[i] += counts[i]; + //std::cout << "calling processPoint_\n"; + std::pair result = processPoint_(output, elements, verticalPair, previter, + Point(x_, currentY), counts, tails, incoming); + verticalCount = result.first; + verticalTail = result.second; + if(verticalPair.first != 0 && iter != scanData_.end() && + (currentIter == inputEnd || currentIter->pt.x() != x_ || + currentIter->pt.y() > (*iter).first.evalAtX(x_))) { + //splice vertical pair into edge above + ActiveTail45* tailabove = (*iter).second; + Point point(x_, (*iter).first.evalAtX(x_)); + verticalPair.second->pushPoint(point); + ActiveTail45::joinChains(point, tailabove, verticalPair.first, true, output); + (*iter).second = verticalPair.second; + verticalPair.first = 0; + verticalPair.second = 0; + } + } + //std::cout << "erasing\n"; + //erase all elements from the tree + for(typename std::vector::iterator iter = elementIters.begin(); + iter != elementIters.end(); ++iter) { + //std::cout << "erasing loop\n"; + scanData_.erase(*iter); + } + //switch comparison tie breaking policy + justBefore_ = false; + //add new elements into tree + //std::cout << "inserting\n"; + for(typename std::vector >::iterator iter = elements.begin(); + iter != elements.end(); ++iter) { + //std::cout << "inserting loop\n"; + scanData_.insert(scanData_.end(), *iter); + } + //std::cout << "end processEvent\n"; + return currentIter; + } + + inline iterator lookUp_(Unit y){ + //if just before then we need to look from 1 not -1 + return scanData_.lower_bound(Vertex45(Point(x_, y), -1+2*justBefore_, 0)); + } + + }; + + template + static inline bool testPolygon45TilingRect(stream_type& stdcout) { + stdcout << "testing polygon tiling\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 2, -1)); + data.push_back(Vertex45(Point(10, 10), 2, 1)); + data.push_back(Vertex45(Point(10, 10), 0, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingP1(stream_type& stdcout) { + stdcout << "testing polygon tiling\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 1, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 1, -1)); + data.push_back(Vertex45(Point(10, 10), 1, -1)); + data.push_back(Vertex45(Point(10, 10), 2, -1)); + data.push_back(Vertex45(Point(10, 20), 2, 1)); + data.push_back(Vertex45(Point(10, 20), 1, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingP2(stream_type& stdcout) { + stdcout << "testing polygon tiling\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 1, -1)); + data.push_back(Vertex45(Point(10, 0), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 1, 1)); + data.push_back(Vertex45(Point(10, 10), 1, 1)); + data.push_back(Vertex45(Point(10, 10), 0, -1)); + data.push_back(Vertex45(Point(20, 10), 1, -1)); + data.push_back(Vertex45(Point(20, 10), 0, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingP3(stream_type& stdcout) { + stdcout << "testing polygon tiling\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 0, -1)); + data.push_back(Vertex45(Point(20, 0), 0, -1)); + data.push_back(Vertex45(Point(20, 0), 2, -1)); + data.push_back(Vertex45(Point(10, 10), 1, -1)); + data.push_back(Vertex45(Point(10, 10), 0, 1)); + data.push_back(Vertex45(Point(20, 20), 1, 1)); + data.push_back(Vertex45(Point(20, 20), 2, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingP4(stream_type& stdcout) { + stdcout << "testing polygon tiling p4\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 0, -1)); + data.push_back(Vertex45(Point(10, 0), -1, 1)); + data.push_back(Vertex45(Point(10, 0), 0, -1)); + data.push_back(Vertex45(Point(20, 10), 2, 1)); + data.push_back(Vertex45(Point(20, 10), 0, 1)); + data.push_back(Vertex45(Point(20, -10), -1, -1)); + data.push_back(Vertex45(Point(20, -10), 2, -1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingP5(stream_type& stdcout) { + stdcout << "testing polygon tiling P5\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 2, -1)); + data.push_back(Vertex45(Point(10, 10), 2, 1)); + data.push_back(Vertex45(Point(10, 10), 0, 1)); + + data.push_back(Vertex45(Point(1, 1), 0, -1)); + data.push_back(Vertex45(Point(1, 1), 1, 1)); + data.push_back(Vertex45(Point(2, 1), 0, 1)); + data.push_back(Vertex45(Point(2, 1), 1, -1)); + data.push_back(Vertex45(Point(2, 2), 1, -1)); + data.push_back(Vertex45(Point(2, 2), 0, 1)); + data.push_back(Vertex45(Point(3, 2), 1, 1)); + data.push_back(Vertex45(Point(3, 2), 0, -1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingP6(stream_type& stdcout) { + stdcout << "testing polygon tiling P6\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 10), 2, -1)); + data.push_back(Vertex45(Point(0, 10), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 0, -1)); + data.push_back(Vertex45(Point(10, 0), 2, -1)); + data.push_back(Vertex45(Point(10, 10), 2, 1)); + data.push_back(Vertex45(Point(10, 10), 0, 1)); + + data.push_back(Vertex45(Point(1, 1), 0, -1)); + data.push_back(Vertex45(Point(1, 1), 2, -1)); + data.push_back(Vertex45(Point(1, 2), 2, 1)); + data.push_back(Vertex45(Point(1, 2), 0, 1)); + data.push_back(Vertex45(Point(2, 1), 0, 1)); + data.push_back(Vertex45(Point(2, 1), 2, 1)); + data.push_back(Vertex45(Point(2, 2), 2, -1)); + data.push_back(Vertex45(Point(2, 2), 0, -1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingStar1(stream_type& stdcout) { + stdcout << "testing polygon tiling star1\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + // result == 0 8 -1 1 + data.push_back(Vertex45(Point(0, 8), -1, 1)); + // result == 0 8 1 -1 + data.push_back(Vertex45(Point(0, 8), 1, -1)); + // result == 4 0 1 1 + data.push_back(Vertex45(Point(4, 0), 1, 1)); + // result == 4 0 2 1 + data.push_back(Vertex45(Point(4, 0), 2, 1)); + // result == 4 4 2 -1 + data.push_back(Vertex45(Point(4, 4), 2, -1)); + // result == 4 4 -1 -1 + data.push_back(Vertex45(Point(4, 4), -1, -1)); + // result == 4 12 1 1 + data.push_back(Vertex45(Point(4, 12), 1, 1)); + // result == 4 12 2 1 + data.push_back(Vertex45(Point(4, 12), 2, 1)); + // result == 4 16 2 -1 + data.push_back(Vertex45(Point(4, 16), 2, 1)); + // result == 4 16 -1 -1 + data.push_back(Vertex45(Point(4, 16), -1, -1)); + // result == 6 2 1 -1 + data.push_back(Vertex45(Point(6, 2), 1, -1)); + // result == 6 14 -1 1 + data.push_back(Vertex45(Point(6, 14), -1, 1)); + // result == 6 2 -1 1 + data.push_back(Vertex45(Point(6, 2), -1, 1)); + // result == 6 14 1 -1 + data.push_back(Vertex45(Point(6, 14), 1, -1)); + // result == 8 0 -1 -1 + data.push_back(Vertex45(Point(8, 0), -1, -1)); + // result == 8 0 2 -1 + data.push_back(Vertex45(Point(8, 0), 2, -1)); + // result == 8 4 2 1 + data.push_back(Vertex45(Point(8, 4), 2, 1)); + // result == 8 4 1 1 + data.push_back(Vertex45(Point(8, 4), 1, 1)); + // result == 8 12 -1 -1 + data.push_back(Vertex45(Point(8, 12), -1, -1)); + // result == 8 12 2 -1 + data.push_back(Vertex45(Point(8, 12), 2, -1)); + // result == 8 16 2 1 + data.push_back(Vertex45(Point(8, 16), 2, 1)); + // result == 8 16 1 1 + data.push_back(Vertex45(Point(8, 16), 1, 1)); + // result == 12 8 1 -1 + data.push_back(Vertex45(Point(12, 8), 1, -1)); + // result == 12 8 -1 1 + data.push_back(Vertex45(Point(12, 8), -1, 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingStar2(stream_type& stdcout) { + stdcout << "testing polygon tiling\n"; + Polygon45Tiling pf; + std::vector polys; + + Scan45 scan45; + std::vector result; + std::vector vertices; + //is a Rectnagle(0, 0, 10, 10); + Count2 count(1, 0); + Count2 ncount(-1, 0); + vertices.push_back(Scan45Vertex(Point(0,4), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,4), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,12), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + count = Count2(0, 1); + ncount = count.invert(); + vertices.push_back(Scan45Vertex(Point(0,8), Scan45Count(count, ncount, Count2(0, 0), Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(16,8), Scan45Count(Count2(0, 0), count, ncount, Count2(0, 0)))); + vertices.push_back(Scan45Vertex(Point(8,0), Scan45Count(ncount, Count2(0, 0), count, Count2(0, 0)))); + sortScan45Vector(vertices); + stdcout << "scanning\n"; + scan45.scan(result, vertices.begin(), vertices.end()); + + polygon_sort(result.begin(), result.end()); + pf.scan(polys, result.begin(), result.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingStarHole1(stream_type& stdcout) { + stdcout << "testing polygon tiling star hole 1\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + // result == 0 8 -1 1 + data.push_back(Vertex45(Point(0, 8), -1, 1)); + // result == 0 8 1 -1 + data.push_back(Vertex45(Point(0, 8), 1, -1)); + // result == 4 0 1 1 + data.push_back(Vertex45(Point(4, 0), 1, 1)); + // result == 4 0 2 1 + data.push_back(Vertex45(Point(4, 0), 2, 1)); + // result == 4 4 2 -1 + data.push_back(Vertex45(Point(4, 4), 2, -1)); + // result == 4 4 -1 -1 + data.push_back(Vertex45(Point(4, 4), -1, -1)); + // result == 4 12 1 1 + data.push_back(Vertex45(Point(4, 12), 1, 1)); + // result == 4 12 2 1 + data.push_back(Vertex45(Point(4, 12), 2, 1)); + // result == 4 16 2 -1 + data.push_back(Vertex45(Point(4, 16), 2, 1)); + // result == 4 16 -1 -1 + data.push_back(Vertex45(Point(4, 16), -1, -1)); + // result == 6 2 1 -1 + data.push_back(Vertex45(Point(6, 2), 1, -1)); + // result == 6 14 -1 1 + data.push_back(Vertex45(Point(6, 14), -1, 1)); + // result == 6 2 -1 1 + data.push_back(Vertex45(Point(6, 2), -1, 1)); + // result == 6 14 1 -1 + data.push_back(Vertex45(Point(6, 14), 1, -1)); + // result == 8 0 -1 -1 + data.push_back(Vertex45(Point(8, 0), -1, -1)); + // result == 8 0 2 -1 + data.push_back(Vertex45(Point(8, 0), 2, -1)); + // result == 8 4 2 1 + data.push_back(Vertex45(Point(8, 4), 2, 1)); + // result == 8 4 1 1 + data.push_back(Vertex45(Point(8, 4), 1, 1)); + // result == 8 12 -1 -1 + data.push_back(Vertex45(Point(8, 12), -1, -1)); + // result == 8 12 2 -1 + data.push_back(Vertex45(Point(8, 12), 2, -1)); + // result == 8 16 2 1 + data.push_back(Vertex45(Point(8, 16), 2, 1)); + // result == 8 16 1 1 + data.push_back(Vertex45(Point(8, 16), 1, 1)); + // result == 12 8 1 -1 + data.push_back(Vertex45(Point(12, 8), 1, -1)); + // result == 12 8 -1 1 + data.push_back(Vertex45(Point(12, 8), -1, 1)); + + data.push_back(Vertex45(Point(6, 4), 1, -1)); + data.push_back(Vertex45(Point(6, 4), 2, -1)); + data.push_back(Vertex45(Point(6, 8), -1, 1)); + data.push_back(Vertex45(Point(6, 8), 2, 1)); + data.push_back(Vertex45(Point(8, 6), -1, -1)); + data.push_back(Vertex45(Point(8, 6), 1, 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45TilingStarHole2(stream_type& stdcout) { + stdcout << "testing polygon tiling star hole 2\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + // result == 0 8 -1 1 + data.push_back(Vertex45(Point(0, 8), -1, 1)); + // result == 0 8 1 -1 + data.push_back(Vertex45(Point(0, 8), 1, -1)); + // result == 4 0 1 1 + data.push_back(Vertex45(Point(4, 0), 1, 1)); + // result == 4 0 2 1 + data.push_back(Vertex45(Point(4, 0), 2, 1)); + // result == 4 4 2 -1 + data.push_back(Vertex45(Point(4, 4), 2, -1)); + // result == 4 4 -1 -1 + data.push_back(Vertex45(Point(4, 4), -1, -1)); + // result == 4 12 1 1 + data.push_back(Vertex45(Point(4, 12), 1, 1)); + // result == 4 12 2 1 + data.push_back(Vertex45(Point(4, 12), 2, 1)); + // result == 4 16 2 -1 + data.push_back(Vertex45(Point(4, 16), 2, 1)); + // result == 4 16 -1 -1 + data.push_back(Vertex45(Point(4, 16), -1, -1)); + // result == 6 2 1 -1 + data.push_back(Vertex45(Point(6, 2), 1, -1)); + // result == 6 14 -1 1 + data.push_back(Vertex45(Point(6, 14), -1, 1)); + // result == 6 2 -1 1 + data.push_back(Vertex45(Point(6, 2), -1, 1)); + // result == 6 14 1 -1 + data.push_back(Vertex45(Point(6, 14), 1, -1)); + // result == 8 0 -1 -1 + data.push_back(Vertex45(Point(8, 0), -1, -1)); + // result == 8 0 2 -1 + data.push_back(Vertex45(Point(8, 0), 2, -1)); + // result == 8 4 2 1 + data.push_back(Vertex45(Point(8, 4), 2, 1)); + // result == 8 4 1 1 + data.push_back(Vertex45(Point(8, 4), 1, 1)); + // result == 8 12 -1 -1 + data.push_back(Vertex45(Point(8, 12), -1, -1)); + // result == 8 12 2 -1 + data.push_back(Vertex45(Point(8, 12), 2, -1)); + // result == 8 16 2 1 + data.push_back(Vertex45(Point(8, 16), 2, 1)); + // result == 8 16 1 1 + data.push_back(Vertex45(Point(8, 16), 1, 1)); + // result == 12 8 1 -1 + data.push_back(Vertex45(Point(12, 8), 1, -1)); + // result == 12 8 -1 1 + data.push_back(Vertex45(Point(12, 8), -1, 1)); + + data.push_back(Vertex45(Point(6, 4), 1, -1)); + data.push_back(Vertex45(Point(6, 4), 2, -1)); + data.push_back(Vertex45(Point(6, 12), -1, 1)); + data.push_back(Vertex45(Point(6, 12), 2, 1)); + data.push_back(Vertex45(Point(10, 8), -1, -1)); + data.push_back(Vertex45(Point(10, 8), 1, 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + + template + static inline bool testPolygon45Tiling(stream_type& stdcout) { + stdcout << "testing polygon tiling\n"; + Polygon45Tiling pf; + std::vector polys; + std::vector data; + + data.push_back(Vertex45(Point(0, 0), 0, 1)); + data.push_back(Vertex45(Point(0, 0), 2, 1)); + data.push_back(Vertex45(Point(0, 100), 2, -1)); + data.push_back(Vertex45(Point(0, 100), 0, -1)); + data.push_back(Vertex45(Point(100, 0), 0, -1)); + data.push_back(Vertex45(Point(100, 0), 2, -1)); + data.push_back(Vertex45(Point(100, 100), 2, 1)); + data.push_back(Vertex45(Point(100, 100), 0, 1)); + + data.push_back(Vertex45(Point(2, 2), 0, -1)); + data.push_back(Vertex45(Point(2, 2), 2, -1)); + data.push_back(Vertex45(Point(2, 10), 2, 1)); + data.push_back(Vertex45(Point(2, 10), 0, 1)); + data.push_back(Vertex45(Point(10, 2), 0, 1)); + data.push_back(Vertex45(Point(10, 2), 2, 1)); + data.push_back(Vertex45(Point(10, 10), 2, -1)); + data.push_back(Vertex45(Point(10, 10), 0, -1)); + + data.push_back(Vertex45(Point(2, 12), 0, -1)); + data.push_back(Vertex45(Point(2, 12), 2, -1)); + data.push_back(Vertex45(Point(2, 22), 2, 1)); + data.push_back(Vertex45(Point(2, 22), 0, 1)); + data.push_back(Vertex45(Point(10, 12), 0, 1)); + data.push_back(Vertex45(Point(10, 12), 2, 1)); + data.push_back(Vertex45(Point(10, 22), 2, -1)); + data.push_back(Vertex45(Point(10, 22), 0, -1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon tiling\n"; + return true; + } + }; + + template + class PolyLine45HoleData { + public: + typedef typename polygon_45_formation::ActiveTail45 ActiveTail45; + typedef typename ActiveTail45::iterator iterator; + + typedef polygon_45_concept geometry_type; + typedef Unit coordinate_type; + typedef point_data Point; + typedef Point point_type; + // typedef iterator_points_to_compact compact_iterator_type; + typedef iterator iterator_type; + typedef typename coordinate_traits::area_type area_type; + + inline PolyLine45HoleData() : p_(0) {} + inline PolyLine45HoleData(ActiveTail45* p) : p_(p) {} + //use default copy and assign + inline iterator begin() const { return p_->getTail()->begin(); } + inline iterator end() const { return p_->getTail()->end(); } + inline std::size_t size() const { return 0; } + private: + ActiveTail45* p_; + }; + + template + class PolyLine45PolygonData { + public: + typedef typename polygon_45_formation::ActiveTail45 ActiveTail45; + typedef typename ActiveTail45::iterator iterator; + typedef PolyLine45HoleData holeType; + + typedef polygon_45_with_holes_concept geometry_type; + typedef Unit coordinate_type; + typedef point_data Point; + typedef Point point_type; + // typedef iterator_points_to_compact compact_iterator_type; + typedef iterator iterator_type; + typedef holeType hole_type; + typedef typename coordinate_traits::area_type area_type; + class iteratorHoles { + private: + typename ActiveTail45::iteratorHoles itr_; + public: + typedef PolyLine45HoleData holeType; + typedef holeType value_type; + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + typedef const value_type* pointer; //immutable + typedef const value_type& reference; //immutable + inline iteratorHoles() : itr_() {} + inline iteratorHoles(typename ActiveTail45::iteratorHoles itr) : itr_(itr) {} + inline iteratorHoles(const iteratorHoles& that) : itr_(that.itr_) {} + inline iteratorHoles& operator=(const iteratorHoles& that) { + itr_ = that.itr_; + return *this; + } + inline bool operator==(const iteratorHoles& that) { return itr_ == that.itr_; } + inline bool operator!=(const iteratorHoles& that) { return itr_ != that.itr_; } + inline iteratorHoles& operator++() { + ++itr_; + return *this; + } + inline const iteratorHoles operator++(int) { + iteratorHoles tmp = *this; + ++(*this); + return tmp; + } + inline holeType operator*() { + return *itr_; + } + }; + typedef iteratorHoles iterator_holes_type; + + + inline PolyLine45PolygonData() : p_(0) {} + inline PolyLine45PolygonData(ActiveTail45* p) : p_(p) {} + //use default copy and assign + inline iterator begin() const { return p_->getTail()->begin(); } + inline iterator end() const { return p_->getTail()->end(); } + inline iteratorHoles begin_holes() const { return iteratorHoles(p_->getHoles().begin()); } + inline iteratorHoles end_holes() const { return iteratorHoles(p_->getHoles().end()); } + inline ActiveTail45* yield() { return p_; } + //stub out these four required functions that will not be used but are needed for the interface + inline std::size_t size_holes() const { return 0; } + inline std::size_t size() const { return 0; } + private: + ActiveTail45* p_; + }; + + template + struct PolyLineByConcept { typedef PolyLine45PolygonData type; }; + template + struct PolyLineByConcept { typedef PolyLine45PolygonData type; }; + template + struct PolyLineByConcept { typedef PolyLine45HoleData type; }; + template + struct PolyLineByConcept { typedef PolyLine45HoleData type; }; + + template + struct geometry_concept > { typedef polygon_45_with_holes_concept type; }; + template + struct geometry_concept > { typedef polygon_45_concept type; }; + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_set_view.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_set_view.hpp new file mode 100644 index 0000000..a0dc046 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_set_view.hpp @@ -0,0 +1,380 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_SET_VIEW_HPP +#define BOOST_POLYGON_POLYGON_45_SET_VIEW_HPP +namespace boost { namespace polygon{ + + template + class polygon_45_set_view; + + template + struct polygon_45_set_traits > { + typedef typename polygon_45_set_view::coordinate_type coordinate_type; + typedef typename polygon_45_set_view::iterator_type iterator_type; + typedef typename polygon_45_set_view::operator_arg_type operator_arg_type; + + static inline iterator_type begin(const polygon_45_set_view& polygon_45_set); + static inline iterator_type end(const polygon_45_set_view& polygon_45_set); + + template + static inline void set(polygon_45_set_view& polygon_45_set, + input_iterator_type input_begin, input_iterator_type input_end); + + static inline bool clean(const polygon_45_set_view& polygon_45_set); + + }; + + template + struct compute_45_set_value { + static + void value(value_type& output_, const ltype& lvalue_, const rtype& rvalue_) { + output_.set(polygon_45_set_traits::begin(lvalue_), + polygon_45_set_traits::end(lvalue_)); + value_type rinput_; + rinput_.set(polygon_45_set_traits::begin(rvalue_), + polygon_45_set_traits::end(rvalue_)); +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op_type == 0) + output_ |= rinput_; + else if(op_type == 1) + output_ &= rinput_; + else if(op_type == 2) + output_ ^= rinput_; + else + output_ -= rinput_; +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + } + }; + + template + struct compute_45_set_value, op_type> { + static + void value(value_type& output_, const ltype& lvalue_, const polygon_45_set_data& rvalue_) { + output_.set(polygon_45_set_traits::begin(lvalue_), + polygon_45_set_traits::end(lvalue_)); +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op_type == 0) + output_ |= rvalue_; + else if(op_type == 1) + output_ &= rvalue_; + else if(op_type == 2) + output_ ^= rvalue_; + else + output_ -= rvalue_; +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + } + }; + + template + class polygon_45_set_view { + public: + typedef typename polygon_45_set_traits::coordinate_type coordinate_type; + typedef polygon_45_set_data value_type; + typedef typename value_type::iterator_type iterator_type; + typedef polygon_45_set_view operator_arg_type; + private: + const ltype& lvalue_; + const rtype& rvalue_; + mutable value_type output_; + mutable bool evaluated_; + + polygon_45_set_view& operator=(const polygon_45_set_view&); + public: + polygon_45_set_view(const ltype& lvalue, + const rtype& rvalue ) : + lvalue_(lvalue), rvalue_(rvalue), output_(), evaluated_(false) {} + + // get iterator to begin vertex data + public: + const value_type& value() const { + if(!evaluated_) { + evaluated_ = true; + compute_45_set_value::value(output_, lvalue_, rvalue_); + } + return output_; + } + public: + iterator_type begin() const { return value().begin(); } + iterator_type end() const { return value().end(); } + + bool dirty() const { return value().dirty(); } //result of a boolean is clean + bool sorted() const { return value().sorted(); } //result of a boolean is sorted + + // template + // void set(input_iterator_type input_begin, input_iterator_type input_end, + // orientation_2d orient) const { + // orient_ = orient; + // output_.clear(); + // output_.insert(output_.end(), input_begin, input_end); + // polygon_sort(output_.begin(), output_.end()); + // } + }; + + template + typename polygon_45_set_traits >::iterator_type + polygon_45_set_traits >:: + begin(const polygon_45_set_view& polygon_45_set) { + return polygon_45_set.begin(); + } + template + typename polygon_45_set_traits >::iterator_type + polygon_45_set_traits >:: + end(const polygon_45_set_view& polygon_45_set) { + return polygon_45_set.end(); + } + template + bool polygon_45_set_traits >:: + clean(const polygon_45_set_view& polygon_45_set) { + return polygon_45_set.value().clean(); } + + template + geometry_type_1& self_assignment_boolean_op_45(geometry_type_1& lvalue_, const geometry_type_2& rvalue_) { + typedef geometry_type_1 ltype; + typedef geometry_type_2 rtype; + typedef typename polygon_45_set_traits::coordinate_type coordinate_type; + typedef polygon_45_set_data value_type; + value_type output_; + value_type rinput_; + output_.set(polygon_45_set_traits::begin(lvalue_), + polygon_45_set_traits::end(lvalue_)); + rinput_.set(polygon_45_set_traits::begin(rvalue_), + polygon_45_set_traits::end(rvalue_)); +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op_type == 0) + output_ |= rinput_; + else if(op_type == 1) + output_ &= rinput_; + else if(op_type == 2) + output_ ^= rinput_; + else + output_ -= rinput_; +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + polygon_45_set_mutable_traits::set(lvalue_, output_.begin(), output_.end()); + return lvalue_; + } + + template + struct fracture_holes_option_by_type { + static const bool value = true; + }; + template <> + struct fracture_holes_option_by_type { + static const bool value = false; + }; + template <> + struct fracture_holes_option_by_type { + static const bool value = false; + }; + + template + struct geometry_concept > { typedef polygon_45_set_concept type; }; + + namespace operators { + struct y_ps45_b : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4< y_ps45_b, + typename is_polygon_45_or_90_set_type::type, + typename is_polygon_45_or_90_set_type::type, + typename is_either_polygon_45_set_type::type>::type, + polygon_45_set_view >::type + operator|(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_45_set_view + (lvalue, rvalue); + } + + struct y_ps45_p : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4< y_ps45_p, + typename gtl_if::type>::type, + typename gtl_if::type>::type, + typename gtl_if::type>::type>::type, + polygon_45_set_view >::type + operator+(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_45_set_view + (lvalue, rvalue); + } + + struct y_ps45_s : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4< y_ps45_s, typename is_polygon_45_or_90_set_type::type, + typename is_polygon_45_or_90_set_type::type, + typename is_either_polygon_45_set_type::type>::type, + polygon_45_set_view >::type + operator*(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_45_set_view + (lvalue, rvalue); + } + + struct y_ps45_a : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4< y_ps45_a, typename is_polygon_45_or_90_set_type::type, + typename is_polygon_45_or_90_set_type::type, + typename is_either_polygon_45_set_type::type>::type, + polygon_45_set_view >::type + operator&(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_45_set_view + (lvalue, rvalue); + } + + struct y_ps45_x : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4< y_ps45_x, typename is_polygon_45_or_90_set_type::type, + typename is_polygon_45_or_90_set_type::type, + typename is_either_polygon_45_set_type::type>::type, + polygon_45_set_view >::type + operator^(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_45_set_view + (lvalue, rvalue); + } + + struct y_ps45_m : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4< y_ps45_m, + typename gtl_if::type>::type, + typename gtl_if::type>::type, + typename gtl_if::type>::type>::type, + polygon_45_set_view >::type + operator-(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_45_set_view + (lvalue, rvalue); + } + + struct y_ps45_pe : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4::type, gtl_yes, + typename is_polygon_45_or_90_set_type::type>::type, + geometry_type_1>::type & + operator+=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op_45(lvalue, rvalue); + } + + struct y_ps45_be : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename is_polygon_45_or_90_set_type::type>::type, + geometry_type_1>::type & + operator|=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op_45(lvalue, rvalue); + } + + struct y_ps45_se : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps45_se, + typename is_mutable_polygon_45_set_type::type, + typename is_polygon_45_or_90_set_type::type>::type, + geometry_type_1>::type & + operator*=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op_45(lvalue, rvalue); + } + + struct y_ps45_ae : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename is_polygon_45_or_90_set_type::type>::type, + geometry_type_1>::type & + operator&=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op_45(lvalue, rvalue); + } + + struct y_ps45_xe : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type, + typename is_polygon_45_or_90_set_type::type>::type, + geometry_type_1>::type & + operator^=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op_45(lvalue, rvalue); + } + + struct y_ps45_me : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename is_polygon_45_or_90_set_type::type>::type, + geometry_type_1>::type & + operator-=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op_45(lvalue, rvalue); + } + + struct y_ps45_rpe : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps45_rpe, typename is_mutable_polygon_45_set_type::type, + typename gtl_same_type::type, + coordinate_concept>::type>::type, + geometry_type_1>::type & + operator+=(geometry_type_1& lvalue, coordinate_type_1 rvalue) { + return resize(lvalue, rvalue); + } + + struct y_ps45_rme : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_same_type::type, + coordinate_concept>::type>::type, + geometry_type_1>::type & + operator-=(geometry_type_1& lvalue, coordinate_type_1 rvalue) { + return resize(lvalue, -rvalue); + } + + struct y_ps45_rp : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_same_type::type, + coordinate_concept>::type> + ::type, geometry_type_1>::type + operator+(const geometry_type_1& lvalue, coordinate_type_1 rvalue) { + geometry_type_1 retval(lvalue); + retval += rvalue; + return retval; + } + + struct y_ps45_rm : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_same_type::type, + coordinate_concept>::type> + ::type, geometry_type_1>::type + operator-(const geometry_type_1& lvalue, coordinate_type_1 rvalue) { + geometry_type_1 retval(lvalue); + retval -= rvalue; + return retval; + } + } +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_touch.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_touch.hpp new file mode 100644 index 0000000..90717e1 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_45_touch.hpp @@ -0,0 +1,238 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_TOUCH_HPP +#define BOOST_POLYGON_POLYGON_45_TOUCH_HPP +namespace boost { namespace polygon{ + + template + struct polygon_45_touch { + + typedef point_data Point; + typedef typename coordinate_traits::manhattan_area_type LongUnit; + + template + static inline void merge_property_maps(property_map& mp, const property_map& mp2, bool subtract = false) { + property_map newmp; + newmp.reserve(mp.size() + mp2.size()); + std::size_t i = 0; + std::size_t j = 0; + while(i != mp.size() && j != mp2.size()) { + if(mp[i].first < mp2[j].first) { + newmp.push_back(mp[i]); + ++i; + } else if(mp[i].first > mp2[j].first) { + newmp.push_back(mp2[j]); + if(subtract) newmp.back().second *= -1; + ++j; + } else { + int count = mp[i].second; + if(subtract) count -= mp2[j].second; + else count += mp2[j].second; + if(count) { + newmp.push_back(mp[i]); + newmp.back().second = count; + } + ++i; + ++j; + } + } + while(i != mp.size()) { + newmp.push_back(mp[i]); + ++i; + } + while(j != mp2.size()) { + newmp.push_back(mp2[j]); + if(subtract) newmp.back().second *= -1; + ++j; + } + mp.swap(newmp); + } + + class CountTouch { + public: + inline CountTouch() : counts() {} + //inline CountTouch(int count) { counts[0] = counts[1] = count; } + //inline CountTouch(int count1, int count2) { counts[0] = count1; counts[1] = count2; } + inline CountTouch(const CountTouch& count) : counts(count.counts) {} + inline bool operator==(const CountTouch& count) const { return counts == count.counts; } + inline bool operator!=(const CountTouch& count) const { return !((*this) == count); } + //inline CountTouch& operator=(int count) { counts[0] = counts[1] = count; return *this; } + inline CountTouch& operator=(const CountTouch& count) { counts = count.counts; return *this; } + inline int& operator[](int index) { + std::vector >::iterator itr = + std::lower_bound(counts.begin(), counts.end(), + std::make_pair(index, int(0))); + if(itr != counts.end() && itr->first == index) { + return itr->second; + } + itr = counts.insert(itr, std::make_pair(index, int(0))); + return itr->second; + } +// inline int operator[](int index) const { +// std::vector >::const_iterator itr = counts.begin(); +// for( ; itr != counts.end() && itr->first <= index; ++itr) { +// if(itr->first == index) { +// return itr->second; +// } +// } +// return 0; +// } + inline CountTouch& operator+=(const CountTouch& count){ + merge_property_maps(counts, count.counts, false); + return *this; + } + inline CountTouch& operator-=(const CountTouch& count){ + merge_property_maps(counts, count.counts, true); + return *this; + } + inline CountTouch operator+(const CountTouch& count) const { + return CountTouch(*this)+=count; + } + inline CountTouch operator-(const CountTouch& count) const { + return CountTouch(*this)-=count; + } + inline CountTouch invert() const { + CountTouch retval; + retval -= *this; + return retval; + } + std::vector > counts; + }; + + typedef std::pair > >, std::map > > map_graph_o; + typedef std::pair > >, std::vector > > vector_graph_o; + + template + static void process_previous_x(cT& output) { + std::map >& y_prop_map = output.first.second; + for(typename std::map >::iterator itr = y_prop_map.begin(); + itr != y_prop_map.end(); ++itr) { + for(std::set::iterator inner_itr = itr->second.begin(); + inner_itr != itr->second.end(); ++inner_itr) { + std::set& output_edges = (*(output.second))[*inner_itr]; + std::set::iterator inner_inner_itr = inner_itr; + ++inner_inner_itr; + for( ; inner_inner_itr != itr->second.end(); ++inner_inner_itr) { + output_edges.insert(output_edges.end(), *inner_inner_itr); + std::set& output_edges_2 = (*(output.second))[*inner_inner_itr]; + output_edges_2.insert(output_edges_2.end(), *inner_itr); + } + } + } + y_prop_map.clear(); + } + + struct touch_45_output_functor { + template + void operator()(cT& output, const CountTouch& count1, const CountTouch& count2, + const Point& pt, int , direction_1d ) { + Unit& x = output.first.first; + std::map >& y_prop_map = output.first.second; + if(pt.x() != x) process_previous_x(output); + x = pt.x(); + std::set& output_set = y_prop_map[pt.y()]; + for(std::vector >::const_iterator itr1 = count1.counts.begin(); + itr1 != count1.counts.end(); ++itr1) { + if(itr1->second > 0) { + output_set.insert(output_set.end(), itr1->first); + } + } + for(std::vector >::const_iterator itr2 = count2.counts.begin(); + itr2 != count2.counts.end(); ++itr2) { + if(itr2->second > 0) { + output_set.insert(output_set.end(), itr2->first); + } + } + } + }; + typedef typename std::pair::template Scan45CountT > Vertex45Compact; + typedef std::vector TouchSetData; + + struct lessVertex45Compact { + bool operator()(const Vertex45Compact& l, const Vertex45Compact& r) { + return l.first < r.first; + } + }; + +// template +// static void print_tsd(TSD& tsd) { +// for(std::size_t i = 0; i < tsd.size(); ++i) { +// std::cout << tsd[i].first << ": "; +// for(unsigned int r = 0; r < 4; ++r) { +// std::cout << r << " { "; +// for(std::vector >::iterator itr = tsd[i].second[r].counts.begin(); +// itr != tsd[i].second[r].counts.end(); ++itr) { +// std::cout << itr->first << "," << itr->second << " "; +// } std::cout << "} "; +// } +// } std::cout << std::endl; +// } + +// template +// static void print_scanline(T& t) { +// for(typename T::iterator itr = t.begin(); itr != t.end(); ++itr) { +// std::cout << itr->x << "," << itr->y << " " << itr->rise << " "; +// for(std::vector >::iterator itr2 = itr->count.counts.begin(); +// itr2 != itr->count.counts.end(); ++itr2) { +// std::cout << itr2->first << ":" << itr2->second << " "; +// } std::cout << std::endl; +// } +// } + + template + static void performTouch(graph_type& graph, TouchSetData& tsd) { + + polygon_sort(tsd.begin(), tsd.end(), lessVertex45Compact()); + typedef std::vector::template Scan45CountT > > TSD; + TSD tsd_; + tsd_.reserve(tsd.size()); + for(typename TouchSetData::iterator itr = tsd.begin(); itr != tsd.end(); ) { + typename TouchSetData::iterator itr2 = itr; + ++itr2; + for(; itr2 != tsd.end() && itr2->first == itr->first; ++itr2) { + (itr->second) += (itr2->second); //accumulate + } + tsd_.push_back(std::make_pair(itr->first, itr->second)); + itr = itr2; + } + std::pair > >, graph_type*> output + (std::make_pair(std::make_pair((std::numeric_limits::max)(), std::map >()), &graph)); + typename boolean_op_45::template Scan45 scanline; + for(typename TSD::iterator itr = tsd_.begin(); itr != tsd_.end(); ) { + typename TSD::iterator itr2 = itr; + ++itr2; + while(itr2 != tsd_.end() && itr2->first.x() == itr->first.x()) { + ++itr2; + } + scanline.scan(output, itr, itr2); + itr = itr2; + } + process_previous_x(output); + } + + template + static void populateTouchSetData(TouchSetData& tsd, iT begin, iT end, int nodeCount) { + for( ; begin != end; ++begin) { + Vertex45Compact vertex; + vertex.first = typename Vertex45Compact::first_type(begin->pt.x() * 2, begin->pt.y() * 2); + tsd.push_back(vertex); + for(unsigned int i = 0; i < 4; ++i) { + if(begin->count[i]) { + tsd.back().second[i][nodeCount] += begin->count[i]; + } + } + } + } + + }; + + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_set_view.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_set_view.hpp new file mode 100644 index 0000000..7e93b03 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_set_view.hpp @@ -0,0 +1,490 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_90_SET_VIEW_HPP +#define BOOST_POLYGON_POLYGON_90_SET_VIEW_HPP +namespace boost { namespace polygon{ + struct operator_provides_storage {}; + struct operator_requires_copy {}; + + template + inline void insert_into_view_arg(value_type& dest, const arg_type& arg, orientation_2d orient); + + template + class polygon_90_set_view; + + template + struct polygon_90_set_traits > { + typedef typename polygon_90_set_view::coordinate_type coordinate_type; + typedef typename polygon_90_set_view::iterator_type iterator_type; + typedef typename polygon_90_set_view::operator_arg_type operator_arg_type; + + static inline iterator_type begin(const polygon_90_set_view& polygon_set); + static inline iterator_type end(const polygon_90_set_view& polygon_set); + + static inline orientation_2d orient(const polygon_90_set_view& polygon_set); + + static inline bool clean(const polygon_90_set_view& polygon_set); + + static inline bool sorted(const polygon_90_set_view& polygon_set); + }; + + template + struct compute_90_set_value { + static + void value(value_type& output_, const ltype& lvalue_, const rtype& rvalue_, orientation_2d orient_) { + value_type linput_(orient_); + value_type rinput_(orient_); + orientation_2d orient_l = polygon_90_set_traits::orient(lvalue_); + orientation_2d orient_r = polygon_90_set_traits::orient(rvalue_); + //std::cout << "compute_90_set_value-0 orientations (left, right, out):\t" << orient_l.to_int() + // << "," << orient_r.to_int() << "," << orient_.to_int() << std::endl; + insert_into_view_arg(linput_, lvalue_, orient_l); + insert_into_view_arg(rinput_, rvalue_, orient_r); + output_.applyBooleanBinaryOp(linput_.begin(), linput_.end(), + rinput_.begin(), rinput_.end(), boolean_op::BinaryCount()); + } + }; + + template + struct compute_90_set_value, polygon_90_set_data, op_type> { + static + void value(value_type& output_, const polygon_90_set_data& lvalue_, + const polygon_90_set_data& rvalue_, orientation_2d orient_) { + orientation_2d orient_l = lvalue_.orient(); + orientation_2d orient_r = rvalue_.orient(); + value_type linput_(orient_); + value_type rinput_(orient_); + //std::cout << "compute_90_set_value-1 orientations (left, right, out):\t" << orient_l.to_int() + // << "," << orient_r.to_int() << "," << orient_.to_int() << std::endl; + if((orient_ == orient_l) && (orient_== orient_r)){ // assume that most of the time this condition is met + lvalue_.sort(); + rvalue_.sort(); + output_.applyBooleanBinaryOp(lvalue_.begin(), lvalue_.end(), + rvalue_.begin(), rvalue_.end(), boolean_op::BinaryCount()); + }else if((orient_ != orient_l) && (orient_!= orient_r)){ // both the orientations are not equal to input + // easier way is to ignore the input orientation and use the input data's orientation, but not done so + insert_into_view_arg(linput_, lvalue_, orient_l); + insert_into_view_arg(rinput_, rvalue_, orient_r); + output_.applyBooleanBinaryOp(linput_.begin(), linput_.end(), + rinput_.begin(), rinput_.end(), boolean_op::BinaryCount()); + }else if(orient_ != orient_l){ // left hand side orientation is different + insert_into_view_arg(linput_, lvalue_, orient_l); + rvalue_.sort(); + output_.applyBooleanBinaryOp(linput_.begin(), linput_.end(), + rvalue_.begin(), rvalue_.end(), boolean_op::BinaryCount()); + } else if(orient_ != orient_r){ // right hand side orientation is different + insert_into_view_arg(rinput_, rvalue_, orient_r); + lvalue_.sort(); + output_.applyBooleanBinaryOp(lvalue_.begin(), lvalue_.end(), + rinput_.begin(), rinput_.end(), boolean_op::BinaryCount()); + } + } + }; + + template + struct compute_90_set_value, rtype, op_type> { + static + void value(value_type& output_, const polygon_90_set_data& lvalue_, + const rtype& rvalue_, orientation_2d orient_) { + value_type rinput_(orient_); + lvalue_.sort(); + orientation_2d orient_r = polygon_90_set_traits::orient(rvalue_); + //std::cout << "compute_90_set_value-2 orientations (right, out):\t" << orient_r.to_int() + // << "," << orient_.to_int() << std::endl; + insert_into_view_arg(rinput_, rvalue_, orient_r); + output_.applyBooleanBinaryOp(lvalue_.begin(), lvalue_.end(), + rinput_.begin(), rinput_.end(), boolean_op::BinaryCount()); + } + }; + + template + struct compute_90_set_value, op_type> { + static + void value(value_type& output_, const ltype& lvalue_, + const polygon_90_set_data& rvalue_, orientation_2d orient_) { + value_type linput_(orient_); + orientation_2d orient_l = polygon_90_set_traits::orient(lvalue_); + insert_into_view_arg(linput_, lvalue_, orient_l); + rvalue_.sort(); + //std::cout << "compute_90_set_value-3 orientations (left, out):\t" << orient_l.to_int() + // << "," << orient_.to_int() << std::endl; + + output_.applyBooleanBinaryOp(linput_.begin(), linput_.end(), + rvalue_.begin(), rvalue_.end(), boolean_op::BinaryCount()); + } + }; + + template + class polygon_90_set_view { + public: + typedef typename polygon_90_set_traits::coordinate_type coordinate_type; + typedef polygon_90_set_data value_type; + typedef typename value_type::iterator_type iterator_type; + typedef polygon_90_set_view operator_arg_type; + private: + const ltype& lvalue_; + const rtype& rvalue_; + orientation_2d orient_; + op_type op_; + mutable value_type output_; + mutable bool evaluated_; + polygon_90_set_view& operator=(const polygon_90_set_view&); + public: + polygon_90_set_view(const ltype& lvalue, + const rtype& rvalue, + orientation_2d orient, + op_type op) : + lvalue_(lvalue), rvalue_(rvalue), orient_(orient), op_(op), output_(orient), evaluated_(false) {} + + // get iterator to begin vertex data + private: + const value_type& value() const { + if(!evaluated_) { + evaluated_ = true; + compute_90_set_value::value(output_, lvalue_, rvalue_, orient_); + } + return output_; + } + public: + iterator_type begin() const { return value().begin(); } + iterator_type end() const { return value().end(); } + + orientation_2d orient() const { return orient_; } + bool dirty() const { return false; } //result of a boolean is clean + bool sorted() const { return true; } //result of a boolean is sorted + +// template +// void set(input_iterator_type input_begin, input_iterator_type input_end, +// orientation_2d orient) const { +// orient_ = orient; +// output_.clear(); +// output_.insert(output_.end(), input_begin, input_end); +// polygon_sort(output_.begin(), output_.end()); +// } + void sort() const {} //is always sorted + }; + + template + struct geometry_concept > { + typedef polygon_90_set_concept type; + }; + + template + typename polygon_90_set_traits >::iterator_type + polygon_90_set_traits >:: + begin(const polygon_90_set_view& polygon_set) { + return polygon_set.begin(); + } + template + typename polygon_90_set_traits >::iterator_type + polygon_90_set_traits >:: + end(const polygon_90_set_view& polygon_set) { + return polygon_set.end(); + } +// template +// template +// void polygon_90_set_traits >:: +// set(polygon_90_set_view& polygon_set, +// input_iterator_type input_begin, input_iterator_type input_end, +// orientation_2d orient) { +// polygon_set.set(input_begin, input_end, orient); +// } + template + orientation_2d polygon_90_set_traits >:: + orient(const polygon_90_set_view& polygon_set) { + return polygon_set.orient(); } + template + bool polygon_90_set_traits >:: + clean(const polygon_90_set_view& polygon_set) { + return !polygon_set.dirty(); } + template + bool polygon_90_set_traits >:: + sorted(const polygon_90_set_view& polygon_set) { + return polygon_set.sorted(); } + + template + inline void insert_into_view_arg(value_type& dest, const arg_type& arg, orientation_2d orient) { + typedef typename polygon_90_set_traits::iterator_type literator; + literator itr1, itr2; + itr1 = polygon_90_set_traits::begin(arg); + itr2 = polygon_90_set_traits::end(arg); + dest.insert(itr1, itr2, orient); + dest.sort(); + } + + template + template + inline polygon_90_set_data& polygon_90_set_data::operator=(const polygon_90_set_view& that) { + set(that.begin(), that.end(), that.orient()); + dirty_ = false; + unsorted_ = false; + return *this; + } + + template + template + inline polygon_90_set_data::polygon_90_set_data(const polygon_90_set_view& that) : + orient_(that.orient()), data_(that.begin(), that.end()), dirty_(false), unsorted_(false) {} + + template + struct self_assign_operator_lvalue { + typedef geometry_type_1& type; + }; + + template + struct by_value_binary_operator { + typedef type_1 type; + }; + + template + geometry_type_1& self_assignment_boolean_op(geometry_type_1& lvalue_, const geometry_type_2& rvalue_) { + typedef geometry_type_1 ltype; + typedef geometry_type_2 rtype; + typedef typename polygon_90_set_traits::coordinate_type coordinate_type; + typedef polygon_90_set_data value_type; + orientation_2d orient_ = polygon_90_set_traits::orient(lvalue_); + //BM: rvalue_ data set may have its own orientation for scanline + orientation_2d orient_r = polygon_90_set_traits::orient(rvalue_); + //std::cout << "self-assignment boolean-op (left, right, out):\t" << orient_.to_int() + // << "," << orient_r.to_int() << "," << orient_.to_int() << std::endl; + value_type linput_(orient_); + // BM: the rinput_ set's (that stores the rvalue_ dataset polygons) scanline orientation is *forced* + // to be same as linput + value_type rinput_(orient_); + //BM: The output dataset's scanline orient is set as equal to first input dataset's (lvalue_) orientation + value_type output_(orient_); + insert_into_view_arg(linput_, lvalue_, orient_); + // BM: The last argument orient_r is the user initialized scanline orientation for rvalue_ data set. + // But since rinput (see above) is initialized to scanline orientation consistent with the lvalue_ + // data set, this insertion operation will change the incoming rvalue_ dataset's scanline orientation + insert_into_view_arg(rinput_, rvalue_, orient_r); + // BM: boolean operation and output uses lvalue_ dataset's scanline orientation. + output_.applyBooleanBinaryOp(linput_.begin(), linput_.end(), + rinput_.begin(), rinput_.end(), boolean_op::BinaryCount()); + polygon_90_set_mutable_traits::set(lvalue_, output_.begin(), output_.end(), orient_); + return lvalue_; + } + + namespace operators { + struct y_ps90_b : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps90_b, + typename is_polygon_90_set_type::type, + typename is_polygon_90_set_type::type>::type, + polygon_90_set_view >::type + operator|(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_90_set_view + (lvalue, rvalue, + polygon_90_set_traits::orient(lvalue), + boolean_op::BinaryOr()); + } + + struct y_ps90_p : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3< y_ps90_p, + typename gtl_if::type>::type, + typename gtl_if::type>::type>::type, + polygon_90_set_view >::type + operator+(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_90_set_view + (lvalue, rvalue, + polygon_90_set_traits::orient(lvalue), + boolean_op::BinaryOr()); + } + + struct y_ps90_s : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps90_s, + typename is_polygon_90_set_type::type, + typename is_polygon_90_set_type::type>::type, + polygon_90_set_view >::type + operator*(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_90_set_view + (lvalue, rvalue, + polygon_90_set_traits::orient(lvalue), + boolean_op::BinaryAnd()); + } + + struct y_ps90_a : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps90_a, + typename is_polygon_90_set_type::type, + typename is_polygon_90_set_type::type>::type, + polygon_90_set_view >::type + operator&(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_90_set_view + (lvalue, rvalue, + polygon_90_set_traits::orient(lvalue), + boolean_op::BinaryAnd()); + } + + struct y_ps90_x : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps90_x, + typename is_polygon_90_set_type::type, + typename is_polygon_90_set_type::type>::type, + polygon_90_set_view >::type + operator^(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_90_set_view + (lvalue, rvalue, + polygon_90_set_traits::orient(lvalue), + boolean_op::BinaryXor()); + } + + struct y_ps90_m : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps90_m, + typename gtl_if::type>::type, + typename gtl_if::type>::type>::type, + polygon_90_set_view >::type + operator-(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_90_set_view + (lvalue, rvalue, + polygon_90_set_traits::orient(lvalue), + boolean_op::BinaryNot()); + } + + struct y_ps90_pe : gtl_yes {}; + + template + typename enable_if< typename gtl_and< y_ps90_pe, typename is_polygon_90_set_type::type>::type, + polygon_90_set_data >::type & + operator+=(polygon_90_set_data& lvalue, const geometry_type_2& rvalue) { + lvalue.insert(polygon_90_set_traits::begin(rvalue), polygon_90_set_traits::end(rvalue), + polygon_90_set_traits::orient(rvalue)); + return lvalue; + } + + struct y_ps90_be : gtl_yes {}; + // + template + typename enable_if< typename gtl_and< y_ps90_be, typename is_polygon_90_set_type::type>::type, + polygon_90_set_data >::type & + operator|=(polygon_90_set_data& lvalue, const geometry_type_2& rvalue) { + return lvalue += rvalue; + } + + struct y_ps90_pe2 : gtl_yes {}; + + //normal self assignment boolean operations + template + typename enable_if< typename gtl_and_3< y_ps90_pe2, typename is_mutable_polygon_90_set_type::type, + typename is_polygon_90_set_type::type>::type, + geometry_type_1>::type & + operator+=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct y_ps90_be2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename is_polygon_90_set_type::type>::type, + geometry_type_1>::type & + operator|=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct y_ps90_se : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename is_polygon_90_set_type::type>::type, + geometry_type_1>::type & + operator*=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct y_ps90_ae : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename is_polygon_90_set_type::type>::type, + geometry_type_1>::type & + operator&=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct y_ps90_xe : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename is_polygon_90_set_type::type>::type, + geometry_type_1>::type & + operator^=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct y_ps90_me : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps90_me, typename is_mutable_polygon_90_set_type::type, + typename is_polygon_90_set_type::type>::type, + geometry_type_1>::type & + operator-=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct y_ps90_rpe : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename gtl_same_type::type, coordinate_concept>::type>::type, + geometry_type_1>::type & + operator+=(geometry_type_1& lvalue, coordinate_type_1 rvalue) { + return resize(lvalue, rvalue); + } + + struct y_ps90_rme : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type, + typename gtl_same_type::type, coordinate_concept>::type>::type, + geometry_type_1>::type & + operator-=(geometry_type_1& lvalue, coordinate_type_1 rvalue) { + return resize(lvalue, -rvalue); + } + + struct y_ps90_rp : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_if::type, coordinate_concept>::type>::type>::type, + geometry_type_1>::type + operator+(const geometry_type_1& lvalue, coordinate_type_1 rvalue) { + geometry_type_1 retval(lvalue); + retval += rvalue; + return retval; + } + + struct y_ps90_rm : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_if::type, coordinate_concept>::type>::type>::type, + geometry_type_1>::type + operator-(const geometry_type_1& lvalue, coordinate_type_1 rvalue) { + geometry_type_1 retval(lvalue); + retval -= rvalue; + return retval; + } + } +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_touch.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_touch.hpp new file mode 100644 index 0000000..77a516b --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_90_touch.hpp @@ -0,0 +1,418 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_90_TOUCH_HPP +#define BOOST_POLYGON_POLYGON_90_TOUCH_HPP +namespace boost { namespace polygon{ + + template + struct touch_90_operation { + typedef interval_data Interval; + + class TouchScanEvent { + private: + typedef std::map > EventData; + EventData eventData_; + public: + + // The TouchScanEvent::iterator is a lazy algorithm that accumulates + // polygon ids in a set as it is incremented through the + // scan event data structure. + // The iterator provides a forward iterator semantic only. + class iterator { + private: + typename EventData::const_iterator itr_; + std::pair > ivlIds_; + bool incremented_; + public: + inline iterator() : itr_(), ivlIds_(), incremented_(false) {} + inline iterator(typename EventData::const_iterator itr, + Unit prevPos, Unit curPos, const std::set& ivlIds) : itr_(itr), ivlIds_(), incremented_(false) { + ivlIds_.second = ivlIds; + ivlIds_.first = Interval(prevPos, curPos); + } + inline iterator(const iterator& that) : itr_(), ivlIds_(), incremented_(false) { (*this) = that; } + inline iterator& operator=(const iterator& that) { + itr_ = that.itr_; + ivlIds_.first = that.ivlIds_.first; + ivlIds_.second = that.ivlIds_.second; + incremented_ = that.incremented_; + return *this; + } + inline bool operator==(const iterator& that) { return itr_ == that.itr_; } + inline bool operator!=(const iterator& that) { return itr_ != that.itr_; } + inline iterator& operator++() { + //std::cout << "increment\n"; + //std::cout << "state\n"; + //for(std::set::iterator itr = ivlIds_.second.begin(); itr != ivlIds_.second.end(); ++itr) { + // std::cout << (*itr) << " "; + //} std::cout << std::endl; + //std::cout << "update\n"; + for(std::set::const_iterator itr = (*itr_).second.begin(); + itr != (*itr_).second.end(); ++itr) { + //std::cout << (*itr) << " "; + std::set::iterator lb = ivlIds_.second.find(*itr); + if(lb != ivlIds_.second.end()) { + ivlIds_.second.erase(lb); + } else { + ivlIds_.second.insert(*itr); + } + } + //std::cout << std::endl; + //std::cout << "new state\n"; + //for(std::set::iterator itr = ivlIds_.second.begin(); itr != ivlIds_.second.end(); ++itr) { + // std::cout << (*itr) << " "; + //} std::cout << std::endl; + ++itr_; + //ivlIds_.first = Interval(ivlIds_.first.get(HIGH), itr_->first); + incremented_ = true; + return *this; + } + inline const iterator operator++(int){ + iterator tmpItr(*this); + ++(*this); + return tmpItr; + } + inline std::pair >& operator*() { + if(incremented_) ivlIds_.first = Interval(ivlIds_.first.get(HIGH), itr_->first); + incremented_ = false; + if(ivlIds_.second.empty())(++(*this)); + if(incremented_) ivlIds_.first = Interval(ivlIds_.first.get(HIGH), itr_->first); + incremented_ = false; + return ivlIds_; } + }; + + inline TouchScanEvent() : eventData_() {} + template + inline TouchScanEvent(iT begin, iT end) : eventData_() { + for( ; begin != end; ++begin){ + insert(*begin); + } + } + inline TouchScanEvent(const TouchScanEvent& that) : eventData_(that.eventData_) {} + inline TouchScanEvent& operator=(const TouchScanEvent& that){ + eventData_ = that.eventData_; + return *this; + } + + //Insert an interval polygon id into the EventData + inline void insert(const std::pair& intervalId){ + insert(intervalId.first.low(), intervalId.second); + insert(intervalId.first.high(), intervalId.second); + } + + //Insert an position and polygon id into EventData + inline void insert(Unit pos, int id) { + typename EventData::iterator lb = eventData_.lower_bound(pos); + if(lb != eventData_.end() && lb->first == pos) { + std::set& mr (lb->second); + std::set::iterator mri = mr.find(id); + if(mri == mr.end()) { + mr.insert(id); + } else { + mr.erase(id); + } + } else { + lb = eventData_.insert(lb, std::pair >(pos, std::set())); + (*lb).second.insert(id); + } + } + + //merge this scan event with that by inserting its data + inline void insert(const TouchScanEvent& that){ + typename EventData::const_iterator itr; + for(itr = that.eventData_.begin(); itr != that.eventData_.end(); ++itr) { + eventData_[(*itr).first].insert(itr->second.begin(), itr->second.end()); + } + } + + //Get the begin iterator over event data + inline iterator begin() const { + //std::cout << "begin\n"; + if(eventData_.empty()) return end(); + typename EventData::const_iterator itr = eventData_.begin(); + Unit pos = itr->first; + const std::set& idr = itr->second; + ++itr; + return iterator(itr, pos, itr->first, idr); + } + + //Get the end iterator over event data + inline iterator end() const { return iterator(eventData_.end(), 0, 0, std::set()); } + + inline void clear() { eventData_.clear(); } + + inline Interval extents() const { + if(eventData_.empty()) return Interval(); + return Interval((*(eventData_.begin())).first, (*(eventData_.rbegin())).first); + } + }; + + //declaration of a map of scan events by coordinate value used to store all the + //polygon data for a single layer input into the scanline algorithm + typedef std::pair, std::map > TouchSetData; + + class TouchOp { + public: + typedef std::map > ScanData; + typedef std::pair > ElementType; + protected: + ScanData scanData_; + typename ScanData::iterator nextItr_; + public: + inline TouchOp () : scanData_(), nextItr_() { nextItr_ = scanData_.end(); } + inline TouchOp (const TouchOp& that) : scanData_(that.scanData_), nextItr_() { nextItr_ = scanData_.begin(); } + inline TouchOp& operator=(const TouchOp& that); + + //moves scanline forward + inline void advanceScan() { nextItr_ = scanData_.begin(); } + + //proceses the given interval and std::set data + //the output data structre is a graph, the indicies in the vector correspond to graph nodes, + //the integers in the set are vector indicies and are the nodes with which that node shares an edge + template + inline void processInterval(graphT& outputContainer, Interval ivl, const std::set& ids, bool leadingEdge) { + //print(); + typename ScanData::iterator lowItr = lookup_(ivl.low()); + typename ScanData::iterator highItr = lookup_(ivl.high()); + //std::cout << "Interval: " << ivl << std::endl; + //for(std::set::const_iterator itr = ids.begin(); itr != ids.end(); ++itr) + // std::cout << (*itr) << " "; + //std::cout << std::endl; + //add interval to scan data if it is past the end + if(lowItr == scanData_.end()) { + //std::cout << "case0" << std::endl; + lowItr = insert_(ivl.low(), ids); + evaluateBorder_(outputContainer, ids, ids); + highItr = insert_(ivl.high(), std::set()); + return; + } + //ensure that highItr points to the end of the ivl + if(highItr == scanData_.end() || (*highItr).first > ivl.high()) { + //std::cout << "case1" << std::endl; + //std::cout << highItr->first << std::endl; + std::set value = std::set(); + if(highItr != scanData_.begin()) { + --highItr; + //std::cout << highItr->first << std::endl; + //std::cout << "high set size " << highItr->second.size() << std::endl; + value = highItr->second; + } + nextItr_ = highItr; + highItr = insert_(ivl.high(), value); + } else { + //evaluate border with next higher interval + //std::cout << "case1a" << std::endl; + if(leadingEdge)evaluateBorder_(outputContainer, highItr->second, ids); + } + //split the low interval if needed + if(lowItr->first > ivl.low()) { + //std::cout << "case2" << std::endl; + if(lowItr != scanData_.begin()) { + //std::cout << "case3" << std::endl; + --lowItr; + nextItr_ = lowItr; + //std::cout << lowItr->first << " " << lowItr->second.size() << std::endl; + lowItr = insert_(ivl.low(), lowItr->second); + } else { + //std::cout << "case4" << std::endl; + nextItr_ = lowItr; + lowItr = insert_(ivl.low(), std::set()); + } + } else { + //evaluate border with next higher interval + //std::cout << "case2a" << std::endl; + typename ScanData::iterator nextLowerItr = lowItr; + if(leadingEdge && nextLowerItr != scanData_.begin()){ + --nextLowerItr; + evaluateBorder_(outputContainer, nextLowerItr->second, ids); + } + } + //std::cout << "low: " << lowItr->first << " high: " << highItr->first << std::endl; + //print(); + //process scan data intersecting interval + for(typename ScanData::iterator itr = lowItr; itr != highItr; ){ + //std::cout << "case5" << std::endl; + //std::cout << itr->first << std::endl; + std::set& beforeIds = itr->second; + ++itr; + evaluateInterval_(outputContainer, beforeIds, ids, leadingEdge); + } + //print(); + //merge the bottom interval with the one below if they have the same count + if(lowItr != scanData_.begin()){ + //std::cout << "case6" << std::endl; + typename ScanData::iterator belowLowItr = lowItr; + --belowLowItr; + if(belowLowItr->second == lowItr->second) { + //std::cout << "case7" << std::endl; + scanData_.erase(lowItr); + } + } + //merge the top interval with the one above if they have the same count + if(highItr != scanData_.begin()) { + //std::cout << "case8" << std::endl; + typename ScanData::iterator beforeHighItr = highItr; + --beforeHighItr; + if(beforeHighItr->second == highItr->second) { + //std::cout << "case9" << std::endl; + scanData_.erase(highItr); + highItr = beforeHighItr; + ++highItr; + } + } + //print(); + nextItr_ = highItr; + } + +// inline void print() const { +// for(typename ScanData::const_iterator itr = scanData_.begin(); itr != scanData_.end(); ++itr) { +// std::cout << itr->first << ": "; +// for(std::set::const_iterator sitr = itr->second.begin(); +// sitr != itr->second.end(); ++sitr){ +// std::cout << *sitr << " "; +// } +// std::cout << std::endl; +// } +// } + + private: + inline typename ScanData::iterator lookup_(Unit pos){ + if(nextItr_ != scanData_.end() && nextItr_->first >= pos) { + return nextItr_; + } + return nextItr_ = scanData_.lower_bound(pos); + } + + inline typename ScanData::iterator insert_(Unit pos, const std::set& ids){ + //std::cout << "inserting " << ids.size() << " ids at: " << pos << std::endl; + return nextItr_ = scanData_.insert(nextItr_, std::pair >(pos, ids)); + } + + template + inline void evaluateInterval_(graphT& outputContainer, std::set& ids, + const std::set& changingIds, bool leadingEdge) { + for(std::set::const_iterator ciditr = changingIds.begin(); ciditr != changingIds.end(); ++ciditr){ + //std::cout << "evaluateInterval " << (*ciditr) << std::endl; + evaluateId_(outputContainer, ids, *ciditr, leadingEdge); + } + } + template + inline void evaluateBorder_(graphT& outputContainer, const std::set& ids, const std::set& changingIds) { + for(std::set::const_iterator ciditr = changingIds.begin(); ciditr != changingIds.end(); ++ciditr){ + //std::cout << "evaluateBorder " << (*ciditr) << std::endl; + evaluateBorderId_(outputContainer, ids, *ciditr); + } + } + template + inline void evaluateBorderId_(graphT& outputContainer, const std::set& ids, int changingId) { + for(std::set::const_iterator scanItr = ids.begin(); scanItr != ids.end(); ++scanItr) { + //std::cout << "create edge: " << changingId << " " << *scanItr << std::endl; + if(changingId != *scanItr){ + outputContainer[changingId].insert(*scanItr); + outputContainer[*scanItr].insert(changingId); + } + } + } + template + inline void evaluateId_(graphT& outputContainer, std::set& ids, int changingId, bool leadingEdge) { + //std::cout << "changingId: " << changingId << std::endl; + //for( std::set::iterator itr = ids.begin(); itr != ids.end(); ++itr){ + // std::cout << *itr << " "; + //}std::cout << std::endl; + std::set::iterator lb = ids.lower_bound(changingId); + if(lb == ids.end() || (*lb) != changingId) { + if(leadingEdge) { + //std::cout << "insert\n"; + //insert and add to output + for(std::set::iterator scanItr = ids.begin(); scanItr != ids.end(); ++scanItr) { + //std::cout << "create edge: " << changingId << " " << *scanItr << std::endl; + if(changingId != *scanItr){ + outputContainer[changingId].insert(*scanItr); + outputContainer[*scanItr].insert(changingId); + } + } + ids.insert(changingId); + } + } else { + if(!leadingEdge){ + //std::cout << "erase\n"; + ids.erase(lb); + } + } + } + }; + + template + static inline void processEvent(graphT& outputContainer, TouchOp& op, const TouchScanEvent& data, bool leadingEdge) { + for(typename TouchScanEvent::iterator itr = data.begin(); itr != data.end(); ++itr) { + //std::cout << "processInterval" << std::endl; + op.processInterval(outputContainer, (*itr).first, (*itr).second, leadingEdge); + } + } + + template + static inline void performTouch(graphT& outputContainer, const TouchSetData& data) { + typename std::map::const_iterator leftItr = data.first.begin(); + typename std::map::const_iterator rightItr = data.second.begin(); + typename std::map::const_iterator leftEnd = data.first.end(); + typename std::map::const_iterator rightEnd = data.second.end(); + TouchOp op; + while(leftItr != leftEnd || rightItr != rightEnd) { + //std::cout << "loop" << std::endl; + op.advanceScan(); + //rightItr cannont be at end if leftItr is not at end + if(leftItr != leftEnd && rightItr != rightEnd && + leftItr->first <= rightItr->first) { + //std::cout << "case1" << std::endl; + //std::cout << leftItr ->first << std::endl; + processEvent(outputContainer, op, leftItr->second, true); + ++leftItr; + } else { + //std::cout << "case2" << std::endl; + //std::cout << rightItr ->first << std::endl; + processEvent(outputContainer, op, rightItr->second, false); + ++rightItr; + } + } + } + + template + static inline void populateTouchSetData(TouchSetData& data, iT beginData, iT endData, int id) { + Unit prevPos = ((std::numeric_limits::max)()); + Unit prevY = prevPos; + int count = 0; + for(iT itr = beginData; itr != endData; ++itr) { + Unit pos = (*itr).first; + if(pos != prevPos) { + prevPos = pos; + prevY = (*itr).second.first; + count = (*itr).second.second; + continue; + } + Unit y = (*itr).second.first; + if(count != 0 && y != prevY) { + std::pair element(Interval(prevY, y), id); + if(count > 0) { + data.first[pos].insert(element); + } else { + data.second[pos].insert(element); + } + } + prevY = y; + count += (*itr).second.second; + } + } + + static inline void populateTouchSetData(TouchSetData& data, const std::vector > >& inputData, int id) { + populateTouchSetData(data, inputData.begin(), inputData.end(), id); + } + + }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_arbitrary_formation.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_arbitrary_formation.hpp new file mode 100644 index 0000000..5495897 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_arbitrary_formation.hpp @@ -0,0 +1,2907 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_ARBITRARY_FORMATION_HPP +#define BOOST_POLYGON_POLYGON_ARBITRARY_FORMATION_HPP +namespace boost { namespace polygon{ + template + struct PolyLineArbitraryByConcept {}; + + template + class poly_line_arbitrary_polygon_data; + template + class poly_line_arbitrary_hole_data; + + template + struct scanline_base { + + typedef point_data Point; + typedef std::pair half_edge; + + class less_point { + public: + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + inline less_point() {} + inline bool operator () (const Point& pt1, const Point& pt2) const { + if(pt1.get(HORIZONTAL) < pt2.get(HORIZONTAL)) return true; + if(pt1.get(HORIZONTAL) == pt2.get(HORIZONTAL)) { + if(pt1.get(VERTICAL) < pt2.get(VERTICAL)) return true; + } + return false; + } + }; + + static inline bool between(Point pt, Point pt1, Point pt2) { + less_point lp; + if(lp(pt1, pt2)) + return lp(pt, pt2) && lp(pt1, pt); + return lp(pt, pt1) && lp(pt2, pt); + } + + template + static inline Unit compute_intercept(const area_type& dy2, + const area_type& dx1, + const area_type& dx2) { + //intercept = dy2 * dx1 / dx2 + //return (Unit)(((area_type)dy2 * (area_type)dx1) / (area_type)dx2); + area_type dx1_q = dx1 / dx2; + area_type dx1_r = dx1 % dx2; + return dx1_q * dy2 + (dy2 * dx1_r)/dx2; + } + + template + static inline bool equal_slope(area_type dx1, area_type dy1, area_type dx2, area_type dy2) { + typedef typename coordinate_traits::unsigned_area_type unsigned_product_type; + unsigned_product_type cross_1 = (unsigned_product_type)(dx2 < 0 ? -dx2 :dx2) * (unsigned_product_type)(dy1 < 0 ? -dy1 : dy1); + unsigned_product_type cross_2 = (unsigned_product_type)(dx1 < 0 ? -dx1 :dx1) * (unsigned_product_type)(dy2 < 0 ? -dy2 : dy2); + int dx1_sign = dx1 < 0 ? -1 : 1; + int dx2_sign = dx2 < 0 ? -1 : 1; + int dy1_sign = dy1 < 0 ? -1 : 1; + int dy2_sign = dy2 < 0 ? -1 : 1; + int cross_1_sign = dx2_sign * dy1_sign; + int cross_2_sign = dx1_sign * dy2_sign; + return cross_1 == cross_2 && (cross_1_sign == cross_2_sign || cross_1 == 0); + } + + template + static inline bool equal_slope_hp(const T& dx1, const T& dy1, const T& dx2, const T& dy2) { + return dx1 * dy2 == dx2 * dy1; + } + + static inline bool equal_slope(const Unit& x, const Unit& y, + const Point& pt1, const Point& pt2) { + const Point* pts[2] = {&pt1, &pt2}; + typedef typename coordinate_traits::manhattan_area_type at; + at dy2 = (at)pts[1]->get(VERTICAL) - (at)y; + at dy1 = (at)pts[0]->get(VERTICAL) - (at)y; + at dx2 = (at)pts[1]->get(HORIZONTAL) - (at)x; + at dx1 = (at)pts[0]->get(HORIZONTAL) - (at)x; + return equal_slope(dx1, dy1, dx2, dy2); + } + + template + static inline bool less_slope(area_type dx1, area_type dy1, area_type dx2, area_type dy2) { + //reflext x and y slopes to right hand side half plane + if(dx1 < 0) { + dy1 *= -1; + dx1 *= -1; + } else if(dx1 == 0) { + //if the first slope is vertical the first cannot be less + return false; + } + if(dx2 < 0) { + dy2 *= -1; + dx2 *= -1; + } else if(dx2 == 0) { + //if the second slope is vertical the first is always less unless it is also vertical, in which case they are equal + return dx1 != 0; + } + typedef typename coordinate_traits::unsigned_area_type unsigned_product_type; + unsigned_product_type cross_1 = (unsigned_product_type)(dx2 < 0 ? -dx2 :dx2) * (unsigned_product_type)(dy1 < 0 ? -dy1 : dy1); + unsigned_product_type cross_2 = (unsigned_product_type)(dx1 < 0 ? -dx1 :dx1) * (unsigned_product_type)(dy2 < 0 ? -dy2 : dy2); + int dx1_sign = dx1 < 0 ? -1 : 1; + int dx2_sign = dx2 < 0 ? -1 : 1; + int dy1_sign = dy1 < 0 ? -1 : 1; + int dy2_sign = dy2 < 0 ? -1 : 1; + int cross_1_sign = dx2_sign * dy1_sign; + int cross_2_sign = dx1_sign * dy2_sign; + if(cross_1_sign < cross_2_sign) return true; + if(cross_2_sign < cross_1_sign) return false; + if(cross_1_sign == -1) return cross_2 < cross_1; + return cross_1 < cross_2; + } + + static inline bool less_slope(const Unit& x, const Unit& y, + const Point& pt1, const Point& pt2) { + const Point* pts[2] = {&pt1, &pt2}; + //compute y value on edge from pt_ to pts[1] at the x value of pts[0] + typedef typename coordinate_traits::manhattan_area_type at; + at dy2 = (at)pts[1]->get(VERTICAL) - (at)y; + at dy1 = (at)pts[0]->get(VERTICAL) - (at)y; + at dx2 = (at)pts[1]->get(HORIZONTAL) - (at)x; + at dx1 = (at)pts[0]->get(HORIZONTAL) - (at)x; + return less_slope(dx1, dy1, dx2, dy2); + } + + //return -1 below, 0 on and 1 above line + static inline int on_above_or_below(Point pt, const half_edge& he) { + if(pt == he.first || pt == he.second) return 0; + if(equal_slope(pt.get(HORIZONTAL), pt.get(VERTICAL), he.first, he.second)) return 0; + bool less_result = less_slope(pt.get(HORIZONTAL), pt.get(VERTICAL), he.first, he.second); + int retval = less_result ? -1 : 1; + less_point lp; + if(lp(he.second, he.first)) retval *= -1; + if(!between(pt, he.first, he.second)) retval *= -1; + return retval; + } + + //returns true is the segment intersects the integer grid square with lower + //left corner at point + static inline bool intersects_grid(Point pt, const half_edge& he) { + if(pt == he.second) return true; + if(pt == he.first) return true; + rectangle_data rect1; + set_points(rect1, he.first, he.second); + if(contains(rect1, pt, true)) { + if(is_vertical(he) || is_horizontal(he)) return true; + } else { + return false; //can't intersect a grid not within bounding box + } + Unit x = pt.get(HORIZONTAL); + Unit y = pt.get(VERTICAL); + if(equal_slope(x, y, he.first, he.second) && + between(pt, he.first, he.second)) return true; + Point pt01(pt.get(HORIZONTAL), pt.get(VERTICAL) + 1); + Point pt10(pt.get(HORIZONTAL) + 1, pt.get(VERTICAL)); + Point pt11(pt.get(HORIZONTAL) + 1, pt.get(VERTICAL) + 1); +// if(pt01 == he.first) return true; +// if(pt10 == he.first) return true; +// if(pt11 == he.first) return true; +// if(pt01 == he.second) return true; +// if(pt10 == he.second) return true; +// if(pt11 == he.second) return true; + //check non-integer intersections + half_edge widget1(pt, pt11); + //intersects but not just at pt11 + if(intersects(widget1, he) && on_above_or_below(pt11, he)) return true; + half_edge widget2(pt01, pt10); + //intersects but not just at pt01 or 10 + if(intersects(widget2, he) && on_above_or_below(pt01, he) && on_above_or_below(pt10, he)) return true; + return false; + } + + static inline Unit evalAtXforYlazy(Unit xIn, Point pt, Point other_pt) { + long double + evalAtXforYret, evalAtXforYxIn, evalAtXforYx1, evalAtXforYy1, evalAtXforYdx1, evalAtXforYdx, + evalAtXforYdy, evalAtXforYx2, evalAtXforYy2, evalAtXforY0; + //y = (x - x1)dy/dx + y1 + //y = (xIn - pt.x)*(other_pt.y-pt.y)/(other_pt.x-pt.x) + pt.y + //assert pt.x != other_pt.x + if(pt.y() == other_pt.y()) + return pt.y(); + evalAtXforYxIn = xIn; + evalAtXforYx1 = pt.get(HORIZONTAL); + evalAtXforYy1 = pt.get(VERTICAL); + evalAtXforYdx1 = evalAtXforYxIn - evalAtXforYx1; + evalAtXforY0 = 0; + if(evalAtXforYdx1 == evalAtXforY0) return (Unit)evalAtXforYy1; + evalAtXforYx2 = other_pt.get(HORIZONTAL); + evalAtXforYy2 = other_pt.get(VERTICAL); + + evalAtXforYdx = evalAtXforYx2 - evalAtXforYx1; + evalAtXforYdy = evalAtXforYy2 - evalAtXforYy1; + evalAtXforYret = ((evalAtXforYdx1) * evalAtXforYdy / evalAtXforYdx + evalAtXforYy1); + return (Unit)evalAtXforYret; + } + + static inline typename high_precision_type::type evalAtXforY(Unit xIn, Point pt, Point other_pt) { + typename high_precision_type::type + evalAtXforYret, evalAtXforYxIn, evalAtXforYx1, evalAtXforYy1, evalAtXforYdx1, evalAtXforYdx, + evalAtXforYdy, evalAtXforYx2, evalAtXforYy2, evalAtXforY0; + //y = (x - x1)dy/dx + y1 + //y = (xIn - pt.x)*(other_pt.y-pt.y)/(other_pt.x-pt.x) + pt.y + //assert pt.x != other_pt.x + typedef typename high_precision_type::type high_precision; + if(pt.y() == other_pt.y()) + return (high_precision)pt.y(); + evalAtXforYxIn = (high_precision)xIn; + evalAtXforYx1 = pt.get(HORIZONTAL); + evalAtXforYy1 = pt.get(VERTICAL); + evalAtXforYdx1 = evalAtXforYxIn - evalAtXforYx1; + evalAtXforY0 = high_precision(0); + if(evalAtXforYdx1 == evalAtXforY0) return evalAtXforYret = evalAtXforYy1; + evalAtXforYx2 = (high_precision)other_pt.get(HORIZONTAL); + evalAtXforYy2 = (high_precision)other_pt.get(VERTICAL); + + evalAtXforYdx = evalAtXforYx2 - evalAtXforYx1; + evalAtXforYdy = evalAtXforYy2 - evalAtXforYy1; + evalAtXforYret = ((evalAtXforYdx1) * evalAtXforYdy / evalAtXforYdx + evalAtXforYy1); + return evalAtXforYret; + } + + struct evalAtXforYPack { + typename high_precision_type::type + evalAtXforYret, evalAtXforYxIn, evalAtXforYx1, evalAtXforYy1, evalAtXforYdx1, evalAtXforYdx, + evalAtXforYdy, evalAtXforYx2, evalAtXforYy2, evalAtXforY0; + inline const typename high_precision_type::type& evalAtXforY(Unit xIn, Point pt, Point other_pt) { + //y = (x - x1)dy/dx + y1 + //y = (xIn - pt.x)*(other_pt.y-pt.y)/(other_pt.x-pt.x) + pt.y + //assert pt.x != other_pt.x + typedef typename high_precision_type::type high_precision; + if(pt.y() == other_pt.y()) { + evalAtXforYret = (high_precision)pt.y(); + return evalAtXforYret; + } + evalAtXforYxIn = (high_precision)xIn; + evalAtXforYx1 = pt.get(HORIZONTAL); + evalAtXforYy1 = pt.get(VERTICAL); + evalAtXforYdx1 = evalAtXforYxIn - evalAtXforYx1; + evalAtXforY0 = high_precision(0); + if(evalAtXforYdx1 == evalAtXforY0) return evalAtXforYret = evalAtXforYy1; + evalAtXforYx2 = (high_precision)other_pt.get(HORIZONTAL); + evalAtXforYy2 = (high_precision)other_pt.get(VERTICAL); + + evalAtXforYdx = evalAtXforYx2 - evalAtXforYx1; + evalAtXforYdy = evalAtXforYy2 - evalAtXforYy1; + evalAtXforYret = ((evalAtXforYdx1) * evalAtXforYdy / evalAtXforYdx + evalAtXforYy1); + return evalAtXforYret; + } + }; + + static inline bool is_vertical(const half_edge& he) { + return he.first.get(HORIZONTAL) == he.second.get(HORIZONTAL); + } + + static inline bool is_horizontal(const half_edge& he) { + return he.first.get(VERTICAL) == he.second.get(VERTICAL); + } + + static inline bool is_45_degree(const half_edge& he) { + return euclidean_distance(he.first, he.second, HORIZONTAL) == euclidean_distance(he.first, he.second, VERTICAL); + } + + //scanline comparator functor + class less_half_edge { + private: + Unit *x_; //x value at which to apply comparison + int *justBefore_; + evalAtXforYPack * pack_; + public: + typedef half_edge first_argument_type; + typedef half_edge second_argument_type; + typedef bool result_type; + inline less_half_edge() : x_(0), justBefore_(0), pack_(0) {} + inline less_half_edge(Unit *x, int *justBefore, evalAtXforYPack * packIn) : x_(x), justBefore_(justBefore), pack_(packIn) {} + inline less_half_edge(const less_half_edge& that) : x_(that.x_), justBefore_(that.justBefore_), + pack_(that.pack_){} + inline less_half_edge& operator=(const less_half_edge& that) { + x_ = that.x_; + justBefore_ = that.justBefore_; + pack_ = that.pack_; + return *this; } + inline bool operator () (const half_edge& elm1, const half_edge& elm2) const { + if((std::max)(elm1.first.y(), elm1.second.y()) < (std::min)(elm2.first.y(), elm2.second.y())) + return true; + if((std::min)(elm1.first.y(), elm1.second.y()) > (std::max)(elm2.first.y(), elm2.second.y())) + return false; + + //check if either x of elem1 is equal to x_ + Unit localx = *x_; + Unit elm1y = 0; + bool elm1_at_x = false; + if(localx == elm1.first.get(HORIZONTAL)) { + elm1_at_x = true; + elm1y = elm1.first.get(VERTICAL); + } else if(localx == elm1.second.get(HORIZONTAL)) { + elm1_at_x = true; + elm1y = elm1.second.get(VERTICAL); + } + Unit elm2y = 0; + bool elm2_at_x = false; + if(localx == elm2.first.get(HORIZONTAL)) { + elm2_at_x = true; + elm2y = elm2.first.get(VERTICAL); + } else if(localx == elm2.second.get(HORIZONTAL)) { + elm2_at_x = true; + elm2y = elm2.second.get(VERTICAL); + } + bool retval = false; + if(!(elm1_at_x && elm2_at_x)) { + //at least one of the segments doesn't have an end point a the current x + //-1 below, 1 above + int pt1_oab = on_above_or_below(elm1.first, half_edge(elm2.first, elm2.second)); + int pt2_oab = on_above_or_below(elm1.second, half_edge(elm2.first, elm2.second)); + if(pt1_oab == pt2_oab) { + if(pt1_oab == -1) + retval = true; //pt1 is below elm2 so elm1 is below elm2 + } else { + //the segments can't cross so elm2 is on whatever side of elm1 that one of its ends is + int pt3_oab = on_above_or_below(elm2.first, half_edge(elm1.first, elm1.second)); + if(pt3_oab == 1) + retval = true; //elm1's point is above elm1 + } + } else { + if(elm1y < elm2y) { + retval = true; + } else if(elm1y == elm2y) { + if(elm1 == elm2) + return false; + retval = less_slope(elm1.second.get(HORIZONTAL) - elm1.first.get(HORIZONTAL), + elm1.second.get(VERTICAL) - elm1.first.get(VERTICAL), + elm2.second.get(HORIZONTAL) - elm2.first.get(HORIZONTAL), + elm2.second.get(VERTICAL) - elm2.first.get(VERTICAL)); + retval = ((*justBefore_) != 0) ^ retval; + } + } + return retval; + } + }; + + template + static inline void unsigned_add(unsigned_product_type& result, int& result_sign, unsigned_product_type a, int a_sign, unsigned_product_type b, int b_sign) { + int switcher = 0; + if(a_sign < 0) switcher += 1; + if(b_sign < 0) switcher += 2; + if(a < b) switcher += 4; + switch (switcher) { + case 0: //both positive + result = a + b; + result_sign = 1; + break; + case 1: //a is negative + result = a - b; + result_sign = -1; + break; + case 2: //b is negative + result = a - b; + result_sign = 1; + break; + case 3: //both negative + result = a + b; + result_sign = -1; + break; + case 4: //both positive + result = a + b; + result_sign = 1; + break; + case 5: //a is negative + result = b - a; + result_sign = 1; + break; + case 6: //b is negative + result = b - a; + result_sign = -1; + break; + case 7: //both negative + result = b + a; + result_sign = -1; + break; + }; + } + + struct compute_intersection_pack { + typedef typename high_precision_type::type high_precision; + high_precision y_high, dx1, dy1, dx2, dy2, x11, x21, y11, y21, x_num, y_num, x_den, y_den, x, y; + static inline bool compute_lazy_intersection(Point& intersection, const half_edge& he1, const half_edge& he2, + bool projected = false, bool round_closest = false) { + long double y_high, dx1, dy1, dx2, dy2, x11, x21, y11, y21, x_num, y_num, x_den, y_den, x, y; + typedef rectangle_data Rectangle; + Rectangle rect1, rect2; + set_points(rect1, he1.first, he1.second); + set_points(rect2, he2.first, he2.second); + if(!projected && !::boost::polygon::intersects(rect1, rect2, true)) return false; + if(is_vertical(he1)) { + if(is_vertical(he2)) return false; + y_high = evalAtXforYlazy(he1.first.get(HORIZONTAL), he2.first, he2.second); + Unit y_local = (Unit)y_high; + if(y_high < y_local) --y_local; + if(projected || contains(rect1.get(VERTICAL), y_local, true)) { + intersection = Point(he1.first.get(HORIZONTAL), y_local); + return true; + } else { + return false; + } + } else if(is_vertical(he2)) { + y_high = evalAtXforYlazy(he2.first.get(HORIZONTAL), he1.first, he1.second); + Unit y_local = (Unit)y_high; + if(y_high < y_local) --y_local; + if(projected || contains(rect2.get(VERTICAL), y_local, true)) { + intersection = Point(he2.first.get(HORIZONTAL), y_local); + return true; + } else { + return false; + } + } + //the bounding boxes of the two line segments intersect, so we check closer to find the intersection point + dy2 = (he2.second.get(VERTICAL)) - + (he2.first.get(VERTICAL)); + dy1 = (he1.second.get(VERTICAL)) - + (he1.first.get(VERTICAL)); + dx2 = (he2.second.get(HORIZONTAL)) - + (he2.first.get(HORIZONTAL)); + dx1 = (he1.second.get(HORIZONTAL)) - + (he1.first.get(HORIZONTAL)); + if(equal_slope_hp(dx1, dy1, dx2, dy2)) return false; + //the line segments have different slopes + //we can assume that the line segments are not vertical because such an intersection is handled elsewhere + x11 = (he1.first.get(HORIZONTAL)); + x21 = (he2.first.get(HORIZONTAL)); + y11 = (he1.first.get(VERTICAL)); + y21 = (he2.first.get(VERTICAL)); + //Unit exp_x = ((at)x11 * (at)dy1 * (at)dx2 - (at)x21 * (at)dy2 * (at)dx1 + (at)y21 * (at)dx1 * (at)dx2 - (at)y11 * (at)dx1 * (at)dx2) / ((at)dy1 * (at)dx2 - (at)dy2 * (at)dx1); + //Unit exp_y = ((at)y11 * (at)dx1 * (at)dy2 - (at)y21 * (at)dx2 * (at)dy1 + (at)x21 * (at)dy1 * (at)dy2 - (at)x11 * (at)dy1 * (at)dy2) / ((at)dx1 * (at)dy2 - (at)dx2 * (at)dy1); + x_num = (x11 * dy1 * dx2 - x21 * dy2 * dx1 + y21 * dx1 * dx2 - y11 * dx1 * dx2); + x_den = (dy1 * dx2 - dy2 * dx1); + y_num = (y11 * dx1 * dy2 - y21 * dx2 * dy1 + x21 * dy1 * dy2 - x11 * dy1 * dy2); + y_den = (dx1 * dy2 - dx2 * dy1); + x = x_num / x_den; + y = y_num / y_den; + //std::cout << "cross1 " << dy1 << " " << dx2 << " " << dy1 * dx2 << "\n"; + //std::cout << "cross2 " << dy2 << " " << dx1 << " " << dy2 * dx1 << "\n"; + //Unit exp_x = compute_x_intercept(x11, x21, y11, y21, dy1, dy2, dx1, dx2); + //Unit exp_y = compute_x_intercept(y11, y21, x11, x21, dx1, dx2, dy1, dy2); + if(round_closest) { + x = x + 0.5; + y = y + 0.5; + } + Unit x_unit = (Unit)(x); + Unit y_unit = (Unit)(y); + //truncate downward if it went up due to negative number + if(x < x_unit) --x_unit; + if(y < y_unit) --y_unit; + if(is_horizontal(he1)) + y_unit = he1.first.y(); + if(is_horizontal(he2)) + y_unit = he2.first.y(); + //if(x != exp_x || y != exp_y) + // std::cout << exp_x << " " << exp_y << " " << x << " " << y << "\n"; + //Unit y1 = evalAtXforY(exp_x, he1.first, he1.second); + //Unit y2 = evalAtXforY(exp_x, he2.first, he2.second); + //std::cout << exp_x << " " << exp_y << " " << y1 << " " << y2 << "\n"; + Point result(x_unit, y_unit); + if(!projected && !contains(rect1, result, true)) return false; + if(!projected && !contains(rect2, result, true)) return false; + if(projected) { + rectangle_data inf_rect(-(long double)(std::numeric_limits::max)(), + -(long double) (std::numeric_limits::max)(), + (long double)(std::numeric_limits::max)(), + (long double) (std::numeric_limits::max)() ); + if(contains(inf_rect, point_data(x, y), true)) { + intersection = result; + return true; + } else + return false; + } + intersection = result; + return true; + } + + inline bool compute_intersection(Point& intersection, const half_edge& he1, const half_edge& he2, + bool projected = false, bool round_closest = false) { + if(!projected && !intersects(he1, he2)) + return false; + bool lazy_success = compute_lazy_intersection(intersection, he1, he2, projected); + if(!projected) { + if(lazy_success) { + if(intersects_grid(intersection, he1) && + intersects_grid(intersection, he2)) + return true; + } + } else { + return lazy_success; + } + return compute_exact_intersection(intersection, he1, he2, projected, round_closest); + } + + inline bool compute_exact_intersection(Point& intersection, const half_edge& he1, const half_edge& he2, + bool projected = false, bool round_closest = false) { + if(!projected && !intersects(he1, he2)) + return false; + typedef rectangle_data Rectangle; + Rectangle rect1, rect2; + set_points(rect1, he1.first, he1.second); + set_points(rect2, he2.first, he2.second); + if(!::boost::polygon::intersects(rect1, rect2, true)) return false; + if(is_vertical(he1)) { + if(is_vertical(he2)) return false; + y_high = evalAtXforY(he1.first.get(HORIZONTAL), he2.first, he2.second); + Unit y = convert_high_precision_type(y_high); + if(y_high < (high_precision)y) --y; + if(contains(rect1.get(VERTICAL), y, true)) { + intersection = Point(he1.first.get(HORIZONTAL), y); + return true; + } else { + return false; + } + } else if(is_vertical(he2)) { + y_high = evalAtXforY(he2.first.get(HORIZONTAL), he1.first, he1.second); + Unit y = convert_high_precision_type(y_high); + if(y_high < (high_precision)y) --y; + if(contains(rect2.get(VERTICAL), y, true)) { + intersection = Point(he2.first.get(HORIZONTAL), y); + return true; + } else { + return false; + } + } + //the bounding boxes of the two line segments intersect, so we check closer to find the intersection point + dy2 = (high_precision)(he2.second.get(VERTICAL)) - + (high_precision)(he2.first.get(VERTICAL)); + dy1 = (high_precision)(he1.second.get(VERTICAL)) - + (high_precision)(he1.first.get(VERTICAL)); + dx2 = (high_precision)(he2.second.get(HORIZONTAL)) - + (high_precision)(he2.first.get(HORIZONTAL)); + dx1 = (high_precision)(he1.second.get(HORIZONTAL)) - + (high_precision)(he1.first.get(HORIZONTAL)); + if(equal_slope_hp(dx1, dy1, dx2, dy2)) return false; + //the line segments have different slopes + //we can assume that the line segments are not vertical because such an intersection is handled elsewhere + x11 = (high_precision)(he1.first.get(HORIZONTAL)); + x21 = (high_precision)(he2.first.get(HORIZONTAL)); + y11 = (high_precision)(he1.first.get(VERTICAL)); + y21 = (high_precision)(he2.first.get(VERTICAL)); + //Unit exp_x = ((at)x11 * (at)dy1 * (at)dx2 - (at)x21 * (at)dy2 * (at)dx1 + (at)y21 * (at)dx1 * (at)dx2 - (at)y11 * (at)dx1 * (at)dx2) / ((at)dy1 * (at)dx2 - (at)dy2 * (at)dx1); + //Unit exp_y = ((at)y11 * (at)dx1 * (at)dy2 - (at)y21 * (at)dx2 * (at)dy1 + (at)x21 * (at)dy1 * (at)dy2 - (at)x11 * (at)dy1 * (at)dy2) / ((at)dx1 * (at)dy2 - (at)dx2 * (at)dy1); + x_num = (x11 * dy1 * dx2 - x21 * dy2 * dx1 + y21 * dx1 * dx2 - y11 * dx1 * dx2); + x_den = (dy1 * dx2 - dy2 * dx1); + y_num = (y11 * dx1 * dy2 - y21 * dx2 * dy1 + x21 * dy1 * dy2 - x11 * dy1 * dy2); + y_den = (dx1 * dy2 - dx2 * dy1); + x = x_num / x_den; + y = y_num / y_den; + //std::cout << x << " " << y << "\n"; + //std::cout << "cross1 " << dy1 << " " << dx2 << " " << dy1 * dx2 << "\n"; + //std::cout << "cross2 " << dy2 << " " << dx1 << " " << dy2 * dx1 << "\n"; + //Unit exp_x = compute_x_intercept(x11, x21, y11, y21, dy1, dy2, dx1, dx2); + //Unit exp_y = compute_x_intercept(y11, y21, x11, x21, dx1, dx2, dy1, dy2); + if(round_closest) { + x = x + (high_precision)0.5; + y = y + (high_precision)0.5; + } + Unit x_unit = convert_high_precision_type(x); + Unit y_unit = convert_high_precision_type(y); + //truncate downward if it went up due to negative number + if(x < (high_precision)x_unit) --x_unit; + if(y < (high_precision)y_unit) --y_unit; + if(is_horizontal(he1)) + y_unit = he1.first.y(); + if(is_horizontal(he2)) + y_unit = he2.first.y(); + //if(x != exp_x || y != exp_y) + // std::cout << exp_x << " " << exp_y << " " << x << " " << y << "\n"; + //Unit y1 = evalAtXforY(exp_x, he1.first, he1.second); + //Unit y2 = evalAtXforY(exp_x, he2.first, he2.second); + //std::cout << exp_x << " " << exp_y << " " << y1 << " " << y2 << "\n"; + Point result(x_unit, y_unit); + if(!contains(rect1, result, true)) return false; + if(!contains(rect2, result, true)) return false; + if(projected) { + high_precision b1 = (high_precision) (std::numeric_limits::min)(); + high_precision b2 = (high_precision) (std::numeric_limits::max)(); + if(x > b2 || y > b2 || x < b1 || y < b1) + return false; + } + intersection = result; + return true; + } + }; + + static inline bool compute_intersection(Point& intersection, const half_edge& he1, const half_edge& he2) { + typedef typename high_precision_type::type high_precision; + typedef rectangle_data Rectangle; + Rectangle rect1, rect2; + set_points(rect1, he1.first, he1.second); + set_points(rect2, he2.first, he2.second); + if(!::boost::polygon::intersects(rect1, rect2, true)) return false; + if(is_vertical(he1)) { + if(is_vertical(he2)) return false; + high_precision y_high = evalAtXforY(he1.first.get(HORIZONTAL), he2.first, he2.second); + Unit y = convert_high_precision_type(y_high); + if(y_high < (high_precision)y) --y; + if(contains(rect1.get(VERTICAL), y, true)) { + intersection = Point(he1.first.get(HORIZONTAL), y); + return true; + } else { + return false; + } + } else if(is_vertical(he2)) { + high_precision y_high = evalAtXforY(he2.first.get(HORIZONTAL), he1.first, he1.second); + Unit y = convert_high_precision_type(y_high); + if(y_high < (high_precision)y) --y; + if(contains(rect2.get(VERTICAL), y, true)) { + intersection = Point(he2.first.get(HORIZONTAL), y); + return true; + } else { + return false; + } + } + //the bounding boxes of the two line segments intersect, so we check closer to find the intersection point + high_precision dy2 = (high_precision)(he2.second.get(VERTICAL)) - + (high_precision)(he2.first.get(VERTICAL)); + high_precision dy1 = (high_precision)(he1.second.get(VERTICAL)) - + (high_precision)(he1.first.get(VERTICAL)); + high_precision dx2 = (high_precision)(he2.second.get(HORIZONTAL)) - + (high_precision)(he2.first.get(HORIZONTAL)); + high_precision dx1 = (high_precision)(he1.second.get(HORIZONTAL)) - + (high_precision)(he1.first.get(HORIZONTAL)); + if(equal_slope_hp(dx1, dy1, dx2, dy2)) return false; + //the line segments have different slopes + //we can assume that the line segments are not vertical because such an intersection is handled elsewhere + high_precision x11 = (high_precision)(he1.first.get(HORIZONTAL)); + high_precision x21 = (high_precision)(he2.first.get(HORIZONTAL)); + high_precision y11 = (high_precision)(he1.first.get(VERTICAL)); + high_precision y21 = (high_precision)(he2.first.get(VERTICAL)); + //Unit exp_x = ((at)x11 * (at)dy1 * (at)dx2 - (at)x21 * (at)dy2 * (at)dx1 + (at)y21 * (at)dx1 * (at)dx2 - (at)y11 * (at)dx1 * (at)dx2) / ((at)dy1 * (at)dx2 - (at)dy2 * (at)dx1); + //Unit exp_y = ((at)y11 * (at)dx1 * (at)dy2 - (at)y21 * (at)dx2 * (at)dy1 + (at)x21 * (at)dy1 * (at)dy2 - (at)x11 * (at)dy1 * (at)dy2) / ((at)dx1 * (at)dy2 - (at)dx2 * (at)dy1); + high_precision x_num = (x11 * dy1 * dx2 - x21 * dy2 * dx1 + y21 * dx1 * dx2 - y11 * dx1 * dx2); + high_precision x_den = (dy1 * dx2 - dy2 * dx1); + high_precision y_num = (y11 * dx1 * dy2 - y21 * dx2 * dy1 + x21 * dy1 * dy2 - x11 * dy1 * dy2); + high_precision y_den = (dx1 * dy2 - dx2 * dy1); + high_precision x = x_num / x_den; + high_precision y = y_num / y_den; + //std::cout << "cross1 " << dy1 << " " << dx2 << " " << dy1 * dx2 << "\n"; + //std::cout << "cross2 " << dy2 << " " << dx1 << " " << dy2 * dx1 << "\n"; + //Unit exp_x = compute_x_intercept(x11, x21, y11, y21, dy1, dy2, dx1, dx2); + //Unit exp_y = compute_x_intercept(y11, y21, x11, x21, dx1, dx2, dy1, dy2); + Unit x_unit = convert_high_precision_type(x); + Unit y_unit = convert_high_precision_type(y); + //truncate downward if it went up due to negative number + if(x < (high_precision)x_unit) --x_unit; + if(y < (high_precision)y_unit) --y_unit; + if(is_horizontal(he1)) + y_unit = he1.first.y(); + if(is_horizontal(he2)) + y_unit = he2.first.y(); + //if(x != exp_x || y != exp_y) + // std::cout << exp_x << " " << exp_y << " " << x << " " << y << "\n"; + //Unit y1 = evalAtXforY(exp_x, he1.first, he1.second); + //Unit y2 = evalAtXforY(exp_x, he2.first, he2.second); + //std::cout << exp_x << " " << exp_y << " " << y1 << " " << y2 << "\n"; + Point result(x_unit, y_unit); + if(!contains(rect1, result, true)) return false; + if(!contains(rect2, result, true)) return false; + intersection = result; + return true; + } + + static inline bool intersects(const half_edge& he1, const half_edge& he2) { + typedef rectangle_data Rectangle; + Rectangle rect1, rect2; + set_points(rect1, he1.first, he1.second); + set_points(rect2, he2.first, he2.second); + if(::boost::polygon::intersects(rect1, rect2, false)) { + if(he1.first == he2.first) { + if(he1.second != he2.second && equal_slope(he1.first.get(HORIZONTAL), he1.first.get(VERTICAL), + he1.second, he2.second)) { + return true; + } else { + return false; + } + } + if(he1.first == he2.second) { + if(he1.second != he2.first && equal_slope(he1.first.get(HORIZONTAL), he1.first.get(VERTICAL), + he1.second, he2.first)) { + return true; + } else { + return false; + } + } + if(he1.second == he2.first) { + if(he1.first != he2.second && equal_slope(he1.second.get(HORIZONTAL), he1.second.get(VERTICAL), + he1.first, he2.second)) { + return true; + } else { + return false; + } + } + if(he1.second == he2.second) { + if(he1.first != he2.first && equal_slope(he1.second.get(HORIZONTAL), he1.second.get(VERTICAL), + he1.first, he2.first)) { + return true; + } else { + return false; + } + } + int oab1 = on_above_or_below(he1.first, he2); + if(oab1 == 0 && between(he1.first, he2.first, he2.second)) return true; + int oab2 = on_above_or_below(he1.second, he2); + if(oab2 == 0 && between(he1.second, he2.first, he2.second)) return true; + if(oab1 == oab2 && oab1 != 0) return false; //both points of he1 are on same side of he2 + int oab3 = on_above_or_below(he2.first, he1); + if(oab3 == 0 && between(he2.first, he1.first, he1.second)) return true; + int oab4 = on_above_or_below(he2.second, he1); + if(oab4 == 0 && between(he2.second, he1.first, he1.second)) return true; + if(oab3 == oab4) return false; //both points of he2 are on same side of he1 + return true; //they must cross + } + if(is_vertical(he1) && is_vertical(he2) && he1.first.get(HORIZONTAL) == he2.first.get(HORIZONTAL)) + return ::boost::polygon::intersects(rect1.get(VERTICAL), rect2.get(VERTICAL), false) && + rect1.get(VERTICAL) != rect2.get(VERTICAL); + if(is_horizontal(he1) && is_horizontal(he2) && he1.first.get(VERTICAL) == he2.first.get(VERTICAL)) + return ::boost::polygon::intersects(rect1.get(HORIZONTAL), rect2.get(HORIZONTAL), false) && + rect1.get(HORIZONTAL) != rect2.get(HORIZONTAL); + return false; + } + + class vertex_half_edge { + public: + typedef typename high_precision_type::type high_precision; + Point pt; + Point other_pt; // 1, 0 or -1 + int count; //dxdydTheta + inline vertex_half_edge() : pt(), other_pt(), count() {} + inline vertex_half_edge(const Point& point, const Point& other_point, int countIn) : pt(point), other_pt(other_point), count(countIn) {} + inline vertex_half_edge(const vertex_half_edge& vertex) : pt(vertex.pt), other_pt(vertex.other_pt), count(vertex.count) {} + inline vertex_half_edge& operator=(const vertex_half_edge& vertex){ + pt = vertex.pt; other_pt = vertex.other_pt; count = vertex.count; return *this; } + inline bool operator==(const vertex_half_edge& vertex) const { + return pt == vertex.pt && other_pt == vertex.other_pt && count == vertex.count; } + inline bool operator!=(const vertex_half_edge& vertex) const { return !((*this) == vertex); } + inline bool operator<(const vertex_half_edge& vertex) const { + if(pt.get(HORIZONTAL) < vertex.pt.get(HORIZONTAL)) return true; + if(pt.get(HORIZONTAL) == vertex.pt.get(HORIZONTAL)) { + if(pt.get(VERTICAL) < vertex.pt.get(VERTICAL)) return true; + if(pt.get(VERTICAL) == vertex.pt.get(VERTICAL)) { return less_slope(pt.get(HORIZONTAL), pt.get(VERTICAL), + other_pt, vertex.other_pt); + } + } + return false; + } + inline bool operator>(const vertex_half_edge& vertex) const { return vertex < (*this); } + inline bool operator<=(const vertex_half_edge& vertex) const { return !((*this) > vertex); } + inline bool operator>=(const vertex_half_edge& vertex) const { return !((*this) < vertex); } + inline high_precision evalAtX(Unit xIn) const { return evalAtXforYlazy(xIn, pt, other_pt); } + inline bool is_vertical() const { + return pt.get(HORIZONTAL) == other_pt.get(HORIZONTAL); + } + inline bool is_begin() const { + return pt.get(HORIZONTAL) < other_pt.get(HORIZONTAL) || + (pt.get(HORIZONTAL) == other_pt.get(HORIZONTAL) && + (pt.get(VERTICAL) < other_pt.get(VERTICAL))); + } + }; + + //when scanning Vertex45 for polygon formation we need a scanline comparator functor + class less_vertex_half_edge { + private: + Unit *x_; //x value at which to apply comparison + int *justBefore_; + public: + typedef vertex_half_edge first_argument_type; + typedef vertex_half_edge second_argument_type; + typedef bool result_type; + inline less_vertex_half_edge() : x_(0), justBefore_(0) {} + inline less_vertex_half_edge(Unit *x, int *justBefore) : x_(x), justBefore_(justBefore) {} + inline less_vertex_half_edge(const less_vertex_half_edge& that) : x_(that.x_), justBefore_(that.justBefore_) {} + inline less_vertex_half_edge& operator=(const less_vertex_half_edge& that) { x_ = that.x_; justBefore_ = that.justBefore_; return *this; } + inline bool operator () (const vertex_half_edge& elm1, const vertex_half_edge& elm2) const { + if((std::max)(elm1.pt.y(), elm1.other_pt.y()) < (std::min)(elm2.pt.y(), elm2.other_pt.y())) + return true; + if((std::min)(elm1.pt.y(), elm1.other_pt.y()) > (std::max)(elm2.pt.y(), elm2.other_pt.y())) + return false; + //check if either x of elem1 is equal to x_ + Unit localx = *x_; + Unit elm1y = 0; + bool elm1_at_x = false; + if(localx == elm1.pt.get(HORIZONTAL)) { + elm1_at_x = true; + elm1y = elm1.pt.get(VERTICAL); + } else if(localx == elm1.other_pt.get(HORIZONTAL)) { + elm1_at_x = true; + elm1y = elm1.other_pt.get(VERTICAL); + } + Unit elm2y = 0; + bool elm2_at_x = false; + if(localx == elm2.pt.get(HORIZONTAL)) { + elm2_at_x = true; + elm2y = elm2.pt.get(VERTICAL); + } else if(localx == elm2.other_pt.get(HORIZONTAL)) { + elm2_at_x = true; + elm2y = elm2.other_pt.get(VERTICAL); + } + bool retval = false; + if(!(elm1_at_x && elm2_at_x)) { + //at least one of the segments doesn't have an end point a the current x + //-1 below, 1 above + int pt1_oab = on_above_or_below(elm1.pt, half_edge(elm2.pt, elm2.other_pt)); + int pt2_oab = on_above_or_below(elm1.other_pt, half_edge(elm2.pt, elm2.other_pt)); + if(pt1_oab == pt2_oab) { + if(pt1_oab == -1) + retval = true; //pt1 is below elm2 so elm1 is below elm2 + } else { + //the segments can't cross so elm2 is on whatever side of elm1 that one of its ends is + int pt3_oab = on_above_or_below(elm2.pt, half_edge(elm1.pt, elm1.other_pt)); + if(pt3_oab == 1) + retval = true; //elm1's point is above elm1 + } + } else { + if(elm1y < elm2y) { + retval = true; + } else if(elm1y == elm2y) { + if(elm1.pt == elm2.pt && elm1.other_pt == elm2.other_pt) + return false; + retval = less_slope(elm1.other_pt.get(HORIZONTAL) - elm1.pt.get(HORIZONTAL), + elm1.other_pt.get(VERTICAL) - elm1.pt.get(VERTICAL), + elm2.other_pt.get(HORIZONTAL) - elm2.pt.get(HORIZONTAL), + elm2.other_pt.get(VERTICAL) - elm2.pt.get(VERTICAL)); + retval = ((*justBefore_) != 0) ^ retval; + } + } + return retval; + } + }; + + }; + + template + class polygon_arbitrary_formation : public scanline_base { + public: + typedef typename scanline_base::Point Point; + typedef typename scanline_base::half_edge half_edge; + typedef typename scanline_base::vertex_half_edge vertex_half_edge; + typedef typename scanline_base::less_vertex_half_edge less_vertex_half_edge; + + class poly_line_arbitrary { + public: + typedef typename std::list::const_iterator iterator; + + // default constructor of point does not initialize x and y + inline poly_line_arbitrary() : points() {} //do nothing default constructor + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline poly_line_arbitrary& set(iT inputBegin, iT inputEnd) { + points.clear(); //just in case there was some old data there + while(inputBegin != inputEnd) { + points.insert(points.end(), *inputBegin); + ++inputBegin; + } + return *this; + } + + // copy constructor (since we have dynamic memory) + inline poly_line_arbitrary(const poly_line_arbitrary& that) : points(that.points) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline poly_line_arbitrary& operator=(const poly_line_arbitrary& that) { + points = that.points; + return *this; + } + + // get begin iterator, returns a pointer to a const Unit + inline iterator begin() const { return points.begin(); } + + // get end iterator, returns a pointer to a const Unit + inline iterator end() const { return points.end(); } + + inline std::size_t size() const { return points.size(); } + + //public data member + std::list points; + }; + + class active_tail_arbitrary { + protected: + //data + poly_line_arbitrary* tailp_; + active_tail_arbitrary *otherTailp_; + std::list holesList_; + bool head_; + public: + + /** + * @brief iterator over coordinates of the figure + */ + typedef typename poly_line_arbitrary::iterator iterator; + + /** + * @brief iterator over holes contained within the figure + */ + typedef typename std::list::const_iterator iteratorHoles; + + //default constructor + inline active_tail_arbitrary() : tailp_(), otherTailp_(), holesList_(), head_() {} + + //constructor + inline active_tail_arbitrary(const vertex_half_edge& vertex, active_tail_arbitrary* otherTailp = 0) : tailp_(), otherTailp_(), holesList_(), head_() { + tailp_ = new poly_line_arbitrary; + tailp_->points.push_back(vertex.pt); + //bool headArray[4] = {false, true, true, true}; + bool inverted = vertex.count == -1; + head_ = (!vertex.is_vertical) ^ inverted; + otherTailp_ = otherTailp; + } + + inline active_tail_arbitrary(Point point, active_tail_arbitrary* otherTailp, bool head = true) : + tailp_(), otherTailp_(), holesList_(), head_() { + tailp_ = new poly_line_arbitrary; + tailp_->points.push_back(point); + head_ = head; + otherTailp_ = otherTailp; + + } + inline active_tail_arbitrary(active_tail_arbitrary* otherTailp) : + tailp_(), otherTailp_(), holesList_(), head_() { + tailp_ = otherTailp->tailp_; + otherTailp_ = otherTailp; + } + + //copy constructor + inline active_tail_arbitrary(const active_tail_arbitrary& that) : + tailp_(), otherTailp_(), holesList_(), head_() { (*this) = that; } + + //destructor + inline ~active_tail_arbitrary() { + destroyContents(); + } + + //assignment operator + inline active_tail_arbitrary& operator=(const active_tail_arbitrary& that) { + tailp_ = new poly_line_arbitrary(*(that.tailp_)); + head_ = that.head_; + otherTailp_ = that.otherTailp_; + holesList_ = that.holesList_; + return *this; + } + + //equivalence operator + inline bool operator==(const active_tail_arbitrary& b) const { + return tailp_ == b.tailp_ && head_ == b.head_; + } + + /** + * @brief get the pointer to the polyline that this is an active tail of + */ + inline poly_line_arbitrary* getTail() const { return tailp_; } + + /** + * @brief get the pointer to the polyline at the other end of the chain + */ + inline poly_line_arbitrary* getOtherTail() const { return otherTailp_->tailp_; } + + /** + * @brief get the pointer to the activetail at the other end of the chain + */ + inline active_tail_arbitrary* getOtherActiveTail() const { return otherTailp_; } + + /** + * @brief test if another active tail is the other end of the chain + */ + inline bool isOtherTail(const active_tail_arbitrary& b) const { return &b == otherTailp_; } + + /** + * @brief update this end of chain pointer to new polyline + */ + inline active_tail_arbitrary& updateTail(poly_line_arbitrary* newTail) { tailp_ = newTail; return *this; } + + inline bool join(active_tail_arbitrary* tail) { + if(tail == otherTailp_) { + //std::cout << "joining to other tail!\n"; + return false; + } + if(tail->head_ == head_) { + //std::cout << "joining head to head!\n"; + return false; + } + if(!tailp_) { + //std::cout << "joining empty tail!\n"; + return false; + } + if(!(otherTailp_->head_)) { + otherTailp_->copyHoles(*tail); + otherTailp_->copyHoles(*this); + } else { + tail->otherTailp_->copyHoles(*this); + tail->otherTailp_->copyHoles(*tail); + } + poly_line_arbitrary* tail1 = tailp_; + poly_line_arbitrary* tail2 = tail->tailp_; + if(head_) std::swap(tail1, tail2); + typename std::list >::reverse_iterator riter = tail1->points.rbegin(); + typename std::list >::iterator iter = tail2->points.begin(); + if(*riter == *iter) { + tail1->points.pop_back(); //remove duplicate point + } + tail1->points.splice(tail1->points.end(), tail2->points); + delete tail2; + otherTailp_->tailp_ = tail1; + tail->otherTailp_->tailp_ = tail1; + otherTailp_->otherTailp_ = tail->otherTailp_; + tail->otherTailp_->otherTailp_ = otherTailp_; + tailp_ = 0; + tail->tailp_ = 0; + tail->otherTailp_ = 0; + otherTailp_ = 0; + return true; + } + + /** + * @brief associate a hole to this active tail by the specified policy + */ + inline active_tail_arbitrary* addHole(active_tail_arbitrary* hole) { + holesList_.push_back(hole); + copyHoles(*hole); + copyHoles(*(hole->otherTailp_)); + return this; + } + + /** + * @brief get the list of holes + */ + inline const std::list& getHoles() const { return holesList_; } + + /** + * @brief copy holes from that to this + */ + inline void copyHoles(active_tail_arbitrary& that) { holesList_.splice(holesList_.end(), that.holesList_); } + + /** + * @brief find out if solid to right + */ + inline bool solidToRight() const { return !head_; } + inline bool solidToLeft() const { return head_; } + + /** + * @brief get vertex + */ + inline Point getPoint() const { + if(head_) return tailp_->points.front(); + return tailp_->points.back(); + } + + /** + * @brief add a coordinate to the polygon at this active tail end, properly handle degenerate edges by removing redundant coordinate + */ + inline void pushPoint(Point point) { + if(head_) { + //if(tailp_->points.size() < 2) { + // tailp_->points.push_front(point); + // return; + //} + typename std::list::iterator iter = tailp_->points.begin(); + if(iter == tailp_->points.end()) { + tailp_->points.push_front(point); + return; + } + ++iter; + if(iter == tailp_->points.end()) { + tailp_->points.push_front(point); + return; + } + --iter; + if(*iter != point) { + tailp_->points.push_front(point); + } + return; + } + //if(tailp_->points.size() < 2) { + // tailp_->points.push_back(point); + // return; + //} + typename std::list::reverse_iterator iter = tailp_->points.rbegin(); + if(iter == tailp_->points.rend()) { + tailp_->points.push_back(point); + return; + } + ++iter; + if(iter == tailp_->points.rend()) { + tailp_->points.push_back(point); + return; + } + --iter; + if(*iter != point) { + tailp_->points.push_back(point); + } + } + + /** + * @brief joins the two chains that the two active tail tails are ends of + * checks for closure of figure and writes out polygons appropriately + * returns a handle to a hole if one is closed + */ + template + static inline active_tail_arbitrary* joinChains(Point point, active_tail_arbitrary* at1, active_tail_arbitrary* at2, bool solid, + cT& output) { + if(at1->otherTailp_ == at2) { + //if(at2->otherTailp_ != at1) std::cout << "half closed error\n"; + //we are closing a figure + at1->pushPoint(point); + at2->pushPoint(point); + if(solid) { + //we are closing a solid figure, write to output + //std::cout << "test1\n"; + at1->copyHoles(*(at1->otherTailp_)); + typename PolyLineArbitraryByConcept::type>::type polyData(at1); + //poly_line_arbitrary_polygon_data polyData(at1); + //std::cout << "test2\n"; + //std::cout << poly << "\n"; + //std::cout << "test3\n"; + typedef typename cT::value_type result_type; + output.push_back(result_type()); + assign(output.back(), polyData); + //std::cout << "test4\n"; + //std::cout << "delete " << at1->otherTailp_ << "\n"; + //at1->print(); + //at1->otherTailp_->print(); + delete at1->otherTailp_; + //at1->print(); + //at1->otherTailp_->print(); + //std::cout << "test5\n"; + //std::cout << "delete " << at1 << "\n"; + delete at1; + //std::cout << "test6\n"; + return 0; + } else { + //we are closing a hole, return the tail end active tail of the figure + return at1; + } + } + //we are not closing a figure + at1->pushPoint(point); + at1->join(at2); + delete at1; + delete at2; + return 0; + } + + inline void destroyContents() { + if(otherTailp_) { + //std::cout << "delete p " << tailp_ << "\n"; + if(tailp_) delete tailp_; + tailp_ = 0; + otherTailp_->otherTailp_ = 0; + otherTailp_->tailp_ = 0; + otherTailp_ = 0; + } + for(typename std::list::iterator itr = holesList_.begin(); itr != holesList_.end(); ++itr) { + //std::cout << "delete p " << (*itr) << "\n"; + if(*itr) { + if((*itr)->otherTailp_) { + delete (*itr)->otherTailp_; + (*itr)->otherTailp_ = 0; + } + delete (*itr); + } + (*itr) = 0; + } + holesList_.clear(); + } + + inline void print() { + //std::cout << this << " " << tailp_ << " " << otherTailp_ << " " << holesList_.size() << " " << head_ << "\n"; + } + + static inline std::pair createActiveTailsAsPair(Point point, bool solid, + active_tail_arbitrary* phole, bool fractureHoles) { + active_tail_arbitrary* at1 = 0; + active_tail_arbitrary* at2 = 0; + if(phole && fractureHoles) { + //std::cout << "adding hole\n"; + at1 = phole; + //assert solid == false, we should be creating a corner with solid below and to the left if there was a hole + at2 = at1->getOtherActiveTail(); + at2->pushPoint(point); + at1->pushPoint(point); + } else { + at1 = new active_tail_arbitrary(point, at2, solid); + at2 = new active_tail_arbitrary(at1); + at1->otherTailp_ = at2; + at2->head_ = !solid; + if(phole) + at2->addHole(phole); //assert fractureHoles == false + } + return std::pair(at1, at2); + } + + }; + + + typedef std::vector > vertex_arbitrary_count; + + class less_half_edge_count { + private: + Point pt_; + public: + typedef vertex_half_edge first_argument_type; + typedef vertex_half_edge second_argument_type; + typedef bool result_type; + inline less_half_edge_count() : pt_() {} + inline less_half_edge_count(Point point) : pt_(point) {} + inline bool operator () (const std::pair& elm1, const std::pair& elm2) const { + return scanline_base::less_slope(pt_.get(HORIZONTAL), pt_.get(VERTICAL), elm1.first, elm2.first); + } + }; + + static inline void sort_vertex_arbitrary_count(vertex_arbitrary_count& count, const Point& pt) { + less_half_edge_count lfec(pt); + polygon_sort(count.begin(), count.end(), lfec); + } + + typedef std::vector, int>, active_tail_arbitrary*> > incoming_count; + + class less_incoming_count { + private: + Point pt_; + public: + typedef std::pair, int>, active_tail_arbitrary*> first_argument_type; + typedef std::pair, int>, active_tail_arbitrary*> second_argument_type; + typedef bool result_type; + inline less_incoming_count() : pt_() {} + inline less_incoming_count(Point point) : pt_(point) {} + inline bool operator () (const std::pair, int>, active_tail_arbitrary*>& elm1, + const std::pair, int>, active_tail_arbitrary*>& elm2) const { + Unit dx1 = elm1.first.first.first.get(HORIZONTAL) - elm1.first.first.second.get(HORIZONTAL); + Unit dx2 = elm2.first.first.first.get(HORIZONTAL) - elm2.first.first.second.get(HORIZONTAL); + Unit dy1 = elm1.first.first.first.get(VERTICAL) - elm1.first.first.second.get(VERTICAL); + Unit dy2 = elm2.first.first.first.get(VERTICAL) - elm2.first.first.second.get(VERTICAL); + return scanline_base::less_slope(dx1, dy1, dx2, dy2); + } + }; + + static inline void sort_incoming_count(incoming_count& count, const Point& pt) { + less_incoming_count lfec(pt); + polygon_sort(count.begin(), count.end(), lfec); + } + + static inline void compact_vertex_arbitrary_count(const Point& pt, vertex_arbitrary_count &count) { + if(count.empty()) return; + vertex_arbitrary_count tmp; + tmp.reserve(count.size()); + tmp.push_back(count[0]); + //merge duplicates + for(std::size_t i = 1; i < count.size(); ++i) { + if(!equal_slope(pt.get(HORIZONTAL), pt.get(VERTICAL), tmp[i-1].first, count[i].first)) { + tmp.push_back(count[i]); + } else { + tmp.back().second += count[i].second; + } + } + count.clear(); + count.swap(tmp); + } + + // inline std::ostream& operator<< (std::ostream& o, const vertex_arbitrary_count& c) { +// for(unsinged int i = 0; i < c.size(); ++i) { +// o << c[i].first << " " << c[i].second << " "; +// } +// return o; +// } + + class vertex_arbitrary_compact { + public: + Point pt; + vertex_arbitrary_count count; + inline vertex_arbitrary_compact() : pt(), count() {} + inline vertex_arbitrary_compact(const Point& point, const Point& other_point, int countIn) : pt(point), count() { + count.push_back(std::pair(other_point, countIn)); + } + inline vertex_arbitrary_compact(const vertex_half_edge& vertex) : pt(vertex.pt), count() { + count.push_back(std::pair(vertex.other_pt, vertex.count)); + } + inline vertex_arbitrary_compact(const vertex_arbitrary_compact& vertex) : pt(vertex.pt), count(vertex.count) {} + inline vertex_arbitrary_compact& operator=(const vertex_arbitrary_compact& vertex){ + pt = vertex.pt; count = vertex.count; return *this; } + inline bool operator==(const vertex_arbitrary_compact& vertex) const { + return pt == vertex.pt && count == vertex.count; } + inline bool operator!=(const vertex_arbitrary_compact& vertex) const { return !((*this) == vertex); } + inline bool operator<(const vertex_arbitrary_compact& vertex) const { + if(pt.get(HORIZONTAL) < vertex.pt.get(HORIZONTAL)) return true; + if(pt.get(HORIZONTAL) == vertex.pt.get(HORIZONTAL)) { + return pt.get(VERTICAL) < vertex.pt.get(VERTICAL); + } + return false; + } + inline bool operator>(const vertex_arbitrary_compact& vertex) const { return vertex < (*this); } + inline bool operator<=(const vertex_arbitrary_compact& vertex) const { return !((*this) > vertex); } + inline bool operator>=(const vertex_arbitrary_compact& vertex) const { return !((*this) < vertex); } + inline bool have_vertex_half_edge(int index) const { return count[index]; } + inline vertex_half_edge operator[](int index) const { return vertex_half_edge(pt, count[index]); } + }; + +// inline std::ostream& operator<< (std::ostream& o, const vertex_arbitrary_compact& c) { +// o << c.pt << ", " << c.count; +// return o; +// } + + protected: + //definitions + typedef std::map scanline_data; + typedef typename scanline_data::iterator iterator; + typedef typename scanline_data::const_iterator const_iterator; + + //data + scanline_data scanData_; + Unit x_; + int justBefore_; + int fractureHoles_; + public: + inline polygon_arbitrary_formation() : + scanData_(), x_((std::numeric_limits::min)()), justBefore_(false), fractureHoles_(0) { + less_vertex_half_edge lessElm(&x_, &justBefore_); + scanData_ = scanline_data(lessElm); + } + inline polygon_arbitrary_formation(bool fractureHoles) : + scanData_(), x_((std::numeric_limits::min)()), justBefore_(false), fractureHoles_(fractureHoles) { + less_vertex_half_edge lessElm(&x_, &justBefore_); + scanData_ = scanline_data(lessElm); + } + inline polygon_arbitrary_formation(const polygon_arbitrary_formation& that) : + scanData_(), x_((std::numeric_limits::min)()), justBefore_(false), fractureHoles_(0) { (*this) = that; } + inline polygon_arbitrary_formation& operator=(const polygon_arbitrary_formation& that) { + x_ = that.x_; + justBefore_ = that.justBefore_; + fractureHoles_ = that.fractureHoles_; + less_vertex_half_edge lessElm(&x_, &justBefore_); + scanData_ = scanline_data(lessElm); + for(const_iterator itr = that.scanData_.begin(); itr != that.scanData_.end(); ++itr){ + scanData_.insert(scanData_.end(), *itr); + } + return *this; + } + + //cT is an output container of Polygon45 or Polygon45WithHoles + //iT is an iterator over vertex_half_edge elements + //inputBegin - inputEnd is a range of sorted iT that represents + //one or more scanline stops worth of data + template + void scan(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "1\n"; + while(inputBegin != inputEnd) { + //std::cout << "2\n"; + x_ = (*inputBegin).pt.get(HORIZONTAL); + //std::cout << "SCAN FORMATION " << x_ << "\n"; + //std::cout << "x_ = " << x_ << "\n"; + //std::cout << "scan line size: " << scanData_.size() << "\n"; + inputBegin = processEvent_(output, inputBegin, inputEnd); + } + //std::cout << "scan line size: " << scanData_.size() << "\n"; + } + + protected: + //functions + template + inline std::pair, active_tail_arbitrary*> processPoint_(cT& output, cT2& elements, Point point, + incoming_count& counts_from_scanline, vertex_arbitrary_count& incoming_count) { + //std::cout << "\nAT POINT: " << point << "\n"; + //join any closing solid corners + std::vector counts; + std::vector incoming; + std::vector tails; + counts.reserve(counts_from_scanline.size()); + tails.reserve(counts_from_scanline.size()); + incoming.reserve(incoming_count.size()); + for(std::size_t i = 0; i < counts_from_scanline.size(); ++i) { + counts.push_back(counts_from_scanline[i].first.second); + tails.push_back(counts_from_scanline[i].second); + } + for(std::size_t i = 0; i < incoming_count.size(); ++i) { + incoming.push_back(incoming_count[i].second); + if(incoming_count[i].first < point) { + incoming.back() = 0; + } + } + + active_tail_arbitrary* returnValue = 0; + std::pair returnCount(Point(0, 0), 0); + int i_size_less_1 = (int)(incoming.size()) -1; + int c_size_less_1 = (int)(counts.size()) -1; + int i_size = incoming.size(); + int c_size = counts.size(); + + bool have_vertical_tail_from_below = false; + if(c_size && + scanline_base::is_vertical(counts_from_scanline.back().first.first)) { + have_vertical_tail_from_below = true; + } + //assert size = size_less_1 + 1 + //std::cout << tails.size() << " " << incoming.size() << " " << counts_from_scanline.size() << " " << incoming_count.size() << "\n"; + // for(std::size_t i = 0; i < counts.size(); ++i) { + // std::cout << counts_from_scanline[i].first.first.first.get(HORIZONTAL) << ","; + // std::cout << counts_from_scanline[i].first.first.first.get(VERTICAL) << " "; + // std::cout << counts_from_scanline[i].first.first.second.get(HORIZONTAL) << ","; + // std::cout << counts_from_scanline[i].first.first.second.get(VERTICAL) << ":"; + // std::cout << counts_from_scanline[i].first.second << " "; + // } std::cout << "\n"; + // print(incoming_count); + { + for(int i = 0; i < c_size_less_1; ++i) { + //std::cout << i << "\n"; + if(counts[i] == -1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < c_size; ++j) { + //std::cout << j << "\n"; + if(counts[j]) { + if(counts[j] == 1) { + //std::cout << "case1: " << i << " " << j << "\n"; + //if a figure is closed it will be written out by this function to output + active_tail_arbitrary::joinChains(point, tails[i], tails[j], true, output); + counts[i] = 0; + counts[j] = 0; + tails[i] = 0; + tails[j] = 0; + } + break; + } + } + } + } + } + //find any pairs of incoming edges that need to create pair for leading solid + //std::cout << "checking case2\n"; + { + for(int i = 0; i < i_size_less_1; ++i) { + //std::cout << i << "\n"; + if(incoming[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < i_size; ++j) { + //std::cout << j << "\n"; + if(incoming[j]) { + //std::cout << incoming[j] << "\n"; + if(incoming[j] == -1) { + //std::cout << "case2: " << i << " " << j << "\n"; + //std::cout << "creating active tail pair\n"; + std::pair tailPair = + active_tail_arbitrary::createActiveTailsAsPair(point, true, 0, fractureHoles_ != 0); + //tailPair.first->print(); + //tailPair.second->print(); + if(j == i_size_less_1 && incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + //vertical active tail becomes return value + returnValue = tailPair.first; + returnCount.first = point; + returnCount.second = 1; + } else { + //std::cout << "new element " << j-1 << " " << -1 << "\n"; + //std::cout << point << " " << incoming_count[j].first << "\n"; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, -1), tailPair.first)); + } + //std::cout << "new element " << i-1 << " " << 1 << "\n"; + //std::cout << point << " " << incoming_count[i].first << "\n"; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[i].first, 1), tailPair.second)); + incoming[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + } + } + //find any active tail that needs to pass through to an incoming edge + //we expect to find no more than two pass through + + //find pass through with solid on top + { + //std::cout << "checking case 3\n"; + for(int i = 0; i < c_size; ++i) { + //std::cout << i << "\n"; + if(counts[i] != 0) { + if(counts[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = i_size_less_1; j >= 0; --j) { + if(incoming[j] != 0) { + if(incoming[j] == 1) { + //std::cout << "case3: " << i << " " << j << "\n"; + //tails[i]->print(); + //pass through solid on top + tails[i]->pushPoint(point); + //std::cout << "after push\n"; + if(j == i_size_less_1 && incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + returnValue = tails[i]; + returnCount.first = point; + returnCount.second = -1; + } else { + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, incoming[j]), tails[i])); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + } + //std::cout << "checking case 4\n"; + //find pass through with solid on bottom + { + for(int i = c_size_less_1; i >= 0; --i) { + //std::cout << "i = " << i << " with count " << counts[i] << "\n"; + if(counts[i] != 0) { + if(counts[i] == -1) { + for(int j = 0; j < i_size; ++j) { + if(incoming[j] != 0) { + if(incoming[j] == -1) { + //std::cout << "case4: " << i << " " << j << "\n"; + //pass through solid on bottom + tails[i]->pushPoint(point); + if(j == i_size_less_1 && incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + returnValue = tails[i]; + returnCount.first = point; + returnCount.second = 1; + } else { + //std::cout << "new element " << j-1 << " " << incoming[j] << "\n"; + //std::cout << point << " " << incoming_count[j].first << "\n"; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, incoming[j]), tails[i])); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + } + //find the end of a hole or the beginning of a hole + + //find end of a hole + { + for(int i = 0; i < c_size_less_1; ++i) { + if(counts[i] != 0) { + for(int j = i+1; j < c_size; ++j) { + if(counts[j] != 0) { + //std::cout << "case5: " << i << " " << j << "\n"; + //we are ending a hole and may potentially close a figure and have to handle the hole + returnValue = active_tail_arbitrary::joinChains(point, tails[i], tails[j], false, output); + if(returnValue) returnCount.first = point; + //std::cout << returnValue << "\n"; + tails[i] = 0; + tails[j] = 0; + counts[i] = 0; + counts[j] = 0; + break; + } + } + break; + } + } + } + //find beginning of a hole + { + for(int i = 0; i < i_size_less_1; ++i) { + if(incoming[i] != 0) { + for(int j = i+1; j < i_size; ++j) { + if(incoming[j] != 0) { + //std::cout << "case6: " << i << " " << j << "\n"; + //we are beginning a empty space + active_tail_arbitrary* holep = 0; + //if(c_size && counts[c_size_less_1] == 0 && + // counts_from_scanline[c_size_less_1].first.first.first.get(HORIZONTAL) == point.get(HORIZONTAL)) + if(have_vertical_tail_from_below) { + holep = tails[c_size_less_1]; + tails[c_size_less_1] = 0; + have_vertical_tail_from_below = false; + } + std::pair tailPair = + active_tail_arbitrary::createActiveTailsAsPair(point, false, holep, fractureHoles_ != 0); + if(j == i_size_less_1 && incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + //std::cout << "vertical element " << point << "\n"; + returnValue = tailPair.first; + returnCount.first = point; + //returnCount = incoming_count[j]; + returnCount.second = -1; + } else { + //std::cout << "new element " << j-1 << " " << incoming[j] << "\n"; + //std::cout << point << " " << incoming_count[j].first << "\n"; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, incoming[j]), tailPair.first)); + } + //std::cout << "new element " << i-1 << " " << incoming[i] << "\n"; + //std::cout << point << " " << incoming_count[i].first << "\n"; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[i].first, incoming[i]), tailPair.second)); + incoming[i] = 0; + incoming[j] = 0; + break; + } + } + break; + } + } + } + if(have_vertical_tail_from_below) { + if(tails.back()) { + tails.back()->pushPoint(point); + returnValue = tails.back(); + returnCount.first = point; + returnCount.second = counts.back(); + } + } + //assert that tails, counts and incoming are all null + return std::pair, active_tail_arbitrary*>(returnCount, returnValue); + } + + static inline void print(const vertex_arbitrary_count& count) { + for(unsigned i = 0; i < count.size(); ++i) { + //std::cout << count[i].first.get(HORIZONTAL) << ","; + //std::cout << count[i].first.get(VERTICAL) << ":"; + //std::cout << count[i].second << " "; + } //std::cout << "\n"; + } + + static inline void print(const scanline_data& data) { + for(typename scanline_data::const_iterator itr = data.begin(); itr != data.end(); ++itr){ + //std::cout << itr->first.pt << ", " << itr->first.other_pt << "; "; + } //std::cout << "\n"; + } + + template + inline iT processEvent_(cT& output, iT inputBegin, iT inputEnd) { + typedef typename high_precision_type::type high_precision; + //std::cout << "processEvent_\n"; + justBefore_ = true; + //collect up all elements from the tree that are at the y + //values of events in the input queue + //create vector of new elements to add into tree + active_tail_arbitrary* verticalTail = 0; + std::pair verticalCount(Point(0, 0), 0); + iT currentIter = inputBegin; + std::vector elementIters; + std::vector > elements; + while(currentIter != inputEnd && currentIter->pt.get(HORIZONTAL) == x_) { + //std::cout << "loop\n"; + Unit currentY = (*currentIter).pt.get(VERTICAL); + //std::cout << "current Y " << currentY << "\n"; + //std::cout << "scanline size " << scanData_.size() << "\n"; + //print(scanData_); + iterator iter = lookUp_(currentY); + //std::cout << "found element in scanline " << (iter != scanData_.end()) << "\n"; + //int counts[4] = {0, 0, 0, 0}; + incoming_count counts_from_scanline; + //std::cout << "finding elements in tree\n"; + //if(iter != scanData_.end()) + // std::cout << "first iter y is " << iter->first.evalAtX(x_) << "\n"; + while(iter != scanData_.end() && + ((iter->first.pt.x() == x_ && iter->first.pt.y() == currentY) || + (iter->first.other_pt.x() == x_ && iter->first.other_pt.y() == currentY))) { + //iter->first.evalAtX(x_) == (high_precision)currentY) { + //std::cout << "loop2\n"; + elementIters.push_back(iter); + counts_from_scanline.push_back(std::pair, int>, active_tail_arbitrary*> + (std::pair, int>(std::pair(iter->first.pt, + iter->first.other_pt), + iter->first.count), + iter->second)); + ++iter; + } + Point currentPoint(x_, currentY); + //std::cout << "counts_from_scanline size " << counts_from_scanline.size() << "\n"; + sort_incoming_count(counts_from_scanline, currentPoint); + + vertex_arbitrary_count incoming; + //std::cout << "aggregating\n"; + do { + //std::cout << "loop3\n"; + const vertex_half_edge& elem = *currentIter; + incoming.push_back(std::pair(elem.other_pt, elem.count)); + ++currentIter; + } while(currentIter != inputEnd && currentIter->pt.get(VERTICAL) == currentY && + currentIter->pt.get(HORIZONTAL) == x_); + //print(incoming); + sort_vertex_arbitrary_count(incoming, currentPoint); + //std::cout << currentPoint.get(HORIZONTAL) << "," << currentPoint.get(VERTICAL) << "\n"; + //print(incoming); + //std::cout << "incoming counts from input size " << incoming.size() << "\n"; + //compact_vertex_arbitrary_count(currentPoint, incoming); + vertex_arbitrary_count tmp; + tmp.reserve(incoming.size()); + for(std::size_t i = 0; i < incoming.size(); ++i) { + if(currentPoint < incoming[i].first) { + tmp.push_back(incoming[i]); + } + } + incoming.swap(tmp); + //std::cout << "incoming counts from input size " << incoming.size() << "\n"; + //now counts_from_scanline has the data from the left and + //incoming has the data from the right at this point + //cancel out any end points + if(verticalTail) { + //std::cout << "adding vertical tail to counts from scanline\n"; + //std::cout << -verticalCount.second << "\n"; + counts_from_scanline.push_back(std::pair, int>, active_tail_arbitrary*> + (std::pair, int>(std::pair(verticalCount.first, + currentPoint), + -verticalCount.second), + verticalTail)); + } + if(!incoming.empty() && incoming.back().first.get(HORIZONTAL) == x_) { + //std::cout << "inverted vertical event\n"; + incoming.back().second *= -1; + } + //std::cout << "calling processPoint_\n"; + std::pair, active_tail_arbitrary*> result = processPoint_(output, elements, Point(x_, currentY), counts_from_scanline, incoming); + verticalCount = result.first; + verticalTail = result.second; + //if(verticalTail) { + // std::cout << "have vertical tail\n"; + // std::cout << verticalCount.second << "\n"; + //} + if(verticalTail && !(verticalCount.second)) { + //we got a hole out of the point we just processed + //iter is still at the next y element above the current y value in the tree + //std::cout << "checking whether ot handle hole\n"; + if(currentIter == inputEnd || + currentIter->pt.get(HORIZONTAL) != x_ || + scanline_base::on_above_or_below(currentIter->pt, half_edge(iter->first.pt, iter->first.other_pt)) != -1) { + //(high_precision)(currentIter->pt.get(VERTICAL)) >= iter->first.evalAtX(x_)) { + + //std::cout << "handle hole here\n"; + if(fractureHoles_) { + //std::cout << "fracture hole here\n"; + //we need to handle the hole now and not at the next input vertex + active_tail_arbitrary* at = iter->second; + high_precision precise_y = iter->first.evalAtX(x_); + Unit fracture_y = convert_high_precision_type(precise_y); + if(precise_y < fracture_y) --fracture_y; + Point point(x_, fracture_y); + verticalTail->getOtherActiveTail()->pushPoint(point); + iter->second = verticalTail->getOtherActiveTail(); + at->pushPoint(point); + verticalTail->join(at); + delete at; + delete verticalTail; + verticalTail = 0; + } else { + //std::cout << "push hole onto list\n"; + iter->second->addHole(verticalTail); + verticalTail = 0; + } + } + } + } + //std::cout << "erasing\n"; + //erase all elements from the tree + for(typename std::vector::iterator iter = elementIters.begin(); + iter != elementIters.end(); ++iter) { + //std::cout << "erasing loop\n"; + scanData_.erase(*iter); + } + //switch comparison tie breaking policy + justBefore_ = false; + //add new elements into tree + //std::cout << "inserting\n"; + for(typename std::vector >::iterator iter = elements.begin(); + iter != elements.end(); ++iter) { + //std::cout << "inserting loop\n"; + scanData_.insert(scanData_.end(), *iter); + } + //std::cout << "end processEvent\n"; + return currentIter; + } + + inline iterator lookUp_(Unit y){ + //if just before then we need to look from 1 not -1 + //std::cout << "just before " << justBefore_ << "\n"; + return scanData_.lower_bound(vertex_half_edge(Point(x_, y), Point(x_, y+1), 0)); + } + + public: //test functions + + template + static inline bool testPolygonArbitraryFormationRect(stream_type& stdcout) { + stdcout << "testing polygon formation\n"; + polygon_arbitrary_formation pf(true); + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(10, 10), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(10, 10), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(0, 10), 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygonArbitraryFormationP1(stream_type& stdcout) { + stdcout << "testing polygon formation P1\n"; + polygon_arbitrary_formation pf(true); + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(10, 20), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(10, 20), -1)); + data.push_back(vertex_half_edge(Point(10, 20), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(10, 20), Point(0, 10), 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygonArbitraryFormationP2(stream_type& stdcout) { + stdcout << "testing polygon formation P2\n"; + polygon_arbitrary_formation pf(true); + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(-3, 1), Point(2, -4), 1)); + data.push_back(vertex_half_edge(Point(-3, 1), Point(-2, 2), -1)); + data.push_back(vertex_half_edge(Point(-2, 2), Point(2, 4), -1)); + data.push_back(vertex_half_edge(Point(-2, 2), Point(-3, 1), 1)); + data.push_back(vertex_half_edge(Point(2, -4), Point(-3, 1), -1)); + data.push_back(vertex_half_edge(Point(2, -4), Point(2, 4), -1)); + data.push_back(vertex_half_edge(Point(2, 4), Point(-2, 2), 1)); + data.push_back(vertex_half_edge(Point(2, 4), Point(2, -4), 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + + template + static inline bool testPolygonArbitraryFormationPolys(stream_type& stdcout) { + stdcout << "testing polygon formation polys\n"; + polygon_arbitrary_formation pf(false); + std::vector > polys; + polygon_arbitrary_formation pf2(true); + std::vector > polys2; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(100, 1), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(1, 100), -1)); + data.push_back(vertex_half_edge(Point(1, 100), Point(0, 0), 1)); + data.push_back(vertex_half_edge(Point(1, 100), Point(101, 101), -1)); + data.push_back(vertex_half_edge(Point(100, 1), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(100, 1), Point(101, 101), 1)); + data.push_back(vertex_half_edge(Point(101, 101), Point(100, 1), -1)); + data.push_back(vertex_half_edge(Point(101, 101), Point(1, 100), 1)); + + data.push_back(vertex_half_edge(Point(2, 2), Point(10, 2), -1)); + data.push_back(vertex_half_edge(Point(2, 2), Point(2, 10), -1)); + data.push_back(vertex_half_edge(Point(2, 10), Point(2, 2), 1)); + data.push_back(vertex_half_edge(Point(2, 10), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(10, 2), Point(2, 2), 1)); + data.push_back(vertex_half_edge(Point(10, 2), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(10, 2), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(2, 10), -1)); + + data.push_back(vertex_half_edge(Point(2, 12), Point(10, 12), -1)); + data.push_back(vertex_half_edge(Point(2, 12), Point(2, 22), -1)); + data.push_back(vertex_half_edge(Point(2, 22), Point(2, 12), 1)); + data.push_back(vertex_half_edge(Point(2, 22), Point(10, 22), 1)); + data.push_back(vertex_half_edge(Point(10, 12), Point(2, 12), 1)); + data.push_back(vertex_half_edge(Point(10, 12), Point(10, 22), 1)); + data.push_back(vertex_half_edge(Point(10, 22), Point(10, 12), -1)); + data.push_back(vertex_half_edge(Point(10, 22), Point(2, 22), -1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + pf2.scan(polys2, data.begin(), data.end()); + stdcout << "result size: " << polys2.size() << "\n"; + for(std::size_t i = 0; i < polys2.size(); ++i) { + stdcout << polys2[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygonArbitraryFormationSelfTouch1(stream_type& stdcout) { + stdcout << "testing polygon formation self touch 1\n"; + polygon_arbitrary_formation pf(true); + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(5, 10), -1)); + + data.push_back(vertex_half_edge(Point(10, 0), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(10, 5), -1)); + + data.push_back(vertex_half_edge(Point(10, 5), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(10, 5), Point(5, 5), 1)); + + data.push_back(vertex_half_edge(Point(5, 10), Point(5, 5), 1)); + data.push_back(vertex_half_edge(Point(5, 10), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(5, 2), Point(5, 5), -1)); + data.push_back(vertex_half_edge(Point(5, 2), Point(7, 2), -1)); + + data.push_back(vertex_half_edge(Point(5, 5), Point(5, 10), -1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(5, 2), 1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(10, 5), -1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(7, 2), 1)); + + data.push_back(vertex_half_edge(Point(7, 2), Point(5, 5), -1)); + data.push_back(vertex_half_edge(Point(7, 2), Point(5, 2), 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygonArbitraryFormationSelfTouch2(stream_type& stdcout) { + stdcout << "testing polygon formation self touch 2\n"; + polygon_arbitrary_formation pf(true); + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(5, 10), -1)); + + data.push_back(vertex_half_edge(Point(10, 0), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(10, 5), -1)); + + data.push_back(vertex_half_edge(Point(10, 5), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(10, 5), Point(5, 5), 1)); + + data.push_back(vertex_half_edge(Point(5, 10), Point(4, 1), -1)); + data.push_back(vertex_half_edge(Point(5, 10), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(4, 1), Point(5, 10), 1)); + data.push_back(vertex_half_edge(Point(4, 1), Point(7, 2), -1)); + + data.push_back(vertex_half_edge(Point(5, 5), Point(10, 5), -1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(7, 2), 1)); + + data.push_back(vertex_half_edge(Point(7, 2), Point(5, 5), -1)); + data.push_back(vertex_half_edge(Point(7, 2), Point(4, 1), 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygonArbitraryFormationSelfTouch3(stream_type& stdcout) { + stdcout << "testing polygon formation self touch 3\n"; + polygon_arbitrary_formation pf(true); + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(6, 10), -1)); + + data.push_back(vertex_half_edge(Point(10, 0), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(10, 5), -1)); + + data.push_back(vertex_half_edge(Point(10, 5), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(10, 5), Point(5, 5), 1)); + + data.push_back(vertex_half_edge(Point(6, 10), Point(4, 1), -1)); + data.push_back(vertex_half_edge(Point(6, 10), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(4, 1), Point(6, 10), 1)); + data.push_back(vertex_half_edge(Point(4, 1), Point(7, 2), -1)); + + data.push_back(vertex_half_edge(Point(5, 5), Point(10, 5), -1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(7, 2), 1)); + + data.push_back(vertex_half_edge(Point(7, 2), Point(5, 5), -1)); + data.push_back(vertex_half_edge(Point(7, 2), Point(4, 1), 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testPolygonArbitraryFormationColinear(stream_type& stdcout) { + stdcout << "testing polygon formation colinear 3\n"; + stdcout << "Polygon Set Data { <-3 2, -2 2>:1 <-3 2, -1 4>:-1 <-2 2, 0 2>:1 <-1 4, 0 2>:-1 } \n"; + polygon_arbitrary_formation pf(true); + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(-3, 2), Point(-2, 2), 1)); + data.push_back(vertex_half_edge(Point(-2, 2), Point(-3, 2), -1)); + + data.push_back(vertex_half_edge(Point(-3, 2), Point(-1, 4), -1)); + data.push_back(vertex_half_edge(Point(-1, 4), Point(-3, 2), 1)); + + data.push_back(vertex_half_edge(Point(-2, 2), Point(0, 2), 1)); + data.push_back(vertex_half_edge(Point(0, 2), Point(-2, 2), -1)); + + data.push_back(vertex_half_edge(Point(-1, 4), Point(0, 2), -1)); + data.push_back(vertex_half_edge(Point(0, 2), Point(-1, 4), 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing polygon formation\n"; + return true; + } + + template + static inline bool testSegmentIntersection(stream_type& stdcout) { + stdcout << "testing segment intersection\n"; + half_edge he1, he2; + he1.first = Point(0, 0); + he1.second = Point(10, 10); + he2.first = Point(0, 0); + he2.second = Point(10, 20); + Point result; + bool b = scanline_base::compute_intersection(result, he1, he2); + if(!b || result != Point(0, 0)) return false; + he1.first = Point(0, 10); + b = scanline_base::compute_intersection(result, he1, he2); + if(!b || result != Point(5, 10)) return false; + he1.first = Point(0, 11); + b = scanline_base::compute_intersection(result, he1, he2); + if(!b || result != Point(5, 10)) return false; + he1.first = Point(0, 0); + he1.second = Point(1, 9); + he2.first = Point(0, 9); + he2.second = Point(1, 0); + b = scanline_base::compute_intersection(result, he1, he2); + if(!b || result != Point(0, 4)) return false; + + he1.first = Point(0, -10); + he1.second = Point(1, -1); + he2.first = Point(0, -1); + he2.second = Point(1, -10); + b = scanline_base::compute_intersection(result, he1, he2); + if(!b || result != Point(0, -5)) return false; + he1.first = Point((std::numeric_limits::max)(), (std::numeric_limits::max)()-1); + he1.second = Point((std::numeric_limits::min)(), (std::numeric_limits::max)()); + //he1.second = Point(0, (std::numeric_limits::max)()); + he2.first = Point((std::numeric_limits::max)()-1, (std::numeric_limits::max)()); + he2.second = Point((std::numeric_limits::max)(), (std::numeric_limits::min)()); + //he2.second = Point((std::numeric_limits::max)(), 0); + b = scanline_base::compute_intersection(result, he1, he2); + //b is false because of overflow error + he1.first = Point(1000, 2000); + he1.second = Point(1010, 2010); + he2.first = Point(1000, 2000); + he2.second = Point(1010, 2020); + b = scanline_base::compute_intersection(result, he1, he2); + if(!b || result != Point(1000, 2000)) return false; + + return b; + } + + }; + + template + class poly_line_arbitrary_hole_data { + private: + typedef typename polygon_arbitrary_formation::active_tail_arbitrary active_tail_arbitrary; + active_tail_arbitrary* p_; + public: + typedef point_data Point; + typedef Point point_type; + typedef Unit coordinate_type; + typedef typename active_tail_arbitrary::iterator iterator_type; + //typedef iterator_points_to_compact compact_iterator_type; + + typedef iterator_type iterator; + inline poly_line_arbitrary_hole_data() : p_(0) {} + inline poly_line_arbitrary_hole_data(active_tail_arbitrary* p) : p_(p) {} + //use default copy and assign + inline iterator begin() const { return p_->getTail()->begin(); } + inline iterator end() const { return p_->getTail()->end(); } + inline std::size_t size() const { return 0; } + }; + + template + class poly_line_arbitrary_polygon_data { + private: + typedef typename polygon_arbitrary_formation::active_tail_arbitrary active_tail_arbitrary; + active_tail_arbitrary* p_; + public: + typedef point_data Point; + typedef Point point_type; + typedef Unit coordinate_type; + typedef typename active_tail_arbitrary::iterator iterator_type; + //typedef iterator_points_to_compact compact_iterator_type; + typedef typename coordinate_traits::coordinate_distance area_type; + + class iterator_holes_type { + private: + typedef poly_line_arbitrary_hole_data holeType; + mutable holeType hole_; + typename active_tail_arbitrary::iteratorHoles itr_; + + public: + typedef std::forward_iterator_tag iterator_category; + typedef holeType value_type; + typedef std::ptrdiff_t difference_type; + typedef const holeType* pointer; //immutable + typedef const holeType& reference; //immutable + inline iterator_holes_type() : hole_(), itr_() {} + inline iterator_holes_type(typename active_tail_arbitrary::iteratorHoles itr) : hole_(), itr_(itr) {} + inline iterator_holes_type(const iterator_holes_type& that) : hole_(that.hole_), itr_(that.itr_) {} + inline iterator_holes_type& operator=(const iterator_holes_type& that) { + itr_ = that.itr_; + return *this; + } + inline bool operator==(const iterator_holes_type& that) { return itr_ == that.itr_; } + inline bool operator!=(const iterator_holes_type& that) { return itr_ != that.itr_; } + inline iterator_holes_type& operator++() { + ++itr_; + return *this; + } + inline const iterator_holes_type operator++(int) { + iterator_holes_type tmp = *this; + ++(*this); + return tmp; + } + inline reference operator*() { + hole_ = holeType(*itr_); + return hole_; + } + }; + + typedef poly_line_arbitrary_hole_data hole_type; + + inline poly_line_arbitrary_polygon_data() : p_(0) {} + inline poly_line_arbitrary_polygon_data(active_tail_arbitrary* p) : p_(p) {} + //use default copy and assign + inline iterator_type begin() const { return p_->getTail()->begin(); } + inline iterator_type end() const { return p_->getTail()->end(); } + //inline compact_iterator_type begin_compact() const { return p_->getTail()->begin(); } + //inline compact_iterator_type end_compact() const { return p_->getTail()->end(); } + inline iterator_holes_type begin_holes() const { return iterator_holes_type(p_->getHoles().begin()); } + inline iterator_holes_type end_holes() const { return iterator_holes_type(p_->getHoles().end()); } + inline active_tail_arbitrary* yield() { return p_; } + //stub out these four required functions that will not be used but are needed for the interface + inline std::size_t size_holes() const { return 0; } + inline std::size_t size() const { return 0; } + }; + + template + class trapezoid_arbitrary_formation : public polygon_arbitrary_formation { + private: + typedef typename scanline_base::Point Point; + typedef typename scanline_base::half_edge half_edge; + typedef typename scanline_base::vertex_half_edge vertex_half_edge; + typedef typename scanline_base::less_vertex_half_edge less_vertex_half_edge; + + typedef typename polygon_arbitrary_formation::poly_line_arbitrary poly_line_arbitrary; + + typedef typename polygon_arbitrary_formation::active_tail_arbitrary active_tail_arbitrary; + + typedef std::vector > vertex_arbitrary_count; + + typedef typename polygon_arbitrary_formation::less_half_edge_count less_half_edge_count; + + typedef std::vector, int>, active_tail_arbitrary*> > incoming_count; + + typedef typename polygon_arbitrary_formation::less_incoming_count less_incoming_count; + + typedef typename polygon_arbitrary_formation::vertex_arbitrary_compact vertex_arbitrary_compact; + + private: + //definitions + typedef std::map scanline_data; + typedef typename scanline_data::iterator iterator; + typedef typename scanline_data::const_iterator const_iterator; + + //data + public: + inline trapezoid_arbitrary_formation() : polygon_arbitrary_formation() {} + inline trapezoid_arbitrary_formation(const trapezoid_arbitrary_formation& that) : polygon_arbitrary_formation(that) {} + inline trapezoid_arbitrary_formation& operator=(const trapezoid_arbitrary_formation& that) { + * static_cast*>(this) = * static_cast*>(&that); + return *this; + } + + //cT is an output container of Polygon45 or Polygon45WithHoles + //iT is an iterator over vertex_half_edge elements + //inputBegin - inputEnd is a range of sorted iT that represents + //one or more scanline stops worth of data + template + void scan(cT& output, iT inputBegin, iT inputEnd) { + //std::cout << "1\n"; + while(inputBegin != inputEnd) { + //std::cout << "2\n"; + polygon_arbitrary_formation::x_ = (*inputBegin).pt.get(HORIZONTAL); + //std::cout << "SCAN FORMATION " << x_ << "\n"; + //std::cout << "x_ = " << x_ << "\n"; + //std::cout << "scan line size: " << scanData_.size() << "\n"; + inputBegin = processEvent_(output, inputBegin, inputEnd); + } + //std::cout << "scan line size: " << scanData_.size() << "\n"; + } + + private: + //functions + inline void getVerticalPair_(std::pair& verticalPair, + iterator previter) { + active_tail_arbitrary* iterTail = (*previter).second; + Point prevPoint(polygon_arbitrary_formation::x_, + convert_high_precision_type(previter->first.evalAtX(polygon_arbitrary_formation::x_))); + iterTail->pushPoint(prevPoint); + std::pair tailPair = + active_tail_arbitrary::createActiveTailsAsPair(prevPoint, true, 0, false); + verticalPair.first = iterTail; + verticalPair.second = tailPair.first; + (*previter).second = tailPair.second; + } + + template + inline std::pair, active_tail_arbitrary*> + processPoint_(cT& output, cT2& elements, + std::pair& verticalPair, + iterator previter, Point point, incoming_count& counts_from_scanline, + vertex_arbitrary_count& incoming_count) { + //std::cout << "\nAT POINT: " << point << "\n"; + //join any closing solid corners + std::vector counts; + std::vector incoming; + std::vector tails; + counts.reserve(counts_from_scanline.size()); + tails.reserve(counts_from_scanline.size()); + incoming.reserve(incoming_count.size()); + for(std::size_t i = 0; i < counts_from_scanline.size(); ++i) { + counts.push_back(counts_from_scanline[i].first.second); + tails.push_back(counts_from_scanline[i].second); + } + for(std::size_t i = 0; i < incoming_count.size(); ++i) { + incoming.push_back(incoming_count[i].second); + if(incoming_count[i].first < point) { + incoming.back() = 0; + } + } + + active_tail_arbitrary* returnValue = 0; + std::pair verticalPairOut; + verticalPairOut.first = 0; + verticalPairOut.second = 0; + std::pair returnCount(Point(0, 0), 0); + int i_size_less_1 = (int)(incoming.size()) -1; + int c_size_less_1 = (int)(counts.size()) -1; + int i_size = incoming.size(); + int c_size = counts.size(); + + bool have_vertical_tail_from_below = false; + if(c_size && + scanline_base::is_vertical(counts_from_scanline.back().first.first)) { + have_vertical_tail_from_below = true; + } + //assert size = size_less_1 + 1 + //std::cout << tails.size() << " " << incoming.size() << " " << counts_from_scanline.size() << " " << incoming_count.size() << "\n"; + // for(std::size_t i = 0; i < counts.size(); ++i) { + // std::cout << counts_from_scanline[i].first.first.first.get(HORIZONTAL) << ","; + // std::cout << counts_from_scanline[i].first.first.first.get(VERTICAL) << " "; + // std::cout << counts_from_scanline[i].first.first.second.get(HORIZONTAL) << ","; + // std::cout << counts_from_scanline[i].first.first.second.get(VERTICAL) << ":"; + // std::cout << counts_from_scanline[i].first.second << " "; + // } std::cout << "\n"; + // print(incoming_count); + { + for(int i = 0; i < c_size_less_1; ++i) { + //std::cout << i << "\n"; + if(counts[i] == -1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < c_size; ++j) { + //std::cout << j << "\n"; + if(counts[j]) { + if(counts[j] == 1) { + //std::cout << "case1: " << i << " " << j << "\n"; + //if a figure is closed it will be written out by this function to output + active_tail_arbitrary::joinChains(point, tails[i], tails[j], true, output); + counts[i] = 0; + counts[j] = 0; + tails[i] = 0; + tails[j] = 0; + } + break; + } + } + } + } + } + //find any pairs of incoming edges that need to create pair for leading solid + //std::cout << "checking case2\n"; + { + for(int i = 0; i < i_size_less_1; ++i) { + //std::cout << i << "\n"; + if(incoming[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = i + 1; j < i_size; ++j) { + //std::cout << j << "\n"; + if(incoming[j]) { + //std::cout << incoming[j] << "\n"; + if(incoming[j] == -1) { + //std::cout << "case2: " << i << " " << j << "\n"; + //std::cout << "creating active tail pair\n"; + std::pair tailPair = + active_tail_arbitrary::createActiveTailsAsPair(point, true, 0, polygon_arbitrary_formation::fractureHoles_ != 0); + //tailPair.first->print(); + //tailPair.second->print(); + if(j == i_size_less_1 && incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + //vertical active tail becomes return value + returnValue = tailPair.first; + returnCount.first = point; + returnCount.second = 1; + } else { + //std::cout << "new element " << j-1 << " " << -1 << "\n"; + //std::cout << point << " " << incoming_count[j].first << "\n"; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, -1), tailPair.first)); + } + //std::cout << "new element " << i-1 << " " << 1 << "\n"; + //std::cout << point << " " << incoming_count[i].first << "\n"; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[i].first, 1), tailPair.second)); + incoming[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + } + } + //find any active tail that needs to pass through to an incoming edge + //we expect to find no more than two pass through + + //find pass through with solid on top + { + //std::cout << "checking case 3\n"; + for(int i = 0; i < c_size; ++i) { + //std::cout << i << "\n"; + if(counts[i] != 0) { + if(counts[i] == 1) { + //std::cout << "fixed i\n"; + for(int j = i_size_less_1; j >= 0; --j) { + if(incoming[j] != 0) { + if(incoming[j] == 1) { + //std::cout << "case3: " << i << " " << j << "\n"; + //tails[i]->print(); + //pass through solid on top + tails[i]->pushPoint(point); + //std::cout << "after push\n"; + if(j == i_size_less_1 && incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + returnValue = tails[i]; + returnCount.first = point; + returnCount.second = -1; + } else { + std::pair tailPair = + active_tail_arbitrary::createActiveTailsAsPair(point, true, 0, false); + verticalPairOut.first = tails[i]; + verticalPairOut.second = tailPair.first; + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, incoming[j]), tailPair.second)); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + } + //std::cout << "checking case 4\n"; + //find pass through with solid on bottom + { + for(int i = c_size_less_1; i >= 0; --i) { + //std::cout << "i = " << i << " with count " << counts[i] << "\n"; + if(counts[i] != 0) { + if(counts[i] == -1) { + for(int j = 0; j < i_size; ++j) { + if(incoming[j] != 0) { + if(incoming[j] == -1) { + //std::cout << "case4: " << i << " " << j << "\n"; + //pass through solid on bottom + + //if count from scanline is vertical + if(i == c_size_less_1 && + counts_from_scanline[i].first.first.first.get(HORIZONTAL) == + point.get(HORIZONTAL)) { + //if incoming count is vertical + if(j == i_size_less_1 && + incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + returnValue = tails[i]; + returnCount.first = point; + returnCount.second = 1; + } else { + tails[i]->pushPoint(point); + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, incoming[j]), tails[i])); + } + } else if(j == i_size_less_1 && + incoming_count[j].first.get(HORIZONTAL) == + point.get(HORIZONTAL)) { + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + active_tail_arbitrary::joinChains(point, tails[i], verticalPair.first, true, output); + returnValue = verticalPair.second; + returnCount.first = point; + returnCount.second = 1; + } else { + //neither is vertical + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + active_tail_arbitrary::joinChains(point, tails[i], verticalPair.first, true, output); + verticalPair.second->pushPoint(point); + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, incoming[j]), verticalPair.second)); + } + tails[i] = 0; + counts[i] = 0; + incoming[j] = 0; + } + break; + } + } + } + break; + } + } + } + //find the end of a hole or the beginning of a hole + + //find end of a hole + { + for(int i = 0; i < c_size_less_1; ++i) { + if(counts[i] != 0) { + for(int j = i+1; j < c_size; ++j) { + if(counts[j] != 0) { + //std::cout << "case5: " << i << " " << j << "\n"; + //we are ending a hole and may potentially close a figure and have to handle the hole + tails[i]->pushPoint(point); + verticalPairOut.first = tails[i]; + if(j == c_size_less_1 && + counts_from_scanline[j].first.first.first.get(HORIZONTAL) == + point.get(HORIZONTAL)) { + verticalPairOut.second = tails[j]; + } else { + //need to close a trapezoid below + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + active_tail_arbitrary::joinChains(point, tails[j], verticalPair.first, true, output); + verticalPairOut.second = verticalPair.second; + } + tails[i] = 0; + tails[j] = 0; + counts[i] = 0; + counts[j] = 0; + break; + } + } + break; + } + } + } + //find beginning of a hole + { + for(int i = 0; i < i_size_less_1; ++i) { + if(incoming[i] != 0) { + for(int j = i+1; j < i_size; ++j) { + if(incoming[j] != 0) { + //std::cout << "case6: " << i << " " << j << "\n"; + //we are beginning a empty space + if(verticalPair.first == 0) { + getVerticalPair_(verticalPair, previter); + } + verticalPair.second->pushPoint(point); + if(j == i_size_less_1 && + incoming_count[j].first.get(HORIZONTAL) == point.get(HORIZONTAL)) { + returnValue = verticalPair.first; + returnCount.first = point; + returnCount.second = -1; + } else { + std::pair tailPair = + active_tail_arbitrary::createActiveTailsAsPair(point, false, 0, false); + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[j].first, incoming[j]), tailPair.second)); + verticalPairOut.second = tailPair.first; + verticalPairOut.first = verticalPair.first; + } + elements.push_back(std::pair(vertex_half_edge(point, + incoming_count[i].first, incoming[i]), verticalPair.second)); + incoming[i] = 0; + incoming[j] = 0; + break; + } + } + break; + } + } + } + if(have_vertical_tail_from_below) { + if(tails.back()) { + tails.back()->pushPoint(point); + returnValue = tails.back(); + returnCount.first = point; + returnCount.second = counts.back(); + } + } + verticalPair = verticalPairOut; + //assert that tails, counts and incoming are all null + return std::pair, active_tail_arbitrary*>(returnCount, returnValue); + } + + static inline void print(const vertex_arbitrary_count& count) { + for(unsigned i = 0; i < count.size(); ++i) { + //std::cout << count[i].first.get(HORIZONTAL) << ","; + //std::cout << count[i].first.get(VERTICAL) << ":"; + //std::cout << count[i].second << " "; + } //std::cout << "\n"; + } + + static inline void print(const scanline_data& data) { + for(typename scanline_data::const_iterator itr = data.begin(); itr != data.end(); ++itr){ + //std::cout << itr->first.pt << ", " << itr->first.other_pt << "; "; + } //std::cout << "\n"; + } + + template + inline iT processEvent_(cT& output, iT inputBegin, iT inputEnd) { + //typedef typename high_precision_type::type high_precision; + //std::cout << "processEvent_\n"; + polygon_arbitrary_formation::justBefore_ = true; + //collect up all elements from the tree that are at the y + //values of events in the input queue + //create vector of new elements to add into tree + active_tail_arbitrary* verticalTail = 0; + std::pair verticalPair; + std::pair verticalCount(Point(0, 0), 0); + iT currentIter = inputBegin; + std::vector elementIters; + std::vector > elements; + while(currentIter != inputEnd && currentIter->pt.get(HORIZONTAL) == polygon_arbitrary_formation::x_) { + //std::cout << "loop\n"; + Unit currentY = (*currentIter).pt.get(VERTICAL); + //std::cout << "current Y " << currentY << "\n"; + //std::cout << "scanline size " << scanData_.size() << "\n"; + //print(scanData_); + iterator iter = this->lookUp_(currentY); + //std::cout << "found element in scanline " << (iter != scanData_.end()) << "\n"; + //int counts[4] = {0, 0, 0, 0}; + incoming_count counts_from_scanline; + //std::cout << "finding elements in tree\n"; + //if(iter != scanData_.end()) + // std::cout << "first iter y is " << iter->first.evalAtX(x_) << "\n"; + iterator previter = iter; + if(previter != polygon_arbitrary_formation::scanData_.end() && + previter->first.evalAtX(polygon_arbitrary_formation::x_) >= currentY && + previter != polygon_arbitrary_formation::scanData_.begin()) + --previter; + while(iter != polygon_arbitrary_formation::scanData_.end() && + ((iter->first.pt.x() == polygon_arbitrary_formation::x_ && iter->first.pt.y() == currentY) || + (iter->first.other_pt.x() == polygon_arbitrary_formation::x_ && iter->first.other_pt.y() == currentY))) { + //iter->first.evalAtX(polygon_arbitrary_formation::x_) == (high_precision)currentY) { + //std::cout << "loop2\n"; + elementIters.push_back(iter); + counts_from_scanline.push_back(std::pair, int>, active_tail_arbitrary*> + (std::pair, int>(std::pair(iter->first.pt, + iter->first.other_pt), + iter->first.count), + iter->second)); + ++iter; + } + Point currentPoint(polygon_arbitrary_formation::x_, currentY); + //std::cout << "counts_from_scanline size " << counts_from_scanline.size() << "\n"; + this->sort_incoming_count(counts_from_scanline, currentPoint); + + vertex_arbitrary_count incoming; + //std::cout << "aggregating\n"; + do { + //std::cout << "loop3\n"; + const vertex_half_edge& elem = *currentIter; + incoming.push_back(std::pair(elem.other_pt, elem.count)); + ++currentIter; + } while(currentIter != inputEnd && currentIter->pt.get(VERTICAL) == currentY && + currentIter->pt.get(HORIZONTAL) == polygon_arbitrary_formation::x_); + //print(incoming); + this->sort_vertex_arbitrary_count(incoming, currentPoint); + //std::cout << currentPoint.get(HORIZONTAL) << "," << currentPoint.get(VERTICAL) << "\n"; + //print(incoming); + //std::cout << "incoming counts from input size " << incoming.size() << "\n"; + //compact_vertex_arbitrary_count(currentPoint, incoming); + vertex_arbitrary_count tmp; + tmp.reserve(incoming.size()); + for(std::size_t i = 0; i < incoming.size(); ++i) { + if(currentPoint < incoming[i].first) { + tmp.push_back(incoming[i]); + } + } + incoming.swap(tmp); + //std::cout << "incoming counts from input size " << incoming.size() << "\n"; + //now counts_from_scanline has the data from the left and + //incoming has the data from the right at this point + //cancel out any end points + if(verticalTail) { + //std::cout << "adding vertical tail to counts from scanline\n"; + //std::cout << -verticalCount.second << "\n"; + counts_from_scanline.push_back(std::pair, int>, active_tail_arbitrary*> + (std::pair, int>(std::pair(verticalCount.first, + currentPoint), + -verticalCount.second), + verticalTail)); + } + if(!incoming.empty() && incoming.back().first.get(HORIZONTAL) == polygon_arbitrary_formation::x_) { + //std::cout << "inverted vertical event\n"; + incoming.back().second *= -1; + } + //std::cout << "calling processPoint_\n"; + std::pair, active_tail_arbitrary*> result = processPoint_(output, elements, verticalPair, previter, Point(polygon_arbitrary_formation::x_, currentY), counts_from_scanline, incoming); + verticalCount = result.first; + verticalTail = result.second; + if(verticalPair.first != 0 && iter != polygon_arbitrary_formation::scanData_.end() && + (currentIter == inputEnd || currentIter->pt.x() != polygon_arbitrary_formation::x_ || + currentIter->pt.y() > (*iter).first.evalAtX(polygon_arbitrary_formation::x_))) { + //splice vertical pair into edge above + active_tail_arbitrary* tailabove = (*iter).second; + Point point(polygon_arbitrary_formation::x_, + convert_high_precision_type((*iter).first.evalAtX(polygon_arbitrary_formation::x_))); + verticalPair.second->pushPoint(point); + active_tail_arbitrary::joinChains(point, tailabove, verticalPair.first, true, output); + (*iter).second = verticalPair.second; + verticalPair.first = 0; + verticalPair.second = 0; + } + } + //std::cout << "erasing\n"; + //erase all elements from the tree + for(typename std::vector::iterator iter = elementIters.begin(); + iter != elementIters.end(); ++iter) { + //std::cout << "erasing loop\n"; + polygon_arbitrary_formation::scanData_.erase(*iter); + } + //switch comparison tie breaking policy + polygon_arbitrary_formation::justBefore_ = false; + //add new elements into tree + //std::cout << "inserting\n"; + for(typename std::vector >::iterator iter = elements.begin(); + iter != elements.end(); ++iter) { + //std::cout << "inserting loop\n"; + polygon_arbitrary_formation::scanData_.insert(polygon_arbitrary_formation::scanData_.end(), *iter); + } + //std::cout << "end processEvent\n"; + return currentIter; + } + public: + template + static inline bool testTrapezoidArbitraryFormationRect(stream_type& stdcout) { + stdcout << "testing trapezoid formation\n"; + trapezoid_arbitrary_formation pf; + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(10, 10), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(10, 10), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(0, 10), 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing trapezoid formation\n"; + return true; + } + template + static inline bool testTrapezoidArbitraryFormationP1(stream_type& stdcout) { + stdcout << "testing trapezoid formation P1\n"; + trapezoid_arbitrary_formation pf; + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(10, 20), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(10, 20), -1)); + data.push_back(vertex_half_edge(Point(10, 20), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(10, 20), Point(0, 10), 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing trapezoid formation\n"; + return true; + } + template + static inline bool testTrapezoidArbitraryFormationP2(stream_type& stdcout) { + stdcout << "testing trapezoid formation P2\n"; + trapezoid_arbitrary_formation pf; + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(-3, 1), Point(2, -4), 1)); + data.push_back(vertex_half_edge(Point(-3, 1), Point(-2, 2), -1)); + data.push_back(vertex_half_edge(Point(-2, 2), Point(2, 4), -1)); + data.push_back(vertex_half_edge(Point(-2, 2), Point(-3, 1), 1)); + data.push_back(vertex_half_edge(Point(2, -4), Point(-3, 1), -1)); + data.push_back(vertex_half_edge(Point(2, -4), Point(2, 4), -1)); + data.push_back(vertex_half_edge(Point(2, 4), Point(-2, 2), 1)); + data.push_back(vertex_half_edge(Point(2, 4), Point(2, -4), 1)); + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing trapezoid formation\n"; + return true; + } + + template + static inline bool testTrapezoidArbitraryFormationPolys(stream_type& stdcout) { + stdcout << "testing trapezoid formation polys\n"; + trapezoid_arbitrary_formation pf; + std::vector > polys; + //trapezoid_arbitrary_formation pf2(true); + //std::vector > polys2; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(100, 1), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(1, 100), -1)); + data.push_back(vertex_half_edge(Point(1, 100), Point(0, 0), 1)); + data.push_back(vertex_half_edge(Point(1, 100), Point(101, 101), -1)); + data.push_back(vertex_half_edge(Point(100, 1), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(100, 1), Point(101, 101), 1)); + data.push_back(vertex_half_edge(Point(101, 101), Point(100, 1), -1)); + data.push_back(vertex_half_edge(Point(101, 101), Point(1, 100), 1)); + + data.push_back(vertex_half_edge(Point(2, 2), Point(10, 2), -1)); + data.push_back(vertex_half_edge(Point(2, 2), Point(2, 10), -1)); + data.push_back(vertex_half_edge(Point(2, 10), Point(2, 2), 1)); + data.push_back(vertex_half_edge(Point(2, 10), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(10, 2), Point(2, 2), 1)); + data.push_back(vertex_half_edge(Point(10, 2), Point(10, 10), 1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(10, 2), -1)); + data.push_back(vertex_half_edge(Point(10, 10), Point(2, 10), -1)); + + data.push_back(vertex_half_edge(Point(2, 12), Point(10, 12), -1)); + data.push_back(vertex_half_edge(Point(2, 12), Point(2, 22), -1)); + data.push_back(vertex_half_edge(Point(2, 22), Point(2, 12), 1)); + data.push_back(vertex_half_edge(Point(2, 22), Point(10, 22), 1)); + data.push_back(vertex_half_edge(Point(10, 12), Point(2, 12), 1)); + data.push_back(vertex_half_edge(Point(10, 12), Point(10, 22), 1)); + data.push_back(vertex_half_edge(Point(10, 22), Point(10, 12), -1)); + data.push_back(vertex_half_edge(Point(10, 22), Point(2, 22), -1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + //pf2.scan(polys2, data.begin(), data.end()); + //stdcout << "result size: " << polys2.size() << "\n"; + //for(std::size_t i = 0; i < polys2.size(); ++i) { + // stdcout << polys2[i] << "\n"; + //} + stdcout << "done testing trapezoid formation\n"; + return true; + } + + template + static inline bool testTrapezoidArbitraryFormationSelfTouch1(stream_type& stdcout) { + stdcout << "testing trapezoid formation self touch 1\n"; + trapezoid_arbitrary_formation pf; + std::vector > polys; + std::vector data; + data.push_back(vertex_half_edge(Point(0, 0), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(0, 0), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(0, 10), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(0, 10), Point(5, 10), -1)); + + data.push_back(vertex_half_edge(Point(10, 0), Point(0, 0), -1)); + data.push_back(vertex_half_edge(Point(10, 0), Point(10, 5), -1)); + + data.push_back(vertex_half_edge(Point(10, 5), Point(10, 0), 1)); + data.push_back(vertex_half_edge(Point(10, 5), Point(5, 5), 1)); + + data.push_back(vertex_half_edge(Point(5, 10), Point(5, 5), 1)); + data.push_back(vertex_half_edge(Point(5, 10), Point(0, 10), 1)); + + data.push_back(vertex_half_edge(Point(5, 2), Point(5, 5), -1)); + data.push_back(vertex_half_edge(Point(5, 2), Point(7, 2), -1)); + + data.push_back(vertex_half_edge(Point(5, 5), Point(5, 10), -1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(5, 2), 1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(10, 5), -1)); + data.push_back(vertex_half_edge(Point(5, 5), Point(7, 2), 1)); + + data.push_back(vertex_half_edge(Point(7, 2), Point(5, 5), -1)); + data.push_back(vertex_half_edge(Point(7, 2), Point(5, 2), 1)); + + polygon_sort(data.begin(), data.end()); + pf.scan(polys, data.begin(), data.end()); + stdcout << "result size: " << polys.size() << "\n"; + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + stdcout << "done testing trapezoid formation\n"; + return true; + } + }; + + template + struct PolyLineArbitraryByConcept { typedef poly_line_arbitrary_polygon_data type; }; + template + struct PolyLineArbitraryByConcept { typedef poly_line_arbitrary_hole_data type; }; + + template + struct geometry_concept > { typedef polygon_45_with_holes_concept type; }; + template + struct geometry_concept > { typedef polygon_45_concept type; }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_formation.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_formation.hpp new file mode 100644 index 0000000..f4c0d70 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_formation.hpp @@ -0,0 +1,2287 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#include +#include +#ifndef BOOST_POLYGON_POLYGON_FORMATION_HPP +#define BOOST_POLYGON_POLYGON_FORMATION_HPP +namespace boost { namespace polygon{ + +namespace polygon_formation { + + /* + * End has two states, HEAD and TAIL as is represented by a bool + */ + typedef bool End; + + /* + * HEAD End is represented as false because it is the lesser state + */ + const End HEAD = false; + + /* + * TAIL End is represented by true because TAIL comes after head and 1 after 0 + */ + const End TAIL = true; + + /* + * 2D turning direction, left and right sides (is a boolean value since it has two states.) + */ + typedef bool Side; + + /* + * LEFT Side is 0 because we inuitively think left to right; left < right + */ + const Side LEFT = false; + + /* + * RIGHT Side is 1 so that right > left + */ + const Side RIGHT = true; + + /* + * The PolyLine class is data storage and services for building and representing partial polygons. + * As the polyline is added to it extends its storage to accomodate the data. + * PolyLines can be joined head-to-head/head-to-tail when it is determined that two polylines are + * part of the same polygon. + * PolyLines keep state information about what orientation their incomplete head and tail geometry have, + * which side of the polyline is solid and whether the polyline is joined head-to-head and tail-to-head. + * PolyLines have nothing whatsoever to do with holes. + * It may be valuable to collect a histogram of PolyLine lengths used by an algorithm on its typical data + * sets and tune the allocation of the initial vector of coordinate data to be greater than or equal to + * the mean, median, mode, or mean plus some number of standard deviation, or just generally large enough + * to prevent too much unnecesary reallocations, but not too big that it wastes a lot of memory and degrades cache + * performance. + */ + template + class PolyLine { + private: + //data + + /* + * ptdata_ a vector of coordiantes + * if VERTICAL_HEAD, first coordiante is an X + * else first coordinate is a Y + */ + std::vector ptdata_; + + /* + * head and tail points to other polylines before and after this in a chain + */ + PolyLine* headp_; + PolyLine* tailp_; + + /* + * state bitmask + * bit zero is orientation, 0 H, 1 V + * bit 1 is head connectivity, 0 for head, 1 for tail + * bit 2 is tail connectivity, 0 for head, 1 for tail + * bit 3 is solid to left of PolyLine when 1, right when 0 + */ + int state_; + + public: + /* + * default constructor (for preallocation) + */ + PolyLine(); + + /* + * constructor that takes the orientation, coordiante and side to which there is solid + */ + PolyLine(orientation_2d orient, Unit coord, Side side); + + //copy constructor + PolyLine(const PolyLine& pline); + + //destructor + ~PolyLine(); + + //assignment operator + PolyLine& operator=(const PolyLine& that); + + //equivalence operator + bool operator==(const PolyLine& b) const; + + /* + * valid PolyLine (only default constructed polylines are invalid.) + */ + bool isValid() const; + + /* + * Orientation of Head + */ + orientation_2d headOrient() const; + + /* + * returns true if first coordinate is an X value (first segment is vertical) + */ + bool verticalHead() const; + + /* + * returns the orientation_2d fo the tail + */ + orientation_2d tailOrient() const; + + /* + * returns true if last coordinate is an X value (last segment is vertical) + */ + bool verticalTail() const; + + /* + * retrun true if PolyLine has odd number of coordiantes + */ + bool oddLength() const; + + /* + * retrun the End of the other polyline that the specified end of this polyline is connected to + */ + End endConnectivity(End end) const; + + /* + * retrun true if the head of this polyline is connect to the tail of a polyline + */ + bool headToTail() const; + /* + * retrun true if the head of this polyline is connect to the head of a polyline + */ + bool headToHead() const; + + /* + * retrun true if the tail of this polyline is connect to the tail of a polyline + */ + bool tailToTail() const; + /* + * retrun true if the tail of this polyline is connect to the head of a polyline + */ + bool tailToHead() const; + + /* + * retrun the side on which there is solid for this polyline + */ + Side solidSide() const; + + /* + * retrun true if there is solid to the right of this polyline + */ + bool solidToRight() const; + + /* + * returns true if the polyline tail is not connected + */ + bool active() const; + + /* + * adds a coordinate value to the end of the polyline changing the tail orientation + */ + PolyLine& pushCoordinate(Unit coord); + + /* + * removes a coordinate value at the end of the polyline changing the tail orientation + */ + PolyLine& popCoordinate(); + + /* + * extends the tail of the polyline to include the point, changing orientation if needed + */ + PolyLine& pushPoint(const point_data& point); + + /* + * changes the last coordinate of the tail of the polyline by the amount of the delta + */ + PolyLine& extendTail(Unit delta); + + /* + * join thisEnd of this polyline to that polyline's end + */ + PolyLine& joinTo(End thisEnd, PolyLine& that, End end); + + /* + * join an end of this polyline to the tail of that polyline + */ + PolyLine& joinToTail(PolyLine& that, End end); + + /* + * join an end of this polyline to the head of that polyline + */ + PolyLine& joinToHead(PolyLine& that, End end); + + /* + * join the head of this polyline to the head of that polyline + */ + //join this to that in the given way + PolyLine& joinHeadToHead(PolyLine& that); + + /* + * join the head of this polyline to the tail of that polyline + */ + PolyLine& joinHeadToTail(PolyLine& that); + + /* + * join the tail of this polyline to the head of that polyline + */ + PolyLine& joinTailToHead(PolyLine& that); + + /* + * join the tail of this polyline to the tail of that polyline + */ + PolyLine& joinTailToTail(PolyLine& that); + + /* + * dissconnect the tail at the end of the polygon + */ + PolyLine& disconnectTails(); + + /* + * get the coordinate at one end of this polyline, by default the tail end + */ + Unit getEndCoord(End end = TAIL) const; + + /* + * get the point on the polyline at the given index (polylines have the same number of coordinates as points + */ + point_data getPoint(unsigned int index) const; + + /* + * get the point on one end of the polyline, by default the tail + */ + point_data getEndPoint(End end = TAIL) const; + + /* + * get the orientation of a segment by index + */ + orientation_2d segmentOrient(unsigned int index = 0) const; + + /* + * get a coordinate by index using the square bracket operator + */ + Unit operator[] (unsigned int index) const; + + /* + * get the number of segments/points/coordinates in the polyline + */ + unsigned int numSegments() const; + + /* + * get the pointer to the next polyline at one end of this + */ + PolyLine* next(End end) const; + + /* + * write out coordinates of this and all attached polylines to a single vector + */ + PolyLine* writeOut(std::vector& outVec, End startEnd = TAIL) const; + + private: + //methods + PolyLine& joinTo_(End thisEnd, PolyLine& that, End end); + }; + + //forward declaration + template + class PolyLinePolygonData; + + //forward declaration + template + class PolyLinePolygonWithHolesData; + + /* + * ActiveTail represents an edge of an incomplete polygon. + * + * An ActiveTail object is the active tail end of a polyline object, which may (should) be the attached to + * a chain of polyline objects through a pointer. The ActiveTail class provides an abstraction between + * and algorithm that builds polygons and the PolyLine data representation of incomplete polygons that are + * being built. It does this by providing an iterface to access the information about the last edge at the + * tail of the PolyLine it is associated with. To a polygon constructing algorithm, an ActiveTail is a floating + * edge of an incomplete polygon and has an orientation and coordinate value, as well as knowing which side of + * that edge is supposed to be solid or space. Any incomplete polygon will have two active tails. Active tails + * may be joined together to merge two incomplete polygons into a larger incomplete polygon. If two active tails + * that are to be merged are the oppositve ends of the same incomplete polygon that indicates that the polygon + * has been closed and is complete. The active tail keeps a pointer to the other active tail of its incomplete + * polygon so that it is easy to check this condition. These pointers are updated when active tails are joined. + * The active tail also keeps a list of pointers to active tail objects that serve as handles to closed holes. In + * this way a hole can be associated to another incomplete polygon, which will eventually be its enclosing shell, + * or reassociate the hole to another incomplete polygon in the case that it become a hole itself. Alternately, + * the active tail may add a filiment to stitch a hole into a shell and "fracture" the hole out of the interior + * of a polygon. The active tail maintains a static output buffer to temporarily write polygon data to when + * it outputs a figure so that outputting a polygon does not require the allocation of a temporary buffer. This + * static buffer should be destroyed whenever the program determines that it won't need it anymore and would prefer to + * release the memory it has allocated back to the system. + */ + template + class ActiveTail { + private: + //data + PolyLine* tailp_; + ActiveTail *otherTailp_; + std::list holesList_; + //Sum of all the polylines which constitute the active tail (including holes)// + size_t polyLineSize_; + public: + + inline size_t getPolyLineSize(){ + return polyLineSize_; + } + + inline void setPolyLineSize(int delta){ + polyLineSize_ = delta; + } + + inline void addPolyLineSize(int delta){ + polyLineSize_ += delta; + } + + /* + * iterator over coordinates of the figure + */ + class iterator { + private: + const PolyLine* pLine_; + const PolyLine* pLineEnd_; + unsigned int index_; + unsigned int indexEnd_; + End startEnd_; + public: + inline iterator() : pLine_(), pLineEnd_(), index_(), indexEnd_(), startEnd_() {} + inline iterator(const ActiveTail* at, bool isHole, orientation_2d orient) : + pLine_(), pLineEnd_(), index_(), indexEnd_(), startEnd_() { + //if it is a hole and orientation is vertical or it is not a hole and orientation is horizontal + //we want to use this active tail, otherwise we want to use the other active tail + startEnd_ = TAIL; + if(!isHole ^ (orient == HORIZONTAL)) { + //switch winding direction + at = at->getOtherActiveTail(); + } + //now we have the right winding direction + //if it is horizontal we need to skip the first element + pLine_ = at->getTail(); + + if(at->getTail()->numSegments() > 0) + index_ = at->getTail()->numSegments() - 1; + + if((at->getOrient() == HORIZONTAL) ^ (orient == HORIZONTAL)) { + pLineEnd_ = at->getTail(); + indexEnd_ = pLineEnd_->numSegments() - 1; + if(index_ == 0) { + pLine_ = at->getTail()->next(HEAD); + if(at->getTail()->endConnectivity(HEAD) == TAIL) { + index_ = pLine_->numSegments() -1; + } else { + startEnd_ = HEAD; + index_ = 0; + } + } else { --index_; } + } else { + pLineEnd_ = at->getOtherActiveTail()->getTail(); + if(pLineEnd_->numSegments() > 0) + indexEnd_ = pLineEnd_->numSegments() - 1; + } + at->getTail()->joinTailToTail(*(at->getOtherActiveTail()->getTail())); + } + + inline size_t size(void){ + size_t count = 0; + End dir = startEnd_; + PolyLine const * currLine = pLine_; + size_t ops = 0; + while(currLine != pLineEnd_){ + ops++; + count += currLine->numSegments(); + currLine = currLine->next(dir == HEAD ? TAIL : HEAD); + dir = currLine->endConnectivity(dir == HEAD ? TAIL : HEAD); + } + count += pLineEnd_->numSegments(); + return count; //no. of vertices + } + + //use bitwise copy and assign provided by the compiler + inline iterator& operator++() { + if(pLine_ == pLineEnd_ && index_ == indexEnd_) { + pLine_ = 0; + index_ = 0; + return *this; + } + if(startEnd_ == HEAD) { + ++index_; + if(index_ == pLine_->numSegments()) { + End end = pLine_->endConnectivity(TAIL); + pLine_ = pLine_->next(TAIL); + if(end == TAIL) { + startEnd_ = TAIL; + index_ = pLine_->numSegments() -1; + } else { + index_ = 0; + } + } + } else { + if(index_ == 0) { + End end = pLine_->endConnectivity(HEAD); + pLine_ = pLine_->next(HEAD); + if(end == TAIL) { + index_ = pLine_->numSegments() -1; + } else { + startEnd_ = HEAD; + index_ = 0; + } + } else { + --index_; + } + } + return *this; + } + inline const iterator operator++(int) { + iterator tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator& that) const { + return pLine_ == that.pLine_ && index_ == that.index_; + } + inline bool operator!=(const iterator& that) const { + return pLine_ != that.pLine_ || index_ != that.index_; + } + inline Unit operator*() { return (*pLine_)[index_]; } + }; + + /* + * iterator over holes contained within the figure + */ + typedef typename std::list::const_iterator iteratorHoles; + + //default constructor + ActiveTail(); + + //constructor + ActiveTail(orientation_2d orient, Unit coord, Side solidToRight, ActiveTail* otherTailp); + + //constructor + ActiveTail(PolyLine* active, ActiveTail* otherTailp); + + //copy constructor + ActiveTail(const ActiveTail& that); + + //destructor + ~ActiveTail(); + + //assignment operator + ActiveTail& operator=(const ActiveTail& that); + + //equivalence operator + bool operator==(const ActiveTail& b) const; + + /* + * comparison operators, ActiveTail objects are sortable by geometry + */ + bool operator<(const ActiveTail& b) const; + bool operator<=(const ActiveTail& b) const; + bool operator>(const ActiveTail& b) const; + bool operator>=(const ActiveTail& b) const; + + /* + * get the pointer to the polyline that this is an active tail of + */ + PolyLine* getTail() const; + + /* + * get the pointer to the polyline at the other end of the chain + */ + PolyLine* getOtherTail() const; + + /* + * get the pointer to the activetail at the other end of the chain + */ + ActiveTail* getOtherActiveTail() const; + + /* + * test if another active tail is the other end of the chain + */ + bool isOtherTail(const ActiveTail& b); + + /* + * update this end of chain pointer to new polyline + */ + ActiveTail& updateTail(PolyLine* newTail); + + /* + * associate a hole to this active tail by the specified policy + */ + ActiveTail* addHole(ActiveTail* hole, bool fractureHoles); + + /* + * get the list of holes + */ + const std::list& getHoles() const; + + /* + * copy holes from that to this + */ + void copyHoles(ActiveTail& that); + + /* + * find out if solid to right + */ + bool solidToRight() const; + + /* + * get coordinate (getCoord and getCoordinate are aliases for eachother) + */ + Unit getCoord() const; + Unit getCoordinate() const; + + /* + * get the tail orientation + */ + orientation_2d getOrient() const; + + /* + * add a coordinate to the polygon at this active tail end, properly handle degenerate edges by removing redundant coordinate + */ + void pushCoordinate(Unit coord); + + /* + * write the figure that this active tail points to out to the temp buffer + */ + void writeOutFigure(std::vector& outVec, bool isHole = false) const; + + /* + * write the figure that this active tail points to out through iterators + */ + void writeOutFigureItrs(iterator& beginOut, iterator& endOut, bool isHole = false, orientation_2d orient = VERTICAL) const; + iterator begin(bool isHole, orientation_2d orient) const; + iterator end() const; + + /* + * write the holes that this active tail points to out through iterators + */ + void writeOutFigureHoleItrs(iteratorHoles& beginOut, iteratorHoles& endOut) const; + iteratorHoles beginHoles() const; + iteratorHoles endHoles() const; + + /* + * joins the two chains that the two active tail tails are ends of + * checks for closure of figure and writes out polygons appropriately + * returns a handle to a hole if one is closed + */ + static ActiveTail* joinChains(ActiveTail* at1, ActiveTail* at2, bool solid, std::vector& outBufferTmp); + template + static ActiveTail* joinChains(ActiveTail* at1, ActiveTail* at2, bool solid, typename std::vector& outBufferTmp); + + /* + * deallocate temp buffer + */ + static void destroyOutBuffer(); + + /* + * deallocate all polygon data this active tail points to (deep delete, call only from one of a pair of active tails) + */ + void destroyContents(); + }; + + /* allocate a polyline object */ + template + PolyLine* createPolyLine(orientation_2d orient, Unit coord, Side side); + + /* deallocate a polyline object */ + template + void destroyPolyLine(PolyLine* pLine); + + /* allocate an activetail object */ + template + ActiveTail* createActiveTail(); + + /* deallocate an activetail object */ + template + void destroyActiveTail(ActiveTail* aTail); + + template + class PolyLineHoleData { + private: + ActiveTail* p_; + public: + typedef Unit coordinate_type; + typedef typename ActiveTail::iterator compact_iterator_type; + typedef iterator_compact_to_points > iterator_type; + inline PolyLineHoleData() : p_(0) {} + inline PolyLineHoleData(ActiveTail* p) : p_(p) {} + //use default copy and assign + inline compact_iterator_type begin_compact() const { return p_->begin(true, (orientT ? VERTICAL : HORIZONTAL)); } + inline compact_iterator_type end_compact() const { return p_->end(); } + inline iterator_type begin() const { return iterator_type(begin_compact(), end_compact()); } + inline iterator_type end() const { return iterator_type(end_compact(), end_compact()); } + inline std::size_t size() const { + return p_->getPolyLineSize(); + } + inline ActiveTail* yield() { return p_; } + }; + + template + class PolyLinePolygonWithHolesData { + private: + ActiveTail* p_; + public: + typedef Unit coordinate_type; + typedef typename ActiveTail::iterator compact_iterator_type; + typedef iterator_compact_to_points > iterator_type; + typedef PolyLineHoleData hole_type; + typedef typename coordinate_traits::area_type area_type; + class iteratorHoles { + private: + typename ActiveTail::iteratorHoles itr_; + public: + inline iteratorHoles() : itr_() {} + inline iteratorHoles(typename ActiveTail::iteratorHoles itr) : itr_(itr) {} + //use bitwise copy and assign provided by the compiler + inline iteratorHoles& operator++() { + ++itr_; + return *this; + } + inline const iteratorHoles operator++(int) { + iteratorHoles tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iteratorHoles& that) const { + return itr_ == that.itr_; + } + inline bool operator!=(const iteratorHoles& that) const { + return itr_ != that.itr_; + } + inline PolyLineHoleData operator*() { return PolyLineHoleData(*itr_);} + }; + typedef iteratorHoles iterator_holes_type; + + inline PolyLinePolygonWithHolesData() : p_(0) {} + inline PolyLinePolygonWithHolesData(ActiveTail* p) : p_(p) {} + //use default copy and assign + inline compact_iterator_type begin_compact() const { return p_->begin(false, (orientT ? VERTICAL : HORIZONTAL)); } + inline compact_iterator_type end_compact() const { return p_->end(); } + inline iterator_type begin() const { return iterator_type(begin_compact(), end_compact()); } + inline iterator_type end() const { return iterator_type(end_compact(), end_compact()); } + inline iteratorHoles begin_holes() const { return iteratorHoles(p_->beginHoles()); } + inline iteratorHoles end_holes() const { return iteratorHoles(p_->endHoles()); } + inline ActiveTail* yield() { return p_; } + //stub out these four required functions that will not be used but are needed for the interface + inline std::size_t size_holes() const { return 0; } + inline std::size_t size() const { return 0; } + }; + + + template + struct PolyLineType { }; + template + struct PolyLineType { typedef PolyLinePolygonWithHolesData type; }; + template + struct PolyLineType { typedef PolyLinePolygonWithHolesData type; }; + template + struct PolyLineType { typedef PolyLinePolygonWithHolesData type; }; + template + struct PolyLineType { typedef PolyLineHoleData type; }; + template + struct PolyLineType { typedef PolyLineHoleData type; }; + template + struct PolyLineType { typedef PolyLineHoleData type; }; + + template + class ScanLineToPolygonItrs { + private: + std::map*> tailMap_; + typedef typename PolyLineType::type PolyLinePolygonData; + std::vector outputPolygons_; + bool fractureHoles_; + public: + typedef typename std::vector::iterator iterator; + inline ScanLineToPolygonItrs() : tailMap_(), outputPolygons_(), fractureHoles_(false) {} + /* construct a scanline with the proper offsets, protocol and options */ + inline ScanLineToPolygonItrs(bool fractureHoles) : tailMap_(), outputPolygons_(), fractureHoles_(fractureHoles) {} + + ~ScanLineToPolygonItrs() { clearOutput_(); } + + /* process all vertical edges, left and right, at a unique x coordinate, edges must be sorted low to high */ + void processEdges(iterator& beginOutput, iterator& endOutput, + Unit currentX, std::vector >& leftEdges, + std::vector >& rightEdges, + size_t vertexThreshold=(std::numeric_limits::max)() ); + + /********************************************************************** + *methods implementing new polygon formation code + * + **********************************************************************/ + void updatePartialSimplePolygonsWithRightEdges(Unit currentX, + const std::vector >& leftEdges, size_t threshold); + + void updatePartialSimplePolygonsWithLeftEdges(Unit currentX, + const std::vector >& leftEdges, size_t threshold); + + void closePartialSimplePolygon(Unit, ActiveTail*, ActiveTail*); + + void maintainPartialSimplePolygonInvariant(iterator& ,iterator& ,Unit, + const std::vector >&, + const std::vector >&, + size_t vertexThreshold=(std::numeric_limits::max)()); + + void insertNewLeftEdgeIntoTailMap(Unit, Unit, Unit, + typename std::map*>::iterator &); + /**********************************************************************/ + + inline size_t getTailMapSize(){ + typename std::map* >::const_iterator itr; + size_t tsize = 0; + for(itr=tailMap_.begin(); itr!=tailMap_.end(); ++itr){ + tsize += (itr->second)->getPolyLineSize(); + } + return tsize; + } + /*print the active tails in this map:*/ + inline void print(){ + typename std::map* >::const_iterator itr; + printf("=========TailMap[%lu]=========\n", tailMap_.size()); + for(itr=tailMap_.begin(); itr!=tailMap_.end(); ++itr){ + std::cout<< "[" << itr->first << "] : " << std::endl; + //print active tail// + ActiveTail const *t = (itr->second); + PolyLine const *pBegin = t->getTail(); + PolyLine const *pEnd = t->getOtherActiveTail()->getTail(); + std::string sorient = pBegin->solidToRight() ? "RIGHT" : "LEFT"; + std::cout<< " ActiveTail.tailp_ (solid= " << sorient ; + End dir = TAIL; + while(pBegin!=pEnd){ + std::cout << pBegin << "={ "; + for(size_t i=0; inumSegments(); i++){ + point_data u = pBegin->getPoint(i); + std::cout << "(" << u.x() << "," << u.y() << ") "; + } + std::cout << "} "; + pBegin = pBegin->next(dir == HEAD ? TAIL : HEAD); + dir = pBegin->endConnectivity(dir == HEAD ? TAIL : HEAD); + } + if(pEnd){ + std::cout << pEnd << "={ "; + for(size_t i=0; inumSegments(); i++){ + point_data u = pEnd->getPoint(i); + std::cout << "(" << u.x() << "," << u.y() << ") "; + } + std::cout << "} "; + } + std::cout << " end= " << pEnd << std::endl; + } + } + + private: + void clearOutput_(); + }; + + /* + * ScanLine does all the work of stitching together polygons from incoming vertical edges + */ +// template +// class ScanLineToPolygons { +// private: +// ScanLineToPolygonItrs scanline_; +// public: +// inline ScanLineToPolygons() : scanline_() {} +// /* construct a scanline with the proper offsets, protocol and options */ +// inline ScanLineToPolygons(bool fractureHoles) : scanline_(fractureHoles) {} + +// /* process all vertical edges, left and right, at a unique x coordinate, edges must be sorted low to high */ +// inline void processEdges(std::vector& outBufferTmp, Unit currentX, std::vector >& leftEdges, +// std::vector >& rightEdges) { +// typename ScanLineToPolygonItrs::iterator itr, endItr; +// scanline_.processEdges(itr, endItr, currentX, leftEdges, rightEdges); +// //copy data into outBufferTmp +// while(itr != endItr) { +// typename PolyLinePolygonData::iterator pditr; +// outBufferTmp.push_back(0); +// unsigned int sizeIndex = outBufferTmp.size() - 1; +// int count = 0; +// for(pditr = (*itr).begin(); pditr != (*itr).end(); ++pditr) { +// outBufferTmp.push_back(*pditr); +// ++count; +// } +// outBufferTmp[sizeIndex] = count; +// typename PolyLinePolygonData::iteratorHoles pdHoleItr; +// for(pdHoleItr = (*itr).beginHoles(); pdHoleItr != (*itr).endHoles(); ++pdHoleItr) { +// outBufferTmp.push_back(0); +// unsigned int sizeIndex2 = outBufferTmp.size() - 1; +// int count2 = 0; +// for(pditr = (*pdHoleItr).begin(); pditr != (*pdHoleItr).end(); ++pditr) { +// outBufferTmp.push_back(*pditr); +// ++count2; +// } +// outBufferTmp[sizeIndex2] = -count; +// } +// ++itr; +// } +// } +// }; + + const int VERTICAL_HEAD = 1, HEAD_TO_TAIL = 2, TAIL_TO_TAIL = 4, SOLID_TO_RIGHT = 8; + + //EVERY FUNCTION in this DEF file should be explicitly defined as inline + + //microsoft compiler improperly warns whenever you cast an integer to bool + //call this function on an integer to convert it to bool without a warning + template + inline bool to_bool(const T& val) { return val != 0; } + + //default constructor (for preallocation) + template + inline PolyLine::PolyLine() : ptdata_() ,headp_(0), tailp_(0), state_(-1) {} + + //constructor + template + inline PolyLine::PolyLine(orientation_2d orient, Unit coord, Side side) : + ptdata_(1, coord), + headp_(0), + tailp_(0), + state_(orient.to_int() + + (side << 3)){} + + //copy constructor + template + inline PolyLine::PolyLine(const PolyLine& pline) : ptdata_(pline.ptdata_), + headp_(pline.headp_), + tailp_(pline.tailp_), + state_(pline.state_) {} + + //destructor + template + inline PolyLine::~PolyLine() { + //clear out data just in case it is read later + headp_ = tailp_ = 0; + state_ = 0; + } + + template + inline PolyLine& PolyLine::operator=(const PolyLine& that) { + if(!(this == &that)) { + headp_ = that.headp_; + tailp_ = that.tailp_; + ptdata_ = that.ptdata_; + state_ = that.state_; + } + return *this; + } + + template + inline bool PolyLine::operator==(const PolyLine& b) const { + return this == &b || (state_ == b.state_ && + headp_ == b.headp_ && + tailp_ == b.tailp_); + } + + //valid PolyLine + template + inline bool PolyLine::isValid() const { + return state_ > -1; } + + //first coordinate is an X value + //first segment is vertical + template + inline bool PolyLine::verticalHead() const { + return state_ & VERTICAL_HEAD; + } + + //retrun true is PolyLine has odd number of coordiantes + template + inline bool PolyLine::oddLength() const { + return to_bool((ptdata_.size()-1) % 2); + } + + //last coordiante is an X value + //last segment is vertical + template + inline bool PolyLine::verticalTail() const { + return to_bool(verticalHead() ^ oddLength()); + } + + template + inline orientation_2d PolyLine::tailOrient() const { + return (verticalTail() ? VERTICAL : HORIZONTAL); + } + + template + inline orientation_2d PolyLine::headOrient() const { + return (verticalHead() ? VERTICAL : HORIZONTAL); + } + + template + inline End PolyLine::endConnectivity(End end) const { + //Tail should be defined as true + if(end) { return tailToTail(); } + return headToTail(); + } + + template + inline bool PolyLine::headToTail() const { + return to_bool(state_ & HEAD_TO_TAIL); + } + + template + inline bool PolyLine::headToHead() const { + return to_bool(!headToTail()); + } + + template + inline bool PolyLine::tailToHead() const { + return to_bool(!tailToTail()); + } + + template + inline bool PolyLine::tailToTail() const { + return to_bool(state_ & TAIL_TO_TAIL); + } + + template + inline Side PolyLine::solidSide() const { + return solidToRight(); } + + template + inline bool PolyLine::solidToRight() const { + return to_bool(state_ & SOLID_TO_RIGHT) != 0; + } + + template + inline bool PolyLine::active() const { + return !to_bool(tailp_); + } + + template + inline PolyLine& PolyLine::pushCoordinate(Unit coord) { + ptdata_.push_back(coord); + return *this; + } + + template + inline PolyLine& PolyLine::popCoordinate() { + ptdata_.pop_back(); + return *this; + } + + template + inline PolyLine& PolyLine::pushPoint(const point_data& point) { + if(numSegments()){ + point_data endPt = getEndPoint(); + //vertical is true, horizontal is false + if((tailOrient().to_int() ? point.get(VERTICAL) == endPt.get(VERTICAL) : point.get(HORIZONTAL) == endPt.get(HORIZONTAL))) { + //we were pushing a colinear segment + return popCoordinate(); + } + } + return pushCoordinate(tailOrient().to_int() ? point.get(VERTICAL) : point.get(HORIZONTAL)); + } + + template + inline PolyLine& PolyLine::extendTail(Unit delta) { + ptdata_.back() += delta; + return *this; + } + + //private member function that creates a link from this PolyLine to that + template + inline PolyLine& PolyLine::joinTo_(End thisEnd, PolyLine& that, End end) { + if(thisEnd){ + tailp_ = &that; + state_ &= ~TAIL_TO_TAIL; //clear any previous state_ of bit (for safety) + state_ |= (end << 2); //place bit into mask + } else { + headp_ = &that; + state_ &= ~HEAD_TO_TAIL; //clear any previous state_ of bit (for safety) + state_ |= (end << 1); //place bit into mask + } + return *this; + } + + //join two PolyLines (both ways of the association) + template + inline PolyLine& PolyLine::joinTo(End thisEnd, PolyLine& that, End end) { + joinTo_(thisEnd, that, end); + that.joinTo_(end, *this, thisEnd); + return *this; + } + + //convenience functions for joining PolyLines + template + inline PolyLine& PolyLine::joinToTail(PolyLine& that, End end) { + return joinTo(TAIL, that, end); + } + template + inline PolyLine& PolyLine::joinToHead(PolyLine& that, End end) { + return joinTo(HEAD, that, end); + } + template + inline PolyLine& PolyLine::joinHeadToHead(PolyLine& that) { + return joinToHead(that, HEAD); + } + template + inline PolyLine& PolyLine::joinHeadToTail(PolyLine& that) { + return joinToHead(that, TAIL); + } + template + inline PolyLine& PolyLine::joinTailToHead(PolyLine& that) { + return joinToTail(that, HEAD); + } + template + inline PolyLine& PolyLine::joinTailToTail(PolyLine& that) { + return joinToTail(that, TAIL); + } + + template + inline PolyLine& PolyLine::disconnectTails() { + next(TAIL)->state_ &= !TAIL_TO_TAIL; + next(TAIL)->tailp_ = 0; + state_ &= !TAIL_TO_TAIL; + tailp_ = 0; + return *this; + } + + template + inline Unit PolyLine::getEndCoord(End end) const { + if(end) + return ptdata_.back(); + return ptdata_.front(); + } + + template + inline orientation_2d PolyLine::segmentOrient(unsigned int index) const { + return (to_bool((unsigned int)verticalHead() ^ (index % 2)) ? VERTICAL : HORIZONTAL); + } + + template + inline point_data PolyLine::getPoint(unsigned int index) const { + //assert(isValid() && headp_->isValid()) ("PolyLine: headp_ must be valid"); + point_data pt; + pt.set(HORIZONTAL, ptdata_[index]); + pt.set(VERTICAL, ptdata_[index]); + Unit prevCoord; + if(index == 0) { + prevCoord = headp_->getEndCoord(headToTail()); + } else { + prevCoord = ptdata_[index-1]; + } + pt.set(segmentOrient(index), prevCoord); + return pt; + } + + template + inline point_data PolyLine::getEndPoint(End end) const { + return getPoint((end ? numSegments() - 1 : (unsigned int)0)); + } + + template + inline Unit PolyLine::operator[] (unsigned int index) const { + //assert(ptdata_.size() > index) ("PolyLine: out of bounds index"); + return ptdata_[index]; + } + + template + inline unsigned int PolyLine::numSegments() const { + return ptdata_.size(); + } + + template + inline PolyLine* PolyLine::next(End end) const { + return (end ? tailp_ : headp_); + } + + template + inline ActiveTail::ActiveTail() : tailp_(0), otherTailp_(0), holesList_(), + polyLineSize_(0) {} + + template + inline ActiveTail::ActiveTail(orientation_2d orient, Unit coord, Side solidToRight, ActiveTail* otherTailp) : + tailp_(0), otherTailp_(0), holesList_(), polyLineSize_(0) { + tailp_ = createPolyLine(orient, coord, solidToRight); + otherTailp_ = otherTailp; + polyLineSize_ = tailp_->numSegments(); + } + + template + inline ActiveTail::ActiveTail(PolyLine* active, ActiveTail* otherTailp) : + tailp_(active), otherTailp_(otherTailp), holesList_(), + polyLineSize_(0) {} + + //copy constructor + template + inline ActiveTail::ActiveTail(const ActiveTail& that) : tailp_(that.tailp_), otherTailp_(that.otherTailp_), holesList_(), polyLineSize_(that.polyLineSize_) {} + + //destructor + template + inline ActiveTail::~ActiveTail() { + //clear them in case the memory is read later + tailp_ = 0; otherTailp_ = 0; + } + + template + inline ActiveTail& ActiveTail::operator=(const ActiveTail& that) { + //self assignment is safe in this case + tailp_ = that.tailp_; + otherTailp_ = that.otherTailp_; + polyLineSize_ = that.polyLineSize_; + return *this; + } + + template + inline bool ActiveTail::operator==(const ActiveTail& b) const { + return tailp_ == b.tailp_ && otherTailp_ == b.otherTailp_; + } + + template + inline bool ActiveTail::operator<(const ActiveTail& b) const { + return tailp_->getEndPoint().get(VERTICAL) < b.tailp_->getEndPoint().get(VERTICAL); + } + + template + inline bool ActiveTail::operator<=(const ActiveTail& b) const { + return !(*this > b); } + + template + inline bool ActiveTail::operator>(const ActiveTail& b) const { + return b < (*this); } + + template + inline bool ActiveTail::operator>=(const ActiveTail& b) const { + return !(*this < b); } + + template + inline PolyLine* ActiveTail::getTail() const { + return tailp_; } + + template + inline PolyLine* ActiveTail::getOtherTail() const { + return otherTailp_->tailp_; } + + template + inline ActiveTail* ActiveTail::getOtherActiveTail() const { + return otherTailp_; } + + template + inline bool ActiveTail::isOtherTail(const ActiveTail& b) { + // assert( (tailp_ == b.getOtherTail() && getOtherTail() == b.tailp_) || + // (tailp_ != b.getOtherTail() && getOtherTail() != b.tailp_)) + // ("ActiveTail: Active tails out of sync"); + return otherTailp_ == &b; + } + + template + inline ActiveTail& ActiveTail::updateTail(PolyLine* newTail) { + //subtract the old size and add new size// + int delta = newTail->numSegments() - tailp_->numSegments(); + addPolyLineSize(delta); + otherTailp_->addPolyLineSize(delta); + tailp_ = newTail; + return *this; + } + + template + inline ActiveTail* ActiveTail::addHole(ActiveTail* hole, bool fractureHoles) { + + if(!fractureHoles){ + holesList_.push_back(hole); + copyHoles(*hole); + copyHoles(*(hole->getOtherActiveTail())); + return this; + } + ActiveTail* h, *v; + ActiveTail* other = hole->getOtherActiveTail(); + if(other->getOrient() == VERTICAL) { + //assert that hole.getOrient() == HORIZONTAL + //this case should never happen + h = hole; + v = other; + } else { + //assert that hole.getOrient() == VERTICAL + h = other; + v = hole; + } + h->pushCoordinate(v->getCoordinate()); + //assert that h->getOrient() == VERTICAL + //v->pushCoordinate(getCoordinate()); + //assert that v->getOrient() == VERTICAL + //I can't close a figure by adding a hole, so pass zero for xMin and yMin + std::vector tmpVec; + ActiveTail::joinChains(this, h, false, tmpVec); + return v; + } + + template + inline const std::list*>& ActiveTail::getHoles() const { + return holesList_; + } + + template + inline void ActiveTail::copyHoles(ActiveTail& that) { + holesList_.splice(holesList_.end(), that.holesList_); //splice the two lists together + } + + template + inline bool ActiveTail::solidToRight() const { + return getTail()->solidToRight(); } + + template + inline Unit ActiveTail::getCoord() const { + return getTail()->getEndCoord(); } + + template + inline Unit ActiveTail::getCoordinate() const { + return getCoord(); } + + template + inline orientation_2d ActiveTail::getOrient() const { + return getTail()->tailOrient(); } + + template + inline void ActiveTail::pushCoordinate(Unit coord) { + //appropriately handle any co-linear polyline segments by calling push point internally + point_data p; + p.set(HORIZONTAL, coord); + p.set(VERTICAL, coord); + //if we are vertical assign the last coordinate (an X) to p.x, else to p.y + p.set(getOrient().get_perpendicular(), getCoordinate()); + int oldSegments = tailp_->numSegments(); + tailp_->pushPoint(p); + int delta = tailp_->numSegments() - oldSegments; + addPolyLineSize(delta); + otherTailp_->addPolyLineSize(delta); + } + + + //global utility functions + template + inline PolyLine* createPolyLine(orientation_2d orient, Unit coord, Side side) { + return new PolyLine(orient, coord, side); + } + + template + inline void destroyPolyLine(PolyLine* pLine) { + delete pLine; + } + + template + inline ActiveTail* createActiveTail() { + //consider replacing system allocator with ActiveTail memory pool + return new ActiveTail(); + } + + template + inline void destroyActiveTail(ActiveTail* aTail) { + delete aTail; + } + + + //no recursion, to prevent max recursion depth errors + template + inline void ActiveTail::destroyContents() { + tailp_->disconnectTails(); + PolyLine* nextPolyLinep = tailp_->next(HEAD); + End end = tailp_->endConnectivity(HEAD); + destroyPolyLine(tailp_); + while(nextPolyLinep) { + End nextEnd = nextPolyLinep->endConnectivity(!end); //get the direction of next polyLine + PolyLine* nextNextPolyLinep = nextPolyLinep->next(!end); //get the next polyline + destroyPolyLine(nextPolyLinep); //destroy the current polyline + end = nextEnd; + nextPolyLinep = nextNextPolyLinep; + } + } + + template + inline typename ActiveTail::iterator ActiveTail::begin(bool isHole, orientation_2d orient) const { + return iterator(this, isHole, orient); + } + + template + inline typename ActiveTail::iterator ActiveTail::end() const { + return iterator(); + } + + template + inline typename ActiveTail::iteratorHoles ActiveTail::beginHoles() const { + return holesList_.begin(); + } + + template + inline typename ActiveTail::iteratorHoles ActiveTail::endHoles() const { + return holesList_.end(); + } + + template + inline void ActiveTail::writeOutFigureItrs(iterator& beginOut, iterator& endOut, bool isHole, orientation_2d orient) const { + beginOut = begin(isHole, orient); + endOut = end(); + } + + template + inline void ActiveTail::writeOutFigureHoleItrs(iteratorHoles& beginOut, iteratorHoles& endOut) const { + beginOut = beginHoles(); + endOut = endHoles(); + } + + template + inline void ActiveTail::writeOutFigure(std::vector& outVec, bool isHole) const { + //we start writing out the polyLine that this active tail points to at its tail + std::size_t size = outVec.size(); + outVec.push_back(0); //place holder for size + PolyLine* nextPolyLinep = 0; + if(!isHole){ + nextPolyLinep = otherTailp_->tailp_->writeOut(outVec); + } else { + nextPolyLinep = tailp_->writeOut(outVec); + } + Unit firsty = outVec[size + 1]; + if((getOrient() == HORIZONTAL) ^ !isHole) { + //our first coordinate is a y value, so we need to rotate it to the end + typename std::vector::iterator tmpItr = outVec.begin(); + tmpItr += size; + outVec.erase(++tmpItr); //erase the 2nd element + } + End startEnd = tailp_->endConnectivity(HEAD); + if(isHole) startEnd = otherTailp_->tailp_->endConnectivity(HEAD); + while(nextPolyLinep) { + bool nextStartEnd = nextPolyLinep->endConnectivity(!startEnd); + nextPolyLinep = nextPolyLinep->writeOut(outVec, startEnd); + startEnd = nextStartEnd; + } + if((getOrient() == HORIZONTAL) ^ !isHole) { + //we want to push the y value onto the end since we ought to have ended with an x + outVec.push_back(firsty); //should never be executed because we want first value to be an x + } + //the vector contains the coordinates of the linked list of PolyLines in the correct order + //first element is supposed to be the size + outVec[size] = outVec.size() - 1 - size; //number of coordinates in vector + //assert outVec[size] % 2 == 0 //it should be even + //make the size negative for holes + outVec[size] *= (isHole ? -1 : 1); + } + + //no recursion to prevent max recursion depth errors + template + inline PolyLine* PolyLine::writeOut(std::vector& outVec, End startEnd) const { + if(startEnd == HEAD){ + //forward order + outVec.insert(outVec.end(), ptdata_.begin(), ptdata_.end()); + return tailp_; + }else{ + //reverse order + //do not reserve because we expect outVec to be large enough already + for(int i = ptdata_.size() - 1; i >= 0; --i){ + outVec.push_back(ptdata_[i]); + } + //NT didn't know about this version of the API.... + //outVec.insert(outVec.end(), ptdata_.rbegin(), ptdata_.rend()); + return headp_; + } + } + + //solid indicates if it was joined by a solit or a space + template + inline ActiveTail* ActiveTail::joinChains(ActiveTail* at1, ActiveTail* at2, bool solid, std::vector& outBufferTmp) + { + //checks to see if we closed a figure + if(at1->isOtherTail(*at2)){ + //value of solid tells us if we closed solid or hole + //and output the solid or handle the hole appropriately + //if the hole needs to fracture across horizontal partition boundary we need to notify + //the calling context to do so + if(solid) { + //the chains are being joined because there is solid to the right + //this means that if the figure is closed at this point it must be a hole + //because otherwise it would have to have another vertex to the right of this one + //and would not be closed at this point + return at1; + } else { + //assert pG != 0 + //the figure that was closed is a shell + at1->writeOutFigure(outBufferTmp); + //process holes of the polygon + at1->copyHoles(*at2); //there should not be holes on at2, but if there are, copy them over + const std::list*>& holes = at1->getHoles(); + for(typename std::list*>::const_iterator litr = holes.begin(); litr != holes.end(); ++litr) { + (*litr)->writeOutFigure(outBufferTmp, true); + //delete the hole + (*litr)->destroyContents(); + destroyActiveTail((*litr)->getOtherActiveTail()); + destroyActiveTail((*litr)); + } + //delete the polygon + at1->destroyContents(); + //at2 contents are the same as at1, so it should not destroy them + destroyActiveTail(at1); + destroyActiveTail(at2); + } + return 0; + } + //join the two partial polygons into one large partial polygon + at1->getTail()->joinTailToTail(*(at2->getTail())); + *(at1->getOtherActiveTail()) = ActiveTail(at1->getOtherTail(), at2->getOtherActiveTail()); + *(at2->getOtherActiveTail()) = ActiveTail(at2->getOtherTail(), at1->getOtherActiveTail()); + + int accumulate = at2->getPolyLineSize() + at1->getPolyLineSize(); + (at1->getOtherActiveTail())->setPolyLineSize(accumulate); + (at2->getOtherActiveTail())->setPolyLineSize(accumulate); + + at1->getOtherActiveTail()->copyHoles(*at1); + at1->getOtherActiveTail()->copyHoles(*at2); + destroyActiveTail(at1); + destroyActiveTail(at2); + return 0; + } + + //solid indicates if it was joined by a solit or a space + template + template + inline ActiveTail* ActiveTail::joinChains(ActiveTail* at1, ActiveTail* at2, bool solid, + std::vector& outBufferTmp) { + //checks to see if we closed a figure + if(at1->isOtherTail(*at2)){ + //value of solid tells us if we closed solid or hole + //and output the solid or handle the hole appropriately + //if the hole needs to fracture across horizontal partition boundary we need to notify + //the calling context to do so + if(solid) { + //the chains are being joined because there is solid to the right + //this means that if the figure is closed at this point it must be a hole + //because otherwise it would have to have another vertex to the right of this one + //and would not be closed at this point + return at1; + } else { + //assert pG != 0 + //the figure that was closed is a shell + outBufferTmp.push_back(at1); + at1->copyHoles(*at2); //there should not be holes on at2, but if there are, copy them over + } + return 0; + } + //join the two partial polygons into one large partial polygon + at1->getTail()->joinTailToTail(*(at2->getTail())); + *(at1->getOtherActiveTail()) = ActiveTail(at1->getOtherTail(), at2->getOtherActiveTail()); + *(at2->getOtherActiveTail()) = ActiveTail(at2->getOtherTail(), at1->getOtherActiveTail()); + + int accumulate = at2->getPolyLineSize() + at1->getPolyLineSize(); + (at1->getOtherActiveTail())->setPolyLineSize(accumulate); + (at2->getOtherActiveTail())->setPolyLineSize(accumulate); + + at1->getOtherActiveTail()->copyHoles(*at1); + at1->getOtherActiveTail()->copyHoles(*at2); + destroyActiveTail(at1); + destroyActiveTail(at2); + return 0; + } + + template inline typename std::map::iterator findAtNext(std::map& theMap, + typename std::map::iterator pos, const TKey& key) + { + if(pos == theMap.end()) return theMap.find(key); + //if they match the mapItr is pointing to the correct position + if(pos->first < key) { + return theMap.find(key); + } + if(pos->first > key) { + return theMap.end(); + } + //else they are equal and no need to do anything to the iterator + return pos; + } + + // createActiveTailsAsPair is called in these two end cases of geometry + // 1. lower left concave corner + // ###| + // ###| + // ###|### + // ###|### + // 2. lower left convex corner + // |### + // |### + // | + // | + // In case 1 there may be a hole propigated up from the bottom. If the fracture option is enabled + // the two active tails that form the filament fracture line edges can become the new active tail pair + // by pushing x and y onto them. Otherwise the hole simply needs to be associated to one of the new active tails + // with add hole + template + inline std::pair*, ActiveTail*> createActiveTailsAsPair(Unit x, Unit y, bool solid, ActiveTail* phole, bool fractureHoles) { + ActiveTail* at1 = 0; + ActiveTail* at2 = 0; + if(!phole || !fractureHoles){ + at1 = createActiveTail(); + at2 = createActiveTail(); + (*at1) = ActiveTail(VERTICAL, x, solid, at2); + (*at2) = ActiveTail(HORIZONTAL, y, !solid, at1); + //provide a function through activeTail class to provide this + at1->getTail()->joinHeadToHead(*(at2->getTail())); + + at1->addPolyLineSize(1); + at2->addPolyLineSize(1); + + if(phole) + at1->addHole(phole, fractureHoles); //assert fractureHoles == false + return std::pair*, ActiveTail*>(at1, at2); + } + //assert phole is not null + //assert fractureHoles is true + if(phole->getOrient() == VERTICAL) { + at2 = phole; + } else { + at2 = phole->getOtherActiveTail(); //should never be executed since orientation is expected to be vertical + } + //assert solid == false, we should be creating a corner with solid below and to the left if there was a hole + at1 = at2->getOtherActiveTail(); + //assert at1 is horizontal + at1->pushCoordinate(x); + //assert at2 is vertical + at2->pushCoordinate(y); + + return std::pair*, ActiveTail*>(at1, at2); + } + + /* + * | + * | + * = + * |######## + * |######## (add a new ActiveTail in the tailMap_). + * |######## + * |######## + * |######## + * = + * | + * | + * + * NOTE: Call this only if you are sure that the $ledege$ is not in the tailMap_ + */ + template + inline void ScanLineToPolygonItrs:: + insertNewLeftEdgeIntoTailMap(Unit currentX, Unit yBegin, Unit yEnd, + typename std::map *>::iterator &hint){ + ActiveTail *currentTail = NULL; + std::pair*, ActiveTail*> tailPair = + createActiveTailsAsPair(currentX, yBegin, true, currentTail, + fractureHoles_); + currentTail = tailPair.first; + if(!tailMap_.empty()){ + ++hint; + } + hint = tailMap_.insert(hint, std::make_pair(yBegin, tailPair.second)); + currentTail->pushCoordinate(yEnd); ++hint; + hint = tailMap_.insert(hint, std::make_pair(yEnd, currentTail)); + } + + template + inline void ScanLineToPolygonItrs:: + closePartialSimplePolygon(Unit currentX, ActiveTail*pfig, + ActiveTail*ppfig){ + pfig->pushCoordinate(currentX); + ActiveTail::joinChains(pfig, ppfig, false, outputPolygons_); + } + /* + * If the invariant is maintained correctly then left edges can do the + * following. + * + * =### + * ####### + * ####### + * ####### + * ####### + * =### + * |### (input left edge) + * |### + * =### + * ####### + * ####### + * =### + */ + template + inline void ScanLineToPolygonItrs:: + updatePartialSimplePolygonsWithLeftEdges(Unit currentX, + const std::vector > &leftEdges, size_t vertexThreshold){ + typename std::map* >::iterator succ, succ1; + typename std::map* >::iterator pred, pred1, hint; + Unit begin, end; + ActiveTail *pfig, *ppfig; + std::pair*, ActiveTail*> tailPair; + size_t pfig_size = 0; + + hint = tailMap_.begin(); + for(size_t i=0; i < leftEdges.size(); i++){ + begin = leftEdges[i].get(LOW); end = leftEdges[i].get(HIGH); + succ = findAtNext(tailMap_, hint, begin); + pred = findAtNext(tailMap_, hint, end); + + if(succ != tailMap_.end() && pred != tailMap_.end()){ //CASE-1// + //join the corresponding active tails// + pfig = succ->second; ppfig = pred->second; + pfig_size = pfig->getPolyLineSize() + ppfig->getPolyLineSize(); + + if(pfig_size >= vertexThreshold){ + size_t bsize = pfig->getPolyLineSize(); + size_t usize = ppfig->getPolyLineSize(); + + if(usize+2 < vertexThreshold){ + //cut-off the lower piece (succ1, succ) join (succ1, pred)// + succ1 = succ; --succ1; + assert((succ1 != tailMap_.end()) && + ((succ->second)->getOtherActiveTail() == succ1->second)); + closePartialSimplePolygon(currentX, succ1->second, succ->second); + tailPair = createActiveTailsAsPair(currentX, succ1->first, + true, NULL, fractureHoles_); + + //just update the succ1 with new ActiveTail*// + succ1->second = tailPair.second; + ActiveTail::joinChains(tailPair.first, pred->second, true, + outputPolygons_); + }else if(bsize+2 < vertexThreshold){ + //cut-off the upper piece () join ()// + pred1 = pred; ++pred1; + assert(pred1 != tailMap_.end() && + ((pred1->second)->getOtherActiveTail() == pred->second)); + closePartialSimplePolygon(currentX, pred->second, pred1->second); + + //just update the pred1 with ActiveTail* = pfig// + pred1->second = pfig; + pfig->pushCoordinate(currentX); + pfig->pushCoordinate(pred1->first); + }else{ + //cut both and create an left edge between (pred->first, succ1)// + succ1 = succ; --succ1; + pred1 = pred; ++pred1; + assert(pred1 != tailMap_.end() && succ1 != tailMap_.end()); + assert((pred1->second)->getOtherActiveTail() == pred->second); + assert((succ1->second)->getOtherActiveTail() == succ->second); + + closePartialSimplePolygon(currentX, succ1->second, succ->second); + closePartialSimplePolygon(currentX, pred->second, pred1->second); + + tailPair = createActiveTailsAsPair(currentX, succ1->first, + true, NULL, fractureHoles_); + succ1->second = tailPair.second; + pred1->second = tailPair.first; + (tailPair.first)->pushCoordinate(pred1->first); + } + }else{ + //just join them with closing// + pfig->pushCoordinate(currentX); + ActiveTail::joinChains(pfig, ppfig, true, outputPolygons_); + } + hint = pred; ++hint; + tailMap_.erase(succ); tailMap_.erase(pred); + }else if(succ == tailMap_.end() && pred != tailMap_.end()){ //CASE-2// + //succ is missing in the map, first insert it into the map// + tailPair = createActiveTailsAsPair(currentX, begin, true, NULL, + fractureHoles_); + hint = pred; ++hint; + hint = tailMap_.insert(hint, std::make_pair(begin, tailPair.second)); + + pfig = pred->second; + pfig_size = pfig->getPolyLineSize() + 2; + if(pfig_size >= vertexThreshold){ + //cut-off piece from [pred, pred1] , add [begin, pred1]// + pred1 = pred; ++pred1; + assert((pred1 != tailMap_.end()) && + ((pred1->second)->getOtherActiveTail() == pred->second)); + closePartialSimplePolygon(currentX, pred->second, pred1->second); + + //update: we need left edge between (begin, pred1->first)// + pred1->second = tailPair.first; + (tailPair.first)->pushCoordinate(pred1->first); + }else{ + //just join// + ActiveTail::joinChains(tailPair.first, pfig, + true, outputPolygons_); + } + tailMap_.erase(pred); + }else if(succ != tailMap_.end() && pred == tailMap_.end()){ //CASE-3// + //pred is missing in the map, first insert it into the map// + hint = succ; ++hint; + hint = tailMap_.insert(hint, std::make_pair(end, (ActiveTail *) NULL)); + pfig = succ->second; + pfig_size = pfig->getPolyLineSize() + 2; + if(pfig_size >= vertexThreshold){ + //this figure needs cutting here// + succ1 = succ; --succ1; + assert((succ1 != tailMap_.end()) && + (succ1->second == pfig->getOtherActiveTail())); + ppfig = succ1->second; + closePartialSimplePolygon(currentX, ppfig, pfig); + + //update: we need a left edge between (succ1->first, end)// + tailPair = createActiveTailsAsPair(currentX, succ1->first, + true, NULL, fractureHoles_); + succ1->second = tailPair.second; + hint->second = tailPair.first; + (tailPair.first)->pushCoordinate(end); + }else{ + //no cutting needed// + hint->second = pfig; + pfig->pushCoordinate(currentX); + pfig->pushCoordinate(end); + } + tailMap_.erase(succ); + }else{ + //insert both pred and succ// + insertNewLeftEdgeIntoTailMap(currentX, begin, end, hint); + } + } + } + + template + inline void ScanLineToPolygonItrs:: + updatePartialSimplePolygonsWithRightEdges(Unit currentX, + const std::vector > &rightEdges, size_t vertexThreshold) + { + + typename std::map* >::iterator succ, pred, hint; + std::pair*, ActiveTail*> tailPair; + Unit begin, end; + size_t i = 0; + //If rightEdges is non-empty Then tailMap_ is non-empty // + assert(rightEdges.empty() || !tailMap_.empty() ); + + while( i < rightEdges.size() ){ + //find the interval in the tailMap which contains this interval// + pred = tailMap_.lower_bound(rightEdges[i].get(HIGH)); + assert(pred != tailMap_.end()); + succ = pred; --succ; + assert(pred != succ); + end = pred->first; begin = succ->first; + + //we now have a [begin, end] // + bool found_solid_opening = false; + bool erase_succ = true, erase_pred = true; + Unit solid_opening_begin = 0; + Unit solid_opening_end = 0; + size_t j = i+1; + ActiveTail *pfig = succ->second; + ActiveTail *ppfig = pred->second; + size_t partial_fig_size = pfig->getPolyLineSize(); + //Invariant:// + assert(succ->second && (pfig)->getOtherActiveTail() == ppfig); + + hint = succ; + Unit key = rightEdges[i].get(LOW); + if(begin != key){ + found_solid_opening = true; + solid_opening_begin = begin; solid_opening_end = key; + } + + while(j < rightEdges.size() && rightEdges[j].get(HIGH) <= end){ + if(rightEdges[j-1].get(HIGH) != rightEdges[j].get(LOW)){ + if(!found_solid_opening){ + found_solid_opening = true; + solid_opening_begin = rightEdges[j-1].get(HIGH); + solid_opening_end = rightEdges[j].get(LOW); + }else{ + ++hint; + insertNewLeftEdgeIntoTailMap(currentX, + rightEdges[j-1].get(HIGH), rightEdges[j].get(LOW), hint); + } + } + j++; + } + + //trailing edge// + if(end != rightEdges[j-1].get(HIGH)){ + if(!found_solid_opening){ + found_solid_opening = true; + solid_opening_begin = rightEdges[j-1].get(HIGH); solid_opening_end = end; + }else{ + // a solid opening has been found already, we need to insert a new left + // between [rightEdges[j-1].get(HIGH), end] + Unit lbegin = rightEdges[j-1].get(HIGH); + tailPair = createActiveTailsAsPair(currentX, lbegin, true, NULL, + fractureHoles_); + hint = tailMap_.insert(pred, std::make_pair(lbegin, tailPair.second)); + pred->second = tailPair.first; + (tailPair.first)->pushCoordinate(end); + erase_pred = false; + } + } + + size_t vertex_delta = ((begin != solid_opening_begin) && + (end != solid_opening_end)) ? 4 : 2; + + if(!found_solid_opening){ + //just close the figure, TODO: call closePartialPolygon// + pfig->pushCoordinate(currentX); + ActiveTail::joinChains(pfig, ppfig, false, outputPolygons_); + hint = pred; ++hint; + }else if(partial_fig_size+vertex_delta >= vertexThreshold){ + //close the figure and add a pseudo left-edge// + closePartialSimplePolygon(currentX, pfig, ppfig); + assert(begin != solid_opening_begin || end != solid_opening_end); + + if(begin != solid_opening_begin && end != solid_opening_end){ + insertNewLeftEdgeIntoTailMap(currentX, solid_opening_begin, + solid_opening_end, hint); + }else if(begin == solid_opening_begin){ + //we just need to update the succ in the tailMap_// + tailPair = createActiveTailsAsPair(currentX, solid_opening_begin, + true, NULL, fractureHoles_); + succ->second = tailPair.second; + hint = succ; ++hint; + hint = tailMap_.insert(pred, std::make_pair(solid_opening_end, + tailPair.first)); + (tailPair.first)->pushCoordinate(solid_opening_end); + erase_succ = false; + }else{ + //we just need to update the pred in the tailMap_// + tailPair = createActiveTailsAsPair(currentX, solid_opening_begin, + true, NULL, fractureHoles_); + hint = tailMap_.insert(pred, std::make_pair(solid_opening_begin, + tailPair.second)); + pred->second = tailPair.first; + (tailPair.first)->pushCoordinate(solid_opening_end); + erase_pred = false; + } + }else{ + //continue the figure (by adding at-most two new vertices)// + if(begin != solid_opening_begin){ + pfig->pushCoordinate(currentX); + pfig->pushCoordinate(solid_opening_begin); + //insert solid_opening_begin// + hint = succ; ++hint; + hint = tailMap_.insert(hint, std::make_pair(solid_opening_begin, pfig)); + }else{ + erase_succ = false; + } + + if(end != solid_opening_end){ + std::pair*, ActiveTail*> tailPair = + createActiveTailsAsPair(currentX, solid_opening_end, false, + NULL, fractureHoles_); + hint = pred; ++hint; + hint = tailMap_.insert(hint, std::make_pair(solid_opening_end, + tailPair.second)); + ActiveTail::joinChains(tailPair.first, ppfig, false, + outputPolygons_); + }else{ + erase_pred = false; + } + } + + //Remove the pred and succ if necessary// + if(erase_succ){ + tailMap_.erase(succ); + } + if(erase_pred){ + tailMap_.erase(pred); + } + i = j; + } + } + + // Maintains the following invariant: + // a. All the partial polygons formed at any state can be closed + // by a single edge. + template + inline void ScanLineToPolygonItrs:: + maintainPartialSimplePolygonInvariant(iterator& beginOutput, + iterator& endOutput, Unit currentX, const std::vector >& l, + const std::vector >& r, size_t vertexThreshold) { + + clearOutput_(); + if(!l.empty()){ + updatePartialSimplePolygonsWithLeftEdges(currentX, l, vertexThreshold); + } + + if(!r.empty()){ + updatePartialSimplePolygonsWithRightEdges(currentX, r, vertexThreshold); + } + beginOutput = outputPolygons_.begin(); + endOutput = outputPolygons_.end(); + + } + + //Process edges connects vertical input edges (right or left edges of figures) to horizontal edges stored as member + //data of the scanline object. It also creates now horizontal edges as needed to construct figures from edge data. + // + //There are only 12 geometric end cases where the scanline intersects a horizontal edge and even fewer unique + //actions to take: + // 1. Solid on both sides of the vertical partition after the current position and space on both sides before + // ###|### + // ###|### + // | + // | + // This case does not need to be handled because there is no vertical edge at the current x coordinate. + // + // 2. Solid on both sides of the vertical partition before the current position and space on both sides after + // | + // | + // ###|### + // ###|### + // This case does not need to be handled because there is no vertical edge at the current x coordinate. + // + // 3. Solid on the left of the vertical partition after the current position and space elsewhere + // ###| + // ###| + // | + // | + // The horizontal edge from the left is found and turns upward because of the vertical right edge to become + // the currently active vertical edge. + // + // 4. Solid on the left of the vertical partion before the current position and space elsewhere + // | + // | + // ###| + // ###| + // The horizontal edge from the left is found and joined to the currently active vertical edge. + // + // 5. Solid to the right above and below and solid to the left above current position. + // ###|### + // ###|### + // |### + // |### + // The horizontal edge from the left is found and joined to the currently active vertical edge, + // potentially closing a hole. + // + // 6. Solid on the left of the vertical partion before the current position and solid to the right above and below + // |### + // |### + // ###|### + // ###|### + // The horizontal edge from the left is found and turns upward because of the vertical right edge to become + // the currently active vertical edge. + // + // 7. Solid on the right of the vertical partition after the current position and space elsewhere + // |### + // |### + // | + // | + // Create two new ActiveTails, one is added to the horizontal edges and the other becomes the vertical currentTail + // + // 8. Solid on the right of the vertical partion before the current position and space elsewhere + // | + // | + // |### + // |### + // The currentTail vertical edge turns right and is added to the horizontal edges data + // + // 9. Solid to the right above and solid to the left above and below current position. + // ###|### + // ###|### + // ###| + // ###| + // The currentTail vertical edge turns right and is added to the horizontal edges data + // + // 10. Solid on the left of the vertical partion above and below the current position and solid to the right below + // ###| + // ###| + // ###|### + // ###|### + // Create two new ActiveTails, one is added to the horizontal edges data and the other becomes the vertical currentTail + // + // 11. Solid to the right above and solid to the left below current position. + // |### + // |### + // ###| + // ###| + // The currentTail vertical edge joins the horizontal edge from the left (may close a polygon) + // Create two new ActiveTails, one is added to the horizontal edges data and the other becomes the vertical currentTail + // + // 12. Solid on the left of the vertical partion above the current position and solid to the right below + // ###| + // ###| + // |### + // |### + // The currentTail vertical edge turns right and is added to the horizontal edges data. + // The horizontal edge from the left turns upward and becomes the currentTail vertical edge + // + template + inline void ScanLineToPolygonItrs:: + processEdges(iterator& beginOutput, iterator& endOutput, + Unit currentX, std::vector >& leftEdges, + std::vector >& rightEdges, + size_t vertexThreshold) { + clearOutput_(); + typename std::map*>::iterator nextMapItr; + //foreach edge + unsigned int leftIndex = 0; + unsigned int rightIndex = 0; + bool bottomAlreadyProcessed = false; + ActiveTail* currentTail = 0; + const Unit UnitMax = (std::numeric_limits::max)(); + + if(vertexThreshold < (std::numeric_limits::max)()){ + maintainPartialSimplePolygonInvariant(beginOutput, endOutput, currentX, + leftEdges, rightEdges, vertexThreshold); + return; + } + + nextMapItr = tailMap_.begin(); + while(leftIndex < leftEdges.size() || rightIndex < rightEdges.size()) { + interval_data edges[2] = {interval_data (UnitMax, UnitMax), + interval_data (UnitMax, UnitMax)}; + bool haveNextEdge = true; + if(leftIndex < leftEdges.size()) + edges[0] = leftEdges[leftIndex]; + else + haveNextEdge = false; + if(rightIndex < rightEdges.size()) + edges[1] = rightEdges[rightIndex]; + else + haveNextEdge = false; + bool trailingEdge = edges[1].get(LOW) < edges[0].get(LOW); + interval_data & edge = edges[trailingEdge]; + interval_data & nextEdge = edges[!trailingEdge]; + //process this edge + if(!bottomAlreadyProcessed) { + //assert currentTail = 0 + + //process the bottom end of this edge + typename std::map*>::iterator thisMapItr = findAtNext(tailMap_, nextMapItr, edge.get(LOW)); + if(thisMapItr != tailMap_.end()) { + //there is an edge in the map at the low end of this edge + //it needs to turn upward and become the current tail + ActiveTail* tail = thisMapItr->second; + if(currentTail) { + //stitch currentTail into this tail + currentTail = tail->addHole(currentTail, fractureHoles_); + if(!fractureHoles_) + currentTail->pushCoordinate(currentX); + } else { + currentTail = tail; + currentTail->pushCoordinate(currentX); + } + //assert currentTail->getOrient() == VERTICAL + nextMapItr = thisMapItr; //set nextMapItr to the next position after this one + ++nextMapItr; + //remove thisMapItr from the map + tailMap_.erase(thisMapItr); + } else { + //there is no edge in the map at the low end of this edge + //we need to create one and another one to be the current vertical tail + //if this is a trailing edge then there is space to the right of the vertical edge + //so pass the inverse of trailingEdge to indicate solid to the right + std::pair*, ActiveTail*> tailPair = + createActiveTailsAsPair(currentX, edge.get(LOW), !trailingEdge, currentTail, fractureHoles_); + currentTail = tailPair.first; + tailMap_.insert(nextMapItr, std::pair*>(edge.get(LOW), tailPair.second)); + // leave nextMapItr unchanged + } + + } + if(haveNextEdge && edge.get(HIGH) == nextEdge.get(LOW)) { + //the top of this edge is equal to the bottom of the next edge, process them both + bottomAlreadyProcessed = true; + typename std::map*>::iterator thisMapItr = findAtNext(tailMap_, nextMapItr, edge.get(HIGH)); + if(thisMapItr == tailMap_.end()) //assert this should never happen + return; + if(trailingEdge) { + //geometry at this position + // |## + // |## + // ----- + // ##| + // ##| + //current tail should join thisMapItr tail + ActiveTail* tail = thisMapItr->second; + //pass false because they are being joined because space is to the right and it will close a solid figure + ActiveTail::joinChains(currentTail, tail, false, outputPolygons_); + //two new tails are created, the vertical becomes current tail, the horizontal becomes thisMapItr tail + //pass true becuase they are created at the lower left corner of some solid + //pass null because there is no hole pointer possible + std::pair*, ActiveTail*> tailPair = + createActiveTailsAsPair(currentX, edge.get(HIGH), true, 0, fractureHoles_); + currentTail = tailPair.first; + thisMapItr->second = tailPair.second; + } else { + //geometry at this position + // ##| + // ##| + // ----- + // |## + // |## + //current tail should turn right + currentTail->pushCoordinate(edge.get(HIGH)); + //thisMapItr tail should turn up + thisMapItr->second->pushCoordinate(currentX); + //thisMapItr tail becomes current tail and current tail becomes thisMapItr tail + std::swap(currentTail, thisMapItr->second); + } + nextMapItr = thisMapItr; //set nextMapItr to the next position after this one + ++nextMapItr; + } else { + //there is a gap between the top of this edge and the bottom of the next, process the top of this edge + bottomAlreadyProcessed = false; + //process the top of this edge + typename std::map*>::iterator thisMapItr = findAtNext(tailMap_, nextMapItr, edge.get(HIGH)); + if(thisMapItr != tailMap_.end()) { + //thisMapItr is pointing to a horizontal edge in the map at the top of this vertical edge + //we need to join them and potentially close a figure + //assert currentTail != 0 + ActiveTail* tail = thisMapItr->second; + //pass the opositve of trailing edge to mean that they are joined because of solid to the right + currentTail = ActiveTail::joinChains(currentTail, tail, !trailingEdge, outputPolygons_); + nextMapItr = thisMapItr; //set nextMapItr to the next position after this one + ++nextMapItr; + if(currentTail) { //figure is not closed// + Unit nextItrY = UnitMax; + if(nextMapItr != tailMap_.end()) { + nextItrY = nextMapItr->first; + } + //for it to be a hole this must have been a left edge + Unit leftY = UnitMax; + if(leftIndex + 1 < leftEdges.size()) + leftY = leftEdges[leftIndex+1].get(LOW); + Unit rightY = nextEdge.get(LOW); + if(!haveNextEdge || (nextItrY < leftY && nextItrY < rightY)) { + //we need to add it to the next edge above it in the map + tail = nextMapItr->second; + tail = tail->addHole(currentTail, fractureHoles_); + if(fractureHoles_) { + //some small additional work stitching in the filament + tail->pushCoordinate(nextItrY); + nextMapItr->second = tail; + } + //set current tail to null + currentTail = 0; + } + } + //delete thisMapItr from the map + tailMap_.erase(thisMapItr); + } else { + //currentTail must turn right and be added into the map + currentTail->pushCoordinate(edge.get(HIGH)); + //assert currentTail->getOrient() == HORIZONTAL + tailMap_.insert(nextMapItr, std::pair*>(edge.get(HIGH), currentTail)); + //set currentTail to null + currentTail = 0; + //leave nextMapItr unchanged, it is still next + } + } + + //increment index + leftIndex += !trailingEdge; + rightIndex += trailingEdge; + } //end while + beginOutput = outputPolygons_.begin(); + endOutput = outputPolygons_.end(); + } //end function + + template + inline void ScanLineToPolygonItrs::clearOutput_() { + for(std::size_t i = 0; i < outputPolygons_.size(); ++i) { + ActiveTail* at1 = outputPolygons_[i].yield(); + const std::list*>& holes = at1->getHoles(); + for(typename std::list*>::const_iterator litr = holes.begin(); litr != holes.end(); ++litr) { + //delete the hole + (*litr)->destroyContents(); + destroyActiveTail((*litr)->getOtherActiveTail()); + destroyActiveTail((*litr)); + } + //delete the polygon + at1->destroyContents(); + //at2 contents are the same as at1, so it should not destroy them + destroyActiveTail((at1)->getOtherActiveTail()); + destroyActiveTail(at1); + } + outputPolygons_.clear(); + } + +} //polygon_formation namespace + + template + struct geometry_concept > { + typedef polygon_90_with_holes_concept type; + }; + + template + struct geometry_concept > { + typedef polygon_90_concept type; + }; + + //public API to access polygon formation algorithm + template + unsigned int get_polygons(output_container& container, + iterator_type begin, iterator_type end, orientation_2d orient, + bool fracture_holes, concept_type, + size_t sliceThreshold = (std::numeric_limits::max)() ) { + typedef typename output_container::value_type polygon_type; + typedef typename std::iterator_traits::value_type::first_type coordinate_type; + polygon_type poly; + unsigned int countPolygons = 0; + typedef typename geometry_concept::type polygon_concept_type; + polygon_formation::ScanLineToPolygonItrs scanlineToPolygonItrsV(fracture_holes); + polygon_formation::ScanLineToPolygonItrs scanlineToPolygonItrsH(fracture_holes); + std::vector > leftEdges; + std::vector > rightEdges; + coordinate_type prevPos = (std::numeric_limits::max)(); + coordinate_type prevY = (std::numeric_limits::max)(); + int count = 0; + for(iterator_type itr = begin; + itr != end; ++ itr) { + coordinate_type pos = (*itr).first; + if(pos != prevPos) { + if(orient == VERTICAL) { + typename polygon_formation::ScanLineToPolygonItrs::iterator itrPoly, itrPolyEnd; + scanlineToPolygonItrsV.processEdges(itrPoly, itrPolyEnd, prevPos, + leftEdges, rightEdges, sliceThreshold); + for( ; itrPoly != itrPolyEnd; ++ itrPoly) { + ++countPolygons; + assign(poly, *itrPoly); + container.insert(container.end(), poly); + } + } else { + typename polygon_formation::ScanLineToPolygonItrs::iterator itrPoly, itrPolyEnd; + scanlineToPolygonItrsH.processEdges(itrPoly, itrPolyEnd, prevPos, + leftEdges, rightEdges, sliceThreshold); + for( ; itrPoly != itrPolyEnd; ++ itrPoly) { + ++countPolygons; + assign(poly, *itrPoly); + container.insert(container.end(), poly); + } + } + leftEdges.clear(); + rightEdges.clear(); + prevPos = pos; + prevY = (*itr).second.first; + count = (*itr).second.second; + continue; + } + coordinate_type y = (*itr).second.first; + if(count != 0 && y != prevY) { + std::pair, int> element(interval_data(prevY, y), count); + if(element.second == 1) { + if(leftEdges.size() && leftEdges.back().high() == element.first.low()) { + encompass(leftEdges.back(), element.first); + } else { + leftEdges.push_back(element.first); + } + } else { + if(rightEdges.size() && rightEdges.back().high() == element.first.low()) { + encompass(rightEdges.back(), element.first); + } else { + rightEdges.push_back(element.first); + } + } + + } + prevY = y; + count += (*itr).second.second; + } + if(orient == VERTICAL) { + typename polygon_formation::ScanLineToPolygonItrs::iterator itrPoly, itrPolyEnd; + scanlineToPolygonItrsV.processEdges(itrPoly, itrPolyEnd, prevPos, leftEdges, rightEdges, sliceThreshold); + for( ; itrPoly != itrPolyEnd; ++ itrPoly) { + ++countPolygons; + assign(poly, *itrPoly); + container.insert(container.end(), poly); + } + } else { + typename polygon_formation::ScanLineToPolygonItrs::iterator itrPoly, itrPolyEnd; + scanlineToPolygonItrsH.processEdges(itrPoly, itrPolyEnd, prevPos, leftEdges, rightEdges, sliceThreshold); + + for( ; itrPoly != itrPolyEnd; ++ itrPoly) { + ++countPolygons; + assign(poly, *itrPoly); + container.insert(container.end(), poly); + } + } + return countPolygons; + } + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_set_view.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_set_view.hpp new file mode 100644 index 0000000..7d27091 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_set_view.hpp @@ -0,0 +1,222 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_SET_VIEW_HPP +#define BOOST_POLYGON_POLYGON_SET_VIEW_HPP +namespace boost { namespace polygon{ + + + template + inline void polygon_set_data::clean() const { + if(dirty_) { + //polygon_45_set_data tmp; + //very important: + //the 45 degree algorithm does not satisfy + //the precondition of arbitrary polygon formation + //that vertices be "linearly consistent" + //therefore it doesn't work to fall back on 45-degree + //booleans for arbitrary angle polygons + //if(0) { //downcast(tmp) ) { + // tmp.clean(); + // data_.clear(); + // is_45_ = true; + // polygon_set_data tmp2; + // tmp2.insert(tmp); + // data_.swap(tmp2.data_); + // dirty_ = false; + // sort(); + //} else { + sort(); + arbitrary_boolean_op abo; + polygon_set_data tmp2; + abo.execute(tmp2, begin(), end(), end(), end(), 0); + data_.swap(tmp2.data_); + is_45_ = tmp2.is_45_; + dirty_ = false; + //} + } + } + + template <> + inline void polygon_set_data::clean() const { + if(dirty_) { + sort(); + arbitrary_boolean_op abo; + polygon_set_data tmp2; + abo.execute(tmp2, begin(), end(), end(), end(), 0); + data_.swap(tmp2.data_); + is_45_ = tmp2.is_45_; + dirty_ = false; + } + } + + template + inline void insert_into_view_arg(value_type& dest, const arg_type& arg); + + template + class polygon_set_view; + + template + struct polygon_set_traits > { + typedef typename polygon_set_view::coordinate_type coordinate_type; + typedef typename polygon_set_view::iterator_type iterator_type; + typedef typename polygon_set_view::operator_arg_type operator_arg_type; + + static inline iterator_type begin(const polygon_set_view& polygon_set); + static inline iterator_type end(const polygon_set_view& polygon_set); + + static inline bool clean(const polygon_set_view& polygon_set); + + static inline bool sort(const polygon_set_view& polygon_set); + }; + + //template + //void execute_boolean_op(value_type& output_, const geometry_type_1& lvalue_, const geometry_type_2& rvalue_, + // double coord) { + // typedef geometry_type_1 ltype; + // typedef geometry_type_2 rtype; + // typedef typename polygon_set_traits::coordinate_type coordinate_type; + // value_type linput_; + // value_type rinput_; + // insert_into_view_arg(linput_, lvalue_); + // insert_into_view_arg(rinput_, rvalue_); + // arbitrary_boolean_op abo; + // abo.execute(output_, linput_.begin(), linput_.end(), + // rinput_.begin(), rinput_.end(), op_type); + //} + + template + void execute_boolean_op(value_type& output_, const geometry_type_1& lvalue_, const geometry_type_2& rvalue_) { + typedef geometry_type_1 ltype; + //typedef geometry_type_2 rtype; + typedef typename polygon_set_traits::coordinate_type coordinate_type; + value_type linput_; + value_type rinput_; + insert_into_view_arg(linput_, lvalue_); + insert_into_view_arg(rinput_, rvalue_); + polygon_45_set_data l45, r45, o45; +// if(linput_.downcast(l45) && rinput_.downcast(r45)) { +// //the op codes are screwed up between 45 and arbitrary +//#ifdef BOOST_POLYGON_MSVC +//#pragma warning (push) +//#pragma warning (disable: 4127) +//#endif +// if(op_type < 2) +// l45.template applyAdaptiveBoolean_(o45, r45); +// else if(op_type == 2) +// l45.template applyAdaptiveBoolean_<3>(o45, r45); +// else +// l45.template applyAdaptiveBoolean_<2>(o45, r45); +//#ifdef BOOST_POLYGON_MSVC +//#pragma warning (pop) +//#endif +// output_.insert(o45); +// } else { + arbitrary_boolean_op abo; + abo.execute(output_, linput_.begin(), linput_.end(), + rinput_.begin(), rinput_.end(), op_type); +// } + } + + template + class polygon_set_view { + public: + typedef typename polygon_set_traits::coordinate_type coordinate_type; + typedef polygon_set_data value_type; + typedef typename value_type::iterator_type iterator_type; + typedef polygon_set_view operator_arg_type; + private: + const ltype& lvalue_; + const rtype& rvalue_; + mutable value_type output_; + mutable bool evaluated_; + polygon_set_view& operator=(const polygon_set_view&); + public: + polygon_set_view(const ltype& lvalue, + const rtype& rvalue ) : + lvalue_(lvalue), rvalue_(rvalue), output_(), evaluated_(false) {} + + // get iterator to begin vertex data + public: + const value_type& value() const { + if(!evaluated_) { + evaluated_ = true; + execute_boolean_op(output_, lvalue_, rvalue_); + } + return output_; + } + public: + iterator_type begin() const { return value().begin(); } + iterator_type end() const { return value().end(); } + + bool dirty() const { return false; } //result of a boolean is clean + bool sorted() const { return true; } //result of a boolean is sorted + + void sort() const {} //is always sorted + }; + + template + typename polygon_set_traits >::iterator_type + polygon_set_traits >:: + begin(const polygon_set_view& polygon_set) { + return polygon_set.begin(); + } + template + typename polygon_set_traits >::iterator_type + polygon_set_traits >:: + end(const polygon_set_view& polygon_set) { + return polygon_set.end(); + } + template + bool polygon_set_traits >:: + clean(const polygon_set_view& ) { + return true; } + template + bool polygon_set_traits >:: + sort(const polygon_set_view& ) { + return true; } + + template + inline void insert_into_view_arg(value_type& dest, const arg_type& arg) { + typedef typename polygon_set_traits::iterator_type literator; + literator itr1, itr2; + itr1 = polygon_set_traits::begin(arg); + itr2 = polygon_set_traits::end(arg); + dest.insert(itr1, itr2); + } + + template + geometry_type_1& self_assignment_boolean_op(geometry_type_1& lvalue_, const geometry_type_2& rvalue_) { + typedef geometry_type_1 ltype; + typedef typename polygon_set_traits::coordinate_type coordinate_type; + typedef polygon_set_data value_type; + value_type output_; + execute_boolean_op(output_, lvalue_, rvalue_); + polygon_set_mutable_traits::set(lvalue_, output_.begin(), output_.end()); + return lvalue_; + } + + // copy constructor + template + template + polygon_set_data::polygon_set_data(const polygon_set_view& that) : + data_(that.value().data_), dirty_(that.value().dirty_), unsorted_(that.value().unsorted_), is_45_(that.value().is_45_) {} + + // equivalence operator + template + inline bool polygon_set_data::operator==(const polygon_set_data& p) const { + typedef polygon_set_data value_type; + value_type output_; + execute_boolean_op(output_, (*this), p); + return output_.data_.empty(); + } + + template + struct geometry_concept > { typedef polygon_set_concept type; }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_simplify.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_simplify.hpp new file mode 100644 index 0000000..4871f50 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_simplify.hpp @@ -0,0 +1,116 @@ +// Copyright 2011, Andrew Ross +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). +#ifndef BOOST_POLYGON_DETAIL_SIMPLIFY_HPP +#define BOOST_POLYGON_DETAIL_SIMPLIFY_HPP +#include + +namespace boost { namespace polygon { namespace detail { namespace simplify_detail { + + // Does a simplification/optimization pass on the polygon. If a given + // vertex lies within "len" of the line segment joining its neighbor + // vertices, it is removed. + template //T is a model of point concept + std::size_t simplify(std::vector& dst, const std::vector& src, + typename coordinate_traits< + typename point_traits::coordinate_type + >::coordinate_distance len) + { + using namespace boost::polygon; + typedef typename point_traits::coordinate_type coordinate_type; + typedef typename coordinate_traits::area_type ftype; + typedef typename std::vector::const_iterator iter; + + std::vector out; + out.reserve(src.size()); + dst = src; + std::size_t final_result = 0; + std::size_t orig_size = src.size(); + + //I can't use == if T doesn't provide it, so use generic point concept compare + bool closed = equivalence(src.front(), src.back()); + + //we need to keep smoothing until we don't find points to remove + //because removing points in the first iteration through the + //polygon may leave it in a state where more removal is possible + bool not_done = true; + while(not_done) { + if(dst.size() < 3) { + dst.clear(); + return orig_size; + } + + // Start with the second, test for the last point + // explicitly, and exit after looping back around to the first. + ftype len2 = ftype(len) * ftype(len); + for(iter prev=dst.begin(), i=prev+1, next; /**/; i = next) { + next = i+1; + if(next == dst.end()) + next = dst.begin(); + + // points A, B, C + ftype ax = x(*prev), ay = y(*prev); + ftype bx = x(*i), by = y(*i); + ftype cx = x(*next), cy = y(*next); + + // vectors AB, BC and AC: + ftype abx = bx-ax, aby = by-ay; + ftype bcx = cx-bx, bcy = cy-by; + ftype acx = cx-ax, acy = cy-ay; + + // dot products + ftype ab_ab = abx*abx + aby*aby; + ftype bc_bc = bcx*bcx + bcy*bcy; + ftype ac_ac = acx*acx + acy*acy; + ftype ab_ac = abx*acx + aby*acy; + + // projection of AB along AC + ftype projf = ab_ac / ac_ac; + ftype projx = acx * projf, projy = acy * projf; + + // perpendicular vector from the line AC to point B (i.e. AB - proj) + ftype perpx = abx - projx, perpy = aby - projy; + + // Squared fractional distance of projection. FIXME: can + // remove this division, the decisions below can be made with + // just the sign of the quotient and a check to see if + // abs(numerator) is greater than abs(divisor). + ftype f2 = (projx*acx + projy*acx) / ac_ac; + + // Square of the relevant distance from point B: + ftype dist2; + if (f2 < 0) dist2 = ab_ab; + else if(f2 > 1) dist2 = bc_bc; + else dist2 = perpx*perpx + perpy*perpy; + + if(dist2 > len2) { + prev = i; // bump prev, we didn't remove the segment + out.push_back(*i); + } + + if(i == dst.begin()) + break; + } + std::size_t result = dst.size() - out.size(); + if(result == 0) { + not_done = false; + } else { + final_result += result; + dst = out; + out.clear(); + } + } //end of while loop + if(closed) { + //if the input was closed we want the output to be closed + --final_result; + dst.push_back(dst.front()); + } + return final_result; + } + + +}}}} + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_sort_adaptor.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_sort_adaptor.hpp new file mode 100644 index 0000000..b3561f8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/polygon_sort_adaptor.hpp @@ -0,0 +1,67 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_SORT_ADAPTOR_HPP +#define BOOST_POLYGON_SORT_ADAPTOR_HPP +#ifdef __ICC +#pragma warning(disable:2022) +#pragma warning(disable:2023) +#endif + +#include + +//! @brief polygon_sort_adaptor default implementation that calls std::sort +namespace boost { + namespace polygon { + + template + struct dummy_to_delay_instantiation{ + typedef int unit_type; // default GTL unit + }; + + //! @brief polygon_sort_adaptor default implementation that calls std::sort + template + struct polygon_sort_adaptor { + //! @brief wrapper that mimics std::sort() function and takes + // the same arguments + template + static void sort(RandomAccessIterator_Type _First, + RandomAccessIterator_Type _Last) + { + std::sort(_First, _Last); + } + //! @brief wrapper that mimics std::sort() function overload and takes + // the same arguments + template + static void sort(RandomAccessIterator_Type _First, + RandomAccessIterator_Type _Last, + const Pred_Type& _Comp) + { + std::sort(_First, _Last, _Comp); + } + }; + + //! @brief user level wrapper for sorting quantities + template + void polygon_sort(iter_type _b_, iter_type _e_) + { + polygon_sort_adaptor::unit_type>::sort(_b_, _e_); + } + + //! @brief user level wrapper for sorting quantities that takes predicate + // as additional argument + template + void polygon_sort(iter_type _b_, iter_type _e_, const pred_type& _pred_) + { + polygon_sort_adaptor::unit_type>::sort(_b_, _e_, _pred_); + } + + + + } // namespace polygon +} // namespace boost +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge.hpp new file mode 100644 index 0000000..6ea0161 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge.hpp @@ -0,0 +1,588 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_PROPERTY_MERGE_HPP +#define BOOST_POLYGON_PROPERTY_MERGE_HPP +namespace boost { namespace polygon{ + +template +class property_merge_point { +private: + coordinate_type x_, y_; +public: + inline property_merge_point() : x_(), y_() {} + inline property_merge_point(coordinate_type x, coordinate_type y) : x_(x), y_(y) {} + //use builtin assign and copy + inline bool operator==(const property_merge_point& that) const { return x_ == that.x_ && y_ == that.y_; } + inline bool operator!=(const property_merge_point& that) const { return !((*this) == that); } + inline bool operator<(const property_merge_point& that) const { + if(x_ < that.x_) return true; + if(x_ > that.x_) return false; + return y_ < that.y_; + } + inline coordinate_type x() const { return x_; } + inline coordinate_type y() const { return y_; } + inline void x(coordinate_type value) { x_ = value; } + inline void y(coordinate_type value) { y_ = value; } +}; + +template +class property_merge_interval { +private: + coordinate_type low_, high_; +public: + inline property_merge_interval() : low_(), high_() {} + inline property_merge_interval(coordinate_type low, coordinate_type high) : low_(low), high_(high) {} + //use builtin assign and copy + inline bool operator==(const property_merge_interval& that) const { return low_ == that.low_ && high_ == that.high_; } + inline bool operator!=(const property_merge_interval& that) const { return !((*this) == that); } + inline bool operator<(const property_merge_interval& that) const { + if(low_ < that.low_) return true; + if(low_ > that.low_) return false; + return high_ < that.high_; + } + inline coordinate_type low() const { return low_; } + inline coordinate_type high() const { return high_; } + inline void low(coordinate_type value) { low_ = value; } + inline void high(coordinate_type value) { high_ = value; } +}; + +template > +class merge_scanline { +public: + //definitions + + typedef keytype property_set; + typedef std::vector > property_map; + typedef std::pair, std::pair > vertex_property; + typedef std::pair, property_map> vertex_data; + typedef std::vector property_merge_data; + //typedef std::map Result; + typedef std::map scanline_type; + typedef typename scanline_type::iterator scanline_iterator; + typedef std::pair, std::pair > edge_property; + typedef std::vector edge_property_vector; + + //static public member functions + + template + static inline void + populate_property_merge_data(property_merge_data& pmd, iT input_begin, iT input_end, + const property_type& property, orientation_2d_type orient) { + for( ; input_begin != input_end; ++input_begin) { + std::pair, std::pair > element; + if(orient == HORIZONTAL) + element.first = property_merge_point((*input_begin).second.first, (*input_begin).first); + else + element.first = property_merge_point((*input_begin).first, (*input_begin).second.first); + element.second.first = property; + element.second.second = (*input_begin).second.second; + pmd.push_back(element); + } + } + + //public member functions + + merge_scanline() : output(), scanline(), currentVertex(), tmpVector(), previousY(), countFromBelow(), scanlinePosition() {} + merge_scanline(const merge_scanline& that) : + output(that.output), + scanline(that.scanline), + currentVertex(that.currentVertex), + tmpVector(that.tmpVector), + previousY(that.previousY), + countFromBelow(that.countFromBelow), + scanlinePosition(that.scanlinePosition) + {} + merge_scanline& operator=(const merge_scanline& that) { + output = that.output; + scanline = that.scanline; + currentVertex = that.currentVertex; + tmpVector = that.tmpVector; + previousY = that.previousY; + countFromBelow = that.countFromBelow; + scanlinePosition = that.scanlinePosition; + return *this; + } + + template + inline void perform_merge(result_type& result, property_merge_data& data) { + if(data.empty()) return; + //sort + polygon_sort(data.begin(), data.end(), less_vertex_data()); + //scanline + bool firstIteration = true; + scanlinePosition = scanline.end(); + for(std::size_t i = 0; i < data.size(); ++i) { + if(firstIteration) { + mergeProperty(currentVertex.second, data[i].second); + currentVertex.first = data[i].first; + firstIteration = false; + } else { + if(data[i].first != currentVertex.first) { + if(data[i].first.x() != currentVertex.first.x()) { + processVertex(output); + //std::cout << scanline.size() << " "; + countFromBelow.clear(); //should already be clear + writeOutput(currentVertex.first.x(), result, output); + currentVertex.second.clear(); + mergeProperty(currentVertex.second, data[i].second); + currentVertex.first = data[i].first; + //std::cout << assertRedundant(scanline) << "/" << scanline.size() << " "; + } else { + processVertex(output); + currentVertex.second.clear(); + mergeProperty(currentVertex.second, data[i].second); + currentVertex.first = data[i].first; + } + } else { + mergeProperty(currentVertex.second, data[i].second); + } + } + } + processVertex(output); + writeOutput(currentVertex.first.x(), result, output); + //std::cout << assertRedundant(scanline) << "/" << scanline.size() << "\n"; + //std::cout << scanline.size() << "\n"; + } + +private: + //private supporting types + + template + class less_vertex_data { + public: + less_vertex_data() {} + bool operator()(const T& lvalue, const T& rvalue) const { + if(lvalue.first.x() < rvalue.first.x()) return true; + if(lvalue.first.x() > rvalue.first.x()) return false; + if(lvalue.first.y() < rvalue.first.y()) return true; + return false; + } + }; + + template + struct lessPropertyCount { + lessPropertyCount() {} + bool operator()(const T& a, const T& b) { + return a.first < b.first; + } + }; + + //private static member functions + + static inline void mergeProperty(property_map& lvalue, std::pair& rvalue) { + typename property_map::iterator itr = std::lower_bound(lvalue.begin(), lvalue.end(), rvalue, + lessPropertyCount >()); + if(itr == lvalue.end() || + (*itr).first != rvalue.first) { + lvalue.insert(itr, rvalue); + } else { + (*itr).second += rvalue.second; + if((*itr).second == 0) + lvalue.erase(itr); + } +// if(assertSorted(lvalue)) { +// std::cout << "in mergeProperty\n"; +// exit(0); +// } + } + +// static inline bool assertSorted(property_map& pset) { +// bool result = false; +// for(std::size_t i = 1; i < pset.size(); ++i) { +// if(pset[i] < pset[i-1]) { +// std::cout << "Out of Order Error "; +// result = true; +// } +// if(pset[i].first == pset[i-1].first) { +// std::cout << "Duplicate Property Error "; +// result = true; +// } +// if(pset[0].second == 0 || pset[1].second == 0) { +// std::cout << "Empty Property Error "; +// result = true; +// } +// } +// return result; +// } + + static inline void setProperty(property_set& pset, property_map& pmap) { + for(typename property_map::iterator itr = pmap.begin(); itr != pmap.end(); ++itr) { + if((*itr).second > 0) { + pset.insert(pset.end(), (*itr).first); + } + } + } + + //private data members + + edge_property_vector output; + scanline_type scanline; + vertex_data currentVertex; + property_map tmpVector; + coordinate_type previousY; + property_map countFromBelow; + scanline_iterator scanlinePosition; + + //private member functions + + inline void mergeCount(property_map& lvalue, property_map& rvalue) { + typename property_map::iterator litr = lvalue.begin(); + typename property_map::iterator ritr = rvalue.begin(); + tmpVector.clear(); + while(litr != lvalue.end() && ritr != rvalue.end()) { + if((*litr).first <= (*ritr).first) { + if(!tmpVector.empty() && + (*litr).first == tmpVector.back().first) { + tmpVector.back().second += (*litr).second; + } else { + tmpVector.push_back(*litr); + } + ++litr; + } else if((*ritr).first <= (*litr).first) { + if(!tmpVector.empty() && + (*ritr).first == tmpVector.back().first) { + tmpVector.back().second += (*ritr).second; + } else { + tmpVector.push_back(*ritr); + } + ++ritr; + } + } + while(litr != lvalue.end()) { + if(!tmpVector.empty() && + (*litr).first == tmpVector.back().first) { + tmpVector.back().second += (*litr).second; + } else { + tmpVector.push_back(*litr); + } + ++litr; + } + while(ritr != rvalue.end()) { + if(!tmpVector.empty() && + (*ritr).first == tmpVector.back().first) { + tmpVector.back().second += (*ritr).second; + } else { + tmpVector.push_back(*ritr); + } + ++ritr; + } + lvalue.clear(); + for(std::size_t i = 0; i < tmpVector.size(); ++i) { + if(tmpVector[i].second != 0) { + lvalue.push_back(tmpVector[i]); + } + } +// if(assertSorted(lvalue)) { +// std::cout << "in mergeCount\n"; +// exit(0); +// } + } + + inline void processVertex(edge_property_vector& output) { + if(!countFromBelow.empty()) { + //we are processing an interval of change in scanline state between + //previous vertex position and current vertex position where + //count from below represents the change on the interval + //foreach scanline element from previous to current we + //write the interval on the scanline that is changing + //the old value and the new value to output + property_merge_interval currentInterval(previousY, currentVertex.first.y()); + coordinate_type currentY = currentInterval.low(); + if(scanlinePosition == scanline.end() || + (*scanlinePosition).first != previousY) { + scanlinePosition = scanline.lower_bound(previousY); + } + scanline_iterator previousScanlinePosition = scanlinePosition; + ++scanlinePosition; + while(scanlinePosition != scanline.end()) { + coordinate_type elementY = (*scanlinePosition).first; + if(elementY <= currentInterval.high()) { + property_map& countOnLeft = (*previousScanlinePosition).second; + edge_property element; + output.push_back(element); + output.back().first = property_merge_interval((*previousScanlinePosition).first, elementY); + setProperty(output.back().second.first, countOnLeft); + mergeCount(countOnLeft, countFromBelow); + setProperty(output.back().second.second, countOnLeft); + if(output.back().second.first == output.back().second.second) { + output.pop_back(); //it was an internal vertical edge, not to be output + } + else if(output.size() > 1) { + edge_property& secondToLast = output[output.size()-2]; + if(secondToLast.first.high() == output.back().first.low() && + secondToLast.second.first == output.back().second.first && + secondToLast.second.second == output.back().second.second) { + //merge output onto previous output because properties are + //identical on both sides implying an internal horizontal edge + secondToLast.first.high(output.back().first.high()); + output.pop_back(); + } + } + if(previousScanlinePosition == scanline.begin()) { + if(countOnLeft.empty()) { + scanline.erase(previousScanlinePosition); + } + } else { + scanline_iterator tmpitr = previousScanlinePosition; + --tmpitr; + if((*tmpitr).second == (*previousScanlinePosition).second) + scanline.erase(previousScanlinePosition); + } + + } else if(currentY < currentInterval.high()){ + //elementY > currentInterval.high() + //split the interval between previous and current scanline elements + std::pair elementScan; + elementScan.first = currentInterval.high(); + elementScan.second = (*previousScanlinePosition).second; + scanlinePosition = scanline.insert(scanlinePosition, elementScan); + continue; + } else { + break; + } + previousScanlinePosition = scanlinePosition; + currentY = previousY = elementY; + ++scanlinePosition; + if(scanlinePosition == scanline.end() && + currentY < currentInterval.high()) { + //insert a new element for top of range + std::pair elementScan; + elementScan.first = currentInterval.high(); + scanlinePosition = scanline.insert(scanline.end(), elementScan); + } + } + if(scanlinePosition == scanline.end() && + currentY < currentInterval.high()) { + //handle case where we iterated to end of the scanline + //we need to insert an element into the scanline at currentY + //with property value coming from below + //and another one at currentInterval.high() with empty property value + mergeCount(scanline[currentY], countFromBelow); + std::pair elementScan; + elementScan.first = currentInterval.high(); + scanline.insert(scanline.end(), elementScan); + + edge_property element; + output.push_back(element); + output.back().first = property_merge_interval(currentY, currentInterval.high()); + setProperty(output.back().second.second, countFromBelow); + mergeCount(countFromBelow, currentVertex.second); + } else { + mergeCount(countFromBelow, currentVertex.second); + if(countFromBelow.empty()) { + if(previousScanlinePosition == scanline.begin()) { + if((*previousScanlinePosition).second.empty()) { + scanline.erase(previousScanlinePosition); + //previousScanlinePosition = scanline.end(); + //std::cout << "ERASE_A "; + } + } else { + scanline_iterator tmpitr = previousScanlinePosition; + --tmpitr; + if((*tmpitr).second == (*previousScanlinePosition).second) { + scanline.erase(previousScanlinePosition); + //previousScanlinePosition = scanline.end(); + //std::cout << "ERASE_B "; + } + } + } + } + } else { + //count from below is empty, we are starting a new interval of change + countFromBelow = currentVertex.second; + scanlinePosition = scanline.lower_bound(currentVertex.first.y()); + if(scanlinePosition != scanline.end()) { + if((*scanlinePosition).first != currentVertex.first.y()) { + if(scanlinePosition != scanline.begin()) { + //decrement to get the lower position of the first interval this vertex intersects + --scanlinePosition; + //insert a new element into the scanline for the incoming vertex + property_map& countOnLeft = (*scanlinePosition).second; + std::pair element(currentVertex.first.y(), countOnLeft); + scanlinePosition = scanline.insert(scanlinePosition, element); + } else { + property_map countOnLeft; + std::pair element(currentVertex.first.y(), countOnLeft); + scanlinePosition = scanline.insert(scanlinePosition, element); + } + } + } else { + property_map countOnLeft; + std::pair element(currentVertex.first.y(), countOnLeft); + scanlinePosition = scanline.insert(scanlinePosition, element); + } + } + previousY = currentVertex.first.y(); + } + + template + inline int assertRedundant(T& t) { + if(t.empty()) return 0; + int count = 0; + typename T::iterator itr = t.begin(); + if((*itr).second.empty()) + ++count; + typename T::iterator itr2 = itr; + ++itr2; + while(itr2 != t.end()) { + if((*itr).second == (*itr2).second) + ++count; + itr = itr2; + ++itr2; + } + return count; + } + + template + inline void performExtract(T& result, property_merge_data& data) { + if(data.empty()) return; + //sort + polygon_sort(data.begin(), data.end(), less_vertex_data()); + + //scanline + bool firstIteration = true; + scanlinePosition = scanline.end(); + for(std::size_t i = 0; i < data.size(); ++i) { + if(firstIteration) { + mergeProperty(currentVertex.second, data[i].second); + currentVertex.first = data[i].first; + firstIteration = false; + } else { + if(data[i].first != currentVertex.first) { + if(data[i].first.x() != currentVertex.first.x()) { + processVertex(output); + //std::cout << scanline.size() << " "; + countFromBelow.clear(); //should already be clear + writeGraph(result, output, scanline); + currentVertex.second.clear(); + mergeProperty(currentVertex.second, data[i].second); + currentVertex.first = data[i].first; + } else { + processVertex(output); + currentVertex.second.clear(); + mergeProperty(currentVertex.second, data[i].second); + currentVertex.first = data[i].first; + } + } else { + mergeProperty(currentVertex.second, data[i].second); + } + } + } + processVertex(output); + writeGraph(result, output, scanline); + //std::cout << scanline.size() << "\n"; + } + + template + inline void insertEdges(T& graph, property_set& p1, property_set& p2) { + for(typename property_set::iterator itr = p1.begin(); itr != p1.end(); ++itr) { + for(typename property_set::iterator itr2 = p2.begin(); itr2 != p2.end(); ++itr2) { + if(*itr != *itr2) { + graph[*itr].insert(*itr2); + graph[*itr2].insert(*itr); + } + } + } + } + + template + inline void propertySetAbove(coordinate_type y, property_set& ps, T& scanline) { + ps.clear(); + typename T::iterator itr = scanline.find(y); + if(itr != scanline.end()) + setProperty(ps, (*itr).second); + } + + template + inline void propertySetBelow(coordinate_type y, property_set& ps, T& scanline) { + ps.clear(); + typename T::iterator itr = scanline.find(y); + if(itr != scanline.begin()) { + --itr; + setProperty(ps, (*itr).second); + } + } + + template + inline void writeGraph(T& graph, edge_property_vector& output, T2& scanline) { + if(output.empty()) return; + edge_property* previousEdgeP = &(output[0]); + bool firstIteration = true; + property_set ps; + for(std::size_t i = 0; i < output.size(); ++i) { + edge_property& previousEdge = *previousEdgeP; + edge_property& edge = output[i]; + if(previousEdge.first.high() == edge.first.low()) { + //horizontal edge + insertEdges(graph, edge.second.first, previousEdge.second.first); + //corner 1 + insertEdges(graph, edge.second.first, previousEdge.second.second); + //other horizontal edge + insertEdges(graph, edge.second.second, previousEdge.second.second); + //corner 2 + insertEdges(graph, edge.second.second, previousEdge.second.first); + } else { + if(!firstIteration){ + //look up regions above previous edge + propertySetAbove(previousEdge.first.high(), ps, scanline); + insertEdges(graph, ps, previousEdge.second.first); + insertEdges(graph, ps, previousEdge.second.second); + } + //look up regions below current edge in the scanline + propertySetBelow(edge.first.high(), ps, scanline); + insertEdges(graph, ps, edge.second.first); + insertEdges(graph, ps, edge.second.second); + } + firstIteration = false; + //vertical edge + insertEdges(graph, edge.second.second, edge.second.first); + //shared region to left + insertEdges(graph, edge.second.second, edge.second.second); + //shared region to right + insertEdges(graph, edge.second.first, edge.second.first); + previousEdgeP = &(output[i]); + } + edge_property& previousEdge = *previousEdgeP; + propertySetAbove(previousEdge.first.high(), ps, scanline); + insertEdges(graph, ps, previousEdge.second.first); + insertEdges(graph, ps, previousEdge.second.second); + output.clear(); + } + + template + inline void writeOutput(coordinate_type x, Result& result, edge_property_vector& output) { + for(std::size_t i = 0; i < output.size(); ++i) { + edge_property& edge = output[i]; + //edge.second.first is the property set on the left of the edge + if(!edge.second.first.empty()) { + typename Result::iterator itr = result.find(edge.second.first); + if(itr == result.end()) { + std::pair element(edge.second.first, polygon_set_type(VERTICAL)); + itr = result.insert(result.end(), element); + } + std::pair, int> element2(interval_data(edge.first.low(), edge.first.high()), -1); //right edge of figure + (*itr).second.insert(x, element2); + } + if(!edge.second.second.empty()) { + //edge.second.second is the property set on the right of the edge + typename Result::iterator itr = result.find(edge.second.second); + if(itr == result.end()) { + std::pair element(edge.second.second, polygon_set_type(VERTICAL)); + itr = result.insert(result.end(), element); + } + std::pair, int> element3(interval_data(edge.first.low(), edge.first.high()), 1); //left edge of figure + (*itr).second.insert(x, element3); + } + } + output.clear(); + } +}; + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge_45.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge_45.hpp new file mode 100644 index 0000000..c2feffc --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/property_merge_45.hpp @@ -0,0 +1,160 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_PROPERTY_MERGE_45_HPP +#define BOOST_POLYGON_PROPERTY_MERGE_45_HPP +namespace boost { namespace polygon{ + + template + struct polygon_45_property_merge { + + typedef point_data Point; + typedef typename coordinate_traits::manhattan_area_type LongUnit; + + template + static inline void merge_property_maps(property_map& mp, const property_map& mp2, bool subtract = false) { + polygon_45_touch::merge_property_maps(mp, mp2, subtract); + } + + class CountMerge { + public: + inline CountMerge() : counts() {} + //inline CountMerge(int count) { counts[0] = counts[1] = count; } + //inline CountMerge(int count1, int count2) { counts[0] = count1; counts[1] = count2; } + inline CountMerge(const CountMerge& count) : counts(count.counts) {} + inline bool operator==(const CountMerge& count) const { return counts == count.counts; } + inline bool operator!=(const CountMerge& count) const { return !((*this) == count); } + //inline CountMerge& operator=(int count) { counts[0] = counts[1] = count; return *this; } + inline CountMerge& operator=(const CountMerge& count) { counts = count.counts; return *this; } + inline int& operator[](property_type index) { + std::vector >::iterator itr = lower_bound(counts.begin(), counts.end(), std::make_pair(index, int(0))); + if(itr != counts.end() && itr->first == index) { + return itr->second; + } + itr = counts.insert(itr, std::make_pair(index, int(0))); + return itr->second; + } +// inline int operator[](int index) const { +// std::vector >::const_iterator itr = counts.begin(); +// for( ; itr != counts.end() && itr->first <= index; ++itr) { +// if(itr->first == index) { +// return itr->second; +// } +// } +// return 0; +// } + inline CountMerge& operator+=(const CountMerge& count){ + merge_property_maps(counts, count.counts, false); + return *this; + } + inline CountMerge& operator-=(const CountMerge& count){ + merge_property_maps(counts, count.counts, true); + return *this; + } + inline CountMerge operator+(const CountMerge& count) const { + return CountMerge(*this)+=count; + } + inline CountMerge operator-(const CountMerge& count) const { + return CountMerge(*this)-=count; + } + inline CountMerge invert() const { + CountMerge retval; + retval -= *this; + return retval; + } + std::vector > counts; + }; + + //output is a std::map, polygon_45_set_data > + struct merge_45_output_functor { + template + void operator()(cT& output, const CountMerge& count1, const CountMerge& count2, + const Point& pt, int rise, direction_1d end) { + typedef typename cT::key_type keytype; + keytype left; + keytype right; + int edgeType = end == LOW ? -1 : 1; + for(typename std::vector >::const_iterator itr = count1.counts.begin(); + itr != count1.counts.end(); ++itr) { + left.insert(left.end(), (*itr).first); + } + for(typename std::vector >::const_iterator itr = count2.counts.begin(); + itr != count2.counts.end(); ++itr) { + right.insert(right.end(), (*itr).first); + } + if(left == right) return; + if(!left.empty()) { + //std::cout << pt.x() << " " << pt.y() << " " << rise << " " << edgeType << std::endl; + output[left].insert_clean(typename boolean_op_45::Vertex45(pt, rise, -edgeType)); + } + if(!right.empty()) { + //std::cout << pt.x() << " " << pt.y() << " " << rise << " " << -edgeType << std::endl; + output[right].insert_clean(typename boolean_op_45::Vertex45(pt, rise, edgeType)); + } + } + }; + + typedef typename std::pair::template Scan45CountT > Vertex45Compact; + typedef std::vector MergeSetData; + + struct lessVertex45Compact { + bool operator()(const Vertex45Compact& l, const Vertex45Compact& r) { + return l.first < r.first; + } + }; + + template + static void performMerge(output_type& result, MergeSetData& tsd) { + + polygon_sort(tsd.begin(), tsd.end(), lessVertex45Compact()); + typedef std::vector::template Scan45CountT > > TSD; + TSD tsd_; + tsd_.reserve(tsd.size()); + for(typename MergeSetData::iterator itr = tsd.begin(); itr != tsd.end(); ) { + typename MergeSetData::iterator itr2 = itr; + ++itr2; + for(; itr2 != tsd.end() && itr2->first == itr->first; ++itr2) { + (itr->second) += (itr2->second); //accumulate + } + tsd_.push_back(std::make_pair(itr->first, itr->second)); + itr = itr2; + } + typename boolean_op_45::template Scan45 scanline; + for(typename TSD::iterator itr = tsd_.begin(); itr != tsd_.end(); ) { + typename TSD::iterator itr2 = itr; + ++itr2; + while(itr2 != tsd_.end() && itr2->first.x() == itr->first.x()) { + ++itr2; + } + scanline.scan(result, itr, itr2); + itr = itr2; + } + } + + template + static void populateMergeSetData(MergeSetData& tsd, iT begin, iT end, property_type property) { + for( ; begin != end; ++begin) { + Vertex45Compact vertex; + vertex.first = typename Vertex45Compact::first_type(begin->pt.x() * 2, begin->pt.y() * 2); + tsd.push_back(vertex); + for(unsigned int i = 0; i < 4; ++i) { + if(begin->count[i]) { + tsd.back().second[i][property] += begin->count[i]; + } + } + } + } + + }; + + + +} +} + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/rectangle_formation.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/rectangle_formation.hpp new file mode 100644 index 0000000..07a2209 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/rectangle_formation.hpp @@ -0,0 +1,266 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_RECTANGLE_FORMATION_HPP +#define BOOST_POLYGON_RECTANGLE_FORMATION_HPP +namespace boost { namespace polygon{ + +namespace rectangle_formation { + template + class ScanLineToRects { + public: + typedef T rectangle_type; + typedef typename rectangle_traits::coordinate_type coordinate_type; + typedef rectangle_data scan_rect_type; + private: + + typedef std::set > ScanData; + ScanData scanData_; + bool haveCurrentRect_; + scan_rect_type currentRect_; + orientation_2d orient_; + typename rectangle_traits::coordinate_type currentCoordinate_; + public: + inline ScanLineToRects() : scanData_(), haveCurrentRect_(), currentRect_(), orient_(), currentCoordinate_() {} + + inline ScanLineToRects(orientation_2d orient, rectangle_type model) : + scanData_(orientation_2d(orient.to_int() ? VERTICAL : HORIZONTAL)), + haveCurrentRect_(false), currentRect_(), orient_(orient), currentCoordinate_() { + assign(currentRect_, model); + currentCoordinate_ = (std::numeric_limits::max)(); + } + + template + inline ScanLineToRects& processEdge(CT& rectangles, const interval_data& edge); + + inline ScanLineToRects& nextMajorCoordinate(coordinate_type currentCoordinate) { + if(haveCurrentRect_) { + scanData_.insert(scanData_.end(), currentRect_); + haveCurrentRect_ = false; + } + currentCoordinate_ = currentCoordinate; + return *this; + } + + }; + + template inline CT& + processEdge_(CT& rectangles, ST& scanData, const interval_type& edge, + bool& haveCurrentRect, rectangle_type& currentRect, coordinate_type currentCoordinate, orientation_2d orient) + { + typedef typename CT::value_type result_type; + bool edgeProcessed = false; + if(!scanData.empty()) { + + //process all rectangles in the scanData that touch the edge + typename ST::iterator dataIter = scanData.lower_bound(rectangle_type(edge, edge)); + //decrement beginIter until its low is less than edge's low + while((dataIter == scanData.end() || (*dataIter).get(orient).get(LOW) > edge.get(LOW)) && + dataIter != scanData.begin()) + { + --dataIter; + } + //process each rectangle until the low end of the rectangle + //is greater than the high end of the edge + while(dataIter != scanData.end() && + (*dataIter).get(orient).get(LOW) <= edge.get(HIGH)) + { + const rectangle_type& rect = *dataIter; + //if the rectangle data intersects the edge at all + if(rect.get(orient).get(HIGH) >= edge.get(LOW)) { + if(contains(rect.get(orient), edge, true)) { + //this is a closing edge + //we need to write out the intersecting rectangle and + //insert between 0 and 2 rectangles into the scanData + //write out rectangle + rectangle_type tmpRect = rect; + + if(rect.get(orient.get_perpendicular()).get(LOW) < currentCoordinate) { + //set the high coordinate perpedicular to slicing orientation + //to the current coordinate of the scan event + tmpRect.set(orient.get_perpendicular().get_direction(HIGH), + currentCoordinate); + result_type result; + assign(result, tmpRect); + rectangles.insert(rectangles.end(), result); + } + //erase the rectangle from the scan data + typename ST::iterator nextIter = dataIter; + ++nextIter; + scanData.erase(dataIter); + if(tmpRect.get(orient).get(LOW) < edge.get(LOW)) { + //insert a rectangle for the overhang of the bottom + //of the rectangle back into scan data + rectangle_type lowRect(tmpRect); + lowRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, + currentCoordinate)); + lowRect.set(orient.get_direction(HIGH), edge.get(LOW)); + scanData.insert(nextIter, lowRect); + } + if(tmpRect.get(orient).get(HIGH) > edge.get(HIGH)) { + //insert a rectangle for the overhang of the top + //of the rectangle back into scan data + rectangle_type highRect(tmpRect); + highRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, + currentCoordinate)); + highRect.set(orient.get_direction(LOW), edge.get(HIGH)); + scanData.insert(nextIter, highRect); + } + //we are done with this edge + edgeProcessed = true; + break; + } else { + //it must be an opening edge + //assert that rect does not overlap the edge but only touches + //write out rectangle + rectangle_type tmpRect = rect; + //set the high coordinate perpedicular to slicing orientation + //to the current coordinate of the scan event + if(tmpRect.get(orient.get_perpendicular().get_direction(LOW)) < currentCoordinate) { + tmpRect.set(orient.get_perpendicular().get_direction(HIGH), + currentCoordinate); + result_type result; + assign(result, tmpRect); + rectangles.insert(rectangles.end(), result); + } + //erase the rectangle from the scan data + typename ST::iterator nextIter = dataIter; + ++nextIter; + scanData.erase(dataIter); + dataIter = nextIter; + if(haveCurrentRect) { + if(currentRect.get(orient).get(HIGH) >= edge.get(LOW)){ + if(!edgeProcessed && currentRect.get(orient.get_direction(HIGH)) > edge.get(LOW)){ + rectangle_type tmpRect2(currentRect); + tmpRect2.set(orient.get_direction(HIGH), edge.get(LOW)); + scanData.insert(nextIter, tmpRect2); + if(currentRect.get(orient.get_direction(HIGH)) > edge.get(HIGH)) { + currentRect.set(orient, interval_data(edge.get(HIGH), currentRect.get(orient.get_direction(HIGH)))); + } else { + haveCurrentRect = false; + } + } else { + //extend the top of current rect + currentRect.set(orient.get_direction(HIGH), + (std::max)(edge.get(HIGH), + tmpRect.get(orient.get_direction(HIGH)))); + } + } else { + //insert current rect into the scanData + scanData.insert(nextIter, currentRect); + //create a new current rect + currentRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, + currentCoordinate)); + currentRect.set(orient, interval_data((std::min)(tmpRect.get(orient).get(LOW), + edge.get(LOW)), + (std::max)(tmpRect.get(orient).get(HIGH), + edge.get(HIGH)))); + } + } else { + haveCurrentRect = true; + currentRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, + currentCoordinate)); + currentRect.set(orient, interval_data((std::min)(tmpRect.get(orient).get(LOW), + edge.get(LOW)), + (std::max)(tmpRect.get(orient).get(HIGH), + edge.get(HIGH)))); + } + //skip to nextIter position + edgeProcessed = true; + continue; + } + //edgeProcessed = true; + } + ++dataIter; + } //end while edge intersects rectangle data + + } + if(!edgeProcessed) { + if(haveCurrentRect) { + if(currentRect.get(orient.get_perpendicular().get_direction(HIGH)) + == currentCoordinate && + currentRect.get(orient.get_direction(HIGH)) >= edge.get(LOW)) + { + if(currentRect.get(orient.get_direction(HIGH)) > edge.get(LOW)){ + rectangle_type tmpRect(currentRect); + tmpRect.set(orient.get_direction(HIGH), edge.get(LOW)); + scanData.insert(scanData.end(), tmpRect); + if(currentRect.get(orient.get_direction(HIGH)) > edge.get(HIGH)) { + currentRect.set(orient, + interval_data(edge.get(HIGH), + currentRect.get(orient.get_direction(HIGH)))); + return rectangles; + } else { + haveCurrentRect = false; + return rectangles; + } + } + //extend current rect + currentRect.set(orient.get_direction(HIGH), edge.get(HIGH)); + return rectangles; + } + scanData.insert(scanData.end(), currentRect); + haveCurrentRect = false; + } + rectangle_type tmpRect(currentRect); + tmpRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, + currentCoordinate)); + tmpRect.set(orient, edge); + scanData.insert(tmpRect); + return rectangles; + } + return rectangles; + + } + + template + template + inline + ScanLineToRects& ScanLineToRects::processEdge(CT& rectangles, const interval_data& edge) + { + processEdge_(rectangles, scanData_, edge, haveCurrentRect_, currentRect_, currentCoordinate_, orient_); + return *this; + } + + +} //namespace rectangle_formation + + template + struct get_coordinate_type_for_rectangles { + typedef typename polygon_traits::coordinate_type type; + }; + template + struct get_coordinate_type_for_rectangles { + typedef typename rectangle_traits::coordinate_type type; + }; + + template + void form_rectangles(output_container& output, iterator_type begin, iterator_type end, + orientation_2d orient, rectangle_concept ) { + typedef typename output_container::value_type rectangle_type; + typedef typename get_coordinate_type_for_rectangles::type>::type Unit; + rectangle_data model; + Unit prevPos = (std::numeric_limits::max)(); + rectangle_formation::ScanLineToRects > scanlineToRects(orient, model); + for(iterator_type itr = begin; + itr != end; ++ itr) { + Unit pos = (*itr).first; + if(pos != prevPos) { + scanlineToRects.nextMajorCoordinate(pos); + prevPos = pos; + } + Unit lowy = (*itr).second.first; + iterator_type tmp_itr = itr; + ++itr; + Unit highy = (*itr).second.first; + scanlineToRects.processEdge(output, interval_data(lowy, highy)); + if(std::abs((*itr).second.second) > 1) itr = tmp_itr; //next edge begins from this vertex + } + } +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/scan_arbitrary.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/scan_arbitrary.hpp new file mode 100644 index 0000000..86f7778 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/scan_arbitrary.hpp @@ -0,0 +1,2861 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_SCAN_ARBITRARY_HPP +#define BOOST_POLYGON_SCAN_ARBITRARY_HPP +#ifdef BOOST_POLYGON_DEBUG_FILE +#include +#endif +#include "polygon_sort_adaptor.hpp" +namespace boost { namespace polygon{ + + template + class line_intersection : public scanline_base { + private: + typedef typename scanline_base::Point Point; + + //the first point is the vertex and and second point establishes the slope of an edge eminating from the vertex + //typedef std::pair half_edge; + typedef typename scanline_base::half_edge half_edge; + + //scanline comparator functor + typedef typename scanline_base::less_half_edge less_half_edge; + typedef typename scanline_base::less_point less_point; + + //when parallel half edges are encounterd the set of segments is expanded + //when a edge leaves the scanline it is removed from the set + //when the set is empty the element is removed from the map + typedef int segment_id; + typedef std::pair > scanline_element; + typedef std::map, less_half_edge> edge_scanline; + typedef typename edge_scanline::iterator iterator; + +// std::map > vertical_data_; +// edge_scanline edge_scanline_; +// Unit x_; +// int just_before_; +// segment_id segment_id_; +// std::vector > event_edges_; +// std::set intersection_queue_; + public: +// inline line_intersection() : vertical_data_(), edge_scanline_(), x_((std::numeric_limits::max)()), just_before_(0), segment_id_(0), event_edges_(), intersection_queue_() { +// less_half_edge lessElm(&x_, &just_before_); +// edge_scanline_ = edge_scanline(lessElm); +// } +// inline line_intersection(const line_intersection& that) : vertical_data_(), edge_scanline_(), x_(), just_before_(), segment_id_(), event_edges_(), intersection_queue_() { (*this) = that; } +// inline line_intersection& operator=(const line_intersection& that) { +// x_ = that.x_; +// just_before_ = that.just_before_; +// segment_id_ = that.segment_id_; + +// //I cannot simply copy that.edge_scanline_ to this edge_scanline_ becuase the functor store pointers to other members! +// less_half_edge lessElm(&x_, &just_before_); +// edge_scanline_ = edge_scanline(lessElm); + +// edge_scanline_.insert(that.edge_scanline_.begin(), that.edge_scanline_.end()); +// return *this; +// } + +// static inline void between(Point pt, Point pt1, Point pt2) { +// less_point lp; +// if(lp(pt1, pt2)) +// return lp(pt, pt2) && lp(pt1, pt); +// return lp(pt, pt1) && lp(pt2, pt); +// } + + template + static inline void compute_histogram_in_y(iT begin, iT end, std::size_t size, std::vector > >& histogram) { + std::vector > ends; + ends.reserve(size * 2); + for(iT itr = begin ; itr != end; ++itr) { + int count = (*itr).first.first.y() < (*itr).first.second.y() ? 1 : -1; + ends.push_back(std::make_pair((*itr).first.first.y(), count)); + ends.push_back(std::make_pair((*itr).first.second.y(), -count)); + } + polygon_sort(ends.begin(), ends.end()); + histogram.reserve(ends.size()); + histogram.push_back(std::make_pair(ends.front().first, std::make_pair(0, 0))); + for(typename std::vector >::iterator itr = ends.begin(); itr != ends.end(); ++itr) { + if((*itr).first != histogram.back().first) { + histogram.push_back(std::make_pair((*itr).first, histogram.back().second)); + } + if((*itr).second < 0) + histogram.back().second.second -= (*itr).second; + histogram.back().second.first += (*itr).second; + } + } + + template + static inline void compute_y_cuts(std::vector& y_cuts, iT begin, iT end, std::size_t size) { + if(begin == end) return; + if(size < 30) return; //30 is empirically chosen, but the algorithm is not sensitive to this constant + std::size_t min_cut = size; + iT cut = begin; + std::size_t position = 0; + std::size_t cut_size = 0; + std::size_t histogram_size = std::distance(begin, end); + for(iT itr = begin; itr != end; ++itr, ++position) { + if(position < histogram_size / 3) + continue; + if(histogram_size - position < histogram_size / 3) break; + if((*itr).second.first < min_cut) { + cut = itr; + min_cut = (*cut).second.first; + cut_size = position; + } + } + if(cut_size == 0 || (*cut).second.first > size / 9) //nine is empirically chosen + return; + compute_y_cuts(y_cuts, begin, cut, (*cut).second.first + (*cut).second.second); + y_cuts.push_back((*cut).first); + compute_y_cuts(y_cuts, cut, end, size - (*cut).second.second); + } + + template + static inline void validate_scan_divide_and_conquer(std::vector >& intersection_points, + iT begin, iT end) { + std::vector > > histogram; + compute_histogram_in_y(begin, end, std::distance(begin, end), histogram); + std::vector y_cuts; + compute_y_cuts(y_cuts, histogram.begin(), histogram.end(), std::distance(begin, end)); + std::map > > bins; + bins[histogram.front().first] = std::vector >(); + for(typename std::vector::iterator itr = y_cuts.begin(); itr != y_cuts.end(); ++itr) { + bins[*itr] = std::vector >(); + } + for(iT itr = begin; itr != end; ++itr) { + typename std::map > >::iterator lb = + bins.lower_bound((std::min)((*itr).first.first.y(), (*itr).first.second.y())); + if(lb != bins.begin()) + --lb; + typename std::map > >::iterator ub = + bins.upper_bound((std::max)((*itr).first.first.y(), (*itr).first.second.y())); + for( ; lb != ub; ++lb) { + (*lb).second.push_back(*itr); + } + } + validate_scan(intersection_points, bins[histogram.front().first].begin(), bins[histogram.front().first].end()); + for(typename std::vector::iterator itr = y_cuts.begin(); itr != y_cuts.end(); ++itr) { + validate_scan(intersection_points, bins[*itr].begin(), bins[*itr].end(), *itr); + } + } + + template + static inline void validate_scan(std::vector >& intersection_points, + iT begin, iT end) { + validate_scan(intersection_points, begin, end, (std::numeric_limits::min)()); + } + //quadratic algorithm to do same work as optimal scan for cross checking + template + static inline void validate_scan(std::vector >& intersection_points, + iT begin, iT end, Unit min_y) { + std::vector pts; + std::vector > data(begin, end); + for(std::size_t i = 0; i < data.size(); ++i) { + if(data[i].first.second < data[i].first.first) { + std::swap(data[i].first.first, data[i].first.second); + } + } + typename scanline_base::compute_intersection_pack pack_; + polygon_sort(data.begin(), data.end()); + //find all intersection points + for(typename std::vector >::iterator outer = data.begin(); + outer != data.end(); ++outer) { + const half_edge& he1 = (*outer).first; + //its own end points + pts.push_back(he1.first); + pts.push_back(he1.second); + std::set& segmentpts = intersection_points[(*outer).second]; + for(typename std::set::iterator itr = segmentpts.begin(); itr != segmentpts.end(); ++itr) { + if ((*itr).y() >= min_y) { + pts.push_back(*itr); + } + } + bool have_first_y = he1.first.y() >= min_y && he1.second.y() >= min_y; + for(typename std::vector >::iterator inner = outer; + inner != data.end(); ++inner) { + const half_edge& he2 = (*inner).first; + if(have_first_y || (he2.first.y() >= min_y && he2.second.y() >= min_y)) { + //at least one segment has a low y value within the range + if(he1 == he2) continue; + if((std::min)(he2. first.get(HORIZONTAL), + he2.second.get(HORIZONTAL)) >= + (std::max)(he1.second.get(HORIZONTAL), + he1.first.get(HORIZONTAL))) + break; + if(he1.first == he2.first || he1.second == he2.second) + continue; + Point intersection; + if(pack_.compute_intersection(intersection, he1, he2)) { + //their intersection point + pts.push_back(intersection); + intersection_points[(*inner).second].insert(intersection); + intersection_points[(*outer).second].insert(intersection); + } + } + } + } + polygon_sort(pts.begin(), pts.end()); + typename std::vector::iterator newend = std::unique(pts.begin(), pts.end()); + typename std::vector::iterator lfinger = pts.begin(); + //find all segments that interact with intersection points + for(typename std::vector >::iterator outer = data.begin(); + outer != data.end(); ++outer) { + const half_edge& he1 = (*outer).first; + segment_id id1 = (*outer).second; + //typedef rectangle_data Rectangle; + //Rectangle rect1; + //set_points(rect1, he1.first, he1.second); + //typename std::vector::iterator itr = lower_bound(pts.begin(), newend, (std::min)(he1.first, he1.second)); + //typename std::vector::iterator itr2 = upper_bound(pts.begin(), newend, (std::max)(he1.first, he1.second)); + Point startpt = (std::min)(he1.first, he1.second); + Point stoppt = (std::max)(he1.first, he1.second); + //while(itr != newend && itr != pts.begin() && (*itr).get(HORIZONTAL) >= (std::min)(he1.first.get(HORIZONTAL), he1.second.get(HORIZONTAL))) --itr; + //while(itr2 != newend && (*itr2).get(HORIZONTAL) <= (std::max)(he1.first.get(HORIZONTAL), he1.second.get(HORIZONTAL))) ++itr2; + //itr = pts.begin(); + //itr2 = pts.end(); + while(lfinger != newend && (*lfinger).x() < startpt.x()) ++lfinger; + for(typename std::vector::iterator itr = lfinger ; itr != newend && (*itr).x() <= stoppt.x(); ++itr) { + if(scanline_base::intersects_grid(*itr, he1)) + intersection_points[id1].insert(*itr); + } + } + } + + template + static inline void validate_scan(std::vector > >& output_segments, + iT begin, iT end) { + std::vector > input_properties; + std::vector > input_segments, intermediate_segments; + int index = 0; + for( ; begin != end; ++begin) { + input_properties.push_back((*begin).second); + input_segments.push_back(std::make_pair((*begin).first, index++)); + } + validate_scan(intermediate_segments, input_segments.begin(), input_segments.end()); + for(std::size_t i = 0; i < intermediate_segments.size(); ++i) { + output_segments.push_back(std::make_pair(intermediate_segments[i].first, + input_properties[intermediate_segments[i].second])); + less_point lp; + if(lp(output_segments.back().first.first, output_segments.back().first.second) != + lp(input_segments[intermediate_segments[i].second].first.first, + input_segments[intermediate_segments[i].second].first.second)) { + //edge changed orientation, invert count on edge + output_segments.back().second.second *= -1; + } + if(!scanline_base::is_vertical(input_segments[intermediate_segments[i].second].first) && + scanline_base::is_vertical(output_segments.back().first)) { + output_segments.back().second.second *= -1; + } + if(lp(output_segments.back().first.second, output_segments.back().first.first)) { + std::swap(output_segments.back().first.first, output_segments.back().first.second); + } + } + } + + template + static inline void validate_scan(std::vector >& output_segments, + iT begin, iT end) { + std::vector > intersection_points(std::distance(begin, end)); + validate_scan_divide_and_conquer(intersection_points, begin, end); + //validate_scan(intersection_points, begin, end); + segment_intersections(output_segments, intersection_points, begin, end); +// std::pair offenders; +// if(!verify_scan(offenders, output_segments.begin(), output_segments.end())) { +// std::cout << "break here!\n"; +// for(typename std::set::iterator itr = intersection_points[offenders.first].begin(); +// itr != intersection_points[offenders.first].end(); ++itr) { +// std::cout << (*itr).x() << " " << (*itr).y() << " "; +// } std::cout << "\n"; +// for(typename std::set::iterator itr = intersection_points[offenders.second].begin(); +// itr != intersection_points[offenders.second].end(); ++itr) { +// std::cout << (*itr).x() << " " << (*itr).y() << " "; +// } std::cout << "\n"; +// exit(1); +// } + } + + //quadratic algorithm to find intersections + template + static inline bool verify_scan(std::pair& offenders, + iT begin, iT end) { + + std::vector > data(begin, end); + for(std::size_t i = 0; i < data.size(); ++i) { + if(data[i].first.second < data[i].first.first) { + std::swap(data[i].first.first, data[i].first.second); + } + } + polygon_sort(data.begin(), data.end()); + for(typename std::vector >::iterator outer = data.begin(); + outer != data.end(); ++outer) { + const half_edge& he1 = (*outer).first; + segment_id id1 = (*outer).second; + for(typename std::vector >::iterator inner = outer; + inner != data.end(); ++inner) { + const half_edge& he2 = (*inner).first; + if(he1 == he2) continue; + if((std::min)(he2. first.get(HORIZONTAL), + he2.second.get(HORIZONTAL)) > + (std::max)(he1.second.get(HORIZONTAL), + he1.first.get(HORIZONTAL))) + break; + segment_id id2 = (*inner).second; + if(scanline_base::intersects(he1, he2)) { + offenders.first = id1; + offenders.second = id2; + //std::cout << he1.first.x() << " " << he1.first.y() << " " << he1.second.x() << " " << he1.second.y() << " " << he2.first.x() << " " << he2.first.y() << " " << he2.second.x() << " " << he2.second.y() << "\n"; + return false; + } + } + } + return true; + } + + class less_point_down_slope { + public: + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + inline less_point_down_slope() {} + inline bool operator () (const Point& pt1, const Point& pt2) const { + if(pt1.get(HORIZONTAL) < pt2.get(HORIZONTAL)) return true; + if(pt1.get(HORIZONTAL) == pt2.get(HORIZONTAL)) { + if(pt1.get(VERTICAL) > pt2.get(VERTICAL)) return true; + } + return false; + } + }; + + template + static inline void segment_edge(std::vector >& output_segments, + const half_edge& , segment_id id, iT begin, iT end) { + iT current = begin; + iT next = begin; + ++next; + while(next != end) { + output_segments.push_back(std::make_pair(half_edge(*current, *next), id)); + current = next; + ++next; + } + } + + template + static inline void segment_intersections(std::vector >& output_segments, + std::vector >& intersection_points, + iT begin, iT end) { + for(iT iter = begin; iter != end; ++iter) { + //less_point lp; + const half_edge& he = (*iter).first; + //if(lp(he.first, he.second)) { + // //it is the begin event + segment_id id = (*iter).second; + const std::set& pts = intersection_points[id]; + Point hpt(he.first.get(HORIZONTAL)+1, he.first.get(VERTICAL)); + if(!scanline_base::is_vertical(he) && scanline_base::less_slope(he.first.get(HORIZONTAL), he.first.get(VERTICAL), + he.second, hpt)) { + //slope is below horizontal + std::vector tmpPts; + tmpPts.reserve(pts.size()); + tmpPts.insert(tmpPts.end(), pts.begin(), pts.end()); + less_point_down_slope lpds; + polygon_sort(tmpPts.begin(), tmpPts.end(), lpds); + segment_edge(output_segments, he, id, tmpPts.begin(), tmpPts.end()); + } else { + segment_edge(output_segments, he, id, pts.begin(), pts.end()); + } + //} + } + } + +// //iT iterator over unsorted pair representing line segments of input +// //output_segments is populated with fully intersected output line segment half +// //edges and the index of the input segment that they are assoicated with +// //duplicate output half edges with different ids will be generated in the case +// //that parallel input segments intersection +// //outputs are in sorted order and include both begin and end events for +// //each segment +// template +// inline void scan(std::vector >& output_segments, +// iT begin, iT end) { +// std::map > intersection_points; +// scan(intersection_points, begin, end); +// segment_intersections(output_segments, intersection_points, begin, end); +// } + +// //iT iterator over sorted sequence of half edge, segment id pairs representing segment begin and end points +// //intersection points provides a mapping from input segment id (vector index) to the set +// //of intersection points assocated with that input segment +// template +// inline void scan(std::map >& intersection_points, +// iT begin, iT end) { +// for(iT iter = begin; iter != end; ++iter) { +// const std::pair& elem = *iter; +// const half_edge& he = elem.first; +// Unit current_x = he.first.get(HORIZONTAL); +// if(current_x != x_) { +// process_scan_event(intersection_points); +// while(!intersection_queue_.empty() && +// (*(intersection_queue_.begin()).get(HORIZONTAL) < current_x)) { +// x_ = *(intersection_queue_.begin()).get(HORIZONTAL); +// process_intersections_at_scan_event(intersection_points); +// } +// x_ = current_x; +// } +// event_edges_.push_back(elem); +// } +// process_scan_event(intersection_points); +// } + +// inline iterator lookup(const half_edge& he) { +// return edge_scanline_.find(he); +// } + +// inline void insert_into_scanline(const half_edge& he, int id) { +// edge_scanline_[he].insert(id); +// } + +// inline void lookup_and_remove(const half_edge& he, int id) { +// iterator remove_iter = lookup(he); +// if(remove_iter == edge_scanline_.end()) { +// //std::cout << "failed to find removal segment in scanline\n"; +// return; +// } +// std::set& ids = (*remove_iter).second; +// std::set::iterator id_iter = ids.find(id); +// if(id_iter == ids.end()) { +// //std::cout << "failed to find removal segment id in scanline set\n"; +// return; +// } +// ids.erase(id_iter); +// if(ids.empty()) +// edge_scanline_.erase(remove_iter); +// } + +// static inline void update_segments(std::map >& intersection_points, +// const std::set& segments, Point pt) { +// for(std::set::const_iterator itr = segments.begin(); itr != segments.end(); ++itr) { +// intersection_points[*itr].insert(pt); +// } +// } + +// inline void process_intersections_at_scan_event(std::map >& intersection_points) { +// //there may be additional intersection points at this x location that haven't been +// //found yet if vertical or near vertical line segments intersect more than +// //once before the next x location +// just_before_ = true; +// std::set intersecting_elements; +// std::set intersection_locations; +// typedef typename std::set::iterator intersection_iterator; +// intersection_iterator iter; +// //first find all secondary intersection locations and all scanline iterators +// //that are intersecting +// for(iter = intersection_queue_.begin(); +// iter != intersection_queue_.end() && (*iter).get(HORIZONTAL) == x_; ++iter) { +// Point pt = *iter; +// Unit y = pt.get(VERTICAL); +// intersection_locations.insert(y); +// //if x_ is max there can be only end events and no sloping edges +// if(x_ != (std::numeric_limits::max)()) { +// //deal with edges that project to the right of scanline +// //first find the edges in the scanline adjacent to primary intersectin points +// //lookup segment in scanline at pt +// iterator itr = edge_scanline_.lower_bound(half_edge(pt, Point(x_+1, y))); +// //look above pt in scanline until reaching end or segment that doesn't intersect +// //1x1 grid upper right of pt +// //look below pt in scanline until reaching begin or segment that doesn't interset +// //1x1 grid upper right of pt + +// //second find edges in scanline on the y interval of each edge found in the previous +// //step for x_ to x_ + 1 + +// //third find overlaps in the y intervals of all found edges to find all +// //secondary intersection points + +// } +// } +// //erase the intersection points from the queue +// intersection_queue_.erase(intersection_queue_.begin(), iter); +// std::vector insertion_edges; +// insertion_edges.reserve(intersecting_elements.size()); +// std::vector > sloping_ends; +// //do all the work of updating the output of all intersecting +// for(typename std::set::iterator inter_iter = intersecting_elements.begin(); +// inter_iter != intersecting_elements.end(); ++inter_iter) { +// //if it is horizontal update it now and continue +// if(is_horizontal((*inter_iter).first)) { +// update_segments(intersection_points, (*inter_iter).second, Point(x_, (*inter_iter).first.get(VERTICAL))); +// } else { +// //if x_ is max there can be only end events and no sloping edges +// if(x_ != (std::numeric_limits::max)()) { +// //insert its end points into the vector of sloping ends +// const half_edge& he = (*inter_iter).first; +// Unit y = evalAtXforY(x_, he.first, he.second); +// Unit y2 = evalAtXforY(x_+1, he.first, he.second); +// if(y2 >= y) y2 +=1; //we round up, in exact case we don't worry about overbite of one +// else y += 1; //downward sloping round up +// sloping_ends.push_back(std::make_pair(y, inter_iter)); +// sloping_ends.push_back(std::make_pair(y2, inter_iter)); +// } +// } +// } + +// //merge sloping element data +// polygon_sort(sloping_ends.begin(), sloping_ends.end()); +// std::map > sloping_elements; +// std::set merge_elements; +// for(typename std::vector >::iterator slop_iter = sloping_ends.begin(); +// slop_iter == sloping_ends.end(); ++slop_iter) { +// //merge into sloping elements +// typename std::set::iterator merge_iterator = merge_elements.find((*slop_iter).second); +// if(merge_iterator == merge_elements.end()) { +// merge_elements.insert((*slop_iter).second); +// } else { +// merge_elements.erase(merge_iterator); +// } +// sloping_elements[(*slop_iter).first] = merge_elements; +// } + +// //scan intersection points +// typename std::map >::iterator vertical_iter = vertical_data_.begin(); +// typename std::map >::iterator sloping_iter = sloping_elements.begin(); +// for(typename std::set::iterator position_iter = intersection_locations.begin(); +// position_iter == intersection_locations.end(); ++position_iter) { +// //look for vertical segments that intersect this point and update them +// Unit y = *position_iter; +// Point pt(x_, y); +// //handle vertical segments +// if(vertical_iter != vertical_data_.end()) { +// typename std::map >::iterator next_vertical = vertical_iter; +// for(++next_vertical; next_vertical != vertical_data_.end() && +// (*next_vertical).first < y; ++next_vertical) { +// vertical_iter = next_vertical; +// } +// if((*vertical_iter).first < y && !(*vertical_iter).second.empty()) { +// update_segments(intersection_points, (*vertical_iter).second, pt); +// ++vertical_iter; +// if(vertical_iter != vertical_data_.end() && (*vertical_iter).first == y) +// update_segments(intersection_points, (*vertical_iter).second, pt); +// } +// } +// //handle sloping segments +// if(sloping_iter != sloping_elements.end()) { +// typename std::map >::iterator next_sloping = sloping_iter; +// for(++next_sloping; next_sloping != sloping_elements.end() && +// (*next_sloping).first < y; ++next_sloping) { +// sloping_iter = next_sloping; +// } +// if((*sloping_iter).first < y && !(*sloping_iter).second.empty()) { +// for(typename std::set::iterator element_iter = (*sloping_iter).second.begin(); +// element_iter != (*sloping_iter).second.end(); ++element_iter) { +// const half_edge& he = (*element_iter).first; +// if(intersects_grid(pt, he)) { +// update_segments(intersection_points, (*element_iter).second, pt); +// } +// } +// ++sloping_iter; +// if(sloping_iter != sloping_elements.end() && (*sloping_iter).first == y && +// !(*sloping_iter).second.empty()) { +// for(typename std::set::iterator element_iter = (*sloping_iter).second.begin(); +// element_iter != (*sloping_iter).second.end(); ++element_iter) { +// const half_edge& he = (*element_iter).first; +// if(intersects_grid(pt, he)) { +// update_segments(intersection_points, (*element_iter).second, pt); +// } +// } +// } +// } +// } +// } + +// //erase and reinsert edges into scanline with check for future intersection +// } + +// inline void process_scan_event(std::map >& intersection_points) { +// just_before_ = true; + +// //process end events by removing those segments from the scanline +// //and insert vertices of all events into intersection queue +// Point prev_point((std::numeric_limits::min)(), (std::numeric_limits::min)()); +// less_point lp; +// std::set vertical_ids; +// vertical_data_.clear(); +// for(std::size_t i = 0; i < event_edges_.size(); ++i) { +// segment_id id = event_edges_[i].second; +// const half_edge& he = event_edges_[i].first; +// //vertical half edges are handled during intersection processing because +// //they cannot be inserted into the scanline +// if(!is_vertical(he)) { +// if(lp(he.second, he.first)) { +// //half edge is end event +// lookup_and_remove(he, id); +// } else { +// //half edge is begin event +// insert_into_scanline(he, id); +// //note that they will be immediately removed and reinserted after +// //handling their intersection (vertex) +// //an optimization would allow them to be processed specially to avoid the redundant +// //removal and reinsertion +// } +// } else { +// //common case if you are lucky +// //update the map of y to set of segment id +// if(lp(he.second, he.first)) { +// //half edge is end event +// std::set::iterator itr = vertical_ids.find(id); +// if(itr == vertical_ids.end()) { +// //std::cout << "Failed to find end event id in vertical ids\n"; +// } else { +// vertical_ids.erase(itr); +// vertical_data_[he.first.get(HORIZONTAL)] = vertical_ids; +// } +// } else { +// //half edge is a begin event +// vertical_ids.insert(id); +// vertical_data_[he.first.get(HORIZONTAL)] = vertical_ids; +// } +// } +// //prevent repeated insertion of same vertex into intersection queue +// if(prev_point != he.first) +// intersection_queue_.insert(he.first); +// else +// prev_point = he.first; +// // process intersections at scan event +// process_intersections_at_scan_event(intersection_points); +// } +// event_edges_.clear(); +// } + + public: + template + static inline bool test_validate_scan(stream_type& stdcout) { + std::vector > input, edges; + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), 0)); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 10)), 1)); + std::pair result; + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail1 " << result.first << " " << result.second << "\n"; + return false; + } + input.push_back(std::make_pair(half_edge(Point(0, 5), Point(5, 5)), 2)); + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 " << result.first << " " << result.second << "\n"; + return false; + } + input.pop_back(); + input.push_back(std::make_pair(half_edge(Point(1, 0), Point(11, 11)), input.size())); + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail3 " << result.first << " " << result.second << "\n"; + return false; + } + input.push_back(std::make_pair(half_edge(Point(1, 0), Point(10, 11)), input.size())); + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail4 " << result.first << " " << result.second << "\n"; + return false; + } + input.pop_back(); + input.push_back(std::make_pair(half_edge(Point(1, 2), Point(11, 11)), input.size())); + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail5 " << result.first << " " << result.second << "\n"; + return false; + } + input.push_back(std::make_pair(half_edge(Point(0, 5), Point(0, 11)), input.size())); + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail6 " << result.first << " " << result.second << "\n"; + return false; + } + input.pop_back(); + for(std::size_t i = 0; i < input.size(); ++i) { + std::swap(input[i].first.first, input[i].first.second); + } + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail5 2 " << result.first << " " << result.second << "\n"; + return false; + } + for(std::size_t i = 0; i < input.size(); ++i) { + input[i].first.first = Point(input[i].first.first.get(HORIZONTAL) * -1, + input[i].first.first.get(VERTICAL) * -1); + input[i].first.second = Point(input[i].first.second.get(HORIZONTAL) * -1, + input[i].first.second.get(VERTICAL) * -1); + } + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + stdcout << edges.size() << "\n"; + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail5 3 " << result.first << " " << result.second << "\n"; + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(5, 7), Point(7, 6)), 0)); + input.push_back(std::make_pair(half_edge(Point(2, 4), Point(6, 7)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 1 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(3, 2), Point(1, 7)), 0)); + input.push_back(std::make_pair(half_edge(Point(0, 6), Point(7, 4)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 2 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(6, 6), Point(1, 0)), 0)); + input.push_back(std::make_pair(half_edge(Point(3, 6), Point(2, 3)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 3 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(7, 0)), 0)); + input.push_back(std::make_pair(half_edge(Point(6, 0), Point(2, 0)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 4 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(-17333131 - -17208131, -10316869 - -10191869), Point(0, 0)), 0)); + input.push_back(std::make_pair(half_edge(Point(-17291260 - -17208131, -10200000 - -10191869), Point(-17075000 - -17208131, -10200000 - -10191869)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 5 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(-17333131, -10316869), Point(-17208131, -10191869)), 0)); + input.push_back(std::make_pair(half_edge(Point(-17291260, -10200000), Point(-17075000, -10200000)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 6 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(-9850009+9853379, -286971+290340), Point(-12777869+9853379, -3214831+290340)), 0)); + input.push_back(std::make_pair(half_edge(Point(-5223510+9853379, -290340+290340), Point(-9858140+9853379, -290340+290340)), 1)); + validate_scan(edges, input.begin(), input.end()); + print(edges); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 7 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(-9850009, -286971), Point(-12777869, -3214831)), 0)); + input.push_back(std::make_pair(half_edge(Point(-5223510, -290340), Point(-9858140, -290340)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 8 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + //3 3 2 2: 0; 4 2 0 6: 1; 0 3 6 3: 2; 4 1 5 5: 3; + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(3, 3), Point(2, 2)), 0)); + input.push_back(std::make_pair(half_edge(Point(4, 2), Point(0, 6)), 1)); + input.push_back(std::make_pair(half_edge(Point(0, 3), Point(6, 3)), 2)); + input.push_back(std::make_pair(half_edge(Point(4, 1), Point(5, 5)), 3)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail4 1 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + //5 7 1 3: 0; 4 5 2 1: 1; 2 5 2 1: 2; 4 1 5 3: 3; + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(5, 7), Point(1, 3)), 0)); + input.push_back(std::make_pair(half_edge(Point(4, 5), Point(2, 1)), 1)); + input.push_back(std::make_pair(half_edge(Point(2, 5), Point(2, 1)), 2)); + input.push_back(std::make_pair(half_edge(Point(4, 1), Point(5, 3)), 3)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail4 2 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + //1 0 -4 -1: 0; 0 0 2 -1: 1; + input.clear(); + edges.clear(); + input.push_back(std::make_pair(half_edge(Point(1, 0), Point(-4, -1)), 0)); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(2, -1)), 1)); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail2 5 " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + Unit min_c =0; + Unit max_c =0; + for(unsigned int outer = 0; outer < 1000; ++outer) { + input.clear(); + for(unsigned int i = 0; i < 4; ++i) { + Unit x1 = rand(); + Unit x2 = rand(); + Unit y1 = rand(); + Unit y2 = rand(); + int neg1 = rand() % 2; + if(neg1) x1 *= -1; + int neg2 = rand() % 2; + if(neg2) x2 *= -1; + int neg3 = rand() % 2; + if(neg3) y1 *= -1; + int neg4 = rand() % 2; + if(neg4) y2 *= -1; + if(x1 < min_c) min_c = x1; + if(x2 < min_c) min_c = x2; + if(y1 < min_c) min_c = y1; + if(y2 < min_c) min_c = y2; + if(x1 > max_c) max_c = x1; + if(x2 > max_c) max_c = x2; + if(y1 > max_c) max_c = y1; + if(y2 > max_c) max_c = y2; + Point pt1(x1, y1); + Point pt2(x2, y2); + if(pt1 != pt2) + input.push_back(std::make_pair(half_edge(pt1, pt2), i)); + } + edges.clear(); + validate_scan(edges, input.begin(), input.end()); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "s fail9 " << outer << ": " << result.first << " " << result.second << "\n"; + print(input); + print(edges); + return false; + } + } + return true; + } + + //static void print(const std::pair& segment) { + //std::cout << segment.first.first << " " << segment.first.second << ": " << segment.second << "; "; + //} + static void print(const std::vector >& vec) { + for(std::size_t i = 0; i < vec.size(); ++ i) { + // print(vec[i]); + } + //std::cout << "\n"; + } + + template + static inline bool test_verify_scan(stream_type& stdcout) { + std::vector > edges; + edges.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), 0)); + edges.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 10)), 1)); + std::pair result; + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail1\n"; + return false; + } + edges.push_back(std::make_pair(half_edge(Point(0, 5), Point(5, 5)), 2)); + if(verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail2\n"; + return false; + } + edges.pop_back(); + edges.push_back(std::make_pair(half_edge(Point(1, 0), Point(11, 11)), (segment_id)edges.size())); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail3\n"; + return false; + } + edges.push_back(std::make_pair(half_edge(Point(1, 0), Point(10, 11)), (segment_id)edges.size())); + if(verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail4\n"; + return false; + } + edges.pop_back(); + edges.push_back(std::make_pair(half_edge(Point(1, 2), Point(11, 11)), (segment_id)edges.size())); + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail5 " << result.first << " " << result.second << "\n"; + return false; + } + edges.push_back(std::make_pair(half_edge(Point(0, 5), Point(0, 11)), (segment_id)edges.size())); + if(verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail6 " << result.first << " " << result.second << "\n"; + return false; + } + edges.pop_back(); + for(std::size_t i = 0; i < edges.size(); ++i) { + std::swap(edges[i].first.first, edges[i].first.second); + } + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail5 2 " << result.first << " " << result.second << "\n"; + return false; + } + for(std::size_t i = 0; i < edges.size(); ++i) { + edges[i].first.first = Point(edges[i].first.first.get(HORIZONTAL) * -1, + edges[i].first.first.get(VERTICAL) * -1); + edges[i].first.second = Point(edges[i].first.second.get(HORIZONTAL) * -1, + edges[i].first.second.get(VERTICAL) * -1); + } + if(!verify_scan(result, edges.begin(), edges.end())) { + stdcout << "fail5 3 " << result.first << " " << result.second << "\n"; + return false; + } + return true; + } + + }; + + //scanline consumes the "flattened" fully intersected line segments produced by + //a pass of line_intersection along with property and count information and performs a + //useful operation like booleans or property merge or connectivity extraction + template > + class scanline : public scanline_base { + public: + //definitions + typedef typename scanline_base::Point Point; + + //the first point is the vertex and and second point establishes the slope of an edge eminating from the vertex + //typedef std::pair half_edge; + typedef typename scanline_base::half_edge half_edge; + + //scanline comparator functor + typedef typename scanline_base::less_half_edge less_half_edge; + typedef typename scanline_base::less_point less_point; + + typedef keytype property_set; + //this is the data type used internally to store the combination of property counts at a given location + typedef std::vector > property_map; + //this data structure assocates a property and count to a half edge + typedef std::pair > vertex_property; + //this data type is used internally to store the combined property data for a given half edge + typedef std::pair vertex_data; + //this data type stores the combination of many half edges + typedef std::vector property_merge_data; + //this data structure stores end points of edges in the scanline + typedef std::set end_point_queue; + + //this is the output data type that is created by the scanline before it is post processed based on content of property sets + typedef std::pair > half_edge_property; + + //this is the scanline data structure + typedef std::map scanline_type; + typedef std::pair scanline_element; + typedef typename scanline_type::iterator iterator; + typedef typename scanline_type::const_iterator const_iterator; + + //data + scanline_type scan_data_; + std::vector removal_set_; //edges to be removed at the current scanline stop + std::vector insertion_set_; //edge to be inserted after current scanline stop + end_point_queue end_point_queue_; + Unit x_; + Unit y_; + int just_before_; + typename scanline_base::evalAtXforYPack evalAtXforYPack_; + public: + inline scanline() : scan_data_(), removal_set_(), insertion_set_(), end_point_queue_(), + x_((std::numeric_limits::max)()), y_((std::numeric_limits::max)()), just_before_(false), evalAtXforYPack_() { + less_half_edge lessElm(&x_, &just_before_, &evalAtXforYPack_); + scan_data_ = scanline_type(lessElm); + } + inline scanline(const scanline& that) : scan_data_(), removal_set_(), insertion_set_(), end_point_queue_(), + x_((std::numeric_limits::max)()), y_((std::numeric_limits::max)()), just_before_(false), evalAtXforYPack_() { + (*this) = that; } + inline scanline& operator=(const scanline& that) { + x_ = that.x_; + y_ = that.y_; + just_before_ = that.just_before_; + end_point_queue_ = that.end_point_queue_; + //I cannot simply copy that.scanline_type to this scanline_type becuase the functor store pointers to other members! + less_half_edge lessElm(&x_, &just_before_); + scan_data_ = scanline_type(lessElm); + + scan_data_.insert(that.scan_data_.begin(), that.scan_data_.end()); + return *this; + } + + template + void write_out(result_type& result, result_functor rf, const half_edge& he, + const property_map& pm_left, const property_map& pm_right) { + //std::cout << "write out "; + //std::cout << he.first << ", " << he.second << "\n"; + property_set ps_left, ps_right; + set_unique_property(ps_left, pm_left); + set_unique_property(ps_right, pm_right); + if(ps_left != ps_right) { + //std::cout << "!equivalent\n"; + rf(result, he, ps_left, ps_right); + } + } + + template + iT handle_input_events(result_type& result, result_functor rf, iT begin, iT end) { + //typedef typename high_precision_type::type high_precision; + //for each event + property_map vertical_properties_above; + property_map vertical_properties_below; + half_edge vertical_edge_above; + half_edge vertical_edge_below; + std::vector insertion_elements; + //current_iter should increase monotonically toward end as we process scanline stop + iterator current_iter = scan_data_.begin(); + just_before_ = true; + Unit y = (std::numeric_limits::min)(); + bool first_iteration = true; + //we want to return from inside the loop when we hit end or new x +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + while(true) { + if(begin == end || (!first_iteration && ((*begin).first.first.get(VERTICAL) != y || + (*begin).first.first.get(HORIZONTAL) != x_))) { + //lookup iterator range in scanline for elements coming in from the left + //that end at this y + Point pt(x_, y); + //grab the properties coming in from below + property_map properties_below; + if(current_iter != scan_data_.end()) { + //make sure we are looking at element in scanline just below y + //if(evalAtXforY(x_, (*current_iter).first.first, (*current_iter).first.second) != y) { + if(scanline_base::on_above_or_below(Point(x_, y), (*current_iter).first) != 0) { + Point e2(pt); + if(e2.get(VERTICAL) != (std::numeric_limits::max)()) + e2.set(VERTICAL, e2.get(VERTICAL) + 1); + else + e2.set(VERTICAL, e2.get(VERTICAL) - 1); + half_edge vhe(pt, e2); + current_iter = scan_data_.lower_bound(vhe); + } + if(current_iter != scan_data_.end()) { + //get the bottom iterator for elements at this point + //while(evalAtXforY(x_, (*current_iter).first.first, (*current_iter).first.second) >= (high_precision)y && + while(scanline_base::on_above_or_below(Point(x_, y), (*current_iter).first) != 1 && + current_iter != scan_data_.begin()) { + --current_iter; + } + //if(evalAtXforY(x_, (*current_iter).first.first, (*current_iter).first.second) >= (high_precision)y) { + if(scanline_base::on_above_or_below(Point(x_, y), (*current_iter).first) != 1) { + properties_below.clear(); + } else { + properties_below = (*current_iter).second; + //move back up to y or one past y + ++current_iter; + } + } + } + std::vector edges_from_left; + while(current_iter != scan_data_.end() && + //can only be true if y is integer + //evalAtXforY(x_, (*current_iter).first.first, (*current_iter).first.second) == y) { + scanline_base::on_above_or_below(Point(x_, y), (*current_iter).first) == 0) { + //removal_set_.push_back(current_iter); + ++current_iter; + } + //merge vertical count with count from below + if(!vertical_properties_below.empty()) { + merge_property_maps(vertical_properties_below, properties_below); + //write out vertical edge + write_out(result, rf, vertical_edge_below, properties_below, vertical_properties_below); + } else { + merge_property_maps(vertical_properties_below, properties_below); + } + //iteratively add intertion element counts to count from below + //and write them to insertion set + for(std::size_t i = 0; i < insertion_elements.size(); ++i) { + if(i == 0) { + merge_property_maps(insertion_elements[i].second, vertical_properties_below); + write_out(result, rf, insertion_elements[i].first, insertion_elements[i].second, vertical_properties_below); + } else { + merge_property_maps(insertion_elements[i].second, insertion_elements[i-1].second); + write_out(result, rf, insertion_elements[i].first, insertion_elements[i].second, insertion_elements[i-1].second); + } + insertion_set_.push_back(insertion_elements[i]); + } + if((begin == end || (*begin).first.first.get(HORIZONTAL) != x_)) { + if(vertical_properties_above.empty()) { + return begin; + } else { + y = vertical_edge_above.second.get(VERTICAL); + vertical_properties_below.clear(); + vertical_properties_above.swap(vertical_properties_below); + vertical_edge_below = vertical_edge_above; + insertion_elements.clear(); + continue; + } + } + vertical_properties_below.clear(); + vertical_properties_above.swap(vertical_properties_below); + vertical_edge_below = vertical_edge_above; + insertion_elements.clear(); + } + if(begin != end) { + const vertex_property& vp = *begin; + const half_edge& he = vp.first; + y = he.first.get(VERTICAL); + first_iteration = false; + if(! vertical_properties_below.empty() && + vertical_edge_below.second.get(VERTICAL) < y) { + y = vertical_edge_below.second.get(VERTICAL); + continue; + } + if(scanline_base::is_vertical(he)) { + update_property_map(vertical_properties_above, vp.second); + vertical_edge_above = he; + } else { + if(insertion_elements.empty() || + insertion_elements.back().first != he) { + insertion_elements.push_back(scanline_element(he, property_map())); + } + update_property_map(insertion_elements.back().second, vp.second); + } + ++begin; + } + } +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + + } + + inline void erase_end_events(typename end_point_queue::iterator epqi) { + end_point_queue_.erase(end_point_queue_.begin(), epqi); + for(typename std::vector::iterator retire_itr = removal_set_.begin(); + retire_itr != removal_set_.end(); ++retire_itr) { + scan_data_.erase(*retire_itr); + } + removal_set_.clear(); + } + + + inline void remove_retired_edges_from_scanline() { + just_before_ = true; + typename end_point_queue::iterator epqi = end_point_queue_.begin(); + Unit current_x = x_; + Unit previous_x = x_; + while(epqi != end_point_queue_.end() && + (*epqi).get(HORIZONTAL) <= current_x) { + x_ = (*epqi).get(HORIZONTAL); + if(x_ != previous_x) erase_end_events(epqi); + previous_x = x_; + //lookup elements + Point e2(*epqi); + if(e2.get(VERTICAL) != (std::numeric_limits::max)()) + e2.set(VERTICAL, e2.get(VERTICAL) + 1); + else + e2.set(VERTICAL, e2.get(VERTICAL) - 1); + half_edge vhe_e(*epqi, e2); + iterator current_iter = scan_data_.lower_bound(vhe_e); + while(current_iter != scan_data_.end() && (*current_iter).first.second == (*epqi)) { + //evalAtXforY(x_, (*current_iter).first.first, (*current_iter).first.second) == (*epqi).get(VERTICAL)) { + removal_set_.push_back(current_iter); + ++current_iter; + } + ++epqi; + } + x_ = current_x; + erase_end_events(epqi); + } + + inline void insert_new_edges_into_scanline() { + just_before_ = false; + for(typename std::vector::iterator insert_itr = insertion_set_.begin(); + insert_itr != insertion_set_.end(); ++insert_itr) { + scan_data_.insert(*insert_itr); + end_point_queue_.insert((*insert_itr).first.second); + } + insertion_set_.clear(); + } + + //iterator over range of vertex property elements and call result functor + //passing edge to be output, the merged data on both sides and the result + template + void scan(result_type& result, result_functor rf, iT begin, iT end) { + while(begin != end) { + x_ = (*begin).first.first.get(HORIZONTAL); //update scanline stop location + //print_scanline(); + --x_; + remove_retired_edges_from_scanline(); + ++x_; + begin = handle_input_events(result, rf, begin, end); + remove_retired_edges_from_scanline(); + //print_scanline(); + insert_new_edges_into_scanline(); + } + //print_scanline(); + x_ = (std::numeric_limits::max)(); + remove_retired_edges_from_scanline(); + } + + //inline void print_scanline() { + // std::cout << "scanline at " << x_ << ": "; + // for(iterator itr = scan_data_.begin(); itr != scan_data_.end(); ++itr) { + // const scanline_element& se = *itr; + // const half_edge& he = se.first; + // const property_map& mp = se.second; + // std::cout << he.first << ", " << he.second << " ( "; + // for(std::size_t i = 0; i < mp.size(); ++i) { + // std::cout << mp[i].first << ":" << mp[i].second << " "; + // } std::cout << ") "; + // } std::cout << "\n"; + //} + + static inline void merge_property_maps(property_map& mp, const property_map& mp2) { + property_map newmp; + newmp.reserve(mp.size() + mp2.size()); + unsigned int i = 0; + unsigned int j = 0; + while(i != mp.size() && j != mp2.size()) { + if(mp[i].first < mp2[j].first) { + newmp.push_back(mp[i]); + ++i; + } else if(mp[i].first > mp2[j].first) { + newmp.push_back(mp2[j]); + ++j; + } else { + int count = mp[i].second; + count += mp2[j].second; + if(count) { + newmp.push_back(mp[i]); + newmp.back().second = count; + } + ++i; + ++j; + } + } + while(i != mp.size()) { + newmp.push_back(mp[i]); + ++i; + } + while(j != mp2.size()) { + newmp.push_back(mp2[j]); + ++j; + } + mp.swap(newmp); + } + + static inline void update_property_map(property_map& mp, const std::pair& prop_data) { + property_map newmp; + newmp.reserve(mp.size() +1); + bool consumed = false; + for(std::size_t i = 0; i < mp.size(); ++i) { + if(!consumed && prop_data.first == mp[i].first) { + consumed = true; + int count = prop_data.second + mp[i].second; + if(count) + newmp.push_back(std::make_pair(prop_data.first, count)); + } else if(!consumed && prop_data.first < mp[i].first) { + consumed = true; + newmp.push_back(prop_data); + newmp.push_back(mp[i]); + } else { + newmp.push_back(mp[i]); + } + } + if(!consumed) newmp.push_back(prop_data); + mp.swap(newmp); + } + + static inline void set_unique_property(property_set& unqiue_property, const property_map& property) { + unqiue_property.clear(); + for(typename property_map::const_iterator itr = property.begin(); itr != property.end(); ++itr) { + if((*itr).second > 0) + unqiue_property.insert(unqiue_property.end(), (*itr).first); + } + } + + static inline bool common_vertex(const half_edge& he1, const half_edge& he2) { + return he1.first == he2.first || + he1.first == he2.second || + he1.second == he2.first || + he1.second == he2.second; + } + + typedef typename scanline_base::vertex_half_edge vertex_half_edge; + template + static inline void convert_segments_to_vertex_half_edges(std::vector& output, iT begin, iT end) { + for( ; begin != end; ++begin) { + const half_edge& he = (*begin).first; + int count = (*begin).second; + output.push_back(vertex_half_edge(he.first, he.second, count)); + output.push_back(vertex_half_edge(he.second, he.first, -count)); + } + polygon_sort(output.begin(), output.end()); + } + + class test_functor { + public: + inline test_functor() {} + inline void operator()(std::vector > >& result, + const half_edge& he, const property_set& ps_left, const property_set& ps_right) { + result.push_back(std::make_pair(he, std::make_pair(ps_left, ps_right))); + } + }; + template + static inline bool test_scanline(stream_type& stdcout) { + std::vector > > result; + std::vector > > input; + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 0)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(10, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(10, 10)), std::make_pair(0, -1))); + scanline sl; + test_functor tf; + sl.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + input.clear(); + result.clear(); + input.push_back(std::make_pair(half_edge(Point(-1, -1), Point(10, 0)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(-1, -1), Point(0, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(11, 11)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(11, 11)), std::make_pair(0, 1))); + scanline sl2; + sl2.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + input.clear(); + result.clear(); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 0)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(10, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(1, 1), Point(8, 2)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(1, 1), Point(2, 8)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(2, 8), Point(9, 9)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(8, 2), Point(9, 9)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(10, 10)), std::make_pair(0, -1))); + scanline sl3; + sl3.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + input.clear(); + result.clear(); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 0)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(10, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(1, 1), Point(8, 2)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(1, 1), Point(2, 8)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(2, 8), Point(9, 9)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(8, 2), Point(9, 9)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(10, 10)), std::make_pair(0, -1))); + scanline sl4; + sl4.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + input.clear(); + result.clear(); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 0)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(9, 1)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(1, 9)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(10, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(1, 9), Point(10, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(9, 1), Point(10, 10)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(10, 10)), std::make_pair(0, -1))); + scanline sl5; + sl5.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + input.clear(); + result.clear(); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 0)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(9, 1)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(1, 9)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(10, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(1, 9), Point(10, 10)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(9, 1), Point(10, 10)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(10, 10)), std::make_pair(0, -1))); + scanline sl6; + sl6.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + input.clear(); + result.clear(); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(10, 0)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(9, 1)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(1, 9)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(0, 0), Point(0, 10)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(10, 10)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(0, 20), Point(10, 20)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 20), Point(9, 21)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 20), Point(1, 29)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(0, 20), Point(0, 30)), std::make_pair(0, 1))); + input.push_back(std::make_pair(half_edge(Point(0, 30), Point(10, 30)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(1, 9), Point(10, 10)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(1, 29), Point(10, 30)), std::make_pair(1, -1))); + input.push_back(std::make_pair(half_edge(Point(9, 1), Point(10, 10)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(9, 21), Point(10, 30)), std::make_pair(1, 1))); + input.push_back(std::make_pair(half_edge(Point(10, 20), Point(10, 30)), std::make_pair(0, -1))); + input.push_back(std::make_pair(half_edge(Point(10, 20), Point(10, 30)), std::make_pair(0, -1))); + scanline sl7; + sl7.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + input.clear(); + result.clear(); + input.push_back(std::make_pair(half_edge(Point(-1, -1), Point(10, 0)), std::make_pair(0, 1))); //a + input.push_back(std::make_pair(half_edge(Point(-1, -1), Point(0, 10)), std::make_pair(0, -1))); //a + input.push_back(std::make_pair(half_edge(Point(0, 10), Point(11, 11)), std::make_pair(0, -1))); //a + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(20, 0)), std::make_pair(0, 1))); //b + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(11, 11)), std::make_pair(0, -1))); //b + input.push_back(std::make_pair(half_edge(Point(10, 0), Point(11, 11)), std::make_pair(0, 1))); //a + input.push_back(std::make_pair(half_edge(Point(11, 11), Point(20, 10)), std::make_pair(0, -1))); //b + input.push_back(std::make_pair(half_edge(Point(20, 0), Point(30, 0)), std::make_pair(0, 1))); //c + input.push_back(std::make_pair(half_edge(Point(20, 0), Point(20, 10)), std::make_pair(0, -1))); //b + input.push_back(std::make_pair(half_edge(Point(20, 0), Point(20, 10)), std::make_pair(0, 1))); //c + input.push_back(std::make_pair(half_edge(Point(20, 10), Point(30, 10)), std::make_pair(0, -1))); //c + input.push_back(std::make_pair(half_edge(Point(30, 0), Point(30, 10)), std::make_pair(0, -1))); //c + scanline sl8; + sl8.scan(result, tf, input.begin(), input.end()); + stdcout << "scanned\n"; + for(std::size_t i = 0; i < result.size(); ++i) { + stdcout << result[i].first.first << ", " << result[i].first.second << "; "; + } stdcout << "\n"; + return true; + } + + }; + + template + class merge_output_functor { + public: + typedef typename scanline_base::half_edge half_edge; + merge_output_functor() {} + template + void operator()(result_type& result, const half_edge& edge, const key_type& left, const key_type& right) { + typename std::pair elem; + elem.first = edge; + elem.second = 1; + if(edge.second < edge.first) elem.second *= -1; + if(scanline_base::is_vertical(edge)) elem.second *= -1; + if(!left.empty()) + result[left].insert_clean(elem); + elem.second *= -1; + if(!right.empty()) + result[right].insert_clean(elem); + } + }; + + template , + typename output_functor_type = merge_output_functor > + class property_merge : public scanline_base { + protected: + typedef typename scanline_base::Point Point; + + //the first point is the vertex and and second point establishes the slope of an edge eminating from the vertex + //typedef std::pair half_edge; + typedef typename scanline_base::half_edge half_edge; + + //scanline comparator functor + typedef typename scanline_base::less_half_edge less_half_edge; + typedef typename scanline_base::less_point less_point; + + //this data structure assocates a property and count to a half edge + typedef std::pair > vertex_property; + //this data type stores the combination of many half edges + typedef std::vector property_merge_data; + + //this is the data type used internally to store the combination of property counts at a given location + typedef std::vector > property_map; + //this data type is used internally to store the combined property data for a given half edge + typedef std::pair vertex_data; + + property_merge_data pmd; + typename scanline_base::evalAtXforYPack evalAtXforYPack_; + + template + class less_vertex_data { + typename scanline_base::evalAtXforYPack* pack_; + public: + less_vertex_data() : pack_() {} + less_vertex_data(typename scanline_base::evalAtXforYPack* pack) : pack_(pack) {} + bool operator() (const vertex_data_type& lvalue, const vertex_data_type& rvalue) const { + less_point lp; + if(lp(lvalue.first.first, rvalue.first.first)) return true; + if(lp(rvalue.first.first, lvalue.first.first)) return false; + Unit x = lvalue.first.first.get(HORIZONTAL); + int just_before_ = 0; + less_half_edge lhe(&x, &just_before_, pack_); + return lhe(lvalue.first, rvalue.first); + } + }; + + + inline void sort_property_merge_data() { + less_vertex_data lvd(&evalAtXforYPack_); + polygon_sort(pmd.begin(), pmd.end(), lvd); + } + public: + inline property_merge_data& get_property_merge_data() { return pmd; } + inline property_merge() : pmd(), evalAtXforYPack_() {} + inline property_merge(const property_merge& pm) : pmd(pm.pmd), evalAtXforYPack_(pm.evalAtXforYPack_) {} + inline property_merge& operator=(const property_merge& pm) { pmd = pm.pmd; return *this; } + + template + void insert(const polygon_type& polygon_object, const property_type& property_value, bool is_hole = false) { + insert(polygon_object, property_value, is_hole, typename geometry_concept::type()); + } + + //result type should be std::map, polygon_set_type> + //or std::map, polygon_set_type> + template + void merge(result_type& result) { + if(pmd.empty()) return; + //intersect data + property_merge_data tmp_pmd; + line_intersection::validate_scan(tmp_pmd, pmd.begin(), pmd.end()); + pmd.swap(tmp_pmd); + sort_property_merge_data(); + scanline sl; + output_functor_type mof; + sl.scan(result, mof, pmd.begin(), pmd.end()); + } + + inline bool verify1() { + std::pair offenders; + std::vector > lines; + int count = 0; + for(std::size_t i = 0; i < pmd.size(); ++i) { + lines.push_back(std::make_pair(pmd[i].first, count++)); + } + if(!line_intersection::verify_scan(offenders, lines.begin(), lines.end())) { + //stdcout << "Intersection failed!\n"; + //stdcout << offenders.first << " " << offenders.second << "\n"; + return false; + } + std::vector pts; + for(std::size_t i = 0; i < lines.size(); ++i) { + pts.push_back(lines[i].first.first); + pts.push_back(lines[i].first.second); + } + polygon_sort(pts.begin(), pts.end()); + for(std::size_t i = 0; i < pts.size(); i+=2) { + if(pts[i] != pts[i+1]) { + //stdcout << "Non-closed figures after line intersection!\n"; + return false; + } + } + return true; + } + + void clear() {*this = property_merge();} + + protected: + template + void insert(const polygon_type& polygon_object, const property_type& property_value, bool is_hole, + polygon_concept ) { + bool first_iteration = true; + bool second_iteration = true; + Point first_point; + Point second_point; + Point previous_previous_point; + Point previous_point; + Point current_point; + direction_1d winding_dir = winding(polygon_object); + for(typename polygon_traits::iterator_type itr = begin_points(polygon_object); + itr != end_points(polygon_object); ++itr) { + assign(current_point, *itr); + if(first_iteration) { + first_iteration = false; + first_point = previous_point = current_point; + } else if(second_iteration) { + if(previous_point != current_point) { + second_iteration = false; + previous_previous_point = previous_point; + second_point = previous_point = current_point; + } + } else { + if(previous_point != current_point) { + create_vertex(pmd, previous_point, current_point, winding_dir, + is_hole, property_value); + previous_previous_point = previous_point; + previous_point = current_point; + } + } + } + current_point = first_point; + if(!first_iteration && !second_iteration) { + if(previous_point != current_point) { + create_vertex(pmd, previous_point, current_point, winding_dir, + is_hole, property_value); + previous_previous_point = previous_point; + previous_point = current_point; + } + current_point = second_point; + create_vertex(pmd, previous_point, current_point, winding_dir, + is_hole, property_value); + previous_previous_point = previous_point; + previous_point = current_point; + } + } + + template + void insert(const polygon_with_holes_type& polygon_with_holes_object, const property_type& property_value, bool is_hole, + polygon_with_holes_concept) { + insert(polygon_with_holes_object, property_value, is_hole, polygon_concept()); + for(typename polygon_with_holes_traits::iterator_holes_type itr = + begin_holes(polygon_with_holes_object); + itr != end_holes(polygon_with_holes_object); ++itr) { + insert(*itr, property_value, !is_hole, polygon_concept()); + } + } + + template + void insert(const rectangle_type& rectangle_object, const property_type& property_value, bool is_hole, + rectangle_concept ) { + polygon_90_data poly; + assign(poly, rectangle_object); + insert(poly, property_value, is_hole, polygon_concept()); + } + + public: //change to private when done testing + + static inline void create_vertex(property_merge_data& pmd, + const Point& current_point, + const Point& next_point, + direction_1d winding, + bool is_hole, const property_type& property) { + if(current_point == next_point) return; + vertex_property current_vertex; + current_vertex.first.first = current_point; + current_vertex.first.second = next_point; + current_vertex.second.first = property; + int multiplier = 1; + if(winding == CLOCKWISE) + multiplier = -1; + if(is_hole) + multiplier *= -1; + if(current_point < next_point) { + multiplier *= -1; + std::swap(current_vertex.first.first, current_vertex.first.second); + } + current_vertex.second.second = multiplier * (euclidean_distance(next_point, current_point, HORIZONTAL) == 0 ? -1: 1); + pmd.push_back(current_vertex); + //current_vertex.first.second = previous_point; + //current_vertex.second.second *= -1; + //pmd.push_back(current_vertex); + } + + static inline void sort_vertex_half_edges(vertex_data& vertex) { + less_half_edge_pair lessF(vertex.first); + polygon_sort(vertex.second.begin(), vertex.second.end(), lessF); + } + + class less_half_edge_pair { + private: + Point pt_; + public: + less_half_edge_pair(const Point& pt) : pt_(pt) {} + bool operator()(const half_edge& e1, const half_edge& e2) { + const Point& pt1 = e1.first; + const Point& pt2 = e2.first; + if(get(pt1, HORIZONTAL) == + get(pt_, HORIZONTAL)) { + //vertical edge is always largest + return false; + } + if(get(pt2, HORIZONTAL) == + get(pt_, HORIZONTAL)) { + //if half edge 1 is not vertical its slope is less than that of half edge 2 + return get(pt1, HORIZONTAL) != get(pt2, HORIZONTAL); + } + return scanline_base::less_slope(get(pt_, HORIZONTAL), + get(pt_, VERTICAL), pt1, pt2); + } + }; + + public: + //test functions + template + static stream_type& print (stream_type& o, const property_map& c) + { + o << "count: {"; + for(typename property_map::const_iterator itr = c.begin(); itr != c.end(); ++itr) { + o << ((*itr).first) << ":" << ((*itr).second) << " "; + } + return o << "} "; + } + + + template + static stream_type& print (stream_type& o, const half_edge& he) + { + o << "half edge: ("; + o << (he.first); + return o << ", " << (he.second) << ") "; + } + + template + static stream_type& print (stream_type& o, const vertex_property& c) + { + o << "vertex property: {"; + print(o, c.first); + o << ", " << c.second.first << ":" << c.second.second << " "; + return o; + } + + template + static stream_type& print (stream_type& o, const std::vector& hev) + { + o << "vertex properties: {"; + for(std::size_t i = 0; i < hev.size(); ++i) { + print(o, (hev[i])) << " "; + } + return o << "} "; + } + + template + static stream_type& print (stream_type& o, const std::vector& hev) + { + o << "half edges: {"; + for(std::size_t i = 0; i < hev.size(); ++i) { + print(o, (hev[i])) << " "; + } + return o << "} "; + } + + template + static stream_type& print (stream_type& o, const vertex_data& v) + { + return print(o << "vertex: <" << (v.first) << ", ", (v.second)) << "> "; + } + + template + static stream_type& print (stream_type& o, const std::vector& vv) + { + o << "vertices: {"; + for(std::size_t i = 0; i < vv.size(); ++i) { + print(o, (vv[i])) << " "; + } + return o << "} "; + } + + + + template + static inline bool test_insertion(stream_type& stdcout) { + property_merge si; + rectangle_data rect; + xl(rect, 0); + yl(rect, 1); + xh(rect, 10); + yh(rect, 11); + + si.insert(rect, 333); + print(stdcout, si.pmd) << "\n"; + + Point pts[4] = {Point(0, 0), Point(10,-3), Point(13, 8), Point(0, 0) }; + polygon_data poly; + property_merge si2; + poly.set(pts, pts+3); + si2.insert(poly, 444); + si2.sort_property_merge_data(); + print(stdcout, si2.pmd) << "\n"; + property_merge si3; + poly.set(pts, pts+4); + si3.insert(poly, 444); + si3.sort_property_merge_data(); + stdcout << (si2.pmd == si3.pmd) << "\n"; + std::reverse(pts, pts+4); + property_merge si4; + poly.set(pts, pts+4); + si4.insert(poly, 444); + si4.sort_property_merge_data(); + print(stdcout, si4.pmd) << "\n"; + stdcout << (si2.pmd == si4.pmd) << "\n"; + std::reverse(pts, pts+3); + property_merge si5; + poly.set(pts, pts+4); + si5.insert(poly, 444); + si5.sort_property_merge_data(); + stdcout << (si2.pmd == si5.pmd) << "\n"; + + return true; + } + + template + static inline bool test_merge(stream_type& stdcout) { + property_merge si; + rectangle_data rect; + xl(rect, 0); + yl(rect, 1); + xh(rect, 10); + yh(rect, 11); + + si.insert(rect, 333); + std::map, polygon_set_data > result; + si.merge(result); + print(stdcout, si.pmd) << "\n"; + polygon_set_data psd = (*(result.begin())).second; + std::vector > polys; + psd.get(polys); + if(polys.size() != 1) { + stdcout << "fail merge 1\n"; + return false; + } + stdcout << (polys[0]) << "\n"; + si.clear(); + std::vector pts; + pts.push_back(Point(0, 0)); + pts.push_back(Point(10, -10)); + pts.push_back(Point(10, 10)); + polygon_data poly; + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(5, 0)); + pts.push_back(Point(-5, -10)); + pts.push_back(Point(-5, 10)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() != 1) { + stdcout << "fail merge 2\n"; + return false; + } + //Polygon { -4 -1, 3 3, -2 3 } + //Polygon { 0 -4, -4 -2, -2 1 } + si.clear(); + pts.clear(); + pts.push_back(Point(-4, -1)); + pts.push_back(Point(3, 3)); + pts.push_back(Point(-2, 3)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(0, -4)); + pts.push_back(Point(-4, -2)); + pts.push_back(Point(-2, 1)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() != 1) { + stdcout << "fail merge 3\n"; + return false; + } + stdcout << "Polygon { -2 2, -2 2, 1 4 } \n"; + stdcout << "Polygon { 2 4, 2 -4, -3 1 } \n"; + si.clear(); + pts.clear(); + pts.push_back(Point(-2, 2)); + pts.push_back(Point(-2, 2)); + pts.push_back(Point(1, 4)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(2, 4)); + pts.push_back(Point(2, -4)); + pts.push_back(Point(-3, 1)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() != 1) { + stdcout << "fail merge 4\n"; + return false; + } + stdcout << (polys[0]) << "\n"; + stdcout << "Polygon { -4 0, -2 -3, 3 -4 } \n"; + stdcout << "Polygon { -1 1, 1 -2, -4 -3 } \n"; + si.clear(); + pts.clear(); + pts.push_back(Point(-4, 0)); + pts.push_back(Point(-2, -3)); + pts.push_back(Point(3, -4)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(-1, 1)); + pts.push_back(Point(1, -2)); + pts.push_back(Point(-4, -3)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() != 1) { + stdcout << "fail merge 5\n"; + return false; + } + stdcout << "Polygon { 2 2, -2 0, 0 1 } \n"; + stdcout << "Polygon { 4 -2, 3 -1, 2 3 } \n"; + si.clear(); + pts.clear(); + pts.push_back(Point(2, 2)); + pts.push_back(Point(-2, 0)); + pts.push_back(Point(0, 1)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(4, -2)); + pts.push_back(Point(3, -1)); + pts.push_back(Point(2, 3)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + if(!result.empty()) { + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() != 1) { + stdcout << "fail merge 6\n"; + return false; + } + stdcout << (polys[0]) << "\n"; + } + stdcout << "Polygon { 0 2, 3 -1, 4 1 } \n"; + stdcout << "Polygon { -4 3, 3 3, 4 2 } \n"; + si.clear(); + pts.clear(); + pts.push_back(Point(0, 2)); + pts.push_back(Point(3, -1)); + pts.push_back(Point(4, 1)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(-4, 3)); + pts.push_back(Point(3, 3)); + pts.push_back(Point(4, 2)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + if(!result.empty()) { + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() == 0) { + stdcout << "fail merge 7\n"; + return false; + } + stdcout << (polys[0]) << "\n"; + } +stdcout << "Polygon { 1 -2, -1 4, 3 -2 } \n"; +stdcout << "Polygon { 0 -3, 3 1, -3 -4 } \n"; + si.clear(); + pts.clear(); + pts.push_back(Point(1, -2)); + pts.push_back(Point(-1, 4)); + pts.push_back(Point(3, -2)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(0, -3)); + pts.push_back(Point(3, 1)); + pts.push_back(Point(-3, -4)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + if(!result.empty()) { + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() == 0) { + stdcout << "fail merge 8\n"; + return false; + } + stdcout << (polys[0]) << "\n"; + } +stdcout << "Polygon { 2 2, 3 0, -3 4 } \n"; +stdcout << "Polygon { -2 -2, 0 0, -1 -1 } \n"; + si.clear(); + pts.clear(); + pts.push_back(Point(2, 2)); + pts.push_back(Point(3, 0)); + pts.push_back(Point(-3, 4)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + pts.clear(); + pts.push_back(Point(-2, -2)); + pts.push_back(Point(0, 0)); + pts.push_back(Point(-1, -1)); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + if(!result.empty()) { + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() == 0) { + stdcout << "fail merge 9\n"; + return false; + } + stdcout << (polys[0]) << "\n"; + } + si.clear(); + pts.clear(); + //5624841,17616200,75000,9125000 + //pts.push_back(Point(5624841,75000)); + //pts.push_back(Point(5624841,9125000)); + //pts.push_back(Point(17616200,9125000)); + //pts.push_back(Point(17616200,75000)); +pts.push_back(Point(12262940, 6652520 )); pts.push_back(Point(12125750, 6652520 )); pts.push_back(Point(12121272, 6652961 )); pts.push_back(Point(12112981, 6656396 )); pts.push_back(Point(12106636, 6662741 )); pts.push_back(Point(12103201, 6671032 )); pts.push_back(Point(12103201, 6680007 )); pts.push_back(Point(12106636, 6688298 )); +pts.push_back(Point(12109500, 6691780 )); pts.push_back(Point(12748600, 7330890 )); pts.push_back(Point(15762600, 7330890 )); pts.push_back(Point(15904620, 7472900 )); pts.push_back(Point(15909200, 7473030 )); pts.push_back(Point(15935830, 7476006 )); pts.push_back(Point(15992796, 7499602 )); pts.push_back(Point(16036397, 7543203 )); +pts.push_back(Point(16059993, 7600169 )); pts.push_back(Point(16059993, 7661830 )); pts.push_back(Point(16036397, 7718796 )); pts.push_back(Point(15992796, 7762397 )); pts.push_back(Point(15935830, 7785993 )); pts.push_back(Point(15874169, 7785993 )); pts.push_back(Point(15817203, 7762397 )); pts.push_back(Point(15773602, 7718796 )); +pts.push_back(Point(15750006, 7661830 )); pts.push_back(Point(15747030, 7635200 )); pts.push_back(Point(15746900, 7630620 )); pts.push_back(Point(15670220, 7553930 )); pts.push_back(Point(14872950, 7553930 )); pts.push_back(Point(14872950, 7626170 )); +pts.push_back(Point(14869973, 7661280 )); pts.push_back(Point(14846377, 7718246 )); pts.push_back(Point(14802776, 7761847 )); pts.push_back(Point(14745810, 7785443 )); pts.push_back(Point(14684149, 7785443 )); pts.push_back(Point(14627183, 7761847 )); pts.push_back(Point(14583582, 7718246 )); +pts.push_back(Point(14559986, 7661280 )); pts.push_back(Point(14557070, 7636660 )); pts.push_back(Point(14556670, 7625570 )); pts.push_back(Point(13703330, 7625570 )); pts.push_back(Point(13702930, 7636660 )); pts.push_back(Point(13699993, 7661830 )); pts.push_back(Point(13676397, 7718796 )); +pts.push_back(Point(13632796, 7762397 )); pts.push_back(Point(13575830, 7785993 )); pts.push_back(Point(13514169, 7785993 )); pts.push_back(Point(13457203, 7762397 )); pts.push_back(Point(13436270, 7745670 )); pts.push_back(Point(13432940, 7742520 )); pts.push_back(Point(12963760, 7742520 )); +pts.push_back(Point(12959272, 7742961 )); pts.push_back(Point(12950981, 7746396 )); pts.push_back(Point(12944636, 7752741 )); pts.push_back(Point(12941201, 7761032 )); pts.push_back(Point(12941201, 7770007 )); pts.push_back(Point(12944636, 7778298 )); pts.push_back(Point(12947490, 7781780 )); +pts.push_back(Point(13425330, 8259620 )); pts.push_back(Point(15601330, 8259620 )); pts.push_back(Point(15904620, 8562900 )); pts.push_back(Point(15909200, 8563030 )); pts.push_back(Point(15935830, 8566006 )); pts.push_back(Point(15992796, 8589602 )); pts.push_back(Point(16036397, 8633203 )); +pts.push_back(Point(16059993, 8690169 )); pts.push_back(Point(16059993, 8751830 )); pts.push_back(Point(16036397, 8808796 )); pts.push_back(Point(15992796, 8852397 )); pts.push_back(Point(15935830, 8875993 )); pts.push_back(Point(15874169, 8875993 )); pts.push_back(Point(15817203, 8852397 )); pts.push_back(Point(15773602, 8808796 )); +pts.push_back(Point(15750006, 8751830 )); pts.push_back(Point(15747030, 8725200 )); pts.push_back(Point(15746900, 8720620 )); pts.push_back(Point(15508950, 8482660 )); pts.push_back(Point(14689890, 8482660 )); pts.push_back(Point(14685412, 8483101 )); pts.push_back(Point(14677121, 8486536 )); +pts.push_back(Point(14670776, 8492881 )); pts.push_back(Point(14667341, 8501172 )); pts.push_back(Point(14667341, 8510147 )); pts.push_back(Point(14670776, 8518438 )); pts.push_back(Point(14673630, 8521920 )); pts.push_back(Point(14714620, 8562900 )); pts.push_back(Point(14719200, 8563030 )); pts.push_back(Point(14745830, 8566006 )); +pts.push_back(Point(14802796, 8589602 )); pts.push_back(Point(14846397, 8633203 )); pts.push_back(Point(14869993, 8690169 )); pts.push_back(Point(14869993, 8751830 )); pts.push_back(Point(14846397, 8808796 )); pts.push_back(Point(14802796, 8852397 )); pts.push_back(Point(14745830, 8875993 )); pts.push_back(Point(14684169, 8875993 )); +pts.push_back(Point(14627203, 8852397 )); pts.push_back(Point(14583602, 8808796 )); pts.push_back(Point(14560006, 8751830 )); pts.push_back(Point(14557030, 8725200 )); pts.push_back(Point(14556900, 8720620 )); pts.push_back(Point(14408270, 8571980 )); pts.push_back(Point(13696320, 8571980 )); pts.push_back(Point(13696320, 8675520 )); +pts.push_back(Point(13699963, 8690161 )); pts.push_back(Point(13699963, 8751818 )); pts.push_back(Point(13676368, 8808781 )); pts.push_back(Point(13632771, 8852378 )); pts.push_back(Point(13575808, 8875973 )); pts.push_back(Point(13514151, 8875973 )); pts.push_back(Point(13457188, 8852378 )); pts.push_back(Point(13436270, 8835670 )); pts.push_back(Point(13432940, 8832520 )); +pts.push_back(Point(13281760, 8832520 )); pts.push_back(Point(13277272, 8832961 )); pts.push_back(Point(13268981, 8836396 )); pts.push_back(Point(13262636, 8842741 )); pts.push_back(Point(13259201, 8851032 )); pts.push_back(Point(13259201, 8860007 )); pts.push_back(Point(13262636, 8868298 )); pts.push_back(Point(13265500, 8871780 )); +pts.push_back(Point(13518710, 9125000 )); pts.push_back(Point(16270720, 9125000 )); pts.push_back(Point(16270720, 8939590 )); pts.push_back(Point(17120780, 8939590 )); pts.push_back(Point(17120780, 9125000 )); pts.push_back(Point(17616200, 9125000 )); pts.push_back(Point(17616200, 75000 )); pts.push_back(Point(16024790, 75000 )); +pts.push_back(Point(16021460, 80700 )); pts.push_back(Point(16016397, 88796 )); pts.push_back(Point(15972796, 132397 )); pts.push_back(Point(15915830, 155993 )); pts.push_back(Point(15908730, 157240 )); pts.push_back(Point(15905000, 157800 )); pts.push_back(Point(15516800, 546000 )); pts.push_back(Point(15905000, 934200 )); +pts.push_back(Point(15908730, 934760 )); pts.push_back(Point(15915830, 936006 )); pts.push_back(Point(15972796, 959602 )); pts.push_back(Point(16016397, 1003203 )); pts.push_back(Point(16039993, 1060169 )); pts.push_back(Point(16039993, 1121830 )); pts.push_back(Point(16016397, 1178796 )); pts.push_back(Point(15972796, 1222397 )); +pts.push_back(Point(15915830, 1245993 )); pts.push_back(Point(15854169, 1245993 )); pts.push_back(Point(15797203, 1222397 )); pts.push_back(Point(15753602, 1178796 )); pts.push_back(Point(15730006, 1121830 )); pts.push_back(Point(15728760, 1114730 )); pts.push_back(Point(15728200, 1111000 )); pts.push_back(Point(15363500, 746300 )); +pts.push_back(Point(14602620, 746300 )); pts.push_back(Point(14598142, 746741 )); pts.push_back(Point(14589851, 750176 )); pts.push_back(Point(14583506, 756521 )); pts.push_back(Point(14580071, 764812 )); pts.push_back(Point(14580071, 773787 )); pts.push_back(Point(14583506, 782078 )); pts.push_back(Point(14586360, 785560 )); +pts.push_back(Point(14586370, 785560 )); pts.push_back(Point(14735000, 934200 )); pts.push_back(Point(14738730, 934760 )); pts.push_back(Point(14745830, 936006 )); pts.push_back(Point(14802796, 959602 )); pts.push_back(Point(14846397, 1003203 )); pts.push_back(Point(14869993, 1060169 )); +pts.push_back(Point(14870450, 1062550 )); pts.push_back(Point(14872170, 1071980 )); pts.push_back(Point(14972780, 1071980 )); pts.push_back(Point(15925000, 2024200 )); pts.push_back(Point(15928730, 2024760 )); pts.push_back(Point(15935830, 2026006 )); pts.push_back(Point(15992796, 2049602 )); +pts.push_back(Point(16036397, 2093203 )); pts.push_back(Point(16059993, 2150169 )); pts.push_back(Point(16059993, 2211830 )); pts.push_back(Point(16036397, 2268796 )); pts.push_back(Point(15992796, 2312397 )); pts.push_back(Point(15935830, 2335993 )); pts.push_back(Point(15874169, 2335993 )); +pts.push_back(Point(15817203, 2312397 )); pts.push_back(Point(15773602, 2268796 )); pts.push_back(Point(15750006, 2211830 )); pts.push_back(Point(15748760, 2204730 )); pts.push_back(Point(15748200, 2201000 )); pts.push_back(Point(14869220, 1322020 )); pts.push_back(Point(14088350, 1322020 )); +pts.push_back(Point(14083862, 1322461 )); pts.push_back(Point(14075571, 1325896 )); pts.push_back(Point(14069226, 1332241 )); pts.push_back(Point(14065791, 1340532 )); pts.push_back(Point(14065791, 1349507 )); pts.push_back(Point(14069226, 1357798 )); pts.push_back(Point(14072080, 1361280 )); +pts.push_back(Point(14072090, 1361280 )); pts.push_back(Point(14735000, 2024200 )); pts.push_back(Point(14738730, 2024760 )); pts.push_back(Point(14745830, 2026006 )); pts.push_back(Point(14802796, 2049602 )); pts.push_back(Point(14846397, 2093203 )); pts.push_back(Point(14869993, 2150169 )); +pts.push_back(Point(14869993, 2211830 )); pts.push_back(Point(14846397, 2268796 )); pts.push_back(Point(14802796, 2312397 )); pts.push_back(Point(14745830, 2335993 )); pts.push_back(Point(14684169, 2335993 )); pts.push_back(Point(14627203, 2312397 )); pts.push_back(Point(14583602, 2268796 )); pts.push_back(Point(14560006, 2211830 )); +pts.push_back(Point(14558760, 2204730 )); pts.push_back(Point(14558200, 2201000 )); pts.push_back(Point(13752220, 1395020 )); pts.push_back(Point(12991340, 1395020 )); pts.push_back(Point(12986862, 1395461 )); pts.push_back(Point(12978571, 1398896 )); pts.push_back(Point(12972226, 1405241 )); +pts.push_back(Point(12968791, 1413532 )); pts.push_back(Point(12968791, 1422507 )); pts.push_back(Point(12972226, 1430798 )); pts.push_back(Point(12975080, 1434280 )); pts.push_back(Point(12975090, 1434280 )); pts.push_back(Point(13565000, 2024200 )); pts.push_back(Point(13568730, 2024760 )); pts.push_back(Point(13575830, 2026006 )); +pts.push_back(Point(13632796, 2049602 )); pts.push_back(Point(13676397, 2093203 )); pts.push_back(Point(13699993, 2150169 )); pts.push_back(Point(13699993, 2211830 )); pts.push_back(Point(13676397, 2268796 )); pts.push_back(Point(13632796, 2312397 )); pts.push_back(Point(13575830, 2335993 )); +pts.push_back(Point(13514169, 2335993 )); pts.push_back(Point(13457203, 2312397 )); pts.push_back(Point(13413602, 2268796 )); pts.push_back(Point(13390006, 2211830 )); pts.push_back(Point(13388760, 2204730 )); pts.push_back(Point(13388200, 2201000 )); pts.push_back(Point(12655220, 1468020 )); +pts.push_back(Point(11894340, 1468020 )); pts.push_back(Point(11889862, 1468461 )); pts.push_back(Point(11881571, 1471896 )); pts.push_back(Point(11875226, 1478241 )); pts.push_back(Point(11871791, 1486532 )); pts.push_back(Point(11871791, 1495507 )); +pts.push_back(Point(11875226, 1503798 )); pts.push_back(Point(11878090, 1507280 )); pts.push_back(Point(12395000, 2024200 )); pts.push_back(Point(12398730, 2024760 )); pts.push_back(Point(12405830, 2026006 )); pts.push_back(Point(12462796, 2049602 )); pts.push_back(Point(12506397, 2093203 )); +pts.push_back(Point(12529993, 2150169 )); pts.push_back(Point(12529993, 2211830 )); pts.push_back(Point(12506397, 2268796 )); pts.push_back(Point(12462796, 2312397 )); pts.push_back(Point(12405830, 2335993 )); pts.push_back(Point(12344169, 2335993 )); +pts.push_back(Point(12287203, 2312397 )); pts.push_back(Point(12243602, 2268796 )); pts.push_back(Point(12220006, 2211830 )); pts.push_back(Point(12218760, 2204730 )); pts.push_back(Point(12218200, 2201000 )); pts.push_back(Point(11558220, 1541020 )); +pts.push_back(Point(10797340, 1541020 )); pts.push_back(Point(10792862, 1541461 )); pts.push_back(Point(10784571, 1544896 )); pts.push_back(Point(10778226, 1551241 )); pts.push_back(Point(10774791, 1559532 )); pts.push_back(Point(10774791, 1568507 )); pts.push_back(Point(10778226, 1576798 )); pts.push_back(Point(10781080, 1580280 )); +pts.push_back(Point(10781090, 1580280 )); pts.push_back(Point(11225000, 2024200 )); pts.push_back(Point(11228730, 2024760 )); pts.push_back(Point(11235830, 2026006 )); pts.push_back(Point(11292796, 2049602 )); pts.push_back(Point(11336397, 2093203 )); pts.push_back(Point(11359993, 2150169 )); +pts.push_back(Point(11359993, 2211830 )); pts.push_back(Point(11336397, 2268796 )); pts.push_back(Point(11292796, 2312397 )); pts.push_back(Point(11235830, 2335993 )); pts.push_back(Point(11174169, 2335993 )); pts.push_back(Point(11117203, 2312397 )); pts.push_back(Point(11073602, 2268796 )); pts.push_back(Point(11050006, 2211830 )); +pts.push_back(Point(11048760, 2204730 )); pts.push_back(Point(11048200, 2201000 )); pts.push_back(Point(10461220, 1614020 )); pts.push_back(Point( 5647400, 1614020 )); pts.push_back(Point( 5642912, 1614461 )); +pts.push_back(Point( 5634621, 1617896 )); pts.push_back(Point( 5628276, 1624241 )); pts.push_back(Point( 5624841, 1632532 )); pts.push_back(Point( 5624841, 1641507 )); pts.push_back(Point( 5628276, 1649798 )); pts.push_back(Point( 5631130, 1653280 )); +pts.push_back(Point( 5688490, 1710640 )); pts.push_back(Point( 9722350, 1710640 )); pts.push_back(Point(10034620, 2022900 )); pts.push_back(Point(10039200, 2023030 )); pts.push_back(Point(10065830, 2026006 )); pts.push_back(Point(10122796, 2049602 )); +pts.push_back(Point(10166397, 2093203 )); pts.push_back(Point(10189993, 2150169 )); pts.push_back(Point(10189993, 2211830 )); pts.push_back(Point(10166397, 2268796 )); pts.push_back(Point(10158620, 2279450 )); pts.push_back(Point(10158620, 2404900 )); pts.push_back(Point(10548950, 2795240 )); +pts.push_back(Point(15586950, 2795240 )); pts.push_back(Point(15904620, 3112900 )); pts.push_back(Point(15909200, 3113030 )); pts.push_back(Point(15935830, 3116006 )); pts.push_back(Point(15992796, 3139602 )); pts.push_back(Point(16036397, 3183203 )); pts.push_back(Point(16059993, 3240169 )); pts.push_back(Point(16059993, 3301830 )); +pts.push_back(Point(16036397, 3358796 )); pts.push_back(Point(15992796, 3402397 )); pts.push_back(Point(15935830, 3425993 )); pts.push_back(Point(15874169, 3425993 )); pts.push_back(Point(15817203, 3402397 )); pts.push_back(Point(15773602, 3358796 )); pts.push_back(Point(15750006, 3301830 )); pts.push_back(Point(15747030, 3275200 )); +pts.push_back(Point(15746900, 3270620 )); pts.push_back(Point(15494570, 3018280 )); pts.push_back(Point(14675510, 3018280 )); pts.push_back(Point(14671032, 3018721 )); pts.push_back(Point(14662741, 3022156 )); pts.push_back(Point(14656396, 3028501 )); pts.push_back(Point(14652961, 3036792 )); +pts.push_back(Point(14652961, 3045767 )); pts.push_back(Point(14656396, 3054058 )); pts.push_back(Point(14659260, 3057540 )); pts.push_back(Point(14714620, 3112900 )); pts.push_back(Point(14719200, 3113030 )); pts.push_back(Point(14745830, 3116006 )); pts.push_back(Point(14802796, 3139602 )); +pts.push_back(Point(14846397, 3183203 )); pts.push_back(Point(14869993, 3240169 )); pts.push_back(Point(14869993, 3301830 )); pts.push_back(Point(14846397, 3358796 )); pts.push_back(Point(14802796, 3402397 )); pts.push_back(Point(14745830, 3425993 )); pts.push_back(Point(14684169, 3425993 )); pts.push_back(Point(14627203, 3402397 )); +pts.push_back(Point(14583602, 3358796 )); pts.push_back(Point(14560006, 3301830 )); pts.push_back(Point(14557030, 3275200 )); pts.push_back(Point(14556900, 3270620 )); pts.push_back(Point(14370700, 3084410 )); pts.push_back(Point(13702830, 3084410 )); pts.push_back(Point(13702830, 3263160 )); +pts.push_back(Point(13700003, 3302210 )); pts.push_back(Point(13676407, 3359176 )); pts.push_back(Point(13632806, 3402777 )); pts.push_back(Point(13575840, 3426373 )); pts.push_back(Point(13514179, 3426373 )); pts.push_back(Point(13457213, 3402777 )); pts.push_back(Point(13413612, 3359176 )); +pts.push_back(Point(13390016, 3302210 )); pts.push_back(Point(13387030, 3275200 )); pts.push_back(Point(13386900, 3270620 )); pts.push_back(Point(13266840, 3150550 )); pts.push_back(Point(12532920, 3150550 )); pts.push_back(Point(12532920, 3264990 )); pts.push_back(Point(12529993, 3301820 )); +pts.push_back(Point(12506397, 3358786 )); pts.push_back(Point(12462796, 3402387 )); pts.push_back(Point(12405830, 3425983 )); pts.push_back(Point(12344169, 3425983 )); pts.push_back(Point(12287203, 3402387 )); pts.push_back(Point(12243602, 3358786 )); pts.push_back(Point(12220006, 3301820 )); pts.push_back(Point(12217030, 3275200 )); +pts.push_back(Point(12216900, 3270620 )); pts.push_back(Point(12157460, 3211170 )); pts.push_back(Point(11362030, 3211170 )); pts.push_back(Point(11360250, 3220520 )); pts.push_back(Point(11359993, 3221830 )); pts.push_back(Point(11336397, 3278796 )); +pts.push_back(Point(11292796, 3322397 )); pts.push_back(Point(11235830, 3345993 )); pts.push_back(Point(11174169, 3345993 )); pts.push_back(Point(11117203, 3322397 )); pts.push_back(Point(11096270, 3305670 )); pts.push_back(Point(11092940, 3302520 )); pts.push_back(Point(10680760, 3302520 )); +pts.push_back(Point(10676272, 3302961 )); pts.push_back(Point(10667981, 3306396 )); pts.push_back(Point(10661636, 3312741 )); pts.push_back(Point(10658201, 3321032 )); pts.push_back(Point(10658201, 3330007 )); pts.push_back(Point(10661636, 3338298 )); pts.push_back(Point(10664500, 3341780 )); +pts.push_back(Point(11264260, 3941550 )); pts.push_back(Point(15643260, 3941550 )); pts.push_back(Point(15904620, 4202900 )); pts.push_back(Point(15909200, 4203030 )); pts.push_back(Point(15935830, 4206006 )); pts.push_back(Point(15992796, 4229602 )); +pts.push_back(Point(16036397, 4273203 )); pts.push_back(Point(16059993, 4330169 )); pts.push_back(Point(16059993, 4391830 )); pts.push_back(Point(16036397, 4448796 )); pts.push_back(Point(15992796, 4492397 )); +pts.push_back(Point(15935830, 4515993 )); pts.push_back(Point(15874169, 4515993 )); pts.push_back(Point(15817203, 4492397 )); pts.push_back(Point(15773602, 4448796 )); pts.push_back(Point(15750006, 4391830 )); pts.push_back(Point(15747030, 4365200 )); pts.push_back(Point(15746900, 4360620 )); +pts.push_back(Point(15550880, 4164590 )); pts.push_back(Point(14825070, 4164590 )); pts.push_back(Point(14825070, 4247610 )); pts.push_back(Point(14846397, 4273213 )); pts.push_back(Point(14869993, 4330179 )); pts.push_back(Point(14869993, 4391840 )); pts.push_back(Point(14846397, 4448806 )); +pts.push_back(Point(14802796, 4492407 )); pts.push_back(Point(14745830, 4516003 )); pts.push_back(Point(14684169, 4516003 )); pts.push_back(Point(14627203, 4492407 )); pts.push_back(Point(14583602, 4448806 )); pts.push_back(Point(14560006, 4391840 )); pts.push_back(Point(14557030, 4365200 )); +pts.push_back(Point(14556900, 4360620 )); pts.push_back(Point(14432520, 4236230 )); pts.push_back(Point(13702830, 4236230 )); pts.push_back(Point(13702830, 4352930 )); pts.push_back(Point(13699993, 4391750 )); pts.push_back(Point(13676397, 4448716 )); +pts.push_back(Point(13632796, 4492317 )); pts.push_back(Point(13575830, 4515913 )); pts.push_back(Point(13514169, 4515913 )); pts.push_back(Point(13457203, 4492317 )); pts.push_back(Point(13413602, 4448716 )); pts.push_back(Point(13390006, 4391750 )); pts.push_back(Point(13387030, 4365200 )); +pts.push_back(Point(13386900, 4360620 )); pts.push_back(Point(13334170, 4307880 )); pts.push_back(Point(12532990, 4307880 )); pts.push_back(Point(12532990, 4357550 )); pts.push_back(Point(12529993, 4391760 )); pts.push_back(Point(12506397, 4448726 )); pts.push_back(Point(12462796, 4492327 )); +pts.push_back(Point(12405830, 4515923 )); pts.push_back(Point(12344169, 4515923 )); pts.push_back(Point(12287203, 4492327 )); pts.push_back(Point(12243602, 4448726 )); pts.push_back(Point(12220006, 4391760 )); pts.push_back(Point(12217970, 4378710 )); pts.push_back(Point(12216810, 4368500 )); +pts.push_back(Point(11363190, 4368500 )); pts.push_back(Point(11362030, 4378710 )); pts.push_back(Point(11359983, 4391828 )); pts.push_back(Point(11336388, 4448791 )); pts.push_back(Point(11292791, 4492388 )); pts.push_back(Point(11235828, 4515983 )); pts.push_back(Point(11174171, 4515983 )); pts.push_back(Point(11117208, 4492388 )); +pts.push_back(Point(11096270, 4475670 )); pts.push_back(Point(11092940, 4472520 )); pts.push_back(Point(11057750, 4472520 )); pts.push_back(Point(11053272, 4472961 )); pts.push_back(Point(11044981, 4476396 )); pts.push_back(Point(11038636, 4482741 )); pts.push_back(Point(11035201, 4491032 )); +pts.push_back(Point(11035201, 4500007 )); pts.push_back(Point(11038636, 4508298 )); pts.push_back(Point(11041490, 4511780 )); pts.push_back(Point(11573490, 5043780 )); pts.push_back(Point(15655490, 5043780 )); pts.push_back(Point(15904620, 5292900 )); +pts.push_back(Point(15909200, 5293030 )); pts.push_back(Point(15935830, 5296006 )); pts.push_back(Point(15992796, 5319602 )); pts.push_back(Point(16036397, 5363203 )); pts.push_back(Point(16059993, 5420169 )); pts.push_back(Point(16059993, 5481830 )); pts.push_back(Point(16036397, 5538796 )); +pts.push_back(Point(15992796, 5582397 )); pts.push_back(Point(15935830, 5605993 )); pts.push_back(Point(15874169, 5605993 )); pts.push_back(Point(15817203, 5582397 )); pts.push_back(Point(15773602, 5538796 )); pts.push_back(Point(15750006, 5481830 )); pts.push_back(Point(15747030, 5455200 )); +pts.push_back(Point(15746900, 5450620 )); pts.push_back(Point(15563110, 5266820 )); pts.push_back(Point(14857380, 5266820 )); pts.push_back(Point(14857380, 5382430 )); pts.push_back(Point(14869993, 5420179 )); pts.push_back(Point(14869993, 5481840 )); pts.push_back(Point(14846397, 5538806 )); pts.push_back(Point(14802796, 5582407 )); +pts.push_back(Point(14745830, 5606003 )); pts.push_back(Point(14684169, 5606003 )); pts.push_back(Point(14627203, 5582407 )); pts.push_back(Point(14583602, 5538806 )); pts.push_back(Point(14560006, 5481840 )); pts.push_back(Point(14557030, 5455200 )); pts.push_back(Point(14556900, 5450620 )); pts.push_back(Point(14444750, 5338460 )); +pts.push_back(Point(13702890, 5338460 )); pts.push_back(Point(13702890, 5364400 )); pts.push_back(Point(13699993, 5401800 )); pts.push_back(Point(13676397, 5458766 )); pts.push_back(Point(13632796, 5502367 )); pts.push_back(Point(13575830, 5525963 )); pts.push_back(Point(13514169, 5525963 )); pts.push_back(Point(13457203, 5502367 )); +pts.push_back(Point(13413602, 5458766 )); pts.push_back(Point(13390006, 5401800 )); pts.push_back(Point(13389230, 5397620 )); pts.push_back(Point(13387590, 5388060 )); pts.push_back(Point(12532960, 5388060 )); pts.push_back(Point(12532960, 5446220 )); pts.push_back(Point(12529993, 5481820 )); +pts.push_back(Point(12506397, 5538786 )); pts.push_back(Point(12462796, 5582387 )); pts.push_back(Point(12405830, 5605983 )); pts.push_back(Point(12344169, 5605983 )); pts.push_back(Point(12287203, 5582387 )); pts.push_back(Point(12266270, 5565670 )); pts.push_back(Point(12262940, 5562520 )); pts.push_back(Point(11737750, 5562520 )); +pts.push_back(Point(11733272, 5562961 )); pts.push_back(Point(11724981, 5566396 )); pts.push_back(Point(11718636, 5572741 )); pts.push_back(Point(11715201, 5581032 )); pts.push_back(Point(11715201, 5590007 )); pts.push_back(Point(11718636, 5598298 )); pts.push_back(Point(11721500, 5601780 )); +pts.push_back(Point(12287760, 6168050 )); pts.push_back(Point(15689760, 6168050 )); pts.push_back(Point(15904620, 6382900 )); pts.push_back(Point(15909200, 6383030 )); pts.push_back(Point(15935830, 6386006 )); pts.push_back(Point(15992796, 6409602 )); +pts.push_back(Point(16036397, 6453203 )); pts.push_back(Point(16059993, 6510169 )); pts.push_back(Point(16059993, 6571830 )); pts.push_back(Point(16036397, 6628796 )); pts.push_back(Point(15992796, 6672397 )); pts.push_back(Point(15935830, 6695993 )); pts.push_back(Point(15874169, 6695993 )); +pts.push_back(Point(15817203, 6672397 )); pts.push_back(Point(15773602, 6628796 )); pts.push_back(Point(15750006, 6571830 )); pts.push_back(Point(15747030, 6545200 )); pts.push_back(Point(15746900, 6540620 )); pts.push_back(Point(15597380, 6391090 )); pts.push_back(Point(14858060, 6391090 )); +pts.push_back(Point(14858060, 6473860 )); pts.push_back(Point(14869993, 6510179 )); pts.push_back(Point(14869993, 6571840 )); pts.push_back(Point(14846397, 6628806 )); pts.push_back(Point(14802796, 6672407 )); pts.push_back(Point(14745830, 6696003 )); pts.push_back(Point(14684169, 6696003 )); +pts.push_back(Point(14627203, 6672407 )); pts.push_back(Point(14583602, 6628806 )); pts.push_back(Point(14560006, 6571840 )); pts.push_back(Point(14557030, 6545200 )); pts.push_back(Point(14556900, 6540620 )); pts.push_back(Point(14479020, 6462730 )); +pts.push_back(Point(13702990, 6462730 )); pts.push_back(Point(13702990, 6537170 )); pts.push_back(Point(13700003, 6571840 )); pts.push_back(Point(13676407, 6628806 )); pts.push_back(Point(13632806, 6672407 )); pts.push_back(Point(13575840, 6696003 )); +pts.push_back(Point(13514179, 6696003 )); pts.push_back(Point(13457213, 6672407 )); pts.push_back(Point(13413612, 6628806 )); pts.push_back(Point(13390016, 6571840 )); pts.push_back(Point(13387040, 6545550 )); pts.push_back(Point(13386710, 6534380 )); +pts.push_back(Point(12533290, 6534380 )); pts.push_back(Point(12532960, 6545550 )); pts.push_back(Point(12529983, 6571828 )); pts.push_back(Point(12506388, 6628791 )); pts.push_back(Point(12462791, 6672388 )); pts.push_back(Point(12405828, 6695983 )); +pts.push_back(Point(12344171, 6695983 )); pts.push_back(Point(12287208, 6672388 )); pts.push_back(Point(12266270, 6655670 )); + poly.set(pts.begin(), pts.end()); + si.insert(poly, 444); + result.clear(); + si.merge(result); + si.verify1(); + print(stdcout, si.pmd) << "\n"; + if(!result.empty()) { + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + std::vector outpts; + for(typename polygon_set_data::iterator_type itr = psd.begin(); + itr != psd.end(); ++itr) { + outpts.push_back((*itr).first.first); + outpts.push_back((*itr).first.second); + } + polygon_sort(outpts.begin(), outpts.end()); + for(std::size_t i = 0; i < outpts.size(); i+=2) { + if(outpts[i] != outpts[i+1]) { + stdcout << "Polygon set not a closed figure\n"; + stdcout << i << "\n"; + stdcout << outpts[i] << " " << outpts[i+1] << "\n"; + return 0; + } + } + polys.clear(); + psd.get(polys); + if(polys.size() == 0) { + stdcout << "fail merge 10\n"; + return false; + } + stdcout << (polys[0]) << "\n"; + } + for(unsigned int i = 0; i < 10; ++i) { + stdcout << "random case # " << i << "\n"; + si.clear(); + pts.clear(); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + polygon_data poly1; + poly1.set(pts.begin(), pts.end()); + stdcout << poly1 << "\n"; + si.insert(poly1, 444); + pts.clear(); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + polygon_data poly2; + poly2.set(pts.begin(), pts.end()); + stdcout << poly2 << "\n"; + si.insert(poly2, 444); + result.clear(); + si.merge(result); + print(stdcout, si.pmd) << "\n"; + if(!result.empty()) { + psd = (*(result.begin())).second; + stdcout << psd << "\n"; + polys.clear(); + psd.get(polys); + if(polys.size() == 0) { + si.clear(); + si.insert(poly1, 333); + result.clear(); + si.merge(result); + psd = (*(result.begin())).second; + std::vector > polys1; + psd.get(polys1); + si.clear(); + si.insert(poly2, 333); + result.clear(); + si.merge(result); + psd = (*(result.begin())).second; + std::vector > polys2; + psd.get(polys2); + if(!polys1.empty() || !polys2.empty()) { + stdcout << "fail random merge " << i << "\n"; + return false; + } + } + } + if(!polys.empty()) + stdcout << polys.size() << ": " << (polys[0]) << "\n"; + } + return true; + } + + template + static inline bool check_rectangle_trio(rectangle_data rect1, rectangle_data rect2, rectangle_data rect3, stream_type& stdcout) { + property_merge si; + std::map, polygon_set_data > result; + std::vector > polys; + property_merge_90 si90; + std::map, polygon_90_set_data > result90; + std::vector > polys90; + si.insert(rect1, 111); + si90.insert(rect1, 111); + stdcout << rect1 << "\n"; + si.insert(rect2, 222); + si90.insert(rect2, 222); + stdcout << rect2 << "\n"; + si.insert(rect3, 333); + si90.insert(rect3, 333); + stdcout << rect3 << "\n"; + si.merge(result); + si90.merge(result90); + if(result.size() != result90.size()) { + stdcout << "merge failed with size mismatch\n"; + return 0; + } + typename std::map, polygon_90_set_data >::iterator itr90 = result90.begin(); + for(typename std::map, polygon_set_data >::iterator itr = result.begin(); + itr != result.end(); ++itr) { + for(typename std::set::const_iterator set_itr = (*itr).first.begin(); + set_itr != (*itr).first.end(); ++set_itr) { + stdcout << (*set_itr) << " "; + } stdcout << ") \n"; + polygon_set_data psd = (*itr).second; + polygon_90_set_data psd90 = (*itr90).second; + polys.clear(); + polys90.clear(); + psd.get(polys); + psd90.get(polys90); + if(polys.size() != polys90.size()) { + stdcout << "merge failed with polygon count mismatch\n"; + stdcout << psd << "\n"; + for(std::size_t j = 0; j < polys.size(); ++j) { + stdcout << polys[j] << "\n"; + } + stdcout << "reference\n"; + for(std::size_t j = 0; j < polys90.size(); ++j) { + stdcout << polys90[j] << "\n"; + } + return 0; + } + bool failed = false; + for(std::size_t j = 0; j < polys.size(); ++j) { + stdcout << polys[j] << "\n"; + stdcout << polys90[j] << "\n"; +#ifdef BOOST_POLYGON_ICC +#pragma warning (push) +#pragma warning (disable:1572) +#endif + if(area(polys[j]) != area(polys90[j])) { +#ifdef BOOST_POLYGON_ICC +#pragma warning (pop) +#endif + stdcout << "merge failed with area mismatch\n"; + failed = true; + } + } + if(failed) return 0; + ++itr90; + } + return true; + } + + template + static inline bool test_manhattan_intersection(stream_type& stdcout) { + rectangle_data rect1, rect2, rect3; + set_points(rect1, (Point(-1, 2)), (Point(1, 4))); + set_points(rect2, (Point(-1, 2)), (Point(2, 3))); + set_points(rect3, (Point(-3, 0)), (Point(4, 2))); + if(!check_rectangle_trio(rect1, rect2, rect3, stdcout)) { + return false; + } + for(unsigned int i = 0; i < 100; ++i) { + property_merge si; + std::map, polygon_set_data > result; + std::vector > polys; + property_merge_90 si90; + std::map, polygon_90_set_data > result90; + std::vector > polys90; + stdcout << "random case # " << i << "\n"; + set_points(rect1, (Point(rand()%9-4, rand()%9-4)), (Point(rand()%9-4, rand()%9-4))); + set_points(rect2, (Point(rand()%9-4, rand()%9-4)), (Point(rand()%9-4, rand()%9-4))); + set_points(rect3, (Point(rand()%9-4, rand()%9-4)), (Point(rand()%9-4, rand()%9-4))); + if(!check_rectangle_trio(rect1, rect2, rect3, stdcout)) { + return false; + } + } + return true; + } + + template + static inline bool test_intersection(stream_type& stdcout) { + property_merge si; + rectangle_data rect; + xl(rect, 0); + yl(rect, 10); + xh(rect, 30); + yh(rect, 20); + si.insert(rect, 333); + xl(rect, 10); + yl(rect, 0); + xh(rect, 20); + yh(rect, 30); + si.insert(rect, 444); + xl(rect, 15); + yl(rect, 0); + xh(rect, 25); + yh(rect, 30); + si.insert(rect, 555); + std::map, polygon_set_data > result; + si.merge(result); + print(stdcout, si.pmd) << "\n"; + for(typename std::map, polygon_set_data >::iterator itr = result.begin(); + itr != result.end(); ++itr) { + stdcout << "( "; + for(typename std::set::const_iterator set_itr = (*itr).first.begin(); + set_itr != (*itr).first.end(); ++set_itr) { + stdcout << (*set_itr) << " "; + } stdcout << ") \n"; + polygon_set_data psd = (*itr).second; + stdcout << psd << "\n"; + std::vector > polys; + psd.get(polys); + for(std::size_t i = 0; i < polys.size(); ++i) { + stdcout << polys[i] << "\n"; + } + } + std::vector pts; + std::vector > polys; + for(unsigned int i = 0; i < 10; ++i) { + property_merge si2; + stdcout << "random case # " << i << "\n"; + si.clear(); + pts.clear(); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + polygon_data poly1; + poly1.set(pts.begin(), pts.end()); + stdcout << poly1 << "\n"; + si.insert(poly1, 444); + si2.insert(poly1, 333); + pts.clear(); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + polygon_data poly2; + poly2.set(pts.begin(), pts.end()); + stdcout << poly2 << "\n"; + si.insert(poly2, 444); + si2.insert(poly2, 444); + pts.clear(); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + pts.push_back(Point(rand()%9-4, rand()%9-4)); + polygon_data poly3; + poly3.set(pts.begin(), pts.end()); + stdcout << poly3 << "\n"; + si.insert(poly3, 444); + si2.insert(poly3, 555); + result.clear(); + std::map, polygon_set_data > result2; + si.merge(result); + si2.merge(result2); + stdcout << "merged result\n"; + for(typename std::map, polygon_set_data >::iterator itr = result.begin(); + itr != result.end(); ++itr) { + stdcout << "( "; + for(typename std::set::const_iterator set_itr = (*itr).first.begin(); + set_itr != (*itr).first.end(); ++set_itr) { + stdcout << (*set_itr) << " "; + } stdcout << ") \n"; + polygon_set_data psd = (*itr).second; + stdcout << psd << "\n"; + std::vector > polys2; + psd.get(polys2); + for(std::size_t ii = 0; ii < polys2.size(); ++ii) { + stdcout << polys2[ii] << "\n"; + } + } + stdcout << "intersected pmd\n"; + print(stdcout, si2.pmd) << "\n"; + stdcout << "intersected result\n"; + for(typename std::map, polygon_set_data >::iterator itr = result2.begin(); + itr != result2.end(); ++itr) { + stdcout << "( "; + for(typename std::set::const_iterator set_itr = (*itr).first.begin(); + set_itr != (*itr).first.end(); ++set_itr) { + stdcout << (*set_itr) << " "; + } stdcout << ") \n"; + polygon_set_data psd = (*itr).second; + stdcout << psd << "\n"; + std::vector > polys2; + psd.get(polys2); + for(std::size_t ii = 0; ii < polys2.size(); ++ii) { + stdcout << polys2[ii] << "\n"; + } + } + si.clear(); + for(typename std::map, polygon_set_data >::iterator itr = result2.begin(); + itr != result2.end(); ++itr) { + polys.clear(); + (*itr).second.get(polys); + for(std::size_t j = 0; j < polys.size(); ++j) { + si.insert(polys[j], 444); + } + } + result2.clear(); + si.merge(result2); + stdcout << "remerged result\n"; + for(typename std::map, polygon_set_data >::iterator itr = result2.begin(); + itr != result2.end(); ++itr) { + stdcout << "( "; + for(typename std::set::const_iterator set_itr = (*itr).first.begin(); + set_itr != (*itr).first.end(); ++set_itr) { + stdcout << (*set_itr) << " "; + } stdcout << ") \n"; + polygon_set_data psd = (*itr).second; + stdcout << psd << "\n"; + std::vector > polys2; + psd.get(polys2); + for(std::size_t ii = 0; ii < polys2.size(); ++ii) { + stdcout << polys2[ii] << "\n"; + } + } + std::vector > polys2; + polys.clear(); + (*(result.begin())).second.get(polys); + (*(result2.begin())).second.get(polys2); + if(!(polys == polys2)) { + stdcout << "failed intersection check # " << i << "\n"; + return false; + } + } + return true; + } + }; + + template + class arbitrary_boolean_op : public scanline_base { + private: + + typedef int property_type; + typedef typename scanline_base::Point Point; + + //the first point is the vertex and and second point establishes the slope of an edge eminating from the vertex + //typedef std::pair half_edge; + typedef typename scanline_base::half_edge half_edge; + + //scanline comparator functor + typedef typename scanline_base::less_half_edge less_half_edge; + typedef typename scanline_base::less_point less_point; + + //this data structure assocates a property and count to a half edge + typedef std::pair > vertex_property; + //this data type stores the combination of many half edges + typedef std::vector property_merge_data; + + //this is the data type used internally to store the combination of property counts at a given location + typedef std::vector > property_map; + //this data type is used internally to store the combined property data for a given half edge + typedef std::pair vertex_data; + + property_merge_data pmd; + typename scanline_base::evalAtXforYPack evalAtXforYPack_; + + template + class less_vertex_data { + typename scanline_base::evalAtXforYPack* pack_; + public: + less_vertex_data() : pack_() {} + less_vertex_data(typename scanline_base::evalAtXforYPack* pack) : pack_(pack) {} + bool operator()(const vertex_data_type& lvalue, const vertex_data_type& rvalue) const { + less_point lp; + if(lp(lvalue.first.first, rvalue.first.first)) return true; + if(lp(rvalue.first.first, lvalue.first.first)) return false; + Unit x = lvalue.first.first.get(HORIZONTAL); + int just_before_ = 0; + less_half_edge lhe(&x, &just_before_, pack_); + return lhe(lvalue.first, rvalue.first); + } + }; + + template + class boolean_output_functor { + public: + boolean_output_functor() {} + void operator()(result_type& result, const half_edge& edge, const key_type& left, const key_type& right) { + typename std::pair elem; + elem.first = edge; + elem.second = 1; + if(edge.second < edge.first) elem.second *= -1; + if(scanline_base::is_vertical(edge)) elem.second *= -1; +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op_type == 0) { //OR + if(!left.empty() && right.empty()) { + result.insert_clean(elem); + } else if(!right.empty() && left.empty()) { + elem.second *= -1; + result.insert_clean(elem); + } + } else if(op_type == 1) { //AND + if(left.size() == 2 && right.size() != 2) { + result.insert_clean(elem); + } else if(right.size() == 2 && left.size() != 2) { + elem.second *= -1; + result.insert_clean(elem); + } + } else if(op_type == 2) { //XOR + if(left.size() == 1 && right.size() != 1) { + result.insert_clean(elem); + } else if(right.size() == 1 && left.size() != 1) { + elem.second *= -1; + result.insert_clean(elem); + } + } else { //SUBTRACT + if(left.size() == 1) { + if((*(left.begin())) == 0) { + result.insert_clean(elem); + } + } +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + if(right.size() == 1) { + if((*(right.begin())) == 0) { + elem.second *= -1; + result.insert_clean(elem); + } + } + } + } + }; + + inline void sort_property_merge_data() { + less_vertex_data lvd(&evalAtXforYPack_); + polygon_sort(pmd.begin(), pmd.end(), lvd); + } + public: + inline arbitrary_boolean_op() : pmd(), evalAtXforYPack_() {} + inline arbitrary_boolean_op(const arbitrary_boolean_op& pm) : pmd(pm.pmd), evalAtXforYPack_(pm.evalAtXforYPack_) {} + inline arbitrary_boolean_op& operator=(const arbitrary_boolean_op& pm) { pmd = pm.pmd; return *this; } + + enum BOOLEAN_OP_TYPE { + BOOLEAN_OR = 0, + BOOLEAN_AND = 1, + BOOLEAN_XOR = 2, + BOOLEAN_NOT = 3 + }; + template + inline void execute(result_type& result, iT1 b1, iT1 e1, iT2 b2, iT2 e2, int op) { + //intersect data + insert(b1, e1, 0); + insert(b2, e2, 1); + property_merge_data tmp_pmd; + //#define BOOST_POLYGON_DEBUG_FILE +#ifdef BOOST_POLYGON_DEBUG_FILE + std::fstream debug_file; + debug_file.open("gtl_debug.txt", std::ios::out); + property_merge >::print(debug_file, pmd); + debug_file.close(); +#endif + if(pmd.empty()) + return; + line_intersection::validate_scan(tmp_pmd, pmd.begin(), pmd.end()); + pmd.swap(tmp_pmd); + sort_property_merge_data(); + scanline > sl; + if(op == BOOLEAN_OR) { + boolean_output_functor, 0> bof; + sl.scan(result, bof, pmd.begin(), pmd.end()); + } else if(op == BOOLEAN_AND) { + boolean_output_functor, 1> bof; + sl.scan(result, bof, pmd.begin(), pmd.end()); + } else if(op == BOOLEAN_XOR) { + boolean_output_functor, 2> bof; + sl.scan(result, bof, pmd.begin(), pmd.end()); + } else if(op == BOOLEAN_NOT) { + boolean_output_functor, 3> bof; + sl.scan(result, bof, pmd.begin(), pmd.end()); + } + } + + inline void clear() {*this = arbitrary_boolean_op();} + + private: + template + void insert(iT b, iT e, int id) { + for(; + b != e; ++b) { + pmd.push_back(vertex_property(half_edge((*b).first.first, (*b).first.second), + std::pair(id, (*b).second))); + } + } + + }; + + template + bool test_arbitrary_boolean_op(stream_type& stdcout) { + polygon_set_data psd; + rectangle_data rect; + set_points(rect, point_data(0, 0), point_data(10, 10)); + psd.insert(rect); + polygon_set_data psd2; + set_points(rect, point_data(5, 5), point_data(15, 15)); + psd2.insert(rect); + std::vector > pv; + pv.clear(); + arbitrary_boolean_op abo; + polygon_set_data psd3; + abo.execute(psd3, psd.begin(), psd.end(), psd2.begin(), psd2.end(), arbitrary_boolean_op::BOOLEAN_OR); + psd3.get(pv); + for(std::size_t i = 0; i < pv.size(); ++i) { + stdcout << pv[i] << "\n"; + } + pv.clear(); + abo.clear(); + psd3.clear(); + abo.execute(psd3, psd.begin(), psd.end(), psd2.begin(), psd2.end(), arbitrary_boolean_op::BOOLEAN_AND); + psd3.get(pv); + for(std::size_t i = 0; i < pv.size(); ++i) { + stdcout << pv[i] << "\n"; + } + pv.clear(); + abo.clear(); + psd3.clear(); + abo.execute(psd3, psd.begin(), psd.end(), psd2.begin(), psd2.end(), arbitrary_boolean_op::BOOLEAN_XOR); + psd3.get(pv); + for(std::size_t i = 0; i < pv.size(); ++i) { + stdcout << pv[i] << "\n"; + } + pv.clear(); + abo.clear(); + psd3.clear(); + abo.execute(psd3, psd.begin(), psd.end(), psd2.begin(), psd2.end(), arbitrary_boolean_op::BOOLEAN_NOT); + psd3.get(pv); + for(std::size_t i = 0; i < pv.size(); ++i) { + stdcout << pv[i] << "\n"; + } + return true; + } + + + + + + + + + + + + + + + template + class arbitrary_connectivity_extraction : public scanline_base { + private: + + typedef typename scanline_base::Point Point; + + //the first point is the vertex and and second point establishes the slope of an edge eminating from the vertex + //typedef std::pair half_edge; + typedef typename scanline_base::half_edge half_edge; + + //scanline comparator functor + typedef typename scanline_base::less_half_edge less_half_edge; + typedef typename scanline_base::less_point less_point; + + //this data structure assocates a property and count to a half edge + typedef std::pair > vertex_property; + //this data type stores the combination of many half edges + typedef std::vector property_merge_data; + + //this is the data type used internally to store the combination of property counts at a given location + typedef std::vector > property_map; + //this data type is used internally to store the combined property data for a given half edge + typedef std::pair vertex_data; + + property_merge_data pmd; + typename scanline_base::evalAtXforYPack evalAtXforYPack_; + + template + class less_vertex_data { + typename scanline_base::evalAtXforYPack* pack_; + public: + less_vertex_data() : pack_() {} + less_vertex_data(typename scanline_base::evalAtXforYPack* pack) : pack_(pack) {} + bool operator()(const vertex_data_type& lvalue, const vertex_data_type& rvalue) const { + less_point lp; + if(lp(lvalue.first.first, rvalue.first.first)) return true; + if(lp(rvalue.first.first, lvalue.first.first)) return false; + Unit x = lvalue.first.first.get(HORIZONTAL); + int just_before_ = 0; + less_half_edge lhe(&x, &just_before_, pack_); + return lhe(lvalue.first, rvalue.first); + } + }; + + + template + static void process_previous_x(cT& output) { + std::map, std::set >& y_prop_map = output.first.second; + if(y_prop_map.empty()) return; + Unit x = output.first.first; + for(typename std::map, std::set >::iterator itr = + y_prop_map.begin(); itr != y_prop_map.end(); ++itr) { + if((*itr).first.x() < x) { + y_prop_map.erase(y_prop_map.begin(), itr); + continue; + } + for(typename std::set::iterator inner_itr = itr->second.begin(); + inner_itr != itr->second.end(); ++inner_itr) { + std::set& output_edges = (*(output.second))[*inner_itr]; + typename std::set::iterator inner_inner_itr = inner_itr; + ++inner_inner_itr; + for( ; inner_inner_itr != itr->second.end(); ++inner_inner_itr) { + output_edges.insert(output_edges.end(), *inner_inner_itr); + std::set& output_edges_2 = (*(output.second))[*inner_inner_itr]; + output_edges_2.insert(output_edges_2.end(), *inner_itr); + } + } + } + } + + template + class connectivity_extraction_output_functor { + public: + connectivity_extraction_output_functor() {} + void operator()(result_type& result, const half_edge& edge, const key_type& left, const key_type& right) { + Unit& x = result.first.first; + std::map, std::set >& y_prop_map = result.first.second; + point_data pt = edge.first; + if(pt.x() != x) process_previous_x(result); + x = pt.x(); + std::set& output_set = y_prop_map[pt]; + { + for(typename key_type::const_iterator itr1 = + left.begin(); itr1 != left.end(); ++itr1) { + output_set.insert(output_set.end(), *itr1); + } + for(typename key_type::const_iterator itr2 = + right.begin(); itr2 != right.end(); ++itr2) { + output_set.insert(output_set.end(), *itr2); + } + } + std::set& output_set2 = y_prop_map[edge.second]; + for(typename key_type::const_iterator itr1 = + left.begin(); itr1 != left.end(); ++itr1) { + output_set2.insert(output_set2.end(), *itr1); + } + for(typename key_type::const_iterator itr2 = + right.begin(); itr2 != right.end(); ++itr2) { + output_set2.insert(output_set2.end(), *itr2); + } + } + }; + + inline void sort_property_merge_data() { + less_vertex_data lvd(&evalAtXforYPack_); + polygon_sort(pmd.begin(), pmd.end(), lvd); + } + public: + inline arbitrary_connectivity_extraction() : pmd(), evalAtXforYPack_() {} + inline arbitrary_connectivity_extraction + (const arbitrary_connectivity_extraction& pm) : pmd(pm.pmd), evalAtXforYPack_(pm.evalAtXforYPack_) {} + inline arbitrary_connectivity_extraction& operator= + (const arbitrary_connectivity_extraction& pm) { pmd = pm.pmd; return *this; } + + template + inline void execute(result_type& result) { + //intersect data + property_merge_data tmp_pmd; + line_intersection::validate_scan(tmp_pmd, pmd.begin(), pmd.end()); + pmd.swap(tmp_pmd); + sort_property_merge_data(); + scanline > sl; + std::pair, std::set > >, + result_type*> output + (std::make_pair(std::make_pair((std::numeric_limits::max)(), + std::map, + std::set >()), &result)); + connectivity_extraction_output_functor, std::set > >, result_type*>, + std::vector > ceof; + sl.scan(output, ceof, pmd.begin(), pmd.end()); + process_previous_x(output); + } + + inline void clear() {*this = arbitrary_connectivity_extraction();} + + template + void populateTouchSetData(iT begin, iT end, + property_type property) { + for( ; begin != end; ++begin) { + pmd.push_back(vertex_property(half_edge((*begin).first.first, (*begin).first.second), + std::pair(property, (*begin).second))); + } + } + + }; + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_ctypes.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_ctypes.hpp new file mode 100644 index 0000000..078d935 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_ctypes.hpp @@ -0,0 +1,643 @@ +// Boost.Polygon library detail/voronoi_ctypes.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_DETAIL_VORONOI_CTYPES +#define BOOST_POLYGON_DETAIL_VORONOI_CTYPES + +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace polygon { +namespace detail { + +typedef boost::int32_t int32; +typedef boost::int64_t int64; +typedef boost::uint32_t uint32; +typedef boost::uint64_t uint64; +typedef double fpt64; + +// If two floating-point numbers in the same format are ordered (x < y), +// then they are ordered the same way when their bits are reinterpreted as +// sign-magnitude integers. Values are considered to be almost equal if +// their integer bits reinterpretations differ in not more than maxUlps units. +template +struct ulp_comparison; + +template <> +struct ulp_comparison { + enum Result { + LESS = -1, + EQUAL = 0, + MORE = 1 + }; + + Result operator()(fpt64 a, fpt64 b, unsigned int maxUlps) const { + uint64 ll_a, ll_b; + + // Reinterpret double bits as 64-bit signed integer. + std::memcpy(&ll_a, &a, sizeof(fpt64)); + std::memcpy(&ll_b, &b, sizeof(fpt64)); + + // Positive 0.0 is integer zero. Negative 0.0 is 0x8000000000000000. + // Map negative zero to an integer zero representation - making it + // identical to positive zero - the smallest negative number is + // represented by negative one, and downwards from there. + if (ll_a < 0x8000000000000000ULL) + ll_a = 0x8000000000000000ULL - ll_a; + if (ll_b < 0x8000000000000000ULL) + ll_b = 0x8000000000000000ULL - ll_b; + + // Compare 64-bit signed integer representations of input values. + // Difference in 1 Ulp is equivalent to a relative error of between + // 1/4,000,000,000,000,000 and 1/8,000,000,000,000,000. + if (ll_a > ll_b) + return (ll_a - ll_b <= maxUlps) ? EQUAL : LESS; + return (ll_b - ll_a <= maxUlps) ? EQUAL : MORE; + } +}; + +template +struct extened_exponent_fpt_traits; + +template <> +struct extened_exponent_fpt_traits { + public: + typedef int exp_type; + enum { + MAX_SIGNIFICANT_EXP_DIF = 54 + }; +}; + +// Floating point type wrapper. Allows to extend exponent boundaries to the +// integer type range. This class does not handle division by zero, subnormal +// numbers or NaNs. +template > +class extended_exponent_fpt { + public: + typedef _fpt fpt_type; + typedef typename _traits::exp_type exp_type; + + explicit extended_exponent_fpt(fpt_type val) { + val_ = std::frexp(val, &exp_); + } + + extended_exponent_fpt(fpt_type val, exp_type exp) { + val_ = std::frexp(val, &exp_); + exp_ += exp; + } + + bool is_pos() const { + return val_ > 0; + } + + bool is_neg() const { + return val_ < 0; + } + + bool is_zero() const { + return val_ == 0; + } + + extended_exponent_fpt operator-() const { + return extended_exponent_fpt(-val_, exp_); + } + + extended_exponent_fpt operator+(const extended_exponent_fpt& that) const { + if (this->val_ == 0.0 || + that.exp_ > this->exp_ + _traits::MAX_SIGNIFICANT_EXP_DIF) { + return that; + } + if (that.val_ == 0.0 || + this->exp_ > that.exp_ + _traits::MAX_SIGNIFICANT_EXP_DIF) { + return *this; + } + if (this->exp_ >= that.exp_) { + exp_type exp_dif = this->exp_ - that.exp_; + fpt_type val = std::ldexp(this->val_, exp_dif) + that.val_; + return extended_exponent_fpt(val, that.exp_); + } else { + exp_type exp_dif = that.exp_ - this->exp_; + fpt_type val = std::ldexp(that.val_, exp_dif) + this->val_; + return extended_exponent_fpt(val, this->exp_); + } + } + + extended_exponent_fpt operator-(const extended_exponent_fpt& that) const { + if (this->val_ == 0.0 || + that.exp_ > this->exp_ + _traits::MAX_SIGNIFICANT_EXP_DIF) { + return extended_exponent_fpt(-that.val_, that.exp_); + } + if (that.val_ == 0.0 || + this->exp_ > that.exp_ + _traits::MAX_SIGNIFICANT_EXP_DIF) { + return *this; + } + if (this->exp_ >= that.exp_) { + exp_type exp_dif = this->exp_ - that.exp_; + fpt_type val = std::ldexp(this->val_, exp_dif) - that.val_; + return extended_exponent_fpt(val, that.exp_); + } else { + exp_type exp_dif = that.exp_ - this->exp_; + fpt_type val = std::ldexp(-that.val_, exp_dif) + this->val_; + return extended_exponent_fpt(val, this->exp_); + } + } + + extended_exponent_fpt operator*(const extended_exponent_fpt& that) const { + fpt_type val = this->val_ * that.val_; + exp_type exp = this->exp_ + that.exp_; + return extended_exponent_fpt(val, exp); + } + + extended_exponent_fpt operator/(const extended_exponent_fpt& that) const { + fpt_type val = this->val_ / that.val_; + exp_type exp = this->exp_ - that.exp_; + return extended_exponent_fpt(val, exp); + } + + extended_exponent_fpt& operator+=(const extended_exponent_fpt& that) { + return *this = *this + that; + } + + extended_exponent_fpt& operator-=(const extended_exponent_fpt& that) { + return *this = *this - that; + } + + extended_exponent_fpt& operator*=(const extended_exponent_fpt& that) { + return *this = *this * that; + } + + extended_exponent_fpt& operator/=(const extended_exponent_fpt& that) { + return *this = *this / that; + } + + extended_exponent_fpt sqrt() const { + fpt_type val = val_; + exp_type exp = exp_; + if (exp & 1) { + val *= 2.0; + --exp; + } + return extended_exponent_fpt(std::sqrt(val), exp >> 1); + } + + fpt_type d() const { + return std::ldexp(val_, exp_); + } + + private: + fpt_type val_; + exp_type exp_; +}; +typedef extended_exponent_fpt efpt64; + +template +extended_exponent_fpt<_fpt> get_sqrt(const extended_exponent_fpt<_fpt>& that) { + return that.sqrt(); +} + +template +bool is_pos(const extended_exponent_fpt<_fpt>& that) { + return that.is_pos(); +} + +template +bool is_neg(const extended_exponent_fpt<_fpt>& that) { + return that.is_neg(); +} + +template +bool is_zero(const extended_exponent_fpt<_fpt>& that) { + return that.is_zero(); +} + +// Very efficient stack allocated big integer class. +// Supports next set of arithmetic operations: +, -, *. +template +class extended_int { + public: + extended_int() {} + + extended_int(int32 that) { + if (that > 0) { + this->chunks_[0] = that; + this->count_ = 1; + } else if (that < 0) { + this->chunks_[0] = -that; + this->count_ = -1; + } else { + this->count_ = 0; + } + } + + extended_int(int64 that) { + if (that > 0) { + this->chunks_[0] = static_cast(that); + this->chunks_[1] = that >> 32; + this->count_ = this->chunks_[1] ? 2 : 1; + } else if (that < 0) { + that = -that; + this->chunks_[0] = static_cast(that); + this->chunks_[1] = that >> 32; + this->count_ = this->chunks_[1] ? -2 : -1; + } else { + this->count_ = 0; + } + } + + extended_int(const std::vector& chunks, bool plus = true) { + this->count_ = static_cast((std::min)(N, chunks.size())); + for (int i = 0; i < this->count_; ++i) + this->chunks_[i] = chunks[chunks.size() - i - 1]; + if (!plus) + this->count_ = -this->count_; + } + + template + extended_int(const extended_int& that) { + this->count_ = that.count(); + std::memcpy(this->chunks_, that.chunks(), that.size() * sizeof(uint32)); + } + + extended_int& operator=(int32 that) { + if (that > 0) { + this->chunks_[0] = that; + this->count_ = 1; + } else if (that < 0) { + this->chunks_[0] = -that; + this->count_ = -1; + } else { + this->count_ = 0; + } + return *this; + } + + extended_int& operator=(int64 that) { + if (that > 0) { + this->chunks_[0] = static_cast(that); + this->chunks_[1] = that >> 32; + this->count_ = this->chunks_[1] ? 2 : 1; + } else if (that < 0) { + that = -that; + this->chunks_[0] = static_cast(that); + this->chunks_[1] = that >> 32; + this->count_ = this->chunks_[1] ? -2 : -1; + } else { + this->count_ = 0; + } + return *this; + } + + template + extended_int& operator=(const extended_int& that) { + this->count_ = that.count(); + std::memcpy(this->chunks_, that.chunks(), that.size() * sizeof(uint32)); + return *this; + } + + bool is_pos() const { + return this->count_ > 0; + } + + bool is_neg() const { + return this->count_ < 0; + } + + bool is_zero() const { + return this->count_ == 0; + } + + bool operator==(const extended_int& that) const { + if (this->count_ != that.count()) + return false; + for (std::size_t i = 0; i < this->size(); ++i) + if (this->chunks_[i] != that.chunks()[i]) + return false; + return true; + } + + bool operator!=(const extended_int& that) const { + return !(*this == that); + } + + bool operator<(const extended_int& that) const { + if (this->count_ != that.count()) + return this->count_ < that.count(); + std::size_t i = this->size(); + if (!i) + return false; + do { + --i; + if (this->chunks_[i] != that.chunks()[i]) + return (this->chunks_[i] < that.chunks()[i]) ^ (this->count_ < 0); + } while (i); + return false; + } + + bool operator>(const extended_int& that) const { + return that < *this; + } + + bool operator<=(const extended_int& that) const { + return !(that < *this); + } + + bool operator>=(const extended_int& that) const { + return !(*this < that); + } + + extended_int operator-() const { + extended_int ret_val = *this; + ret_val.neg(); + return ret_val; + } + + void neg() { + this->count_ = -this->count_; + } + + extended_int operator+(const extended_int& that) const { + extended_int ret_val; + ret_val.add(*this, that); + return ret_val; + } + + void add(const extended_int& e1, const extended_int& e2) { + if (!e1.count()) { + *this = e2; + return; + } + if (!e2.count()) { + *this = e1; + return; + } + if ((e1.count() > 0) ^ (e2.count() > 0)) { + dif(e1.chunks(), e1.size(), e2.chunks(), e2.size()); + } else { + add(e1.chunks(), e1.size(), e2.chunks(), e2.size()); + } + if (e1.count() < 0) + this->count_ = -this->count_; + } + + extended_int operator-(const extended_int& that) const { + extended_int ret_val; + ret_val.dif(*this, that); + return ret_val; + } + + void dif(const extended_int& e1, const extended_int& e2) { + if (!e1.count()) { + *this = e2; + this->count_ = -this->count_; + return; + } + if (!e2.count()) { + *this = e1; + return; + } + if ((e1.count() > 0) ^ (e2.count() > 0)) { + add(e1.chunks(), e1.size(), e2.chunks(), e2.size()); + } else { + dif(e1.chunks(), e1.size(), e2.chunks(), e2.size()); + } + if (e1.count() < 0) + this->count_ = -this->count_; + } + + extended_int operator*(int32 that) const { + extended_int temp(that); + return (*this) * temp; + } + + extended_int operator*(int64 that) const { + extended_int temp(that); + return (*this) * temp; + } + + extended_int operator*(const extended_int& that) const { + extended_int ret_val; + ret_val.mul(*this, that); + return ret_val; + } + + void mul(const extended_int& e1, const extended_int& e2) { + if (!e1.count() || !e2.count()) { + this->count_ = 0; + return; + } + mul(e1.chunks(), e1.size(), e2.chunks(), e2.size()); + if ((e1.count() > 0) ^ (e2.count() > 0)) + this->count_ = -this->count_; + } + + const uint32* chunks() const { + return chunks_; + } + + int32 count() const { + return count_; + } + + std::size_t size() const { + return (std::abs)(count_); + } + + std::pair p() const { + std::pair ret_val(0, 0); + std::size_t sz = this->size(); + if (!sz) { + return ret_val; + } else { + if (sz == 1) { + ret_val.first = static_cast(this->chunks_[0]); + } else if (sz == 2) { + ret_val.first = static_cast(this->chunks_[1]) * + static_cast(0x100000000LL) + + static_cast(this->chunks_[0]); + } else { + for (std::size_t i = 1; i <= 3; ++i) { + ret_val.first *= static_cast(0x100000000LL); + ret_val.first += static_cast(this->chunks_[sz - i]); + } + ret_val.second = static_cast((sz - 3) << 5); + } + } + if (this->count_ < 0) + ret_val.first = -ret_val.first; + return ret_val; + } + + fpt64 d() const { + std::pair p = this->p(); + return std::ldexp(p.first, p.second); + } + + private: + void add(const uint32* c1, std::size_t sz1, + const uint32* c2, std::size_t sz2) { + if (sz1 < sz2) { + add(c2, sz2, c1, sz1); + return; + } + this->count_ = static_cast(sz1); + uint64 temp = 0; + for (std::size_t i = 0; i < sz2; ++i) { + temp += static_cast(c1[i]) + static_cast(c2[i]); + this->chunks_[i] = static_cast(temp); + temp >>= 32; + } + for (std::size_t i = sz2; i < sz1; ++i) { + temp += static_cast(c1[i]); + this->chunks_[i] = static_cast(temp); + temp >>= 32; + } + if (temp && (this->count_ != N)) { + this->chunks_[this->count_] = static_cast(temp); + ++this->count_; + } + } + + void dif(const uint32* c1, std::size_t sz1, + const uint32* c2, std::size_t sz2, + bool rec = false) { + if (sz1 < sz2) { + dif(c2, sz2, c1, sz1, true); + this->count_ = -this->count_; + return; + } else if ((sz1 == sz2) && !rec) { + do { + --sz1; + if (c1[sz1] < c2[sz1]) { + ++sz1; + dif(c2, sz1, c1, sz1, true); + this->count_ = -this->count_; + return; + } else if (c1[sz1] > c2[sz1]) { + ++sz1; + break; + } + } while (sz1); + if (!sz1) { + this->count_ = 0; + return; + } + sz2 = sz1; + } + this->count_ = static_cast(sz1-1); + bool flag = false; + for (std::size_t i = 0; i < sz2; ++i) { + this->chunks_[i] = c1[i] - c2[i] - (flag?1:0); + flag = (c1[i] < c2[i]) || ((c1[i] == c2[i]) && flag); + } + for (std::size_t i = sz2; i < sz1; ++i) { + this->chunks_[i] = c1[i] - (flag?1:0); + flag = !c1[i] && flag; + } + if (this->chunks_[this->count_]) + ++this->count_; + } + + void mul(const uint32* c1, std::size_t sz1, + const uint32* c2, std::size_t sz2) { + uint64 cur = 0, nxt, tmp; + this->count_ = static_cast((std::min)(N, sz1 + sz2 - 1)); + for (std::size_t shift = 0; shift < static_cast(this->count_); + ++shift) { + nxt = 0; + for (std::size_t first = 0; first <= shift; ++first) { + if (first >= sz1) + break; + std::size_t second = shift - first; + if (second >= sz2) + continue; + tmp = static_cast(c1[first]) * static_cast(c2[second]); + cur += static_cast(tmp); + nxt += tmp >> 32; + } + this->chunks_[shift] = static_cast(cur); + cur = nxt + (cur >> 32); + } + if (cur && (this->count_ != N)) { + this->chunks_[this->count_] = static_cast(cur); + ++this->count_; + } + } + + uint32 chunks_[N]; + int32 count_; +}; + +template +bool is_pos(const extended_int& that) { + return that.count() > 0; +} + +template +bool is_neg(const extended_int& that) { + return that.count() < 0; +} + +template +bool is_zero(const extended_int& that) { + return !that.count(); +} + +struct type_converter_fpt { + template + fpt64 operator()(const T& that) const { + return static_cast(that); + } + + template + fpt64 operator()(const extended_int& that) const { + return that.d(); + } + + fpt64 operator()(const extended_exponent_fpt& that) const { + return that.d(); + } +}; + +struct type_converter_efpt { + template + extended_exponent_fpt operator()(const extended_int& that) const { + std::pair p = that.p(); + return extended_exponent_fpt(p.first, p.second); + } +}; + +// Voronoi coordinate type traits make it possible to extend algorithm +// input coordinate range to any user provided integer type and algorithm +// output coordinate range to any ieee-754 like floating point type. +template +struct voronoi_ctype_traits; + +template <> +struct voronoi_ctype_traits { + typedef int32 int_type; + typedef int64 int_x2_type; + typedef uint64 uint_x2_type; + typedef extended_int<64> big_int_type; + typedef fpt64 fpt_type; + typedef extended_exponent_fpt efpt_type; + typedef ulp_comparison ulp_cmp_type; + typedef type_converter_fpt to_fpt_converter_type; + typedef type_converter_efpt to_efpt_converter_type; +}; +} // detail +} // polygon +} // boost + +#endif // BOOST_POLYGON_DETAIL_VORONOI_CTYPES diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_predicates.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_predicates.hpp new file mode 100644 index 0000000..b6314d3 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_predicates.hpp @@ -0,0 +1,1533 @@ +// Boost.Polygon library detail/voronoi_predicates.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_DETAIL_VORONOI_PREDICATES +#define BOOST_POLYGON_DETAIL_VORONOI_PREDICATES + +#include + +#include "voronoi_robust_fpt.hpp" + +namespace boost { +namespace polygon { +namespace detail { + +// Predicate utilities. Operates with the coordinate types that could +// be converted to the 32-bit signed integer without precision loss. +template +class voronoi_predicates { + public: + typedef typename CTYPE_TRAITS::int_type int_type; + typedef typename CTYPE_TRAITS::int_x2_type int_x2_type; + typedef typename CTYPE_TRAITS::uint_x2_type uint_x2_type; + typedef typename CTYPE_TRAITS::big_int_type big_int_type; + typedef typename CTYPE_TRAITS::fpt_type fpt_type; + typedef typename CTYPE_TRAITS::efpt_type efpt_type; + typedef typename CTYPE_TRAITS::ulp_cmp_type ulp_cmp_type; + typedef typename CTYPE_TRAITS::to_fpt_converter_type to_fpt_converter; + typedef typename CTYPE_TRAITS::to_efpt_converter_type to_efpt_converter; + + enum { + ULPS = 64, + ULPSx2 = 128 + }; + + template + static bool is_vertical(const Point& point1, const Point& point2) { + return point1.x() == point2.x(); + } + + template + static bool is_vertical(const Site& site) { + return is_vertical(site.point0(), site.point1()); + } + + // Compute robust cross_product: a1 * b2 - b1 * a2. + // It was mathematically proven that the result is correct + // with epsilon relative error equal to 1EPS. + static fpt_type robust_cross_product(int_x2_type a1_, + int_x2_type b1_, + int_x2_type a2_, + int_x2_type b2_) { + static to_fpt_converter to_fpt; + uint_x2_type a1 = static_cast(is_neg(a1_) ? -a1_ : a1_); + uint_x2_type b1 = static_cast(is_neg(b1_) ? -b1_ : b1_); + uint_x2_type a2 = static_cast(is_neg(a2_) ? -a2_ : a2_); + uint_x2_type b2 = static_cast(is_neg(b2_) ? -b2_ : b2_); + + uint_x2_type l = a1 * b2; + uint_x2_type r = b1 * a2; + + if (is_neg(a1_) ^ is_neg(b2_)) { + if (is_neg(a2_) ^ is_neg(b1_)) + return (l > r) ? -to_fpt(l - r) : to_fpt(r - l); + else + return -to_fpt(l + r); + } else { + if (is_neg(a2_) ^ is_neg(b1_)) + return to_fpt(l + r); + else + return (l < r) ? -to_fpt(r - l) : to_fpt(l - r); + } + } + + struct orientation_test { + public: + // Represents orientation test result. + enum Orientation { + RIGHT = -1, + COLLINEAR = 0, + LEFT = 1 + }; + + // Value is a determinant of two vectors (e.g. x1 * y2 - x2 * y1). + // Return orientation based on the sign of the determinant. + template + static Orientation eval(T value) { + if (is_zero(value)) return COLLINEAR; + return (is_neg(value)) ? RIGHT : LEFT; + } + + static Orientation eval(int_x2_type dif_x1_, + int_x2_type dif_y1_, + int_x2_type dif_x2_, + int_x2_type dif_y2_) { + return eval(robust_cross_product(dif_x1_, dif_y1_, dif_x2_, dif_y2_)); + } + + template + static Orientation eval(const Point& point1, + const Point& point2, + const Point& point3) { + int_x2_type dx1 = static_cast(point1.x()) - + static_cast(point2.x()); + int_x2_type dx2 = static_cast(point2.x()) - + static_cast(point3.x()); + int_x2_type dy1 = static_cast(point1.y()) - + static_cast(point2.y()); + int_x2_type dy2 = static_cast(point2.y()) - + static_cast(point3.y()); + return eval(robust_cross_product(dx1, dy1, dx2, dy2)); + } + }; + typedef orientation_test ot; + + template + class point_comparison_predicate { + public: + typedef Point point_type; + + bool operator()(const point_type& lhs, const point_type& rhs) const { + if (lhs.x() == rhs.x()) + return lhs.y() < rhs.y(); + return lhs.x() < rhs.x(); + } + }; + + template + class event_comparison_predicate { + public: + typedef Site site_type; + typedef Circle circle_type; + + bool operator()(const site_type& lhs, const site_type& rhs) const { + if (lhs.x0() != rhs.x0()) + return lhs.x0() < rhs.x0(); + if (!lhs.is_segment()) { + if (!rhs.is_segment()) + return lhs.y0() < rhs.y0(); + if (is_vertical(rhs)) + return lhs.y0() <= rhs.y0(); + return true; + } else { + if (is_vertical(rhs)) { + if (is_vertical(lhs)) + return lhs.y0() < rhs.y0(); + return false; + } + if (is_vertical(lhs)) + return true; + if (lhs.y0() != rhs.y0()) + return lhs.y0() < rhs.y0(); + return ot::eval(lhs.point1(), lhs.point0(), rhs.point1()) == ot::LEFT; + } + } + + bool operator()(const site_type& lhs, const circle_type& rhs) const { + typename ulp_cmp_type::Result xCmp = + ulp_cmp(to_fpt(lhs.x0()), to_fpt(rhs.lower_x()), ULPS); + return xCmp == ulp_cmp_type::LESS; + } + + bool operator()(const circle_type& lhs, const site_type& rhs) const { + typename ulp_cmp_type::Result xCmp = + ulp_cmp(to_fpt(lhs.lower_x()), to_fpt(rhs.x0()), ULPS); + return xCmp == ulp_cmp_type::LESS; + } + + bool operator()(const circle_type& lhs, const circle_type& rhs) const { + if (lhs.lower_x() != rhs.lower_x()) { + return lhs.lower_x() < rhs.lower_x(); + } + return lhs.y() < rhs.y(); + } + + private: + ulp_cmp_type ulp_cmp; + to_fpt_converter to_fpt; + }; + + template + class distance_predicate { + public: + typedef Site site_type; + typedef typename site_type::point_type point_type; + + // Returns true if a horizontal line going through a new site intersects + // right arc at first, else returns false. If horizontal line goes + // through intersection point of the given two arcs returns false also. + bool operator()(const site_type& left_site, + const site_type& right_site, + const point_type& new_point) const { + if (!left_site.is_segment()) { + if (!right_site.is_segment()) { + return pp(left_site, right_site, new_point); + } else { + return ps(left_site, right_site, new_point, false); + } + } else { + if (!right_site.is_segment()) { + return ps(right_site, left_site, new_point, true); + } else { + return ss(left_site, right_site, new_point); + } + } + } + + private: + // Represents the result of the epsilon robust predicate. If the + // result is undefined some further processing is usually required. + enum kPredicateResult { + LESS = -1, + UNDEFINED = 0, + MORE = 1 + }; + + // Robust predicate, avoids using high-precision libraries. + // Returns true if a horizontal line going through the new point site + // intersects right arc at first, else returns false. If horizontal line + // goes through intersection point of the given two arcs returns false. + bool pp(const site_type& left_site, + const site_type& right_site, + const point_type& new_point) const { + const point_type& left_point = left_site.point0(); + const point_type& right_point = right_site.point0(); + if (left_point.x() > right_point.x()) { + if (new_point.y() <= left_point.y()) + return false; + } else if (left_point.x() < right_point.x()) { + if (new_point.y() >= right_point.y()) + return true; + } else { + return static_cast(left_point.y()) + + static_cast(right_point.y()) < + static_cast(new_point.y()) * 2; + } + + fpt_type dist1 = find_distance_to_point_arc(left_site, new_point); + fpt_type dist2 = find_distance_to_point_arc(right_site, new_point); + + // The undefined ulp range is equal to 3EPS + 3EPS <= 6ULP. + return dist1 < dist2; + } + + bool ps(const site_type& left_site, const site_type& right_site, + const point_type& new_point, bool reverse_order) const { + kPredicateResult fast_res = fast_ps( + left_site, right_site, new_point, reverse_order); + if (fast_res != UNDEFINED) { + return fast_res == LESS; + } + + fpt_type dist1 = find_distance_to_point_arc(left_site, new_point); + fpt_type dist2 = find_distance_to_segment_arc(right_site, new_point); + + // The undefined ulp range is equal to 3EPS + 7EPS <= 10ULP. + return reverse_order ^ (dist1 < dist2); + } + + bool ss(const site_type& left_site, + const site_type& right_site, + const point_type& new_point) const { + // Handle temporary segment sites. + if (left_site.sorted_index() == right_site.sorted_index()) { + return ot::eval( + left_site.point0(), left_site.point1(), new_point) == ot::LEFT; + } + + fpt_type dist1 = find_distance_to_segment_arc(left_site, new_point); + fpt_type dist2 = find_distance_to_segment_arc(right_site, new_point); + + // The undefined ulp range is equal to 7EPS + 7EPS <= 14ULP. + return dist1 < dist2; + } + + fpt_type find_distance_to_point_arc( + const site_type& site, const point_type& point) const { + fpt_type dx = to_fpt(site.x()) - to_fpt(point.x()); + fpt_type dy = to_fpt(site.y()) - to_fpt(point.y()); + // The relative error is at most 3EPS. + return (dx * dx + dy * dy) / (to_fpt(2.0) * dx); + } + + fpt_type find_distance_to_segment_arc( + const site_type& site, const point_type& point) const { + if (is_vertical(site)) { + return (to_fpt(site.x()) - to_fpt(point.x())) * to_fpt(0.5); + } else { + const point_type& segment0 = site.point0(); + const point_type& segment1 = site.point1(); + fpt_type a1 = to_fpt(segment1.x()) - to_fpt(segment0.x()); + fpt_type b1 = to_fpt(segment1.y()) - to_fpt(segment0.y()); + fpt_type k = get_sqrt(a1 * a1 + b1 * b1); + // Avoid subtraction while computing k. + if (!is_neg(b1)) { + k = to_fpt(1.0) / (b1 + k); + } else { + k = (k - b1) / (a1 * a1); + } + // The relative error is at most 7EPS. + return k * robust_cross_product( + static_cast(segment1.x()) - + static_cast(segment0.x()), + static_cast(segment1.y()) - + static_cast(segment0.y()), + static_cast(point.x()) - + static_cast(segment0.x()), + static_cast(point.y()) - + static_cast(segment0.y())); + } + } + + kPredicateResult fast_ps( + const site_type& left_site, const site_type& right_site, + const point_type& new_point, bool reverse_order) const { + const point_type& site_point = left_site.point0(); + const point_type& segment_start = right_site.point0(); + const point_type& segment_end = right_site.point1(); + + if (ot::eval(segment_start, segment_end, new_point) != ot::RIGHT) + return (!right_site.is_inverse()) ? LESS : MORE; + + fpt_type dif_x = to_fpt(new_point.x()) - to_fpt(site_point.x()); + fpt_type dif_y = to_fpt(new_point.y()) - to_fpt(site_point.y()); + fpt_type a = to_fpt(segment_end.x()) - to_fpt(segment_start.x()); + fpt_type b = to_fpt(segment_end.y()) - to_fpt(segment_start.y()); + + if (is_vertical(right_site)) { + if (new_point.y() < site_point.y() && !reverse_order) + return MORE; + else if (new_point.y() > site_point.y() && reverse_order) + return LESS; + return UNDEFINED; + } else { + typename ot::Orientation orientation = ot::eval( + static_cast(segment_end.x()) - + static_cast(segment_start.x()), + static_cast(segment_end.y()) - + static_cast(segment_start.y()), + static_cast(new_point.x()) - + static_cast(site_point.x()), + static_cast(new_point.y()) - + static_cast(site_point.y())); + if (orientation == ot::LEFT) { + if (!right_site.is_inverse()) + return reverse_order ? LESS : UNDEFINED; + return reverse_order ? UNDEFINED : MORE; + } + } + + fpt_type fast_left_expr = a * (dif_y + dif_x) * (dif_y - dif_x); + fpt_type fast_right_expr = (to_fpt(2.0) * b) * dif_x * dif_y; + typename ulp_cmp_type::Result expr_cmp = + ulp_cmp(fast_left_expr, fast_right_expr, 4); + if (expr_cmp != ulp_cmp_type::EQUAL) { + if ((expr_cmp == ulp_cmp_type::MORE) ^ reverse_order) + return reverse_order ? LESS : MORE; + return UNDEFINED; + } + return UNDEFINED; + } + + private: + ulp_cmp_type ulp_cmp; + to_fpt_converter to_fpt; + }; + + template + class node_comparison_predicate { + public: + typedef Node node_type; + typedef typename Node::site_type site_type; + typedef typename site_type::point_type point_type; + typedef typename point_type::coordinate_type coordinate_type; + typedef point_comparison_predicate point_comparison_type; + typedef distance_predicate distance_predicate_type; + + // Compares nodes in the balanced binary search tree. Nodes are + // compared based on the y coordinates of the arcs intersection points. + // Nodes with less y coordinate of the intersection point go first. + // Comparison is only called during the new site events processing. + // That's why one of the nodes will always lie on the sweepline and may + // be represented as a straight horizontal line. + bool operator() (const node_type& node1, + const node_type& node2) const { + // Get x coordinate of the rightmost site from both nodes. + const site_type& site1 = get_comparison_site(node1); + const site_type& site2 = get_comparison_site(node2); + const point_type& point1 = get_comparison_point(site1); + const point_type& point2 = get_comparison_point(site2); + + if (point1.x() < point2.x()) { + // The second node contains a new site. + return distance_predicate_( + node1.left_site(), node1.right_site(), point2); + } else if (point1.x() > point2.x()) { + // The first node contains a new site. + return !distance_predicate_( + node2.left_site(), node2.right_site(), point1); + } else { + // This checks were evaluated experimentally. + if (site1.sorted_index() == site2.sorted_index()) { + // Both nodes are new (inserted during same site event processing). + return get_comparison_y(node1) < get_comparison_y(node2); + } else if (site1.sorted_index() < site2.sorted_index()) { + std::pair y1 = get_comparison_y(node1, false); + std::pair y2 = get_comparison_y(node2, true); + if (y1.first != y2.first) return y1.first < y2.first; + return (!site1.is_segment()) ? (y1.second < 0) : false; + } else { + std::pair y1 = get_comparison_y(node1, true); + std::pair y2 = get_comparison_y(node2, false); + if (y1.first != y2.first) return y1.first < y2.first; + return (!site2.is_segment()) ? (y2.second > 0) : true; + } + } + } + + private: + // Get the newer site. + const site_type& get_comparison_site(const node_type& node) const { + if (node.left_site().sorted_index() > node.right_site().sorted_index()) { + return node.left_site(); + } + return node.right_site(); + } + + const point_type& get_comparison_point(const site_type& site) const { + return point_comparison_(site.point0(), site.point1()) ? + site.point0() : site.point1(); + } + + // Get comparison pair: y coordinate and direction of the newer site. + std::pair get_comparison_y( + const node_type& node, bool is_new_node = true) const { + if (node.left_site().sorted_index() == + node.right_site().sorted_index()) { + return std::make_pair(node.left_site().y0(), 0); + } + if (node.left_site().sorted_index() > node.right_site().sorted_index()) { + if (!is_new_node && + node.left_site().is_segment() && + is_vertical(node.left_site())) { + return std::make_pair(node.left_site().y0(), 1); + } + return std::make_pair(node.left_site().y1(), 1); + } + return std::make_pair(node.right_site().y0(), -1); + } + + point_comparison_type point_comparison_; + distance_predicate_type distance_predicate_; + }; + + template + class circle_existence_predicate { + public: + typedef typename Site::point_type point_type; + typedef Site site_type; + + bool ppp(const site_type& site1, + const site_type& site2, + const site_type& site3) const { + return ot::eval(site1.point0(), + site2.point0(), + site3.point0()) == ot::RIGHT; + } + + bool pps(const site_type& site1, + const site_type& site2, + const site_type& site3, + int segment_index) const { + if (segment_index != 2) { + typename ot::Orientation orient1 = ot::eval( + site1.point0(), site2.point0(), site3.point0()); + typename ot::Orientation orient2 = ot::eval( + site1.point0(), site2.point0(), site3.point1()); + if (segment_index == 1 && site1.x0() >= site2.x0()) { + if (orient1 != ot::RIGHT) + return false; + } else if (segment_index == 3 && site2.x0() >= site1.x0()) { + if (orient2 != ot::RIGHT) + return false; + } else if (orient1 != ot::RIGHT && orient2 != ot::RIGHT) { + return false; + } + } else { + return (site3.point0() != site1.point0()) || + (site3.point1() != site2.point0()); + } + return true; + } + + bool pss(const site_type& site1, + const site_type& site2, + const site_type& site3, + int point_index) const { + if (site2.sorted_index() == site3.sorted_index()) { + return false; + } + if (point_index == 2) { + if (!site2.is_inverse() && site3.is_inverse()) + return false; + if (site2.is_inverse() == site3.is_inverse() && + ot::eval(site2.point0(), + site1.point0(), + site3.point1()) != ot::RIGHT) + return false; + } + return true; + } + + bool sss(const site_type& site1, + const site_type& site2, + const site_type& site3) const { + return (site1.sorted_index() != site2.sorted_index()) && + (site2.sorted_index() != site3.sorted_index()); + } + }; + + template + class mp_circle_formation_functor { + public: + typedef typename Site::point_type point_type; + typedef Site site_type; + typedef Circle circle_type; + typedef robust_sqrt_expr + robust_sqrt_expr_type; + + void ppp(const site_type& site1, + const site_type& site2, + const site_type& site3, + circle_type& circle, + bool recompute_c_x = true, + bool recompute_c_y = true, + bool recompute_lower_x = true) { + big_int_type dif_x[3], dif_y[3], sum_x[2], sum_y[2]; + dif_x[0] = static_cast(site1.x()) - + static_cast(site2.x()); + dif_x[1] = static_cast(site2.x()) - + static_cast(site3.x()); + dif_x[2] = static_cast(site1.x()) - + static_cast(site3.x()); + dif_y[0] = static_cast(site1.y()) - + static_cast(site2.y()); + dif_y[1] = static_cast(site2.y()) - + static_cast(site3.y()); + dif_y[2] = static_cast(site1.y()) - + static_cast(site3.y()); + sum_x[0] = static_cast(site1.x()) + + static_cast(site2.x()); + sum_x[1] = static_cast(site2.x()) + + static_cast(site3.x()); + sum_y[0] = static_cast(site1.y()) + + static_cast(site2.y()); + sum_y[1] = static_cast(site2.y()) + + static_cast(site3.y()); + fpt_type inv_denom = to_fpt(0.5) / to_fpt(static_cast( + dif_x[0] * dif_y[1] - dif_x[1] * dif_y[0])); + big_int_type numer1 = dif_x[0] * sum_x[0] + dif_y[0] * sum_y[0]; + big_int_type numer2 = dif_x[1] * sum_x[1] + dif_y[1] * sum_y[1]; + + if (recompute_c_x || recompute_lower_x) { + big_int_type c_x = numer1 * dif_y[1] - numer2 * dif_y[0]; + circle.x(to_fpt(c_x) * inv_denom); + + if (recompute_lower_x) { + // Evaluate radius of the circle. + big_int_type sqr_r = (dif_x[0] * dif_x[0] + dif_y[0] * dif_y[0]) * + (dif_x[1] * dif_x[1] + dif_y[1] * dif_y[1]) * + (dif_x[2] * dif_x[2] + dif_y[2] * dif_y[2]); + fpt_type r = get_sqrt(to_fpt(sqr_r)); + + // If c_x >= 0 then lower_x = c_x + r, + // else lower_x = (c_x * c_x - r * r) / (c_x - r). + // To guarantee epsilon relative error. + if (!is_neg(circle.x())) { + if (!is_neg(inv_denom)) { + circle.lower_x(circle.x() + r * inv_denom); + } else { + circle.lower_x(circle.x() - r * inv_denom); + } + } else { + big_int_type numer = c_x * c_x - sqr_r; + fpt_type lower_x = to_fpt(numer) * inv_denom / (to_fpt(c_x) + r); + circle.lower_x(lower_x); + } + } + } + + if (recompute_c_y) { + big_int_type c_y = numer2 * dif_x[0] - numer1 * dif_x[1]; + circle.y(to_fpt(c_y) * inv_denom); + } + } + + // Recompute parameters of the circle event using high-precision library. + void pps(const site_type& site1, + const site_type& site2, + const site_type& site3, + int segment_index, + circle_type& c_event, + bool recompute_c_x = true, + bool recompute_c_y = true, + bool recompute_lower_x = true) { + big_int_type cA[4], cB[4]; + big_int_type line_a = static_cast(site3.y1()) - + static_cast(site3.y0()); + big_int_type line_b = static_cast(site3.x0()) - + static_cast(site3.x1()); + big_int_type segm_len = line_a * line_a + line_b * line_b; + big_int_type vec_x = static_cast(site2.y()) - + static_cast(site1.y()); + big_int_type vec_y = static_cast(site1.x()) - + static_cast(site2.x()); + big_int_type sum_x = static_cast(site1.x()) + + static_cast(site2.x()); + big_int_type sum_y = static_cast(site1.y()) + + static_cast(site2.y()); + big_int_type teta = line_a * vec_x + line_b * vec_y; + big_int_type denom = vec_x * line_b - vec_y * line_a; + + big_int_type dif0 = static_cast(site3.y1()) - + static_cast(site1.y()); + big_int_type dif1 = static_cast(site1.x()) - + static_cast(site3.x1()); + big_int_type A = line_a * dif1 - line_b * dif0; + dif0 = static_cast(site3.y1()) - + static_cast(site2.y()); + dif1 = static_cast(site2.x()) - + static_cast(site3.x1()); + big_int_type B = line_a * dif1 - line_b * dif0; + big_int_type sum_AB = A + B; + + if (is_zero(denom)) { + big_int_type numer = teta * teta - sum_AB * sum_AB; + denom = teta * sum_AB; + cA[0] = denom * sum_x * 2 + numer * vec_x; + cB[0] = segm_len; + cA[1] = denom * sum_AB * 2 + numer * teta; + cB[1] = 1; + cA[2] = denom * sum_y * 2 + numer * vec_y; + fpt_type inv_denom = to_fpt(1.0) / to_fpt(denom); + if (recompute_c_x) + c_event.x(to_fpt(0.25) * to_fpt(cA[0]) * inv_denom); + if (recompute_c_y) + c_event.y(to_fpt(0.25) * to_fpt(cA[2]) * inv_denom); + if (recompute_lower_x) { + c_event.lower_x(to_fpt(0.25) * to_fpt(sqrt_expr_.eval2(cA, cB)) * + inv_denom / get_sqrt(to_fpt(segm_len))); + } + return; + } + + big_int_type det = (teta * teta + denom * denom) * A * B * 4; + fpt_type inv_denom_sqr = to_fpt(1.0) / to_fpt(denom); + inv_denom_sqr *= inv_denom_sqr; + + if (recompute_c_x || recompute_lower_x) { + cA[0] = sum_x * denom * denom + teta * sum_AB * vec_x; + cB[0] = 1; + cA[1] = (segment_index == 2) ? -vec_x : vec_x; + cB[1] = det; + if (recompute_c_x) { + c_event.x(to_fpt(0.5) * to_fpt(sqrt_expr_.eval2(cA, cB)) * + inv_denom_sqr); + } + } + + if (recompute_c_y || recompute_lower_x) { + cA[2] = sum_y * denom * denom + teta * sum_AB * vec_y; + cB[2] = 1; + cA[3] = (segment_index == 2) ? -vec_y : vec_y; + cB[3] = det; + if (recompute_c_y) { + c_event.y(to_fpt(0.5) * to_fpt(sqrt_expr_.eval2(&cA[2], &cB[2])) * + inv_denom_sqr); + } + } + + if (recompute_lower_x) { + cB[0] = cB[0] * segm_len; + cB[1] = cB[1] * segm_len; + cA[2] = sum_AB * (denom * denom + teta * teta); + cB[2] = 1; + cA[3] = (segment_index == 2) ? -teta : teta; + cB[3] = det; + c_event.lower_x(to_fpt(0.5) * to_fpt(sqrt_expr_.eval4(cA, cB)) * + inv_denom_sqr / get_sqrt(to_fpt(segm_len))); + } + } + + // Recompute parameters of the circle event using high-precision library. + void pss(const site_type& site1, + const site_type& site2, + const site_type& site3, + int point_index, + circle_type& c_event, + bool recompute_c_x = true, + bool recompute_c_y = true, + bool recompute_lower_x = true) { + big_int_type a[2], b[2], c[2], cA[4], cB[4]; + const point_type& segm_start1 = site2.point1(); + const point_type& segm_end1 = site2.point0(); + const point_type& segm_start2 = site3.point0(); + const point_type& segm_end2 = site3.point1(); + a[0] = static_cast(segm_end1.x()) - + static_cast(segm_start1.x()); + b[0] = static_cast(segm_end1.y()) - + static_cast(segm_start1.y()); + a[1] = static_cast(segm_end2.x()) - + static_cast(segm_start2.x()); + b[1] = static_cast(segm_end2.y()) - + static_cast(segm_start2.y()); + big_int_type orientation = a[1] * b[0] - a[0] * b[1]; + if (is_zero(orientation)) { + fpt_type denom = to_fpt(2.0) * to_fpt( + static_cast(a[0] * a[0] + b[0] * b[0])); + c[0] = b[0] * (static_cast(segm_start2.x()) - + static_cast(segm_start1.x())) - + a[0] * (static_cast(segm_start2.y()) - + static_cast(segm_start1.y())); + big_int_type dx = a[0] * (static_cast(site1.y()) - + static_cast(segm_start1.y())) - + b[0] * (static_cast(site1.x()) - + static_cast(segm_start1.x())); + big_int_type dy = b[0] * (static_cast(site1.x()) - + static_cast(segm_start2.x())) - + a[0] * (static_cast(site1.y()) - + static_cast(segm_start2.y())); + cB[0] = dx * dy; + cB[1] = 1; + + if (recompute_c_y) { + cA[0] = b[0] * ((point_index == 2) ? 2 : -2); + cA[1] = a[0] * a[0] * (static_cast(segm_start1.y()) + + static_cast(segm_start2.y())) - + a[0] * b[0] * (static_cast(segm_start1.x()) + + static_cast(segm_start2.x()) - + static_cast(site1.x()) * 2) + + b[0] * b[0] * (static_cast(site1.y()) * 2); + fpt_type c_y = to_fpt(sqrt_expr_.eval2(cA, cB)); + c_event.y(c_y / denom); + } + + if (recompute_c_x || recompute_lower_x) { + cA[0] = a[0] * ((point_index == 2) ? 2 : -2); + cA[1] = b[0] * b[0] * (static_cast(segm_start1.x()) + + static_cast(segm_start2.x())) - + a[0] * b[0] * (static_cast(segm_start1.y()) + + static_cast(segm_start2.y()) - + static_cast(site1.y()) * 2) + + a[0] * a[0] * (static_cast(site1.x()) * 2); + + if (recompute_c_x) { + fpt_type c_x = to_fpt(sqrt_expr_.eval2(cA, cB)); + c_event.x(c_x / denom); + } + + if (recompute_lower_x) { + cA[2] = is_neg(c[0]) ? -c[0] : c[0]; + cB[2] = a[0] * a[0] + b[0] * b[0]; + fpt_type lower_x = to_fpt(sqrt_expr_.eval3(cA, cB)); + c_event.lower_x(lower_x / denom); + } + } + return; + } + c[0] = b[0] * segm_end1.x() - a[0] * segm_end1.y(); + c[1] = a[1] * segm_end2.y() - b[1] * segm_end2.x(); + big_int_type ix = a[0] * c[1] + a[1] * c[0]; + big_int_type iy = b[0] * c[1] + b[1] * c[0]; + big_int_type dx = ix - orientation * site1.x(); + big_int_type dy = iy - orientation * site1.y(); + if (is_zero(dx) && is_zero(dy)) { + fpt_type denom = to_fpt(orientation); + fpt_type c_x = to_fpt(ix) / denom; + fpt_type c_y = to_fpt(iy) / denom; + c_event = circle_type(c_x, c_y, c_x); + return; + } + + big_int_type sign = ((point_index == 2) ? 1 : -1) * + (is_neg(orientation) ? 1 : -1); + cA[0] = a[1] * -dx + b[1] * -dy; + cA[1] = a[0] * -dx + b[0] * -dy; + cA[2] = sign; + cA[3] = 0; + cB[0] = a[0] * a[0] + b[0] * b[0]; + cB[1] = a[1] * a[1] + b[1] * b[1]; + cB[2] = a[0] * a[1] + b[0] * b[1]; + cB[3] = (a[0] * dy - b[0] * dx) * (a[1] * dy - b[1] * dx) * -2; + fpt_type temp = to_fpt( + sqrt_expr_evaluator_pss4(cA, cB)); + fpt_type denom = temp * to_fpt(orientation); + + if (recompute_c_y) { + cA[0] = b[1] * (dx * dx + dy * dy) - iy * (dx * a[1] + dy * b[1]); + cA[1] = b[0] * (dx * dx + dy * dy) - iy * (dx * a[0] + dy * b[0]); + cA[2] = iy * sign; + fpt_type cy = to_fpt( + sqrt_expr_evaluator_pss4(cA, cB)); + c_event.y(cy / denom); + } + + if (recompute_c_x || recompute_lower_x) { + cA[0] = a[1] * (dx * dx + dy * dy) - ix * (dx * a[1] + dy * b[1]); + cA[1] = a[0] * (dx * dx + dy * dy) - ix * (dx * a[0] + dy * b[0]); + cA[2] = ix * sign; + + if (recompute_c_x) { + fpt_type cx = to_fpt( + sqrt_expr_evaluator_pss4(cA, cB)); + c_event.x(cx / denom); + } + + if (recompute_lower_x) { + cA[3] = orientation * (dx * dx + dy * dy) * (is_neg(temp) ? -1 : 1); + fpt_type lower_x = to_fpt( + sqrt_expr_evaluator_pss4(cA, cB)); + c_event.lower_x(lower_x / denom); + } + } + } + + // Recompute parameters of the circle event using high-precision library. + void sss(const site_type& site1, + const site_type& site2, + const site_type& site3, + circle_type& c_event, + bool recompute_c_x = true, + bool recompute_c_y = true, + bool recompute_lower_x = true) { + big_int_type a[3], b[3], c[3], cA[4], cB[4]; + // cA - corresponds to the cross product. + // cB - corresponds to the squared length. + a[0] = static_cast(site1.x1()) - + static_cast(site1.x0()); + a[1] = static_cast(site2.x1()) - + static_cast(site2.x0()); + a[2] = static_cast(site3.x1()) - + static_cast(site3.x0()); + + b[0] = static_cast(site1.y1()) - + static_cast(site1.y0()); + b[1] = static_cast(site2.y1()) - + static_cast(site2.y0()); + b[2] = static_cast(site3.y1()) - + static_cast(site3.y0()); + + c[0] = static_cast(site1.x0()) * + static_cast(site1.y1()) - + static_cast(site1.y0()) * + static_cast(site1.x1()); + c[1] = static_cast(site2.x0()) * + static_cast(site2.y1()) - + static_cast(site2.y0()) * + static_cast(site2.x1()); + c[2] = static_cast(site3.x0()) * + static_cast(site3.y1()) - + static_cast(site3.y0()) * + static_cast(site3.x1()); + + for (int i = 0; i < 3; ++i) + cB[i] = a[i] * a[i] + b[i] * b[i]; + + for (int i = 0; i < 3; ++i) { + int j = (i+1) % 3; + int k = (i+2) % 3; + cA[i] = a[j] * b[k] - a[k] * b[j]; + } + fpt_type denom = to_fpt(sqrt_expr_.eval3(cA, cB)); + + if (recompute_c_y) { + for (int i = 0; i < 3; ++i) { + int j = (i+1) % 3; + int k = (i+2) % 3; + cA[i] = b[j] * c[k] - b[k] * c[j]; + } + fpt_type c_y = to_fpt(sqrt_expr_.eval3(cA, cB)); + c_event.y(c_y / denom); + } + + if (recompute_c_x || recompute_lower_x) { + cA[3] = 0; + for (int i = 0; i < 3; ++i) { + int j = (i+1) % 3; + int k = (i+2) % 3; + cA[i] = a[j] * c[k] - a[k] * c[j]; + if (recompute_lower_x) { + cA[3] = cA[3] + cA[i] * b[i]; + } + } + + if (recompute_c_x) { + fpt_type c_x = to_fpt(sqrt_expr_.eval3(cA, cB)); + c_event.x(c_x / denom); + } + + if (recompute_lower_x) { + cB[3] = 1; + fpt_type lower_x = to_fpt(sqrt_expr_.eval4(cA, cB)); + c_event.lower_x(lower_x / denom); + } + } + } + + private: + // Evaluates A[3] + A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]) + + // A[2] * sqrt(B[3] * (sqrt(B[0] * B[1]) + B[2])). + template + _fpt sqrt_expr_evaluator_pss4(_int *A, _int *B) { + _int cA[4], cB[4]; + if (is_zero(A[3])) { + _fpt lh = sqrt_expr_.eval2(A, B); + cA[0] = 1; + cB[0] = B[0] * B[1]; + cA[1] = B[2]; + cB[1] = 1; + _fpt rh = sqrt_expr_.eval1(A+2, B+3) * + get_sqrt(sqrt_expr_.eval2(cA, cB)); + if ((!is_neg(lh) && !is_neg(rh)) || (!is_pos(lh) && !is_pos(rh))) + return lh + rh; + cA[0] = A[0] * A[0] * B[0] + A[1] * A[1] * B[1] - + A[2] * A[2] * B[3] * B[2]; + cB[0] = 1; + cA[1] = A[0] * A[1] * 2 - A[2] * A[2] * B[3]; + cB[1] = B[0] * B[1]; + _fpt numer = sqrt_expr_.eval2(cA, cB); + return numer / (lh - rh); + } + cA[0] = 1; + cB[0] = B[0] * B[1]; + cA[1] = B[2]; + cB[1] = 1; + _fpt rh = sqrt_expr_.eval1(A+2, B+3) * get_sqrt(sqrt_expr_.eval2(cA, cB)); + cA[0] = A[0]; + cB[0] = B[0]; + cA[1] = A[1]; + cB[1] = B[1]; + cA[2] = A[3]; + cB[2] = 1; + _fpt lh = sqrt_expr_.eval3(cA, cB); + if ((!is_neg(lh) && !is_neg(rh)) || (!is_pos(lh) && !is_pos(rh))) + return lh + rh; + cA[0] = A[3] * A[0] * 2; + cA[1] = A[3] * A[1] * 2; + cA[2] = A[0] * A[0] * B[0] + A[1] * A[1] * B[1] + + A[3] * A[3] - A[2] * A[2] * B[2] * B[3]; + cA[3] = A[0] * A[1] * 2 - A[2] * A[2] * B[3]; + cB[3] = B[0] * B[1]; + _fpt numer = sqrt_expr_evaluator_pss3<_int, _fpt>(cA, cB); + return numer / (lh - rh); + } + + template + // Evaluates A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]) + + // A[2] + A[3] * sqrt(B[0] * B[1]). + // B[3] = B[0] * B[1]. + _fpt sqrt_expr_evaluator_pss3(_int *A, _int *B) { + _int cA[2], cB[2]; + _fpt lh = sqrt_expr_.eval2(A, B); + _fpt rh = sqrt_expr_.eval2(A+2, B+2); + if ((!is_neg(lh) && !is_neg(rh)) || (!is_pos(lh) && !is_pos(rh))) + return lh + rh; + cA[0] = A[0] * A[0] * B[0] + A[1] * A[1] * B[1] - + A[2] * A[2] - A[3] * A[3] * B[0] * B[1]; + cB[0] = 1; + cA[1] = (A[0] * A[1] - A[2] * A[3]) * 2; + cB[1] = B[3]; + _fpt numer = sqrt_expr_.eval2(cA, cB); + return numer / (lh - rh); + } + + robust_sqrt_expr_type sqrt_expr_; + to_fpt_converter to_fpt; + }; + + template + class lazy_circle_formation_functor { + public: + typedef robust_fpt robust_fpt_type; + typedef robust_dif robust_dif_type; + typedef typename Site::point_type point_type; + typedef Site site_type; + typedef Circle circle_type; + typedef mp_circle_formation_functor + exact_circle_formation_functor_type; + + void ppp(const site_type& site1, + const site_type& site2, + const site_type& site3, + circle_type& c_event) { + fpt_type dif_x1 = to_fpt(site1.x()) - to_fpt(site2.x()); + fpt_type dif_x2 = to_fpt(site2.x()) - to_fpt(site3.x()); + fpt_type dif_y1 = to_fpt(site1.y()) - to_fpt(site2.y()); + fpt_type dif_y2 = to_fpt(site2.y()) - to_fpt(site3.y()); + fpt_type orientation = robust_cross_product( + static_cast(site1.x()) - + static_cast(site2.x()), + static_cast(site2.x()) - + static_cast(site3.x()), + static_cast(site1.y()) - + static_cast(site2.y()), + static_cast(site2.y()) - + static_cast(site3.y())); + robust_fpt_type inv_orientation(to_fpt(0.5) / orientation, to_fpt(2.0)); + fpt_type sum_x1 = to_fpt(site1.x()) + to_fpt(site2.x()); + fpt_type sum_x2 = to_fpt(site2.x()) + to_fpt(site3.x()); + fpt_type sum_y1 = to_fpt(site1.y()) + to_fpt(site2.y()); + fpt_type sum_y2 = to_fpt(site2.y()) + to_fpt(site3.y()); + fpt_type dif_x3 = to_fpt(site1.x()) - to_fpt(site3.x()); + fpt_type dif_y3 = to_fpt(site1.y()) - to_fpt(site3.y()); + robust_dif_type c_x, c_y; + c_x += robust_fpt_type(dif_x1 * sum_x1 * dif_y2, to_fpt(2.0)); + c_x += robust_fpt_type(dif_y1 * sum_y1 * dif_y2, to_fpt(2.0)); + c_x -= robust_fpt_type(dif_x2 * sum_x2 * dif_y1, to_fpt(2.0)); + c_x -= robust_fpt_type(dif_y2 * sum_y2 * dif_y1, to_fpt(2.0)); + c_y += robust_fpt_type(dif_x2 * sum_x2 * dif_x1, to_fpt(2.0)); + c_y += robust_fpt_type(dif_y2 * sum_y2 * dif_x1, to_fpt(2.0)); + c_y -= robust_fpt_type(dif_x1 * sum_x1 * dif_x2, to_fpt(2.0)); + c_y -= robust_fpt_type(dif_y1 * sum_y1 * dif_x2, to_fpt(2.0)); + robust_dif_type lower_x(c_x); + lower_x -= robust_fpt_type(get_sqrt( + (dif_x1 * dif_x1 + dif_y1 * dif_y1) * + (dif_x2 * dif_x2 + dif_y2 * dif_y2) * + (dif_x3 * dif_x3 + dif_y3 * dif_y3)), to_fpt(5.0)); + c_event = circle_type( + c_x.dif().fpv() * inv_orientation.fpv(), + c_y.dif().fpv() * inv_orientation.fpv(), + lower_x.dif().fpv() * inv_orientation.fpv()); + bool recompute_c_x = c_x.dif().ulp() > ULPS; + bool recompute_c_y = c_y.dif().ulp() > ULPS; + bool recompute_lower_x = lower_x.dif().ulp() > ULPS; + if (recompute_c_x || recompute_c_y || recompute_lower_x) { + exact_circle_formation_functor_.ppp( + site1, site2, site3, c_event, + recompute_c_x, recompute_c_y, recompute_lower_x); + } + } + + void pps(const site_type& site1, + const site_type& site2, + const site_type& site3, + int segment_index, + circle_type& c_event) { + fpt_type line_a = to_fpt(site3.y1()) - to_fpt(site3.y0()); + fpt_type line_b = to_fpt(site3.x0()) - to_fpt(site3.x1()); + fpt_type vec_x = to_fpt(site2.y()) - to_fpt(site1.y()); + fpt_type vec_y = to_fpt(site1.x()) - to_fpt(site2.x()); + robust_fpt_type teta(robust_cross_product( + static_cast(site3.y1()) - + static_cast(site3.y0()), + static_cast(site3.x0()) - + static_cast(site3.x1()), + static_cast(site2.x()) - + static_cast(site1.x()), + static_cast(site2.y()) - + static_cast(site1.y())), to_fpt(1.0)); + robust_fpt_type A(robust_cross_product( + static_cast(site3.y0()) - + static_cast(site3.y1()), + static_cast(site3.x0()) - + static_cast(site3.x1()), + static_cast(site3.y1()) - + static_cast(site1.y()), + static_cast(site3.x1()) - + static_cast(site1.x())), to_fpt(1.0)); + robust_fpt_type B(robust_cross_product( + static_cast(site3.y0()) - + static_cast(site3.y1()), + static_cast(site3.x0()) - + static_cast(site3.x1()), + static_cast(site3.y1()) - + static_cast(site2.y()), + static_cast(site3.x1()) - + static_cast(site2.x())), to_fpt(1.0)); + robust_fpt_type denom(robust_cross_product( + static_cast(site1.y()) - + static_cast(site2.y()), + static_cast(site1.x()) - + static_cast(site2.x()), + static_cast(site3.y1()) - + static_cast(site3.y0()), + static_cast(site3.x1()) - + static_cast(site3.x0())), to_fpt(1.0)); + robust_fpt_type inv_segm_len(to_fpt(1.0) / + get_sqrt(line_a * line_a + line_b * line_b), to_fpt(3.0)); + robust_dif_type t; + if (ot::eval(denom) == ot::COLLINEAR) { + t += teta / (robust_fpt_type(to_fpt(8.0)) * A); + t -= A / (robust_fpt_type(to_fpt(2.0)) * teta); + } else { + robust_fpt_type det = ((teta * teta + denom * denom) * A * B).sqrt(); + if (segment_index == 2) { + t -= det / (denom * denom); + } else { + t += det / (denom * denom); + } + t += teta * (A + B) / (robust_fpt_type(to_fpt(2.0)) * denom * denom); + } + robust_dif_type c_x, c_y; + c_x += robust_fpt_type(to_fpt(0.5) * + (to_fpt(site1.x()) + to_fpt(site2.x()))); + c_x += robust_fpt_type(vec_x) * t; + c_y += robust_fpt_type(to_fpt(0.5) * + (to_fpt(site1.y()) + to_fpt(site2.y()))); + c_y += robust_fpt_type(vec_y) * t; + robust_dif_type r, lower_x(c_x); + r -= robust_fpt_type(line_a) * robust_fpt_type(site3.x0()); + r -= robust_fpt_type(line_b) * robust_fpt_type(site3.y0()); + r += robust_fpt_type(line_a) * c_x; + r += robust_fpt_type(line_b) * c_y; + if (r.pos().fpv() < r.neg().fpv()) + r = -r; + lower_x += r * inv_segm_len; + c_event = circle_type( + c_x.dif().fpv(), c_y.dif().fpv(), lower_x.dif().fpv()); + bool recompute_c_x = c_x.dif().ulp() > ULPS; + bool recompute_c_y = c_y.dif().ulp() > ULPS; + bool recompute_lower_x = lower_x.dif().ulp() > ULPS; + if (recompute_c_x || recompute_c_y || recompute_lower_x) { + exact_circle_formation_functor_.pps( + site1, site2, site3, segment_index, c_event, + recompute_c_x, recompute_c_y, recompute_lower_x); + } + } + + void pss(const site_type& site1, + const site_type& site2, + const site_type& site3, + int point_index, + circle_type& c_event) { + const point_type& segm_start1 = site2.point1(); + const point_type& segm_end1 = site2.point0(); + const point_type& segm_start2 = site3.point0(); + const point_type& segm_end2 = site3.point1(); + fpt_type a1 = to_fpt(segm_end1.x()) - to_fpt(segm_start1.x()); + fpt_type b1 = to_fpt(segm_end1.y()) - to_fpt(segm_start1.y()); + fpt_type a2 = to_fpt(segm_end2.x()) - to_fpt(segm_start2.x()); + fpt_type b2 = to_fpt(segm_end2.y()) - to_fpt(segm_start2.y()); + bool recompute_c_x, recompute_c_y, recompute_lower_x; + robust_fpt_type orientation(robust_cross_product( + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(segm_end2.y()) - + static_cast(segm_start2.y()), + static_cast(segm_end2.x()) - + static_cast(segm_start2.x())), to_fpt(1.0)); + if (ot::eval(orientation) == ot::COLLINEAR) { + robust_fpt_type a(a1 * a1 + b1 * b1, to_fpt(2.0)); + robust_fpt_type c(robust_cross_product( + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(segm_start2.y()) - + static_cast(segm_start1.y()), + static_cast(segm_start2.x()) - + static_cast(segm_start1.x())), to_fpt(1.0)); + robust_fpt_type det( + robust_cross_product( + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(site1.x()) - + static_cast(segm_start1.x()), + static_cast(site1.y()) - + static_cast(segm_start1.y())) * + robust_cross_product( + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(site1.y()) - + static_cast(segm_start2.y()), + static_cast(site1.x()) - + static_cast(segm_start2.x())), + to_fpt(3.0)); + robust_dif_type t; + t -= robust_fpt_type(a1) * robust_fpt_type(( + to_fpt(segm_start1.x()) + to_fpt(segm_start2.x())) * to_fpt(0.5) - + to_fpt(site1.x())); + t -= robust_fpt_type(b1) * robust_fpt_type(( + to_fpt(segm_start1.y()) + to_fpt(segm_start2.y())) * to_fpt(0.5) - + to_fpt(site1.y())); + if (point_index == 2) { + t += det.sqrt(); + } else { + t -= det.sqrt(); + } + t /= a; + robust_dif_type c_x, c_y; + c_x += robust_fpt_type(to_fpt(0.5) * ( + to_fpt(segm_start1.x()) + to_fpt(segm_start2.x()))); + c_x += robust_fpt_type(a1) * t; + c_y += robust_fpt_type(to_fpt(0.5) * ( + to_fpt(segm_start1.y()) + to_fpt(segm_start2.y()))); + c_y += robust_fpt_type(b1) * t; + robust_dif_type lower_x(c_x); + if (is_neg(c)) { + lower_x -= robust_fpt_type(to_fpt(0.5)) * c / a.sqrt(); + } else { + lower_x += robust_fpt_type(to_fpt(0.5)) * c / a.sqrt(); + } + recompute_c_x = c_x.dif().ulp() > ULPS; + recompute_c_y = c_y.dif().ulp() > ULPS; + recompute_lower_x = lower_x.dif().ulp() > ULPS; + c_event = + circle_type(c_x.dif().fpv(), c_y.dif().fpv(), lower_x.dif().fpv()); + } else { + robust_fpt_type sqr_sum1(get_sqrt(a1 * a1 + b1 * b1), to_fpt(2.0)); + robust_fpt_type sqr_sum2(get_sqrt(a2 * a2 + b2 * b2), to_fpt(2.0)); + robust_fpt_type a(robust_cross_product( + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(segm_start2.y()) - + static_cast(segm_end2.y()), + static_cast(segm_end2.x()) - + static_cast(segm_start2.x())), to_fpt(1.0)); + if (!is_neg(a)) { + a += sqr_sum1 * sqr_sum2; + } else { + a = (orientation * orientation) / (sqr_sum1 * sqr_sum2 - a); + } + robust_fpt_type or1(robust_cross_product( + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(segm_end1.y()) - + static_cast(site1.y()), + static_cast(segm_end1.x()) - + static_cast(site1.x())), to_fpt(1.0)); + robust_fpt_type or2(robust_cross_product( + static_cast(segm_end2.x()) - + static_cast(segm_start2.x()), + static_cast(segm_end2.y()) - + static_cast(segm_start2.y()), + static_cast(segm_end2.x()) - + static_cast(site1.x()), + static_cast(segm_end2.y()) - + static_cast(site1.y())), to_fpt(1.0)); + robust_fpt_type det = robust_fpt_type(to_fpt(2.0)) * a * or1 * or2; + robust_fpt_type c1(robust_cross_product( + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(segm_end1.y()), + static_cast(segm_end1.x())), to_fpt(1.0)); + robust_fpt_type c2(robust_cross_product( + static_cast(segm_end2.x()) - + static_cast(segm_start2.x()), + static_cast(segm_end2.y()) - + static_cast(segm_start2.y()), + static_cast(segm_end2.x()), + static_cast(segm_end2.y())), to_fpt(1.0)); + robust_fpt_type inv_orientation = + robust_fpt_type(to_fpt(1.0)) / orientation; + robust_dif_type t, b, ix, iy; + ix += robust_fpt_type(a2) * c1 * inv_orientation; + ix += robust_fpt_type(a1) * c2 * inv_orientation; + iy += robust_fpt_type(b1) * c2 * inv_orientation; + iy += robust_fpt_type(b2) * c1 * inv_orientation; + + b += ix * (robust_fpt_type(a1) * sqr_sum2); + b += ix * (robust_fpt_type(a2) * sqr_sum1); + b += iy * (robust_fpt_type(b1) * sqr_sum2); + b += iy * (robust_fpt_type(b2) * sqr_sum1); + b -= sqr_sum1 * robust_fpt_type(robust_cross_product( + static_cast(segm_end2.x()) - + static_cast(segm_start2.x()), + static_cast(segm_end2.y()) - + static_cast(segm_start2.y()), + static_cast(-site1.y()), + static_cast(site1.x())), to_fpt(1.0)); + b -= sqr_sum2 * robust_fpt_type(robust_cross_product( + static_cast(segm_end1.x()) - + static_cast(segm_start1.x()), + static_cast(segm_end1.y()) - + static_cast(segm_start1.y()), + static_cast(-site1.y()), + static_cast(site1.x())), to_fpt(1.0)); + t -= b; + if (point_index == 2) { + t += det.sqrt(); + } else { + t -= det.sqrt(); + } + t /= (a * a); + robust_dif_type c_x(ix), c_y(iy); + c_x += t * (robust_fpt_type(a1) * sqr_sum2); + c_x += t * (robust_fpt_type(a2) * sqr_sum1); + c_y += t * (robust_fpt_type(b1) * sqr_sum2); + c_y += t * (robust_fpt_type(b2) * sqr_sum1); + if (t.pos().fpv() < t.neg().fpv()) { + t = -t; + } + robust_dif_type lower_x(c_x); + if (is_neg(orientation)) { + lower_x -= t * orientation; + } else { + lower_x += t * orientation; + } + recompute_c_x = c_x.dif().ulp() > ULPS; + recompute_c_y = c_y.dif().ulp() > ULPS; + recompute_lower_x = lower_x.dif().ulp() > ULPS; + c_event = circle_type( + c_x.dif().fpv(), c_y.dif().fpv(), lower_x.dif().fpv()); + } + if (recompute_c_x || recompute_c_y || recompute_lower_x) { + exact_circle_formation_functor_.pss( + site1, site2, site3, point_index, c_event, + recompute_c_x, recompute_c_y, recompute_lower_x); + } + } + + void sss(const site_type& site1, + const site_type& site2, + const site_type& site3, + circle_type& c_event) { + robust_fpt_type a1(to_fpt(site1.x1()) - to_fpt(site1.x0())); + robust_fpt_type b1(to_fpt(site1.y1()) - to_fpt(site1.y0())); + robust_fpt_type c1(robust_cross_product( + site1.x0(), site1.y0(), + site1.x1(), site1.y1()), to_fpt(1.0)); + + robust_fpt_type a2(to_fpt(site2.x1()) - to_fpt(site2.x0())); + robust_fpt_type b2(to_fpt(site2.y1()) - to_fpt(site2.y0())); + robust_fpt_type c2(robust_cross_product( + site2.x0(), site2.y0(), + site2.x1(), site2.y1()), to_fpt(1.0)); + + robust_fpt_type a3(to_fpt(site3.x1()) - to_fpt(site3.x0())); + robust_fpt_type b3(to_fpt(site3.y1()) - to_fpt(site3.y0())); + robust_fpt_type c3(robust_cross_product( + site3.x0(), site3.y0(), + site3.x1(), site3.y1()), to_fpt(1.0)); + + robust_fpt_type len1 = (a1 * a1 + b1 * b1).sqrt(); + robust_fpt_type len2 = (a2 * a2 + b2 * b2).sqrt(); + robust_fpt_type len3 = (a3 * a3 + b3 * b3).sqrt(); + robust_fpt_type cross_12(robust_cross_product( + static_cast(site1.x1()) - + static_cast(site1.x0()), + static_cast(site1.y1()) - + static_cast(site1.y0()), + static_cast(site2.x1()) - + static_cast(site2.x0()), + static_cast(site2.y1()) - + static_cast(site2.y0())), to_fpt(1.0)); + robust_fpt_type cross_23(robust_cross_product( + static_cast(site2.x1()) - + static_cast(site2.x0()), + static_cast(site2.y1()) - + static_cast(site2.y0()), + static_cast(site3.x1()) - + static_cast(site3.x0()), + static_cast(site3.y1()) - + static_cast(site3.y0())), to_fpt(1.0)); + robust_fpt_type cross_31(robust_cross_product( + static_cast(site3.x1()) - + static_cast(site3.x0()), + static_cast(site3.y1()) - + static_cast(site3.y0()), + static_cast(site1.x1()) - + static_cast(site1.x0()), + static_cast(site1.y1()) - + static_cast(site1.y0())), to_fpt(1.0)); + + // denom = cross_12 * len3 + cross_23 * len1 + cross_31 * len2. + robust_dif_type denom; + denom += cross_12 * len3; + denom += cross_23 * len1; + denom += cross_31 * len2; + + // denom * r = (b2 * c_x - a2 * c_y - c2 * denom) / len2. + robust_dif_type r; + r -= cross_12 * c3; + r -= cross_23 * c1; + r -= cross_31 * c2; + + robust_dif_type c_x; + c_x += a1 * c2 * len3; + c_x -= a2 * c1 * len3; + c_x += a2 * c3 * len1; + c_x -= a3 * c2 * len1; + c_x += a3 * c1 * len2; + c_x -= a1 * c3 * len2; + + robust_dif_type c_y; + c_y += b1 * c2 * len3; + c_y -= b2 * c1 * len3; + c_y += b2 * c3 * len1; + c_y -= b3 * c2 * len1; + c_y += b3 * c1 * len2; + c_y -= b1 * c3 * len2; + + robust_dif_type lower_x = c_x + r; + + robust_fpt_type denom_dif = denom.dif(); + robust_fpt_type c_x_dif = c_x.dif() / denom_dif; + robust_fpt_type c_y_dif = c_y.dif() / denom_dif; + robust_fpt_type lower_x_dif = lower_x.dif() / denom_dif; + + bool recompute_c_x = c_x_dif.ulp() > ULPS; + bool recompute_c_y = c_y_dif.ulp() > ULPS; + bool recompute_lower_x = lower_x_dif.ulp() > ULPS; + c_event = circle_type(c_x_dif.fpv(), c_y_dif.fpv(), lower_x_dif.fpv()); + if (recompute_c_x || recompute_c_y || recompute_lower_x) { + exact_circle_formation_functor_.sss( + site1, site2, site3, c_event, + recompute_c_x, recompute_c_y, recompute_lower_x); + } + } + + private: + exact_circle_formation_functor_type exact_circle_formation_functor_; + to_fpt_converter to_fpt; + }; + + template , + typename CFF = lazy_circle_formation_functor > + class circle_formation_predicate { + public: + typedef Site site_type; + typedef Circle circle_type; + typedef CEP circle_existence_predicate_type; + typedef CFF circle_formation_functor_type; + + // Create a circle event from the given three sites. + // Returns true if the circle event exists, else false. + // If exists circle event is saved into the c_event variable. + bool operator()(const site_type& site1, const site_type& site2, + const site_type& site3, circle_type& circle) { + if (!site1.is_segment()) { + if (!site2.is_segment()) { + if (!site3.is_segment()) { + // (point, point, point) sites. + if (!circle_existence_predicate_.ppp(site1, site2, site3)) + return false; + circle_formation_functor_.ppp(site1, site2, site3, circle); + } else { + // (point, point, segment) sites. + if (!circle_existence_predicate_.pps(site1, site2, site3, 3)) + return false; + circle_formation_functor_.pps(site1, site2, site3, 3, circle); + } + } else { + if (!site3.is_segment()) { + // (point, segment, point) sites. + if (!circle_existence_predicate_.pps(site1, site3, site2, 2)) + return false; + circle_formation_functor_.pps(site1, site3, site2, 2, circle); + } else { + // (point, segment, segment) sites. + if (!circle_existence_predicate_.pss(site1, site2, site3, 1)) + return false; + circle_formation_functor_.pss(site1, site2, site3, 1, circle); + } + } + } else { + if (!site2.is_segment()) { + if (!site3.is_segment()) { + // (segment, point, point) sites. + if (!circle_existence_predicate_.pps(site2, site3, site1, 1)) + return false; + circle_formation_functor_.pps(site2, site3, site1, 1, circle); + } else { + // (segment, point, segment) sites. + if (!circle_existence_predicate_.pss(site2, site1, site3, 2)) + return false; + circle_formation_functor_.pss(site2, site1, site3, 2, circle); + } + } else { + if (!site3.is_segment()) { + // (segment, segment, point) sites. + if (!circle_existence_predicate_.pss(site3, site1, site2, 3)) + return false; + circle_formation_functor_.pss(site3, site1, site2, 3, circle); + } else { + // (segment, segment, segment) sites. + if (!circle_existence_predicate_.sss(site1, site2, site3)) + return false; + circle_formation_functor_.sss(site1, site2, site3, circle); + } + } + } + if (lies_outside_vertical_segment(circle, site1) || + lies_outside_vertical_segment(circle, site2) || + lies_outside_vertical_segment(circle, site3)) { + return false; + } + return true; + } + + private: + bool lies_outside_vertical_segment( + const circle_type& c, const site_type& s) { + if (!s.is_segment() || !is_vertical(s)) { + return false; + } + fpt_type y0 = to_fpt(s.is_inverse() ? s.y1() : s.y0()); + fpt_type y1 = to_fpt(s.is_inverse() ? s.y0() : s.y1()); + return ulp_cmp(c.y(), y0, ULPS) == ulp_cmp_type::LESS || + ulp_cmp(c.y(), y1, ULPS) == ulp_cmp_type::MORE; + } + + private: + to_fpt_converter to_fpt; + ulp_cmp_type ulp_cmp; + circle_existence_predicate_type circle_existence_predicate_; + circle_formation_functor_type circle_formation_functor_; + }; +}; +} // detail +} // polygon +} // boost + +#endif // BOOST_POLYGON_DETAIL_VORONOI_PREDICATES diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_robust_fpt.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_robust_fpt.hpp new file mode 100644 index 0000000..ce72ba8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_robust_fpt.hpp @@ -0,0 +1,501 @@ +// Boost.Polygon library detail/voronoi_robust_fpt.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_DETAIL_VORONOI_ROBUST_FPT +#define BOOST_POLYGON_DETAIL_VORONOI_ROBUST_FPT + +#include +#include + +// Geometry predicates with floating-point variables usually require +// high-precision predicates to retrieve the correct result. +// Epsilon robust predicates give the result within some epsilon relative +// error, but are a lot faster than high-precision predicates. +// To make algorithm robust and efficient epsilon robust predicates are +// used at the first step. In case of the undefined result high-precision +// arithmetic is used to produce required robustness. This approach +// requires exact computation of epsilon intervals within which epsilon +// robust predicates have undefined value. +// There are two ways to measure an error of floating-point calculations: +// relative error and ULPs (units in the last place). +// Let EPS be machine epsilon, then next inequalities have place: +// 1 EPS <= 1 ULP <= 2 EPS (1), 0.5 ULP <= 1 EPS <= 1 ULP (2). +// ULPs are good for measuring rounding errors and comparing values. +// Relative errors are good for computation of general relative +// error of formulas or expressions. So to calculate epsilon +// interval within which epsilon robust predicates have undefined result +// next schema is used: +// 1) Compute rounding errors of initial variables using ULPs; +// 2) Transform ULPs to epsilons using upper bound of the (1); +// 3) Compute relative error of the formula using epsilon arithmetic; +// 4) Transform epsilon to ULPs using upper bound of the (2); +// In case two values are inside undefined ULP range use high-precision +// arithmetic to produce the correct result, else output the result. +// Look at almost_equal function to see how two floating-point variables +// are checked to fit in the ULP range. +// If A has relative error of r(A) and B has relative error of r(B) then: +// 1) r(A + B) <= max(r(A), r(B)), for A * B >= 0; +// 2) r(A - B) <= B*r(A)+A*r(B)/(A-B), for A * B >= 0; +// 2) r(A * B) <= r(A) + r(B); +// 3) r(A / B) <= r(A) + r(B); +// In addition rounding error should be added, that is always equal to +// 0.5 ULP or at most 1 epsilon. As you might see from the above formulas +// subtraction relative error may be extremely large, that's why +// epsilon robust comparator class is used to store floating point values +// and compute subtraction as the final step of the evaluation. +// For further information about relative errors and ULPs try this link: +// http://docs.sun.com/source/806-3568/ncg_goldberg.html + +namespace boost { +namespace polygon { +namespace detail { + +template +T get_sqrt(const T& that) { + return (std::sqrt)(that); +} + +template +bool is_pos(const T& that) { + return that > 0; +} + +template +bool is_neg(const T& that) { + return that < 0; +} + +template +bool is_zero(const T& that) { + return that == 0; +} + +template +class robust_fpt { + public: + typedef _fpt floating_point_type; + typedef _fpt relative_error_type; + + // Rounding error is at most 1 EPS. + enum { + ROUNDING_ERROR = 1 + }; + + robust_fpt() : fpv_(0.0), re_(0.0) {} + explicit robust_fpt(floating_point_type fpv) : + fpv_(fpv), re_(0.0) {} + robust_fpt(floating_point_type fpv, relative_error_type error) : + fpv_(fpv), re_(error) {} + + floating_point_type fpv() const { return fpv_; } + relative_error_type re() const { return re_; } + relative_error_type ulp() const { return re_; } + + bool has_pos_value() const { + return is_pos(fpv_); + } + + bool has_neg_value() const { + return is_neg(fpv_); + } + + bool has_zero_value() const { + return is_zero(fpv_); + } + + robust_fpt operator-() const { + return robust_fpt(-fpv_, re_); + } + + robust_fpt& operator+=(const robust_fpt& that) { + floating_point_type fpv = this->fpv_ + that.fpv_; + if ((!is_neg(this->fpv_) && !is_neg(that.fpv_)) || + (!is_pos(this->fpv_) && !is_pos(that.fpv_))) { + this->re_ = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; + } else { + floating_point_type temp = + (this->fpv_ * this->re_ - that.fpv_ * that.re_) / fpv; + if (is_neg(temp)) + temp = -temp; + this->re_ = temp + ROUNDING_ERROR; + } + this->fpv_ = fpv; + return *this; + } + + robust_fpt& operator-=(const robust_fpt& that) { + floating_point_type fpv = this->fpv_ - that.fpv_; + if ((!is_neg(this->fpv_) && !is_pos(that.fpv_)) || + (!is_pos(this->fpv_) && !is_neg(that.fpv_))) { + this->re_ = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; + } else { + floating_point_type temp = + (this->fpv_ * this->re_ + that.fpv_ * that.re_) / fpv; + if (is_neg(temp)) + temp = -temp; + this->re_ = temp + ROUNDING_ERROR; + } + this->fpv_ = fpv; + return *this; + } + + robust_fpt& operator*=(const robust_fpt& that) { + this->re_ += that.re_ + ROUNDING_ERROR; + this->fpv_ *= that.fpv_; + return *this; + } + + robust_fpt& operator/=(const robust_fpt& that) { + this->re_ += that.re_ + ROUNDING_ERROR; + this->fpv_ /= that.fpv_; + return *this; + } + + robust_fpt operator+(const robust_fpt& that) const { + floating_point_type fpv = this->fpv_ + that.fpv_; + relative_error_type re; + if ((!is_neg(this->fpv_) && !is_neg(that.fpv_)) || + (!is_pos(this->fpv_) && !is_pos(that.fpv_))) { + re = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; + } else { + floating_point_type temp = + (this->fpv_ * this->re_ - that.fpv_ * that.re_) / fpv; + if (is_neg(temp)) + temp = -temp; + re = temp + ROUNDING_ERROR; + } + return robust_fpt(fpv, re); + } + + robust_fpt operator-(const robust_fpt& that) const { + floating_point_type fpv = this->fpv_ - that.fpv_; + relative_error_type re; + if ((!is_neg(this->fpv_) && !is_pos(that.fpv_)) || + (!is_pos(this->fpv_) && !is_neg(that.fpv_))) { + re = (std::max)(this->re_, that.re_) + ROUNDING_ERROR; + } else { + floating_point_type temp = + (this->fpv_ * this->re_ + that.fpv_ * that.re_) / fpv; + if (is_neg(temp)) + temp = -temp; + re = temp + ROUNDING_ERROR; + } + return robust_fpt(fpv, re); + } + + robust_fpt operator*(const robust_fpt& that) const { + floating_point_type fpv = this->fpv_ * that.fpv_; + relative_error_type re = this->re_ + that.re_ + ROUNDING_ERROR; + return robust_fpt(fpv, re); + } + + robust_fpt operator/(const robust_fpt& that) const { + floating_point_type fpv = this->fpv_ / that.fpv_; + relative_error_type re = this->re_ + that.re_ + ROUNDING_ERROR; + return robust_fpt(fpv, re); + } + + robust_fpt sqrt() const { + return robust_fpt(get_sqrt(fpv_), + re_ * static_cast(0.5) + + ROUNDING_ERROR); + } + + private: + floating_point_type fpv_; + relative_error_type re_; +}; + +template +robust_fpt get_sqrt(const robust_fpt& that) { + return that.sqrt(); +} + +template +bool is_pos(const robust_fpt& that) { + return that.has_pos_value(); +} + +template +bool is_neg(const robust_fpt& that) { + return that.has_neg_value(); +} + +template +bool is_zero(const robust_fpt& that) { + return that.has_zero_value(); +} + +// robust_dif consists of two not negative values: value1 and value2. +// The resulting expression is equal to the value1 - value2. +// Subtraction of a positive value is equivalent to the addition to value2 +// and subtraction of a negative value is equivalent to the addition to +// value1. The structure implicitly avoids difference computation. +template +class robust_dif { + public: + robust_dif() : + positive_sum_(0), + negative_sum_(0) {} + + explicit robust_dif(const T& value) : + positive_sum_((value > 0)?value:0), + negative_sum_((value < 0)?-value:0) {} + + robust_dif(const T& pos, const T& neg) : + positive_sum_(pos), + negative_sum_(neg) {} + + T dif() const { + return positive_sum_ - negative_sum_; + } + + T pos() const { + return positive_sum_; + } + + T neg() const { + return negative_sum_; + } + + robust_dif operator-() const { + return robust_dif(negative_sum_, positive_sum_); + } + + robust_dif& operator+=(const T& val) { + if (!is_neg(val)) + positive_sum_ += val; + else + negative_sum_ -= val; + return *this; + } + + robust_dif& operator+=(const robust_dif& that) { + positive_sum_ += that.positive_sum_; + negative_sum_ += that.negative_sum_; + return *this; + } + + robust_dif& operator-=(const T& val) { + if (!is_neg(val)) + negative_sum_ += val; + else + positive_sum_ -= val; + return *this; + } + + robust_dif& operator-=(const robust_dif& that) { + positive_sum_ += that.negative_sum_; + negative_sum_ += that.positive_sum_; + return *this; + } + + robust_dif& operator*=(const T& val) { + if (!is_neg(val)) { + positive_sum_ *= val; + negative_sum_ *= val; + } else { + positive_sum_ *= -val; + negative_sum_ *= -val; + swap(); + } + return *this; + } + + robust_dif& operator*=(const robust_dif& that) { + T positive_sum = this->positive_sum_ * that.positive_sum_ + + this->negative_sum_ * that.negative_sum_; + T negative_sum = this->positive_sum_ * that.negative_sum_ + + this->negative_sum_ * that.positive_sum_; + positive_sum_ = positive_sum; + negative_sum_ = negative_sum; + return *this; + } + + robust_dif& operator/=(const T& val) { + if (!is_neg(val)) { + positive_sum_ /= val; + negative_sum_ /= val; + } else { + positive_sum_ /= -val; + negative_sum_ /= -val; + swap(); + } + return *this; + } + + private: + void swap() { + (std::swap)(positive_sum_, negative_sum_); + } + + T positive_sum_; + T negative_sum_; +}; + +template +robust_dif operator+(const robust_dif& lhs, + const robust_dif& rhs) { + return robust_dif(lhs.pos() + rhs.pos(), lhs.neg() + rhs.neg()); +} + +template +robust_dif operator+(const robust_dif& lhs, const T& rhs) { + if (!is_neg(rhs)) { + return robust_dif(lhs.pos() + rhs, lhs.neg()); + } else { + return robust_dif(lhs.pos(), lhs.neg() - rhs); + } +} + +template +robust_dif operator+(const T& lhs, const robust_dif& rhs) { + if (!is_neg(lhs)) { + return robust_dif(lhs + rhs.pos(), rhs.neg()); + } else { + return robust_dif(rhs.pos(), rhs.neg() - lhs); + } +} + +template +robust_dif operator-(const robust_dif& lhs, + const robust_dif& rhs) { + return robust_dif(lhs.pos() + rhs.neg(), lhs.neg() + rhs.pos()); +} + +template +robust_dif operator-(const robust_dif& lhs, const T& rhs) { + if (!is_neg(rhs)) { + return robust_dif(lhs.pos(), lhs.neg() + rhs); + } else { + return robust_dif(lhs.pos() - rhs, lhs.neg()); + } +} + +template +robust_dif operator-(const T& lhs, const robust_dif& rhs) { + if (!is_neg(lhs)) { + return robust_dif(lhs + rhs.neg(), rhs.pos()); + } else { + return robust_dif(rhs.neg(), rhs.pos() - lhs); + } +} + +template +robust_dif operator*(const robust_dif& lhs, + const robust_dif& rhs) { + T res_pos = lhs.pos() * rhs.pos() + lhs.neg() * rhs.neg(); + T res_neg = lhs.pos() * rhs.neg() + lhs.neg() * rhs.pos(); + return robust_dif(res_pos, res_neg); +} + +template +robust_dif operator*(const robust_dif& lhs, const T& val) { + if (!is_neg(val)) { + return robust_dif(lhs.pos() * val, lhs.neg() * val); + } else { + return robust_dif(-lhs.neg() * val, -lhs.pos() * val); + } +} + +template +robust_dif operator*(const T& val, const robust_dif& rhs) { + if (!is_neg(val)) { + return robust_dif(val * rhs.pos(), val * rhs.neg()); + } else { + return robust_dif(-val * rhs.neg(), -val * rhs.pos()); + } +} + +template +robust_dif operator/(const robust_dif& lhs, const T& val) { + if (!is_neg(val)) { + return robust_dif(lhs.pos() / val, lhs.neg() / val); + } else { + return robust_dif(-lhs.neg() / val, -lhs.pos() / val); + } +} + +// Used to compute expressions that operate with sqrts with predefined +// relative error. Evaluates expressions of the next type: +// sum(i = 1 .. n)(A[i] * sqrt(B[i])), 1 <= n <= 4. +template +class robust_sqrt_expr { + public: + enum MAX_RELATIVE_ERROR { + MAX_RELATIVE_ERROR_EVAL1 = 4, + MAX_RELATIVE_ERROR_EVAL2 = 7, + MAX_RELATIVE_ERROR_EVAL3 = 16, + MAX_RELATIVE_ERROR_EVAL4 = 25 + }; + + // Evaluates expression (re = 4 EPS): + // A[0] * sqrt(B[0]). + _fpt eval1(_int* A, _int* B) { + _fpt a = convert(A[0]); + _fpt b = convert(B[0]); + return a * get_sqrt(b); + } + + // Evaluates expression (re = 7 EPS): + // A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]). + _fpt eval2(_int* A, _int* B) { + _fpt a = eval1(A, B); + _fpt b = eval1(A + 1, B + 1); + if ((!is_neg(a) && !is_neg(b)) || + (!is_pos(a) && !is_pos(b))) + return a + b; + return convert(A[0] * A[0] * B[0] - A[1] * A[1] * B[1]) / (a - b); + } + + // Evaluates expression (re = 16 EPS): + // A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]) + A[2] * sqrt(B[2]). + _fpt eval3(_int* A, _int* B) { + _fpt a = eval2(A, B); + _fpt b = eval1(A + 2, B + 2); + if ((!is_neg(a) && !is_neg(b)) || + (!is_pos(a) && !is_pos(b))) + return a + b; + tA[3] = A[0] * A[0] * B[0] + A[1] * A[1] * B[1] - A[2] * A[2] * B[2]; + tB[3] = 1; + tA[4] = A[0] * A[1] * 2; + tB[4] = B[0] * B[1]; + return eval2(tA + 3, tB + 3) / (a - b); + } + + + // Evaluates expression (re = 25 EPS): + // A[0] * sqrt(B[0]) + A[1] * sqrt(B[1]) + + // A[2] * sqrt(B[2]) + A[3] * sqrt(B[3]). + _fpt eval4(_int* A, _int* B) { + _fpt a = eval2(A, B); + _fpt b = eval2(A + 2, B + 2); + if ((!is_neg(a) && !is_neg(b)) || + (!is_pos(a) && !is_pos(b))) + return a + b; + tA[0] = A[0] * A[0] * B[0] + A[1] * A[1] * B[1] - + A[2] * A[2] * B[2] - A[3] * A[3] * B[3]; + tB[0] = 1; + tA[1] = A[0] * A[1] * 2; + tB[1] = B[0] * B[1]; + tA[2] = A[2] * A[3] * -2; + tB[2] = B[2] * B[3]; + return eval3(tA, tB) / (a - b); + } + + private: + _int tA[5]; + _int tB[5]; + _converter convert; +}; +} // detail +} // polygon +} // boost + +#endif // BOOST_POLYGON_DETAIL_VORONOI_ROBUST_FPT diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_structures.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_structures.hpp new file mode 100644 index 0000000..f37a454 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/detail/voronoi_structures.hpp @@ -0,0 +1,450 @@ +// Boost.Polygon library detail/voronoi_structures.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_DETAIL_VORONOI_STRUCTURES +#define BOOST_POLYGON_DETAIL_VORONOI_STRUCTURES + +#include +#include +#include + +#include "boost/polygon/voronoi_geometry_type.hpp" + +namespace boost { +namespace polygon { +namespace detail { +// Cartesian 2D point data structure. +template +class point_2d { + public: + typedef T coordinate_type; + + point_2d() {} + + point_2d(coordinate_type x, coordinate_type y) : + x_(x), + y_(y) {} + + bool operator==(const point_2d& that) const { + return (this->x_ == that.x()) && (this->y_ == that.y()); + } + + bool operator!=(const point_2d& that) const { + return (this->x_ != that.x()) || (this->y_ != that.y()); + } + + coordinate_type x() const { + return x_; + } + + coordinate_type y() const { + return y_; + } + + point_2d& x(coordinate_type x) { + x_ = x; + return *this; + } + + point_2d& y(coordinate_type y) { + y_ = y; + return *this; + } + + private: + coordinate_type x_; + coordinate_type y_; +}; + +// Site event type. +// Occurs when the sweepline sweeps over one of the initial sites: +// 1) point site +// 2) start-point of the segment site +// 3) endpoint of the segment site +// Implicit segment direction is defined: the start-point of +// the segment compares less than its endpoint. +// Each input segment is divided onto two site events: +// 1) One going from the start-point to the endpoint +// (is_inverse() = false) +// 2) Another going from the endpoint to the start-point +// (is_inverse() = true) +// In beach line data structure segment sites of the first +// type precede sites of the second type for the same segment. +// Members: +// point0_ - point site or segment's start-point +// point1_ - segment's endpoint if site is a segment +// sorted_index_ - the last bit encodes information if the site is inverse; +// the other bits encode site event index among the sorted site events +// initial_index_ - site index among the initial input set +// Note: for all sites is_inverse_ flag is equal to false by default. +template +class site_event { + public: + typedef T coordinate_type; + typedef point_2d point_type; + + site_event() : + point0_(0, 0), + point1_(0, 0), + sorted_index_(0), + flags_(0) {} + + site_event(coordinate_type x, coordinate_type y) : + point0_(x, y), + point1_(x, y), + sorted_index_(0), + flags_(0) {} + + explicit site_event(const point_type& point) : + point0_(point), + point1_(point), + sorted_index_(0), + flags_(0) {} + + site_event(coordinate_type x1, coordinate_type y1, + coordinate_type x2, coordinate_type y2): + point0_(x1, y1), + point1_(x2, y2), + sorted_index_(0), + flags_(0) {} + + site_event(const point_type& point1, const point_type& point2) : + point0_(point1), + point1_(point2), + sorted_index_(0), + flags_(0) {} + + bool operator==(const site_event& that) const { + return (this->point0_ == that.point0_) && + (this->point1_ == that.point1_); + } + + bool operator!=(const site_event& that) const { + return (this->point0_ != that.point0_) || + (this->point1_ != that.point1_); + } + + coordinate_type x() const { + return point0_.x(); + } + + coordinate_type y() const { + return point0_.y(); + } + + coordinate_type x0() const { + return point0_.x(); + } + + coordinate_type y0() const { + return point0_.y(); + } + + coordinate_type x1() const { + return point1_.x(); + } + + coordinate_type y1() const { + return point1_.y(); + } + + const point_type& point0() const { + return point0_; + } + + const point_type& point1() const { + return point1_; + } + + std::size_t sorted_index() const { + return sorted_index_; + } + + site_event& sorted_index(std::size_t index) { + sorted_index_ = index; + return *this; + } + + std::size_t initial_index() const { + return initial_index_; + } + + site_event& initial_index(std::size_t index) { + initial_index_ = index; + return *this; + } + + bool is_inverse() const { + return (flags_ & IS_INVERSE) ? true : false; + } + + site_event& inverse() { + std::swap(point0_, point1_); + flags_ ^= IS_INVERSE; + return *this; + } + + SourceCategory source_category() const { + return static_cast(flags_ & SOURCE_CATEGORY_BITMASK); + } + + site_event& source_category(SourceCategory source_category) { + flags_ |= source_category; + return *this; + } + + bool is_point() const { + return (point0_.x() == point1_.x()) && (point0_.y() == point1_.y()); + } + + bool is_segment() const { + return (point0_.x() != point1_.x()) || (point0_.y() != point1_.y()); + } + + private: + enum Bits { + IS_INVERSE = 0x20 // 32 + }; + + point_type point0_; + point_type point1_; + std::size_t sorted_index_; + std::size_t initial_index_; + std::size_t flags_; +}; + +// Circle event type. +// Occurs when the sweepline sweeps over the rightmost point of the Voronoi +// circle (with the center at the intersection point of the bisectors). +// Circle event is made of the two consecutive nodes in the beach line data +// structure. In case another node was inserted during algorithm execution +// between the given two nodes circle event becomes inactive. +// Variables: +// center_x_ - center x-coordinate; +// center_y_ - center y-coordinate; +// lower_x_ - leftmost x-coordinate; +// is_active_ - states whether circle event is still active. +// NOTE: lower_y coordinate is always equal to center_y. +template +class circle_event { + public: + typedef T coordinate_type; + + circle_event() : is_active_(true) {} + + circle_event(coordinate_type c_x, + coordinate_type c_y, + coordinate_type lower_x) : + center_x_(c_x), + center_y_(c_y), + lower_x_(lower_x), + is_active_(true) {} + + coordinate_type x() const { + return center_x_; + } + + circle_event& x(coordinate_type center_x) { + center_x_ = center_x; + return *this; + } + + coordinate_type y() const { + return center_y_; + } + + circle_event& y(coordinate_type center_y) { + center_y_ = center_y; + return *this; + } + + coordinate_type lower_x() const { + return lower_x_; + } + + circle_event& lower_x(coordinate_type lower_x) { + lower_x_ = lower_x; + return *this; + } + + coordinate_type lower_y() const { + return center_y_; + } + + bool is_active() const { + return is_active_; + } + + circle_event& deactivate() { + is_active_ = false; + return *this; + } + + private: + coordinate_type center_x_; + coordinate_type center_y_; + coordinate_type lower_x_; + bool is_active_; +}; + +// Event queue data structure, holds circle events. +// During algorithm run, some of the circle events disappear (become +// inactive). Priority queue data structure doesn't support +// iterators (there is no direct ability to modify its elements). +// Instead list is used to store all the circle events and priority queue +// of the iterators to the list elements is used to keep the correct circle +// events ordering. +template +class ordered_queue { + public: + ordered_queue() {} + + bool empty() const { + return c_.empty(); + } + + const T &top() const { + return *c_.top(); + } + + void pop() { + list_iterator_type it = c_.top(); + c_.pop(); + c_list_.erase(it); + } + + T &push(const T &e) { + c_list_.push_front(e); + c_.push(c_list_.begin()); + return c_list_.front(); + } + + void clear() { + while (!c_.empty()) + c_.pop(); + c_list_.clear(); + } + + private: + typedef typename std::list::iterator list_iterator_type; + + struct comparison { + bool operator() (const list_iterator_type &it1, + const list_iterator_type &it2) const { + return cmp_(*it1, *it2); + } + Predicate cmp_; + }; + + std::priority_queue< list_iterator_type, + std::vector, + comparison > c_; + std::list c_list_; + + // Disallow copy constructor and operator= + ordered_queue(const ordered_queue&); + void operator=(const ordered_queue&); +}; + +// Represents a bisector node made by two arcs that correspond to the left +// and right sites. Arc is defined as a curve with points equidistant from +// the site and from the sweepline. If the site is a point then arc is +// a parabola, otherwise it's a line segment. A segment site event will +// produce different bisectors based on its direction. +// In general case two sites will create two opposite bisectors. That's +// why the order of the sites is important to define the unique bisector. +// The one site is considered to be newer than the other one if it was +// processed by the algorithm later (has greater index). +template +class beach_line_node_key { + public: + typedef Site site_type; + + // Constructs degenerate bisector, used to search an arc that is above + // the given site. The input to the constructor is the new site point. + explicit beach_line_node_key(const site_type &new_site) : + left_site_(new_site), + right_site_(new_site) {} + + // Constructs a new bisector. The input to the constructor is the two + // sites that create the bisector. The order of sites is important. + beach_line_node_key(const site_type &left_site, + const site_type &right_site) : + left_site_(left_site), + right_site_(right_site) {} + + const site_type &left_site() const { + return left_site_; + } + + site_type &left_site() { + return left_site_; + } + + beach_line_node_key& left_site(const site_type &site) { + left_site_ = site; + return *this; + } + + const site_type &right_site() const { + return right_site_; + } + + site_type &right_site() { + return right_site_; + } + + beach_line_node_key& right_site(const site_type &site) { + right_site_ = site; + return *this; + } + + private: + site_type left_site_; + site_type right_site_; +}; + +// Represents edge data structure from the Voronoi output, that is +// associated as a value with beach line bisector in the beach +// line. Contains pointer to the circle event in the circle event +// queue if the edge corresponds to the right bisector of the circle event. +template +class beach_line_node_data { + public: + explicit beach_line_node_data(Edge* new_edge) : + circle_event_(NULL), + edge_(new_edge) {} + + Circle* circle_event() const { + return circle_event_; + } + + beach_line_node_data& circle_event(Circle* circle_event) { + circle_event_ = circle_event; + return *this; + } + + Edge* edge() const { + return edge_; + } + + beach_line_node_data& edge(Edge* new_edge) { + edge_ = new_edge; + return *this; + } + + private: + Circle* circle_event_; + Edge* edge_; +}; +} // detail +} // polygon +} // boost + +#endif // BOOST_POLYGON_DETAIL_VORONOI_STRUCTURES diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gmp_override.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gmp_override.hpp new file mode 100644 index 0000000..322e05d --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gmp_override.hpp @@ -0,0 +1,128 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_GMP_OVERRIDE_HPP +#define BOOST_POLYGON_GMP_OVERRIDE_HPP +#include +namespace boost { namespace polygon { + + class gmp_int { + private: + inline gmp_int(const mpq_class& input) : v_(input) {} + public: + inline gmp_int() {} + explicit inline gmp_int(long input) : v_(input) {} + inline gmp_int(const gmp_int& input) : v_(input.v_) {} + inline gmp_int& operator=(const gmp_int& that) { + v_ = that.v_; + return (*this); + } + inline gmp_int& operator=(long that) { + v_ = that; + return (*this); + } + inline operator int() const { + std::cout << "cast\n"; + mpz_class num = v_.get_num(); + mpz_class den = v_.get_den(); + num /= den; + return num.get_si(); + } + inline double get_d() const { + return v_.get_d(); + } + inline int get_num() const { + return v_.get_num().get_si(); + } + inline int get_den() const { + return v_.get_den().get_si(); + } + inline bool operator==(const gmp_int& that) const { + return v_ == that.v_; + } + inline bool operator!=(const gmp_int& that) const { + return v_ != that.v_; + } + inline bool operator<(const gmp_int& that) const { + bool retval = v_ < that.v_; + return retval; + } + inline bool operator<=(const gmp_int& that) const { + return v_ <= that.v_; + } + inline bool operator>(const gmp_int& that) const { + return v_ > that.v_; + } + inline bool operator>=(const gmp_int& that) const { + return v_ >= that.v_; + } + inline gmp_int operator+(const gmp_int& b) { + return gmp_int((*this).v_ + b.v_); + } + inline gmp_int operator-(const gmp_int& b) { + return gmp_int((*this).v_ - b.v_); + } + inline gmp_int operator*(const gmp_int& b) { + return gmp_int((*this).v_ * b.v_); + } + inline gmp_int operator/(const gmp_int& b) { + return gmp_int((*this).v_ / b.v_); + } + inline gmp_int& operator+=(const gmp_int& b) { + (*this).v_ += b.v_; + return (*this); + } + inline gmp_int& operator-=(const gmp_int& b) { + (*this).v_ -= b.v_; + return (*this); + } + inline gmp_int& operator*=(const gmp_int& b) { + (*this).v_ *= b.v_; + return (*this); + } + inline gmp_int& operator/=(const gmp_int& b) { + (*this).v_ /= b.v_; + return (*this); + } + inline gmp_int& operator++() { + ++v_; + return (*this); + } + inline gmp_int& operator--() { + --v_; + return (*this); + } + inline gmp_int operator++(int) { + gmp_int retval(*this); + ++(*this); + return retval; + } + inline gmp_int operator--(int) { + gmp_int retval(*this); + --(*this); + return retval; + } + private: + mpq_class v_; + }; + + template <> + struct high_precision_type { + typedef mpq_class type; + }; + + template <> + int convert_high_precision_type(const mpq_class& v) { + mpz_class num = v.get_num(); + mpz_class den = v.get_den(); + num /= den; + return num.get_si(); + }; + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gtl.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gtl.hpp new file mode 100644 index 0000000..3a98d26 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/gtl.hpp @@ -0,0 +1,35 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_GTL_HPP +#define BOOST_POLYGON_GTL_HPP + +#ifdef __ICC +#pragma warning (push) +#pragma warning (disable:1125) +#endif + +#ifdef WIN32 +#pragma warning (push) +#pragma warning( disable: 4996 ) +#pragma warning( disable: 4800 ) +#endif + +#define BOOST_POLYGON_NO_DEPS +#include "polygon.hpp" +namespace gtl = boost::polygon; +using namespace boost::polygon::operators; + +#ifdef WIN32 +#pragma warning (pop) +#endif + +#ifdef __ICC +#pragma warning (pop) +#endif + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_concept.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_concept.hpp new file mode 100644 index 0000000..3eeec0f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_concept.hpp @@ -0,0 +1,934 @@ +// Boost.Polygon library interval_concept.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_INTERVAL_CONCEPT_HPP +#define BOOST_POLYGON_INTERVAL_CONCEPT_HPP + +#include "isotropy.hpp" +#include "interval_traits.hpp" + +namespace boost { +namespace polygon { + +struct interval_concept {}; + +template +struct is_interval_concept { + typedef gtl_no type; +}; + +template <> +struct is_interval_concept { + typedef gtl_yes type; +}; + +template +struct is_mutable_interval_concept { + typedef gtl_no type; +}; + +template <> +struct is_mutable_interval_concept { + typedef gtl_yes type; +}; + +template +struct interval_coordinate_type_by_concept { + typedef void type; +}; + +template +struct interval_coordinate_type_by_concept { + typedef typename interval_traits::coordinate_type type; +}; + +template +struct interval_coordinate_type { + typedef typename interval_coordinate_type_by_concept< + GeometryType, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +template +struct interval_difference_type_by_concept { + typedef void type; +}; + +template +struct interval_difference_type_by_concept { + typedef typename coordinate_traits< + typename interval_traits::coordinate_type + >::coordinate_difference type; +}; + +template +struct interval_difference_type { + typedef typename interval_difference_type_by_concept< + GeometryType, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +struct y_i_get : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_get, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + typename interval_coordinate_type::type +>::type get(const IntervalType& interval, direction_1d dir) { + return interval_traits::get(interval, dir); +} + +struct y_i_set : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_set, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + void +>::type set(IntervalType& interval, direction_1d dir, + typename interval_mutable_traits::coordinate_type value) { + interval_mutable_traits::set(interval, dir, value); +} + +struct y_i_construct : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_construct, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type construct( + typename interval_mutable_traits::coordinate_type low, + typename interval_mutable_traits::coordinate_type high) { + if (low > high) { + (std::swap)(low, high); + } + return interval_mutable_traits::construct(low, high); +} + +struct y_i_copy_construct : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_copy_construct, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType1 +>::type copy_construct(const IntervalType2& interval) { + return construct(get(interval, LOW), get(interval, HIGH)); +} + +struct y_i_assign : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_assign, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType1 +>::type& assign(IntervalType1& lvalue, const IntervalType2& rvalue) { + set(lvalue, LOW, get(rvalue, LOW)); + set(lvalue, HIGH, get(rvalue, HIGH)); + return lvalue; +} + +struct y_i_low : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_low, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + typename interval_coordinate_type::type +>::type low(const IntervalType& interval) { + return get(interval, LOW); +} + +struct y_i_high : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_high, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + typename interval_coordinate_type::type +>::type high(const IntervalType& interval) { + return get(interval, HIGH); +} + +struct y_i_low2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_low2, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + void +>::type low(IntervalType& interval, + typename interval_mutable_traits::coordinate_type value) { + set(interval, LOW, value); +} + +struct y_i_high2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_high2, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + void +>::type high(IntervalType& interval, + typename interval_mutable_traits::coordinate_type value) { + set(interval, HIGH, value); +} + +struct y_i_equivalence : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_equivalence, + typename is_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type equivalence( + const IntervalType1& interval1, + const IntervalType2& interval2) { + return (get(interval1, LOW) == get(interval2, LOW)) && + (get(interval1, HIGH) == get(interval2, HIGH)); +} + +struct y_i_contains : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_contains, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type contains( + const IntervalType& interval, + typename interval_coordinate_type::type value, + bool consider_touch = true ) { + if (consider_touch) { + return value <= high(interval) && value >= low(interval); + } else { + return value < high(interval) && value > low(interval); + } +} + +struct y_i_contains2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_contains2, + typename is_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type contains( + const IntervalType1& interval1, + const IntervalType2& interval2, + bool consider_touch = true) { + return contains(interval1, get(interval2, LOW), consider_touch) && + contains(interval1, get(interval2, HIGH), consider_touch); +} + +struct y_i_center : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_center, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + typename interval_coordinate_type::type +>::type center(const IntervalType& interval) { + return (high(interval) + low(interval)) / 2; +} + +struct y_i_delta : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_delta, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + typename interval_difference_type::type +>::type delta(const IntervalType& interval) { + typedef typename interval_difference_type::type diff_type; + return static_cast(high(interval)) - + static_cast(low(interval)); +} + +struct y_i_flip : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_flip, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, +IntervalType>::type& flip( + IntervalType& interval, + typename interval_coordinate_type::type axis = 0) { + typename interval_coordinate_type::type newLow, newHigh; + newLow = 2 * axis - high(interval); + newHigh = 2 * axis - low(interval); + low(interval, newLow); + high(interval, newHigh); + return interval; +} + +struct y_i_scale_up : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_scale_up, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& scale_up( + IntervalType& interval, + typename interval_coordinate_type::type factor) { + typename interval_coordinate_type::type newHigh = + high(interval) * factor; + low(interval, low(interval) * factor); + high(interval, (newHigh)); + return interval; +} + +struct y_i_scale_down : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_scale_down, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& scale_down( + IntervalType& interval, + typename interval_coordinate_type::type factor) { + typename interval_coordinate_type::type newHigh = + high(interval) / factor; + low(interval, low(interval) / factor); + high(interval, (newHigh)); + return interval; +} + +// TODO(asydorchuk): Deprecated. +struct y_i_scale : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_scale, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& scale(IntervalType& interval, double factor) { + typedef typename interval_coordinate_type::type Unit; + Unit newHigh = scaling_policy::round( + static_cast(high(interval)) * factor); + low(interval, scaling_policy::round( + static_cast(low(interval)) * factor)); + high(interval, (newHigh)); + return interval; +} + +struct y_i_move : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_move, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& move( + IntervalType& interval, + typename interval_difference_type::type displacement) { + typedef typename interval_coordinate_type::type ctype; + typedef typename coordinate_traits::coordinate_difference Unit; + low(interval, static_cast( + static_cast(low(interval)) + displacement)); + high(interval, static_cast( + static_cast(high(interval)) + displacement)); + return interval; +} + +struct y_i_convolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_convolve, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& convolve( + IntervalType& interval, + typename interval_coordinate_type::type value) { + typedef typename interval_coordinate_type::type Unit; + Unit newLow = low(interval) + value; + Unit newHigh = high(interval) + value; + low(interval, newLow); + high(interval, newHigh); + return interval; +} + +struct y_i_deconvolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_deconvolve, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& deconvolve( + IntervalType& interval, + typename interval_coordinate_type::type value) { + typedef typename interval_coordinate_type::type Unit; + Unit newLow = low(interval) - value; + Unit newHigh = high(interval) - value; + low(interval, newLow); + high(interval, newHigh); + return interval; +} + +struct y_i_convolve2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_convolve2, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType1 +>::type& convolve(IntervalType1& lvalue, const IntervalType2& rvalue) { + typedef typename interval_coordinate_type::type Unit; + Unit newLow = low(lvalue) + low(rvalue); + Unit newHigh = high(lvalue) + high(rvalue); + low(lvalue, newLow); + high(lvalue, newHigh); + return lvalue; +} + +struct y_i_deconvolve2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_deconvolve2, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType1 +>::type& deconvolve(IntervalType1& lvalue, const IntervalType2& rvalue) { + typedef typename interval_coordinate_type::type Unit; + Unit newLow = low(lvalue) - low(rvalue); + Unit newHigh = high(lvalue) - high(rvalue); + low(lvalue, newLow); + high(lvalue, newHigh); + return lvalue; +} + +struct y_i_reconvolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_reconvolve, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType1 +>::type& reflected_convolve( + IntervalType1& lvalue, + const IntervalType2& rvalue) { + typedef typename interval_coordinate_type::type Unit; + Unit newLow = low(lvalue) - high(rvalue); + Unit newHigh = high(lvalue) - low(rvalue); + low(lvalue, newLow); + high(lvalue, newHigh); + return lvalue; +} + +struct y_i_redeconvolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_redeconvolve, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType1 +>::type& reflected_deconvolve( + IntervalType1& lvalue, + const IntervalType2& rvalue) { + typedef typename interval_coordinate_type::type Unit; + Unit newLow = low(lvalue) + high(rvalue); + Unit newHigh = high(lvalue) + low(rvalue); + low(lvalue, newLow); + high(lvalue, newHigh); + return lvalue; +} + +struct y_i_e_dist1 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and::type + >::type + >::type, + typename interval_difference_type::type +>::type euclidean_distance( + const IntervalType& interval, + typename interval_coordinate_type::type position) { + typedef typename interval_difference_type::type Unit; + Unit dist[3] = { + 0, + (Unit)low(interval) - (Unit)position, + (Unit)position - (Unit)high(interval) + }; + return dist[(dist[1] > 0) + ((dist[2] > 0) << 1)]; +} + +struct y_i_e_dist2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_e_dist2, + typename is_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + typename interval_difference_type::type +>::type euclidean_distance( + const IntervalType1& interval1, + const IntervalType2& interval2) { + typedef typename interval_difference_type::type Unit; + Unit dist[3] = { + 0, + (Unit)low(interval1) - (Unit)high(interval2), + (Unit)low(interval2) - (Unit)high(interval1) + }; + return dist[(dist[1] > 0) + ((dist[2] > 0) << 1)]; +} + +struct y_i_e_intersects : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_e_intersects, + typename is_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type intersects( + const IntervalType1& interval1, + const IntervalType2& interval2, + bool consider_touch = true) { + return consider_touch ? + (low(interval1) <= high(interval2)) && + (high(interval1) >= low(interval2)) : + (low(interval1) < high(interval2)) && + (high(interval1) > low(interval2)); +} + +struct y_i_e_bintersect : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_e_bintersect, + typename is_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type boundaries_intersect( + const IntervalType1& interval1, + const IntervalType2& interval2, + bool consider_touch = true) { + return (contains(interval1, low(interval2), consider_touch) || + contains(interval1, high(interval2), consider_touch)) && + (contains(interval2, low(interval1), consider_touch) || + contains(interval2, high(interval1), consider_touch)); +} + +struct y_i_intersect : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_intersect, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type intersect( + IntervalType1& lvalue, + const IntervalType2& rvalue, + bool consider_touch = true) { + typedef typename interval_coordinate_type::type Unit; + Unit lowVal = (std::max)(low(lvalue), low(rvalue)); + Unit highVal = (std::min)(high(lvalue), high(rvalue)); + bool valid = consider_touch ? lowVal <= highVal : lowVal < highVal; + if (valid) { + low(lvalue, lowVal); + high(lvalue, highVal); + } + return valid; +} + +struct y_i_g_intersect : gtl_yes {}; + +// TODO(asydorchuk): Deprecated. +template +typename enable_if< + typename gtl_and_3< + y_i_g_intersect, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType1 +>::type& generalized_intersect( + IntervalType1& lvalue, + const IntervalType2& rvalue) { + typedef typename interval_coordinate_type::type Unit; + Unit coords[4] = {low(lvalue), high(lvalue), low(rvalue), high(rvalue)}; + // TODO(asydorchuk): consider implementing faster sorting of small + // fixed length range. + polygon_sort(coords, coords+4); + low(lvalue, coords[1]); + high(lvalue, coords[2]); + return lvalue; +} + +struct y_i_abuts1 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_abuts1, + typename is_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type abuts( + const IntervalType1& interval1, + const IntervalType2& interval2, + direction_1d dir) { + return dir.to_int() ? low(interval2) == high(interval1) : + low(interval1) == high(interval2); +} + +struct y_i_abuts2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_abuts2, + typename is_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type abuts( + const IntervalType1& interval1, + const IntervalType2& interval2) { + return abuts(interval1, interval2, HIGH) || + abuts(interval1, interval2, LOW); +} + +struct y_i_bloat : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_bloat, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& bloat( + IntervalType& interval, + typename interval_coordinate_type::type bloating) { + low(interval, low(interval) - bloating); + high(interval, high(interval) + bloating); + return interval; +} + +struct y_i_bloat2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_bloat2, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& bloat( + IntervalType& interval, + direction_1d dir, + typename interval_coordinate_type::type bloating) { + set(interval, dir, get(interval, dir) + dir.get_sign() * bloating); + return interval; +} + +struct y_i_shrink : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_shrink, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& shrink( + IntervalType& interval, + typename interval_coordinate_type::type shrinking) { + return bloat(interval, -shrinking); +} + +struct y_i_shrink2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_shrink2, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type& shrink( + IntervalType& interval, + direction_1d dir, + typename interval_coordinate_type::type shrinking) { + return bloat(interval, dir, -shrinking); +} + +struct y_i_encompass : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_encompass, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type encompass(IntervalType1& interval1, const IntervalType2& interval2) { + bool retval = !contains(interval1, interval2, true); + low(interval1, (std::min)(low(interval1), low(interval2))); + high(interval1, (std::max)(high(interval1), high(interval2))); + return retval; +} + +struct y_i_encompass2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_encompass2, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + bool +>::type encompass( + IntervalType& interval, + typename interval_coordinate_type::type value) { + bool retval = !contains(interval, value, true); + low(interval, (std::min)(low(interval), value)); + high(interval, (std::max)(high(interval), value)); + return retval; +} + +struct y_i_get_half : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_i_get_half, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type + >::type, + IntervalType +>::type get_half(const IntervalType& interval, direction_1d dir) { + typedef typename interval_coordinate_type::type Unit; + Unit c = (get(interval, LOW) + get(interval, HIGH)) / 2; + return construct( + (dir == LOW) ? get(interval, LOW) : c, + (dir == LOW) ? c : get(interval, HIGH)); +} + +struct y_i_join_with : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_i_join_with, + typename is_mutable_interval_concept< + typename geometry_concept::type + >::type, + typename is_interval_concept< + typename geometry_concept::type + >::type>::type, + bool +>::type join_with(IntervalType1& interval1, const IntervalType2& interval2) { + if (abuts(interval1, interval2)) { + encompass(interval1, interval2); + return true; + } + return false; +} +} // polygon +} // boost + +#endif // BOOST_POLYGON_INTERVAL_CONCEPT_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_data.hpp new file mode 100644 index 0000000..b297624 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_data.hpp @@ -0,0 +1,118 @@ +// Boost.Polygon library interval_data.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_INTERVAL_DATA_HPP +#define BOOST_POLYGON_INTERVAL_DATA_HPP + +#include "isotropy.hpp" +#include "interval_concept.hpp" + +namespace boost { +namespace polygon { + +template +class interval_data { + public: + typedef T coordinate_type; + + interval_data() +#ifndef BOOST_POLYGON_MSVC + : coords_() +#endif + {} + + interval_data(coordinate_type low, coordinate_type high) { + coords_[LOW] = low; + coords_[HIGH] = high; + } + + interval_data(const interval_data& that) { + coords_[0] = that.coords_[0]; + coords_[1] = that.coords_[1]; + } + + interval_data& operator=(const interval_data& that) { + coords_[0] = that.coords_[0]; + coords_[1] = that.coords_[1]; + return *this; + } + + template + interval_data& operator=(const IntervalType& that) { + assign(*this, that); + return *this; + } + + coordinate_type get(direction_1d dir) const { + return coords_[dir.to_int()]; + } + + void set(direction_1d dir, coordinate_type value) { + coords_[dir.to_int()] = value; + } + + coordinate_type low() const { + return coords_[0]; + } + + interval_data& low(coordinate_type value) { + coords_[LOW] = value; + return *this; + } + + coordinate_type high() const { + return coords_[1]; + } + + interval_data& high(coordinate_type value) { + coords_[HIGH] = value; + return *this; + } + + bool operator==(const interval_data& that) const { + return low() == that.low() && high() == that.high(); + } + + bool operator!=(const interval_data& that) const { + return low() != that.low() || high() != that.high(); + } + + bool operator<(const interval_data& that) const { + if (coords_[0] != that.coords_[0]) { + return coords_[0] < that.coords_[0]; + } + return coords_[1] < that.coords_[1]; + } + + bool operator<=(const interval_data& that) const { + return !(that < *this); + } + + bool operator>(const interval_data& that) const { + return that < *this; + } + + bool operator>=(const interval_data& that) const { + return !((*this) < that); + } + + private: + coordinate_type coords_[2]; +}; + +template +struct geometry_concept< interval_data > { + typedef interval_concept type; +}; +} // polygon +} // boost + +#endif // BOOST_POLYGON_INTERVAL_DATA_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_traits.hpp new file mode 100644 index 0000000..9c9062f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/interval_traits.hpp @@ -0,0 +1,47 @@ +// Boost.Polygon library interval_traits.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_INTERVAL_TRAITS_HPP +#define BOOST_POLYGON_INTERVAL_TRAITS_HPP + +#include "isotropy.hpp" + +namespace boost { +namespace polygon { + +template +struct interval_traits { + typedef Interval interval_type; + typedef typename interval_type::coordinate_type coordinate_type; + + static coordinate_type get(const interval_type& interval, direction_1d dir) { + return interval.get(dir); + } +}; + +template +struct interval_mutable_traits { + typedef Interval interval_type; + typedef typename interval_type::coordinate_type coordinate_type; + + static void set( + interval_type& interval, direction_1d dir, coordinate_type value) { + interval.set(dir, value); + } + + static interval_type construct(coordinate_type low, coordinate_type high) { + return interval_type(low, high); + } +}; +} // polygon +} // boost + +#endif // BOOST_POLICY_INTERVAL_TRAITS_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/isotropy.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/isotropy.hpp new file mode 100644 index 0000000..615ae39 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/isotropy.hpp @@ -0,0 +1,525 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ + +#ifndef BOOST_POLYGON_ISOTROPY_HPP +#define BOOST_POLYGON_ISOTROPY_HPP + +//external +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +#ifndef BOOST_POLYGON_NO_DEPS + +#include +#ifdef BOOST_MSVC +#define BOOST_POLYGON_MSVC +#endif +#ifdef BOOST_INTEL +#define BOOST_POLYGON_ICC +#endif +#ifdef BOOST_HAS_LONG_LONG +#define BOOST_POLYGON_USE_LONG_LONG +typedef boost::long_long_type polygon_long_long_type; +typedef boost::ulong_long_type polygon_ulong_long_type; +//typedef long long polygon_long_long_type; +//typedef unsigned long long polygon_ulong_long_type; +#endif +#else + +#ifdef _WIN32 +#define BOOST_POLYGON_MSVC +#endif +#ifdef __ICC +#define BOOST_POLYGON_ICC +#endif +#define BOOST_POLYGON_USE_LONG_LONG +typedef long long polygon_long_long_type; +typedef unsigned long long polygon_ulong_long_type; +#endif + +namespace boost { namespace polygon{ + + enum GEOMETRY_CONCEPT_ID { + COORDINATE_CONCEPT, + INTERVAL_CONCEPT, + POINT_CONCEPT, + POINT_3D_CONCEPT, + RECTANGLE_CONCEPT, + POLYGON_90_CONCEPT, + POLYGON_90_WITH_HOLES_CONCEPT, + POLYGON_45_CONCEPT, + POLYGON_45_WITH_HOLES_CONCEPT, + POLYGON_CONCEPT, + POLYGON_WITH_HOLES_CONCEPT, + POLYGON_90_SET_CONCEPT, + POLYGON_45_SET_CONCEPT, + POLYGON_SET_CONCEPT + }; + + struct undefined_concept {}; + + template + struct geometry_concept { typedef undefined_concept type; }; + + template + struct view_of {}; + + template + view_of view_as(const T2& obj) { return view_of(obj); } + + template + struct coordinate_traits {}; + + //used to override long double with an infinite precision datatype + template + struct high_precision_type { + typedef long double type; + }; + + template + T convert_high_precision_type(const typename high_precision_type::type& v) { + return T(v); + } + + //used to override std::sort with an alternative (parallel) algorithm + template + void polygon_sort(iter_type _b_, iter_type _e_); + + template + void polygon_sort(iter_type _b_, iter_type _e_, const pred_type& _pred_); + + + template <> + struct coordinate_traits { + typedef int coordinate_type; + typedef long double area_type; +#ifdef BOOST_POLYGON_USE_LONG_LONG + typedef polygon_long_long_type manhattan_area_type; + typedef polygon_ulong_long_type unsigned_area_type; + typedef polygon_long_long_type coordinate_difference; +#else + typedef long manhattan_area_type; + typedef unsigned long unsigned_area_type; + typedef long coordinate_difference; +#endif + typedef long double coordinate_distance; + }; + + template<> + struct coordinate_traits { + typedef coordinate_traits cT_; + typedef cT_::coordinate_type coordinate_type; + typedef cT_::area_type area_type; + typedef cT_::manhattan_area_type manhattan_area_type; + typedef cT_::unsigned_area_type unsigned_area_type; + typedef cT_::coordinate_difference coordinate_difference; + typedef cT_::coordinate_distance coordinate_distance; + }; + +#ifdef BOOST_POLYGON_USE_LONG_LONG + template <> + struct coordinate_traits { + typedef polygon_long_long_type coordinate_type; + typedef long double area_type; + typedef polygon_long_long_type manhattan_area_type; + typedef polygon_ulong_long_type unsigned_area_type; + typedef polygon_long_long_type coordinate_difference; + typedef long double coordinate_distance; + }; + + template<> + struct coordinate_traits { + typedef coordinate_traits cT_; + typedef cT_::coordinate_type coordinate_type; + typedef cT_::area_type area_type; + typedef cT_::manhattan_area_type manhattan_area_type; + typedef cT_::unsigned_area_type unsigned_area_type; + typedef cT_::coordinate_difference coordinate_difference; + typedef cT_::coordinate_distance coordinate_distance; + }; +#endif + + template <> + struct coordinate_traits { + typedef float coordinate_type; + typedef float area_type; + typedef float manhattan_area_type; + typedef float unsigned_area_type; + typedef float coordinate_difference; + typedef float coordinate_distance; + }; + + template <> + struct coordinate_traits { + typedef double coordinate_type; + typedef double area_type; + typedef double manhattan_area_type; + typedef double unsigned_area_type; + typedef double coordinate_difference; + typedef double coordinate_distance; + }; + + template <> + struct coordinate_traits { + typedef long double coordinate_type; + typedef long double area_type; + typedef long double manhattan_area_type; + typedef long double unsigned_area_type; + typedef long double coordinate_difference; + typedef long double coordinate_distance; + }; + + template + struct scaling_policy { + template + static inline T round(T2 t2) { + return (T)std::floor(t2+0.5); + } + + static inline T round(T t2) { + return t2; + } + }; + + struct coordinate_concept {}; + + template <> + struct geometry_concept { typedef coordinate_concept type; }; +#ifdef BOOST_POLYGON_USE_LONG_LONG + template <> + struct geometry_concept { typedef coordinate_concept type; }; +#endif + template <> + struct geometry_concept { typedef coordinate_concept type; }; + template <> + struct geometry_concept { typedef coordinate_concept type; }; + template <> + struct geometry_concept { typedef coordinate_concept type; }; + + struct gtl_no { static const bool value = false; }; + struct gtl_yes { typedef gtl_yes type; + static const bool value = true; }; + + template + struct gtl_and_c { typedef gtl_no type; }; + template <> + struct gtl_and_c { typedef gtl_yes type; }; + + template + struct gtl_and : gtl_and_c {}; + template + struct gtl_and_3 { typedef typename gtl_and< + T, typename gtl_and::type>::type type; }; + + template + struct gtl_and_4 { typedef typename gtl_and_3< + T, T2, typename gtl_and::type>::type type; }; + template + struct gtl_or { typedef gtl_yes type; }; + template + struct gtl_or { typedef T type; }; + + template + struct gtl_or_3 { typedef typename gtl_or< + T, typename gtl_or::type>::type type; }; + + template + struct gtl_or_4 { typedef typename gtl_or< + T, typename gtl_or_3::type>::type type; }; + + template + struct gtl_not { typedef gtl_no type; }; + template <> + struct gtl_not { typedef gtl_yes type; }; + + template + struct gtl_if { +#ifdef BOOST_POLYGON_MSVC + typedef gtl_no type; +#endif + }; + template <> + struct gtl_if { typedef gtl_yes type; }; + + template + struct gtl_same_type { typedef gtl_no type; }; + template + struct gtl_same_type { typedef gtl_yes type; }; + template + struct gtl_different_type { typedef typename gtl_not::type>::type type; }; + + struct manhattan_domain {}; + struct forty_five_domain {}; + struct general_domain {}; + + template + struct geometry_domain { typedef general_domain type; }; + + template + struct area_type_by_domain { typedef typename coordinate_traits::area_type type; }; + template + struct area_type_by_domain { + typedef typename coordinate_traits::manhattan_area_type type; }; + + template + struct enable_if_ { + typedef R type; + }; + + template + struct enable_if_ { }; + + template + struct enable_if + : enable_if_ { }; + + struct y_c_edist : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type, coordinate_concept>::type, + typename gtl_same_type::type, coordinate_concept>::type>::type, + typename coordinate_traits::coordinate_difference>::type + euclidean_distance(const coordinate_type_1& lvalue, const coordinate_type_2& rvalue) { + typedef typename coordinate_traits::coordinate_difference Unit; + return (lvalue < rvalue) ? (Unit)rvalue - (Unit)lvalue : (Unit)lvalue - (Unit)rvalue; + } + + + + // predicated_swap swaps a and b if pred is true + + // predicated_swap is guarenteed to behave the same as + // if(pred){ + // T tmp = a; + // a = b; + // b = tmp; + // } + // but will not generate a branch instruction. + // predicated_swap always creates a temp copy of a, but does not + // create more than one temp copy of an input. + // predicated_swap can be used to optimize away branch instructions in C++ + template + inline bool predicated_swap(const bool& pred, + T& a, + T& b) { + const T tmp = a; + const T* input[2] = {&b, &tmp}; + a = *input[!pred]; + b = *input[pred]; + return pred; + } + + enum direction_1d_enum { LOW = 0, HIGH = 1, + LEFT = 0, RIGHT = 1, + CLOCKWISE = 0, COUNTERCLOCKWISE = 1, + REVERSE = 0, FORWARD = 1, + NEGATIVE = 0, POSITIVE = 1 }; + enum orientation_2d_enum { HORIZONTAL = 0, VERTICAL = 1 }; + enum direction_2d_enum { WEST = 0, EAST = 1, SOUTH = 2, NORTH = 3 }; + enum orientation_3d_enum { PROXIMAL = 2 }; + enum direction_3d_enum { DOWN = 4, UP = 5 }; + enum winding_direction { + clockwise_winding = 0, + counterclockwise_winding = 1, + unknown_winding = 2 + }; + + class direction_2d; + class direction_3d; + class orientation_2d; + + class direction_1d { + private: + unsigned int val_; + explicit direction_1d(int d); + public: + inline direction_1d() : val_(LOW) {} + inline direction_1d(const direction_1d& that) : val_(that.val_) {} + inline direction_1d(const direction_1d_enum val) : val_(val) {} + explicit inline direction_1d(const direction_2d& that); + explicit inline direction_1d(const direction_3d& that); + inline direction_1d& operator = (const direction_1d& d) { + val_ = d.val_; return * this; } + inline bool operator==(direction_1d d) const { return (val_ == d.val_); } + inline bool operator!=(direction_1d d) const { return !((*this) == d); } + inline unsigned int to_int(void) const { return val_; } + inline direction_1d& backward() { val_ ^= 1; return *this; } + inline int get_sign() const { return val_ * 2 - 1; } + }; + + class direction_2d; + + class orientation_2d { + private: + unsigned int val_; + explicit inline orientation_2d(int o); + public: + inline orientation_2d() : val_(HORIZONTAL) {} + inline orientation_2d(const orientation_2d& ori) : val_(ori.val_) {} + inline orientation_2d(const orientation_2d_enum val) : val_(val) {} + explicit inline orientation_2d(const direction_2d& that); + inline orientation_2d& operator=(const orientation_2d& ori) { + val_ = ori.val_; return * this; } + inline bool operator==(orientation_2d that) const { return (val_ == that.val_); } + inline bool operator!=(orientation_2d that) const { return (val_ != that.val_); } + inline unsigned int to_int() const { return (val_); } + inline void turn_90() { val_ = val_^ 1; } + inline orientation_2d get_perpendicular() const { + orientation_2d retval = *this; + retval.turn_90(); + return retval; + } + inline direction_2d get_direction(direction_1d dir) const; + }; + + class direction_2d { + private: + int val_; + + public: + + inline direction_2d() : val_(WEST) {} + + inline direction_2d(const direction_2d& that) : val_(that.val_) {} + + inline direction_2d(const direction_2d_enum val) : val_(val) {} + + inline direction_2d& operator=(const direction_2d& d) { + val_ = d.val_; + return * this; + } + + inline ~direction_2d() { } + + inline bool operator==(direction_2d d) const { return (val_ == d.val_); } + inline bool operator!=(direction_2d d) const { return !((*this) == d); } + inline bool operator< (direction_2d d) const { return (val_ < d.val_); } + inline bool operator<=(direction_2d d) const { return (val_ <= d.val_); } + inline bool operator> (direction_2d d) const { return (val_ > d.val_); } + inline bool operator>=(direction_2d d) const { return (val_ >= d.val_); } + + // Casting to int + inline unsigned int to_int(void) const { return val_; } + + inline direction_2d backward() const { + // flip the LSB, toggles 0 - 1 and 2 - 3 + return direction_2d(direction_2d_enum(val_ ^ 1)); + } + + // Returns a direction 90 degree left (LOW) or right(HIGH) to this one + inline direction_2d turn(direction_1d t) const { + return direction_2d(direction_2d_enum(val_ ^ 3 ^ (val_ >> 1) ^ t.to_int())); + } + + // Returns a direction 90 degree left to this one + inline direction_2d left() const {return turn(HIGH);} + + // Returns a direction 90 degree right to this one + inline direction_2d right() const {return turn(LOW);} + + // N, E are positive, S, W are negative + inline bool is_positive() const {return (val_ & 1);} + inline bool is_negative() const {return !is_positive();} + inline int get_sign() const {return ((is_positive()) << 1) -1;} + + }; + + direction_1d::direction_1d(const direction_2d& that) : val_(that.to_int() & 1) {} + + orientation_2d::orientation_2d(const direction_2d& that) : val_(that.to_int() >> 1) {} + + direction_2d orientation_2d::get_direction(direction_1d dir) const { + return direction_2d(direction_2d_enum((val_ << 1) + dir.to_int())); + } + + class orientation_3d { + private: + unsigned int val_; + explicit inline orientation_3d(int o); + public: + inline orientation_3d() : val_((int)HORIZONTAL) {} + inline orientation_3d(const orientation_3d& ori) : val_(ori.val_) {} + inline orientation_3d(orientation_2d ori) : val_(ori.to_int()) {} + inline orientation_3d(const orientation_3d_enum val) : val_(val) {} + explicit inline orientation_3d(const direction_2d& that); + explicit inline orientation_3d(const direction_3d& that); + inline ~orientation_3d() { } + inline orientation_3d& operator=(const orientation_3d& ori) { + val_ = ori.val_; return * this; } + inline bool operator==(orientation_3d that) const { return (val_ == that.val_); } + inline bool operator!=(orientation_3d that) const { return (val_ != that.val_); } + inline unsigned int to_int() const { return (val_); } + inline direction_3d get_direction(direction_1d dir) const; + }; + + class direction_3d { + private: + int val_; + + public: + + inline direction_3d() : val_(WEST) {} + + inline direction_3d(direction_2d that) : val_(that.to_int()) {} + inline direction_3d(const direction_3d& that) : val_(that.val_) {} + + inline direction_3d(const direction_2d_enum val) : val_(val) {} + inline direction_3d(const direction_3d_enum val) : val_(val) {} + + inline direction_3d& operator=(direction_3d d) { + val_ = d.val_; + return * this; + } + + inline ~direction_3d() { } + + inline bool operator==(direction_3d d) const { return (val_ == d.val_); } + inline bool operator!=(direction_3d d) const { return !((*this) == d); } + inline bool operator< (direction_3d d) const { return (val_ < d.val_); } + inline bool operator<=(direction_3d d) const { return (val_ <= d.val_); } + inline bool operator> (direction_3d d) const { return (val_ > d.val_); } + inline bool operator>=(direction_3d d) const { return (val_ >= d.val_); } + + // Casting to int + inline unsigned int to_int(void) const { return val_; } + + inline direction_3d backward() const { + // flip the LSB, toggles 0 - 1 and 2 - 3 and 4 - 5 + return direction_2d(direction_2d_enum(val_ ^ 1)); + } + + // N, E, U are positive, S, W, D are negative + inline bool is_positive() const {return (val_ & 1);} + inline bool is_negative() const {return !is_positive();} + inline int get_sign() const {return ((is_positive()) << 1) -1;} + + }; + + direction_1d::direction_1d(const direction_3d& that) : val_(that.to_int() & 1) {} + orientation_3d::orientation_3d(const direction_3d& that) : val_(that.to_int() >> 1) {} + orientation_3d::orientation_3d(const direction_2d& that) : val_(that.to_int() >> 1) {} + + direction_3d orientation_3d::get_direction(direction_1d dir) const { + return direction_3d(direction_3d_enum((val_ << 1) + dir.to_int())); + } + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_concept.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_concept.hpp new file mode 100644 index 0000000..4d4c1dd --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_concept.hpp @@ -0,0 +1,469 @@ +// Boost.Polygon library point_concept.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_POINT_CONCEPT_HPP +#define BOOST_POLYGON_POINT_CONCEPT_HPP + +#include "isotropy.hpp" +#include "point_traits.hpp" + +namespace boost { +namespace polygon { + +struct point_concept {}; + +template +struct is_point_concept { + typedef gtl_no type; +}; + +template <> +struct is_point_concept { + typedef gtl_yes type; +}; + +template +struct is_mutable_point_concept { + typedef gtl_no type; +}; + +template <> +struct is_mutable_point_concept { + typedef gtl_yes type; +}; + +template +struct point_coordinate_type_by_concept { + typedef void type; +}; + +template +struct point_coordinate_type_by_concept { + typedef typename point_traits::coordinate_type type; +}; + +template +struct point_coordinate_type { + typedef typename point_coordinate_type_by_concept< + GeometryType, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +template +struct point_difference_type_by_concept { + typedef void type; +}; + +template +struct point_difference_type_by_concept { + typedef typename coordinate_traits< + typename point_traits::coordinate_type + >::coordinate_difference type; +}; + +template +struct point_difference_type { + typedef typename point_difference_type_by_concept< + GeometryType, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +template +struct point_distance_type_by_concept { + typedef void type; +}; + +template +struct point_distance_type_by_concept { + typedef typename coordinate_traits< + typename point_coordinate_type::type + >::coordinate_distance type; +}; + +template +struct point_distance_type { + typedef typename point_distance_type_by_concept< + GeometryType, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +struct y_pt_get : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_get, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, + typename point_coordinate_type::type +>::type get(const PointType& point, orientation_2d orient) { + return point_traits::get(point, orient); +} + +struct y_pt_set : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_set, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, + void +>::type set(PointType& point, orientation_2d orient, + typename point_mutable_traits::coordinate_type value) { + point_mutable_traits::set(point, orient, value); +} + +struct y_pt_construct : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_construct, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType>::type construct( + typename point_mutable_traits::coordinate_type x, + typename point_mutable_traits::coordinate_type y) { + return point_mutable_traits::construct(x, y); +} + +struct y_pt_assign : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_assign, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type +>::type, +PointType1>::type& assign(PointType1& lvalue, const PointType2& rvalue) { + set(lvalue, HORIZONTAL, get(rvalue, HORIZONTAL)); + set(lvalue, VERTICAL, get(rvalue, VERTICAL)); + return lvalue; +} + +struct y_p_x : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_p_x, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, + typename point_coordinate_type::type +>::type x(const PointType& point) { + return get(point, HORIZONTAL); +} + +struct y_p_y : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_p_y, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, + typename point_coordinate_type::type +>::type y(const PointType& point) { + return get(point, VERTICAL); +} + +struct y_p_sx : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_p_sx, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +void>::type x(PointType& point, + typename point_mutable_traits::coordinate_type value) { + set(point, HORIZONTAL, value); +} + +struct y_p_sy : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_p_sy, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +void>::type y(PointType& point, + typename point_mutable_traits::coordinate_type value) { + set(point, VERTICAL, value); +} + +struct y_pt_equiv : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_equiv, + typename is_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +bool>::type equivalence( + const PointType1& point1, const PointType2& point2) { + return (x(point1) == x(point2)) && (y(point1) == y(point2)); +} + +struct y_pt_man_dist : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_man_dist, + typename is_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +typename point_difference_type::type>::type +manhattan_distance(const PointType1& point1, const PointType2& point2) { + return euclidean_distance(point1, point2, HORIZONTAL) + + euclidean_distance(point1, point2, VERTICAL); +} + +struct y_pt_ed1 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_ed1, + typename is_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +typename point_difference_type::type>::type +euclidean_distance( + const PointType1& point1, + const PointType2& point2, + orientation_2d orient) { + typename point_difference_type::type dif = + get(point1, orient) - get(point2, orient); + return (dif < 0) ? -dif : dif; +} + +struct y_pt_eds : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_eds, + typename is_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +typename point_difference_type::type>::type +distance_squared(const PointType1& point1, const PointType2& point2) { + typename point_difference_type::type dx = + euclidean_distance(point1, point2, HORIZONTAL); + typename point_difference_type::type dy = + euclidean_distance(point1, point2, VERTICAL); + dx *= dx; + dy *= dy; + return dx + dy; +} + +struct y_pt_ed2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_ed2, + typename is_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +typename point_distance_type::type>::type +euclidean_distance(const PointType1& point1, const PointType2& point2) { + return (std::sqrt)( + static_cast(distance_squared(point1, point2))); +} + +struct y_pt_convolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_convolve, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType1>::type& convolve(PointType1& lvalue, const PointType2& rvalue) { + x(lvalue, x(lvalue) + x(rvalue)); + y(lvalue, y(lvalue) + y(rvalue)); + return lvalue; +} + +struct y_pt_deconvolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_pt_deconvolve, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType1>::type& deconvolve(PointType1& lvalue, const PointType2& rvalue) { + x(lvalue, x(lvalue) - x(rvalue)); + y(lvalue, y(lvalue) - y(rvalue)); + return lvalue; +} + +struct y_pt_scale_up : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_scale_up, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType>::type& scale_up(PointType& point, CType factor) { + typedef typename point_coordinate_type::type Unit; + x(point, x(point) * (Unit)factor); + y(point, y(point) * (Unit)factor); + return point; +} + +struct y_pt_scale_down : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_scale_down, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType>::type& scale_down(PointType& point, CType factor) { + typedef typename point_coordinate_type::type Unit; + typedef typename coordinate_traits::coordinate_distance dt; + x(point, scaling_policy::round((dt)(x(point)) / (dt)factor)); + y(point, scaling_policy::round((dt)(y(point)) / (dt)factor)); + return point; +} + +struct y_pt_scale : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_scale, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType>::type& scale(PointType& point, const ScaleType& scaling) { + typedef typename point_coordinate_type::type Unit; + Unit x_coord(x(point)); + Unit y_coord(y(point)); + scaling.scale(x_coord, y_coord); + x(point, x_coord); + y(point, y_coord); + return point; +} + +struct y_pt_transform : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_transform, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType>::type& transform(PointType& point, const TransformType& transform) { + typedef typename point_coordinate_type::type Unit; + Unit x_coord(x(point)); + Unit y_coord(y(point)); + transform.transform(x_coord, y_coord); + x(point, x_coord); + y(point, y_coord); + return point; +} + +struct y_pt_move : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_pt_move, + typename is_mutable_point_concept< + typename geometry_concept::type + >::type + >::type, +PointType>::type& move(PointType& point, orientation_2d orient, + typename point_coordinate_type::type displacement) { + typedef typename point_coordinate_type::type Unit; + Unit coord = get(point, orient); + set(point, orient, coord + displacement); + return point; +} +} // polygon +} // boost + +#endif // BOOST_POLYGON_POINT_CONCEPT_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_data.hpp new file mode 100644 index 0000000..deda14d --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_data.hpp @@ -0,0 +1,138 @@ +// Boost.Polygon library point_data.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_POINT_DATA_HPP +#define BOOST_POLYGON_POINT_DATA_HPP + +#include "isotropy.hpp" +#include "point_concept.hpp" + +namespace boost { +namespace polygon { + +template +class point_data { + public: + typedef T coordinate_type; + + point_data() +#ifndef BOOST_POLYGON_MSVC + : coords_() +#endif + {} + + point_data(coordinate_type x, coordinate_type y) { + coords_[HORIZONTAL] = x; + coords_[VERTICAL] = y; + } + + explicit point_data(const point_data& that) { + coords_[0] = that.coords_[0]; + coords_[1] = that.coords_[1]; + } + + point_data& operator=(const point_data& that) { + coords_[0] = that.coords_[0]; + coords_[1] = that.coords_[1]; + return *this; + } + +#if defined(__GNUC__) && __GNUC__ < 6 + // "explicit" to work around a bug in GCC < 6: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63356 + template + explicit point_data(const PointType& that) { + *this = that; + } +#else // __GNUC__ < 6 + template + point_data(const PointType& that) { + *this = that; + } +#endif // __GNUC__ < 6 + + template + point_data& operator=(const PointType& that) { + assign(*this, that); + return *this; + } + + // TODO(asydorchuk): Deprecated. + template + point_data(const point_data& that) { + coords_[HORIZONTAL] = (coordinate_type)that.x(); + coords_[VERTICAL] = (coordinate_type)that.y(); + } + + coordinate_type get(orientation_2d orient) const { + return coords_[orient.to_int()]; + } + + void set(orientation_2d orient, coordinate_type value) { + coords_[orient.to_int()] = value; + } + + coordinate_type x() const { + return coords_[HORIZONTAL]; + } + + point_data& x(coordinate_type value) { + coords_[HORIZONTAL] = value; + return *this; + } + + coordinate_type y() const { + return coords_[VERTICAL]; + } + + point_data& y(coordinate_type value) { + coords_[VERTICAL] = value; + return *this; + } + + bool operator==(const point_data& that) const { + return (coords_[0] == that.coords_[0]) && + (coords_[1] == that.coords_[1]); + } + + bool operator!=(const point_data& that) const { + return !(*this == that); + } + + bool operator<(const point_data& that) const { + return (coords_[0] < that.coords_[0]) || + ((coords_[0] == that.coords_[0]) && + (coords_[1] < that.coords_[1])); + } + + bool operator<=(const point_data& that) const { + return !(that < *this); + } + + bool operator>(const point_data& that) const { + return that < *this; + } + + bool operator>=(const point_data& that) const { + return !(*this < that); + } + + private: + coordinate_type coords_[2]; +}; + +template +struct geometry_concept< point_data > { + typedef point_concept type; +}; +} // polygon +} // boost + +#endif // BOOST_POLYGON_POINT_DATA_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_traits.hpp new file mode 100644 index 0000000..5bc43d1 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/point_traits.hpp @@ -0,0 +1,48 @@ +// Boost.Polygon library point_traits.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_POINT_TRAITS_HPP +#define BOOST_POLYGON_POINT_TRAITS_HPP + +#include "isotropy.hpp" + +namespace boost { +namespace polygon { + +template +struct point_traits { + typedef PointType point_type; + typedef typename point_type::coordinate_type coordinate_type; + + static coordinate_type get( + const point_type& point, orientation_2d orient) { + return point.get(orient); + } +}; + +template +struct point_mutable_traits { + typedef PointType point_type; + typedef typename point_type::coordinate_type coordinate_type; + + static void set( + point_type& point, orientation_2d orient, coordinate_type value) { + point.set(orient, value); + } + + static point_type construct(coordinate_type x, coordinate_type y) { + return point_type(x, y); + } +}; +} // polygon +} // boost + +#endif // BOOST_POLYGON_POINT_TRAITS_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon.hpp new file mode 100644 index 0000000..90a7c1f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon.hpp @@ -0,0 +1,92 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_HPP +#define BOOST_POLYGON_POLYGON_HPP +#define BOOST_POLYGON_VERSION 014401 + +#include "isotropy.hpp" + +//point +#include "point_data.hpp" +#include "point_traits.hpp" +#include "point_concept.hpp" + +#include "transform.hpp" + +//interval +#include "interval_data.hpp" +#include "interval_traits.hpp" +#include "interval_concept.hpp" + +//rectangle +#include "rectangle_data.hpp" +#include "rectangle_traits.hpp" +#include "rectangle_concept.hpp" + +//segment +#include "segment_data.hpp" +#include "segment_traits.hpp" +#include "segment_concept.hpp" + +//algorithms needed by polygon types +#include "detail/iterator_points_to_compact.hpp" +#include "detail/iterator_compact_to_points.hpp" + +//polygons +#include "polygon_45_data.hpp" +#include "polygon_data.hpp" +#include "polygon_90_data.hpp" +#include "polygon_90_with_holes_data.hpp" +#include "polygon_45_with_holes_data.hpp" +#include "polygon_with_holes_data.hpp" +#include "polygon_traits.hpp" + +//manhattan boolean algorithms +#include "detail/boolean_op.hpp" +#include "detail/polygon_formation.hpp" +#include "detail/rectangle_formation.hpp" +#include "detail/max_cover.hpp" +#include "detail/property_merge.hpp" +#include "detail/polygon_90_touch.hpp" +#include "detail/iterator_geometry_to_set.hpp" + +//45 boolean op algorithms +#include "detail/boolean_op_45.hpp" +#include "detail/polygon_45_formation.hpp" + +//polygon set data types +#include "polygon_90_set_data.hpp" +//polygon set trait types +#include "polygon_90_set_traits.hpp" +//polygon set concepts +#include "polygon_90_set_concept.hpp" +//boolean operator syntax +#include "detail/polygon_90_set_view.hpp" + +//45 boolean op algorithms +#include "detail/polygon_45_touch.hpp" +#include "detail/property_merge_45.hpp" +#include "polygon_45_set_data.hpp" +#include "polygon_45_set_traits.hpp" +#include "polygon_45_set_concept.hpp" +#include "detail/polygon_45_set_view.hpp" + +//arbitrary polygon algorithms +#include "detail/polygon_arbitrary_formation.hpp" +#include "polygon_set_data.hpp" + +//general scanline +#include "detail/scan_arbitrary.hpp" +#include "polygon_set_traits.hpp" +#include "detail/polygon_set_view.hpp" + +#include "polygon_set_concept.hpp" + +#include "segment_utils.hpp" + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_data.hpp new file mode 100644 index 0000000..cdd5c24 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_data.hpp @@ -0,0 +1,72 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_DATA_HPP +#define BOOST_POLYGON_POLYGON_45_DATA_HPP +#include "isotropy.hpp" +namespace boost { namespace polygon{ +struct polygon_45_concept; +template class polygon_data; +template +class polygon_45_data { +public: + typedef polygon_45_concept geometry_type; + typedef T coordinate_type; + typedef typename std::vector >::const_iterator iterator_type; + typedef typename coordinate_traits::coordinate_distance area_type; + typedef point_data point_type; + + inline polygon_45_data() : coords_() {} //do nothing default constructor + + template + inline polygon_45_data(iT input_begin, iT input_end) : coords_(input_begin, input_end) {} + + template + inline polygon_45_data& set(iT input_begin, iT input_end) { + coords_.clear(); //just in case there was some old data there + coords_.insert(coords_.end(), input_begin, input_end); + return *this; + } + + // copy constructor (since we have dynamic memory) + inline polygon_45_data(const polygon_45_data& that) : coords_(that.coords_) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline polygon_45_data& operator=(const polygon_45_data& that) { + coords_ = that.coords_; + return *this; + } + + template + inline polygon_45_data& operator=(const T2& rvalue); + + inline bool operator==(const polygon_45_data& that) const { + if(coords_.size() != that.coords_.size()) return false; + for(std::size_t i = 0; i < coords_.size(); ++i) { + if(coords_[i] != that.coords_[i]) return false; + } + return true; + } + + inline bool operator!=(const polygon_45_data& that) const { return !((*this) == that); } + + // get begin iterator, returns a pointer to a const Unit + inline iterator_type begin() const { return coords_.begin(); } + + // get end iterator, returns a pointer to a const Unit + inline iterator_type end() const { return coords_.end(); } + + inline std::size_t size() const { return coords_.size(); } + +public: + std::vector > coords_; +}; + + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_concept.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_concept.hpp new file mode 100644 index 0000000..623415f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_concept.hpp @@ -0,0 +1,441 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_SET_CONCEPT_HPP +#define BOOST_POLYGON_POLYGON_45_SET_CONCEPT_HPP +#include "polygon_45_set_data.hpp" +#include "polygon_45_set_traits.hpp" +#include "detail/polygon_45_touch.hpp" +namespace boost { namespace polygon{ + + template + struct is_either_polygon_45_set_type { + typedef typename gtl_or::type, typename is_polygon_45_set_type::type >::type type; + }; + + template + struct is_polygon_45_or_90_set_type { + typedef typename gtl_or::type, typename is_polygon_90_set_type::type >::type type; + }; + + template + typename enable_if< typename gtl_if::type>::type, + typename polygon_45_set_traits::iterator_type>::type + begin_45_set_data(const polygon_set_type& polygon_set) { + return polygon_45_set_traits::begin(polygon_set); + } + + template + typename enable_if< typename gtl_if::type>::type, + typename polygon_45_set_traits::iterator_type>::type + end_45_set_data(const polygon_set_type& polygon_set) { + return polygon_45_set_traits::end(polygon_set); + } + + template + typename enable_if< typename gtl_if::type>::type, + bool>::type + clean(const polygon_set_type& polygon_set) { + return polygon_45_set_traits::clean(polygon_set); + } + + //assign + template + typename enable_if< typename gtl_and< typename gtl_if::type>::type, + typename gtl_if::type>::type>::type, + polygon_set_type_1>::type & + assign(polygon_set_type_1& lvalue, const polygon_set_type_2& rvalue) { + polygon_45_set_mutable_traits::set(lvalue, begin_45_set_data(rvalue), end_45_set_data(rvalue)); + return lvalue; + } + + //get trapezoids + template + typename enable_if< typename gtl_if::type>::type, + void>::type + get_trapezoids(output_container_type& output, const polygon_set_type& polygon_set) { + clean(polygon_set); + polygon_45_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.get_trapezoids(output); + } + + //get trapezoids + template + typename enable_if< typename gtl_if::type>::type, + void>::type + get_trapezoids(output_container_type& output, const polygon_set_type& polygon_set, orientation_2d slicing_orientation) { + clean(polygon_set); + polygon_45_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.get_trapezoids(output, slicing_orientation); + } + + //equivalence + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_if::type>::type, + typename gtl_if::type>::type>::type, + bool>::type + equivalence(const polygon_set_type_1& lvalue, + const polygon_set_type_2& rvalue) { + polygon_45_set_data::coordinate_type> ps1; + assign(ps1, lvalue); + polygon_45_set_data::coordinate_type> ps2; + assign(ps2, rvalue); + return ps1 == ps2; + } + + //clear + template + typename enable_if< typename gtl_if::type>::type, + void>::type + clear(polygon_set_type& polygon_set) { + polygon_45_set_data::coordinate_type> ps; + assign(polygon_set, ps); + } + + //empty + template + typename enable_if< typename gtl_if::type>::type, + bool>::type + empty(const polygon_set_type& polygon_set) { + if(clean(polygon_set)) return begin_45_set_data(polygon_set) == end_45_set_data(polygon_set); + polygon_45_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.clean(); + return ps.empty(); + } + + //extents + template + typename enable_if< + typename gtl_and< typename gtl_if::type>::type, + typename is_mutable_rectangle_concept::type>::type>::type, + bool>::type + extents(rectangle_type& extents_rectangle, + const polygon_set_type& polygon_set) { + clean(polygon_set); + polygon_45_set_data::coordinate_type> ps; + assign(ps, polygon_set); + return ps.extents(extents_rectangle); + } + + //area + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + typename coordinate_traits::coordinate_type>::area_type>::type + area(const polygon_set_type& polygon_set) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + typedef polygon_45_with_holes_data p_type; + typedef typename coordinate_traits::area_type area_type; + std::vector polys; + assign(polys, polygon_set); + area_type retval = (area_type)0; + for(std::size_t i = 0; i < polys.size(); ++i) { + retval += area(polys[i]); + } + return retval; + } + + //interact + template + typename enable_if < + typename gtl_and< typename gtl_if::type>::type, + typename gtl_if::type>::type >::type, + polygon_set_type_1>::type& + interact(polygon_set_type_1& polygon_set_1, const polygon_set_type_2& polygon_set_2) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + std::vector > polys; + assign(polys, polygon_set_1); + std::vector > graph(polys.size()+1, std::set()); + connectivity_extraction_45 ce; + ce.insert(polygon_set_2); + for(std::size_t i = 0; i < polys.size(); ++i){ + ce.insert(polys[i]); + } + ce.extract(graph); + clear(polygon_set_1); + polygon_45_set_data ps; + for(std::set::iterator itr = graph[0].begin(); itr != graph[0].end(); ++itr){ + ps.insert(polys[(*itr)-1]); + } + assign(polygon_set_1, ps); + return polygon_set_1; + } + +// //self_intersect +// template +// typename enable_if< typename is_mutable_polygon_45_set_type::type>::type, +// polygon_set_type>::type & +// self_intersect(polygon_set_type& polygon_set) { +// typedef typename polygon_45_set_traits::coordinate_type Unit; +// //TODO +// } + + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + resize(polygon_set_type& polygon_set, coord_type resizing, + RoundingOption rounding = CLOSEST, CornerOption corner = INTERSECTION) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_45_set_data ps; + assign(ps, polygon_set); + ps.resize(resizing, rounding, corner); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + bloat(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + return resize(polygon_set, static_cast::coordinate_type>(bloating)); + } + + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + shrink(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type shrinking) { + return resize(polygon_set, -(typename polygon_45_set_traits::coordinate_type)shrinking); + } + + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + grow_and(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + std::vector > polys; + assign(polys, polygon_set); + clear(polygon_set); + polygon_45_set_data ps; + for(std::size_t i = 0; i < polys.size(); ++i) { + polygon_45_set_data tmpPs; + tmpPs.insert(polys[i]); + bloat(tmpPs, bloating); + tmpPs.clean(); //apply implicit OR on tmp polygon set + ps.insert(tmpPs); + } + ps.self_intersect(); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + scale_up(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_45_set_data ps; + assign(ps, polygon_set); + ps.scale_up(factor); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + scale_down(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_45_set_data ps; + assign(ps, polygon_set); + ps.scale_down(factor); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + scale(polygon_set_type& polygon_set, double factor) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_45_set_data ps; + assign(ps, polygon_set); + ps.scale(factor); + assign(polygon_set, ps); + return polygon_set; + } + + //self_intersect + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + self_intersect(polygon_set_type& polygon_set) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + polygon_45_set_data ps; + assign(ps, polygon_set); + ps.self_intersect(); + assign(polygon_set, ps); + return polygon_set; + } + + //self_xor + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + self_xor(polygon_set_type& polygon_set) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + polygon_45_set_data ps; + assign(ps, polygon_set); + ps.self_xor(); + assign(polygon_set, ps); + return polygon_set; + } + + //transform + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + transform(polygon_set_type& polygon_set, + const transformation_type& transformation) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_45_set_data ps; + assign(ps, polygon_set); + ps.transform(transformation); + assign(polygon_set, ps); + return polygon_set; + } + + //keep + template + typename enable_if< typename is_mutable_polygon_45_set_type::type, + polygon_set_type>::type & + keep(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::area_type min_area, + typename coordinate_traits::coordinate_type>::area_type max_area, + typename coordinate_traits::coordinate_type>::unsigned_area_type min_width, + typename coordinate_traits::coordinate_type>::unsigned_area_type max_width, + typename coordinate_traits::coordinate_type>::unsigned_area_type min_height, + typename coordinate_traits::coordinate_type>::unsigned_area_type max_height) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + typedef typename coordinate_traits::unsigned_area_type uat; + std::list > polys; + assign(polys, polygon_set); + typename std::list >::iterator itr_nxt; + for(typename std::list >::iterator itr = polys.begin(); itr != polys.end(); itr = itr_nxt){ + itr_nxt = itr; + ++itr_nxt; + rectangle_data bbox; + extents(bbox, *itr); + uat pwidth = delta(bbox, HORIZONTAL); + if(pwidth > min_width && pwidth <= max_width){ + uat pheight = delta(bbox, VERTICAL); + if(pheight > min_height && pheight <= max_height){ + typename coordinate_traits::area_type parea = area(*itr); + if(parea <= max_area && parea >= min_area) { + continue; + } + } + } + polys.erase(itr); + } + assign(polygon_set, polys); + return polygon_set; + } + + template + struct view_of { + typedef typename get_coordinate_type::type >::type coordinate_type; + T* tp; + std::vector > polys; + view_of(T& obj) : tp(&obj), polys() { + std::vector > gpolys; + assign(gpolys, obj); + for(typename std::vector >::iterator itr = gpolys.begin(); + itr != gpolys.end(); ++itr) { + polys.push_back(polygon_90_with_holes_data()); + assign(polys.back(), view_as(*itr)); + } + } + view_of(const T& obj) : tp(), polys() { + std::vector > gpolys; + assign(gpolys, obj); + for(typename std::vector >::iterator itr = gpolys.begin(); + itr != gpolys.end(); ++itr) { + polys.push_back(polygon_90_with_holes_data()); + assign(polys.back(), view_as(*itr)); + } + } + + typedef typename std::vector >::const_iterator iterator_type; + typedef view_of operator_arg_type; + + inline iterator_type begin() const { + return polys.begin(); + } + + inline iterator_type end() const { + return polys.end(); + } + + inline orientation_2d orient() const { return HORIZONTAL; } + + inline bool clean() const { return false; } + + inline bool sorted() const { return false; } + + inline T& get() { return *tp; } + + }; + + template + struct polygon_90_set_traits > { + typedef typename view_of::coordinate_type coordinate_type; + typedef typename view_of::iterator_type iterator_type; + typedef view_of operator_arg_type; + + static inline iterator_type begin(const view_of& polygon_set) { + return polygon_set.begin(); + } + + static inline iterator_type end(const view_of& polygon_set) { + return polygon_set.end(); + } + + static inline orientation_2d orient(const view_of& polygon_set) { + return polygon_set.orient(); } + + static inline bool clean(const view_of& polygon_set) { + return polygon_set.clean(); } + + static inline bool sorted(const view_of& polygon_set) { + return polygon_set.sorted(); } + + }; + + template + struct geometry_concept > { + typedef polygon_90_set_concept type; + }; + + template + struct get_coordinate_type, polygon_90_set_concept> { + typedef typename view_of::coordinate_type type; + }; + template + struct get_iterator_type_2, polygon_90_set_concept> { + typedef typename view_of::iterator_type type; + static type begin(const view_of& t) { return t.begin(); } + static type end(const view_of& t) { return t.end(); } + }; + +} +} +#include "detail/polygon_45_set_view.hpp" +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_data.hpp new file mode 100644 index 0000000..97585f6 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_data.hpp @@ -0,0 +1,1880 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_SET_DATA_HPP +#define BOOST_POLYGON_POLYGON_45_SET_DATA_HPP +#include "polygon_90_set_data.hpp" +#include "detail/boolean_op_45.hpp" +#include "detail/polygon_45_formation.hpp" +#include "detail/polygon_45_touch.hpp" +#include "detail/property_merge_45.hpp" +namespace boost { namespace polygon{ + + enum RoundingOption { CLOSEST = 0, OVERSIZE = 1, UNDERSIZE = 2, SQRT2 = 3, SQRT1OVER2 = 4 }; + enum CornerOption { INTERSECTION = 0, ORTHOGONAL = 1, UNFILLED = 2 }; + + template + class polygon_45_set_view; + + struct polygon_45_set_concept {}; + + template + class polygon_45_set_data { + public: + typedef typename polygon_45_formation::Vertex45Compact Vertex45Compact; + typedef std::vector Polygon45VertexData; + + typedef Unit coordinate_type; + typedef Polygon45VertexData value_type; + typedef typename value_type::const_iterator iterator_type; + typedef polygon_45_set_data operator_arg_type; + + // default constructor + inline polygon_45_set_data() : error_data_(), data_(), dirty_(false), unsorted_(false), is_manhattan_(true) {} + + // constructor from a geometry object + template + inline polygon_45_set_data(const geometry_type& that) : error_data_(), data_(), dirty_(false), unsorted_(false), is_manhattan_(true) { + insert(that); + } + + // copy constructor + inline polygon_45_set_data(const polygon_45_set_data& that) : + error_data_(that.error_data_), data_(that.data_), dirty_(that.dirty_), + unsorted_(that.unsorted_), is_manhattan_(that.is_manhattan_) {} + + template + inline polygon_45_set_data(const polygon_45_set_view& that) : + error_data_(), data_(), dirty_(false), unsorted_(false), is_manhattan_(true) { + (*this) = that.value(); + } + + // destructor + inline ~polygon_45_set_data() {} + + // assignement operator + inline polygon_45_set_data& operator=(const polygon_45_set_data& that) { + if(this == &that) return *this; + error_data_ = that.error_data_; + data_ = that.data_; + dirty_ = that.dirty_; + unsorted_ = that.unsorted_; + is_manhattan_ = that.is_manhattan_; + return *this; + } + + template + inline polygon_45_set_data& operator=(const polygon_45_set_view& that) { + (*this) = that.value(); + return *this; + } + + template + inline polygon_45_set_data& operator=(const geometry_object& geometry) { + data_.clear(); + insert(geometry); + return *this; + } + + // insert iterator range + inline void insert(iterator_type input_begin, iterator_type input_end, bool is_hole = false) { + if(input_begin == input_end || (!data_.empty() && &(*input_begin) == &(*(data_.begin())))) return; + dirty_ = true; + unsorted_ = true; + while(input_begin != input_end) { + insert(*input_begin, is_hole); + ++input_begin; + } + } + + // insert iterator range + template + inline void insert(iT input_begin, iT input_end, bool is_hole = false) { + if(input_begin == input_end) return; + dirty_ = true; + unsorted_ = true; + while(input_begin != input_end) { + insert(*input_begin, is_hole); + ++input_begin; + } + } + + inline void insert(const polygon_45_set_data& polygon_set, bool is_hole = false); + template + inline void insert(const polygon_45_set_data& polygon_set, bool is_hole = false); + + template + inline void insert(const geometry_type& geometry_object, bool is_hole = false) { + insert_dispatch(geometry_object, is_hole, typename geometry_concept::type()); + } + + inline void insert_clean(const Vertex45Compact& vertex_45, bool is_hole = false) { + if(vertex_45.count.is_45()) is_manhattan_ = false; + data_.push_back(vertex_45); + if(is_hole) data_.back().count.invert(); + } + + inline void insert(const Vertex45Compact& vertex_45, bool is_hole = false) { + dirty_ = true; + unsorted_ = true; + insert_clean(vertex_45, is_hole); + } + + template + inline void insert(const polygon_90_set_data& polygon_set, bool is_hole = false) { + if(polygon_set.orient() == VERTICAL) { + for(typename polygon_90_set_data::iterator_type itr = polygon_set.begin(); + itr != polygon_set.end(); ++itr) { + Vertex45Compact vertex_45(point_data((*itr).first, (*itr).second.first), 2, (*itr).second.second); + vertex_45.count[1] = (*itr).second.second; + if(is_hole) vertex_45.count[1] *= - 1; + insert_clean(vertex_45, is_hole); + } + } else { + for(typename polygon_90_set_data::iterator_type itr = polygon_set.begin(); + itr != polygon_set.end(); ++itr) { + Vertex45Compact vertex_45(point_data((*itr).second.first, (*itr).first), 2, (*itr).second.second); + vertex_45.count[1] = (*itr).second.second; + if(is_hole) vertex_45.count[1] *= - 1; + insert_clean(vertex_45, is_hole); + } + } + dirty_ = true; + unsorted_ = true; + } + + template + inline void get(output_container& output) const { + get_dispatch(output, typename geometry_concept::type()); + } + + inline bool has_error_data() const { return !error_data_.empty(); } + inline std::size_t error_count() const { return error_data_.size() / 4; } + inline void get_error_data(polygon_45_set_data& p) const { + p.data_.insert(p.data_.end(), error_data_.begin(), error_data_.end()); + } + + // equivalence operator + inline bool operator==(const polygon_45_set_data& p) const { + clean(); + p.clean(); + return data_ == p.data_; + } + + // inequivalence operator + inline bool operator!=(const polygon_45_set_data& p) const { + return !((*this) == p); + } + + // get iterator to begin vertex data + inline iterator_type begin() const { + return data_.begin(); + } + + // get iterator to end vertex data + inline iterator_type end() const { + return data_.end(); + } + + const value_type& value() const { + return data_; + } + + // clear the contents of the polygon_45_set_data + inline void clear() { data_.clear(); error_data_.clear(); dirty_ = unsorted_ = false; is_manhattan_ = true; } + + // find out if Polygon set is empty + inline bool empty() const { return data_.empty(); } + + // get the Polygon set size in vertices + inline std::size_t size() const { clean(); return data_.size(); } + + // get the current Polygon set capacity in vertices + inline std::size_t capacity() const { return data_.capacity(); } + + // reserve size of polygon set in vertices + inline void reserve(std::size_t size) { return data_.reserve(size); } + + // find out if Polygon set is sorted + inline bool sorted() const { return !unsorted_; } + + // find out if Polygon set is clean + inline bool dirty() const { return dirty_; } + + // find out if Polygon set is clean + inline bool is_manhattan() const { return is_manhattan_; } + + bool clean() const; + + void sort() const{ + if(unsorted_) { + polygon_sort(data_.begin(), data_.end()); + unsorted_ = false; + } + } + + template + void set(input_iterator_type input_begin, input_iterator_type input_end) { + data_.clear(); + reserve(std::distance(input_begin, input_end)); + insert(input_begin, input_end); + dirty_ = true; + unsorted_ = true; + } + + void set_clean(const value_type& value) { + data_ = value; + dirty_ = false; + unsorted_ = false; + } + + void set(const value_type& value) { + data_ = value; + dirty_ = true; + unsorted_ = true; + } + + // append to the container cT with polygons (holes will be fractured vertically) + template + void get_polygons(cT& container) const { + get_dispatch(container, polygon_45_concept()); + } + + // append to the container cT with PolygonWithHoles objects + template + void get_polygons_with_holes(cT& container) const { + get_dispatch(container, polygon_45_with_holes_concept()); + } + + // append to the container cT with polygons of three or four verticies + // slicing orientation is vertical + template + void get_trapezoids(cT& container) const { + clean(); + typename polygon_45_formation::Polygon45Tiling pf; + //std::cout << "FORMING POLYGONS\n"; + pf.scan(container, data_.begin(), data_.end()); + //std::cout << "DONE FORMING POLYGONS\n"; + } + + // append to the container cT with polygons of three or four verticies + template + void get_trapezoids(cT& container, orientation_2d slicing_orientation) const { + if(slicing_orientation == VERTICAL) { + get_trapezoids(container); + } else { + polygon_45_set_data ps(*this); + ps.transform(axis_transformation(axis_transformation::SWAP_XY)); + cT result; + ps.get_trapezoids(result); + for(typename cT::iterator itr = result.begin(); itr != result.end(); ++itr) { + ::boost::polygon::transform(*itr, axis_transformation(axis_transformation::SWAP_XY)); + } + container.insert(container.end(), result.begin(), result.end()); + } + } + + // insert vertex sequence + template + void insert_vertex_sequence(iT begin_vertex, iT end_vertex, + direction_1d winding, bool is_hole = false); + + // get the external boundary rectangle + template + bool extents(rectangle_type& rect) const; + + // snap verticies of set to even,even or odd,odd coordinates + void snap() const; + + // |= &= += *= -= ^= binary operators + polygon_45_set_data& operator|=(const polygon_45_set_data& b); + polygon_45_set_data& operator&=(const polygon_45_set_data& b); + polygon_45_set_data& operator+=(const polygon_45_set_data& b); + polygon_45_set_data& operator*=(const polygon_45_set_data& b); + polygon_45_set_data& operator-=(const polygon_45_set_data& b); + polygon_45_set_data& operator^=(const polygon_45_set_data& b); + + // resizing operations + polygon_45_set_data& operator+=(Unit delta); + polygon_45_set_data& operator-=(Unit delta); + + // shrink the Polygon45Set by shrinking + polygon_45_set_data& resize(coordinate_type resizing, RoundingOption rounding = CLOSEST, + CornerOption corner = INTERSECTION); + + // transform set + template + polygon_45_set_data& transform(const transformation_type& tr); + + // scale set + polygon_45_set_data& scale_up(typename coordinate_traits::unsigned_area_type factor); + polygon_45_set_data& scale_down(typename coordinate_traits::unsigned_area_type factor); + polygon_45_set_data& scale(double scaling); + + // self_intersect + polygon_45_set_data& self_intersect() { + sort(); + applyAdaptiveUnary_<1>(); //1 = AND + dirty_ = false; + return *this; + } + + // self_xor + polygon_45_set_data& self_xor() { + sort(); + applyAdaptiveUnary_<3>(); //3 = XOR + dirty_ = false; + return *this; + } + + // accumulate the bloated polygon + template + polygon_45_set_data& insert_with_resize(const geometry_type& poly, + coordinate_type resizing, RoundingOption rounding = CLOSEST, + CornerOption corner = INTERSECTION, + bool hole = false) { + return insert_with_resize_dispatch(poly, resizing, rounding, corner, hole, typename geometry_concept::type()); + } + + private: + mutable value_type error_data_; + mutable value_type data_; + mutable bool dirty_; + mutable bool unsorted_; + mutable bool is_manhattan_; + + private: + //functions + template + void get_dispatch(output_container& output, polygon_45_concept tag) const { + get_fracture(output, true, tag); + } + template + void get_dispatch(output_container& output, polygon_45_with_holes_concept tag) const { + get_fracture(output, false, tag); + } + template + void get_dispatch(output_container& output, polygon_concept tag) const { + get_fracture(output, true, tag); + } + template + void get_dispatch(output_container& output, polygon_with_holes_concept tag) const { + get_fracture(output, false, tag); + } + template + void get_fracture(output_container& container, bool fracture_holes, concept_type ) const { + clean(); + typename polygon_45_formation::Polygon45Formation pf(fracture_holes); + //std::cout << "FORMING POLYGONS\n"; + pf.scan(container, data_.begin(), data_.end()); + } + + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, undefined_concept) { + insert(geometry_object.begin(), geometry_object.end(), is_hole); + } + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, rectangle_concept tag); + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, polygon_90_concept ) { + insert_vertex_sequence(begin_points(geometry_object), end_points(geometry_object), winding(geometry_object), is_hole); + } + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, polygon_90_with_holes_concept ) { + insert_vertex_sequence(begin_points(geometry_object), end_points(geometry_object), winding(geometry_object), is_hole); + for(typename polygon_with_holes_traits::iterator_holes_type itr = + begin_holes(geometry_object); itr != end_holes(geometry_object); + ++itr) { + insert_vertex_sequence(begin_points(*itr), end_points(*itr), winding(*itr), !is_hole); + } + } + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, polygon_45_concept ) { + insert_vertex_sequence(begin_points(geometry_object), end_points(geometry_object), winding(geometry_object), is_hole); + } + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, polygon_45_with_holes_concept ) { + insert_vertex_sequence(begin_points(geometry_object), end_points(geometry_object), winding(geometry_object), is_hole); + for(typename polygon_with_holes_traits::iterator_holes_type itr = + begin_holes(geometry_object); itr != end_holes(geometry_object); + ++itr) { + insert_vertex_sequence(begin_points(*itr), end_points(*itr), winding(*itr), !is_hole); + } + } + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, polygon_45_set_concept ) { + polygon_45_set_data ps; + assign(ps, geometry_object); + insert(ps, is_hole); + } + template + void insert_dispatch(const geometry_type& geometry_object, bool is_hole, polygon_90_set_concept ) { + std::list > pl; + assign(pl, geometry_object); + insert(pl.begin(), pl.end(), is_hole); + } + + void insert_vertex_half_edge_45_pair(const point_data& pt1, point_data& pt2, + const point_data& pt3, direction_1d wdir); + + template + polygon_45_set_data& insert_with_resize_dispatch(const geometry_type& poly, + coordinate_type resizing, RoundingOption rounding, + CornerOption corner, bool hole, polygon_45_concept tag); + + // accumulate the bloated polygon with holes + template + polygon_45_set_data& insert_with_resize_dispatch(const geometry_type& poly, + coordinate_type resizing, RoundingOption rounding, + CornerOption corner, bool hole, polygon_45_with_holes_concept tag); + + static void snap_vertex_45(Vertex45Compact& vertex); + + public: + template + void applyAdaptiveBoolean_(const polygon_45_set_data& rvalue) const; + template + void applyAdaptiveBoolean_(polygon_45_set_data& result, const polygon_45_set_data& rvalue) const; + template + void applyAdaptiveUnary_() const; + }; + + template + struct geometry_concept > { + typedef polygon_45_set_concept type; + }; + + template + void scale_up_vertex_45_compact_range(iT beginr, iT endr, T factor) { + for( ; beginr != endr; ++beginr) { + scale_up((*beginr).pt, factor); + } + } + template + void scale_down_vertex_45_compact_range_blindly(iT beginr, iT endr, T factor) { + for( ; beginr != endr; ++beginr) { + scale_down((*beginr).pt, factor); + } + } + + template + inline std::pair characterizeEdge45(const point_data& pt1, const point_data& pt2) { + std::pair retval(0, 1); + if(pt1.x() == pt2.x()) { + retval.first = 3; + retval.second = -1; + return retval; + } + //retval.second = pt1.x() < pt2.x() ? -1 : 1; + retval.second = 1; + if(pt1.y() == pt2.y()) { + retval.first = 1; + } else if(pt1.x() < pt2.x()) { + if(pt1.y() < pt2.y()) { + retval.first = 2; + } else { + retval.first = 0; + } + } else { + if(pt1.y() < pt2.y()) { + retval.first = 0; + } else { + retval.first = 2; + } + } + return retval; + } + + template + bool insert_vertex_half_edge_45_pair_into_vector(cT& output, + const pT& pt1, pT& pt2, + const pT& pt3, + direction_1d wdir) { + int multiplier = wdir == LOW ? -1 : 1; + typename cT::value_type vertex(pt2, 0, 0); + //std::cout << pt1 << " " << pt2 << " " << pt3 << std::endl; + std::pair check; + check = characterizeEdge45(pt1, pt2); + //std::cout << "index " << check.first << " " << check.second * -multiplier << std::endl; + vertex.count[check.first] += check.second * -multiplier; + check = characterizeEdge45(pt2, pt3); + //std::cout << "index " << check.first << " " << check.second * multiplier << std::endl; + vertex.count[check.first] += check.second * multiplier; + output.push_back(vertex); + return vertex.count.is_45(); + } + + template + inline void polygon_45_set_data::insert_vertex_half_edge_45_pair(const point_data& pt1, point_data& pt2, + const point_data& pt3, + direction_1d wdir) { + if(insert_vertex_half_edge_45_pair_into_vector(data_, pt1, pt2, pt3, wdir)) is_manhattan_ = false; + } + + template + template + inline void polygon_45_set_data::insert_vertex_sequence(iT begin_vertex, iT end_vertex, + direction_1d winding, bool is_hole) { + if(begin_vertex == end_vertex) return; + if(is_hole) winding = winding.backward(); + iT itr = begin_vertex; + if(itr == end_vertex) return; + point_data firstPt = *itr; + ++itr; + point_data secondPt(firstPt); + //skip any duplicate points + do { + if(itr == end_vertex) return; + secondPt = *itr; + ++itr; + } while(secondPt == firstPt); + point_data prevPt = secondPt; + point_data prevPrevPt = firstPt; + while(itr != end_vertex) { + point_data pt = *itr; + //skip any duplicate points + if(pt == prevPt) { + ++itr; + continue; + } + //operate on the three points + insert_vertex_half_edge_45_pair(prevPrevPt, prevPt, pt, winding); + prevPrevPt = prevPt; + prevPt = pt; + ++itr; + } + if(prevPt != firstPt) { + insert_vertex_half_edge_45_pair(prevPrevPt, prevPt, firstPt, winding); + insert_vertex_half_edge_45_pair(prevPt, firstPt, secondPt, winding); + } else { + insert_vertex_half_edge_45_pair(prevPrevPt, firstPt, secondPt, winding); + } + dirty_ = true; + unsorted_ = true; + } + + // insert polygon set + template + inline void polygon_45_set_data::insert(const polygon_45_set_data& polygon_set, bool is_hole) { + std::size_t count = data_.size(); + data_.insert(data_.end(), polygon_set.data_.begin(), polygon_set.data_.end()); + error_data_.insert(error_data_.end(), polygon_set.error_data_.begin(), + polygon_set.error_data_.end()); + if(is_hole) { + for(std::size_t i = count; i < data_.size(); ++i) { + data_[i].count = data_[i].count.invert(); + } + } + dirty_ = true; + unsorted_ = true; + if(polygon_set.is_manhattan_ == false) is_manhattan_ = false; + return; + } + // insert polygon set + template + template + inline void polygon_45_set_data::insert(const polygon_45_set_data& polygon_set, bool is_hole) { + std::size_t count = data_.size(); + for(typename polygon_45_set_data::iterator_type itr = polygon_set.begin(); + itr != polygon_set.end(); ++itr) { + const typename polygon_45_set_data::Vertex45Compact& v = *itr; + typename polygon_45_set_data::Vertex45Compact v2; + v2.pt.x(static_cast(v.pt.x())); + v2.pt.y(static_cast(v.pt.y())); + v2.count = typename polygon_45_formation::Vertex45Count(v.count[0], v.count[1], v.count[2], v.count[3]); + data_.push_back(v2); + } + polygon_45_set_data tmp; + polygon_set.get_error_data(tmp); + for(typename polygon_45_set_data::iterator_type itr = tmp.begin(); + itr != tmp.end(); ++itr) { + const typename polygon_45_set_data::Vertex45Compact& v = *itr; + typename polygon_45_set_data::Vertex45Compact v2; + v2.pt.x(static_cast(v.pt.x())); + v2.pt.y(static_cast(v.pt.y())); + v2.count = typename polygon_45_formation::Vertex45Count(v.count[0], v.count[1], v.count[2], v.count[3]); + error_data_.push_back(v2); + } + if(is_hole) { + for(std::size_t i = count; i < data_.size(); ++i) { + data_[i].count = data_[i].count.invert(); + } + } + dirty_ = true; + unsorted_ = true; + if(polygon_set.is_manhattan() == false) is_manhattan_ = false; + return; + } + + template + void insert_rectangle_into_vector_45(cT& output, const rT& rect, bool is_hole) { + point_data::coordinate_type> + llpt = ll(rect), lrpt = lr(rect), ulpt = ul(rect), urpt = ur(rect); + direction_1d dir = COUNTERCLOCKWISE; + if(is_hole) dir = CLOCKWISE; + insert_vertex_half_edge_45_pair_into_vector(output, llpt, lrpt, urpt, dir); + insert_vertex_half_edge_45_pair_into_vector(output, lrpt, urpt, ulpt, dir); + insert_vertex_half_edge_45_pair_into_vector(output, urpt, ulpt, llpt, dir); + insert_vertex_half_edge_45_pair_into_vector(output, ulpt, llpt, lrpt, dir); + } + + template + template + inline void polygon_45_set_data::insert_dispatch(const geometry_type& geometry_object, + bool is_hole, rectangle_concept ) { + dirty_ = true; + unsorted_ = true; + insert_rectangle_into_vector_45(data_, geometry_object, is_hole); + } + + // get the external boundary rectangle + template + template + inline bool polygon_45_set_data::extents(rectangle_type& rect) const{ + clean(); + if(empty()) { + return false; + } + Unit low = (std::numeric_limits::max)(); + Unit high = (std::numeric_limits::min)(); + interval_data xivl(low, high); + interval_data yivl(low, high); + for(typename value_type::const_iterator itr = data_.begin(); + itr != data_.end(); ++ itr) { + if((*itr).pt.x() > xivl.get(HIGH)) + xivl.set(HIGH, (*itr).pt.x()); + if((*itr).pt.x() < xivl.get(LOW)) + xivl.set(LOW, (*itr).pt.x()); + if((*itr).pt.y() > yivl.get(HIGH)) + yivl.set(HIGH, (*itr).pt.y()); + if((*itr).pt.y() < yivl.get(LOW)) + yivl.set(LOW, (*itr).pt.y()); + } + rect = construct(xivl, yivl); + return true; + } + + //this function snaps the vertex and two half edges + //to be both even or both odd coordinate values if one of the edges is 45 + //and throws an excpetion if an edge is non-manhattan, non-45. + template + inline void polygon_45_set_data::snap_vertex_45(typename polygon_45_set_data::Vertex45Compact& vertex) { + bool plus45 = vertex.count[2] != 0; + bool minus45 = vertex.count[0] != 0; + if(plus45 || minus45) { + if(local_abs(vertex.pt.x()) % 2 != local_abs(vertex.pt.y()) % 2) { + if(vertex.count[1] != 0 || + (plus45 && minus45)) { + //move right + vertex.pt.x(vertex.pt.x() + 1); + } else { + //assert that vertex.count[3] != 0 + Unit modifier = plus45 ? -1 : 1; + vertex.pt.y(vertex.pt.y() + modifier); + } + } + } + } + + template + inline void polygon_45_set_data::snap() const { + for(typename value_type::iterator itr = data_.begin(); + itr != data_.end(); ++itr) { + snap_vertex_45(*itr); + } + } + + // |= &= += *= -= ^= binary operators + template + inline polygon_45_set_data& polygon_45_set_data::operator|=(const polygon_45_set_data& b) { + insert(b); + return *this; + } + template + inline polygon_45_set_data& polygon_45_set_data::operator&=(const polygon_45_set_data& b) { + //b.sort(); + //sort(); + applyAdaptiveBoolean_<1>(b); + dirty_ = false; + unsorted_ = false; + return *this; + } + template + inline polygon_45_set_data& polygon_45_set_data::operator+=(const polygon_45_set_data& b) { + return (*this) |= b; + } + template + inline polygon_45_set_data& polygon_45_set_data::operator*=(const polygon_45_set_data& b) { + return (*this) &= b; + } + template + inline polygon_45_set_data& polygon_45_set_data::operator-=(const polygon_45_set_data& b) { + //b.sort(); + //sort(); + applyAdaptiveBoolean_<2>(b); + dirty_ = false; + unsorted_ = false; + return *this; + } + template + inline polygon_45_set_data& polygon_45_set_data::operator^=(const polygon_45_set_data& b) { + //b.sort(); + //sort(); + applyAdaptiveBoolean_<3>(b); + dirty_ = false; + unsorted_ = false; + return *this; + } + + template + inline polygon_45_set_data& polygon_45_set_data::operator+=(Unit delta) { + return resize(delta); + } + template + inline polygon_45_set_data& polygon_45_set_data::operator-=(Unit delta) { + return (*this) += -delta; + } + + template + inline polygon_45_set_data& + polygon_45_set_data::resize(Unit resizing, RoundingOption rounding, CornerOption corner) { + if(resizing == 0) return *this; + std::list > pl; + get_polygons_with_holes(pl); + clear(); + for(typename std::list >::iterator itr = pl.begin(); itr != pl.end(); ++itr) { + insert_with_resize(*itr, resizing, rounding, corner); + } + clean(); + //perterb 45 edges to prevent non-integer intersection errors upon boolean op + //snap(); + return *this; + } + + //distance is assumed to be positive + inline int roundClosest(double distance) { + int f = (int)distance; + if(distance - (double)f < 0.5) return f; + return f+1; + } + + //distance is assumed to be positive + template + inline Unit roundWithOptions(double distance, RoundingOption rounding) { + if(rounding == CLOSEST) { + return roundClosest(distance); + } else if(rounding == OVERSIZE) { + return (Unit)distance + 1; + } else { //UNDERSIZE + return (Unit)distance; + } + } + + // 0 is east, 1 is northeast, 2 is north, 3 is northwest, 4 is west, 5 is southwest, 6 is south + // 7 is southwest + template + inline point_data bloatVertexInDirWithOptions(const point_data& point, unsigned int dir, + Unit bloating, RoundingOption rounding) { + const double sqrt2 = 1.4142135623730950488016887242097; + if(dir & 1) { + Unit unitDistance = (Unit)bloating; + if(rounding != SQRT2) { + //45 degree bloating + double distance = (double)bloating; + distance /= sqrt2; // multiply by 1/sqrt2 + unitDistance = roundWithOptions(distance, rounding); + } + int xMultiplier = 1; + int yMultiplier = 1; + if(dir == 3 || dir == 5) xMultiplier = -1; + if(dir == 5 || dir == 7) yMultiplier = -1; + return point_data(point.x()+xMultiplier*unitDistance, + point.y()+yMultiplier*unitDistance); + } else { + if(dir == 0) + return point_data(point.x()+bloating, point.y()); + if(dir == 2) + return point_data(point.x(), point.y()+bloating); + if(dir == 4) + return point_data(point.x()-bloating, point.y()); + if(dir == 6) + return point_data(point.x(), point.y()-bloating); + return point_data(); + } + } + + template + inline unsigned int getEdge45Direction(const point_data& pt1, const point_data& pt2) { + if(pt1.x() == pt2.x()) { + if(pt1.y() < pt2.y()) return 2; + return 6; + } + if(pt1.y() == pt2.y()) { + if(pt1.x() < pt2.x()) return 0; + return 4; + } + if(pt2.y() > pt1.y()) { + if(pt2.x() > pt1.x()) return 1; + return 3; + } + if(pt2.x() > pt1.x()) return 7; + return 5; + } + + inline unsigned int getEdge45NormalDirection(unsigned int dir, int multiplier) { + if(multiplier < 0) + return (dir + 2) % 8; + return (dir + 4 + 2) % 8; + } + + template + inline point_data getIntersectionPoint(const point_data& pt1, unsigned int slope1, + const point_data& pt2, unsigned int slope2) { + //the intention here is to use all integer arithmetic without causing overflow + //turncation error or divide by zero error + //I don't use floating point arithmetic because its precision may not be high enough + //at the extremes of the integer range + typedef typename coordinate_traits::area_type LongUnit; + const Unit rises[8] = {0, 1, 1, 1, 0, -1, -1, -1}; + const Unit runs[8] = {1, 1, 0, -1, -1, -1, 0, 1}; + LongUnit rise1 = rises[slope1]; + LongUnit rise2 = rises[slope2]; + LongUnit run1 = runs[slope1]; + LongUnit run2 = runs[slope2]; + LongUnit x1 = (LongUnit)pt1.x(); + LongUnit x2 = (LongUnit)pt2.x(); + LongUnit y1 = (LongUnit)pt1.y(); + LongUnit y2 = (LongUnit)pt2.y(); + Unit x = 0; + Unit y = 0; + if(run1 == 0) { + x = pt1.x(); + y = (Unit)(((x1 - x2) * rise2) / run2) + pt2.y(); + } else if(run2 == 0) { + x = pt2.x(); + y = (Unit)(((x2 - x1) * rise1) / run1) + pt1.y(); + } else { + // y - y1 = (rise1/run1)(x - x1) + // y - y2 = (rise2/run2)(x - x2) + // y = (rise1/run1)(x - x1) + y1 = (rise2/run2)(x - x2) + y2 + // (rise1/run1 - rise2/run2)x = y2 - y1 + rise1/run1 x1 - rise2/run2 x2 + // x = (y2 - y1 + rise1/run1 x1 - rise2/run2 x2)/(rise1/run1 - rise2/run2) + // x = (y2 - y1 + rise1/run1 x1 - rise2/run2 x2)(rise1 run2 - rise2 run1)/(run1 run2) + x = (Unit)((y2 - y1 + ((rise1 * x1) / run1) - ((rise2 * x2) / run2)) * + (run1 * run2) / (rise1 * run2 - rise2 * run1)); + if(rise1 == 0) { + y = pt1.y(); + } else if(rise2 == 0) { + y = pt2.y(); + } else { + // y - y1 = (rise1/run1)(x - x1) + // (run1/rise1)(y - y1) = x - x1 + // x = (run1/rise1)(y - y1) + x1 = (run2/rise2)(y - y2) + x2 + y = (Unit)((x2 - x1 + ((run1 * y1) / rise1) - ((run2 * y2) / rise2)) * + (rise1 * rise2) / (run1 * rise2 - run2 * rise1)); + } + } + return point_data(x, y); + } + + template + inline + void handleResizingEdge45_SQRT1OVER2(polygon_45_set_data& sizingSet, point_data first, + point_data second, Unit resizing, CornerOption corner) { + if(first.x() == second.x()) { + sizingSet.insert(rectangle_data(first.x() - resizing, first.y(), first.x() + resizing, second.y())); + return; + } + if(first.y() == second.y()) { + sizingSet.insert(rectangle_data(first.x(), first.y() - resizing, second.x(), first.y() + resizing)); + return; + } + std::vector > pts; + Unit bloating = resizing < 0 ? -resizing : resizing; + if(corner == UNFILLED) { + //we have to round up + bloating = bloating / 2 + bloating % 2 ; //round up + if(second.x() < first.x()) std::swap(first, second); + if(first.y() < second.y()) { //upward sloping + pts.push_back(point_data(first.x() + bloating, first.y() - bloating)); + pts.push_back(point_data(first.x() - bloating, first.y() + bloating)); + pts.push_back(point_data(second.x() - bloating, second.y() + bloating)); + pts.push_back(point_data(second.x() + bloating, second.y() - bloating)); + sizingSet.insert_vertex_sequence(pts.begin(), pts.end(), CLOCKWISE, false); + } else { //downward sloping + pts.push_back(point_data(first.x() + bloating, first.y() + bloating)); + pts.push_back(point_data(first.x() - bloating, first.y() - bloating)); + pts.push_back(point_data(second.x() - bloating, second.y() - bloating)); + pts.push_back(point_data(second.x() + bloating, second.y() + bloating)); + sizingSet.insert_vertex_sequence(pts.begin(), pts.end(), COUNTERCLOCKWISE, false); + } + return; + } + if(second.x() < first.x()) std::swap(first, second); + if(first.y() < second.y()) { //upward sloping + pts.push_back(point_data(first.x(), first.y() - bloating)); + pts.push_back(point_data(first.x() - bloating, first.y())); + pts.push_back(point_data(second.x(), second.y() + bloating)); + pts.push_back(point_data(second.x() + bloating, second.y())); + sizingSet.insert_vertex_sequence(pts.begin(), pts.end(), CLOCKWISE, false); + } else { //downward sloping + pts.push_back(point_data(first.x() - bloating, first.y())); + pts.push_back(point_data(first.x(), first.y() + bloating)); + pts.push_back(point_data(second.x() + bloating, second.y())); + pts.push_back(point_data(second.x(), second.y() - bloating)); + sizingSet.insert_vertex_sequence(pts.begin(), pts.end(), CLOCKWISE, false); + } + } + + + template + inline + void handleResizingEdge45(polygon_45_set_data& sizingSet, point_data first, + point_data second, Unit resizing, RoundingOption rounding) { + if(first.x() == second.x()) { + sizingSet.insert(rectangle_data(first.x() - resizing, first.y(), first.x() + resizing, second.y())); + return; + } + if(first.y() == second.y()) { + sizingSet.insert(rectangle_data(first.x(), first.y() - resizing, second.x(), first.y() + resizing)); + return; + } + //edge is 45 + std::vector > pts; + Unit bloating = resizing < 0 ? -resizing : resizing; + if(second.x() < first.x()) std::swap(first, second); + if(first.y() < second.y()) { + pts.push_back(bloatVertexInDirWithOptions(first, 3, bloating, rounding)); + pts.push_back(bloatVertexInDirWithOptions(first, 7, bloating, rounding)); + pts.push_back(bloatVertexInDirWithOptions(second, 7, bloating, rounding)); + pts.push_back(bloatVertexInDirWithOptions(second, 3, bloating, rounding)); + sizingSet.insert_vertex_sequence(pts.begin(), pts.end(), HIGH, false); + } else { + pts.push_back(bloatVertexInDirWithOptions(first, 1, bloating, rounding)); + pts.push_back(bloatVertexInDirWithOptions(first, 5, bloating, rounding)); + pts.push_back(bloatVertexInDirWithOptions(second, 5, bloating, rounding)); + pts.push_back(bloatVertexInDirWithOptions(second, 1, bloating, rounding)); + sizingSet.insert_vertex_sequence(pts.begin(), pts.end(), HIGH, false); + } + } + + template + inline point_data bloatVertexInDirWithSQRT1OVER2(int edge1, int normal1, const point_data& second, Unit bloating, + bool first) { + orientation_2d orient = first ? HORIZONTAL : VERTICAL; + orientation_2d orientp = orient.get_perpendicular(); + int multiplier = first ? 1 : -1; + point_data pt1(second); + if(edge1 == 1) { + if(normal1 == 3) { + move(pt1, orient, -multiplier * bloating); + } else { + move(pt1, orientp, -multiplier * bloating); + } + } else if(edge1 == 3) { + if(normal1 == 1) { + move(pt1, orient, multiplier * bloating); + } else { + move(pt1, orientp, -multiplier * bloating); + } + } else if(edge1 == 5) { + if(normal1 == 3) { + move(pt1, orientp, multiplier * bloating); + } else { + move(pt1, orient, multiplier * bloating); + } + } else { + if(normal1 == 5) { + move(pt1, orient, -multiplier * bloating); + } else { + move(pt1, orientp, multiplier * bloating); + } + } + return pt1; + } + + template + inline + void handleResizingVertex45(polygon_45_set_data& sizingSet, const point_data& first, + const point_data& second, const point_data& third, Unit resizing, + RoundingOption rounding, CornerOption corner, + int multiplier) { + unsigned int edge1 = getEdge45Direction(first, second); + unsigned int edge2 = getEdge45Direction(second, third); + unsigned int diffAngle; + if(multiplier < 0) + diffAngle = (edge2 + 8 - edge1) % 8; + else + diffAngle = (edge1 + 8 - edge2) % 8; + if(diffAngle < 4) { + if(resizing > 0) return; //accute interior corner + else multiplier *= -1; //make it appear to be an accute exterior angle + } + Unit bloating = local_abs(resizing); + if(rounding == SQRT1OVER2) { + if(edge1 % 2 && edge2 % 2) return; + if(corner == ORTHOGONAL && edge1 % 2 == 0 && edge2 % 2 == 0) { + rectangle_data insertion_rect; + set_points(insertion_rect, second, second); + bloat(insertion_rect, bloating); + sizingSet.insert(insertion_rect); + } else if(corner != ORTHOGONAL) { + point_data pt1(0, 0); + point_data pt2(0, 0); + unsigned int normal1 = getEdge45NormalDirection(edge1, multiplier); + unsigned int normal2 = getEdge45NormalDirection(edge2, multiplier); + if(edge1 % 2) { + pt1 = bloatVertexInDirWithSQRT1OVER2(edge1, normal1, second, bloating, true); + } else { + pt1 = bloatVertexInDirWithOptions(second, normal1, bloating, UNDERSIZE); + } + if(edge2 % 2) { + pt2 = bloatVertexInDirWithSQRT1OVER2(edge2, normal2, second, bloating, false); + } else { + pt2 = bloatVertexInDirWithOptions(second, normal2, bloating, UNDERSIZE); + } + std::vector > pts; + pts.push_back(pt1); + pts.push_back(second); + pts.push_back(pt2); + pts.push_back(getIntersectionPoint(pt1, edge1, pt2, edge2)); + polygon_45_data poly(pts.begin(), pts.end()); + sizingSet.insert(poly); + } else { + //ORTHOGONAL of a 45 degree corner + int normal = 0; + if(edge1 % 2) { + normal = getEdge45NormalDirection(edge2, multiplier); + } else { + normal = getEdge45NormalDirection(edge1, multiplier); + } + rectangle_data insertion_rect; + point_data edgePoint = bloatVertexInDirWithOptions(second, normal, bloating, UNDERSIZE); + set_points(insertion_rect, second, edgePoint); + if(normal == 0 || normal == 4) + bloat(insertion_rect, VERTICAL, bloating); + else + bloat(insertion_rect, HORIZONTAL, bloating); + sizingSet.insert(insertion_rect); + } + return; + } + unsigned int normal1 = getEdge45NormalDirection(edge1, multiplier); + unsigned int normal2 = getEdge45NormalDirection(edge2, multiplier); + point_data edgePoint1 = bloatVertexInDirWithOptions(second, normal1, bloating, rounding); + point_data edgePoint2 = bloatVertexInDirWithOptions(second, normal2, bloating, rounding); + //if the change in angle is 135 degrees it is an accute exterior corner + if((edge1+ multiplier * 3) % 8 == edge2) { + if(corner == ORTHOGONAL) { + rectangle_data insertion_rect; + set_points(insertion_rect, edgePoint1, edgePoint2); + sizingSet.insert(insertion_rect); + return; + } + } + std::vector > pts; + pts.push_back(edgePoint1); + pts.push_back(second); + pts.push_back(edgePoint2); + pts.push_back(getIntersectionPoint(edgePoint1, edge1, edgePoint2, edge2)); + polygon_45_data poly(pts.begin(), pts.end()); + sizingSet.insert(poly); + } + + template + template + inline polygon_45_set_data& + polygon_45_set_data::insert_with_resize_dispatch(const geometry_type& poly, + coordinate_type resizing, + RoundingOption rounding, + CornerOption corner, + bool hole, polygon_45_concept ) { + direction_1d wdir = winding(poly); + int multiplier = wdir == LOW ? -1 : 1; + if(hole) resizing *= -1; + typedef typename polygon_45_data::iterator_type piterator; + piterator first, second, third, end, real_end; + real_end = end_points(poly); + third = begin_points(poly); + first = third; + if(first == real_end) return *this; + ++third; + if(third == real_end) return *this; + second = end = third; + ++third; + if(third == real_end) return *this; + polygon_45_set_data sizingSet; + //insert minkofski shapes on edges and corners + do { + if(rounding != SQRT1OVER2) { + handleResizingEdge45(sizingSet, *first, *second, resizing, rounding); + } else { + handleResizingEdge45_SQRT1OVER2(sizingSet, *first, *second, resizing, corner); + } + if(corner != UNFILLED) + handleResizingVertex45(sizingSet, *first, *second, *third, resizing, rounding, corner, multiplier); + first = second; + second = third; + ++third; + if(third == real_end) { + third = begin_points(poly); + if(*second == *third) { + ++third; //skip first point if it is duplicate of last point + } + } + } while(second != end); + //sizingSet.snap(); + polygon_45_set_data tmp; + //insert original shape + tmp.insert_dispatch(poly, false, polygon_45_concept()); + if(resizing < 0) tmp -= sizingSet; + else tmp += sizingSet; + tmp.clean(); + insert(tmp, hole); + dirty_ = true; + unsorted_ = true; + return (*this); + } + + // accumulate the bloated polygon with holes + template + template + inline polygon_45_set_data& + polygon_45_set_data::insert_with_resize_dispatch(const geometry_type& poly, + coordinate_type resizing, + RoundingOption rounding, + CornerOption corner, + bool hole, polygon_45_with_holes_concept ) { + insert_with_resize_dispatch(poly, resizing, rounding, corner, hole, polygon_45_concept()); + for(typename polygon_with_holes_traits::iterator_holes_type itr = + begin_holes(poly); itr != end_holes(poly); + ++itr) { + insert_with_resize_dispatch(*itr, resizing, rounding, corner, !hole, polygon_45_concept()); + } + return *this; + } + + // transform set + template + template + inline polygon_45_set_data& polygon_45_set_data::transform(const transformation_type& tr){ + clean(); + std::vector > polys; + get(polys); + for(typename std::vector >::iterator itr = polys.begin(); + itr != polys.end(); ++itr) { + ::boost::polygon::transform(*itr, tr); + } + clear(); + insert(polys.begin(), polys.end()); + dirty_ = true; + unsorted_ = true; + return *this; + } + + template + inline polygon_45_set_data& polygon_45_set_data::scale_up(typename coordinate_traits::unsigned_area_type factor) { + scale_up_vertex_45_compact_range(data_.begin(), data_.end(), factor); + return *this; + } + + template + inline polygon_45_set_data& polygon_45_set_data::scale_down(typename coordinate_traits::unsigned_area_type factor) { + clean(); + std::vector > polys; + get_polygons_with_holes(polys); + for(typename std::vector >::iterator itr = polys.begin(); + itr != polys.end(); ++itr) { + ::boost::polygon::scale_down(*itr, factor); + } + clear(); + insert(polys.begin(), polys.end()); + dirty_ = true; + unsorted_ = true; + return *this; + } + + template + inline polygon_45_set_data& polygon_45_set_data::scale(double factor) { + clean(); + std::vector > polys; + get_polygons_with_holes(polys); + for(typename std::vector >::iterator itr = polys.begin(); + itr != polys.end(); ++itr) { + ::boost::polygon::scale(*itr, factor); + } + clear(); + insert(polys.begin(), polys.end()); + dirty_ = true; + unsorted_ = true; + return *this; + } + + template + inline bool polygon_45_set_data::clean() const { + if(unsorted_) sort(); + if(dirty_) { + applyAdaptiveUnary_<0>(); + dirty_ = false; + } + return true; + } + + template + template + inline void polygon_45_set_data::applyAdaptiveBoolean_(const polygon_45_set_data& rvalue) const { + polygon_45_set_data tmp; + applyAdaptiveBoolean_(tmp, rvalue); + data_.swap(tmp.data_); //swapping vectors should be constant time operation + error_data_.swap(tmp.error_data_); + is_manhattan_ = tmp.is_manhattan_; + unsorted_ = false; + dirty_ = false; + } + + template + bool applyBoolean45OpOnVectors(std::vector::Vertex45Compact>& result_data, + std::vector::Vertex45Compact>& lvalue_data, + std::vector::Vertex45Compact>& rvalue_data + ) { + bool result_is_manhattan_ = true; + typename boolean_op_45::template Scan45::Count2, + typename boolean_op_45::template boolean_op_45_output_functor > scan45; + std::vector::Vertex45> eventOut; + typedef std::pair::Point, + typename boolean_op_45::template Scan45CountT::Count2> > Scan45Vertex; + std::vector eventIn; + typedef std::vector::Vertex45Compact> value_type; + typename value_type::const_iterator iter1 = lvalue_data.begin(); + typename value_type::const_iterator iter2 = rvalue_data.begin(); + typename value_type::const_iterator end1 = lvalue_data.end(); + typename value_type::const_iterator end2 = rvalue_data.end(); + const Unit2 UnitMax = (std::numeric_limits::max)(); + Unit2 x = UnitMax; + while(iter1 != end1 || iter2 != end2) { + Unit2 currentX = UnitMax; + if(iter1 != end1) currentX = iter1->pt.x(); + if(iter2 != end2) currentX = (std::min)(currentX, iter2->pt.x()); + if(currentX != x) { + //std::cout << "SCAN " << currentX << "\n"; + //scan event + scan45.scan(eventOut, eventIn.begin(), eventIn.end()); + polygon_sort(eventOut.begin(), eventOut.end()); + std::size_t ptCount = 0; + for(std::size_t i = 0; i < eventOut.size(); ++i) { + if(!result_data.empty() && + result_data.back().pt == eventOut[i].pt) { + result_data.back().count += eventOut[i]; + ++ptCount; + } else { + if(!result_data.empty()) { + if(result_data.back().count.is_45()) { + result_is_manhattan_ = false; + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + } + result_data.push_back(eventOut[i]); + ptCount = 1; + } + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + eventOut.clear(); + eventIn.clear(); + x = currentX; + } + //std::cout << "get next\n"; + if(iter2 != end2 && (iter1 == end1 || iter2->pt.x() < iter1->pt.x() || + (iter2->pt.x() == iter1->pt.x() && + iter2->pt.y() < iter1->pt.y()) )) { + //std::cout << "case1 next\n"; + eventIn.push_back(Scan45Vertex + (iter2->pt, + typename polygon_45_formation:: + Scan45Count(typename polygon_45_formation::Count2(0, iter2->count[0]), + typename polygon_45_formation::Count2(0, iter2->count[1]), + typename polygon_45_formation::Count2(0, iter2->count[2]), + typename polygon_45_formation::Count2(0, iter2->count[3])))); + ++iter2; + } else if(iter1 != end1 && (iter2 == end2 || iter1->pt.x() < iter2->pt.x() || + (iter1->pt.x() == iter2->pt.x() && + iter1->pt.y() < iter2->pt.y()) )) { + //std::cout << "case2 next\n"; + eventIn.push_back(Scan45Vertex + (iter1->pt, + typename polygon_45_formation:: + Scan45Count( + typename polygon_45_formation::Count2(iter1->count[0], 0), + typename polygon_45_formation::Count2(iter1->count[1], 0), + typename polygon_45_formation::Count2(iter1->count[2], 0), + typename polygon_45_formation::Count2(iter1->count[3], 0)))); + ++iter1; + } else { + //std::cout << "case3 next\n"; + eventIn.push_back(Scan45Vertex + (iter2->pt, + typename polygon_45_formation:: + Scan45Count(typename polygon_45_formation::Count2(iter1->count[0], + iter2->count[0]), + typename polygon_45_formation::Count2(iter1->count[1], + iter2->count[1]), + typename polygon_45_formation::Count2(iter1->count[2], + iter2->count[2]), + typename polygon_45_formation::Count2(iter1->count[3], + iter2->count[3])))); + ++iter1; + ++iter2; + } + } + scan45.scan(eventOut, eventIn.begin(), eventIn.end()); + polygon_sort(eventOut.begin(), eventOut.end()); + + std::size_t ptCount = 0; + for(std::size_t i = 0; i < eventOut.size(); ++i) { + if(!result_data.empty() && + result_data.back().pt == eventOut[i].pt) { + result_data.back().count += eventOut[i]; + ++ptCount; + } else { + if(!result_data.empty()) { + if(result_data.back().count.is_45()) { + result_is_manhattan_ = false; + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + } + result_data.push_back(eventOut[i]); + ptCount = 1; + } + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + if(!result_data.empty() && + result_data.back().count.is_45()) { + result_is_manhattan_ = false; + } + return result_is_manhattan_; + } + + template + bool applyUnary45OpOnVectors(std::vector::Vertex45Compact>& result_data, + std::vector::Vertex45Compact>& lvalue_data ) { + bool result_is_manhattan_ = true; + typename boolean_op_45::template Scan45::Count1, + typename boolean_op_45::template unary_op_45_output_functor > scan45; + std::vector::Vertex45> eventOut; + typedef typename boolean_op_45::template Scan45CountT::Count1> Scan45Count; + typedef std::pair::Point, Scan45Count> Scan45Vertex; + std::vector eventIn; + typedef std::vector::Vertex45Compact> value_type; + typename value_type::const_iterator iter1 = lvalue_data.begin(); + typename value_type::const_iterator end1 = lvalue_data.end(); + const Unit2 UnitMax = (std::numeric_limits::max)(); + Unit2 x = UnitMax; + while(iter1 != end1) { + Unit2 currentX = iter1->pt.x(); + if(currentX != x) { + //std::cout << "SCAN " << currentX << "\n"; + //scan event + scan45.scan(eventOut, eventIn.begin(), eventIn.end()); + polygon_sort(eventOut.begin(), eventOut.end()); + std::size_t ptCount = 0; + for(std::size_t i = 0; i < eventOut.size(); ++i) { + if(!result_data.empty() && + result_data.back().pt == eventOut[i].pt) { + result_data.back().count += eventOut[i]; + ++ptCount; + } else { + if(!result_data.empty()) { + if(result_data.back().count.is_45()) { + result_is_manhattan_ = false; + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + } + result_data.push_back(eventOut[i]); + ptCount = 1; + } + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + eventOut.clear(); + eventIn.clear(); + x = currentX; + } + //std::cout << "get next\n"; + eventIn.push_back(Scan45Vertex + (iter1->pt, + Scan45Count( typename boolean_op_45::Count1(iter1->count[0]), + typename boolean_op_45::Count1(iter1->count[1]), + typename boolean_op_45::Count1(iter1->count[2]), + typename boolean_op_45::Count1(iter1->count[3])))); + ++iter1; + } + scan45.scan(eventOut, eventIn.begin(), eventIn.end()); + polygon_sort(eventOut.begin(), eventOut.end()); + + std::size_t ptCount = 0; + for(std::size_t i = 0; i < eventOut.size(); ++i) { + if(!result_data.empty() && + result_data.back().pt == eventOut[i].pt) { + result_data.back().count += eventOut[i]; + ++ptCount; + } else { + if(!result_data.empty()) { + if(result_data.back().count.is_45()) { + result_is_manhattan_ = false; + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + } + result_data.push_back(eventOut[i]); + ptCount = 1; + } + } + if(ptCount == 2 && result_data.back().count == (typename polygon_45_formation::Vertex45Count(0, 0, 0, 0))) { + result_data.pop_back(); + } + if(!result_data.empty() && + result_data.back().count.is_45()) { + result_is_manhattan_ = false; + } + return result_is_manhattan_; + } + + template + void get_error_rects_shell(cT& posE, cT& negE, iT beginr, iT endr) { + typedef typename std::iterator_traits::value_type Point; + typedef typename point_traits::coordinate_type Unit; + typedef typename coordinate_traits::area_type area_type; + Point pt1, pt2, pt3; + bool i1 = true; + bool i2 = true; + bool not_done = beginr != endr; + bool next_to_last = false; + bool last = false; + Point first, second; + while(not_done) { + if(last) { + last = false; + not_done = false; + pt3 = second; + } else if(next_to_last) { + next_to_last = false; + last = true; + pt3 = first; + } else if(i1) { + const Point& pt = *beginr; + first = pt1 = pt; + i1 = false; + i2 = true; + ++beginr; + if(beginr == endr) return; //too few points + continue; + } else if (i2) { + const Point& pt = *beginr; + second = pt2 = pt; + i2 = false; + ++beginr; + if(beginr == endr) return; //too few points + continue; + } else { + const Point& pt = *beginr; + pt3 = pt; + ++beginr; + if(beginr == endr) { + next_to_last = true; + //skip last point equal to first + continue; + } + } + if(local_abs(x(pt2)) % 2) { //y % 2 should also be odd + //is corner concave or convex? + Point pts[] = {pt1, pt2, pt3}; + area_type ar = point_sequence_area(pts, pts+3); + direction_1d dir = ar < 0 ? COUNTERCLOCKWISE : CLOCKWISE; + //std::cout << pt1 << " " << pt2 << " " << pt3 << " " << ar << std::endl; + if(dir == CLOCKWISE) { + posE.push_back(rectangle_data + (x(pt2) - 1, y(pt2) - 1, x(pt2) + 1, y(pt2) + 1)); + + } else { + negE.push_back(rectangle_data + (x(pt2) - 1, y(pt2) - 1, x(pt2) + 1, y(pt2) + 1)); + } + } + pt1 = pt2; + pt2 = pt3; + } + } + + template + void get_error_rects(cT& posE, cT& negE, const pT& p) { + get_error_rects_shell(posE, negE, p.begin(), p.end()); + for(typename pT::iterator_holes_type iHb = p.begin_holes(); + iHb != p.end_holes(); ++iHb) { + get_error_rects_shell(posE, negE, iHb->begin(), iHb->end()); + } + } + + template + template + inline void polygon_45_set_data::applyAdaptiveBoolean_(polygon_45_set_data& result, + const polygon_45_set_data& rvalue) const { + result.clear(); + result.error_data_ = error_data_; + result.error_data_.insert(result.error_data_.end(), rvalue.error_data_.begin(), + rvalue.error_data_.end()); + if(is_manhattan() && rvalue.is_manhattan()) { + //convert each into polygon_90_set data and call boolean operations + polygon_90_set_data l90sd(VERTICAL), r90sd(VERTICAL), output(VERTICAL); + for(typename value_type::const_iterator itr = data_.begin(); itr != data_.end(); ++itr) { + if((*itr).count[3] == 0) continue; //skip all non vertical edges + l90sd.insert(std::make_pair((*itr).pt.x(), std::make_pair((*itr).pt.y(), (*itr).count[3])), false, VERTICAL); + } + for(typename value_type::const_iterator itr = rvalue.data_.begin(); itr != rvalue.data_.end(); ++itr) { + if((*itr).count[3] == 0) continue; //skip all non vertical edges + r90sd.insert(std::make_pair((*itr).pt.x(), std::make_pair((*itr).pt.y(), (*itr).count[3])), false, VERTICAL); + } + l90sd.sort(); + r90sd.sort(); +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op == 0) { + output.applyBooleanBinaryOp(l90sd.begin(), l90sd.end(), + r90sd.begin(), r90sd.end(), boolean_op::BinaryCount()); + } else if (op == 1) { + output.applyBooleanBinaryOp(l90sd.begin(), l90sd.end(), + r90sd.begin(), r90sd.end(), boolean_op::BinaryCount()); + } else if (op == 2) { + output.applyBooleanBinaryOp(l90sd.begin(), l90sd.end(), + r90sd.begin(), r90sd.end(), boolean_op::BinaryCount()); + } else if (op == 3) { + output.applyBooleanBinaryOp(l90sd.begin(), l90sd.end(), + r90sd.begin(), r90sd.end(), boolean_op::BinaryCount()); + } +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + result.data_.clear(); + result.insert(output); + result.is_manhattan_ = true; + result.dirty_ = false; + result.unsorted_ = false; + } else { + sort(); + rvalue.sort(); + try { + result.is_manhattan_ = applyBoolean45OpOnVectors(result.data_, data_, rvalue.data_); + } catch (std::string str) { + std::string msg = "GTL 45 Boolean error, precision insufficient to represent edge intersection coordinate value."; + if(str == msg) { + result.clear(); + typedef typename coordinate_traits::manhattan_area_type Unit2; + typedef typename polygon_45_formation::Vertex45Compact Vertex45Compact2; + typedef std::vector Data2; + Data2 rvalue_data, lvalue_data, result_data; + rvalue_data.reserve(rvalue.data_.size()); + lvalue_data.reserve(data_.size()); + for(std::size_t i = 0 ; i < data_.size(); ++i) { + const Vertex45Compact& vi = data_[i]; + Vertex45Compact2 ci; + ci.pt = point_data(x(vi.pt), y(vi.pt)); + ci.count = typename polygon_45_formation::Vertex45Count + ( vi.count[0], vi.count[1], vi.count[2], vi.count[3]); + lvalue_data.push_back(ci); + } + for(std::size_t i = 0 ; i < rvalue.data_.size(); ++i) { + const Vertex45Compact& vi = rvalue.data_[i]; + Vertex45Compact2 ci; + ci.pt = (point_data(x(vi.pt), y(vi.pt))); + ci.count = typename polygon_45_formation::Vertex45Count + ( vi.count[0], vi.count[1], vi.count[2], vi.count[3]); + rvalue_data.push_back(ci); + } + scale_up_vertex_45_compact_range(lvalue_data.begin(), lvalue_data.end(), 2); + scale_up_vertex_45_compact_range(rvalue_data.begin(), rvalue_data.end(), 2); + bool result_is_manhattan = applyBoolean45OpOnVectors(result_data, + lvalue_data, + rvalue_data ); + if(!result_is_manhattan) { + typename polygon_45_formation::Polygon45Formation pf(false); + //std::cout << "FORMING POLYGONS\n"; + std::vector > container; + pf.scan(container, result_data.begin(), result_data.end()); + Data2 error_data_out; + std::vector > pos_error_rects; + std::vector > neg_error_rects; + for(std::size_t i = 0; i < container.size(); ++i) { + get_error_rects(pos_error_rects, neg_error_rects, container[i]); + } + for(std::size_t i = 0; i < pos_error_rects.size(); ++i) { + insert_rectangle_into_vector_45(result_data, pos_error_rects[i], false); + insert_rectangle_into_vector_45(error_data_out, pos_error_rects[i], false); + } + for(std::size_t i = 0; i < neg_error_rects.size(); ++i) { + insert_rectangle_into_vector_45(result_data, neg_error_rects[i], true); + insert_rectangle_into_vector_45(error_data_out, neg_error_rects[i], false); + } + scale_down_vertex_45_compact_range_blindly(error_data_out.begin(), error_data_out.end(), 2); + for(std::size_t i = 0 ; i < error_data_out.size(); ++i) { + const Vertex45Compact2& vi = error_data_out[i]; + Vertex45Compact ci; + ci.pt.x(static_cast(x(vi.pt))); + ci.pt.y(static_cast(y(vi.pt))); + ci.count = typename polygon_45_formation::Vertex45Count + ( vi.count[0], vi.count[1], vi.count[2], vi.count[3]); + result.error_data_.push_back(ci); + } + Data2 new_result_data; + polygon_sort(result_data.begin(), result_data.end()); + applyUnary45OpOnVectors(new_result_data, result_data); //OR operation + result_data.swap(new_result_data); + } + scale_down_vertex_45_compact_range_blindly(result_data.begin(), result_data.end(), 2); + //result.data_.reserve(result_data.size()); + for(std::size_t i = 0 ; i < result_data.size(); ++i) { + const Vertex45Compact2& vi = result_data[i]; + Vertex45Compact ci; + ci.pt.x(static_cast(x(vi.pt))); + ci.pt.y(static_cast(y(vi.pt))); + ci.count = typename polygon_45_formation::Vertex45Count + ( vi.count[0], vi.count[1], vi.count[2], vi.count[3]); + result.data_.push_back(ci); + } + result.is_manhattan_ = result_is_manhattan; + result.dirty_ = false; + result.unsorted_ = false; + } else { throw str; } + } + //std::cout << "DONE SCANNING\n"; + } + } + + template + template + inline void polygon_45_set_data::applyAdaptiveUnary_() const { + polygon_45_set_data result; + result.error_data_ = error_data_; + if(is_manhattan()) { + //convert each into polygon_90_set data and call boolean operations + polygon_90_set_data l90sd(VERTICAL); + for(typename value_type::const_iterator itr = data_.begin(); itr != data_.end(); ++itr) { + if((*itr).count[3] == 0) continue; //skip all non vertical edges + l90sd.insert(std::make_pair((*itr).pt.x(), std::make_pair((*itr).pt.y(), (*itr).count[3])), false, VERTICAL); + } + l90sd.sort(); +#ifdef BOOST_POLYGON_MSVC +#pragma warning (push) +#pragma warning (disable: 4127) +#endif + if(op == 0) { + l90sd.clean(); + } else if (op == 1) { + l90sd.self_intersect(); + } else if (op == 3) { + l90sd.self_xor(); + } +#ifdef BOOST_POLYGON_MSVC +#pragma warning (pop) +#endif + result.data_.clear(); + result.insert(l90sd); + result.is_manhattan_ = true; + result.dirty_ = false; + result.unsorted_ = false; + } else { + sort(); + try { + result.is_manhattan_ = applyUnary45OpOnVectors(result.data_, data_); + } catch (std::string str) { + std::string msg = "GTL 45 Boolean error, precision insufficient to represent edge intersection coordinate value."; + if(str == msg) { + result.clear(); + typedef typename coordinate_traits::manhattan_area_type Unit2; + typedef typename polygon_45_formation::Vertex45Compact Vertex45Compact2; + typedef std::vector Data2; + Data2 lvalue_data, result_data; + lvalue_data.reserve(data_.size()); + for(std::size_t i = 0 ; i < data_.size(); ++i) { + const Vertex45Compact& vi = data_[i]; + Vertex45Compact2 ci; + ci.pt.x(static_cast(x(vi.pt))); + ci.pt.y(static_cast(y(vi.pt))); + ci.count = typename polygon_45_formation::Vertex45Count + ( vi.count[0], vi.count[1], vi.count[2], vi.count[3]); + lvalue_data.push_back(ci); + } + scale_up_vertex_45_compact_range(lvalue_data.begin(), lvalue_data.end(), 2); + bool result_is_manhattan = applyUnary45OpOnVectors(result_data, + lvalue_data ); + if(!result_is_manhattan) { + typename polygon_45_formation::Polygon45Formation pf(false); + //std::cout << "FORMING POLYGONS\n"; + std::vector > container; + pf.scan(container, result_data.begin(), result_data.end()); + Data2 error_data_out; + std::vector > pos_error_rects; + std::vector > neg_error_rects; + for(std::size_t i = 0; i < container.size(); ++i) { + get_error_rects(pos_error_rects, neg_error_rects, container[i]); + } + for(std::size_t i = 0; i < pos_error_rects.size(); ++i) { + insert_rectangle_into_vector_45(result_data, pos_error_rects[i], false); + insert_rectangle_into_vector_45(error_data_out, pos_error_rects[i], false); + } + for(std::size_t i = 0; i < neg_error_rects.size(); ++i) { + insert_rectangle_into_vector_45(result_data, neg_error_rects[i], true); + insert_rectangle_into_vector_45(error_data_out, neg_error_rects[i], false); + } + scale_down_vertex_45_compact_range_blindly(error_data_out.begin(), error_data_out.end(), 2); + for(std::size_t i = 0 ; i < error_data_out.size(); ++i) { + const Vertex45Compact2& vi = error_data_out[i]; + Vertex45Compact ci; + ci.pt.x(static_cast(x(vi.pt))); + ci.pt.y(static_cast(y(vi.pt))); + ci.count = typename polygon_45_formation::Vertex45Count + ( vi.count[0], vi.count[1], vi.count[2], vi.count[3]); + result.error_data_.push_back(ci); + } + Data2 new_result_data; + polygon_sort(result_data.begin(), result_data.end()); + applyUnary45OpOnVectors(new_result_data, result_data); //OR operation + result_data.swap(new_result_data); + } + scale_down_vertex_45_compact_range_blindly(result_data.begin(), result_data.end(), 2); + //result.data_.reserve(result_data.size()); + for(std::size_t i = 0 ; i < result_data.size(); ++i) { + const Vertex45Compact2& vi = result_data[i]; + Vertex45Compact ci; + ci.pt.x(static_cast(x(vi.pt))); + ci.pt.y(static_cast(y(vi.pt))); + ci.count = typename polygon_45_formation::Vertex45Count + ( vi.count[0], vi.count[1], vi.count[2], vi.count[3]); + result.data_.push_back(ci); + } + result.is_manhattan_ = result_is_manhattan; + result.dirty_ = false; + result.unsorted_ = false; + } else { throw str; } + } + //std::cout << "DONE SCANNING\n"; + } + data_.swap(result.data_); + error_data_.swap(result.error_data_); + dirty_ = result.dirty_; + unsorted_ = result.unsorted_; + is_manhattan_ = result.is_manhattan_; + } + + template + class property_merge_45 { + private: + typedef typename coordinate_traits::manhattan_area_type big_coord; + typedef typename polygon_45_property_merge::MergeSetData tsd; + tsd tsd_; + public: + inline property_merge_45() : tsd_() {} + inline property_merge_45(const property_merge_45& that) : tsd_(that.tsd_) {} + inline property_merge_45& operator=(const property_merge_45& that) { + tsd_ = that.tsd_; + return *this; + } + + inline void insert(const polygon_45_set_data& ps, property_type property) { + ps.clean(); + polygon_45_property_merge::populateMergeSetData(tsd_, ps.begin(), ps.end(), property); + } + template + inline void insert(const GeoObjT& geoObj, property_type property) { + polygon_45_set_data ps; + ps.insert(geoObj); + insert(ps, property); + } + + //merge properties of input geometries and store the resulting geometries of regions + //with unique sets of merged properties to polygons sets in a map keyed by sets of properties + // T = std::map, polygon_45_set_data > or + // T = std::map, polygon_45_set_data > + template + inline void merge(result_type& result) { + typedef typename result_type::key_type keytype; + typedef std::map > bigtype; + bigtype result_big; + polygon_45_property_merge::performMerge(result_big, tsd_); + std::vector > polys; + std::vector > pos_error_rects; + std::vector > neg_error_rects; + for(typename std::map >::iterator itr = result_big.begin(); + itr != result_big.end(); ++itr) { + polys.clear(); + (*itr).second.get(polys); + for(std::size_t i = 0; i < polys.size(); ++i) { + get_error_rects(pos_error_rects, neg_error_rects, polys[i]); + } + (*itr).second += pos_error_rects; + (*itr).second -= neg_error_rects; + (*itr).second.scale_down(2); + result[(*itr).first].insert((*itr).second); + } + } + }; + + //ConnectivityExtraction computes the graph of connectivity between rectangle, polygon and + //polygon set graph nodes where an edge is created whenever the geometry in two nodes overlap + template + class connectivity_extraction_45 { + private: + typedef typename coordinate_traits::manhattan_area_type big_coord; + typedef typename polygon_45_touch::TouchSetData tsd; + tsd tsd_; + unsigned int nodeCount_; + public: + inline connectivity_extraction_45() : tsd_(), nodeCount_(0) {} + inline connectivity_extraction_45(const connectivity_extraction_45& that) : tsd_(that.tsd_), + nodeCount_(that.nodeCount_) {} + inline connectivity_extraction_45& operator=(const connectivity_extraction_45& that) { + tsd_ = that.tsd_; + nodeCount_ = that.nodeCount_; {} + return *this; + } + + //insert a polygon set graph node, the value returned is the id of the graph node + inline unsigned int insert(const polygon_45_set_data& ps) { + ps.clean(); + polygon_45_touch::populateTouchSetData(tsd_, ps.begin(), ps.end(), nodeCount_); + return nodeCount_++; + } + template + inline unsigned int insert(const GeoObjT& geoObj) { + polygon_45_set_data ps; + ps.insert(geoObj); + return insert(ps); + } + + //extract connectivity and store the edges in the graph + //graph must be indexable by graph node id and the indexed value must be a std::set of + //graph node id + template + inline void extract(GraphT& graph) { + polygon_45_touch::performTouch(graph, tsd_); + } + }; +} +} +#endif + diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_traits.hpp new file mode 100644 index 0000000..f52f57f --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_set_traits.hpp @@ -0,0 +1,149 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_SET_TRAITS_HPP +#define BOOST_POLYGON_POLYGON_45_SET_TRAITS_HPP +namespace boost { namespace polygon{ + + //default definition of polygon 45 set traits works for any model of polygon 45, polygon 45 with holes or any vector or list thereof + template + struct polygon_45_set_traits { + typedef typename get_coordinate_type::type >::type coordinate_type; + typedef typename get_iterator_type::type iterator_type; + typedef T operator_arg_type; + + static inline iterator_type begin(const T& polygon_set) { + return get_iterator_type::begin(polygon_set); + } + + static inline iterator_type end(const T& polygon_set) { + return get_iterator_type::end(polygon_set); + } + + static inline bool clean(const T& ) { return false; } + + static inline bool sorted(const T& ) { return false; } + }; + + template + struct is_45_polygonal_concept { typedef gtl_no type; }; + template <> + struct is_45_polygonal_concept { typedef gtl_yes type; }; + template <> + struct is_45_polygonal_concept { typedef gtl_yes type; }; + template <> + struct is_45_polygonal_concept { typedef gtl_yes type; }; + + template + struct is_polygon_45_set_type { + typedef typename is_45_polygonal_concept::type>::type type; + }; + template + struct is_polygon_45_set_type > { + typedef typename gtl_or< + typename is_45_polygonal_concept >::type>::type, + typename is_45_polygonal_concept::value_type>::type>::type>::type type; + }; + template + struct is_polygon_45_set_type > { + typedef typename gtl_or< + typename is_45_polygonal_concept >::type>::type, + typename is_45_polygonal_concept::value_type>::type>::type>::type type; + }; + + template + struct is_mutable_polygon_45_set_type { + typedef typename gtl_same_type::type>::type type; + }; + template + struct is_mutable_polygon_45_set_type > { + typedef typename gtl_or< + typename gtl_same_type >::type>::type, + typename is_45_polygonal_concept::value_type>::type>::type>::type type; + }; + template + struct is_mutable_polygon_45_set_type > { + typedef typename gtl_or< + typename gtl_same_type >::type>::type, + typename is_45_polygonal_concept::value_type>::type>::type>::type type; + }; + + template + bool fracture_holes_45_by_concept() { return false; } + template <> + inline bool fracture_holes_45_by_concept() { return true; } + + template + void get_45_polygons_T(T& t, iT begin, iT end) { + typedef typename polygon_45_set_traits::coordinate_type Unit; + typedef typename geometry_concept::type CType; + typename polygon_45_formation::Polygon45Formation pf(fracture_holes_45_by_concept()); + //std::cout << "FORMING POLYGONS\n"; + pf.scan(t, begin, end); + } + + template + struct polygon_45_set_mutable_traits {}; + template + struct polygon_45_set_mutable_traits > { + template + static inline void set(std::list& polygon_set, input_iterator_type input_begin, input_iterator_type input_end) { + polygon_set.clear(); + polygon_45_set_data >::coordinate_type> ps; + ps.reserve(std::distance(input_begin, input_end)); + ps.insert(input_begin, input_end); + ps.sort(); + ps.clean(); + get_45_polygons_T(polygon_set, ps.begin(), ps.end()); + } + }; + template + struct polygon_45_set_mutable_traits > { + template + static inline void set(std::vector& polygon_set, input_iterator_type input_begin, input_iterator_type input_end) { + polygon_set.clear(); + size_t num_ele = std::distance(input_begin, input_end); + polygon_set.reserve(num_ele); + polygon_45_set_data >::coordinate_type> ps; + ps.reserve(num_ele); + ps.insert(input_begin, input_end); + ps.sort(); + ps.clean(); + get_45_polygons_T(polygon_set, ps.begin(), ps.end()); + } + }; + + template + struct polygon_45_set_mutable_traits > { + template + static inline void set(polygon_45_set_data& polygon_set, + input_iterator_type input_begin, input_iterator_type input_end) { + polygon_set.set(input_begin, input_end); + } + }; + template + struct polygon_45_set_traits > { + typedef typename polygon_45_set_data::coordinate_type coordinate_type; + typedef typename polygon_45_set_data::iterator_type iterator_type; + typedef typename polygon_45_set_data::operator_arg_type operator_arg_type; + + static inline iterator_type begin(const polygon_45_set_data& polygon_set) { + return polygon_set.begin(); + } + + static inline iterator_type end(const polygon_45_set_data& polygon_set) { + return polygon_set.end(); + } + + static inline bool clean(const polygon_45_set_data& polygon_set) { polygon_set.clean(); return true; } + + static inline bool sorted(const polygon_45_set_data& polygon_set) { polygon_set.sort(); return true; } + + }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_with_holes_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_with_holes_data.hpp new file mode 100644 index 0000000..0601bdc --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_45_with_holes_data.hpp @@ -0,0 +1,107 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_45_WITH_HOLES_DATA_HPP +#define BOOST_POLYGON_POLYGON_45_WITH_HOLES_DATA_HPP +#include "isotropy.hpp" +#include "polygon_45_data.hpp" +namespace boost { namespace polygon{ +struct polygon_45_with_holes_concept; +template +class polygon_45_with_holes_data { +public: + typedef polygon_45_with_holes_concept geometry_type; + typedef T coordinate_type; + typedef typename polygon_45_data::iterator_type iterator_type; + typedef typename std::list >::const_iterator iterator_holes_type; + typedef polygon_45_data hole_type; + typedef typename coordinate_traits::coordinate_distance area_type; + typedef point_data point_type; + + // default constructor of point does not initialize x and y + inline polygon_45_with_holes_data() : self_(), holes_() {} //do nothing default constructor + + template + inline polygon_45_with_holes_data(iT input_begin, iT input_end) : self_(), holes_() { + set(input_begin, input_end); + } + + template + inline polygon_45_with_holes_data(iT input_begin, iT input_end, hiT holes_begin, hiT holes_end) : self_(), holes_() { + set(input_begin, input_end); + set_holes(holes_begin, holes_end); + } + + template + inline polygon_45_with_holes_data& set(iT input_begin, iT input_end) { + self_.set(input_begin, input_end); + return *this; + } + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline polygon_45_with_holes_data& set_holes(iT input_begin, iT input_end) { + holes_.clear(); //just in case there was some old data there + for( ; input_begin != input_end; ++ input_begin) { + holes_.push_back(hole_type()); + holes_.back().set((*input_begin).begin(), (*input_begin).end()); + } + return *this; + } + + // copy constructor (since we have dynamic memory) + inline polygon_45_with_holes_data(const polygon_45_with_holes_data& that) : self_(that.self_), + holes_(that.holes_) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline polygon_45_with_holes_data& operator=(const polygon_45_with_holes_data& that) { + self_ = that.self_; + holes_ = that.holes_; + return *this; + } + + template + inline polygon_45_with_holes_data& operator=(const T2& rvalue); + + // get begin iterator, returns a pointer to a const coordinate_type + inline const iterator_type begin() const { + return self_.begin(); + } + + // get end iterator, returns a pointer to a const coordinate_type + inline const iterator_type end() const { + return self_.end(); + } + + inline std::size_t size() const { + return self_.size(); + } + + // get begin iterator, returns a pointer to a const polygon + inline const iterator_holes_type begin_holes() const { + return holes_.begin(); + } + + // get end iterator, returns a pointer to a const polygon + inline const iterator_holes_type end_holes() const { + return holes_.end(); + } + + inline std::size_t size_holes() const { + return holes_.size(); + } + +public: + polygon_45_data self_; + std::list holes_; +}; + + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_data.hpp new file mode 100644 index 0000000..1f14ed7 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_data.hpp @@ -0,0 +1,79 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_90_DATA_HPP +#define BOOST_POLYGON_POLYGON_90_DATA_HPP +namespace boost { namespace polygon{ +struct polygon_90_concept; +template +class polygon_90_data { +public: + typedef polygon_90_concept geometry_type; + typedef T coordinate_type; + typedef typename std::vector::const_iterator compact_iterator_type; + typedef iterator_compact_to_points > iterator_type; + typedef typename coordinate_traits::area_type area_type; + + inline polygon_90_data() : coords_() {} //do nothing default constructor + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline polygon_90_data& set(iT begin_point, iT end_point) { + return set_compact(iterator_points_to_compact::value_type>(begin_point, end_point), + iterator_points_to_compact::value_type>(end_point, end_point)); + } + + template + inline polygon_90_data& set_compact(iT input_begin, iT input_end) { + coords_.clear(); //just in case there was some old data there + while(input_begin != input_end) { + coords_.insert(coords_.end(), *input_begin); + ++input_begin; + } + return *this; + } + + // copy constructor (since we have dynamic memory) + inline polygon_90_data(const polygon_90_data& that) : coords_(that.coords_) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline polygon_90_data& operator=(const polygon_90_data& that) { + coords_ = that.coords_; + return *this; + } + + template + inline polygon_90_data& operator=(const T2& rvalue); + + // assignment operator (since we have dynamic memory do a deep copy) + inline bool operator==(const polygon_90_data& that) const { + return coords_ == that.coords_; + } + + // get begin iterator, returns a pointer to a const Unit + inline iterator_type begin() const { return iterator_type(coords_.begin(), coords_.end()); } + + // get end iterator, returns a pointer to a const Unit + inline iterator_type end() const { return iterator_type(coords_.end(), coords_.end()); } + + // get begin iterator, returns a pointer to a const Unit + inline compact_iterator_type begin_compact() const { return coords_.begin(); } + + // get end iterator, returns a pointer to a const Unit + inline compact_iterator_type end_compact() const { return coords_.end(); } + + inline std::size_t size() const { return coords_.size(); } + +private: + std::vector coords_; +}; + + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_concept.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_concept.hpp new file mode 100644 index 0000000..5259a07 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_concept.hpp @@ -0,0 +1,550 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_90_SET_CONCEPT_HPP +#define BOOST_POLYGON_POLYGON_90_SET_CONCEPT_HPP +#include "polygon_90_set_data.hpp" +#include "polygon_90_set_traits.hpp" +namespace boost { namespace polygon{ + + template + typename enable_if< typename is_polygon_90_set_type::type, + typename polygon_90_set_traits::iterator_type>::type + begin_90_set_data(const polygon_set_type& polygon_set) { + return polygon_90_set_traits::begin(polygon_set); + } + + template + typename enable_if< typename is_polygon_90_set_type::type, + typename polygon_90_set_traits::iterator_type>::type + end_90_set_data(const polygon_set_type& polygon_set) { + return polygon_90_set_traits::end(polygon_set); + } + + template + typename enable_if< typename is_polygon_90_set_type::type, + orientation_2d>::type + scanline_orientation(const polygon_set_type& polygon_set) { + return polygon_90_set_traits::orient(polygon_set); + } + + template + typename enable_if< typename is_polygon_90_set_type::type, + bool>::type + clean(const polygon_set_type& polygon_set) { + return polygon_90_set_traits::clean(polygon_set); + } + + //assign + template + typename enable_if < + typename gtl_and< + typename is_mutable_polygon_90_set_type::type, + typename is_polygon_90_set_type::type>::type, + polygon_set_type_1>::type & + assign(polygon_set_type_1& lvalue, const polygon_set_type_2& rvalue) { + polygon_90_set_mutable_traits::set(lvalue, begin_90_set_data(rvalue), end_90_set_data(rvalue), + scanline_orientation(rvalue)); + return lvalue; + } + + template + struct are_not_both_rectangle_concept { typedef gtl_yes type; }; + template <> + struct are_not_both_rectangle_concept { typedef gtl_no type; }; + + //equivalence + template + typename enable_if< typename gtl_and_3< + typename is_polygon_90_set_type::type, + typename is_polygon_90_set_type::type, + typename are_not_both_rectangle_concept::type, + typename geometry_concept::type>::type>::type, + bool>::type + equivalence(const polygon_set_type_1& lvalue, + const polygon_set_type_2& rvalue) { + polygon_90_set_data::coordinate_type> ps1; + assign(ps1, lvalue); + polygon_90_set_data::coordinate_type> ps2; + assign(ps2, rvalue); + return ps1 == ps2; + } + + + //get rectangle tiles (slicing orientation is vertical) + template + typename enable_if< typename gtl_if::type>::type, + void>::type + get_rectangles(output_container_type& output, const polygon_set_type& polygon_set) { + clean(polygon_set); + polygon_90_set_data::coordinate_type> ps(VERTICAL); + assign(ps, polygon_set); + ps.get_rectangles(output); + } + + //get rectangle tiles + template + typename enable_if< typename gtl_if::type>::type, + void>::type + get_rectangles(output_container_type& output, const polygon_set_type& polygon_set, orientation_2d slicing_orientation) { + clean(polygon_set); + polygon_90_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.get_rectangles(output, slicing_orientation); + } + + //get: min_rectangles max_rectangles + template + typename enable_if ::type, + typename gtl_same_type::value_type>::type>::type>::type, + void>::type + get_max_rectangles(output_container_type& output, const polygon_set_type& polygon_set) { + std::vector::coordinate_type> > rects; + assign(rects, polygon_set); + MaxCover::coordinate_type>::getMaxCover(output, rects, scanline_orientation(polygon_set)); + } + + //clear + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + void>::type + clear(polygon_set_type& polygon_set) { + polygon_90_set_data::coordinate_type> ps(scanline_orientation(polygon_set)); + assign(polygon_set, ps); + } + + //empty + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + bool>::type + empty(const polygon_set_type& polygon_set) { + if(clean(polygon_set)) return begin_90_set_data(polygon_set) == end_90_set_data(polygon_set); + polygon_90_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.clean(); + return ps.empty(); + } + + //extents + template + typename enable_if ::type, + typename is_mutable_rectangle_concept::type>::type>::type, + bool>::type + extents(rectangle_type& extents_rectangle, + const polygon_set_type& polygon_set) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + return ps.extents(extents_rectangle); + } + + //area + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + typename coordinate_traits::coordinate_type>::manhattan_area_type>::type + area(const polygon_set_type& polygon_set) { + typedef rectangle_data::coordinate_type> rectangle_type; + typedef typename coordinate_traits::coordinate_type>::manhattan_area_type area_type; + std::vector rects; + assign(rects, polygon_set); + area_type retval = (area_type)0; + for(std::size_t i = 0; i < rects.size(); ++i) { + retval += (area_type)area(rects[i]); + } + return retval; + } + + //interact + template + typename enable_if ::type, + typename is_mutable_polygon_90_set_type::type>::type, + polygon_set_type_1>::type& + interact(polygon_set_type_1& polygon_set_1, const polygon_set_type_2& polygon_set_2) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps(scanline_orientation(polygon_set_2)); + polygon_90_set_data ps2(ps); + ps.insert(polygon_set_1); + ps2.insert(polygon_set_2); + ps.interact(ps2); + assign(polygon_set_1, ps); + return polygon_set_1; + } + + //self_intersect + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + self_intersect(polygon_set_type& polygon_set) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.self_intersect(); + assign(polygon_set, ps); + return polygon_set; + } + + //self_xor + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + self_xor(polygon_set_type& polygon_set) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.self_xor(); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + bloat(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + return bloat(polygon_set, bloating, bloating, bloating, bloating); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + bloat(polygon_set_type& polygon_set, orientation_2d orient, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + if(orient == orientation_2d(HORIZONTAL)) + return bloat(polygon_set, bloating, bloating, 0, 0); + return bloat(polygon_set, 0, 0, bloating, bloating); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + bloat(polygon_set_type& polygon_set, orientation_2d orient, + typename coordinate_traits::coordinate_type>::unsigned_area_type low_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type high_bloating) { + if(orient == orientation_2d(HORIZONTAL)) + return bloat(polygon_set, low_bloating, high_bloating, 0, 0); + return bloat(polygon_set, 0, 0, low_bloating, high_bloating); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + bloat(polygon_set_type& polygon_set, direction_2d dir, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + if(dir == direction_2d(EAST)) + return bloat(polygon_set, 0, bloating, 0, 0); + if(dir == direction_2d(WEST)) + return bloat(polygon_set, bloating, 0, 0, 0); + if(dir == direction_2d(SOUTH)) + return bloat(polygon_set, 0, 0, bloating, 0); + return bloat(polygon_set, 0, 0, 0, bloating); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + bloat(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type west_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type east_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type south_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type north_bloating) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.bloat(west_bloating, east_bloating, south_bloating, north_bloating); + ps.clean(); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + shrink(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type shrinking) { + return shrink(polygon_set, shrinking, shrinking, shrinking, shrinking); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + shrink(polygon_set_type& polygon_set, orientation_2d orient, + typename coordinate_traits::coordinate_type>::unsigned_area_type shrinking) { + if(orient == orientation_2d(HORIZONTAL)) + return shrink(polygon_set, shrinking, shrinking, 0, 0); + return shrink(polygon_set, 0, 0, shrinking, shrinking); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + shrink(polygon_set_type& polygon_set, orientation_2d orient, + typename coordinate_traits::coordinate_type>::unsigned_area_type low_shrinking, + typename coordinate_traits::coordinate_type>::unsigned_area_type high_shrinking) { + if(orient == orientation_2d(HORIZONTAL)) + return shrink(polygon_set, low_shrinking, high_shrinking, 0, 0); + return shrink(polygon_set, 0, 0, low_shrinking, high_shrinking); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + shrink(polygon_set_type& polygon_set, direction_2d dir, + typename coordinate_traits::coordinate_type>::unsigned_area_type shrinking) { + if(dir == direction_2d(EAST)) + return shrink(polygon_set, 0, shrinking, 0, 0); + if(dir == direction_2d(WEST)) + return shrink(polygon_set, shrinking, 0, 0, 0); + if(dir == direction_2d(SOUTH)) + return shrink(polygon_set, 0, 0, shrinking, 0); + return shrink(polygon_set, 0, 0, 0, shrinking); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + shrink(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type west_shrinking, + typename coordinate_traits::coordinate_type>::unsigned_area_type east_shrinking, + typename coordinate_traits::coordinate_type>::unsigned_area_type south_shrinking, + typename coordinate_traits::coordinate_type>::unsigned_area_type north_shrinking) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.shrink(west_shrinking, east_shrinking, south_shrinking, north_shrinking); + ps.clean(); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + resize(polygon_set_type& polygon_set, coord_type resizing) { + if(resizing > 0) { + return bloat(polygon_set, resizing); + } + if(resizing < 0) { + return shrink(polygon_set, -resizing); + } + return polygon_set; + } + + //positive or negative values allow for any and all directions of sizing + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + resize(polygon_set_type& polygon_set, coord_type west, coord_type east, coord_type south, coord_type north) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.resize(west, east, south, north); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + grow_and(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + return grow_and(polygon_set, bloating, bloating, bloating, bloating); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + grow_and(polygon_set_type& polygon_set, orientation_2d orient, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + if(orient == orientation_2d(HORIZONTAL)) + return grow_and(polygon_set, bloating, bloating, 0, 0); + return grow_and(polygon_set, 0, 0, bloating, bloating); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + grow_and(polygon_set_type& polygon_set, orientation_2d orient, + typename coordinate_traits::coordinate_type>::unsigned_area_type low_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type high_bloating) { + if(orient == orientation_2d(HORIZONTAL)) + return grow_and(polygon_set, low_bloating, high_bloating, 0, 0); + return grow_and(polygon_set, 0, 0, low_bloating, high_bloating); + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + grow_and(polygon_set_type& polygon_set, direction_2d dir, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + if(dir == direction_2d(EAST)) + return grow_and(polygon_set, 0, bloating, 0, 0); + if(dir == direction_2d(WEST)) + return grow_and(polygon_set, bloating, 0, 0, 0); + if(dir == direction_2d(SOUTH)) + return grow_and(polygon_set, 0, 0, bloating, 0); + return grow_and(polygon_set, 0, 0, 0, bloating); + } + + template + typename enable_if< typename gtl_if::type>::type, + polygon_set_type>::type & + grow_and(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type west_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type east_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type south_bloating, + typename coordinate_traits::coordinate_type>::unsigned_area_type north_bloating) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + std::vector > polys; + assign(polys, polygon_set); + clear(polygon_set); + polygon_90_set_data ps(scanline_orientation(polygon_set)); + for(std::size_t i = 0; i < polys.size(); ++i) { + polygon_90_set_data tmpPs(scanline_orientation(polygon_set)); + tmpPs.insert(polys[i]); + bloat(tmpPs, west_bloating, east_bloating, south_bloating, north_bloating); + tmpPs.clean(); //apply implicit OR on tmp polygon set + ps.insert(tmpPs); + } + self_intersect(ps); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + scale_up(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type> + ::unsigned_area_type factor) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.scale_up(factor); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + scale_down(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type> + ::unsigned_area_type factor) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.scale_down(factor); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + scale(polygon_set_type& polygon_set, + const scaling_type& scaling) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.scale(scaling); + assign(polygon_set, ps); + return polygon_set; + } + + struct y_p_s_move : gtl_yes {}; + + //move + template + typename enable_if< typename gtl_and::type>::type>::type, + polygon_set_type>::type & + move(polygon_set_type& polygon_set, + orientation_2d orient, typename polygon_90_set_traits::coordinate_type displacement) { + if(orient == HORIZONTAL) + return move(polygon_set, displacement, 0); + else + return move(polygon_set, 0, displacement); + } + + struct y_p_s_move2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + polygon_set_type>::type & + move(polygon_set_type& polygon_set, typename polygon_90_set_traits::coordinate_type x_displacement, + typename polygon_90_set_traits::coordinate_type y_displacement) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.move(x_displacement, y_displacement); + ps.clean(); + assign(polygon_set, ps); + return polygon_set; + } + + //transform + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + transform(polygon_set_type& polygon_set, + const transformation_type& transformation) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + polygon_90_set_data ps; + assign(ps, polygon_set); + ps.transform(transformation); + ps.clean(); + assign(polygon_set, ps); + return polygon_set; + typedef typename polygon_90_set_traits::coordinate_type Unit; + } + + //keep + template + typename enable_if< typename is_mutable_polygon_90_set_type::type, + polygon_set_type>::type & + keep(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type min_area, + typename coordinate_traits::coordinate_type>::unsigned_area_type max_area, + typename coordinate_traits::coordinate_type>::unsigned_area_type min_width, + typename coordinate_traits::coordinate_type>::unsigned_area_type max_width, + typename coordinate_traits::coordinate_type>::unsigned_area_type min_height, + typename coordinate_traits::coordinate_type>::unsigned_area_type max_height) { + typedef typename polygon_90_set_traits::coordinate_type Unit; + typedef typename coordinate_traits::unsigned_area_type uat; + std::list > polys; + assign(polys, polygon_set); + clear(polygon_set); + typename std::list >::iterator itr_nxt; + for(typename std::list >::iterator itr = polys.begin(); itr != polys.end(); itr = itr_nxt){ + itr_nxt = itr; + ++itr_nxt; + rectangle_data bbox; + extents(bbox, *itr); + uat pwidth = delta(bbox, HORIZONTAL); + if(pwidth > min_width && pwidth <= max_width){ + uat pheight = delta(bbox, VERTICAL); + if(pheight > min_height && pheight <= max_height){ + uat parea = area(*itr); + if(parea <= max_area && parea >= min_area) { + continue; + } + } + } + polys.erase(itr); + } + assign(polygon_set, polys); + return polygon_set; + } + + +} +} +#include "detail/polygon_90_set_view.hpp" +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_data.hpp new file mode 100644 index 0000000..2c640bf --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_data.hpp @@ -0,0 +1,989 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_90_SET_DATA_HPP +#define BOOST_POLYGON_POLYGON_90_SET_DATA_HPP +#include "isotropy.hpp" +#include "point_concept.hpp" +#include "transform.hpp" +#include "interval_concept.hpp" +#include "rectangle_concept.hpp" +#include "segment_concept.hpp" +#include "detail/iterator_points_to_compact.hpp" +#include "detail/iterator_compact_to_points.hpp" +#include "polygon_traits.hpp" + +//manhattan boolean algorithms +#include "detail/boolean_op.hpp" +#include "detail/polygon_formation.hpp" +#include "detail/rectangle_formation.hpp" +#include "detail/max_cover.hpp" +#include "detail/property_merge.hpp" +#include "detail/polygon_90_touch.hpp" +#include "detail/iterator_geometry_to_set.hpp" + +namespace boost { namespace polygon{ + template + class polygon_90_set_view; + + template + class polygon_90_set_data { + public: + typedef T coordinate_type; + typedef std::vector > > value_type; + typedef typename std::vector > >::const_iterator iterator_type; + typedef polygon_90_set_data operator_arg_type; + + // default constructor + inline polygon_90_set_data() : orient_(HORIZONTAL), data_(), dirty_(false), unsorted_(false) {} + + // constructor + inline polygon_90_set_data(orientation_2d orient) : orient_(orient), data_(), dirty_(false), unsorted_(false) {} + + // constructor from an iterator pair over vertex data + template + inline polygon_90_set_data(orientation_2d, iT input_begin, iT input_end) : + orient_(HORIZONTAL), data_(), dirty_(false), unsorted_(false) { + dirty_ = true; + unsorted_ = true; + for( ; input_begin != input_end; ++input_begin) { insert(*input_begin); } + } + + // copy constructor + inline polygon_90_set_data(const polygon_90_set_data& that) : + orient_(that.orient_), data_(that.data_), dirty_(that.dirty_), unsorted_(that.unsorted_) {} + + template + inline polygon_90_set_data(const polygon_90_set_view& that); + + // copy with orientation change constructor + inline polygon_90_set_data(orientation_2d orient, const polygon_90_set_data& that) : + orient_(orient), data_(), dirty_(false), unsorted_(false) { + insert(that, false, that.orient_); + } + + // destructor + inline ~polygon_90_set_data() {} + + // assignement operator + inline polygon_90_set_data& operator=(const polygon_90_set_data& that) { + if(this == &that) return *this; + orient_ = that.orient_; + data_ = that.data_; + dirty_ = that.dirty_; + unsorted_ = that.unsorted_; + return *this; + } + + template + inline polygon_90_set_data& operator=(const polygon_90_set_view& that); + + template + inline polygon_90_set_data& operator=(const geometry_object& geometry) { + data_.clear(); + insert(geometry); + return *this; + } + + // insert iterator range + inline void insert(iterator_type input_begin, iterator_type input_end, orientation_2d orient = HORIZONTAL) { + if(input_begin == input_end || (!data_.empty() && &(*input_begin) == &(*(data_.begin())))) return; + dirty_ = true; + unsorted_ = true; + if(orient == orient_) + data_.insert(data_.end(), input_begin, input_end); + else { + for( ; input_begin != input_end; ++input_begin) { + insert(*input_begin, false, orient); + } + } + } + + // insert iterator range + template + inline void insert(iT input_begin, iT input_end, orientation_2d orient = HORIZONTAL) { + if(input_begin == input_end) return; + dirty_ = true; + unsorted_ = true; + for( ; input_begin != input_end; ++input_begin) { + insert(*input_begin, false, orient); + } + } + + inline void insert(const polygon_90_set_data& polygon_set) { + insert(polygon_set.begin(), polygon_set.end(), polygon_set.orient()); + } + + inline void insert(const std::pair, point_data >, int>& edge, bool is_hole = false, + orientation_2d = HORIZONTAL) { + std::pair > vertex; + vertex.first = edge.first.first.x(); + vertex.second.first = edge.first.first.y(); + vertex.second.second = edge.second * (is_hole ? -1 : 1); + insert(vertex, false, VERTICAL); + vertex.first = edge.first.second.x(); + vertex.second.first = edge.first.second.y(); + vertex.second.second *= -1; + insert(vertex, false, VERTICAL); + } + + template + inline void insert(const geometry_type& geometry_object, bool is_hole = false, orientation_2d = HORIZONTAL) { + iterator_geometry_to_set::type, geometry_type> + begin_input(geometry_object, LOW, orient_, is_hole), end_input(geometry_object, HIGH, orient_, is_hole); + insert(begin_input, end_input, orient_); + } + + inline void insert(const std::pair >& vertex, bool is_hole = false, + orientation_2d orient = HORIZONTAL) { + data_.push_back(vertex); + if(orient != orient_) std::swap(data_.back().first, data_.back().second.first); + if(is_hole) data_.back().second.second *= -1; + dirty_ = true; + unsorted_ = true; + } + + inline void insert(coordinate_type major_coordinate, const std::pair, int>& edge) { + std::pair > vertex; + vertex.first = major_coordinate; + vertex.second.first = edge.first.get(LOW); + vertex.second.second = edge.second; + insert(vertex, false, orient_); + vertex.second.first = edge.first.get(HIGH); + vertex.second.second *= -1; + insert(vertex, false, orient_); + } + + template + inline void get(output_container& output) const { + get_dispatch(output, typename geometry_concept::type()); + } + + template + inline void get(output_container& output, size_t vthreshold) const { + get_dispatch(output, typename geometry_concept::type(), vthreshold); + } + + + template + inline void get_polygons(output_container& output) const { + get_dispatch(output, polygon_90_concept()); + } + + template + inline void get_rectangles(output_container& output) const { + clean(); + form_rectangles(output, data_.begin(), data_.end(), orient_, rectangle_concept()); + } + + template + inline void get_rectangles(output_container& output, orientation_2d slicing_orientation) const { + if(slicing_orientation == orient_) { + get_rectangles(output); + } else { + polygon_90_set_data ps(*this); + ps.transform(axis_transformation(axis_transformation::SWAP_XY)); + output_container result; + ps.get_rectangles(result); + for(typename output_container::iterator itr = result.begin(); itr != result.end(); ++itr) { + ::boost::polygon::transform(*itr, axis_transformation(axis_transformation::SWAP_XY)); + } + output.insert(output.end(), result.begin(), result.end()); + } + } + + // equivalence operator + inline bool operator==(const polygon_90_set_data& p) const { + if(orient_ == p.orient()) { + clean(); + p.clean(); + return data_ == p.data_; + } else { + return false; + } + } + + // inequivalence operator + inline bool operator!=(const polygon_90_set_data& p) const { + return !((*this) == p); + } + + // get iterator to begin vertex data + inline iterator_type begin() const { + return data_.begin(); + } + + // get iterator to end vertex data + inline iterator_type end() const { + return data_.end(); + } + + const value_type& value() const { + return data_; + } + + // clear the contents of the polygon_90_set_data + inline void clear() { data_.clear(); dirty_ = unsorted_ = false; } + + // find out if Polygon set is empty + inline bool empty() const { clean(); return data_.empty(); } + + // get the Polygon set size in vertices + inline std::size_t size() const { clean(); return data_.size(); } + + // get the current Polygon set capacity in vertices + inline std::size_t capacity() const { return data_.capacity(); } + + // reserve size of polygon set in vertices + inline void reserve(std::size_t size) { return data_.reserve(size); } + + // find out if Polygon set is sorted + inline bool sorted() const { return !unsorted_; } + + // find out if Polygon set is clean + inline bool dirty() const { return dirty_; } + + // get the scanline orientation of the polygon set + inline orientation_2d orient() const { return orient_; } + + // Start BM + // The problem: If we have two polygon sets with two different scanline orientations: + // I tried changing the orientation of one to coincide with other (If not, resulting boolean operation + // produces spurious results). + // First I tried copying polygon data from one of the sets into another set with corrected orientation + // using one of the copy constructor that takes in orientation (see somewhere above in this file) --> copy constructor throws error + // Then I tried another approach:(see below). This approach also fails to produce the desired results when test case is run. + // Here is the part that beats me: If I comment out the whole section, I can do all the operations (^=, -=, &= )these commented out + // operations perform. So then why do we need them?. Hence, I commented out this whole section. + // End BM + // polygon_90_set_data& operator-=(const polygon_90_set_data& that) { + // sort(); + // that.sort(); + // value_type data; + // std::swap(data, data_); + // applyBooleanBinaryOp(data.begin(), data.end(), + // that.begin(), that.end(), boolean_op::BinaryCount()); + // return *this; + // } + // polygon_90_set_data& operator^=(const polygon_90_set_data& that) { + // sort(); + // that.sort(); + // value_type data; + // std::swap(data, data_); + // applyBooleanBinaryOp(data.begin(), data.end(), + // that.begin(), that.end(), boolean_op::BinaryCount()); + // return *this; + // } + // polygon_90_set_data& operator&=(const polygon_90_set_data& that) { + // sort(); + // that.sort(); + // value_type data; + // std::swap(data, data_); + // applyBooleanBinaryOp(data.begin(), data.end(), + // that.begin(), that.end(), boolean_op::BinaryCount()); + // return *this; + // } + // polygon_90_set_data& operator|=(const polygon_90_set_data& that) { + // insert(that); + // return *this; + // } + + void clean() const { + sort(); + if(dirty_) { + boolean_op::default_arg_workaround::applyBooleanOr(data_); + dirty_ = false; + } + } + + void sort() const{ + if(unsorted_) { + polygon_sort(data_.begin(), data_.end()); + unsorted_ = false; + } + } + + template + void set(input_iterator_type input_begin, input_iterator_type input_end, orientation_2d orient) { + data_.clear(); + reserve(std::distance(input_begin, input_end)); + data_.insert(data_.end(), input_begin, input_end); + orient_ = orient; + dirty_ = true; + unsorted_ = true; + } + + void set(const value_type& value, orientation_2d orient) { + data_ = value; + orient_ = orient; + dirty_ = true; + unsorted_ = true; + } + + //extents + template + bool + extents(rectangle_type& extents_rectangle) const { + clean(); + if(data_.empty()) return false; + if(orient_ == HORIZONTAL) + set_points(extents_rectangle, point_data(data_[0].second.first, data_[0].first), + point_data(data_[data_.size() - 1].second.first, data_[data_.size() - 1].first)); + else + set_points(extents_rectangle, point_data(data_[0].first, data_[0].second.first), + point_data(data_[data_.size() - 1].first, data_[data_.size() - 1].second.first)); + for(std::size_t i = 1; i < data_.size() - 1; ++i) { + if(orient_ == HORIZONTAL) + encompass(extents_rectangle, point_data(data_[i].second.first, data_[i].first)); + else + encompass(extents_rectangle, point_data(data_[i].first, data_[i].second.first)); + } + return true; + } + + polygon_90_set_data& + bloat2(typename coordinate_traits::unsigned_area_type west_bloating, + typename coordinate_traits::unsigned_area_type east_bloating, + typename coordinate_traits::unsigned_area_type south_bloating, + typename coordinate_traits::unsigned_area_type north_bloating) { + std::vector > rects; + clean(); + rects.reserve(data_.size() / 2); + get(rects); + rectangle_data convolutionRectangle(interval_data(-((coordinate_type)west_bloating), + (coordinate_type)east_bloating), + interval_data(-((coordinate_type)south_bloating), + (coordinate_type)north_bloating)); + for(typename std::vector >::iterator itr = rects.begin(); + itr != rects.end(); ++itr) { + convolve(*itr, convolutionRectangle); + } + clear(); + insert(rects.begin(), rects.end()); + return *this; + } + + static void modify_pt(point_data& pt, const point_data& prev_pt, + const point_data& current_pt, const point_data& next_pt, + coordinate_type west_bloating, + coordinate_type east_bloating, + coordinate_type south_bloating, + coordinate_type north_bloating) { + bool pxl = prev_pt.x() < current_pt.x(); + bool pyl = prev_pt.y() < current_pt.y(); + bool nxl = next_pt.x() < current_pt.x(); + bool nyl = next_pt.y() < current_pt.y(); + bool pxg = prev_pt.x() > current_pt.x(); + bool pyg = prev_pt.y() > current_pt.y(); + bool nxg = next_pt.x() > current_pt.x(); + bool nyg = next_pt.y() > current_pt.y(); + //two of the four if statements will execute + if(pxl) + pt.y(current_pt.y() - south_bloating); + if(pxg) + pt.y(current_pt.y() + north_bloating); + if(nxl) + pt.y(current_pt.y() + north_bloating); + if(nxg) + pt.y(current_pt.y() - south_bloating); + if(pyl) + pt.x(current_pt.x() + east_bloating); + if(pyg) + pt.x(current_pt.x() - west_bloating); + if(nyl) + pt.x(current_pt.x() - west_bloating); + if(nyg) + pt.x(current_pt.x() + east_bloating); + } + static void resize_poly_up(std::vector >& poly, + coordinate_type west_bloating, + coordinate_type east_bloating, + coordinate_type south_bloating, + coordinate_type north_bloating) { + point_data first_pt = poly[0]; + point_data second_pt = poly[1]; + point_data prev_pt = poly[0]; + point_data current_pt = poly[1]; + for(std::size_t i = 2; i < poly.size(); ++i) { + point_data next_pt = poly[i]; + modify_pt(poly[i-1], prev_pt, current_pt, next_pt, west_bloating, east_bloating, south_bloating, north_bloating); + prev_pt = current_pt; + current_pt = next_pt; + } + point_data next_pt = first_pt; + modify_pt(poly.back(), prev_pt, current_pt, next_pt, west_bloating, east_bloating, south_bloating, north_bloating); + prev_pt = current_pt; + current_pt = next_pt; + next_pt = second_pt; + modify_pt(poly[0], prev_pt, current_pt, next_pt, west_bloating, east_bloating, south_bloating, north_bloating); + remove_colinear_pts(poly); + } + static bool resize_poly_down(std::vector >& poly, + coordinate_type west_shrinking, + coordinate_type east_shrinking, + coordinate_type south_shrinking, + coordinate_type north_shrinking) { + rectangle_data extents_rectangle; + set_points(extents_rectangle, poly[0], poly[0]); + point_data first_pt = poly[0]; + point_data second_pt = poly[1]; + point_data prev_pt = poly[0]; + point_data current_pt = poly[1]; + encompass(extents_rectangle, current_pt); + for(std::size_t i = 2; i < poly.size(); ++i) { + point_data next_pt = poly[i]; + encompass(extents_rectangle, next_pt); + modify_pt(poly[i-1], prev_pt, current_pt, next_pt, west_shrinking, east_shrinking, south_shrinking, north_shrinking); + prev_pt = current_pt; + current_pt = next_pt; + } + if(delta(extents_rectangle, HORIZONTAL) < std::abs(west_shrinking + east_shrinking)) + return false; + if(delta(extents_rectangle, VERTICAL) < std::abs(north_shrinking + south_shrinking)) + return false; + point_data next_pt = first_pt; + modify_pt(poly.back(), prev_pt, current_pt, next_pt, west_shrinking, east_shrinking, south_shrinking, north_shrinking); + prev_pt = current_pt; + current_pt = next_pt; + next_pt = second_pt; + modify_pt(poly[0], prev_pt, current_pt, next_pt, west_shrinking, east_shrinking, south_shrinking, north_shrinking); + return remove_colinear_pts(poly); + } + + static bool remove_colinear_pts(std::vector >& poly) { + bool found_colinear = true; + while(found_colinear && poly.size() >= 4) { + found_colinear = false; + typename std::vector >::iterator itr = poly.begin(); + itr += poly.size() - 1; //get last element position + typename std::vector >::iterator itr2 = poly.begin(); + typename std::vector >::iterator itr3 = itr2; + ++itr3; + std::size_t count = 0; + for( ; itr3 < poly.end(); ++itr3) { + if(((*itr).x() == (*itr2).x() && (*itr).x() == (*itr3).x()) || + ((*itr).y() == (*itr2).y() && (*itr).y() == (*itr3).y()) ) { + ++count; + found_colinear = true; + } else { + itr = itr2; + ++itr2; + } + *itr2 = *itr3; + } + itr3 = poly.begin(); + if(((*itr).x() == (*itr2).x() && (*itr).x() == (*itr3).x()) || + ((*itr).y() == (*itr2).y() && (*itr).y() == (*itr3).y()) ) { + ++count; + found_colinear = true; + } + poly.erase(poly.end() - count, poly.end()); + } + return poly.size() >= 4; + } + + polygon_90_set_data& + bloat(typename coordinate_traits::unsigned_area_type west_bloating, + typename coordinate_traits::unsigned_area_type east_bloating, + typename coordinate_traits::unsigned_area_type south_bloating, + typename coordinate_traits::unsigned_area_type north_bloating) { + std::list > polys; + get(polys); + clear(); + for(typename std::list >::iterator itr = polys.begin(); + itr != polys.end(); ++itr) { + //polygon_90_set_data psref; + //psref.insert(view_as((*itr).self_)); + //rectangle_data prerect; + //psref.extents(prerect); + resize_poly_up((*itr).self_.coords_, (coordinate_type)west_bloating, (coordinate_type)east_bloating, + (coordinate_type)south_bloating, (coordinate_type)north_bloating); + iterator_geometry_to_set > > + begin_input(view_as((*itr).self_), LOW, orient_, false, true, COUNTERCLOCKWISE), + end_input(view_as((*itr).self_), HIGH, orient_, false, true, COUNTERCLOCKWISE); + insert(begin_input, end_input, orient_); + //polygon_90_set_data pstest; + //pstest.insert(view_as((*itr).self_)); + //psref.bloat2(west_bloating, east_bloating, south_bloating, north_bloating); + //if(!equivalence(psref, pstest)) { + // std::cout << "test failed\n"; + //} + for(typename std::list >::iterator itrh = (*itr).holes_.begin(); + itrh != (*itr).holes_.end(); ++itrh) { + //rectangle_data rect; + //psref.extents(rect); + //polygon_90_set_data psrefhole; + //psrefhole.insert(prerect); + //psrefhole.insert(view_as(*itrh), true); + //polygon_45_data testpoly(*itrh); + if(resize_poly_down((*itrh).coords_,(coordinate_type)west_bloating, (coordinate_type)east_bloating, + (coordinate_type)south_bloating, (coordinate_type)north_bloating)) { + iterator_geometry_to_set > > + begin_input2(view_as(*itrh), LOW, orient_, true, true), + end_input2(view_as(*itrh), HIGH, orient_, true, true); + insert(begin_input2, end_input2, orient_); + //polygon_90_set_data pstesthole; + //pstesthole.insert(rect); + //iterator_geometry_to_set > > + // begin_input2(view_as(*itrh), LOW, orient_, true, true); + //pstesthole.insert(begin_input2, end_input, orient_); + //psrefhole.bloat2(west_bloating, east_bloating, south_bloating, north_bloating); + //if(!equivalence(psrefhole, pstesthole)) { + // std::cout << (winding(testpoly) == CLOCKWISE) << std::endl; + // std::cout << (winding(*itrh) == CLOCKWISE) << std::endl; + // polygon_90_set_data c(psrefhole); + // c.clean(); + // polygon_90_set_data a(pstesthole); + // polygon_90_set_data b(pstesthole); + // a.sort(); + // b.clean(); + // std::cout << "test hole failed\n"; + // //std::cout << testpoly << std::endl; + //} + } + } + } + return *this; + } + + polygon_90_set_data& + shrink(typename coordinate_traits::unsigned_area_type west_shrinking, + typename coordinate_traits::unsigned_area_type east_shrinking, + typename coordinate_traits::unsigned_area_type south_shrinking, + typename coordinate_traits::unsigned_area_type north_shrinking) { + std::list > polys; + get(polys); + clear(); + for(typename std::list >::iterator itr = polys.begin(); + itr != polys.end(); ++itr) { + //polygon_90_set_data psref; + //psref.insert(view_as((*itr).self_)); + //rectangle_data prerect; + //psref.extents(prerect); + //polygon_45_data testpoly((*itr).self_); + if(resize_poly_down((*itr).self_.coords_, -(coordinate_type)west_shrinking, -(coordinate_type)east_shrinking, + -(coordinate_type)south_shrinking, -(coordinate_type)north_shrinking)) { + iterator_geometry_to_set > > + begin_input(view_as((*itr).self_), LOW, orient_, false, true, COUNTERCLOCKWISE), + end_input(view_as((*itr).self_), HIGH, orient_, false, true, COUNTERCLOCKWISE); + insert(begin_input, end_input, orient_); + //iterator_geometry_to_set > > + // begin_input2(view_as((*itr).self_), LOW, orient_, false, true, COUNTERCLOCKWISE); + //polygon_90_set_data pstest; + //pstest.insert(begin_input2, end_input, orient_); + //psref.shrink2(west_shrinking, east_shrinking, south_shrinking, north_shrinking); + //if(!equivalence(psref, pstest)) { + // std::cout << "test failed\n"; + //} + for(typename std::list >::iterator itrh = (*itr).holes_.begin(); + itrh != (*itr).holes_.end(); ++itrh) { + //rectangle_data rect; + //psref.extents(rect); + //polygon_90_set_data psrefhole; + //psrefhole.insert(prerect); + //psrefhole.insert(view_as(*itrh), true); + //polygon_45_data testpoly(*itrh); + resize_poly_up((*itrh).coords_, -(coordinate_type)west_shrinking, -(coordinate_type)east_shrinking, + -(coordinate_type)south_shrinking, -(coordinate_type)north_shrinking); + iterator_geometry_to_set > > + begin_input2(view_as(*itrh), LOW, orient_, true, true), + end_input2(view_as(*itrh), HIGH, orient_, true, true); + insert(begin_input2, end_input2, orient_); + //polygon_90_set_data pstesthole; + //pstesthole.insert(rect); + //iterator_geometry_to_set > > + // begin_input2(view_as(*itrh), LOW, orient_, true, true); + //pstesthole.insert(begin_input2, end_input, orient_); + //psrefhole.shrink2(west_shrinking, east_shrinking, south_shrinking, north_shrinking); + //if(!equivalence(psrefhole, pstesthole)) { + // std::cout << (winding(testpoly) == CLOCKWISE) << std::endl; + // std::cout << (winding(*itrh) == CLOCKWISE) << std::endl; + // polygon_90_set_data c(psrefhole); + // c.clean(); + // polygon_90_set_data a(pstesthole); + // polygon_90_set_data b(pstesthole); + // a.sort(); + // b.clean(); + // std::cout << "test hole failed\n"; + // //std::cout << testpoly << std::endl; + //} + } + } + } + return *this; + } + + polygon_90_set_data& + shrink2(typename coordinate_traits::unsigned_area_type west_shrinking, + typename coordinate_traits::unsigned_area_type east_shrinking, + typename coordinate_traits::unsigned_area_type south_shrinking, + typename coordinate_traits::unsigned_area_type north_shrinking) { + rectangle_data externalBoundary; + if(!extents(externalBoundary)) return *this; + ::boost::polygon::bloat(externalBoundary, 10); //bloat by diferential ammount + //insert a hole that encompasses the data + insert(externalBoundary, true); //note that the set is in a dirty state now + sort(); //does not apply implicit OR operation + std::vector > rects; + rects.reserve(data_.size() / 2); + //begin does not apply implicit or operation, this is a dirty range + form_rectangles(rects, data_.begin(), data_.end(), orient_, rectangle_concept()); + clear(); + rectangle_data convolutionRectangle(interval_data(-((coordinate_type)east_shrinking), + (coordinate_type)west_shrinking), + interval_data(-((coordinate_type)north_shrinking), + (coordinate_type)south_shrinking)); + for(typename std::vector >::iterator itr = rects.begin(); + itr != rects.end(); ++itr) { + rectangle_data& rect = *itr; + convolve(rect, convolutionRectangle); + //insert rectangle as a hole + insert(rect, true); + } + convolve(externalBoundary, convolutionRectangle); + //insert duplicate of external boundary as solid to cancel out the external hole boundaries + insert(externalBoundary); + clean(); //we have negative values in the set, so we need to apply an OR operation to make it valid input to a boolean + return *this; + } + + polygon_90_set_data& + shrink(direction_2d dir, typename coordinate_traits::unsigned_area_type shrinking) { + if(dir == WEST) + return shrink(shrinking, 0, 0, 0); + if(dir == EAST) + return shrink(0, shrinking, 0, 0); + if(dir == SOUTH) + return shrink(0, 0, shrinking, 0); + return shrink(0, 0, 0, shrinking); + } + + polygon_90_set_data& + bloat(direction_2d dir, typename coordinate_traits::unsigned_area_type shrinking) { + if(dir == WEST) + return bloat(shrinking, 0, 0, 0); + if(dir == EAST) + return bloat(0, shrinking, 0, 0); + if(dir == SOUTH) + return bloat(0, 0, shrinking, 0); + return bloat(0, 0, 0, shrinking); + } + + polygon_90_set_data& + resize(coordinate_type west, coordinate_type east, coordinate_type south, coordinate_type north); + + polygon_90_set_data& move(coordinate_type x_delta, coordinate_type y_delta) { + for(typename std::vector > >::iterator + itr = data_.begin(); itr != data_.end(); ++itr) { + if(orient_ == orientation_2d(VERTICAL)) { + (*itr).first += x_delta; + (*itr).second.first += y_delta; + } else { + (*itr).second.first += x_delta; + (*itr).first += y_delta; + } + } + return *this; + } + + // transform set + template + polygon_90_set_data& transform(const transformation_type& transformation) { + direction_2d dir1, dir2; + transformation.get_directions(dir1, dir2); + int sign = dir1.get_sign() * dir2.get_sign(); + for(typename std::vector > >::iterator + itr = data_.begin(); itr != data_.end(); ++itr) { + if(orient_ == orientation_2d(VERTICAL)) { + transformation.transform((*itr).first, (*itr).second.first); + } else { + transformation.transform((*itr).second.first, (*itr).first); + } + (*itr).second.second *= sign; + } + if(dir1 != EAST || dir2 != NORTH) + unsorted_ = true; //some mirroring or rotation must have happened + return *this; + } + + // scale set + polygon_90_set_data& scale_up(typename coordinate_traits::unsigned_area_type factor) { + for(typename std::vector > >::iterator + itr = data_.begin(); itr != data_.end(); ++itr) { + (*itr).first *= (coordinate_type)factor; + (*itr).second.first *= (coordinate_type)factor; + } + return *this; + } + polygon_90_set_data& scale_down(typename coordinate_traits::unsigned_area_type factor) { + typedef typename coordinate_traits::coordinate_distance dt; + for(typename std::vector > >::iterator + itr = data_.begin(); itr != data_.end(); ++itr) { + (*itr).first = scaling_policy::round((dt)((*itr).first) / (dt)factor); + (*itr).second.first = scaling_policy::round((dt)((*itr).second.first) / (dt)factor); + } + unsorted_ = true; //scaling down can make coordinates equal that were not previously equal + return *this; + } + template + polygon_90_set_data& scale(const anisotropic_scale_factor& scaling) { + for(typename std::vector > >::iterator + itr = data_.begin(); itr != data_.end(); ++itr) { + if(orient_ == orientation_2d(VERTICAL)) { + scaling.scale((*itr).first, (*itr).second.first); + } else { + scaling.scale((*itr).second.first, (*itr).first); + } + } + unsorted_ = true; + return *this; + } + template + polygon_90_set_data& scale_with(const scaling_type& scaling) { + for(typename std::vector > >::iterator + itr = data_.begin(); itr != data_.end(); ++itr) { + if(orient_ == orientation_2d(VERTICAL)) { + scaling.scale((*itr).first, (*itr).second.first); + } else { + scaling.scale((*itr).second.first, (*itr).first); + } + } + unsorted_ = true; + return *this; + } + polygon_90_set_data& scale(double factor) { + typedef typename coordinate_traits::coordinate_distance dt; + for(typename std::vector > >::iterator + itr = data_.begin(); itr != data_.end(); ++itr) { + (*itr).first = scaling_policy::round((dt)((*itr).first) * (dt)factor); + (*itr).second.first = scaling_policy::round((dt)((*itr).second.first) * (dt)factor); + } + unsorted_ = true; //scaling make coordinates equal that were not previously equal + return *this; + } + + polygon_90_set_data& self_xor() { + sort(); + if(dirty_) { //if it is clean it is a no-op + boolean_op::default_arg_workaround::applyBooleanOr(data_); + dirty_ = false; + } + return *this; + } + + polygon_90_set_data& self_intersect() { + sort(); + if(dirty_) { //if it is clean it is a no-op + interval_data ivl((std::numeric_limits::min)(), (std::numeric_limits::max)()); + rectangle_data rect(ivl, ivl); + insert(rect, true); + clean(); + } + return *this; + } + + inline polygon_90_set_data& interact(const polygon_90_set_data& that) { + typedef coordinate_type Unit; + if(that.dirty_) that.clean(); + typename touch_90_operation::TouchSetData tsd; + touch_90_operation::populateTouchSetData(tsd, that.data_, 0); + std::vector > polys; + get(polys); + std::vector > graph(polys.size()+1, std::set()); + for(std::size_t i = 0; i < polys.size(); ++i){ + polygon_90_set_data psTmp(that.orient_); + psTmp.insert(polys[i]); + psTmp.clean(); + touch_90_operation::populateTouchSetData(tsd, psTmp.data_, i+1); + } + touch_90_operation::performTouch(graph, tsd); + clear(); + for(std::set::iterator itr = graph[0].begin(); itr != graph[0].end(); ++itr){ + insert(polys[(*itr)-1]); + } + dirty_ = false; + return *this; + } + + + template + void applyBooleanBinaryOp(iterator_type_1 itr1, iterator_type_1 itr1_end, + iterator_type_2 itr2, iterator_type_2 itr2_end, + T2 defaultCount) { + data_.clear(); + boolean_op::applyBooleanBinaryOp(data_, itr1, itr1_end, itr2, itr2_end, defaultCount); + } + + private: + orientation_2d orient_; + mutable value_type data_; + mutable bool dirty_; + mutable bool unsorted_; + + private: + //functions + template + void get_dispatch(output_container& output, rectangle_concept ) const { + clean(); + form_rectangles(output, data_.begin(), data_.end(), orient_, rectangle_concept()); + } + template + void get_dispatch(output_container& output, polygon_90_concept tag) const { + get_fracture(output, true, tag); + } + + template + void get_dispatch(output_container& output, polygon_90_concept tag, + size_t vthreshold) const { + get_fracture(output, true, tag, vthreshold); + } + + template + void get_dispatch(output_container& output, polygon_90_with_holes_concept tag) const { + get_fracture(output, false, tag); + } + + template + void get_dispatch(output_container& output, polygon_90_with_holes_concept tag, + size_t vthreshold) const { + get_fracture(output, false, tag, vthreshold); + } + + + template + void get_dispatch(output_container& output, polygon_45_concept tag) const { + get_fracture(output, true, tag); + } + template + void get_dispatch(output_container& output, polygon_45_with_holes_concept tag) const { + get_fracture(output, false, tag); + } + template + void get_dispatch(output_container& output, polygon_concept tag) const { + get_fracture(output, true, tag); + } + template + void get_dispatch(output_container& output, polygon_with_holes_concept tag) const { + get_fracture(output, false, tag); + } + template + void get_fracture(output_container& container, bool fracture_holes, concept_type tag) const { + clean(); + ::boost::polygon::get_polygons(container, data_.begin(), data_.end(), orient_, fracture_holes, tag); + } + + template + void get_fracture(output_container& container, bool fracture_holes, concept_type tag, + size_t vthreshold) const { + clean(); + ::boost::polygon::get_polygons(container, data_.begin(), data_.end(), orient_, fracture_holes, tag, vthreshold); + } + }; + + template + polygon_90_set_data& + polygon_90_set_data::resize(coordinate_type west, + coordinate_type east, + coordinate_type south, + coordinate_type north) { + move(-west, -south); + coordinate_type e_total = west + east; + coordinate_type n_total = south + north; + if((e_total < 0) ^ (n_total < 0)) { + //different signs + if(e_total < 0) { + shrink(0, -e_total, 0, 0); + if(n_total != 0) + return bloat(0, 0, 0, n_total); + else + return (*this); + } else { + shrink(0, 0, 0, -n_total); //shrink first + if(e_total != 0) + return bloat(0, e_total, 0, 0); + else + return (*this); + } + } else { + if(e_total < 0) { + return shrink(0, -e_total, 0, -n_total); + } + return bloat(0, e_total, 0, n_total); + } + } + + template + class property_merge_90 { + private: + std::vector, std::pair > > pmd_; + public: + inline property_merge_90() : pmd_() {} + inline property_merge_90(const property_merge_90& that) : pmd_(that.pmd_) {} + inline property_merge_90& operator=(const property_merge_90& that) { pmd_ = that.pmd_; return *this; } + inline void insert(const polygon_90_set_data& ps, const property_type& property) { + merge_scanline >:: + populate_property_merge_data(pmd_, ps.begin(), ps.end(), property, ps.orient()); + } + template + inline void insert(const GeoObjT& geoObj, const property_type& property) { + polygon_90_set_data ps; + ps.insert(geoObj); + insert(ps, property); + } + //merge properties of input geometries and store the resulting geometries of regions + //with unique sets of merged properties to polygons sets in a map keyed by sets of properties + // T = std::map, polygon_90_set_data > or + // T = std::map, polygon_90_set_data > + template + inline void merge(ResultType& result) { + merge_scanline, typename ResultType::key_type> ms; + ms.perform_merge(result, pmd_); + } + }; + + //ConnectivityExtraction computes the graph of connectivity between rectangle, polygon and + //polygon set graph nodes where an edge is created whenever the geometry in two nodes overlap + template + class connectivity_extraction_90 { + private: + typedef typename touch_90_operation::TouchSetData tsd; + tsd tsd_; + unsigned int nodeCount_; + public: + inline connectivity_extraction_90() : tsd_(), nodeCount_(0) {} + inline connectivity_extraction_90(const connectivity_extraction_90& that) : tsd_(that.tsd_), + nodeCount_(that.nodeCount_) {} + inline connectivity_extraction_90& operator=(const connectivity_extraction_90& that) { + tsd_ = that.tsd_; + nodeCount_ = that.nodeCount_; {} + return *this; + } + + //insert a polygon set graph node, the value returned is the id of the graph node + inline unsigned int insert(const polygon_90_set_data& ps) { + ps.clean(); + touch_90_operation::populateTouchSetData(tsd_, ps.begin(), ps.end(), nodeCount_); + return nodeCount_++; + } + template + inline unsigned int insert(const GeoObjT& geoObj) { + polygon_90_set_data ps; + ps.insert(geoObj); + return insert(ps); + } + + //extract connectivity and store the edges in the graph + //graph must be indexable by graph node id and the indexed value must be a std::set of + //graph node id + template + inline void extract(GraphT& graph) { + touch_90_operation::performTouch(graph, tsd_); + } + }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_traits.hpp new file mode 100644 index 0000000..c1312e8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_set_traits.hpp @@ -0,0 +1,366 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_90_SET_TRAITS_HPP +#define BOOST_POLYGON_POLYGON_90_SET_TRAITS_HPP +namespace boost { namespace polygon{ + + struct polygon_90_set_concept {}; + + template + struct traits_by_concept {}; + template + struct traits_by_concept { typedef coordinate_traits type; }; + template + struct traits_by_concept { typedef interval_traits type; }; + template + struct traits_by_concept { typedef point_traits type; }; + template + struct traits_by_concept { typedef rectangle_traits type; }; + template + struct traits_by_concept { typedef segment_traits type; }; + template + struct traits_by_concept { typedef polygon_traits type; }; + template + struct traits_by_concept { typedef polygon_traits type; }; + template + struct traits_by_concept { typedef polygon_traits type; }; + template + struct traits_by_concept { typedef polygon_traits type; }; + template + struct traits_by_concept { typedef polygon_traits type; }; + template + struct traits_by_concept { typedef polygon_traits type; }; + + struct polygon_45_set_concept; + struct polygon_set_concept; + template + struct polygon_90_set_traits; + template + struct polygon_45_set_traits; + template + struct polygon_set_traits; + template + struct traits_by_concept { typedef polygon_90_set_traits type; }; + template + struct traits_by_concept { typedef polygon_45_set_traits type; }; + template + struct traits_by_concept { typedef polygon_set_traits type; }; + + template + struct get_coordinate_type { + typedef typename traits_by_concept::type traits_type; + typedef typename traits_type::coordinate_type type; + }; + //want to prevent recursive template definition syntax errors, so duplicate get_coordinate_type + template + struct get_coordinate_type_2 { + typedef typename traits_by_concept::type traits_type; + typedef typename traits_type::coordinate_type type; + }; + template + struct get_coordinate_type { + typedef typename get_coordinate_type_2::value_type, + typename geometry_concept::value_type>::type>::type type; }; + + template + struct get_iterator_type_2 { + typedef const T* type; + static type begin(const T& t) { return &t; } + static type end(const T& t) { const T* tp = &t; ++tp; return tp; } + }; + template + struct get_iterator_type { + typedef get_iterator_type_2::type> indirect_type; + typedef typename indirect_type::type type; + static type begin(const T& t) { return indirect_type::begin(t); } + static type end(const T& t) { return indirect_type::end(t); } + }; + template + struct get_iterator_type_2 { + typedef typename T::const_iterator type; + static type begin(const T& t) { return t.begin(); } + static type end(const T& t) { return t.end(); } + }; + +// //helpers for allowing polygon 45 and containers of polygon 45 to behave interchangably in polygon_45_set_traits +// template +// struct get_coordinate_type_45 {}; +// template +// struct get_coordinate_type_2_45 {}; +// template +// struct get_coordinate_type_45 { +// typedef typename get_coordinate_type_2_45< typename T::value_type, typename geometry_concept::type >::type type; }; +// template +// struct get_coordinate_type_45 { typedef typename polygon_traits::coordinate_type type; }; +// template +// struct get_coordinate_type_45 { typedef typename polygon_traits::coordinate_type type; }; +// template +// struct get_coordinate_type_2_45 { typedef typename polygon_traits::coordinate_type type; }; +// template +// struct get_coordinate_type_2_45 { typedef typename polygon_traits::coordinate_type type; }; +// template +// struct get_iterator_type_45 {}; +// template +// struct get_iterator_type_45 { +// typedef typename T::const_iterator type; +// static type begin(const T& t) { return t.begin(); } +// static type end(const T& t) { return t.end(); } +// }; +// template +// struct get_iterator_type_45 { +// typedef const T* type; +// static type begin(const T& t) { return &t; } +// static type end(const T& t) { const T* tp = &t; ++tp; return tp; } +// }; +// template +// struct get_iterator_type_45 { +// typedef const T* type; +// static type begin(const T& t) { return &t; } +// static type end(const T& t) { const T* tp = &t; ++tp; return tp; } +// }; +// template +// struct get_iterator_type_45 { +// typedef const T* type; +// static type begin(const T& t) { return &t; } +// static type end(const T& t) { const T* tp = &t; ++tp; return tp; } +// }; + + template + struct polygon_90_set_traits { + typedef typename get_coordinate_type::type >::type coordinate_type; + typedef get_iterator_type indirection_type; + typedef typename get_iterator_type::type iterator_type; + typedef T operator_arg_type; + + static inline iterator_type begin(const T& polygon_set) { + return indirection_type::begin(polygon_set); + } + + static inline iterator_type end(const T& polygon_set) { + return indirection_type::end(polygon_set); + } + + static inline orientation_2d orient(const T&) { return HORIZONTAL; } + + static inline bool clean(const T&) { return false; } + + static inline bool sorted(const T&) { return false; } + }; + + template + struct is_manhattan_polygonal_concept { typedef gtl_no type; }; + template <> + struct is_manhattan_polygonal_concept { typedef gtl_yes type; }; + template <> + struct is_manhattan_polygonal_concept { typedef gtl_yes type; }; + template <> + struct is_manhattan_polygonal_concept { typedef gtl_yes type; }; + template <> + struct is_manhattan_polygonal_concept { typedef gtl_yes type; }; + + template + struct is_polygon_90_set_type { + typedef typename is_manhattan_polygonal_concept::type>::type type; + }; + template + struct is_polygon_90_set_type > { + typedef typename gtl_or< + typename is_manhattan_polygonal_concept >::type>::type, + typename is_manhattan_polygonal_concept::value_type>::type>::type>::type type; + }; + template + struct is_polygon_90_set_type > { + typedef typename gtl_or< + typename is_manhattan_polygonal_concept >::type>::type, + typename is_manhattan_polygonal_concept::value_type>::type>::type>::type type; + }; + + template + struct is_mutable_polygon_90_set_type { + typedef typename gtl_same_type::type>::type type; + }; + template + struct is_mutable_polygon_90_set_type > { + typedef typename gtl_or< + typename gtl_same_type >::type>::type, + typename is_manhattan_polygonal_concept::value_type>::type>::type>::type type; + }; + template + struct is_mutable_polygon_90_set_type > { + typedef typename gtl_or< + typename gtl_same_type >::type>::type, + typename is_manhattan_polygonal_concept::value_type>::type>::type>::type type; + }; + +// //specialization for rectangle, polygon_90 and polygon_90_with_holes types +// template +// struct polygon_90_set_traits +// typedef typename geometry_concept::type concept_type; +// typedef typename get_coordinate_type::type coordinate_type; +// typedef iterator_geometry_to_set iterator_type; +// typedef T operator_arg_type; + +// static inline iterator_type begin(const T& polygon_set) { +// return iterator_geometry_to_set(polygon_set, LOW, HORIZONTAL); +// } + +// static inline iterator_type end(const T& polygon_set) { +// return iterator_geometry_to_set(polygon_set, HIGH, HORIZONTAL); +// } + +// static inline orientation_2d orient(const T& polygon_set) { return HORIZONTAL; } + +// static inline bool clean(const T& polygon_set) { return false; } + +// static inline bool sorted(const T& polygon_set) { return false; } + +// }; + +// //specialization for containers of recangle, polygon_90, polygon_90_with_holes +// template +// struct polygon_90_set_traits::value_type>::type> { +// typedef typename std::iterator_traits::value_type geometry_type; +// typedef typename geometry_concept::type concept_type; +// typedef typename get_coordinate_type::type coordinate_type; +// typedef iterator_geometry_range_to_set iterator_type; +// typedef T operator_arg_type; + +// static inline iterator_type begin(const T& polygon_set) { +// return iterator_type(polygon_set.begin(), HORIZONTAL); +// } + +// static inline iterator_type end(const T& polygon_set) { +// return iterator_type(polygon_set.end(), HORIZONTAL); +// } + +// static inline orientation_2d orient(const T& polygon_set) { return HORIZONTAL; } + +// static inline bool clean(const T& polygon_set) { return false; } + +// static inline bool sorted(const T& polygon_set) { return false; } + +// }; + + //get dispatch functions + template + void get_90_dispatch(output_container_type& output, const pst& ps, + orientation_2d orient, rectangle_concept ) { + form_rectangles(output, ps.begin(), ps.end(), orient, rectangle_concept()); + } + + template + void get_90_dispatch(output_container_type& output, const pst& ps, + orientation_2d orient, polygon_90_concept tag) { + get_polygons(output, ps.begin(), ps.end(), orient, true, tag); + } + + template + void get_90_dispatch(output_container_type& output, const pst& ps, + orientation_2d orient, polygon_90_with_holes_concept tag) { + get_polygons(output, ps.begin(), ps.end(), orient, false, tag); + } + + //by default works with containers of rectangle, polygon or polygon with holes + //must be specialized to work with anything else + template + struct polygon_90_set_mutable_traits {}; + template + struct polygon_90_set_mutable_traits > { + typedef typename geometry_concept::type concept_type; + template + static inline void set(std::list& polygon_set, input_iterator_type input_begin, input_iterator_type input_end, orientation_2d orient) { + polygon_set.clear(); + polygon_90_set_data >::coordinate_type> ps(orient); + ps.reserve(std::distance(input_begin, input_end)); + ps.insert(input_begin, input_end, orient); + ps.clean(); + get_90_dispatch(polygon_set, ps, orient, concept_type()); + } + }; + template + struct polygon_90_set_mutable_traits > { + typedef typename geometry_concept::type concept_type; + template + static inline void set(std::vector& polygon_set, input_iterator_type input_begin, input_iterator_type input_end, orientation_2d orient) { + polygon_set.clear(); + size_t num_ele = std::distance(input_begin, input_end); + polygon_set.reserve(num_ele); + polygon_90_set_data >::coordinate_type> ps(orient); + ps.reserve(num_ele); + ps.insert(input_begin, input_end, orient); + ps.clean(); + get_90_dispatch(polygon_set, ps, orient, concept_type()); + } + }; + + template + struct polygon_90_set_mutable_traits > { + + template + static inline void set(polygon_90_set_data& polygon_set, + input_iterator_type input_begin, input_iterator_type input_end, + orientation_2d orient) { + polygon_set.clear(); + polygon_set.reserve(std::distance(input_begin, input_end)); + polygon_set.insert(input_begin, input_end, orient); + } + + }; + + template + struct polygon_90_set_traits > { + typedef typename polygon_90_set_data::coordinate_type coordinate_type; + typedef typename polygon_90_set_data::iterator_type iterator_type; + typedef typename polygon_90_set_data::operator_arg_type operator_arg_type; + + static inline iterator_type begin(const polygon_90_set_data& polygon_set) { + return polygon_set.begin(); + } + + static inline iterator_type end(const polygon_90_set_data& polygon_set) { + return polygon_set.end(); + } + + static inline orientation_2d orient(const polygon_90_set_data& polygon_set) { return polygon_set.orient(); } + + static inline bool clean(const polygon_90_set_data& polygon_set) { polygon_set.clean(); return true; } + + static inline bool sorted(const polygon_90_set_data& polygon_set) { polygon_set.sort(); return true; } + + }; + + template + struct is_polygon_90_set_concept { }; + template <> + struct is_polygon_90_set_concept { typedef gtl_yes type; }; + template <> + struct is_polygon_90_set_concept { typedef gtl_yes type; }; + template <> + struct is_polygon_90_set_concept { typedef gtl_yes type; }; + template <> + struct is_polygon_90_set_concept { typedef gtl_yes type; }; + + template + struct is_mutable_polygon_90_set_concept { typedef gtl_no type; }; + template <> + struct is_mutable_polygon_90_set_concept { typedef gtl_yes type; }; + + template + struct geometry_concept > { typedef polygon_90_set_concept type; }; + + //template + //typename enable_if::type, void>::type + //print_is_polygon_90_set_concept(const T& t) { std::cout << "is polygon 90 set concept\n"; } + //template + //typename enable_if::type, void>::type + //print_is_mutable_polygon_90_set_concept(const T& t) { std::cout << "is mutable polygon 90 set concept\n"; } +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_with_holes_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_with_holes_data.hpp new file mode 100644 index 0000000..be43b65 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_90_with_holes_data.hpp @@ -0,0 +1,115 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_90_WITH_HOLES_DATA_HPP +#define BOOST_POLYGON_POLYGON_90_WITH_HOLES_DATA_HPP +namespace boost { namespace polygon{ +#include "isotropy.hpp" +#include "polygon_90_data.hpp" +struct polygon_90_with_holes_concept; +template +class polygon_90_with_holes_data { +public: + typedef polygon_90_with_holes_concept geometry_type; + typedef T coordinate_type; + typedef typename polygon_90_data::iterator_type iterator_type; + typedef typename polygon_90_data::compact_iterator_type compact_iterator_type; + typedef typename std::list >::const_iterator iterator_holes_type; + typedef polygon_90_data hole_type; + typedef typename coordinate_traits::area_type area_type; + typedef point_data point_type; + + // default constructor of point does not initialize x and y + inline polygon_90_with_holes_data() : self_(), holes_() {} //do nothing default constructor + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline polygon_90_with_holes_data& set(iT input_begin, iT input_end) { + self_.set(input_begin, input_end); + return *this; + } + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline polygon_90_with_holes_data& set_compact(iT input_begin, iT input_end) { + self_.set_compact(input_begin, input_end); + return *this; + } + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline polygon_90_with_holes_data& set_holes(iT input_begin, iT input_end) { + holes_.clear(); //just in case there was some old data there + for( ; input_begin != input_end; ++ input_begin) { + holes_.push_back(hole_type()); + holes_.back().set_compact((*input_begin).begin_compact(), (*input_begin).end_compact()); + } + return *this; + } + + // copy constructor (since we have dynamic memory) + inline polygon_90_with_holes_data(const polygon_90_with_holes_data& that) : self_(that.self_), + holes_(that.holes_) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline polygon_90_with_holes_data& operator=(const polygon_90_with_holes_data& that) { + self_ = that.self_; + holes_ = that.holes_; + return *this; + } + + template + inline polygon_90_with_holes_data& operator=(const T2& rvalue); + + // get begin iterator, returns a pointer to a const coordinate_type + inline const iterator_type begin() const { + return self_.begin(); + } + + // get end iterator, returns a pointer to a const coordinate_type + inline const iterator_type end() const { + return self_.end(); + } + + // get begin iterator, returns a pointer to a const coordinate_type + inline const compact_iterator_type begin_compact() const { + return self_.begin_compact(); + } + + // get end iterator, returns a pointer to a const coordinate_type + inline const compact_iterator_type end_compact() const { + return self_.end_compact(); + } + + inline std::size_t size() const { + return self_.size(); + } + + // get begin iterator, returns a pointer to a const polygon + inline const iterator_holes_type begin_holes() const { + return holes_.begin(); + } + + // get end iterator, returns a pointer to a const polygon + inline const iterator_holes_type end_holes() const { + return holes_.end(); + } + + inline std::size_t size_holes() const { + return holes_.size(); + } + +private: + polygon_90_data self_; + std::list holes_; +}; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_data.hpp new file mode 100644 index 0000000..9784466 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_data.hpp @@ -0,0 +1,69 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_DATA_HPP +#define BOOST_POLYGON_POLYGON_DATA_HPP +namespace boost { namespace polygon{ +struct polygon_concept; +template +class polygon_data { +public: + typedef polygon_concept geometry_type; + typedef T coordinate_type; + typedef typename std::vector >::const_iterator iterator_type; + typedef typename coordinate_traits::coordinate_distance area_type; + typedef point_data point_type; + + inline polygon_data() : coords_() {} //do nothing default constructor + + template + inline polygon_data(iT input_begin, iT input_end) : coords_(input_begin, input_end) {} + + template + inline polygon_data& set(iT input_begin, iT input_end) { + coords_.clear(); //just in case there was some old data there + coords_.insert(coords_.end(), input_begin, input_end); + return *this; + } + + // copy constructor (since we have dynamic memory) + inline polygon_data(const polygon_data& that) : coords_(that.coords_) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline polygon_data& operator=(const polygon_data& that) { + coords_ = that.coords_; + return *this; + } + + template + inline polygon_data& operator=(const T2& rvalue); + + inline bool operator==(const polygon_data& that) const { + if(coords_.size() != that.coords_.size()) return false; + for(std::size_t i = 0; i < coords_.size(); ++i) { + if(coords_[i] != that.coords_[i]) return false; + } + return true; + } + + inline bool operator!=(const polygon_data& that) const { return !((*this) == that); } + + // get begin iterator, returns a pointer to a const Unit + inline iterator_type begin() const { return coords_.begin(); } + + // get end iterator, returns a pointer to a const Unit + inline iterator_type end() const { return coords_.end(); } + + inline std::size_t size() const { return coords_.size(); } + +public: + std::vector > coords_; +}; + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_concept.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_concept.hpp new file mode 100644 index 0000000..e3d37b8 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_concept.hpp @@ -0,0 +1,581 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_SET_CONCEPT_HPP +#define BOOST_POLYGON_POLYGON_SET_CONCEPT_HPP +#include "polygon_set_data.hpp" +#include "detail/polygon_simplify.hpp" +namespace boost { namespace polygon{ + + template + struct is_either_polygon_set_type { + typedef typename gtl_or::type, typename is_polygon_set_type::type >::type type; + }; + + template + struct is_any_polygon_set_type { + typedef typename gtl_or::type, typename is_polygon_set_type::type >::type type; + }; + + template + typename enable_if< typename is_any_polygon_set_type::type, + typename polygon_set_traits::iterator_type>::type + begin_polygon_set_data(const polygon_set_type& polygon_set) { + return polygon_set_traits::begin(polygon_set); + } + + template + typename enable_if< typename is_any_polygon_set_type::type, + typename polygon_set_traits::iterator_type>::type + end_polygon_set_data(const polygon_set_type& polygon_set) { + return polygon_set_traits::end(polygon_set); + } + + template + typename enable_if< typename is_polygon_set_type::type, + bool>::type + clean(const polygon_set_type& polygon_set) { + return polygon_set_traits::clean(polygon_set); + } + + //assign + template + typename enable_if< typename gtl_and< + typename is_mutable_polygon_set_type::type, + typename is_any_polygon_set_type::type>::type, + polygon_set_type_1>::type & + assign(polygon_set_type_1& lvalue, const polygon_set_type_2& rvalue) { + if(clean(rvalue)) + polygon_set_mutable_traits::set(lvalue, begin_polygon_set_data(rvalue), end_polygon_set_data(rvalue)); + else { + polygon_set_data::coordinate_type> ps; + ps.insert(begin_polygon_set_data(rvalue), end_polygon_set_data(rvalue)); + ps.clean(); + polygon_set_mutable_traits::set(lvalue, ps.begin(), ps.end()); + } + return lvalue; + } + + //get trapezoids + template + typename enable_if< typename is_mutable_polygon_set_type::type, + void>::type + get_trapezoids(output_container_type& output, const polygon_set_type& polygon_set) { + polygon_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.get_trapezoids(output); + } + + //get trapezoids + template + typename enable_if< typename is_mutable_polygon_set_type::type, + void>::type + get_trapezoids(output_container_type& output, const polygon_set_type& polygon_set, + orientation_2d orient) { + polygon_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.get_trapezoids(output, orient); + } + + //equivalence + template + typename enable_if< typename gtl_and_3 < + typename is_any_polygon_set_type::type, + typename is_any_polygon_set_type::type, + typename is_either_polygon_set_type::type>::type, + bool>::type + equivalence(const polygon_set_type_1& lvalue, + const polygon_set_type_2& rvalue) { + polygon_set_data::coordinate_type> ps1; + assign(ps1, lvalue); + polygon_set_data::coordinate_type> ps2; + assign(ps2, rvalue); + return ps1 == ps2; + } + + //clear + template + typename enable_if< typename is_mutable_polygon_set_type::type, + void>::type + clear(polygon_set_type& polygon_set) { + polygon_set_data::coordinate_type> ps; + assign(polygon_set, ps); + } + + //empty + template + typename enable_if< typename is_mutable_polygon_set_type::type, + bool>::type + empty(const polygon_set_type& polygon_set) { + if(clean(polygon_set)) return begin_polygon_set_data(polygon_set) == end_polygon_set_data(polygon_set); + polygon_set_data::coordinate_type> ps; + assign(ps, polygon_set); + ps.clean(); + return ps.empty(); + } + + //extents + template + typename enable_if< typename gtl_and< + typename is_mutable_polygon_set_type::type, + typename is_mutable_rectangle_concept::type>::type>::type, + bool>::type + extents(rectangle_type& extents_rectangle, + const polygon_set_type& polygon_set) { + clean(polygon_set); + polygon_set_data::coordinate_type> ps; + assign(ps, polygon_set); + return ps.extents(extents_rectangle); + } + + //area + template + typename enable_if< typename is_mutable_polygon_set_type::type, + typename coordinate_traits::coordinate_type>::area_type>::type + area(const polygon_set_type& polygon_set) { + typedef typename polygon_set_traits::coordinate_type Unit; + typedef polygon_with_holes_data p_type; + typedef typename coordinate_traits::area_type area_type; + std::vector polys; + assign(polys, polygon_set); + area_type retval = (area_type)0; + for(std::size_t i = 0; i < polys.size(); ++i) { + retval += area(polys[i]); + } + return retval; + } + + template + typename enable_if< typename is_mutable_polygon_set_type::type, + std::size_t>::type + simplify(polygon_set_type& polygon_set, typename coordinate_traits< + typename polygon_set_traits::coordinate_type + >::coordinate_distance threshold) { + typedef typename polygon_set_traits::coordinate_type Unit; + typedef polygon_with_holes_data p_type; + std::vector polys; + assign(polys, polygon_set); + std::size_t retval = 0; + for(std::size_t i = 0; i < polys.size(); ++i) { + retval += detail::simplify_detail::simplify(polys[i].self_.coords_, + polys[i].self_.coords_, threshold); + for(typename std::list >::iterator itrh = + polys[i].holes_.begin(); itrh != (polys[i].holes_.end()); ++itrh) { + retval += detail::simplify_detail::simplify((*itrh).coords_, + (*itrh).coords_, threshold); + } + } + assign(polygon_set, polys); + return retval; + } + + template + typename enable_if< typename is_mutable_polygon_set_type::type, + polygon_set_type>::type & + resize(polygon_set_type& polygon_set, coord_type resizing, bool corner_fill_arcs = false, int num_circle_segments = 0) { + typedef typename polygon_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_set_data ps; + assign(ps, polygon_set); + ps.resize(resizing, corner_fill_arcs,num_circle_segments); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_set_type::type, + polygon_set_type>::type & + bloat(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type bloating) { + return resize(polygon_set, bloating); + } + + template + typename enable_if< typename is_mutable_polygon_set_type::type, + polygon_set_type>::type & + shrink(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type shrinking) { + return resize(polygon_set, -(typename polygon_set_traits::coordinate_type)shrinking); + } + + //interact + template + typename enable_if< typename gtl_and_3 < + typename is_any_polygon_set_type::type, + typename is_any_polygon_set_type::type, + typename is_either_polygon_set_type::type>::type, + polygon_set_type_1>::type& + interact(polygon_set_type_1& polygon_set_1, const polygon_set_type_2& polygon_set_2) { + polygon_set_data::coordinate_type> ps1; + assign(ps1, polygon_set_1); + polygon_set_data::coordinate_type> ps2; + assign(ps2, polygon_set_2); + ps1.interact(ps2); + assign(polygon_set_1, ps1); + return polygon_set_1; + } + + template + typename enable_if< typename is_mutable_polygon_set_type::type, + polygon_set_type>::type & + scale_up(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + typedef typename polygon_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_set_data ps; + assign(ps, polygon_set); + ps.scale_up(factor); + assign(polygon_set, ps); + return polygon_set; + } + + template + typename enable_if< typename is_mutable_polygon_set_type::type, + polygon_set_type>::type & + scale_down(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + typedef typename polygon_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_set_data ps; + assign(ps, polygon_set); + ps.scale_down(factor); + assign(polygon_set, ps); + return polygon_set; + } + + //transform + template + typename enable_if< typename is_mutable_polygon_set_type::type, + polygon_set_type>::type & + transform(polygon_set_type& polygon_set, + const transformation_type& transformation) { + typedef typename polygon_set_traits::coordinate_type Unit; + clean(polygon_set); + polygon_set_data ps; + assign(ps, polygon_set); + ps.transform(transformation); + assign(polygon_set, ps); + return polygon_set; + } + + //keep + template + typename enable_if< typename is_mutable_polygon_set_type::type, + polygon_set_type>::type & + keep(polygon_set_type& polygon_set, + typename coordinate_traits::coordinate_type>::area_type min_area, + typename coordinate_traits::coordinate_type>::area_type max_area, + typename coordinate_traits::coordinate_type>::unsigned_area_type min_width, + typename coordinate_traits::coordinate_type>::unsigned_area_type max_width, + typename coordinate_traits::coordinate_type>::unsigned_area_type min_height, + typename coordinate_traits::coordinate_type>::unsigned_area_type max_height) { + typedef typename polygon_set_traits::coordinate_type Unit; + typedef typename coordinate_traits::unsigned_area_type uat; + std::list > polys; + assign(polys, polygon_set); + typename std::list >::iterator itr_nxt; + for(typename std::list >::iterator itr = polys.begin(); itr != polys.end(); itr = itr_nxt){ + itr_nxt = itr; + ++itr_nxt; + rectangle_data bbox; + extents(bbox, *itr); + uat pwidth = delta(bbox, HORIZONTAL); + if(pwidth > min_width && pwidth <= max_width){ + uat pheight = delta(bbox, VERTICAL); + if(pheight > min_height && pheight <= max_height){ + typename coordinate_traits::area_type parea = area(*itr); + if(parea <= max_area && parea >= min_area) { + continue; + } + } + } + polys.erase(itr); + } + assign(polygon_set, polys); + return polygon_set; + } + + namespace operators { + + struct yes_ps_ob : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4 < yes_ps_ob, typename is_any_polygon_set_type::type, + typename is_any_polygon_set_type::type, + typename is_either_polygon_set_type::type>::type, + polygon_set_view >::type + operator|(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_set_view + (lvalue, rvalue); + } + + struct yes_ps_op : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4 < yes_ps_op, + typename gtl_if::type>::type, + typename gtl_if::type>::type, + typename gtl_if::type>::type> + ::type, polygon_set_view >::type + operator+(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_set_view + (lvalue, rvalue); + } + + struct yes_ps_os : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4 < yes_ps_os, + typename is_any_polygon_set_type::type, + typename is_any_polygon_set_type::type, + typename is_either_polygon_set_type::type>::type, + polygon_set_view >::type + operator*(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_set_view + (lvalue, rvalue); + } + + struct yes_ps_oa : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4 < yes_ps_oa, + typename is_any_polygon_set_type::type, + typename is_any_polygon_set_type::type, + typename is_either_polygon_set_type::type>::type, + polygon_set_view >::type + operator&(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_set_view + (lvalue, rvalue); + } + + struct yes_ps_ox : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4 < yes_ps_ox, + typename is_any_polygon_set_type::type, + typename is_any_polygon_set_type::type, + typename is_either_polygon_set_type::type>::type, + polygon_set_view >::type + operator^(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_set_view + (lvalue, rvalue); + } + + struct yes_ps_om : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4 < yes_ps_om, + typename gtl_if::type>::type, + typename gtl_if::type>::type, + typename gtl_if::type>::type> + ::type, polygon_set_view >::type + operator-(const geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return polygon_set_view + (lvalue, rvalue); + } + + struct yes_ps_ope : gtl_yes {}; + + template + typename enable_if< typename gtl_and_4< yes_ps_ope, gtl_yes, typename is_mutable_polygon_set_type::type, + typename is_any_polygon_set_type::type>::type, + geometry_type_1>::type & + operator+=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct yes_ps_obe : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< yes_ps_obe, typename is_mutable_polygon_set_type::type, + typename is_any_polygon_set_type::type>::type, + geometry_type_1>::type & + operator|=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct yes_ps_ose : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< yes_ps_ose, typename is_mutable_polygon_set_type::type, + typename is_any_polygon_set_type::type>::type, + geometry_type_1>::type & + operator*=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct yes_ps_oae : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3< yes_ps_oae, typename is_mutable_polygon_set_type::type, + typename is_any_polygon_set_type::type>::type, + geometry_type_1>::type & + operator&=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct yes_ps_oxe : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< yes_ps_oxe, typename is_mutable_polygon_set_type::type, + typename is_any_polygon_set_type::type>::type, + geometry_type_1>::type & + operator^=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + struct yes_ps_ome : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3< yes_ps_ome, typename is_mutable_polygon_set_type::type, + typename is_any_polygon_set_type::type>::type, + geometry_type_1>::type & + operator-=(geometry_type_1& lvalue, const geometry_type_2& rvalue) { + return self_assignment_boolean_op(lvalue, rvalue); + } + + // TODO: Dafna, test these four resizing operators + struct y_ps_rpe : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3< y_ps_rpe, typename is_mutable_polygon_set_type::type, + typename gtl_same_type::type, + coordinate_concept>::type>::type, + geometry_type_1>::type & + operator+=(geometry_type_1& lvalue, coordinate_type_1 rvalue) { + return resize(lvalue, rvalue); + } + + struct y_ps_rme : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_same_type::type, + coordinate_concept>::type>::type, + geometry_type_1>::type & + operator-=(geometry_type_1& lvalue, coordinate_type_1 rvalue) { + return resize(lvalue, -rvalue); + } + + struct y_ps_rp : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_same_type::type, + coordinate_concept>::type> + ::type, geometry_type_1>::type + operator+(const geometry_type_1& lvalue, coordinate_type_1 rvalue) { + geometry_type_1 retval(lvalue); + retval += rvalue; + return retval; + } + + struct y_ps_rm : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename gtl_same_type::type, + coordinate_concept>::type> + ::type, geometry_type_1>::type + operator-(const geometry_type_1& lvalue, coordinate_type_1 rvalue) { + geometry_type_1 retval(lvalue); + retval -= rvalue; + return retval; + } + + + } //end operators namespace + + template + struct view_of { + typedef typename get_coordinate_type::type >::type coordinate_type; + T* tp; + std::vector > polys; + view_of(const T& obj) : tp(), polys() { + std::vector > gpolys; + assign(gpolys, obj); + for(typename std::vector >::iterator itr = gpolys.begin(); + itr != gpolys.end(); ++itr) { + polys.push_back(polygon_45_with_holes_data()); + assign(polys.back(), view_as(*itr)); + } + } + view_of(T& obj) : tp(&obj), polys() { + std::vector > gpolys; + assign(gpolys, obj); + for(typename std::vector >::iterator itr = gpolys.begin(); + itr != gpolys.end(); ++itr) { + polys.push_back(polygon_45_with_holes_data()); + assign(polys.back(), view_as(*itr)); + } + } + + typedef typename std::vector >::const_iterator iterator_type; + typedef view_of operator_arg_type; + + inline iterator_type begin() const { + return polys.begin(); + } + + inline iterator_type end() const { + return polys.end(); + } + + inline orientation_2d orient() const { return HORIZONTAL; } + + inline bool clean() const { return false; } + + inline bool sorted() const { return false; } + + inline T& get() { return *tp; } + }; + + template + struct polygon_45_set_traits > { + typedef typename view_of::coordinate_type coordinate_type; + typedef typename view_of::iterator_type iterator_type; + typedef view_of operator_arg_type; + + static inline iterator_type begin(const view_of& polygon_set) { + return polygon_set.begin(); + } + + static inline iterator_type end(const view_of& polygon_set) { + return polygon_set.end(); + } + + static inline orientation_2d orient(const view_of& polygon_set) { + return polygon_set.orient(); } + + static inline bool clean(const view_of& polygon_set) { + return polygon_set.clean(); } + + static inline bool sorted(const view_of& polygon_set) { + return polygon_set.sorted(); } + + }; + + template + struct geometry_concept > { + typedef polygon_45_set_concept type; + }; + + template + struct get_coordinate_type, polygon_45_set_concept> { + typedef typename view_of::coordinate_type type; + }; + template + struct get_iterator_type_2, polygon_45_set_concept> { + typedef typename view_of::iterator_type type; + static type begin(const view_of& t) { return t.begin(); } + static type end(const view_of& t) { return t.end(); } + }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_data.hpp new file mode 100644 index 0000000..32dd800 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_data.hpp @@ -0,0 +1,1006 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_SET_DATA_HPP +#define BOOST_POLYGON_POLYGON_SET_DATA_HPP +#include "polygon_45_set_data.hpp" +#include "polygon_45_set_concept.hpp" +#include "polygon_traits.hpp" +#include "detail/polygon_arbitrary_formation.hpp" + +namespace boost { namespace polygon { + + + // utility function to round coordinate types down + // rounds down for both negative and positive numbers + // intended really for integer type T (does not make sense for float) + template + static inline T round_down(double val) { + T rounded_val = (T)(val); + if(val < (double)rounded_val) + --rounded_val; + return rounded_val; + } + template + static inline point_data round_down(point_data v) { + return point_data(round_down(v.x()),round_down(v.y())); + } + + + + //foward declare view + template class polygon_set_view; + + template + class polygon_set_data { + public: + typedef T coordinate_type; + typedef point_data point_type; + typedef std::pair edge_type; + typedef std::pair element_type; + typedef std::vector value_type; + typedef typename value_type::const_iterator iterator_type; + typedef polygon_set_data operator_arg_type; + + // default constructor + inline polygon_set_data() : data_(), dirty_(false), unsorted_(false), is_45_(true) {} + + // constructor from an iterator pair over edge data + template + inline polygon_set_data(iT input_begin, iT input_end) : data_(), dirty_(false), unsorted_(false), is_45_(true) { + for( ; input_begin != input_end; ++input_begin) { insert(*input_begin); } + } + + // copy constructor + inline polygon_set_data(const polygon_set_data& that) : + data_(that.data_), dirty_(that.dirty_), unsorted_(that.unsorted_), is_45_(that.is_45_) {} + + // copy constructor + template + inline polygon_set_data(const polygon_set_view& that); + + // destructor + inline ~polygon_set_data() {} + + // assignement operator + inline polygon_set_data& operator=(const polygon_set_data& that) { + if(this == &that) return *this; + data_ = that.data_; + dirty_ = that.dirty_; + unsorted_ = that.unsorted_; + is_45_ = that.is_45_; + return *this; + } + + template + inline polygon_set_data& operator=(const polygon_set_view& geometry) { + (*this) = geometry.value(); + dirty_ = false; + unsorted_ = false; + return *this; + } + + template + inline polygon_set_data& operator=(const geometry_object& geometry) { + data_.clear(); + insert(geometry); + return *this; + } + + + // insert iterator range + inline void insert(iterator_type input_begin, iterator_type input_end, bool is_hole = false) { + if(input_begin == input_end || (!data_.empty() && &(*input_begin) == &(*(data_.begin())))) return; + dirty_ = true; + unsorted_ = true; + while(input_begin != input_end) { + insert(*input_begin, is_hole); + ++input_begin; + } + } + + // insert iterator range + template + inline void insert(iT input_begin, iT input_end, bool is_hole = false) { + if(input_begin == input_end) return; + for(; input_begin != input_end; ++input_begin) { + insert(*input_begin, is_hole); + } + } + + template + inline void insert(const geometry_type& geometry_object, bool is_hole = false) { + insert(geometry_object, is_hole, typename geometry_concept::type()); + } + + template + inline void insert(const polygon_type& polygon_object, bool is_hole, polygon_concept ) { + insert_vertex_sequence(begin_points(polygon_object), end_points(polygon_object), winding(polygon_object), is_hole); + } + + inline void insert(const polygon_set_data& ps, bool is_hole = false) { + insert(ps.data_.begin(), ps.data_.end(), is_hole); + } + + template + inline void insert(const polygon_45_set_type& ps, bool is_hole, polygon_45_set_concept) { + std::vector::coordinate_type> > polys; + assign(polys, ps); + insert(polys.begin(), polys.end(), is_hole); + } + + template + inline void insert(const polygon_90_set_type& ps, bool is_hole, polygon_90_set_concept) { + std::vector::coordinate_type> > polys; + assign(polys, ps); + insert(polys.begin(), polys.end(), is_hole); + } + + template + inline void insert(const polygon_type& polygon_object, bool is_hole, polygon_45_concept ) { + insert(polygon_object, is_hole, polygon_concept()); } + + template + inline void insert(const polygon_type& polygon_object, bool is_hole, polygon_90_concept ) { + insert(polygon_object, is_hole, polygon_concept()); } + + template + inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, + polygon_with_holes_concept ) { + insert(polygon_with_holes_object, is_hole, polygon_concept()); + for(typename polygon_with_holes_traits::iterator_holes_type itr = + begin_holes(polygon_with_holes_object); + itr != end_holes(polygon_with_holes_object); ++itr) { + insert(*itr, !is_hole, polygon_concept()); + } + } + + template + inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, + polygon_45_with_holes_concept ) { + insert(polygon_with_holes_object, is_hole, polygon_with_holes_concept()); } + + template + inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, + polygon_90_with_holes_concept ) { + insert(polygon_with_holes_object, is_hole, polygon_with_holes_concept()); } + + template + inline void insert(const rectangle_type& rectangle_object, bool is_hole, rectangle_concept ) { + polygon_90_data poly; + assign(poly, rectangle_object); + insert(poly, is_hole, polygon_concept()); + } + + inline void insert_clean(const element_type& edge, bool is_hole = false) { + if( ! scanline_base::is_45_degree(edge.first) && + ! scanline_base::is_horizontal(edge.first) && + ! scanline_base::is_vertical(edge.first) ) is_45_ = false; + data_.push_back(edge); + if(data_.back().first.second < data_.back().first.first) { + std::swap(data_.back().first.second, data_.back().first.first); + data_.back().second *= -1; + } + if(is_hole) + data_.back().second *= -1; + } + + inline void insert(const element_type& edge, bool is_hole = false) { + insert_clean(edge, is_hole); + dirty_ = true; + unsorted_ = true; + } + + template + inline void insert_vertex_sequence(iT begin_vertex, iT end_vertex, direction_1d winding, bool is_hole) { + if (begin_vertex == end_vertex) { + // No edges to insert. + return; + } + // Current edge endpoints. + iT vertex0 = begin_vertex; + iT vertex1 = begin_vertex; + if (++vertex1 == end_vertex) { + // No edges to insert. + return; + } + int wmultiplier = (winding == COUNTERCLOCKWISE) ? 1 : -1; + if (is_hole) { + wmultiplier = -wmultiplier; + } + dirty_ = true; + unsorted_ = true; + while (vertex0 != end_vertex) { + point_type p0, p1; + assign(p0, *vertex0); + assign(p1, *vertex1); + if (p0 != p1) { + int hmultiplier = (p0.get(HORIZONTAL) == p1.get(HORIZONTAL)) ? -1 : 1; + element_type elem(edge_type(p0, p1), hmultiplier * wmultiplier); + insert_clean(elem); + } + ++vertex0; + ++vertex1; + if (vertex1 == end_vertex) { + vertex1 = begin_vertex; + } + } + } + + template + inline void get(output_container& output) const { + get_dispatch(output, typename geometry_concept::type()); + } + + // append to the container cT with polygons of three or four verticies + // slicing orientation is vertical + template + void get_trapezoids(cT& container) const { + clean(); + trapezoid_arbitrary_formation pf; + typedef typename polygon_arbitrary_formation::vertex_half_edge vertex_half_edge; + std::vector data; + for(iterator_type itr = data_.begin(); itr != data_.end(); ++itr){ + data.push_back(vertex_half_edge((*itr).first.first, (*itr).first.second, (*itr).second)); + data.push_back(vertex_half_edge((*itr).first.second, (*itr).first.first, -1 * (*itr).second)); + } + polygon_sort(data.begin(), data.end()); + pf.scan(container, data.begin(), data.end()); + //std::cout << "DONE FORMING POLYGONS\n"; + } + + // append to the container cT with polygons of three or four verticies + template + void get_trapezoids(cT& container, orientation_2d slicing_orientation) const { + if(slicing_orientation == VERTICAL) { + get_trapezoids(container); + } else { + polygon_set_data ps(*this); + ps.transform(axis_transformation(axis_transformation::SWAP_XY)); + cT result; + ps.get_trapezoids(result); + for(typename cT::iterator itr = result.begin(); itr != result.end(); ++itr) { + ::boost::polygon::transform(*itr, axis_transformation(axis_transformation::SWAP_XY)); + } + container.insert(container.end(), result.begin(), result.end()); + } + } + + // equivalence operator + inline bool operator==(const polygon_set_data& p) const; + + // inequivalence operator + inline bool operator!=(const polygon_set_data& p) const { + return !((*this) == p); + } + + // get iterator to begin vertex data + inline iterator_type begin() const { + return data_.begin(); + } + + // get iterator to end vertex data + inline iterator_type end() const { + return data_.end(); + } + + const value_type& value() const { + return data_; + } + + // clear the contents of the polygon_set_data + inline void clear() { data_.clear(); dirty_ = unsorted_ = false; } + + // find out if Polygon set is empty + inline bool empty() const { return data_.empty(); } + + // get the Polygon set size in vertices + inline std::size_t size() const { clean(); return data_.size(); } + + // get the current Polygon set capacity in vertices + inline std::size_t capacity() const { return data_.capacity(); } + + // reserve size of polygon set in vertices + inline void reserve(std::size_t size) { return data_.reserve(size); } + + // find out if Polygon set is sorted + inline bool sorted() const { return !unsorted_; } + + // find out if Polygon set is clean + inline bool dirty() const { return dirty_; } + + void clean() const; + + void sort() const{ + if(unsorted_) { + polygon_sort(data_.begin(), data_.end()); + unsorted_ = false; + } + } + + template + void set(input_iterator_type input_begin, input_iterator_type input_end) { + clear(); + reserve(std::distance(input_begin,input_end)); + insert(input_begin, input_end); + dirty_ = true; + unsorted_ = true; + } + + void set(const value_type& value) { + data_ = value; + dirty_ = true; + unsorted_ = true; + } + + template + bool extents(rectangle_type& rect) { + clean(); + if(empty()) return false; + bool first_iteration = true; + for(iterator_type itr = begin(); + itr != end(); ++itr) { + rectangle_type edge_box; + set_points(edge_box, (*itr).first.first, (*itr).first.second); + if(first_iteration) + rect = edge_box; + else + encompass(rect, edge_box); + first_iteration = false; + } + return true; + } + + inline polygon_set_data& + resize(coordinate_type resizing, bool corner_fill_arc = false, unsigned int num_circle_segments=0); + + template + inline polygon_set_data& + transform(const transform_type& tr) { + std::vector > polys; + get(polys); + clear(); + for(std::size_t i = 0 ; i < polys.size(); ++i) { + ::boost::polygon::transform(polys[i], tr); + insert(polys[i]); + } + unsorted_ = true; + dirty_ = true; + return *this; + } + + inline polygon_set_data& + scale_up(typename coordinate_traits::unsigned_area_type factor) { + for(typename value_type::iterator itr = data_.begin(); itr != data_.end(); ++itr) { + ::boost::polygon::scale_up((*itr).first.first, factor); + ::boost::polygon::scale_up((*itr).first.second, factor); + } + return *this; + } + + inline polygon_set_data& + scale_down(typename coordinate_traits::unsigned_area_type factor) { + for(typename value_type::iterator itr = data_.begin(); itr != data_.end(); ++itr) { + bool vb = (*itr).first.first.x() == (*itr).first.second.x(); + ::boost::polygon::scale_down((*itr).first.first, factor); + ::boost::polygon::scale_down((*itr).first.second, factor); + bool va = (*itr).first.first.x() == (*itr).first.second.x(); + if(!vb && va) { + (*itr).second *= -1; + } + } + unsorted_ = true; + dirty_ = true; + return *this; + } + + template + inline polygon_set_data& scale(polygon_set_data&, + const scaling_type& scaling) { + for(typename value_type::iterator itr = begin(); itr != end(); ++itr) { + bool vb = (*itr).first.first.x() == (*itr).first.second.x(); + ::boost::polygon::scale((*itr).first.first, scaling); + ::boost::polygon::scale((*itr).first.second, scaling); + bool va = (*itr).first.first.x() == (*itr).first.second.x(); + if(!vb && va) { + (*itr).second *= -1; + } + } + unsorted_ = true; + dirty_ = true; + return *this; + } + + static inline void compute_offset_edge(point_data& pt1, point_data& pt2, + const point_data& prev_pt, + const point_data& current_pt, + long double distance, int multiplier) { + long double dx = current_pt.x() - prev_pt.x(); + long double dy = current_pt.y() - prev_pt.y(); + long double edge_length = std::sqrt(dx*dx + dy*dy); + long double dnx = dy; + long double dny = -dx; + dnx = dnx * (long double)distance / edge_length; + dny = dny * (long double)distance / edge_length; + pt1.x(prev_pt.x() + (long double)dnx * (long double)multiplier); + pt2.x(current_pt.x() + (long double)dnx * (long double)multiplier); + pt1.y(prev_pt.y() + (long double)dny * (long double)multiplier); + pt2.y(current_pt.y() + (long double)dny * (long double)multiplier); + } + + static inline void modify_pt(point_data& pt, const point_data& prev_pt, + const point_data& current_pt, const point_data& next_pt, + coordinate_type distance, coordinate_type multiplier) { + std::pair, point_data > he1, he2; + he1.first.x((long double)(prev_pt.x())); + he1.first.y((long double)(prev_pt.y())); + he1.second.x((long double)(current_pt.x())); + he1.second.y((long double)(current_pt.y())); + he2.first.x((long double)(current_pt.x())); + he2.first.y((long double)(current_pt.y())); + he2.second.x((long double)(next_pt.x())); + he2.second.y((long double)(next_pt.y())); + compute_offset_edge(he1.first, he1.second, prev_pt, current_pt, distance, multiplier); + compute_offset_edge(he2.first, he2.second, current_pt, next_pt, distance, multiplier); + typedef scanline_base::compute_intersection_pack pack; + point_data rpt; + point_data bisectorpt((he1.second.x()+he2.first.x())/2, + (he1.second.y()+he2.first.y())/2); + point_data orig_pt((long double)pt.x(), (long double)pt.y()); + if(euclidean_distance(bisectorpt, orig_pt) < distance/2) { + if(!pack::compute_lazy_intersection(rpt, he1, he2, true, false)) { + rpt = he1.second; //colinear offset edges use shared point + } + } else { + if(!pack::compute_lazy_intersection(rpt, he1, std::pair, point_data >(orig_pt, bisectorpt), true, false)) { + rpt = he1.second; //colinear offset edges use shared point + } + } + pt.x((coordinate_type)(std::floor(rpt.x()+0.5))); + pt.y((coordinate_type)(std::floor(rpt.y()+0.5))); + } + + static void resize_poly_up(std::vector >& poly, coordinate_type distance, coordinate_type multiplier) { + point_data first_pt = poly[0]; + point_data second_pt = poly[1]; + point_data prev_pt = poly[0]; + point_data current_pt = poly[1]; + for(std::size_t i = 2; i < poly.size()-1; ++i) { + point_data next_pt = poly[i]; + modify_pt(poly[i-1], prev_pt, current_pt, next_pt, distance, multiplier); + prev_pt = current_pt; + current_pt = next_pt; + } + point_data next_pt = first_pt; + modify_pt(poly[poly.size()-2], prev_pt, current_pt, next_pt, distance, multiplier); + prev_pt = current_pt; + current_pt = next_pt; + next_pt = second_pt; + modify_pt(poly[0], prev_pt, current_pt, next_pt, distance, multiplier); + poly.back() = poly.front(); + } + static bool resize_poly_down(std::vector >& poly, coordinate_type distance, coordinate_type multiplier) { + std::vector > orig_poly(poly); + rectangle_data extents_rectangle; + set_points(extents_rectangle, poly[0], poly[0]); + point_data first_pt = poly[0]; + point_data second_pt = poly[1]; + point_data prev_pt = poly[0]; + point_data current_pt = poly[1]; + encompass(extents_rectangle, current_pt); + for(std::size_t i = 2; i < poly.size()-1; ++i) { + point_data next_pt = poly[i]; + encompass(extents_rectangle, next_pt); + modify_pt(poly[i-1], prev_pt, current_pt, next_pt, distance, multiplier); + prev_pt = current_pt; + current_pt = next_pt; + } + if(delta(extents_rectangle, HORIZONTAL) <= std::abs(2*distance)) + return false; + if(delta(extents_rectangle, VERTICAL) <= std::abs(2*distance)) + return false; + point_data next_pt = first_pt; + modify_pt(poly[poly.size()-2], prev_pt, current_pt, next_pt, distance, multiplier); + prev_pt = current_pt; + current_pt = next_pt; + next_pt = second_pt; + modify_pt(poly[0], prev_pt, current_pt, next_pt, distance, multiplier); + poly.back() = poly.front(); + //if the line segments formed between orignial and new points cross for an edge that edge inverts + //if all edges invert the polygon should be discarded + //if even one edge does not invert return true because the polygon is valid + bool non_inverting_edge = false; + for(std::size_t i = 1; i < poly.size(); ++i) { + std::pair, point_data > + he1(poly[i], orig_poly[i]), + he2(poly[i-1], orig_poly[i-1]); + if(!scanline_base::intersects(he1, he2)) { + non_inverting_edge = true; + break; + } + } + return non_inverting_edge; + } + + polygon_set_data& + bloat(typename coordinate_traits::unsigned_area_type distance) { + std::list > polys; + get(polys); + clear(); + for(typename std::list >::iterator itr = polys.begin(); + itr != polys.end(); ++itr) { + resize_poly_up((*itr).self_.coords_, (coordinate_type)distance, (coordinate_type)1); + insert_vertex_sequence((*itr).self_.begin(), (*itr).self_.end(), COUNTERCLOCKWISE, false); //inserts without holes + for(typename std::list >::iterator itrh = (*itr).holes_.begin(); + itrh != (*itr).holes_.end(); ++itrh) { + if(resize_poly_down((*itrh).coords_, (coordinate_type)distance, (coordinate_type)1)) { + insert_vertex_sequence((*itrh).coords_.begin(), (*itrh).coords_.end(), CLOCKWISE, true); + } + } + } + return *this; + } + + polygon_set_data& + shrink(typename coordinate_traits::unsigned_area_type distance) { + std::list > polys; + get(polys); + clear(); + for(typename std::list >::iterator itr = polys.begin(); + itr != polys.end(); ++itr) { + if(resize_poly_down((*itr).self_.coords_, (coordinate_type)distance, (coordinate_type)-1)) { + insert_vertex_sequence((*itr).self_.begin(), (*itr).self_.end(), COUNTERCLOCKWISE, false); //inserts without holes + for(typename std::list >::iterator itrh = (*itr).holes_.begin(); + itrh != (*itr).holes_.end(); ++itrh) { + resize_poly_up((*itrh).coords_, (coordinate_type)distance, (coordinate_type)-1); + insert_vertex_sequence((*itrh).coords_.begin(), (*itrh).coords_.end(), CLOCKWISE, true); + } + } + } + return *this; + } + + // TODO:: should be private + template + inline polygon_set_data& + insert_with_resize(const geometry_type& poly, coordinate_type resizing, bool corner_fill_arc=false, unsigned int num_circle_segments=0, bool hole = false) { + return insert_with_resize_dispatch(poly, resizing, corner_fill_arc, num_circle_segments, hole, typename geometry_concept::type()); + } + + template + inline polygon_set_data& + insert_with_resize_dispatch(const geometry_type& poly, coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments, bool hole, + polygon_with_holes_concept) { + insert_with_resize_dispatch(poly, resizing, corner_fill_arc, num_circle_segments, hole, polygon_concept()); + for(typename polygon_with_holes_traits::iterator_holes_type itr = + begin_holes(poly); itr != end_holes(poly); + ++itr) { + insert_with_resize_dispatch(*itr, resizing, corner_fill_arc, num_circle_segments, !hole, polygon_concept()); + } + return *this; + } + + template + inline polygon_set_data& + insert_with_resize_dispatch(const geometry_type& poly, coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments, bool hole, + polygon_concept) { + + if (resizing==0) + return *this; + + + // one dimensional used to store CCW/CW flag + //direction_1d wdir = winding(poly); + // LOW==CLOCKWISE just faster to type + // so > 0 is CCW + //int multiplier = wdir == LOW ? -1 : 1; + //std::cout<<" multiplier : "<0?COUNTERCLOCKWISE:CLOCKWISE; + + typedef typename polygon_data::iterator_type piterator; + piterator first, second, third, end, real_end; + real_end = end_points(poly); + third = begin_points(poly); + first = third; + if(first == real_end) return *this; + ++third; + if(third == real_end) return *this; + second = end = third; + ++third; + if(third == real_end) return *this; + + // for 1st corner + std::vector > first_pts; + std::vector > all_pts; + direction_1d first_wdir = CLOCKWISE; + + // for all corners + polygon_set_data sizingSet; + bool sizing_sign = resizing<0; + bool prev_concave = true; + point_data prev_point; + //int iCtr=0; + + + //insert minkofski shapes on edges and corners + do { // REAL WORK IS HERE + + + //first, second and third point to points in correct CCW order + // check if convex or concave case + point_data normal1( second->y()-first->y(), first->x()-second->x()); + point_data normal2( third->y()-second->y(), second->x()-third->x()); + double direction = normal1.x()*normal2.y()- normal2.x()*normal1.y(); + bool convex = direction>0; + + bool treat_as_concave = !convex; + if(sizing_sign) + treat_as_concave = convex; + point_data v; + assign(v, normal1); + double s2 = (v.x()*v.x()+v.y()*v.y()); + double s = std::sqrt(s2)/resizing; + v = point_data(v.x()/s,v.y()/s); + point_data curr_prev; + if (prev_concave) + //TODO missing round_down() + curr_prev = point_data(first->x()+v.x(),first->y()+v.y()); + else + curr_prev = prev_point; + + // around concave corners - insert rectangle + // if previous corner is concave it's point info may be ignored + if ( treat_as_concave) { + std::vector > pts; + + pts.push_back(point_data(second->x()+v.x(),second->y()+v.y())); + pts.push_back(*second); + pts.push_back(*first); + pts.push_back(point_data(curr_prev)); + if (first_pts.size()){ + sizingSet.insert_vertex_sequence(pts.begin(),pts.end(), resize_wdir,false); + }else { + first_pts=pts; + first_wdir = resize_wdir; + } + } else { + + // add either intersection_quad or pie_shape, based on corner_fill_arc option + // for convex corner (convexity depends on sign of resizing, whether we shrink or grow) + std::vector< std::vector > > pts; + direction_1d winding; + winding = convex?COUNTERCLOCKWISE:CLOCKWISE; + if (make_resizing_vertex_list(pts, curr_prev, prev_concave, *first, *second, *third, resizing + , num_circle_segments, corner_fill_arc)) + { + if (first_pts.size()) { + for (int i=0; i tmp; + + //insert original shape + tmp.insert(poly, false, polygon_concept()); + if((resizing < 0) ^ hole) tmp -= sizingSet; + else tmp += sizingSet; + //tmp.clean(); + insert(tmp, hole); + return (*this); + } + + + inline polygon_set_data& + interact(const polygon_set_data& that); + + inline bool downcast(polygon_45_set_data& result) const { + if(!is_45_) return false; + for(iterator_type itr = begin(); itr != end(); ++itr) { + const element_type& elem = *itr; + int count = elem.second; + int rise = 1; //up sloping 45 + if(scanline_base::is_horizontal(elem.first)) rise = 0; + else if(scanline_base::is_vertical(elem.first)) rise = 2; + else { + if(!scanline_base::is_45_degree(elem.first)) { + is_45_ = false; + return false; //consider throwing because is_45_ has be be wrong + } + if(elem.first.first.y() > elem.first.second.y()) rise = -1; //down sloping 45 + } + typename polygon_45_set_data::Vertex45Compact vertex(elem.first.first, rise, count); + result.insert(vertex); + typename polygon_45_set_data::Vertex45Compact vertex2(elem.first.second, rise, -count); + result.insert(vertex2); + } + return true; + } + + inline GEOMETRY_CONCEPT_ID concept_downcast() const { + typedef typename coordinate_traits::coordinate_difference delta_type; + bool is_45 = false; + for(iterator_type itr = begin(); itr != end(); ++itr) { + const element_type& elem = *itr; + delta_type h_delta = euclidean_distance(elem.first.first, elem.first.second, HORIZONTAL); + delta_type v_delta = euclidean_distance(elem.first.first, elem.first.second, VERTICAL); + if(h_delta != 0 || v_delta != 0) { + //neither delta is zero and the edge is not MANHATTAN + if(v_delta != h_delta && v_delta != -h_delta) return POLYGON_SET_CONCEPT; + else is_45 = true; + } + } + if(is_45) return POLYGON_45_SET_CONCEPT; + return POLYGON_90_SET_CONCEPT; + } + + private: + mutable value_type data_; + mutable bool dirty_; + mutable bool unsorted_; + mutable bool is_45_; + + private: + //functions + + template + void get_dispatch(output_container& output, polygon_concept tag) const { + get_fracture(output, true, tag); + } + template + void get_dispatch(output_container& output, polygon_with_holes_concept tag) const { + get_fracture(output, false, tag); + } + template + void get_fracture(output_container& container, bool fracture_holes, concept_type ) const { + clean(); + polygon_arbitrary_formation pf(fracture_holes); + typedef typename polygon_arbitrary_formation::vertex_half_edge vertex_half_edge; + std::vector data; + for(iterator_type itr = data_.begin(); itr != data_.end(); ++itr){ + data.push_back(vertex_half_edge((*itr).first.first, (*itr).first.second, (*itr).second)); + data.push_back(vertex_half_edge((*itr).first.second, (*itr).first.first, -1 * (*itr).second)); + } + polygon_sort(data.begin(), data.end()); + pf.scan(container, data.begin(), data.end()); + } + }; + + struct polygon_set_concept; + template + struct geometry_concept > { + typedef polygon_set_concept type; + }; + +// template +// inline double compute_area(point_data& a, point_data& b, point_data& c) { + +// return (double)(b.x()-a.x())*(double)(c.y()-a.y())- (double)(c.x()-a.x())*(double)(b.y()-a.y()); + + +// } + + template + inline int make_resizing_vertex_list(std::vector > >& return_points, + point_data& curr_prev, bool ignore_prev_point, + point_data< T> start, point_data middle, point_data< T> end, + double sizing_distance, unsigned int num_circle_segments, bool corner_fill_arc) { + + // handle the case of adding an intersection point + point_data dn1( middle.y()-start.y(), start.x()-middle.x()); + double size = sizing_distance/std::sqrt( dn1.x()*dn1.x()+dn1.y()*dn1.y()); + dn1 = point_data( dn1.x()*size, dn1.y()* size); + point_data dn2( end.y()-middle.y(), middle.x()-end.x()); + size = sizing_distance/std::sqrt( dn2.x()*dn2.x()+dn2.y()*dn2.y()); + dn2 = point_data( dn2.x()*size, dn2.y()* size); + point_data start_offset((start.x()+dn1.x()),(start.y()+dn1.y())); + point_data mid1_offset((middle.x()+dn1.x()),(middle.y()+dn1.y())); + point_data end_offset((end.x()+dn2.x()),(end.y()+dn2.y())); + point_data mid2_offset((middle.x()+dn2.x()),(middle.y()+dn2.y())); + if (ignore_prev_point) + curr_prev = round_down(start_offset); + + + if (corner_fill_arc) { + std::vector > return_points1; + return_points.push_back(return_points1); + std::vector >& return_points_back = return_points[return_points.size()-1]; + return_points_back.push_back(round_down(mid1_offset)); + return_points_back.push_back(middle); + return_points_back.push_back(start); + return_points_back.push_back(curr_prev); + point_data dmid(middle.x(),middle.y()); + return_points.push_back(return_points1); + int num = make_arc(return_points[return_points.size()-1],mid1_offset,mid2_offset,dmid,sizing_distance,num_circle_segments); + curr_prev = round_down(mid2_offset); + return num; + + } + + std::pair,point_data > he1(start_offset,mid1_offset); + std::pair,point_data > he2(mid2_offset ,end_offset); + //typedef typename high_precision_type::type high_precision; + + point_data intersect; + typename scanline_base::compute_intersection_pack pack; + bool res = pack.compute_intersection(intersect,he1,he2,true); + if( res ) { + std::vector > return_points1; + return_points.push_back(return_points1); + std::vector >& return_points_back = return_points[return_points.size()-1]; + return_points_back.push_back(intersect); + return_points_back.push_back(middle); + return_points_back.push_back(start); + return_points_back.push_back(curr_prev); + + //double d1= compute_area(intersect,middle,start); + //double d2= compute_area(start,curr_prev,intersect); + + curr_prev = intersect; + + + return return_points.size(); + } + return 0; + + } + + // this routine should take in start and end point s.t. end point is CCW from start + // it sould make a pie slice polygon that is an intersection of that arc + // with an ngon segments approximation of the circle centered at center with radius r + // point start is gauaranteed to be on the segmentation + // returnPoints will start with the first point after start + // returnPoints vector may be empty + template + inline int make_arc(std::vector >& return_points, + point_data< double> start, point_data< double> end, + point_data< double> center, double r, unsigned int num_circle_segments) { + const double our_pi=3.1415926535897932384626433832795028841971; + + // derive start and end angles + double ps = atan2(start.y()-center.y(), start.x()-center.x()); + double pe = atan2(end.y()-center.y(), end.x()-center.x()); + if (ps < 0.0) + ps += 2.0 * our_pi; + if (pe <= 0.0) + pe += 2.0 * our_pi; + if (ps >= 2.0 * our_pi) + ps -= 2.0 * our_pi; + while (pe <= ps) + pe += 2.0 * our_pi; + double delta_angle = (2.0 * our_pi) / (double)num_circle_segments; + if ( start==end) // full circle? + { + ps = delta_angle*0.5; + pe = ps + our_pi * 2.0; + double x,y; + x = center.x() + r * cos(ps); + y = center.y() + r * sin(ps); + start = point_data(x,y); + end = start; + } + return_points.push_back(round_down(center)); + return_points.push_back(round_down(start)); + unsigned int i=0; + double curr_angle = ps+delta_angle; + while( curr_angle < pe - 0.01 && i < 2 * num_circle_segments) { + i++; + double x = center.x() + r * cos( curr_angle); + double y = center.y() + r * sin( curr_angle); + return_points.push_back( round_down((point_data(x,y)))); + curr_angle+=delta_angle; + } + return_points.push_back(round_down(end)); + return return_points.size(); + } + +}// close namespace +}// close name space + +#include "detail/scan_arbitrary.hpp" + +namespace boost { namespace polygon { + //ConnectivityExtraction computes the graph of connectivity between rectangle, polygon and + //polygon set graph nodes where an edge is created whenever the geometry in two nodes overlap + template + class connectivity_extraction{ + private: + typedef arbitrary_connectivity_extraction ce; + ce ce_; + unsigned int nodeCount_; + public: + inline connectivity_extraction() : ce_(), nodeCount_(0) {} + inline connectivity_extraction(const connectivity_extraction& that) : ce_(that.ce_), + nodeCount_(that.nodeCount_) {} + inline connectivity_extraction& operator=(const connectivity_extraction& that) { + ce_ = that.ce_; + nodeCount_ = that.nodeCount_; {} + return *this; + } + + //insert a polygon set graph node, the value returned is the id of the graph node + inline unsigned int insert(const polygon_set_data& ps) { + ps.clean(); + ce_.populateTouchSetData(ps.begin(), ps.end(), nodeCount_); + return nodeCount_++; + } + template + inline unsigned int insert(const GeoObjT& geoObj) { + polygon_set_data ps; + ps.insert(geoObj); + return insert(ps); + } + + //extract connectivity and store the edges in the graph + //graph must be indexable by graph node id and the indexed value must be a std::set of + //graph node id + template + inline void extract(GraphT& graph) { + ce_.execute(graph); + } + }; + + template + polygon_set_data& + polygon_set_data::interact(const polygon_set_data& that) { + connectivity_extraction ce; + std::vector > polys; + get(polys); + clear(); + for(std::size_t i = 0; i < polys.size(); ++i) { + ce.insert(polys[i]); + } + int id = ce.insert(that); + std::vector > graph(id+1); + ce.extract(graph); + for(std::set::iterator itr = graph[id].begin(); + itr != graph[id].end(); ++itr) { + insert(polys[*itr]); + } + return *this; + } +} +} + +#include "polygon_set_traits.hpp" +#include "detail/polygon_set_view.hpp" + +#include "polygon_set_concept.hpp" +#include "detail/minkowski.hpp" +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_traits.hpp new file mode 100644 index 0000000..b68bbc1 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_set_traits.hpp @@ -0,0 +1,133 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_SET_TRAITS_HPP +#define BOOST_POLYGON_POLYGON_SET_TRAITS_HPP +namespace boost { namespace polygon{ + + struct polygon_set_concept {}; + + //default definition of polygon set traits works for any model of polygon , polygon with holes or any vector or list thereof + template + struct polygon_set_traits { + typedef typename get_coordinate_type::type >::type coordinate_type; + typedef typename get_iterator_type::type iterator_type; + typedef T operator_arg_type; + + static inline iterator_type begin(const T& polygon_set) { + return get_iterator_type::begin(polygon_set); + } + + static inline iterator_type end(const T& polygon_set) { + return get_iterator_type::end(polygon_set); + } + + static inline bool clean(const T& ) { return false; } + + static inline bool sorted(const T& ) { return false; } + }; + + template + struct is_polygonal_concept { typedef gtl_no type; }; + template <> + struct is_polygonal_concept { typedef gtl_yes type; }; + template <> + struct is_polygonal_concept { typedef gtl_yes type; }; + template <> + struct is_polygonal_concept { typedef gtl_yes type; }; + + template + struct is_polygon_set_type { + typedef typename is_polygonal_concept::type>::type type; + }; + template + struct is_polygon_set_type > { + typedef typename gtl_or< + typename is_polygonal_concept >::type>::type, + typename is_polygonal_concept::value_type>::type>::type>::type type; + }; + template + struct is_polygon_set_type > { + typedef typename gtl_or< + typename is_polygonal_concept >::type>::type, + typename is_polygonal_concept::value_type>::type>::type>::type type; + }; + + template + struct is_mutable_polygon_set_type { + typedef typename gtl_same_type::type>::type type; + }; + template + struct is_mutable_polygon_set_type > { + typedef typename gtl_or< + typename gtl_same_type >::type>::type, + typename is_polygonal_concept::value_type>::type>::type>::type type; + }; + template + struct is_mutable_polygon_set_type > { + typedef typename gtl_or< + typename gtl_same_type >::type>::type, + typename is_polygonal_concept::value_type>::type>::type>::type type; + }; + + template + struct polygon_set_mutable_traits {}; + template + struct polygon_set_mutable_traits > { + template + static inline void set(std::list& polygon_set, input_iterator_type input_begin, input_iterator_type input_end) { + polygon_set.clear(); + polygon_set_data >::coordinate_type> ps; + ps.reserve(std::distance(input_begin, input_end)); + ps.insert(input_begin, input_end); + ps.get(polygon_set); + } + }; + template + struct polygon_set_mutable_traits > { + template + static inline void set(std::vector& polygon_set, input_iterator_type input_begin, input_iterator_type input_end) { + polygon_set.clear(); + size_t num_ele = std::distance(input_begin, input_end); + polygon_set.reserve(num_ele); + polygon_set_data >::coordinate_type> ps; + ps.reserve(num_ele); + ps.insert(input_begin, input_end); + ps.get(polygon_set); + } + }; + + template + struct polygon_set_mutable_traits > { + template + static inline void set(polygon_set_data& polygon_set, + input_iterator_type input_begin, input_iterator_type input_end) { + polygon_set.set(input_begin, input_end); + } + }; + template + struct polygon_set_traits > { + typedef typename polygon_set_data::coordinate_type coordinate_type; + typedef typename polygon_set_data::iterator_type iterator_type; + typedef typename polygon_set_data::operator_arg_type operator_arg_type; + + static inline iterator_type begin(const polygon_set_data& polygon_set) { + return polygon_set.begin(); + } + + static inline iterator_type end(const polygon_set_data& polygon_set) { + return polygon_set.end(); + } + + static inline bool clean(const polygon_set_data& polygon_set) { polygon_set.clean(); return true; } + + static inline bool sorted(const polygon_set_data& polygon_set) { polygon_set.sort(); return true; } + + }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_traits.hpp new file mode 100644 index 0000000..e3d1859 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_traits.hpp @@ -0,0 +1,1861 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_TRAITS_HPP +#define BOOST_POLYGON_POLYGON_TRAITS_HPP +namespace boost { namespace polygon{ + + template + struct polygon_90_traits { + typedef typename T::coordinate_type coordinate_type; + typedef typename T::compact_iterator_type compact_iterator_type; + + // Get the begin iterator + static inline compact_iterator_type begin_compact(const T& t) { + return t.begin_compact(); + } + + // Get the end iterator + static inline compact_iterator_type end_compact(const T& t) { + return t.end_compact(); + } + + // Get the number of sides of the polygon + static inline std::size_t size(const T& t) { + return t.size(); + } + + // Get the winding direction of the polygon + static inline winding_direction winding(const T&) { + return unknown_winding; + } + }; + + template + struct polygon_traits_general { + typedef typename T::coordinate_type coordinate_type; + typedef typename T::iterator_type iterator_type; + typedef typename T::point_type point_type; + + // Get the begin iterator + static inline iterator_type begin_points(const T& t) { + return t.begin(); + } + + // Get the end iterator + static inline iterator_type end_points(const T& t) { + return t.end(); + } + + // Get the number of sides of the polygon + static inline std::size_t size(const T& t) { + return t.size(); + } + + // Get the winding direction of the polygon + static inline winding_direction winding(const T&) { + return unknown_winding; + } + }; + + template + struct polygon_traits_90 { + typedef typename polygon_90_traits::coordinate_type coordinate_type; + typedef iterator_compact_to_points::compact_iterator_type, point_data > iterator_type; + typedef point_data point_type; + + // Get the begin iterator + static inline iterator_type begin_points(const T& t) { + return iterator_type(polygon_90_traits::begin_compact(t), + polygon_90_traits::end_compact(t)); + } + + // Get the end iterator + static inline iterator_type end_points(const T& t) { + return iterator_type(polygon_90_traits::end_compact(t), + polygon_90_traits::end_compact(t)); + } + + // Get the number of sides of the polygon + static inline std::size_t size(const T& t) { + return polygon_90_traits::size(t); + } + + // Get the winding direction of the polygon + static inline winding_direction winding(const T& t) { + return polygon_90_traits::winding(t); + } + }; + +#ifndef BOOST_VERY_LITTLE_SFINAE + + template + struct polygon_traits {}; + + template + struct polygon_traits::type, polygon_concept>::type, + typename gtl_same_type::type, polygon_45_concept>::type, + typename gtl_same_type::type, polygon_with_holes_concept>::type, + typename gtl_same_type::type, polygon_45_with_holes_concept>::type + >::type> : public polygon_traits_general {}; + + template + struct polygon_traits< T, + typename gtl_or< + typename gtl_same_type::type, polygon_90_concept>::type, + typename gtl_same_type::type, polygon_90_with_holes_concept>::type + >::type > : public polygon_traits_90 {}; + +#else + + template + struct gtl_ifelse {}; + template + struct gtl_ifelse { + typedef T_ELSE type; + }; + template + struct gtl_ifelse { + typedef T_IF type; + }; + + template + struct polygon_traits {}; + + template + struct polygon_traits::type, polygon_concept>::type, + typename gtl_same_type::type, polygon_45_concept>::type, + typename gtl_same_type::type, polygon_with_holes_concept>::type, + typename gtl_same_type::type, polygon_45_with_holes_concept>::type + >::type, typename gtl_or< + typename gtl_same_type::type, polygon_90_concept>::type, + typename gtl_same_type::type, polygon_90_with_holes_concept>::type + >::type>::type > : public gtl_ifelse::type, polygon_90_concept>::type, + typename gtl_same_type::type, polygon_90_with_holes_concept>::type >::type, + polygon_traits_90, + polygon_traits_general >::type { + }; + +#endif + + template + struct polygon_with_holes_traits { + typedef typename T::iterator_holes_type iterator_holes_type; + typedef typename T::hole_type hole_type; + + // Get the begin iterator + static inline iterator_holes_type begin_holes(const T& t) { + return t.begin_holes(); + } + + // Get the end iterator + static inline iterator_holes_type end_holes(const T& t) { + return t.end_holes(); + } + + // Get the number of holes + static inline std::size_t size_holes(const T& t) { + return t.size_holes(); + } + }; + + template + struct polygon_90_mutable_traits { + + // Set the data of a polygon with the unique coordinates in an iterator, starting with an x + template + static inline T& set_compact(T& t, iT input_begin, iT input_end) { + t.set_compact(input_begin, input_end); + return t; + } + + }; + + template + struct polygon_mutable_traits { + + // Set the data of a polygon with the unique coordinates in an iterator, starting with an x + template + static inline T& set_points(T& t, iT input_begin, iT input_end) { + t.set(input_begin, input_end); + return t; + } + + }; + + template + struct polygon_with_holes_mutable_traits { + + // Set the data of a polygon with the unique coordinates in an iterator, starting with an x + template + static inline T& set_holes(T& t, iT inputBegin, iT inputEnd) { + t.set_holes(inputBegin, inputEnd); + return t; + } + + }; +} +} +#include "isotropy.hpp" + +//point +#include "point_data.hpp" +#include "point_traits.hpp" +#include "point_concept.hpp" + +//interval +#include "interval_data.hpp" +#include "interval_traits.hpp" +#include "interval_concept.hpp" + +//rectangle +#include "rectangle_data.hpp" +#include "rectangle_traits.hpp" +#include "rectangle_concept.hpp" + +//algorithms needed by polygon types +#include "detail/iterator_points_to_compact.hpp" +#include "detail/iterator_compact_to_points.hpp" + +//polygons +#include "polygon_45_data.hpp" +#include "polygon_data.hpp" +#include "polygon_90_data.hpp" +#include "polygon_90_with_holes_data.hpp" +#include "polygon_45_with_holes_data.hpp" +#include "polygon_with_holes_data.hpp" + +namespace boost { namespace polygon{ + struct polygon_concept {}; + struct polygon_with_holes_concept {}; + struct polygon_45_concept {}; + struct polygon_45_with_holes_concept {}; + struct polygon_90_concept {}; + struct polygon_90_with_holes_concept {}; + + + template + struct is_polygon_90_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_same_type::type type; + }; + + template + struct is_polygon_45_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_or::type, + typename gtl_same_type::type>::type type; + }; + + template + struct is_polygon_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_or::type, + typename gtl_same_type::type>::type type; + }; + + template + struct is_polygon_90_with_holes_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_or::type, + typename gtl_same_type::type>::type type; + }; + + template + struct is_polygon_45_with_holes_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_or_3::type, + typename is_polygon_45_type::type, + typename gtl_same_type::type>::type type; + }; + + template + struct is_polygon_with_holes_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_or_3::type, + typename is_polygon_type::type, + typename gtl_same_type::type>::type type; + }; + + template + struct is_mutable_polygon_90_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_same_type::type type; + }; + + template + struct is_mutable_polygon_45_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_same_type::type type; + }; + + template + struct is_mutable_polygon_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_same_type::type type; + }; + + template + struct is_mutable_polygon_90_with_holes_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_same_type::type type; + }; + + template + struct is_mutable_polygon_45_with_holes_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_same_type::type type; + }; + + template + struct is_mutable_polygon_with_holes_type { + typedef typename geometry_concept::type GC; + typedef typename gtl_same_type::type type; + }; + + template + struct is_any_mutable_polygon_with_holes_type { + typedef typename gtl_or_3::type, + typename is_mutable_polygon_45_with_holes_type::type, + typename is_mutable_polygon_with_holes_type::type>::type type; + }; + template + struct is_any_mutable_polygon_without_holes_type { + typedef typename gtl_or_3< + typename is_mutable_polygon_90_type::type, + typename is_mutable_polygon_45_type::type, + typename is_mutable_polygon_type::type>::type type; }; + + template + struct is_any_mutable_polygon_type { + typedef typename gtl_or::type, + typename is_any_mutable_polygon_without_holes_type::type>::type type; + }; + + template + struct polygon_from_polygon_with_holes_type {}; + template <> + struct polygon_from_polygon_with_holes_type { typedef polygon_concept type; }; + template <> + struct polygon_from_polygon_with_holes_type { typedef polygon_45_concept type; }; + template <> + struct polygon_from_polygon_with_holes_type { typedef polygon_90_concept type; }; + + template <> + struct geometry_domain { typedef forty_five_domain type; }; + template <> + struct geometry_domain { typedef forty_five_domain type; }; + template <> + struct geometry_domain { typedef manhattan_domain type; }; + template <> + struct geometry_domain { typedef manhattan_domain type; }; + + template + struct distance_type_by_domain { typedef typename coordinate_traits::coordinate_distance type; }; + template + struct distance_type_by_domain { + typedef typename coordinate_traits::coordinate_difference type; }; + + // \brief Sets the boundary of the polygon to the points in the iterator range + // \tparam T A type that models polygon_concept + // \tparam iT Iterator type over objects that model point_concept + // \param t The polygon to set + // \param begin_points The start of the range of points + // \param end_points The end of the range of points + + /// \relatesalso polygon_concept + template + typename enable_if ::type, T>::type & + set_points(T& t, iT begin_points, iT end_points) { + polygon_mutable_traits::set_points(t, begin_points, end_points); + return t; + } + + // \brief Sets the boundary of the polygon to the non-redundant coordinates in the iterator range + // \tparam T A type that models polygon_90_concept + // \tparam iT Iterator type over objects that model coordinate_concept + // \param t The polygon to set + // \param begin_compact_coordinates The start of the range of coordinates + // \param end_compact_coordinates The end of the range of coordinates + +/// \relatesalso polygon_90_concept + template + typename enable_if ::type, + typename is_mutable_polygon_90_with_holes_type::type>::type, T>::type & + set_compact(T& t, iT begin_compact_coordinates, iT end_compact_coordinates) { + polygon_90_mutable_traits::set_compact(t, begin_compact_coordinates, end_compact_coordinates); + return t; + } + +/// \relatesalso polygon_with_holes_concept + template + typename enable_if< typename gtl_and < + typename is_any_mutable_polygon_with_holes_type::type, + typename gtl_different_type::type>::type, + manhattan_domain>::type>::type, + T>::type & + set_compact(T& t, iT begin_compact_coordinates, iT end_compact_coordinates) { + iterator_compact_to_points::coordinate_type> > + itrb(begin_compact_coordinates, end_compact_coordinates), + itre(end_compact_coordinates, end_compact_coordinates); + return set_points(t, itrb, itre); + } + +/// \relatesalso polygon_with_holes_concept + template + typename enable_if ::type, T>::type & + set_holes(T& t, iT begin_holes, iT end_holes) { + polygon_with_holes_mutable_traits::set_holes(t, begin_holes, end_holes); + return t; + } + +/// \relatesalso polygon_90_concept + template + typename polygon_90_traits::compact_iterator_type + begin_compact(const T& polygon, + typename enable_if< + typename gtl_and ::type, + typename gtl_same_type::type>::type, + manhattan_domain>::type>::type>::type * = 0 + ) { + return polygon_90_traits::begin_compact(polygon); + } + +/// \relatesalso polygon_90_concept + template + typename polygon_90_traits::compact_iterator_type + end_compact(const T& polygon, + typename enable_if< + typename gtl_and ::type, + typename gtl_same_type::type>::type, + manhattan_domain>::type>::type>::type * = 0 + ) { + return polygon_90_traits::end_compact(polygon); + } + + /// \relatesalso polygon_concept + template + typename enable_if < typename gtl_if< + typename is_polygon_with_holes_type::type>::type, + typename polygon_traits::iterator_type>::type + begin_points(const T& polygon) { + return polygon_traits::begin_points(polygon); + } + + /// \relatesalso polygon_concept + template + typename enable_if < typename gtl_if< + typename is_polygon_with_holes_type::type>::type, + typename polygon_traits::iterator_type>::type + end_points(const T& polygon) { + return polygon_traits::end_points(polygon); + } + + /// \relatesalso polygon_concept + template + typename enable_if ::type, + std::size_t>::type + size(const T& polygon) { + return polygon_traits::size(polygon); + } + +/// \relatesalso polygon_with_holes_concept + template + typename enable_if < typename gtl_if< + typename is_polygon_with_holes_type::type>::type, + typename polygon_with_holes_traits::iterator_holes_type>::type + begin_holes(const T& polygon) { + return polygon_with_holes_traits::begin_holes(polygon); + } + +/// \relatesalso polygon_with_holes_concept + template + typename enable_if < typename gtl_if< + typename is_polygon_with_holes_type::type>::type, + typename polygon_with_holes_traits::iterator_holes_type>::type + end_holes(const T& polygon) { + return polygon_with_holes_traits::end_holes(polygon); + } + +/// \relatesalso polygon_with_holes_concept + template + typename enable_if ::type, + std::size_t>::type + size_holes(const T& polygon) { + return polygon_with_holes_traits::size_holes(polygon); + } + + // \relatesalso polygon_concept + template + typename enable_if< + typename gtl_and< typename is_mutable_polygon_type::type, + typename is_polygon_type::type>::type, T1>::type & + assign(T1& lvalue, const T2& rvalue) { + polygon_mutable_traits::set_points(lvalue, polygon_traits::begin_points(rvalue), + polygon_traits::end_points(rvalue)); + return lvalue; + } + +// \relatesalso polygon_with_holes_concept + template + typename enable_if< + typename gtl_and< typename is_mutable_polygon_with_holes_type::type, + typename is_polygon_with_holes_type::type>::type, T1>::type & + assign(T1& lvalue, const T2& rvalue) { + polygon_mutable_traits::set_points(lvalue, polygon_traits::begin_points(rvalue), + polygon_traits::end_points(rvalue)); + polygon_with_holes_mutable_traits::set_holes(lvalue, polygon_with_holes_traits::begin_holes(rvalue), + polygon_with_holes_traits::end_holes(rvalue)); + return lvalue; + } + + // \relatesalso polygon_45_concept + template + typename enable_if< typename gtl_and< typename is_mutable_polygon_45_type::type, typename is_polygon_45_type::type>::type, T1>::type & + assign(T1& lvalue, const T2& rvalue) { + polygon_mutable_traits::set_points(lvalue, polygon_traits::begin_points(rvalue), + polygon_traits::end_points(rvalue)); + return lvalue; + } + +// \relatesalso polygon_45_with_holes_concept + template + typename enable_if< + typename gtl_and< typename is_mutable_polygon_45_with_holes_type::type, + typename is_polygon_45_with_holes_type::type>::type, T1>::type & + assign(T1& lvalue, const T2& rvalue) { + polygon_mutable_traits::set_points(lvalue, polygon_traits::begin_points(rvalue), + polygon_traits::end_points(rvalue)); + polygon_with_holes_mutable_traits::set_holes(lvalue, polygon_with_holes_traits::begin_holes(rvalue), + polygon_with_holes_traits::end_holes(rvalue)); + return lvalue; + } + + // \relatesalso polygon_90_concept + template + typename enable_if< + typename gtl_and< typename is_mutable_polygon_90_type::type, + typename is_polygon_90_type::type>::type, T1>::type & + assign(T1& lvalue, const T2& rvalue) { + polygon_90_mutable_traits::set_compact(lvalue, polygon_90_traits::begin_compact(rvalue), + polygon_90_traits::end_compact(rvalue)); + return lvalue; + } + +// \relatesalso polygon_90_with_holes_concept + template + typename enable_if< + typename gtl_and< typename is_mutable_polygon_90_with_holes_type::type, + typename is_polygon_90_with_holes_type::type>::type, T1>::type & + assign(T1& lvalue, const T2& rvalue) { + polygon_90_mutable_traits::set_compact(lvalue, polygon_90_traits::begin_compact(rvalue), + polygon_90_traits::end_compact(rvalue)); + polygon_with_holes_mutable_traits::set_holes(lvalue, polygon_with_holes_traits::begin_holes(rvalue), + polygon_with_holes_traits::end_holes(rvalue)); + return lvalue; + } + + // \relatesalso polygon_90_concept + template + typename enable_if< + typename gtl_and< typename is_any_mutable_polygon_type::type, + typename is_rectangle_concept::type>::type>::type, T1>::type & + assign(T1& polygon, const T2& rect) { + typedef point_data::coordinate_type> PT; + PT points[4] = {PT(xl(rect), yl(rect)), PT(xh(rect), yl(rect)), PT(xh(rect), yh(rect)), PT(xl(rect), yh(rect))}; + set_points(polygon, points, points+4); + return polygon; + } + +/// \relatesalso polygon_90_concept + template + typename enable_if< typename gtl_and< typename is_mutable_polygon_90_type::type, + typename is_point_concept::type>::type>::type, + polygon_type>::type & + convolve(polygon_type& polygon, const point_type& point) { + std::vector::coordinate_type> coords; + coords.reserve(::boost::polygon::size(polygon)); + bool pingpong = true; + for(typename polygon_90_traits::compact_iterator_type iter = begin_compact(polygon); + iter != end_compact(polygon); ++iter) { + coords.push_back((*iter) + (pingpong ? x(point) : y(point))); + pingpong = !pingpong; + } + polygon_90_mutable_traits::set_compact(polygon, coords.begin(), coords.end()); + return polygon; + } + +/// \relatesalso polygon_concept + template + typename enable_if< typename gtl_and< typename gtl_or< + typename is_mutable_polygon_45_type::type, + typename is_mutable_polygon_type::type>::type, + typename is_point_concept::type>::type>::type, + polygon_type>::type & + convolve(polygon_type& polygon, const point_type& point) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + convolve(points.back(), point); + } + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + +/// \relatesalso polygon_with_holes_concept + template + typename enable_if< + typename gtl_and< typename is_any_mutable_polygon_with_holes_type::type, + typename is_point_concept::type>::type>::type, + polygon_type>::type & + convolve(polygon_type& polygon, const point_type& point) { + typedef typename polygon_with_holes_traits::hole_type hole_type; + hole_type h; + set_points(h, begin_points(polygon), end_points(polygon)); + convolve(h, point); + std::vector holes; + holes.reserve(size_holes(polygon)); + for(typename polygon_with_holes_traits::iterator_holes_type itr = begin_holes(polygon); + itr != end_holes(polygon); ++itr) { + holes.push_back(*itr); + convolve(holes.back(), point); + } + assign(polygon, h); + set_holes(polygon, holes.begin(), holes.end()); + return polygon; + } + +/// \relatesalso polygon_concept + template + typename enable_if< typename is_any_mutable_polygon_type::type, T>::type & + move(T& polygon, orientation_2d orient, typename polygon_traits::coordinate_type displacement) { + typedef typename polygon_traits::coordinate_type Unit; + if(orient == HORIZONTAL) return convolve(polygon, point_data(displacement, Unit(0))); + return convolve(polygon, point_data(Unit(0), displacement)); + } + +/// \relatesalso polygon_concept +/// \brief Applies a transformation to the polygon. +/// \tparam polygon_type A type that models polygon_concept +/// \tparam transform_type A type that may be either axis_transformation or transformation or that overloads point_concept::transform +/// \param polygon The polygon to transform +/// \param tr The transformation to apply + template + typename enable_if< typename is_any_mutable_polygon_without_holes_type::type, polygon_type>::type & + transform(polygon_type& polygon, const transform_type& tr) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + transform(points.back(), tr); + } + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + +/// \relatesalso polygon_with_holes_concept + template + typename enable_if< typename is_any_mutable_polygon_with_holes_type::type, T>::type & + transform(T& polygon, const transform_type& tr) { + typedef typename polygon_with_holes_traits::hole_type hole_type; + hole_type h; + set_points(h, begin_points(polygon), end_points(polygon)); + transform(h, tr); + std::vector holes; + holes.reserve(size_holes(polygon)); + for(typename polygon_with_holes_traits::iterator_holes_type itr = begin_holes(polygon); + itr != end_holes(polygon); ++itr) { + holes.push_back(*itr); + transform(holes.back(), tr); + } + assign(polygon, h); + set_holes(polygon, holes.begin(), holes.end()); + return polygon; + } + + template + typename enable_if< typename is_any_mutable_polygon_without_holes_type::type, polygon_type>::type & + scale_up(polygon_type& polygon, typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + scale_up(points.back(), factor); + } + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + + template + typename enable_if< typename is_any_mutable_polygon_with_holes_type::type, T>::type & + scale_up(T& polygon, typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + typedef typename polygon_with_holes_traits::hole_type hole_type; + hole_type h; + set_points(h, begin_points(polygon), end_points(polygon)); + scale_up(h, factor); + std::vector holes; + holes.reserve(size_holes(polygon)); + for(typename polygon_with_holes_traits::iterator_holes_type itr = begin_holes(polygon); + itr != end_holes(polygon); ++itr) { + holes.push_back(*itr); + scale_up(holes.back(), factor); + } + assign(polygon, h); + set_holes(polygon, holes.begin(), holes.end()); + return polygon; + } + + //scale non-45 down + template + typename enable_if< + typename gtl_and< typename is_any_mutable_polygon_without_holes_type::type, + typename gtl_not::type>::type>::type>::type>::type, + polygon_type>::type & + scale_down(polygon_type& polygon, typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + scale_down(points.back(), factor); + } + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + + template + Unit local_abs(Unit value) { return value < 0 ? (Unit)-value : value; } + + template + void snap_point_vector_to_45(std::vector >& pts) { + typedef point_data Point; + if(pts.size() < 3) { pts.clear(); return; } + typename std::vector >::iterator endLocation = std::unique(pts.begin(), pts.end()); + if(endLocation != pts.end()){ + pts.resize(endLocation - pts.begin()); + } + if(pts.back() == pts[0]) pts.pop_back(); + //iterate over point triplets + int numPts = pts.size(); + bool wrap_around = false; + for(int i = 0; i < numPts; ++i) { + Point& pt1 = pts[i]; + Point& pt2 = pts[(i + 1) % numPts]; + Point& pt3 = pts[(i + 2) % numPts]; + //check if non-45 edge + Unit deltax = x(pt2) - x(pt1); + Unit deltay = y(pt2) - y(pt1); + if(deltax && deltay && + local_abs(deltax) != local_abs(deltay)) { + //adjust the middle point + Unit ndx = x(pt3) - x(pt2); + Unit ndy = y(pt3) - y(pt2); + if(ndx && ndy) { + Unit diff = local_abs(local_abs(deltax) - local_abs(deltay)); + Unit halfdiff = diff/2; + if((deltax > 0 && deltay > 0) || + (deltax < 0 && deltay < 0)) { + //previous edge is rising slope + if(local_abs(deltax + halfdiff + (diff % 2)) == + local_abs(deltay - halfdiff)) { + x(pt2, x(pt2) + halfdiff + (diff % 2)); + y(pt2, y(pt2) - halfdiff); + } else if(local_abs(deltax - halfdiff - (diff % 2)) == + local_abs(deltay + halfdiff)) { + x(pt2, x(pt2) - halfdiff - (diff % 2)); + y(pt2, y(pt2) + halfdiff); + } else{ + //std::cout << "fail1\n"; + } + } else { + //previous edge is falling slope + if(local_abs(deltax + halfdiff + (diff % 2)) == + local_abs(deltay + halfdiff)) { + x(pt2, x(pt2) + halfdiff + (diff % 2)); + y(pt2, y(pt2) + halfdiff); + } else if(local_abs(deltax - halfdiff - (diff % 2)) == + local_abs(deltay - halfdiff)) { + x(pt2, x(pt2) - halfdiff - (diff % 2)); + y(pt2, y(pt2) - halfdiff); + } else { + //std::cout << "fail2\n"; + } + } + if(i == numPts - 1 && (diff % 2)) { + //we have a wrap around effect + if(!wrap_around) { + wrap_around = true; + i = -1; + } + } + } else if(ndx) { + //next edge is horizontal + //find the x value for pt1 that would make the abs(deltax) == abs(deltay) + Unit newDeltaX = local_abs(deltay); + if(deltax < 0) newDeltaX *= -1; + x(pt2, x(pt1) + newDeltaX); + } else { //ndy + //next edge is vertical + //find the y value for pt1 that would make the abs(deltax) == abs(deltay) + Unit newDeltaY = local_abs(deltax); + if(deltay < 0) newDeltaY *= -1; + y(pt2, y(pt1) + newDeltaY); + } + } + } + } + + template + typename enable_if< typename is_any_mutable_polygon_without_holes_type::type, polygon_type>::type & + snap_to_45(polygon_type& polygon) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + } + snap_point_vector_to_45(points); + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + + template + typename enable_if< typename is_any_mutable_polygon_with_holes_type::type, polygon_type>::type & + snap_to_45(polygon_type& polygon) { + typedef typename polygon_with_holes_traits::hole_type hole_type; + hole_type h; + set_points(h, begin_points(polygon), end_points(polygon)); + snap_to_45(h); + std::vector holes; + holes.reserve(size_holes(polygon)); + for(typename polygon_with_holes_traits::iterator_holes_type itr = begin_holes(polygon); + itr != end_holes(polygon); ++itr) { + holes.push_back(*itr); + snap_to_45(holes.back()); + } + assign(polygon, h); + set_holes(polygon, holes.begin(), holes.end()); + return polygon; + } + + //scale specifically 45 down + template + typename enable_if< + typename gtl_and< typename is_any_mutable_polygon_without_holes_type::type, + typename gtl_same_type + < forty_five_domain, + typename geometry_domain::type>::type>::type>::type, + polygon_type>::type & + scale_down(polygon_type& polygon, typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + scale_down(points.back(), factor); + } + snap_point_vector_to_45(points); + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + + template + typename enable_if< typename is_any_mutable_polygon_with_holes_type::type, T>::type & + scale_down(T& polygon, typename coordinate_traits::coordinate_type>::unsigned_area_type factor) { + typedef typename polygon_with_holes_traits::hole_type hole_type; + hole_type h; + set_points(h, begin_points(polygon), end_points(polygon)); + scale_down(h, factor); + std::vector holes; + holes.reserve(size_holes(polygon)); + for(typename polygon_with_holes_traits::iterator_holes_type itr = begin_holes(polygon); + itr != end_holes(polygon); ++itr) { + holes.push_back(*itr); + scale_down(holes.back(), factor); + } + assign(polygon, h); + set_holes(polygon, holes.begin(), holes.end()); + return polygon; + } + + //scale non-45 + template + typename enable_if< + typename gtl_and< typename is_any_mutable_polygon_without_holes_type::type, + typename gtl_not::type>::type>::type>::type>::type, + polygon_type>::type & + scale(polygon_type& polygon, double factor) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + scale(points.back(), anisotropic_scale_factor(factor, factor)); + } + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + + //scale specifically 45 + template + polygon_type& + scale(polygon_type& polygon, double factor, + typename enable_if< + typename gtl_and< typename is_any_mutable_polygon_without_holes_type::type, + typename gtl_same_type + < forty_five_domain, + typename geometry_domain::type>::type>::type>::type>::type * = 0 + ) { + std::vector::iterator_type>::value_type> points; + points.reserve(::boost::polygon::size(polygon)); + for(typename polygon_traits::iterator_type iter = begin_points(polygon); + iter != end_points(polygon); ++iter) { + points.push_back(*iter); + scale(points.back(), anisotropic_scale_factor(factor, factor)); + } + snap_point_vector_to_45(points); + polygon_mutable_traits::set_points(polygon, points.begin(), points.end()); + return polygon; + } + + template + T& + scale(T& polygon, double factor, + typename enable_if< typename is_any_mutable_polygon_with_holes_type::type>::type * = 0 + ) { + typedef typename polygon_with_holes_traits::hole_type hole_type; + hole_type h; + set_points(h, begin_points(polygon), end_points(polygon)); + scale(h, factor); + std::vector holes; + holes.reserve(size_holes(polygon)); + for(typename polygon_with_holes_traits::iterator_holes_type itr = begin_holes(polygon); + itr != end_holes(polygon); ++itr) { + holes.push_back(*itr); + scale(holes.back(), factor); + } + assign(polygon, h); + set_holes(polygon, holes.begin(), holes.end()); + return polygon; + } + + template + static area_type + point_sequence_area(iterator_type begin_range, iterator_type end_range) { + typedef typename std::iterator_traits::value_type point_type; + if(begin_range == end_range) return area_type(0); + point_type first = *begin_range; + point_type previous = first; + ++begin_range; + // Initialize trapezoid base line + area_type y_base = (area_type)y(first); + // Initialize area accumulator + + area_type area(0); + while (begin_range != end_range) { + area_type x1 = (area_type)x(previous); + area_type x2 = (area_type)x(*begin_range); +#ifdef BOOST_POLYGON_ICC +#pragma warning (push) +#pragma warning (disable:1572) +#endif + if(x1 != x2) { +#ifdef BOOST_POLYGON_ICC +#pragma warning (pop) +#endif + // do trapezoid area accumulation + area += (x2 - x1) * (((area_type)y(*begin_range) - y_base) + + ((area_type)y(previous) - y_base)) / 2; + } + previous = *begin_range; + // go to next point + ++begin_range; + } + //wrap around to evaluate the edge between first and last if not closed + if(!equivalence(first, previous)) { + area_type x1 = (area_type)x(previous); + area_type x2 = (area_type)x(first); + area += (x2 - x1) * (((area_type)y(first) - y_base) + + ((area_type)y(previous) - y_base)) / 2; + } + return area; + } + + template + typename enable_if< + typename is_polygon_with_holes_type::type, + typename area_type_by_domain< typename geometry_domain::type>::type, + typename polygon_traits::coordinate_type>::type>::type + area(const T& polygon) { + typedef typename area_type_by_domain< typename geometry_domain::type>::type, + typename polygon_traits::coordinate_type>::type area_type; + area_type retval = point_sequence_area::iterator_type, area_type> + (begin_points(polygon), end_points(polygon)); + if(retval < 0) retval *= -1; + for(typename polygon_with_holes_traits::iterator_holes_type itr = + polygon_with_holes_traits::begin_holes(polygon); + itr != polygon_with_holes_traits::end_holes(polygon); ++itr) { + area_type tmp_area = point_sequence_area + ::hole_type>::iterator_type, area_type> + (begin_points(*itr), end_points(*itr)); + if(tmp_area < 0) tmp_area *= -1; + retval -= tmp_area; + } + return retval; + } + + template + bool point_sequence_is_45(iT itr, iT itr_end) { + typedef typename std::iterator_traits::value_type Point; + typedef typename point_traits::coordinate_type Unit; + if(itr == itr_end) return true; + Point firstPt = *itr; + Point prevPt = firstPt; + ++itr; + while(itr != itr_end) { + Point pt = *itr; + Unit deltax = x(pt) - x(prevPt); + Unit deltay = y(pt) - y(prevPt); + if(deltax && deltay && + local_abs(deltax) != local_abs(deltay)) + return false; + prevPt = pt; + ++itr; + } + Unit deltax = x(firstPt) - x(prevPt); + Unit deltay = y(firstPt) - y(prevPt); + if(deltax && deltay && + local_abs(deltax) != local_abs(deltay)) + return false; + return true; + } + + template + typename enable_if< typename is_polygon_with_holes_type::type, bool>::type + is_45(const polygon_type& polygon) { + typename polygon_traits::iterator_type itr = begin_points(polygon), itr_end = end_points(polygon); + if(!point_sequence_is_45(itr, itr_end)) return false; + typename polygon_with_holes_traits::iterator_holes_type itrh = begin_holes(polygon), itrh_end = end_holes(polygon); + typedef typename polygon_with_holes_traits::hole_type hole_type; + for(; itrh != itrh_end; ++ itrh) { + typename polygon_traits::iterator_type itr1 = begin_points(polygon), itr1_end = end_points(polygon); + if(!point_sequence_is_45(itr1, itr1_end)) return false; + } + return true; + } + + template + distance_type point_sequence_distance(iterator_type itr, iterator_type itr_end) { + typedef distance_type Unit; + typedef iterator_type iterator; + typedef typename std::iterator_traits::value_type point_type; + Unit return_value = Unit(0); + point_type previous_point, first_point; + if(itr == itr_end) return return_value; + previous_point = first_point = *itr; + ++itr; + for( ; itr != itr_end; ++itr) { + point_type current_point = *itr; + return_value += (Unit)euclidean_distance(current_point, previous_point); + previous_point = current_point; + } + return_value += (Unit)euclidean_distance(previous_point, first_point); + return return_value; + } + + template + typename distance_type_by_domain + ::type>::type, typename polygon_traits::coordinate_type>::type + perimeter(const T& polygon, + typename enable_if< + typename is_polygon_with_holes_type::type>::type * = 0 + ) { + typedef typename distance_type_by_domain + ::type>::type, typename polygon_traits::coordinate_type>::type Unit; + typedef typename polygon_traits::iterator_type iterator; + iterator itr = begin_points(polygon); + iterator itr_end = end_points(polygon); + Unit return_value = point_sequence_distance(itr, itr_end); + for(typename polygon_with_holes_traits::iterator_holes_type itr_holes = begin_holes(polygon); + itr_holes != end_holes(polygon); ++itr_holes) { + typedef typename polygon_traits::hole_type>::iterator_type hitertype; + return_value += point_sequence_distance(begin_points(*itr_holes), end_points(*itr_holes)); + } + return return_value; + } + + template + typename enable_if ::type, + direction_1d>::type + winding(const T& polygon) { + winding_direction wd = polygon_traits::winding(polygon); + if(wd != unknown_winding) { + return wd == clockwise_winding ? CLOCKWISE: COUNTERCLOCKWISE; + } + typedef typename area_type_by_domain< typename geometry_domain::type>::type, + typename polygon_traits::coordinate_type>::type area_type; + return point_sequence_area::iterator_type, area_type>(begin_points(polygon), end_points(polygon)) < 0 ? + COUNTERCLOCKWISE : CLOCKWISE; + } + + template + typename enable_if< + typename gtl_and< + typename is_polygon_90_type::type, + typename gtl_same_type< + typename geometry_concept::type, + point_concept + >::type + >::type, + bool + >::type contains( + const T& polygon, + const input_point_type& point, + bool consider_touch = true) { + typedef T polygon_type; + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef typename polygon_traits::iterator_type iterator; + typedef typename std::iterator_traits::value_type point_type; + coordinate_type point_x = x(point); + coordinate_type point_y = y(point); + // Check how many intersections has the ray extended from the given + // point in the x-axis negative direction with the polygon edges. + // If the number is odd the point is within the polygon, otherwise not. + // We can safely ignore horizontal edges, however intersections with + // end points of the vertical edges require special handling. We should + // add one intersection in case horizontal edges that extend vertical edge + // point in the same direction. + int num_full_intersections = 0; + int num_half_intersections = 0; + for (iterator iter = begin_points(polygon); iter != end_points(polygon);) { + point_type curr_point = *iter; + ++iter; + point_type next_point = (iter == end_points(polygon)) ? *begin_points(polygon) : *iter; + if (x(curr_point) == x(next_point)) { + if (x(curr_point) > point_x) { + continue; + } + coordinate_type min_y = (std::min)(y(curr_point), y(next_point)); + coordinate_type max_y = (std::max)(y(curr_point), y(next_point)); + if (point_y > min_y && point_y < max_y) { + if (x(curr_point) == point_x) { + return consider_touch; + } + ++num_full_intersections; + } + if (point_y == min_y || point_y == max_y) { + num_half_intersections += (y(curr_point) < y(next_point) ? 1 : -1); + } + } else { + coordinate_type min_x = (std::min)(x(curr_point), x(next_point)); + coordinate_type max_x = (std::max)(x(curr_point), x(next_point)); + if (point_x >= min_x && point_x <= max_x) { + if (y(curr_point) == point_y) { + return consider_touch; + } + } + } + } + int total_intersections = num_full_intersections + (num_half_intersections >> 1); + return total_intersections & 1; + } + + //TODO: refactor to expose as user APIs + template + struct edge_utils { + typedef point_data Point; + typedef std::pair half_edge; + + class less_point { + public: + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + inline less_point() {} + inline bool operator () (const Point& pt1, const Point& pt2) const { + if(pt1.get(HORIZONTAL) < pt2.get(HORIZONTAL)) return true; + if(pt1.get(HORIZONTAL) == pt2.get(HORIZONTAL)) { + if(pt1.get(VERTICAL) < pt2.get(VERTICAL)) return true; + } + return false; + } + }; + + static inline bool between(Point pt, Point pt1, Point pt2) { + less_point lp; + if(lp(pt1, pt2)) + return lp(pt, pt2) && lp(pt1, pt); + return lp(pt, pt1) && lp(pt2, pt); + } + + template + static inline bool equal_slope(area_type dx1, area_type dy1, area_type dx2, area_type dy2) { + typedef typename coordinate_traits::unsigned_area_type unsigned_product_type; + unsigned_product_type cross_1 = (unsigned_product_type)(dx2 < 0 ? -dx2 :dx2) * (unsigned_product_type)(dy1 < 0 ? -dy1 : dy1); + unsigned_product_type cross_2 = (unsigned_product_type)(dx1 < 0 ? -dx1 :dx1) * (unsigned_product_type)(dy2 < 0 ? -dy2 : dy2); + int dx1_sign = dx1 < 0 ? -1 : 1; + int dx2_sign = dx2 < 0 ? -1 : 1; + int dy1_sign = dy1 < 0 ? -1 : 1; + int dy2_sign = dy2 < 0 ? -1 : 1; + int cross_1_sign = dx2_sign * dy1_sign; + int cross_2_sign = dx1_sign * dy2_sign; + return cross_1 == cross_2 && (cross_1_sign == cross_2_sign || cross_1 == 0); + } + + static inline bool equal_slope(const Unit& x, const Unit& y, + const Point& pt1, const Point& pt2) { + const Point* pts[2] = {&pt1, &pt2}; + typedef typename coordinate_traits::manhattan_area_type at; + at dy2 = (at)pts[1]->get(VERTICAL) - (at)y; + at dy1 = (at)pts[0]->get(VERTICAL) - (at)y; + at dx2 = (at)pts[1]->get(HORIZONTAL) - (at)x; + at dx1 = (at)pts[0]->get(HORIZONTAL) - (at)x; + return equal_slope(dx1, dy1, dx2, dy2); + } + + template + static inline bool less_slope(area_type dx1, area_type dy1, area_type dx2, area_type dy2) { + //reflext x and y slopes to right hand side half plane + if(dx1 < 0) { + dy1 *= -1; + dx1 *= -1; + } else if(dx1 == 0) { + //if the first slope is vertical the first cannot be less + return false; + } + if(dx2 < 0) { + dy2 *= -1; + dx2 *= -1; + } else if(dx2 == 0) { + //if the second slope is vertical the first is always less unless it is also vertical, in which case they are equal + return dx1 != 0; + } + typedef typename coordinate_traits::unsigned_area_type unsigned_product_type; + unsigned_product_type cross_1 = (unsigned_product_type)(dx2 < 0 ? -dx2 :dx2) * (unsigned_product_type)(dy1 < 0 ? -dy1 : dy1); + unsigned_product_type cross_2 = (unsigned_product_type)(dx1 < 0 ? -dx1 :dx1) * (unsigned_product_type)(dy2 < 0 ? -dy2 : dy2); + int dx1_sign = dx1 < 0 ? -1 : 1; + int dx2_sign = dx2 < 0 ? -1 : 1; + int dy1_sign = dy1 < 0 ? -1 : 1; + int dy2_sign = dy2 < 0 ? -1 : 1; + int cross_1_sign = dx2_sign * dy1_sign; + int cross_2_sign = dx1_sign * dy2_sign; + if(cross_1_sign < cross_2_sign) return true; + if(cross_2_sign < cross_1_sign) return false; + if(cross_1_sign == -1) return cross_2 < cross_1; + return cross_1 < cross_2; + } + + static inline bool less_slope(const Unit& x, const Unit& y, + const Point& pt1, const Point& pt2) { + const Point* pts[2] = {&pt1, &pt2}; + //compute y value on edge from pt_ to pts[1] at the x value of pts[0] + typedef typename coordinate_traits::manhattan_area_type at; + at dy2 = (at)pts[1]->get(VERTICAL) - (at)y; + at dy1 = (at)pts[0]->get(VERTICAL) - (at)y; + at dx2 = (at)pts[1]->get(HORIZONTAL) - (at)x; + at dx1 = (at)pts[0]->get(HORIZONTAL) - (at)x; + return less_slope(dx1, dy1, dx2, dy2); + } + + //return -1 below, 0 on and 1 above line + //assumes point is on x interval of segment + static inline int on_above_or_below(Point pt, const half_edge& he) { + if(pt == he.first || pt == he.second) return 0; + if(equal_slope(pt.get(HORIZONTAL), pt.get(VERTICAL), he.first, he.second)) return 0; + bool less_result = less_slope(pt.get(HORIZONTAL), pt.get(VERTICAL), he.first, he.second); + int retval = less_result ? -1 : 1; + less_point lp; + if(lp(he.second, he.first)) retval *= -1; + if(!between(pt, he.first, he.second)) retval *= -1; + return retval; + } + }; + + template + typename enable_if< + typename gtl_and< typename is_any_mutable_polygon_with_holes_type::type, + typename gtl_same_type::type, point_concept>::type>::type, + bool>::type + contains(const T& polygon, const input_point_type& point, bool consider_touch = true) { + typedef typename polygon_with_holes_traits::iterator_holes_type holes_iterator; + bool isInside = contains( view_as< typename polygon_from_polygon_with_holes_type< + typename geometry_concept::type>::type>( polygon ), point, consider_touch ); + if(!isInside) return false; //no need to check holes + holes_iterator itH = begin_holes( polygon ); + while( itH != end_holes( polygon ) ) { + if( contains( *itH, point, !consider_touch ) ) { + isInside = false; + break; + } + ++itH; + } + return isInside; + } + + template + typename enable_if< + typename gtl_and_3< + typename is_polygon_type::type, + typename gtl_different_type::type>::type, manhattan_domain>::type, + typename gtl_same_type::type, point_concept>::type>::type, + bool>::type + contains(const T& polygon, const input_point_type& point, bool consider_touch = true) { + typedef typename point_traits::coordinate_type Unit; + typedef point_data Point; + typedef std::pair half_edge; + typedef typename polygon_traits::iterator_type iterator; + iterator itr = begin_points(polygon); + iterator itrEnd = end_points(polygon); + half_edge he; + if(itr == itrEnd) return false; + assign(he.first, *itr); + Point firstPt; + assign(firstPt, *itr); + ++itr; + if(itr == itrEnd) return false; + bool done = false; + int above = 0; + while(!done) { + Point currentPt; + if(itr == itrEnd) { + done = true; + currentPt = firstPt; + } else { + assign(currentPt, *itr); + ++itr; + } + if(currentPt == he.first) { + continue; + } else { + he.second = currentPt; + if(equivalence(point, currentPt)) return consider_touch; + Unit xmin = (std::min)(x(he.first), x(he.second)); + Unit xmax = (std::max)(x(he.first), x(he.second)); + if(x(point) >= xmin && x(point) < xmax) { //double counts if <= xmax + Point tmppt; + assign(tmppt, point); + int oabedge = edge_utils::on_above_or_below(tmppt, he); + if(oabedge == 0) return consider_touch; + if(oabedge == 1) ++above; + } else if(x(point) == xmax) { + if(x(point) == xmin) { + Unit ymin = (std::min)(y(he.first), y(he.second)); + Unit ymax = (std::max)(y(he.first), y(he.second)); + Unit ypt = y(point); + if(ypt <= ymax && ypt >= ymin) + return consider_touch; + } else { + Point tmppt; + assign(tmppt, point); + if( edge_utils::on_above_or_below(tmppt, he) == 0 ) { + return consider_touch; + } + } + } + } + he.first = he.second; + } + return above % 2 != 0; //if the point is above an odd number of edges is must be inside polygon + } + + /* + template + typename enable_if< + typename gtl_and_3< + typename is_polygon_with_holes_type::type, + typename gtl_different_type::type>::type, manhattan_domain>::type, + typename gtl_same_type::type, point_concept>::type>::type, + bool>::type + contains(const T& polygon, const input_point_type& point, bool consider_touch = true) { + typedef typename point_traits::coordinate_type Unit; + typedef point_data Point; + typedef std::pair half_edge; + typedef typename polygon_traits::iterator_type iterator; + iterator itr = begin_points(polygon); + iterator itrEnd = end_points(polygon); + half_edge he; + if(itr == itrEnd) return false; + assign(he.first, *itr); + Point firstPt; + assign(firstPt, *itr); + ++itr; + if(itr == itrEnd) return false; + bool done = false; + int above = 0; + while(!done) { + Point currentPt; + if(itr == itrEnd) { + done = true; + currentPt = firstPt; + } else { + assign(currentPt, *itr); + ++itr; + } + if(currentPt == he.first) { + continue; + } else { + he.second = currentPt; + if(equivalence(point, currentPt)) return consider_touch; + Unit xmin = (std::min)(x(he.first), x(he.second)); + Unit xmax = (std::max)(x(he.first), x(he.second)); + if(x(point) >= xmin && x(point) < xmax) { //double counts if <= xmax + Point tmppt; + assign(tmppt, point); + int oabedge = edge_utils::on_above_or_below(tmppt, he); + if(oabedge == 0) return consider_touch; + if(oabedge == 1) ++above; + } + } + he.first = he.second; + } + return above % 2 != 0; //if the point is above an odd number of edges is must be inside polygon + } + */ + + template + typename enable_if< + typename gtl_and< typename is_mutable_rectangle_concept::type>::type, + typename is_polygon_with_holes_type::type>::type, + bool>::type + extents(T1& bounding_box, const T2& polygon) { + typedef typename polygon_traits::iterator_type iterator; + bool first_iteration = true; + iterator itr_end = end_points(polygon); + for(iterator itr = begin_points(polygon); itr != itr_end; ++itr) { + if(first_iteration) { + set_points(bounding_box, *itr, *itr); + first_iteration = false; + } else { + encompass(bounding_box, *itr); + } + } + if(first_iteration) return false; + return true; + } + + template + typename enable_if< + typename gtl_and< typename is_mutable_point_concept::type>::type, + typename is_polygon_with_holes_type::type>::type, + bool>::type + center(T1& center_point, const T2& polygon) { + typedef typename polygon_traits::coordinate_type coordinate_type; + rectangle_data bbox; + extents(bbox, polygon); + return center(center_point, bbox); + } + + template + template + polygon_90_data& polygon_90_data::operator=(const T2& rvalue) { + assign(*this, rvalue); + return *this; + } + + template + template + polygon_45_data& polygon_45_data::operator=(const T2& rvalue) { + assign(*this, rvalue); + return *this; + } + + template + template + polygon_data& polygon_data::operator=(const T2& rvalue) { + assign(*this, rvalue); + return *this; + } + + template + template + polygon_90_with_holes_data& polygon_90_with_holes_data::operator=(const T2& rvalue) { + assign(*this, rvalue); + return *this; + } + + template + template + polygon_45_with_holes_data& polygon_45_with_holes_data::operator=(const T2& rvalue) { + assign(*this, rvalue); + return *this; + } + + template + template + polygon_with_holes_data& polygon_with_holes_data::operator=(const T2& rvalue) { + assign(*this, rvalue); + return *this; + } + + template + struct geometry_concept > { + typedef polygon_concept type; + }; + template + struct geometry_concept > { + typedef polygon_45_concept type; + }; + template + struct geometry_concept > { + typedef polygon_90_concept type; + }; + template + struct geometry_concept > { + typedef polygon_with_holes_concept type; + }; + template + struct geometry_concept > { + typedef polygon_45_with_holes_concept type; + }; + template + struct geometry_concept > { + typedef polygon_90_with_holes_concept type; + }; + +// template struct polygon_with_holes_traits > { +// typedef polygon_90_data hole_type; +// typedef const hole_type* iterator_holes_type; +// static inline iterator_holes_type begin_holes(const hole_type& t) { return &t; } +// static inline iterator_holes_type end_holes(const hole_type& t) { return &t; } +// static inline std::size_t size_holes(const hole_type& t) { return 0; } +// }; +// template struct polygon_with_holes_traits > { +// typedef polygon_45_data hole_type; +// typedef const hole_type* iterator_holes_type; +// static inline iterator_holes_type begin_holes(const hole_type& t) { return &t; } +// static inline iterator_holes_type end_holes(const hole_type& t) { return &t; } +// static inline std::size_t size_holes(const hole_type& t) { return 0; } +// }; +// template struct polygon_with_holes_traits > { +// typedef polygon_data hole_type; +// typedef const hole_type* iterator_holes_type; +// static inline iterator_holes_type begin_holes(const hole_type& t) { return &t; } +// static inline iterator_holes_type end_holes(const hole_type& t) { return &t; } +// static inline std::size_t size_holes(const hole_type& t) { return 0; } +// }; + template struct get_void {}; + template <> struct get_void { typedef void type; }; + + template struct polygon_with_holes_traits< + T, typename get_void::type>::type > { + typedef T hole_type; + typedef const hole_type* iterator_holes_type; + static inline iterator_holes_type begin_holes(const hole_type& t) { return &t; } + static inline iterator_holes_type end_holes(const hole_type& t) { return &t; } + }; + + template + struct view_of { + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef interval_data interval_type; + rectangle_data rect; + view_of(const T& obj) : rect() { + point_data pts[2]; + typename polygon_traits::iterator_type itr = + begin_points(obj), itre = end_points(obj); + if(itr == itre) return; + assign(pts[0], *itr); + ++itr; + if(itr == itre) return; + ++itr; + if(itr == itre) return; + assign(pts[1], *itr); + set_points(rect, pts[0], pts[1]); + } + inline interval_type get(orientation_2d orient) const { + return rect.get(orient); } + }; + + template + struct geometry_concept > { + typedef rectangle_concept type; + }; + + template + struct view_of { + const T* t; + view_of(const T& obj) : t(&obj) {} + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef typename polygon_traits::iterator_type iterator_type; + typedef typename polygon_traits::point_type point_type; + + /// Get the begin iterator + inline iterator_type begin() const { + return polygon_traits::begin_points(*t); + } + + /// Get the end iterator + inline iterator_type end() const { + return polygon_traits::end_points(*t); + } + + /// Get the number of sides of the polygon + inline std::size_t size() const { + return polygon_traits::size(*t); + } + + /// Get the winding direction of the polygon + inline winding_direction winding() const { + return polygon_traits::winding(*t); + } + }; + + template + struct geometry_concept > { + typedef polygon_45_concept type; + }; + + template + struct view_of { + const T* t; + view_of(const T& obj) : t(&obj) {} + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef typename polygon_traits::iterator_type iterator_type; + typedef typename polygon_traits::point_type point_type; + typedef iterator_points_to_compact compact_iterator_type; + + /// Get the begin iterator + inline compact_iterator_type begin_compact() const { + return compact_iterator_type(polygon_traits::begin_points(*t), + polygon_traits::end_points(*t)); + } + + /// Get the end iterator + inline compact_iterator_type end_compact() const { + return compact_iterator_type(polygon_traits::end_points(*t), + polygon_traits::end_points(*t)); + } + + /// Get the number of sides of the polygon + inline std::size_t size() const { + return polygon_traits::size(*t); + } + + /// Get the winding direction of the polygon + inline winding_direction winding() const { + return polygon_traits::winding(*t); + } + }; + + template + struct geometry_concept > { + typedef polygon_90_concept type; + }; + + template + struct view_of { + const T* t; + view_of(const T& obj) : t(&obj) {} + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef typename polygon_traits::iterator_type iterator_type; + typedef typename polygon_traits::point_type point_type; + typedef view_of::hole_type> hole_type; + struct iterator_holes_type { + typedef std::forward_iterator_tag iterator_category; + typedef hole_type value_type; + typedef std::ptrdiff_t difference_type; + typedef const hole_type* pointer; //immutable + typedef const hole_type& reference; //immutable + typedef typename polygon_with_holes_traits::iterator_holes_type iht; + iht internal_itr; + iterator_holes_type() : internal_itr() {} + iterator_holes_type(iht iht_in) : internal_itr(iht_in) {} + inline iterator_holes_type& operator++() { + ++internal_itr; + return *this; + } + inline const iterator_holes_type operator++(int) { + iterator_holes_type tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator_holes_type& that) const { + return (internal_itr == that.internal_itr); + } + inline bool operator!=(const iterator_holes_type& that) const { + return (internal_itr != that.internal_itr); + } + inline value_type operator*() const { + return view_as(*internal_itr); + } + }; + + /// Get the begin iterator + inline iterator_type begin() const { + return polygon_traits::begin_points(*t); + } + + /// Get the end iterator + inline iterator_type end() const { + return polygon_traits::end_points(*t); + } + + /// Get the number of sides of the polygon + inline std::size_t size() const { + return polygon_traits::size(*t); + } + + /// Get the winding direction of the polygon + inline winding_direction winding() const { + return polygon_traits::winding(*t); + } + + /// Get the begin iterator + inline iterator_holes_type begin_holes() const { + return polygon_with_holes_traits::begin_holes(*t); + } + + /// Get the end iterator + inline iterator_holes_type end_holes() const { + return polygon_with_holes_traits::end_holes(*t); + } + + /// Get the number of sides of the polygon + inline std::size_t size_holes() const { + return polygon_with_holes_traits::size_holes(*t); + } + + }; + + template + struct geometry_concept > { + typedef polygon_45_with_holes_concept type; + }; + + template + struct view_of { + const T* t; + view_of(const T& obj) : t(&obj) {} + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef typename polygon_traits::iterator_type iterator_type; + typedef typename polygon_traits::point_type point_type; + typedef iterator_points_to_compact compact_iterator_type; + typedef view_of::hole_type> hole_type; + struct iterator_holes_type { + typedef std::forward_iterator_tag iterator_category; + typedef hole_type value_type; + typedef std::ptrdiff_t difference_type; + typedef const hole_type* pointer; //immutable + typedef const hole_type& reference; //immutable + typedef typename polygon_with_holes_traits::iterator_holes_type iht; + iht internal_itr; + iterator_holes_type() : internal_itr() {} + iterator_holes_type(iht iht_in) : internal_itr(iht_in) {} + inline iterator_holes_type& operator++() { + ++internal_itr; + return *this; + } + inline const iterator_holes_type operator++(int) { + iterator_holes_type tmp(*this); + ++(*this); + return tmp; + } + inline bool operator==(const iterator_holes_type& that) const { + return (internal_itr == that.internal_itr); + } + inline bool operator!=(const iterator_holes_type& that) const { + return (internal_itr != that.internal_itr); + } + inline value_type operator*() const { + return view_as(*internal_itr); + } + }; + + /// Get the begin iterator + inline compact_iterator_type begin_compact() const { + return compact_iterator_type(polygon_traits::begin_points(*t), + polygon_traits::end_points(*t)); + } + + /// Get the end iterator + inline compact_iterator_type end_compact() const { + return compact_iterator_type(polygon_traits::end_points(*t), + polygon_traits::end_points(*t)); + } + + /// Get the number of sides of the polygon + inline std::size_t size() const { + return polygon_traits::size(*t); + } + + /// Get the winding direction of the polygon + inline winding_direction winding() const { + return polygon_traits::winding(*t); + } + + /// Get the begin iterator + inline iterator_holes_type begin_holes() const { + return polygon_with_holes_traits::begin_holes(*t); + } + + /// Get the end iterator + inline iterator_holes_type end_holes() const { + return polygon_with_holes_traits::end_holes(*t); + } + + /// Get the number of sides of the polygon + inline std::size_t size_holes() const { + return polygon_with_holes_traits::size_holes(*t); + } + + }; + + template + struct geometry_concept > { + typedef polygon_90_with_holes_concept type; + }; + + template + struct view_of { + const T* t; + view_of(const T& obj) : t(&obj) {} + typedef typename polygon_traits::coordinate_type coordinate_type; + typedef typename polygon_traits::iterator_type iterator_type; + typedef typename polygon_traits::point_type point_type; + + /// Get the begin iterator + inline iterator_type begin() const { + return polygon_traits::begin_points(*t); + } + + /// Get the end iterator + inline iterator_type end() const { + return polygon_traits::end_points(*t); + } + + /// Get the number of sides of the polygon + inline std::size_t size() const { + return polygon_traits::size(*t); + } + + /// Get the winding direction of the polygon + inline winding_direction winding() const { + return polygon_traits::winding(*t); + } + }; + + template + struct geometry_concept > { + typedef polygon_concept type; + }; +} +} + +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_with_holes_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_with_holes_data.hpp new file mode 100644 index 0000000..a1a0e1d --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/polygon_with_holes_data.hpp @@ -0,0 +1,107 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_POLYGON_WITH_HOLES_DATA_HPP +#define BOOST_POLYGON_POLYGON_WITH_HOLES_DATA_HPP +#include "isotropy.hpp" +#include "polygon_data.hpp" +namespace boost { namespace polygon{ + struct polygon_with_holes_concept; + template + class polygon_with_holes_data { +public: + typedef polygon_with_holes_concept geometry_type; + typedef T coordinate_type; + typedef typename polygon_data::iterator_type iterator_type; + typedef typename std::list >::const_iterator iterator_holes_type; + typedef polygon_data hole_type; + typedef typename coordinate_traits::coordinate_distance area_type; + typedef point_data point_type; + + // default constructor of point does not initialize x and y + inline polygon_with_holes_data() : self_(), holes_() {} //do nothing default constructor + + template + inline polygon_with_holes_data(iT input_begin, iT input_end) : self_(), holes_() { + set(input_begin, input_end); + } + + template + inline polygon_with_holes_data(iT input_begin, iT input_end, hiT holes_begin, hiT holes_end) : self_(), holes_() { + set(input_begin, input_end); + set_holes(holes_begin, holes_end); + } + + template + inline polygon_with_holes_data& set(iT input_begin, iT input_end) { + self_.set(input_begin, input_end); + return *this; + } + + // initialize a polygon from x,y values, it is assumed that the first is an x + // and that the input is a well behaved polygon + template + inline polygon_with_holes_data& set_holes(iT input_begin, iT input_end) { + holes_.clear(); //just in case there was some old data there + for( ; input_begin != input_end; ++ input_begin) { + holes_.push_back(hole_type()); + holes_.back().set((*input_begin).begin(), (*input_begin).end()); + } + return *this; + } + + // copy constructor (since we have dynamic memory) + inline polygon_with_holes_data(const polygon_with_holes_data& that) : self_(that.self_), + holes_(that.holes_) {} + + // assignment operator (since we have dynamic memory do a deep copy) + inline polygon_with_holes_data& operator=(const polygon_with_holes_data& that) { + self_ = that.self_; + holes_ = that.holes_; + return *this; + } + + template + inline polygon_with_holes_data& operator=(const T2& rvalue); + + // get begin iterator, returns a pointer to a const coordinate_type + inline const iterator_type begin() const { + return self_.begin(); + } + + // get end iterator, returns a pointer to a const coordinate_type + inline const iterator_type end() const { + return self_.end(); + } + + inline std::size_t size() const { + return self_.size(); + } + + // get begin iterator, returns a pointer to a const polygon + inline const iterator_holes_type begin_holes() const { + return holes_.begin(); + } + + // get end iterator, returns a pointer to a const polygon + inline const iterator_holes_type end_holes() const { + return holes_.end(); + } + + inline std::size_t size_holes() const { + return holes_.size(); + } + +public: + polygon_data self_; + std::list holes_; + }; + + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_concept.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_concept.hpp new file mode 100644 index 0000000..e824319 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_concept.hpp @@ -0,0 +1,1082 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_RECTANGLE_CONCEPT_HPP +#define BOOST_POLYGON_RECTANGLE_CONCEPT_HPP + +#include "isotropy.hpp" + +//point +#include "point_data.hpp" +#include "point_traits.hpp" +#include "point_concept.hpp" + +//interval +#include "interval_data.hpp" +#include "interval_traits.hpp" +#include "interval_concept.hpp" + +#include "rectangle_data.hpp" +#include "rectangle_traits.hpp" + +namespace boost { namespace polygon{ + struct rectangle_concept {}; + + template + struct is_rectangle_concept { typedef gtl_no type; }; + template <> + struct is_rectangle_concept { typedef gtl_yes type; }; + + template + struct is_mutable_rectangle_concept { typedef gtl_no type; }; + template <> + struct is_mutable_rectangle_concept { typedef gtl_yes type; }; + + template <> + struct geometry_domain { typedef manhattan_domain type; }; + + template + struct rectangle_interval_type_by_concept { typedef void type; }; + template + struct rectangle_interval_type_by_concept { typedef typename rectangle_traits::interval_type type; }; + + template + struct rectangle_interval_type { + typedef typename rectangle_interval_type_by_concept::type>::type>::type type; + }; + + template + struct rectangle_coordinate_type_by_concept { typedef void type; }; + template + struct rectangle_coordinate_type_by_concept { typedef typename rectangle_traits::coordinate_type type; }; + + template + struct rectangle_coordinate_type { + typedef typename rectangle_coordinate_type_by_concept::type>::type>::type type; + }; + + template + struct rectangle_difference_type_by_concept { typedef void type; }; + template + struct rectangle_difference_type_by_concept { + typedef typename coordinate_traits::coordinate_type>::coordinate_difference type; }; + + template + struct rectangle_difference_type { + typedef typename rectangle_difference_type_by_concept< + T, typename is_rectangle_concept::type>::type>::type type; + }; + + template + struct rectangle_distance_type_by_concept { typedef void type; }; + template + struct rectangle_distance_type_by_concept { + typedef typename coordinate_traits::type>::coordinate_distance type; }; + + template + struct rectangle_distance_type { + typedef typename rectangle_distance_type_by_concept< + T, typename is_rectangle_concept::type>::type>::type type; + }; + + struct y_r_get_interval : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_interval_type::type>::type + get(const T& rectangle, orientation_2d orient) { + return rectangle_traits::get(rectangle, orient); + } + + struct y_r_h : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_interval_type::type>::type + horizontal(const T& rectangle) { + return rectangle_traits::get(rectangle, HORIZONTAL); + } + + struct y_r_v : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_interval_type::type>::type + vertical(const T& rectangle) { + return rectangle_traits::get(rectangle, VERTICAL); + } + + struct y_r_set : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_interval_concept::type>::type>::type, + void>::type + set(T& rectangle, const T2& interval) { + rectangle_mutable_traits::set(rectangle, orient, interval); + } + + struct y_r_set2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_interval_concept::type>::type>::type, + void>::type + set(T& rectangle, orientation_2d orient, const T2& interval) { + rectangle_mutable_traits::set(rectangle, orient, interval); + } + + struct y_r_h2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_interval_concept::type>::type>::type, + void>::type + horizontal(T& rectangle, const T2& interval) { + rectangle_mutable_traits::set(rectangle, HORIZONTAL, interval); + } + + struct y_r_v2 : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_interval_concept::type>::type>::type, void>::type + vertical(T& rectangle, const T2& interval) { + rectangle_mutable_traits::set(rectangle, VERTICAL, interval); + } + + struct y_r_construct : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + T>::type + construct(const T2& interval_horizontal, + const T3& interval_vertical) { + return rectangle_mutable_traits::construct(interval_horizontal, interval_vertical); } + + struct y_r_construct2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + T>::type + construct(coord_type xl, coord_type yl, coord_type xh, coord_type yh) { + return rectangle_mutable_traits::construct(interval_data(xl, xh), + interval_data(yl, yh)); + } + + struct y_r_cconstruct : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + T>::type + copy_construct(const T2& rectangle) { + return construct (get(rectangle, HORIZONTAL), get(rectangle, VERTICAL)); + } + + struct y_r_assign : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3< y_r_assign, + typename is_mutable_rectangle_concept::type>::type, + typename is_rectangle_concept::type>::type>::type, + rectangle_type_1>::type & + assign(rectangle_type_1& lvalue, const rectangle_type_2& rvalue) { + set(lvalue, HORIZONTAL, get(rvalue, HORIZONTAL)); + set(lvalue, VERTICAL, get(rvalue, VERTICAL)); + return lvalue; + } + + struct y_r_equiv : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3< y_r_equiv, + typename is_rectangle_concept::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + equivalence(const T& rect1, const T2& rect2) { + return equivalence(get(rect1, HORIZONTAL), get(rect2, HORIZONTAL)) && + equivalence(get(rect1, VERTICAL), get(rect2, VERTICAL)); + } + + struct y_r_get : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_coordinate_type::type>::type + get(const rectangle_type& rectangle, orientation_2d orient, direction_1d dir) { + return get(rectangle_traits::get(rectangle, orient), dir); + } + + struct y_r_set3 : gtl_yes {}; + + template + typename enable_if::type>::type>::type, void>::type + set(rectangle_type& rectangle, orientation_2d orient, direction_1d dir, + typename rectangle_coordinate_type::type value) { + typename rectangle_interval_type::type ivl = get(rectangle, orient); + set(ivl, dir, value); + set(rectangle, orient, ivl); + } + + struct y_r_xl : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_coordinate_type::type>::type + xl(const rectangle_type& rectangle) { + return get(rectangle, HORIZONTAL, LOW); + } + + struct y_r_xl2 : gtl_yes {}; + + template + typename enable_if::type>::type>::type, void>::type + xl(rectangle_type& rectangle, typename rectangle_coordinate_type::type value) { + return set(rectangle, HORIZONTAL, LOW, value); + } + + struct y_r_xh : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_coordinate_type::type>::type + xh(const rectangle_type& rectangle) { + return get(rectangle, HORIZONTAL, HIGH); + } + + struct y_r_xh2 : gtl_yes {}; + + template + typename enable_if::type>::type>::type, void>::type + xh(rectangle_type& rectangle, typename rectangle_coordinate_type::type value) { + return set(rectangle, HORIZONTAL, HIGH, value); + } + + struct y_r_yl : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_coordinate_type::type>::type + yl(const rectangle_type& rectangle) { + return get(rectangle, VERTICAL, LOW); + } + + struct y_r_yl2 : gtl_yes {}; + + template + typename enable_if::type>::type>::type, void>::type + yl(rectangle_type& rectangle, typename rectangle_coordinate_type::type value) { + return set(rectangle, VERTICAL, LOW, value); + } + + struct y_r_yh : gtl_yes {}; + + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_coordinate_type::type>::type + yh(const rectangle_type& rectangle) { + return get(rectangle, VERTICAL, HIGH); + } + + struct y_r_yh2 : gtl_yes {}; + + template + typename enable_if::type>::type>::type, void>::type + yh(rectangle_type& rectangle, typename rectangle_coordinate_type::type value) { + return set(rectangle, VERTICAL, HIGH, value); + } + + struct y_r_ll : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + point_data::type> >::type + ll(const rectangle_type& rectangle) { + return point_data::type> (xl(rectangle), yl(rectangle)); + } + + struct y_r_lr : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + point_data::type> >::type + lr(const rectangle_type& rectangle) { + return point_data::type> (xh(rectangle), yl(rectangle)); + } + + struct y_r_ul : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + point_data::type> >::type + ul(const rectangle_type& rectangle) { + return point_data::type> (xl(rectangle), yh(rectangle)); + } + + struct y_r_ur : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + point_data::type> >::type + ur(const rectangle_type& rectangle) { + return point_data::type> (xh(rectangle), yh(rectangle)); + } + + struct y_r_contains : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + contains(const rectangle_type& rectangle, const rectangle_type_2 rectangle_contained, + bool consider_touch = true) { + return contains(horizontal(rectangle), horizontal(rectangle_contained), consider_touch) && + contains(vertical(rectangle), vertical(rectangle_contained), consider_touch); + } + + struct y_r_contains2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_point_concept::type>::type>::type, bool>::type + contains(const rectangle_type& rectangle, const point_type point_contained, + bool consider_touch = true) { + return contains(horizontal(rectangle), x(point_contained), consider_touch) && + contains(vertical(rectangle), y(point_contained), consider_touch); + } + + struct y_r_set_points : gtl_yes {}; + + // set all four coordinates based upon two points + template + typename enable_if< typename gtl_and_4< y_r_set_points, + typename is_mutable_rectangle_concept::type>::type, + typename is_point_concept::type>::type, + typename is_point_concept::type>::type>::type, + rectangle_type>::type & + set_points(rectangle_type& rectangle, const point_type_1& p1, + const point_type_2& p2) { + typedef typename rectangle_coordinate_type::type Unit; + Unit x1(x(p1)); + Unit x2(x(p2)); + Unit y1(y(p1)); + Unit y2(y(p2)); + horizontal(rectangle, construct::type>(x1, x2)); + vertical(rectangle, construct::type>(y1, y2)); + return rectangle; + } + + struct y_r_move : gtl_yes {}; + + // move rectangle by delta in orient + template + typename enable_if< typename gtl_and::type>::type>::type, + rectangle_type>::type & + move(rectangle_type& rectangle, orientation_2d orient, + typename coordinate_traits::type>::coordinate_difference delta) { + typename rectangle_interval_type::type ivl = get(rectangle, orient); + move(ivl, delta); + set(rectangle, orient, ivl); + return rectangle; + } + + struct y_r_convolve : gtl_yes {}; + + // convolve this with b + template + typename enable_if< + typename gtl_and_3< y_r_convolve, + typename is_mutable_rectangle_concept::type>::type, + typename is_rectangle_concept::type>::type>::type, + rectangle_type_1>::type & + convolve(rectangle_type_1& rectangle, + const rectangle_type_2& convolution_rectangle) { + typename rectangle_interval_type::type ivl = horizontal(rectangle); + horizontal(rectangle, convolve(ivl, horizontal(convolution_rectangle))); + ivl = vertical(rectangle); + vertical(rectangle, convolve(ivl, vertical(convolution_rectangle))); + return rectangle; + } + + struct y_r_deconvolve : gtl_yes {}; + + // deconvolve this with b + template + typename enable_if< typename gtl_and_3< y_r_deconvolve, + typename is_mutable_rectangle_concept::type>::type, + typename is_rectangle_concept::type>::type>::type, + rectangle_type_1>::type & + deconvolve(rectangle_type_1& rectangle, const rectangle_type_2& convolution_rectangle) { + typename rectangle_interval_type::type ivl = horizontal(rectangle); + horizontal(rectangle, deconvolve(ivl, horizontal(convolution_rectangle))); + ivl = vertical(rectangle); + vertical(rectangle, deconvolve(ivl, vertical(convolution_rectangle))); + return rectangle; + } + + struct y_r_reconvolve : gtl_yes {}; + + // reflectedConvolve this with b + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + rectangle_type_1>::type & + reflected_convolve(rectangle_type_1& rectangle, const rectangle_type_2& convolution_rectangle) { + typename rectangle_interval_type::type ivl = horizontal(rectangle); + horizontal(rectangle, reflected_convolve(ivl, horizontal(convolution_rectangle))); + ivl = vertical(rectangle); + vertical(rectangle, reflected_convolve(ivl, vertical(convolution_rectangle))); + return rectangle; + } + + struct y_r_redeconvolve : gtl_yes {}; + + // reflectedDeconvolve this with b + // deconvolve this with b + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + rectangle_type_1>::type & + reflected_deconvolve(rectangle_type_1& rectangle, const rectangle_type_2& convolution_rectangle) { + typename rectangle_interval_type::type ivl = horizontal(rectangle); + horizontal(rectangle, reflected_deconvolve(ivl, horizontal(convolution_rectangle))); + ivl = vertical(rectangle); + vertical(rectangle, reflected_deconvolve(ivl, vertical(convolution_rectangle))); + return rectangle; + } + + struct y_r_convolve2 : gtl_yes {}; + + // convolve with point + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_point_concept::type>::type>::type, + rectangle_type>::type & + convolve(rectangle_type& rectangle, const point_type& convolution_point) { + typename rectangle_interval_type::type ivl = horizontal(rectangle); + horizontal(rectangle, convolve(ivl, x(convolution_point))); + ivl = vertical(rectangle); + vertical(rectangle, convolve(ivl, y(convolution_point))); + return rectangle; + } + + struct y_r_deconvolve2 : gtl_yes {}; + + // deconvolve with point + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_point_concept::type>::type>::type, rectangle_type>::type & + deconvolve(rectangle_type& rectangle, const point_type& convolution_point) { + typename rectangle_interval_type::type ivl = horizontal(rectangle); + horizontal(rectangle, deconvolve(ivl, x(convolution_point))); + ivl = vertical(rectangle); + vertical(rectangle, deconvolve(ivl, y(convolution_point))); + return rectangle; + } + + struct y_r_delta : gtl_yes {}; + + // get the magnitude of the interval range depending on orient + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_difference_type::type>::type + delta(const rectangle_type& rectangle, orientation_2d orient) { + return delta(get(rectangle, orient)); + } + + struct y_r_area : gtl_yes {}; + + // get the area of the rectangle + template + typename enable_if< typename gtl_and::type>::type>::type, + typename coordinate_traits::type>::manhattan_area_type>::type + area(const rectangle_type& rectangle) { + typedef typename coordinate_traits::type>::manhattan_area_type area_type; + return (area_type)delta(rectangle, HORIZONTAL) * (area_type)delta(rectangle, VERTICAL); + } + + struct y_r_go : gtl_yes {}; + + // returns the orientation of the longest side + template + typename enable_if::type>::type>::type, + orientation_2d>::type + guess_orientation(const rectangle_type& rectangle) { + return delta(rectangle, HORIZONTAL) >= delta(rectangle, VERTICAL) ? + HORIZONTAL : VERTICAL; + } + + struct y_r_half_p : gtl_yes {}; + + // get the half perimeter of the rectangle + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_difference_type::type>::type + half_perimeter(const rectangle_type& rectangle) { + return delta(rectangle, HORIZONTAL) + delta(rectangle, VERTICAL); + } + + struct y_r_perimeter : gtl_yes {}; + + // get the perimeter of the rectangle + template + typename enable_if< typename gtl_and::type>::type>::type, + typename rectangle_difference_type::type>::type + perimeter(const rectangle_type& rectangle) { + return 2 * half_perimeter(rectangle); + } + + struct y_r_intersects : gtl_yes {}; + + // check if Rectangle b intersects `this` Rectangle + // [in] b Rectangle that will be checked + // [in] considerTouch If true, return true even if b touches the boundary + // [ret] . true if `t` intersects b + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + intersects(const rectangle_type_1& rectangle, const rectangle_type_2& b, bool consider_touch = true) { + return intersects(horizontal(rectangle), horizontal(b), consider_touch) && + intersects(vertical(rectangle), vertical(b), consider_touch); + } + + struct y_r_b_intersect : gtl_yes {}; + + // Check if boundaries of Rectangle b and `this` Rectangle intersect + // [in] b Rectangle that will be checked + // [in] considerTouch If true, return true even if p is on the foundary + // [ret] . true if `t` contains p + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + boundaries_intersect(const rectangle_type_1& rectangle, const rectangle_type_2& b, + bool consider_touch = true) { + return (intersects(rectangle, b, consider_touch) && + !(contains(rectangle, b, !consider_touch)) && + !(contains(b, rectangle, !consider_touch))); + } + + struct y_r_b_abuts : gtl_yes {}; + + // check if b is touching 'this' on the end specified by dir + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + abuts(const rectangle_type_1& rectangle, const rectangle_type_2& b, + direction_2d dir) { + return + abuts(get(rectangle, orientation_2d(dir)), + get(b, orientation_2d(dir)), + direction_1d(dir)) && + intersects(get(rectangle, orientation_2d(dir).get_perpendicular()), + get(b, orientation_2d(dir).get_perpendicular()), true); + } + + struct y_r_b_abuts2 : gtl_yes {}; + + // check if they are touching in the given orientation + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + abuts(const rectangle_type_1& rectangle, const rectangle_type_2& b, + orientation_2d orient) { + return + abuts(get(rectangle, orient), get(b, orient)) && + intersects(get(rectangle, orient.get_perpendicular()), + get(b, orient.get_perpendicular()), true); + } + + struct y_r_b_abuts3 : gtl_yes {}; + + // check if they are touching but not overlapping + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + abuts(const rectangle_type_1& rectangle, const rectangle_type_2& b) { + return abuts(rectangle, b, HORIZONTAL) || abuts(rectangle, b, VERTICAL); + } + + struct y_r_b_intersect2 : gtl_yes {}; + + // intersect rectangle with interval on orient + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_interval_concept::type>::type>::type, + bool>::type + intersect(rectangle_type& rectangle, const interval_type& b, + orientation_2d orient, bool consider_touch = true) { + typename rectangle_interval_type::type ivl = get(rectangle, orient); + if(intersect(ivl, b, consider_touch)) { + set(rectangle, orient, ivl); + return true; + } + return false; + } + + struct y_r_b_intersect3 : gtl_yes {}; + + // clip rectangle to b + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + intersect(rectangle_type_1& rectangle, const rectangle_type_2& b, bool consider_touch = true) { + if(intersects(rectangle, b)) { + intersect(rectangle, horizontal(b), HORIZONTAL, consider_touch); + intersect(rectangle, vertical(b), VERTICAL, consider_touch); + return true; + } + return false; + } + + struct y_r_g_intersect : gtl_yes {}; + + // Sets this to the generalized intersection of this and the given rectangle + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + rectangle_type_1>::type & + generalized_intersect(rectangle_type_1& rectangle, const rectangle_type_2& b) { + typename rectangle_interval_type::type ivl = get(rectangle, HORIZONTAL); + generalized_intersect(ivl, horizontal(b)); + horizontal(rectangle, ivl); + ivl = vertical(rectangle); + generalized_intersect(ivl, vertical(b)); + vertical(rectangle, ivl); + return rectangle; + } + + struct y_r_bloat : gtl_yes {}; + + // bloat the interval specified by orient by bloating + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + bloat(rectangle_type& rectangle, orientation_2d orient, + typename rectangle_coordinate_type::type bloating) { + typename rectangle_interval_type::type ivl = get(rectangle, orient); + bloat(ivl, bloating); + set(rectangle, orient, ivl); + return rectangle; + } + + struct y_r_bloat2 : gtl_yes {}; + + // bloat the Rectangle by bloating + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + bloat(rectangle_type& rectangle, + typename rectangle_coordinate_type::type bloating) { + bloat(rectangle, HORIZONTAL, bloating); + return bloat(rectangle, VERTICAL, bloating); + } + + struct y_r_bloat3 : gtl_yes {}; + + // bloat the interval cooresponding to orient by bloating in dir direction + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + bloat(rectangle_type& rectangle, direction_2d dir, + typename rectangle_coordinate_type::type bloating) { + typename rectangle_interval_type::type ivl = get(rectangle, orientation_2d(dir)); + bloat(ivl, direction_1d(dir), bloating); + set(rectangle, orientation_2d(dir), ivl); + return rectangle; + } + + struct y_r_shrink : gtl_yes {}; + + // shrink the interval specified by orient by bloating + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + shrink(rectangle_type& rectangle, orientation_2d orient, + typename rectangle_coordinate_type::type shrinking) { + return bloat(rectangle, orient, -shrinking); + } + + struct y_r_shrink2 : gtl_yes {}; + + // shrink the Rectangle by bloating + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + shrink(rectangle_type& rectangle, + typename rectangle_coordinate_type::type shrinking) { + return bloat(rectangle, -shrinking); + } + + struct y_r_shrink3 : gtl_yes {}; + + // shrink the interval cooresponding to orient by bloating in dir direction + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + shrink(rectangle_type& rectangle, direction_2d dir, + typename rectangle_coordinate_type::type shrinking) { + return bloat(rectangle, dir, -shrinking); + } + + struct y_r_encompass : gtl_yes {}; + + // encompass interval on orient + template + typename enable_if::type>::type, + typename is_interval_concept::type>::type>::type, + bool>::type + encompass(rectangle_type& rectangle, const interval_type& b, orientation_2d orient) { + typename rectangle_interval_type::type ivl = get(rectangle, orient); + if(encompass(ivl, b)) { + set(rectangle, orient, ivl); + return true; + } + return false; + } + + struct y_r_encompass2 : gtl_yes {}; + + // enlarge rectangle to encompass the Rectangle b + template + typename enable_if< typename gtl_and_3< + y_r_encompass2, + typename is_mutable_rectangle_concept::type>::type, + typename is_rectangle_concept::type>::type >::type, + bool>::type + encompass(rectangle_type_1& rectangle, const rectangle_type_2& b) { + //note that operator | is intentional because both should be called regardless + return encompass(rectangle, horizontal(b), HORIZONTAL) | + encompass(rectangle, vertical(b), VERTICAL); + } + + struct y_r_encompass3 : gtl_yes {}; + + // enlarge rectangle to encompass the point b + template + typename enable_if::type>::type, + typename is_point_concept::type>::type>::type, + bool>::type + encompass(rectangle_type_1& rectangle, const point_type& b) { + typename rectangle_interval_type::type hivl, vivl; + hivl = horizontal(rectangle); + vivl = vertical(rectangle); + //note that operator | is intentional because both should be called regardless + bool retval = encompass(hivl, x(b)) | encompass(vivl, y(b)); + if(retval) { + horizontal(rectangle, hivl); + vertical(rectangle, vivl); + } + return retval; + } + + struct y_r_center : gtl_yes {}; + + // returns the center of the rectangle + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + center(point_type& center_point, const rectangle_type& rectangle) { + center_point = construct(center(horizontal(rectangle)), + center(vertical(rectangle))); + return true; + } + + struct y_r_get_corner : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + get_corner(point_type& corner_point, const rectangle_type& rectangle, direction_2d direction_facing, direction_1d direction_turning) { + typedef typename rectangle_coordinate_type::type Unit; + Unit u1 = get(rectangle, direction_facing); + Unit u2 = get(rectangle, direction_facing.turn(direction_turning)); + if(orientation_2d(direction_facing).to_int()) std::swap(u1, u2); + corner_point = construct(u1, u2); + return true; + } + + struct y_r_get_half : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + rectangle_type>::type + get_half(const rectangle_type& rectangle, direction_2d dir) { + rectangle_type retval(rectangle); + set(retval, orientation_2d(dir), get_half(get(rectangle, orientation_2d(dir)), direction_1d(dir))); + return retval; + } + + struct y_r_join_with : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + join_with(rectangle_type_1& rectangle, const rectangle_type_2& b) { + typedef typename rectangle_interval_type::type Interval1; + typedef typename rectangle_interval_type::type Interval2; + Interval1 hi1 = get(rectangle, HORIZONTAL); + Interval1 vi1 = get(rectangle, VERTICAL); + Interval2 hi2 = get(b, HORIZONTAL), vi2 = get(b, VERTICAL); + Interval1 temp; + if (equivalence(hi1, hi2) && join_with(vi1, vi2)) { + vertical(rectangle, vi1); + return true; + } + if (equivalence(vi1, vi2) && join_with(hi1, hi2)) { + horizontal(rectangle, hi1); + return true; + } + return false; + } + + struct y_r_eda2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_point_concept::type>::type>::type, + typename rectangle_difference_type::type>::type + euclidean_distance(const rectangle_type& lvalue, const point_type& rvalue, orientation_2d orient) { + return euclidean_distance(get(lvalue, orient), get(rvalue, orient)); + } + + struct y_r_eda : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + typename rectangle_difference_type::type>::type + euclidean_distance(const rectangle_type& lvalue, const rectangle_type_2& rvalue, orientation_2d orient) { + return euclidean_distance(get(lvalue, orient), get(rvalue, orient)); + } + + struct y_r_sed : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_point_concept::type>::type>::type, + typename rectangle_difference_type::type>::type + square_euclidean_distance(rectangle_type& lvalue, const point_type& rvalue) { + typename coordinate_traits::type>::coordinate_difference xdist, ydist; + xdist = euclidean_distance(lvalue, rvalue, HORIZONTAL); + ydist = euclidean_distance(lvalue, rvalue, VERTICAL); + return (xdist * xdist) + (ydist * ydist); + } + + struct y_r_sed2 : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept< typename geometry_concept::type>::type>::type, + typename rectangle_difference_type::type>::type + square_euclidean_distance(const rectangle_type& lvalue, const rectangle_type_2& rvalue) { + typename coordinate_traits::type>::coordinate_difference xdist, ydist; + xdist = euclidean_distance(lvalue, rvalue, HORIZONTAL); + ydist = euclidean_distance(lvalue, rvalue, VERTICAL); + return (xdist * xdist) + (ydist * ydist); + } + + struct y_r_edist : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_point_concept::type>::type>::type, + typename rectangle_distance_type::type>::type + euclidean_distance(rectangle_type& lvalue, const point_type& rvalue) { + return std::sqrt((double)(square_euclidean_distance(lvalue, rvalue))); + } + + struct y_r_edist2 : gtl_yes {}; + + template + typename enable_if< typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + typename rectangle_distance_type::type>::type + euclidean_distance(const rectangle_type& lvalue, const rectangle_type_2& rvalue) { + double val = (int)square_euclidean_distance(lvalue, rvalue); + return std::sqrt(val); + } + + struct y_r_mdist : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_point_concept::type>::type>::type, + typename rectangle_difference_type::type>::type + manhattan_distance(rectangle_type& lvalue, const point_type& rvalue) { + typename coordinate_traits::type>::coordinate_difference xdist, ydist; + xdist = euclidean_distance(lvalue, rvalue, HORIZONTAL); + ydist = euclidean_distance(lvalue, rvalue, VERTICAL); + return xdist + ydist; + } + + struct y_r_mdist2 : gtl_yes {}; + + template + typename enable_if< + typename gtl_and_3::type>::type, + typename is_rectangle_concept::type>::type>::type, + typename rectangle_difference_type::type>::type + manhattan_distance(const rectangle_type& lvalue, const rectangle_type_2& rvalue) { + typename coordinate_traits::type>::coordinate_difference xdist, ydist; + xdist = euclidean_distance(lvalue, rvalue, HORIZONTAL); + ydist = euclidean_distance(lvalue, rvalue, VERTICAL); + return xdist + ydist; + } + + struct y_r_scale_up : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + scale_up(rectangle_type& rectangle, + typename coordinate_traits::type>::unsigned_area_type factor) { + typename rectangle_interval_type::type h = horizontal(rectangle); + horizontal(rectangle, scale_up(h, factor)); + typename rectangle_interval_type::type v = vertical(rectangle); + vertical(rectangle, scale_up(v, factor)); + return rectangle; + } + + struct y_r_scale_down : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + scale_down(rectangle_type& rectangle, + typename coordinate_traits::type>::unsigned_area_type factor) { + typename rectangle_interval_type::type h = horizontal(rectangle); + horizontal(rectangle, scale_down(h, factor)); + typename rectangle_interval_type::type v = vertical(rectangle); + vertical(rectangle, scale_down(v, factor)); + return rectangle; + } + + struct y_r_scale : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + scale(rectangle_type& rectangle, const scaling_type& scaling) { + point_data::type> llp(xl(rectangle), yl(rectangle)); + point_data::type> urp(xl(rectangle), yl(rectangle)); + scale(llp, scaling); + scale(urp, scaling); + set_points(rectangle, llp, urp); + return rectangle; + } + + struct y_r_transform : gtl_yes {}; + + template + typename enable_if::type>::type>::type, + rectangle_type>::type & + transform(rectangle_type& rectangle, const transformation_type& transformation) { + point_data::type> llp(xl(rectangle), yl(rectangle)); + point_data::type> urp(xh(rectangle), yh(rectangle)); + transform(llp, transformation); + transform(urp, transformation); + set_points(rectangle, llp, urp); + return rectangle; + } + + template + class less_rectangle_concept { + private: + orientation_2d orient_; + public: + inline less_rectangle_concept(orientation_2d orient = VERTICAL) : orient_(orient) {} + typename enable_if< + typename gtl_and< typename is_rectangle_concept::type>::type, + typename is_rectangle_concept::type>::type>::type, + bool>::type + operator () (const rectangle_type_1& a, + const rectangle_type_2& b) const { + typedef typename rectangle_coordinate_type::type Unit; + Unit vl1 = get(get(a, orient_), LOW); + Unit vl2 = get(get(b, orient_), LOW); + if(vl1 > vl2) return false; + if(vl1 == vl2) { + orientation_2d perp = orient_.get_perpendicular(); + Unit hl1 = get(get(a, perp), LOW); + Unit hl2 = get(get(b, perp), LOW); + if(hl1 > hl2) return false; + if(hl1 == hl2) { + Unit vh1 = get(get(a, orient_), HIGH); + Unit vh2 = get(get(b, orient_), HIGH); + if(vh1 > vh2) return false; + if(vh1 == vh2) { + Unit hh1 = get(get(a, perp), HIGH); + Unit hh2 = get(get(b, perp), HIGH); + return hh1 < hh2; + } + } + } + return true; + } + + }; + + template + template + inline void rectangle_data::set(orientation_2d orient, const interval_type_1& interval) { + assign(ranges_[orient.to_int()], interval); + } + + template + template + rectangle_data& rectangle_data::operator=(const T2& rvalue) { + assign(*this, rvalue); + return *this; + } + + template + template + bool rectangle_data::operator==(const T2& rvalue) const { + return equivalence(*this, rvalue); + } + + template + struct geometry_concept > { + typedef rectangle_concept type; + }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_data.hpp new file mode 100644 index 0000000..5a1f99e --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_data.hpp @@ -0,0 +1,63 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_RECTANGLE_DATA_HPP +#define BOOST_POLYGON_RECTANGLE_DATA_HPP + +#include "isotropy.hpp" +//interval +#include "interval_data.hpp" + +namespace boost { namespace polygon{ + +template +class rectangle_data { +public: + typedef T coordinate_type; + typedef interval_data interval_type; + inline rectangle_data():ranges_() {} + inline rectangle_data(T xl, T yl, T xh, T yh):ranges_() { + if(xl > xh) std::swap(xl, xh); + if(yl > yh) std::swap(yl, yh); + ranges_[HORIZONTAL] = interval_data(xl, xh); + ranges_[VERTICAL] = interval_data(yl, yh); + } + template + inline rectangle_data(const interval_type_1& hrange, + const interval_type_2& vrange):ranges_() { + set(HORIZONTAL, hrange); set(VERTICAL, vrange); } + + inline rectangle_data(const rectangle_data& that):ranges_() { (*this) = that; } + inline rectangle_data& operator=(const rectangle_data& that) { + ranges_[0] = that.ranges_[0]; ranges_[1] = that.ranges_[1]; return *this; + } + template + inline rectangle_data& operator=(const T2& rvalue); + + template + inline bool operator==(const T2& rvalue) const; + template + inline bool operator!=(const T2& rvalue) const { return !((*this) == rvalue); } + + inline interval_data get(orientation_2d orient) const { + return ranges_[orient.to_int()]; } + inline coordinate_type get(direction_2d dir) const { + return ranges_[orientation_2d(dir).to_int()].get(direction_1d(dir)); + } + inline void set(direction_2d dir, coordinate_type value) { + return ranges_[orientation_2d(dir).to_int()].set(direction_1d(dir), value); + } + template + inline void set(orientation_2d orient, const interval_type_1& interval); +private: + interval_data ranges_[2]; +}; + + +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_traits.hpp new file mode 100644 index 0000000..bd49474 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/rectangle_traits.hpp @@ -0,0 +1,40 @@ +/* + Copyright 2008 Intel Corporation + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ +#ifndef BOOST_POLYGON_RECTANGLE_TRAITS_HPP +#define BOOST_POLYGON_RECTANGLE_TRAITS_HPP + +#include "isotropy.hpp" + +namespace boost { namespace polygon{ + + template + struct rectangle_traits {}; + template + struct rectangle_traits {}; + + template + struct rectangle_traits::type> { + typedef typename T::coordinate_type coordinate_type; + typedef typename T::interval_type interval_type; + static inline interval_type get(const T& rectangle, orientation_2d orient) { + return rectangle.get(orient); } + }; + + template + struct rectangle_mutable_traits { + template + static inline void set(T& rectangle, orientation_2d orient, const T2& interval) { + rectangle.set(orient, interval); } + template + static inline T construct(const T2& interval_horizontal, + const T3& interval_vertical) { + return T(interval_horizontal, interval_vertical); } + }; +} +} +#endif diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_concept.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_concept.hpp new file mode 100644 index 0000000..2f41c1b --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_concept.hpp @@ -0,0 +1,696 @@ +// Boost.Polygon library segment_concept.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_SEGMENT_CONCEPT_HPP +#define BOOST_POLYGON_SEGMENT_CONCEPT_HPP + +#include "isotropy.hpp" +#include "segment_traits.hpp" +#include "rectangle_concept.hpp" + +namespace boost { +namespace polygon { + +struct segment_concept {}; + +template +struct is_segment_concept { + typedef gtl_no type; +}; + +template <> +struct is_segment_concept { + typedef gtl_yes type; +}; + +template +struct is_mutable_segment_concept { + typedef gtl_no type; +}; + +template <> +struct is_mutable_segment_concept { + typedef gtl_yes type; +}; + +template +struct segment_distance_type_by_concept { + typedef void type; +}; + +template +struct segment_distance_type_by_concept { + typedef typename coordinate_traits< + typename segment_traits::coordinate_type + >::coordinate_distance type; +}; + +template +struct segment_distance_type { + typedef typename segment_distance_type_by_concept< + GeometryType, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +template +struct segment_point_type_by_concept { + typedef void type; +}; + +template +struct segment_point_type_by_concept { + typedef typename segment_traits::point_type type; +}; + +template +struct segment_point_type { + typedef typename segment_point_type_by_concept< + GeometryType, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +template +struct segment_coordinate_type_by_concept { + typedef void type; +}; + +template +struct segment_coordinate_type_by_concept { + typedef typename segment_traits::coordinate_type type; +}; + +template +struct segment_coordinate_type { + typedef typename segment_coordinate_type_by_concept< + GeometryType, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type type; +}; + +struct y_s_get : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_get, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +typename segment_point_type::type>::type +get(const Segment& segment, direction_1d dir) { + return segment_traits::get(segment, dir); +} + +struct y_s_set : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_set, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +void>::type set(Segment& segment, direction_1d dir, const Point& point) { + segment_mutable_traits::set(segment, dir, point); +} + +struct y_s_construct : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_4< + y_s_construct, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type construct(const Point1& low, const Point2& high) { + return segment_mutable_traits::construct(low, high); +} + +struct y_s_copy_construct : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_copy_construct, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +Segment1>::type copy_construct(const Segment2& segment) { + return construct(get(segment, LOW), get(segment, HIGH)); +} + +struct y_s_assign : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_assign, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +Segment1>::type& assign(Segment1& segment1, const Segment2& segment2) { + return segment1 = copy_construct(segment2); +} + +struct y_s_equivalence : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_equivalence, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +bool>::type equivalence(const Segment1& segment1, const Segment2& segment2) { + return get(segment1, LOW) == get(segment2, LOW) && + get(segment1, HIGH) == get(segment2, HIGH); +} + +struct y_s_low : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_low, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +typename segment_point_type::type>::type low(const Segment& segment) { + return get(segment, LOW); +} + +struct y_s_high : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_high, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +typename segment_point_type::type>::type high(const Segment& segment) { + return get(segment, HIGH); +} + +struct y_s_center : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_center, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +typename segment_point_type::type>::type +center(const Segment& segment) { + return construct::type>( + (x(high(segment)) + x(low(segment)))/2, + (y(high(segment)) + y(low(segment)))/2); +} + +struct y_s_low2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_low2, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +void>::type low(Segment& segment, const Point& point) { + set(segment, LOW, point); +} + +struct y_s_high2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_high2, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +void>::type high(Segment& segment, const Point& point) { + set(segment, HIGH, point); +} + +struct y_s_orientation1 : gtl_yes {}; + +// -1 for CW, 0 for collinear and 1 for CCW. +template +typename enable_if< + typename gtl_and_3< + y_s_orientation1, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +int>::type orientation(const Segment1& segment1, const Segment2& segment2) { + typedef typename coordinate_traits< + typename segment_traits::coordinate_type + >::manhattan_area_type int_x2; + typedef typename coordinate_traits< + typename segment_traits::coordinate_type + >::unsigned_area_type uint_x2; + int_x2 a1 = (int_x2)x(high(segment1)) - (int_x2)x(low(segment1)); + int_x2 b1 = (int_x2)y(high(segment1)) - (int_x2)y(low(segment1)); + int_x2 a2 = (int_x2)x(high(segment2)) - (int_x2)x(low(segment2)); + int_x2 b2 = (int_x2)y(high(segment2)) - (int_x2)y(low(segment2)); + + int sign1 = 0; + int sign2 = 0; + if (a1 && b2) + sign1 = ((a1 > 0) ^ (b2 > 0)) ? -1 : 1; + if (a2 && b1) + sign2 = ((a2 > 0) ^ (b1 > 0)) ? -1 : 1; + + if (sign1 != sign2) + return (sign1 < sign2) ? -1 : 1; + uint_x2 a3 = (uint_x2)(a1 < 0 ? -a1 : a1) * (uint_x2)(b2 < 0 ? -b2 : b2); + uint_x2 b3 = (uint_x2)(b1 < 0 ? -b1 : b1) * (uint_x2)(a2 < 0 ? -a2 : a2); + if (a3 == b3) + return 0; + return ((a3 < b3) ^ (sign1 == 1)) ? 1 : -1; +} + +struct y_s_orientation2 : gtl_yes {}; + +// -1 for right, 0 for collinear and 1 for left. +template +typename enable_if< + typename gtl_and_3< + y_s_orientation2, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +int>::type orientation(const Segment& segment, const Point& point) { + Segment segment2 = construct(high(segment), point); + return orientation(segment, segment2); +} + +struct y_s_contains : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_contains, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +bool>::type contains(const Segment& segment, + const Point& point, bool consider_touch = true ) { + if (orientation(segment, point)) + return false; + rectangle_data::type> rect; + set_points(rect, low(segment), high(segment)); + if (!contains(rect, point, true)) + return false; + if (!consider_touch && + (equivalence(low(segment), point) || + equivalence(high(segment), point))) + return false; + return true; +} + +struct y_s_contains2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_contains2, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +bool>::type contains(const Segment1& segment1, + const Segment2& segment2, bool consider_touch = true) { + return contains(segment1, get(segment2, LOW), consider_touch) && + contains(segment1, get(segment2, HIGH), consider_touch); +} + +struct y_s_length : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_length, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +typename segment_distance_type::type>::type +length(const Segment& segment) { + return euclidean_distance(low(segment), high(segment)); +} + +struct y_s_scale_up : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_scale_up, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type& scale_up(Segment& segment, + typename coordinate_traits< + typename segment_coordinate_type::type + >::unsigned_area_type factor) { + typename segment_point_type::type l = low(segment); + typename segment_point_type::type h = high(segment); + low(segment, scale_up(l, factor)); + high(segment, scale_up(h, factor)); + return segment; +} + +struct y_s_scale_down : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_scale_down, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type& scale_down(Segment& segment, + typename coordinate_traits< + typename segment_coordinate_type::type + >::unsigned_area_type factor) { + typename segment_point_type::type l = low(segment); + typename segment_point_type::type h = high(segment); + low(segment, scale_down(l, factor)); + high(segment, scale_down(h, factor)); + return segment; +} + +struct y_s_scale : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_scale, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type& scale(Segment& segment, const Scale& sc) { + typename segment_point_type::type l = low(segment); + typename segment_point_type::type h = high(segment); + low(segment, scale(l, sc)); + high(segment, scale(h, sc)); + return segment; +} + +struct y_s_transform : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_transform, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type& transform(Segment& segment, const Transform& tr) { + typename segment_point_type::type l = low(segment); + typename segment_point_type::type h = high(segment); + low(segment, transform(l, tr)); + high(segment, transform(h, tr)); + return segment; +} + +struct y_s_move : gtl_yes {}; + +template +typename enable_if< + typename gtl_and< + y_s_move, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type& move(Segment& segment, orientation_2d orient, + typename segment_coordinate_type::type displacement) { + typename segment_point_type::type l = low(segment); + typename segment_point_type::type h = high(segment); + low(segment, move(l, orient, displacement)); + high(segment, move(h, orient, displacement)); + return segment; +} + +struct y_s_convolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_convolve, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type& convolve(Segment& segment, const Point& point) { + typename segment_point_type::type l = low(segment); + typename segment_point_type::type h = high(segment); + low(segment, convolve(l, point)); + high(segment, convolve(h, point)); + return segment; +} + +struct y_s_deconvolve : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_deconvolve, + typename is_mutable_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +Segment>::type& deconvolve(Segment& segment, const Point& point) { + typename segment_point_type::type l = low(segment); + typename segment_point_type::type h = high(segment); + low(segment, deconvolve(l, point)); + high(segment, deconvolve(h, point)); + return segment; +} + +struct y_s_abuts1 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_abuts1, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +bool>::type abuts(const Segment1& segment1, + const Segment2& segment2, direction_1d dir) { + return dir.to_int() ? equivalence(low(segment2) , high(segment1)) : + equivalence(low(segment1) , high(segment2)); +} + +struct y_s_abuts2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_abuts2, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +bool>::type abuts(const Segment1& segment1, const Segment2& segment2) { + return abuts(segment1, segment2, HIGH) || abuts(segment1, segment2, LOW); +} + +struct y_s_e_intersects : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_e_intersects, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +bool +>::type intersects(const Segment1& segment1, const Segment2& segment2, + bool consider_touch = true) { + rectangle_data::type> rect1, rect2; + set_points(rect1, low(segment1), high(segment1)); + set_points(rect2, low(segment2), high(segment2)); + // Check if axis-parallel rectangles containing segments intersect. + if (!intersects(rect1, rect2, true)) + return false; + int or1_1 = orientation(segment1, low(segment2)); + int or1_2 = orientation(segment1, high(segment2)); + if (or1_1 * or1_2 > 0) + return false; + int or2_1 = orientation(segment2, low(segment1)); + int or2_2 = orientation(segment2, high(segment1)); + if (or2_1 * or2_2 > 0) + return false; + if (consider_touch || (or1_1 && or1_2) || (or2_1 && or2_2)) + return true; + if (or1_1 || or1_2) + return false; + return intersects(vertical(rect1), vertical(rect2), false) || + intersects(horizontal(rect1), horizontal(rect2), false); +} + +struct y_s_e_dist : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_e_dist, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, +typename segment_distance_type::type>::type +euclidean_distance(const Segment& segment, const Point& point) { + typedef typename segment_distance_type::type Unit; + Unit x1 = x(low(segment)); + Unit y1 = y(low(segment)); + Unit x2 = x(high(segment)); + Unit y2 = y(high(segment)); + Unit X = x(point); + Unit Y = y(point); + Unit A = X - x1; + Unit B = Y - y1; + Unit C = x2 - x1; + Unit D = y2 - y1; + Unit param = (A * C + B * D); + Unit length_sq = C * C + D * D; + if (param > length_sq) { + return euclidean_distance(high(segment), point); + } else if (param < 0.0) { + return euclidean_distance(low(segment), point); + } + if (length_sq == 0.0) + return 0.0; + Unit denom = std::sqrt(length_sq); + Unit result = (A * D - C * B) / denom; + return (result < 0.0) ? -result : result; +} + +struct y_s_e_dist2 : gtl_yes {}; + +template +typename enable_if< + typename gtl_and_3< + y_s_e_dist2, + typename is_segment_concept< + typename geometry_concept::type + >::type, + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, +typename segment_distance_type::type>::type +euclidean_distance(const Segment1& segment1, const Segment2& segment2) { + if (intersects(segment1, segment2)) + return 0.0; + typename segment_distance_type::type + result1 = euclidean_distance(segment1, low(segment2)), + result2 = euclidean_distance(segment1, high(segment2)), + result3 = euclidean_distance(segment2, low(segment1)), + result4 = euclidean_distance(segment2, high(segment1)); + if (result2 < result1) + result1 = result2; + if (result4 < result3) + result3 = result4; + return (result1 < result3) ? result1 : result3; +} +} // polygon +} // boost + +#endif // BOOST_POLYGON_SEGMENT_CONCEPT_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_data.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_data.hpp new file mode 100644 index 0000000..2bcfe11 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_data.hpp @@ -0,0 +1,121 @@ +// Boost.Polygon library segment_data.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_SEGMENT_DATA_HPP +#define BOOST_POLYGON_SEGMENT_DATA_HPP + +#include "isotropy.hpp" +#include "segment_concept.hpp" + +namespace boost { +namespace polygon { + +template +class segment_data { + public: + typedef T coordinate_type; + typedef point_data point_type; + + segment_data() +#ifndef BOOST_POLYGON_MSVC + : points_() +#endif + {} + + segment_data(const point_type& low, const point_type& high) { + points_[LOW] = low; + points_[HIGH] = high; + } + + segment_data(const segment_data& that) { + points_[0] = that.points_[0]; + points_[1] = that.points_[1]; + } + + segment_data& operator=(const segment_data& that) { + points_[0] = that.points_[0]; + points_[1] = that.points_[1]; + return *this; + } + + template + segment_data& operator=(const SegmentType& that) { + assign(*this, that); + return *this; + } + + point_type get(direction_1d dir) const { + return points_[dir.to_int()]; + } + + void set(direction_1d dir, const point_type& point) { + points_[dir.to_int()] = point; + } + + point_type low() const { + return points_[LOW]; + } + + segment_data& low(const point_type& point) { + points_[LOW] = point; + return *this; + } + + point_type high() const { + return points_[HIGH]; + } + + segment_data& high(const point_type& point) { + points_[HIGH] = point; + return *this; + } + + bool operator==(const segment_data& that) const { + return (points_[0] == that.points_[0]) && + (points_[1] == that.points_[1]); + } + + bool operator!=(const segment_data& that) const { + return (points_[0] != that.points_[0]) || + (points_[1] != that.points_[1]); + } + + bool operator<(const segment_data& that) const { + if (points_[0] != that.points_[0]) { + return points_[0] < that.points_[0]; + } + return points_[1] < that.points_[1]; + } + + bool operator<=(const segment_data& that) const { + return !(that < *this); + } + + bool operator>(const segment_data& that) const { + return that < *this; + } + + bool operator>=(const segment_data& that) const { + return !((*this) < that); + } + + private: + point_type points_[2]; +}; + +template +struct geometry_concept > { + typedef segment_concept type; +}; +} // polygon +} // boost + +#endif // BOOST_POLYGON_SEGMENT_DATA_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_traits.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_traits.hpp new file mode 100644 index 0000000..cb092bd --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_traits.hpp @@ -0,0 +1,50 @@ +// Boost.Polygon library segment_traits.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_SEGMENT_TRAITS_HPP +#define BOOST_POLYGON_SEGMENT_TRAITS_HPP + +#include "isotropy.hpp" + +namespace boost { +namespace polygon { + +template +struct segment_traits { + typedef Segment segment_type; + typedef typename segment_type::point_type point_type; + typedef typename segment_type::coordinate_type coordinate_type; + + static point_type get( + const segment_type& segment, direction_1d dir) { + return segment.get(dir); + } +}; + +template +struct segment_mutable_traits { + typedef Segment segment_type; + typedef typename segment_type::point_type point_type; + typedef typename segment_type::coordinate_type coordinate_type; + + static void set( + segment_type& segment, direction_1d dir, const point_type& point) { + segment.set(dir, point); + } + + static segment_type construct(const point_type& low, const point_type& high) { + return segment_type(low, high); + } +}; +} // polygon +} // boost + +#endif // BOOST_POLYGON_SEGMENT_TRAITS_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_utils.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_utils.hpp new file mode 100644 index 0000000..075f7c3 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/segment_utils.hpp @@ -0,0 +1,164 @@ +/* + Copyright 2012 Lucanus Simonson + + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ + +#ifndef BOOST_POLYGON_SEGMENT_UTILS_HPP +#define BOOST_POLYGON_SEGMENT_UTILS_HPP + +#include +#include +#include + +#include "detail/scan_arbitrary.hpp" +#include "isotropy.hpp" +#include "rectangle_concept.hpp" +#include "segment_concept.hpp" + +namespace boost { +namespace polygon { + +template +typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type + >::type, + void +>::type +intersect_segments( + std::vector >& result, + SegmentIterator first, SegmentIterator last) { + typedef typename segment_traits::coordinate_type Unit; + typedef typename scanline_base::Point Point; + typedef typename scanline_base::half_edge half_edge; + typedef int segment_id; + std::vector > half_edges; + std::vector > half_edges_out; + segment_id id_in = 0; + half_edges.reserve(std::distance(first, last)); + for (; first != last; ++first) { + Point l, h; + assign(l, low(*first)); + assign(h, high(*first)); + half_edges.push_back(std::make_pair(half_edge(l, h), id_in++)); + } + half_edges_out.reserve(half_edges.size()); + // Apparently no need to pre-sort data when calling validate_scan. + if (half_edges.size() != 0) { + line_intersection::validate_scan( + half_edges_out, half_edges.begin(), half_edges.end()); + } + + result.reserve(result.size() + half_edges_out.size()); + for (std::size_t i = 0; i < half_edges_out.size(); ++i) { + std::size_t id = (std::size_t)(half_edges_out[i].second); + Point l = half_edges_out[i].first.first; + Point h = half_edges_out[i].first.second; + result.push_back(std::make_pair(id, construct(l, h))); + } +} + +template +typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< + typename SegmentContainer::value_type + >::type + >::type + >::type + >::type, + void +>::type +intersect_segments( + SegmentContainer& result, + SegmentIterator first, + SegmentIterator last) { + typedef typename SegmentContainer::value_type segment_type; + typedef typename segment_traits::coordinate_type Unit; + typedef typename scanline_base::Point Point; + typedef typename scanline_base::half_edge half_edge; + typedef int segment_id; + std::vector > half_edges; + std::vector > half_edges_out; + segment_id id_in = 0; + half_edges.reserve(std::distance(first, last)); + for (; first != last; ++first) { + Point l, h; + assign(l, low(*first)); + assign(h, high(*first)); + half_edges.push_back(std::make_pair(half_edge(l, h), id_in++)); + } + half_edges_out.reserve(half_edges.size()); + // Apparently no need to pre-sort data when calling validate_scan. + if (half_edges.size() != 0) { + line_intersection::validate_scan( + half_edges_out, half_edges.begin(), half_edges.end()); + } + + result.reserve(result.size() + half_edges_out.size()); + for (std::size_t i = 0; i < half_edges_out.size(); ++i) { + Point l = half_edges_out[i].first.first; + Point h = half_edges_out[i].first.second; + result.push_back(construct(l, h)); + } +} + +template +typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_rectangle_concept< + typename geometry_concept::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type + >::type, + bool +>::type +envelope_segments( + Rectangle& rect, + SegmentIterator first, + SegmentIterator last) { + for (SegmentIterator it = first; it != last; ++it) { + if (it == first) { + set_points(rect, low(*it), high(*it)); + } else { + encompass(rect, low(*it)); + encompass(rect, high(*it)); + } + } + return first != last; +} +} // polygon +} // boost + +#endif // BOOST_POLYGON_SEGMENT_UTILS_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/transform.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/transform.hpp new file mode 100644 index 0000000..1bb0cd9 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/transform.hpp @@ -0,0 +1,476 @@ +// Boost.Polygon library transform.hpp header file + +// Copyright (c) Intel Corporation 2008. +// Copyright (c) 2008-2012 Simonson Lucanus. +// Copyright (c) 2012-2012 Andrii Sydorchuk. + +// See http://www.boost.org for updates, documentation, and revision history. +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_POLYGON_TRANSFORM_HPP +#define BOOST_POLYGON_TRANSFORM_HPP + +#include "isotropy.hpp" + +namespace boost { +namespace polygon { +// Transformation of Coordinate System. +// Enum meaning: +// Select which direction_2d to change the positive direction of each +// axis in the old coordinate system to map it to the new coordiante system. +// The first direction_2d listed for each enum is the direction to map the +// positive horizontal direction to. +// The second direction_2d listed for each enum is the direction to map the +// positive vertical direction to. +// The zero position bit (LSB) indicates whether the horizontal axis flips +// when transformed. +// The 1st postion bit indicates whether the vertical axis flips when +// transformed. +// The 2nd position bit indicates whether the horizontal and vertical axis +// swap positions when transformed. +// Enum Values: +// 000 EAST NORTH +// 001 WEST NORTH +// 010 EAST SOUTH +// 011 WEST SOUTH +// 100 NORTH EAST +// 101 SOUTH EAST +// 110 NORTH WEST +// 111 SOUTH WEST +class axis_transformation { + public: + enum ATR { +#ifdef BOOST_POLYGON_ENABLE_DEPRECATED + EN = 0, + WN = 1, + ES = 2, + WS = 3, + NE = 4, + SE = 5, + NW = 6, + SW = 7, +#endif + NULL_TRANSFORM = 0, + BEGIN_TRANSFORM = 0, + EAST_NORTH = 0, + WEST_NORTH = 1, FLIP_X = 1, + EAST_SOUTH = 2, FLIP_Y = 2, + WEST_SOUTH = 3, FLIP_XY = 3, + NORTH_EAST = 4, SWAP_XY = 4, + SOUTH_EAST = 5, ROTATE_LEFT = 5, + NORTH_WEST = 6, ROTATE_RIGHT = 6, + SOUTH_WEST = 7, FLIP_SWAP_XY = 7, + END_TRANSFORM = 7 + }; + + // Individual axis enum values indicate which axis an implicit individual + // axis will be mapped to. + // The value of the enum paired with an axis provides the information + // about what the axis will transform to. + // Three individual axis values, one for each axis, are equivalent to one + // ATR enum value, but easier to work with because they are independent. + // Converting to and from the individual axis values from the ATR value + // is a convenient way to implement tranformation related functionality. + // Enum meanings: + // PX: map to positive x axis + // NX: map to negative x axis + // PY: map to positive y axis + // NY: map to negative y axis + enum INDIVIDUAL_AXIS { + PX = 0, + NX = 1, + PY = 2, + NY = 3 + }; + + axis_transformation() : atr_(NULL_TRANSFORM) {} + explicit axis_transformation(ATR atr) : atr_(atr) {} + axis_transformation(const axis_transformation& atr) : atr_(atr.atr_) {} + + explicit axis_transformation(const orientation_2d& orient) { + const ATR tmp[2] = { + NORTH_EAST, // sort x, then y + EAST_NORTH // sort y, then x + }; + atr_ = tmp[orient.to_int()]; + } + + explicit axis_transformation(const direction_2d& dir) { + const ATR tmp[4] = { + SOUTH_EAST, // sort x, then y + NORTH_EAST, // sort x, then y + EAST_SOUTH, // sort y, then x + EAST_NORTH // sort y, then x + }; + atr_ = tmp[dir.to_int()]; + } + + // assignment operator + axis_transformation& operator=(const axis_transformation& a) { + atr_ = a.atr_; + return *this; + } + + // assignment operator + axis_transformation& operator=(const ATR& atr) { + atr_ = atr; + return *this; + } + + // equivalence operator + bool operator==(const axis_transformation& a) const { + return atr_ == a.atr_; + } + + // inequivalence operator + bool operator!=(const axis_transformation& a) const { + return !(*this == a); + } + + // ordering + bool operator<(const axis_transformation& a) const { + return atr_ < a.atr_; + } + + // concatenate this with that + axis_transformation& operator+=(const axis_transformation& a) { + bool abit2 = (a.atr_ & 4) != 0; + bool abit1 = (a.atr_ & 2) != 0; + bool abit0 = (a.atr_ & 1) != 0; + bool bit2 = (atr_ & 4) != 0; + bool bit1 = (atr_ & 2) != 0; + bool bit0 = (atr_ & 1) != 0; + int indexes[2][2] = { + { (int)bit2, (int)(!bit2) }, + { (int)abit2, (int)(!abit2) } + }; + int zero_bits[2][2] = { + {bit0, bit1}, {abit0, abit1} + }; + int nbit1 = zero_bits[0][1] ^ zero_bits[1][indexes[0][1]]; + int nbit0 = zero_bits[0][0] ^ zero_bits[1][indexes[0][0]]; + indexes[0][0] = indexes[1][indexes[0][0]]; + indexes[0][1] = indexes[1][indexes[0][1]]; + int nbit2 = indexes[0][0] & 1; // swap xy + atr_ = (ATR)((nbit2 << 2) + (nbit1 << 1) + nbit0); + return *this; + } + + // concatenation operator + axis_transformation operator+(const axis_transformation& a) const { + axis_transformation retval(*this); + return retval+=a; + } + + // populate_axis_array writes the three INDIVIDUAL_AXIS values that the + // ATR enum value of 'this' represent into axis_array + void populate_axis_array(INDIVIDUAL_AXIS axis_array[]) const { + bool bit2 = (atr_ & 4) != 0; + bool bit1 = (atr_ & 2) != 0; + bool bit0 = (atr_ & 1) != 0; + axis_array[1] = (INDIVIDUAL_AXIS)(((int)(!bit2) << 1) + bit1); + axis_array[0] = (INDIVIDUAL_AXIS)(((int)(bit2) << 1) + bit0); + } + + // it is recommended that the directions stored in an array + // in the caller code for easier isotropic access by orientation value + void get_directions(direction_2d& horizontal_dir, + direction_2d& vertical_dir) const { + bool bit2 = (atr_ & 4) != 0; + bool bit1 = (atr_ & 2) != 0; + bool bit0 = (atr_ & 1) != 0; + vertical_dir = direction_2d((direction_2d_enum)(((int)(!bit2) << 1) + !bit1)); + horizontal_dir = direction_2d((direction_2d_enum)(((int)(bit2) << 1) + !bit0)); + } + + // combine_axis_arrays concatenates this_array and that_array overwriting + // the result into this_array + static void combine_axis_arrays(INDIVIDUAL_AXIS this_array[], + const INDIVIDUAL_AXIS that_array[]) { + int indexes[2] = { this_array[0] >> 1, this_array[1] >> 1 }; + int zero_bits[2][2] = { + { this_array[0] & 1, this_array[1] & 1 }, + { that_array[0] & 1, that_array[1] & 1 } + }; + this_array[0] = (INDIVIDUAL_AXIS)((int)this_array[0] | + ((int)zero_bits[0][0] ^ + (int)zero_bits[1][indexes[0]])); + this_array[1] = (INDIVIDUAL_AXIS)((int)this_array[1] | + ((int)zero_bits[0][1] ^ + (int)zero_bits[1][indexes[1]])); + } + + // write_back_axis_array converts an array of three INDIVIDUAL_AXIS values + // to the ATR enum value and sets 'this' to that value + void write_back_axis_array(const INDIVIDUAL_AXIS this_array[]) { + int bit2 = ((int)this_array[0] & 2) != 0; // swap xy + int bit1 = ((int)this_array[1] & 1); + int bit0 = ((int)this_array[0] & 1); + atr_ = ATR((bit2 << 2) + (bit1 << 1) + bit0); + } + + // behavior is deterministic but undefined in the case where illegal + // combinations of directions are passed in. + axis_transformation& set_directions(const direction_2d& horizontal_dir, + const direction_2d& vertical_dir) { + int bit2 = (static_cast(horizontal_dir).to_int()) != 0; + int bit1 = !(vertical_dir.to_int() & 1); + int bit0 = !(horizontal_dir.to_int() & 1); + atr_ = ATR((bit2 << 2) + (bit1 << 1) + bit0); + return *this; + } + + // transform the three coordinates by reference + template + void transform(coordinate_type& x, coordinate_type& y) const { + int bit2 = (atr_ & 4) != 0; + int bit1 = (atr_ & 2) != 0; + int bit0 = (atr_ & 1) != 0; + x *= -((bit0 << 1) - 1); + y *= -((bit1 << 1) - 1); + predicated_swap(bit2 != 0, x, y); + } + + // invert this axis_transformation + axis_transformation& invert() { + int bit2 = ((atr_ & 4) != 0); + int bit1 = ((atr_ & 2) != 0); + int bit0 = ((atr_ & 1) != 0); + // swap bit 0 and bit 1 if bit2 is 1 + predicated_swap(bit2 != 0, bit0, bit1); + bit1 = bit1 << 1; + atr_ = (ATR)(atr_ & (32+16+8+4)); // mask away bit0 and bit1 + atr_ = (ATR)(atr_ | bit0 | bit1); + return *this; + } + + // get the inverse axis_transformation of this + axis_transformation inverse() const { + axis_transformation retval(*this); + return retval.invert(); + } + + private: + ATR atr_; +}; + +// Scaling object to be used to store the scale factor for each axis. +// For use by the transformation object, in that context the scale factor +// is the amount that each axis scales by when transformed. +template +class anisotropic_scale_factor { + public: + anisotropic_scale_factor() { + scale_[0] = 1; + scale_[1] = 1; + } + anisotropic_scale_factor(scale_factor_type xscale, + scale_factor_type yscale) { + scale_[0] = xscale; + scale_[1] = yscale; + } + + // get a component of the anisotropic_scale_factor by orientation + scale_factor_type get(orientation_2d orient) const { + return scale_[orient.to_int()]; + } + + // set a component of the anisotropic_scale_factor by orientation + void set(orientation_2d orient, scale_factor_type value) { + scale_[orient.to_int()] = value; + } + + scale_factor_type x() const { + return scale_[HORIZONTAL]; + } + + scale_factor_type y() const { + return scale_[VERTICAL]; + } + + void x(scale_factor_type value) { + scale_[HORIZONTAL] = value; + } + + void y(scale_factor_type value) { + scale_[VERTICAL] = value; + } + + // concatination operator (convolve scale factors) + anisotropic_scale_factor operator+(const anisotropic_scale_factor& s) const { + anisotropic_scale_factor retval(*this); + return retval += s; + } + + // concatinate this with that + const anisotropic_scale_factor& operator+=( + const anisotropic_scale_factor& s) { + scale_[0] *= s.scale_[0]; + scale_[1] *= s.scale_[1]; + return *this; + } + + // transform this scale with an axis_transform + anisotropic_scale_factor& transform(axis_transformation atr) { + direction_2d dirs[2]; + atr.get_directions(dirs[0], dirs[1]); + scale_factor_type tmp[2] = {scale_[0], scale_[1]}; + for (int i = 0; i < 2; ++i) { + scale_[orientation_2d(dirs[i]).to_int()] = tmp[i]; + } + return *this; + } + + // scale the two coordinates + template + void scale(coordinate_type& x, coordinate_type& y) const { + x = scaling_policy::round( + (scale_factor_type)x * get(HORIZONTAL)); + y = scaling_policy::round( + (scale_factor_type)y * get(HORIZONTAL)); + } + + // invert this scale factor to give the reverse scale factor + anisotropic_scale_factor& invert() { + x(1/x()); + y(1/y()); + return *this; + } + + private: + scale_factor_type scale_[2]; +}; + +// Transformation object, stores and provides services for transformations. +// Consits of axis transformation, scale factor and translation. +// The tranlation is the position of the origin of the new coordinate system of +// in the old system. Coordinates are scaled before they are transformed. +template +class transformation { + public: + transformation() : atr_(), p_(0, 0) {} + explicit transformation(axis_transformation atr) : atr_(atr), p_(0, 0) {} + explicit transformation(axis_transformation::ATR atr) : atr_(atr), p_(0, 0) {} + transformation(const transformation& tr) : atr_(tr.atr_), p_(tr.p_) {} + + template + explicit transformation(const point_type& p) : atr_(), p_(0, 0) { + set_translation(p); + } + + template + transformation(axis_transformation atr, + const point_type& p) : atr_(atr), p_(0, 0) { + set_translation(p); + } + + template + transformation(axis_transformation atr, + const point_type& referencePt, + const point_type& destinationPt) : atr_(), p_(0, 0) { + transformation tmp(referencePt); + transformation rotRef(atr); + transformation tmpInverse = tmp.inverse(); + point_type decon(referencePt); + deconvolve(decon, destinationPt); + transformation displacement(decon); + tmp += rotRef; + tmp += tmpInverse; + tmp += displacement; + (*this) = tmp; + } + + // equivalence operator + bool operator==(const transformation& tr) const { + return (atr_ == tr.atr_) && (p_ == tr.p_); + } + + // inequivalence operator + bool operator!=(const transformation& tr) const { + return !(*this == tr); + } + + // ordering + bool operator<(const transformation& tr) const { + return (atr_ < tr.atr_) || ((atr_ == tr.atr_) && (p_ < tr.p_)); + } + + // concatenation operator + transformation operator+(const transformation& tr) const { + transformation retval(*this); + return retval+=tr; + } + + // concatenate this with that + const transformation& operator+=(const transformation& tr) { + coordinate_type x, y; + transformation inv = inverse(); + inv.transform(x, y); + p_.set(HORIZONTAL, p_.get(HORIZONTAL) + x); + p_.set(VERTICAL, p_.get(VERTICAL) + y); + // concatenate axis transforms + atr_ += tr.atr_; + return *this; + } + + // get the axis_transformation portion of this + axis_transformation get_axis_transformation() const { + return atr_; + } + + // set the axis_transformation portion of this + void set_axis_transformation(const axis_transformation& atr) { + atr_ = atr; + } + + // get the translation + template + void get_translation(point_type& p) const { + assign(p, p_); + } + + // set the translation + template + void set_translation(const point_type& p) { + assign(p_, p); + } + + // apply the 2D portion of this transformation to the two coordinates given + void transform(coordinate_type& x, coordinate_type& y) const { + y -= p_.get(VERTICAL); + x -= p_.get(HORIZONTAL); + atr_.transform(x, y); + } + + // invert this transformation + transformation& invert() { + coordinate_type x = p_.get(HORIZONTAL), y = p_.get(VERTICAL); + atr_.transform(x, y); + x *= -1; + y *= -1; + p_ = point_data(x, y); + atr_.invert(); + return *this; + } + + // get the inverse of this transformation + transformation inverse() const { + transformation ret_val(*this); + return ret_val.invert(); + } + + void get_directions(direction_2d& horizontal_dir, + direction_2d& vertical_dir) const { + return atr_.get_directions(horizontal_dir, vertical_dir); + } + + private: + axis_transformation atr_; + point_data p_; +}; +} // polygon +} // boost + +#endif // BOOST_POLYGON_TRANSFORM_HPP diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi.hpp new file mode 100644 index 0000000..bca6add --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi.hpp @@ -0,0 +1,157 @@ +// Boost.Polygon library voronoi.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_VORONOI +#define BOOST_POLYGON_VORONOI + +#include "isotropy.hpp" +#include "point_concept.hpp" +#include "segment_concept.hpp" + +#include "voronoi_builder.hpp" +#include "voronoi_diagram.hpp" + +// Public methods to compute Voronoi diagram of a set of points and segments. +// Coordinates of the points and of the endpoints of the segments should belong +// to the 32-bit signed integer range [-2^31, 2^31-1]. To use wider input +// coordinate range voronoi_builder configuration via coordinate type traits +// is required. +// Complexity - O(N*logN), memory usage - O(N), N - number of input objects. +namespace boost { +namespace polygon { + +template +typename enable_if< + typename gtl_if< + typename is_point_concept< + typename geometry_concept::type + >::type + >::type, + std::size_t +>::type insert(const Point& point, VB* vb) { + return vb->insert_point(x(point), y(point)); +} + +template +typename enable_if< + typename gtl_if< + typename is_point_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type, + void +>::type insert(const PointIterator first, const PointIterator last, VB* vb) { + for (PointIterator it = first; it != last; ++it) { + insert(*it, vb); + } +} + +template +typename enable_if< + typename gtl_if< + typename is_segment_concept< + typename geometry_concept::type + >::type + >::type, + std::size_t +>::type insert(const Segment& segment, VB* vb) { + return vb->insert_segment( + x(low(segment)), y(low(segment)), + x(high(segment)), y(high(segment))); +} + +template +typename enable_if< + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type, + void +>::type insert(const SegmentIterator first, + const SegmentIterator last, + VB* vb) { + for (SegmentIterator it = first; it != last; ++it) { + insert(*it, vb); + } +} + +template +typename enable_if< + typename gtl_if< + typename is_point_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type, + void +>::type construct_voronoi(const PointIterator first, + const PointIterator last, + VD* vd) { + default_voronoi_builder builder; + insert(first, last, &builder); + builder.construct(vd); +} + +template +typename enable_if< + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type, + void +>::type construct_voronoi(const SegmentIterator first, + const SegmentIterator last, + VD* vd) { + default_voronoi_builder builder; + insert(first, last, &builder); + builder.construct(vd); +} + +template +typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_point_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< + typename std::iterator_traits::value_type + >::type + >::type + >::type + >::type, + void +>::type construct_voronoi(const PointIterator p_first, + const PointIterator p_last, + const SegmentIterator s_first, + const SegmentIterator s_last, + VD* vd) { + default_voronoi_builder builder; + insert(p_first, p_last, &builder); + insert(s_first, s_last, &builder); + builder.construct(vd); +} +} // polygon +} // boost + +#endif // BOOST_POLYGON_VORONOI diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_builder.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_builder.hpp new file mode 100644 index 0000000..6a3d504 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_builder.hpp @@ -0,0 +1,521 @@ +// Boost.Polygon library voronoi_builder.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_VORONOI_BUILDER +#define BOOST_POLYGON_VORONOI_BUILDER + +#include +#include +#include +#include +#include + +#include "detail/voronoi_ctypes.hpp" +#include "detail/voronoi_predicates.hpp" +#include "detail/voronoi_structures.hpp" + +#include "voronoi_geometry_type.hpp" + +namespace boost { +namespace polygon { +// GENERAL INFO: +// The sweepline algorithm implementation to compute Voronoi diagram of +// points and non-intersecting segments (excluding endpoints). +// Complexity - O(N*logN), memory usage - O(N), where N is the total number +// of input geometries. +// +// CONTRACT: +// 1) Input geometries should have integral (e.g. int32, int64) coordinate type. +// 2) Input geometries should not intersect except their endpoints. +// +// IMPLEMENTATION DETAILS: +// Each input point creates one input site. Each input segment creates three +// input sites: two for its endpoints and one for the segment itself (this is +// made to simplify output construction). All the site objects are constructed +// and sorted at the algorithm initialization step. Priority queue is used to +// dynamically hold circle events. At each step of the algorithm execution the +// leftmost event is retrieved by comparing the current site event and the +// topmost element from the circle event queue. STL map (red-black tree) +// container was chosen to hold state of the beach line. The keys of the map +// correspond to the neighboring sites that form a bisector and values map to +// the corresponding Voronoi edges in the output data structure. +template , + typename VP = detail::voronoi_predicates > +class voronoi_builder { + public: + typedef typename CTT::int_type int_type; + typedef typename CTT::fpt_type fpt_type; + + voronoi_builder() : index_(0) {} + + // Each point creates a single site event. + std::size_t insert_point(const int_type& x, const int_type& y) { + site_events_.push_back(site_event_type(x, y)); + site_events_.back().initial_index(index_); + site_events_.back().source_category(SOURCE_CATEGORY_SINGLE_POINT); + return index_++; + } + + // Each segment creates three site events that correspond to: + // 1) the start point of the segment; + // 2) the end point of the segment; + // 3) the segment itself defined by its start point. + std::size_t insert_segment( + const int_type& x1, const int_type& y1, + const int_type& x2, const int_type& y2) { + // Set up start point site. + point_type p1(x1, y1); + site_events_.push_back(site_event_type(p1)); + site_events_.back().initial_index(index_); + site_events_.back().source_category(SOURCE_CATEGORY_SEGMENT_START_POINT); + + // Set up end point site. + point_type p2(x2, y2); + site_events_.push_back(site_event_type(p2)); + site_events_.back().initial_index(index_); + site_events_.back().source_category(SOURCE_CATEGORY_SEGMENT_END_POINT); + + // Set up segment site. + if (point_comparison_(p1, p2)) { + site_events_.push_back(site_event_type(p1, p2)); + site_events_.back().source_category(SOURCE_CATEGORY_INITIAL_SEGMENT); + } else { + site_events_.push_back(site_event_type(p2, p1)); + site_events_.back().source_category(SOURCE_CATEGORY_REVERSE_SEGMENT); + } + site_events_.back().initial_index(index_); + return index_++; + } + + // Run sweepline algorithm and fill output data structure. + template + void construct(OUTPUT* output) { + // Init structures. + output->_reserve(site_events_.size()); + init_sites_queue(); + init_beach_line(output); + + // The algorithm stops when there are no events to process. + event_comparison_predicate event_comparison; + while (!circle_events_.empty() || + !(site_event_iterator_ == site_events_.end())) { + if (circle_events_.empty()) { + process_site_event(output); + } else if (site_event_iterator_ == site_events_.end()) { + process_circle_event(output); + } else { + if (event_comparison(*site_event_iterator_, + circle_events_.top().first)) { + process_site_event(output); + } else { + process_circle_event(output); + } + } + while (!circle_events_.empty() && + !circle_events_.top().first.is_active()) { + circle_events_.pop(); + } + } + beach_line_.clear(); + + // Finish construction. + output->_build(); + } + + void clear() { + index_ = 0; + site_events_.clear(); + } + + private: + typedef detail::point_2d point_type; + typedef detail::site_event site_event_type; + typedef typename std::vector::const_iterator + site_event_iterator_type; + typedef detail::circle_event circle_event_type; + typedef typename VP::template point_comparison_predicate + point_comparison_predicate; + typedef typename VP:: + template event_comparison_predicate + event_comparison_predicate; + typedef typename VP:: + template circle_formation_predicate + circle_formation_predicate_type; + typedef void edge_type; + typedef detail::beach_line_node_key key_type; + typedef detail::beach_line_node_data + value_type; + typedef typename VP::template node_comparison_predicate + node_comparer_type; + typedef std::map< key_type, value_type, node_comparer_type > beach_line_type; + typedef typename beach_line_type::iterator beach_line_iterator; + typedef std::pair event_type; + struct event_comparison_type { + bool operator()(const event_type& lhs, const event_type& rhs) const { + return predicate(rhs.first, lhs.first); + } + event_comparison_predicate predicate; + }; + typedef detail::ordered_queue + circle_event_queue_type; + typedef std::pair end_point_type; + + void init_sites_queue() { + // Sort site events. + std::sort(site_events_.begin(), site_events_.end(), + event_comparison_predicate()); + + // Remove duplicates. + site_events_.erase(std::unique( + site_events_.begin(), site_events_.end()), site_events_.end()); + + // Index sites. + for (std::size_t cur = 0; cur < site_events_.size(); ++cur) { + site_events_[cur].sorted_index(cur); + } + + // Init site iterator. + site_event_iterator_ = site_events_.begin(); + } + + template + void init_beach_line(OUTPUT* output) { + if (site_events_.empty()) + return; + if (site_events_.size() == 1) { + // Handle single site event case. + output->_process_single_site(site_events_[0]); + ++site_event_iterator_; + } else { + int skip = 0; + + while (site_event_iterator_ != site_events_.end() && + VP::is_vertical(site_event_iterator_->point0(), + site_events_.begin()->point0()) && + VP::is_vertical(*site_event_iterator_)) { + ++site_event_iterator_; + ++skip; + } + + if (skip == 1) { + // Init beach line with the first two sites. + init_beach_line_default(output); + } else { + // Init beach line with collinear vertical sites. + init_beach_line_collinear_sites(output); + } + } + } + + // Init beach line with the two first sites. + // The first site is always a point. + template + void init_beach_line_default(OUTPUT* output) { + // Get the first and the second site event. + site_event_iterator_type it_first = site_events_.begin(); + site_event_iterator_type it_second = site_events_.begin(); + ++it_second; + insert_new_arc( + *it_first, *it_first, *it_second, beach_line_.end(), output); + // The second site was already processed. Move the iterator. + ++site_event_iterator_; + } + + // Init beach line with collinear sites. + template + void init_beach_line_collinear_sites(OUTPUT* output) { + site_event_iterator_type it_first = site_events_.begin(); + site_event_iterator_type it_second = site_events_.begin(); + ++it_second; + while (it_second != site_event_iterator_) { + // Create a new beach line node. + key_type new_node(*it_first, *it_second); + + // Update the output. + edge_type* edge = output->_insert_new_edge(*it_first, *it_second).first; + + // Insert a new bisector into the beach line. + beach_line_.insert(beach_line_.end(), + std::pair(new_node, value_type(edge))); + + // Update iterators. + ++it_first; + ++it_second; + } + } + + void deactivate_circle_event(value_type* value) { + if (value->circle_event()) { + value->circle_event()->deactivate(); + value->circle_event(NULL); + } + } + + template + void process_site_event(OUTPUT* output) { + // Get next site event to process. + site_event_type site_event = *site_event_iterator_; + + // Move site iterator. + site_event_iterator_type last = site_event_iterator_ + 1; + + // If a new site is an end point of some segment, + // remove temporary nodes from the beach line data structure. + if (!site_event.is_segment()) { + while (!end_points_.empty() && + end_points_.top().first == site_event.point0()) { + beach_line_iterator b_it = end_points_.top().second; + end_points_.pop(); + beach_line_.erase(b_it); + } + } else { + while (last != site_events_.end() && + last->is_segment() && last->point0() == site_event.point0()) + ++last; + } + + // Find the node in the binary search tree with left arc + // lying above the new site point. + key_type new_key(*site_event_iterator_); + beach_line_iterator right_it = beach_line_.lower_bound(new_key); + + for (; site_event_iterator_ != last; ++site_event_iterator_) { + site_event = *site_event_iterator_; + beach_line_iterator left_it = right_it; + + // Do further processing depending on the above node position. + // For any two neighboring nodes the second site of the first node + // is the same as the first site of the second node. + if (right_it == beach_line_.end()) { + // The above arc corresponds to the second arc of the last node. + // Move the iterator to the last node. + --left_it; + + // Get the second site of the last node + const site_event_type& site_arc = left_it->first.right_site(); + + // Insert new nodes into the beach line. Update the output. + right_it = insert_new_arc( + site_arc, site_arc, site_event, right_it, output); + + // Add a candidate circle to the circle event queue. + // There could be only one new circle event formed by + // a new bisector and the one on the left. + activate_circle_event(left_it->first.left_site(), + left_it->first.right_site(), + site_event, right_it); + } else if (right_it == beach_line_.begin()) { + // The above arc corresponds to the first site of the first node. + const site_event_type& site_arc = right_it->first.left_site(); + + // Insert new nodes into the beach line. Update the output. + left_it = insert_new_arc( + site_arc, site_arc, site_event, right_it, output); + + // If the site event is a segment, update its direction. + if (site_event.is_segment()) { + site_event.inverse(); + } + + // Add a candidate circle to the circle event queue. + // There could be only one new circle event formed by + // a new bisector and the one on the right. + activate_circle_event(site_event, right_it->first.left_site(), + right_it->first.right_site(), right_it); + right_it = left_it; + } else { + // The above arc corresponds neither to the first, + // nor to the last site in the beach line. + const site_event_type& site_arc2 = right_it->first.left_site(); + const site_event_type& site3 = right_it->first.right_site(); + + // Remove the candidate circle from the event queue. + deactivate_circle_event(&right_it->second); + --left_it; + const site_event_type& site_arc1 = left_it->first.right_site(); + const site_event_type& site1 = left_it->first.left_site(); + + // Insert new nodes into the beach line. Update the output. + beach_line_iterator new_node_it = + insert_new_arc(site_arc1, site_arc2, site_event, right_it, output); + + // Add candidate circles to the circle event queue. + // There could be up to two circle events formed by + // a new bisector and the one on the left or right. + activate_circle_event(site1, site_arc1, site_event, new_node_it); + + // If the site event is a segment, update its direction. + if (site_event.is_segment()) { + site_event.inverse(); + } + activate_circle_event(site_event, site_arc2, site3, right_it); + right_it = new_node_it; + } + } + } + + // In general case circle event is made of the three consecutive sites + // that form two bisectors in the beach line data structure. + // Let circle event sites be A, B, C, two bisectors that define + // circle event are (A, B), (B, C). During circle event processing + // we remove (A, B), (B, C) and insert (A, C). As beach line comparison + // works correctly only if one of the nodes is a new one we remove + // (B, C) bisector and change (A, B) bisector to the (A, C). That's + // why we use const_cast there and take all the responsibility that + // map data structure keeps correct ordering. + template + void process_circle_event(OUTPUT* output) { + // Get the topmost circle event. + const event_type& e = circle_events_.top(); + const circle_event_type& circle_event = e.first; + beach_line_iterator it_first = e.second; + beach_line_iterator it_last = it_first; + + // Get the C site. + site_event_type site3 = it_first->first.right_site(); + + // Get the half-edge corresponding to the second bisector - (B, C). + edge_type* bisector2 = it_first->second.edge(); + + // Get the half-edge corresponding to the first bisector - (A, B). + --it_first; + edge_type* bisector1 = it_first->second.edge(); + + // Get the A site. + site_event_type site1 = it_first->first.left_site(); + + if (!site1.is_segment() && site3.is_segment() && + site3.point1() == site1.point0()) { + site3.inverse(); + } + + // Change the (A, B) bisector node to the (A, C) bisector node. + const_cast(it_first->first).right_site(site3); + + // Insert the new bisector into the beach line. + it_first->second.edge(output->_insert_new_edge( + site1, site3, circle_event, bisector1, bisector2).first); + + // Remove the (B, C) bisector node from the beach line. + beach_line_.erase(it_last); + it_last = it_first; + + // Pop the topmost circle event from the event queue. + circle_events_.pop(); + + // Check new triplets formed by the neighboring arcs + // to the left for potential circle events. + if (it_first != beach_line_.begin()) { + deactivate_circle_event(&it_first->second); + --it_first; + const site_event_type& site_l1 = it_first->first.left_site(); + activate_circle_event(site_l1, site1, site3, it_last); + } + + // Check the new triplet formed by the neighboring arcs + // to the right for potential circle events. + ++it_last; + if (it_last != beach_line_.end()) { + deactivate_circle_event(&it_last->second); + const site_event_type& site_r1 = it_last->first.right_site(); + activate_circle_event(site1, site3, site_r1, it_last); + } + } + + // Insert new nodes into the beach line. Update the output. + template + beach_line_iterator insert_new_arc( + const site_event_type& site_arc1, const site_event_type &site_arc2, + const site_event_type& site_event, beach_line_iterator position, + OUTPUT* output) { + // Create two new bisectors with opposite directions. + key_type new_left_node(site_arc1, site_event); + key_type new_right_node(site_event, site_arc2); + + // Set correct orientation for the first site of the second node. + if (site_event.is_segment()) { + new_right_node.left_site().inverse(); + } + + // Update the output. + std::pair edges = + output->_insert_new_edge(site_arc2, site_event); + position = beach_line_.insert(position, + typename beach_line_type::value_type( + new_right_node, value_type(edges.second))); + + if (site_event.is_segment()) { + // Update the beach line with temporary bisector, that will + // disappear after processing site event corresponding to the + // second endpoint of the segment site. + key_type new_node(site_event, site_event); + new_node.right_site().inverse(); + position = beach_line_.insert(position, + typename beach_line_type::value_type(new_node, value_type(NULL))); + + // Update the data structure that holds temporary bisectors. + end_points_.push(std::make_pair(site_event.point1(), position)); + } + + position = beach_line_.insert(position, + typename beach_line_type::value_type( + new_left_node, value_type(edges.first))); + + return position; + } + + // Add a new circle event to the event queue. + // bisector_node corresponds to the (site2, site3) bisector. + void activate_circle_event(const site_event_type& site1, + const site_event_type& site2, + const site_event_type& site3, + beach_line_iterator bisector_node) { + circle_event_type c_event; + // Check if the three input sites create a circle event. + if (circle_formation_predicate_(site1, site2, site3, c_event)) { + // Add the new circle event to the circle events queue. + // Update bisector's circle event iterator to point to the + // new circle event in the circle event queue. + event_type& e = circle_events_.push( + std::pair( + c_event, bisector_node)); + bisector_node->second.circle_event(&e.first); + } + } + + private: + point_comparison_predicate point_comparison_; + struct end_point_comparison { + bool operator() (const end_point_type& end1, + const end_point_type& end2) const { + return point_comparison(end2.first, end1.first); + } + point_comparison_predicate point_comparison; + }; + + std::vector site_events_; + site_event_iterator_type site_event_iterator_; + std::priority_queue< end_point_type, std::vector, + end_point_comparison > end_points_; + circle_event_queue_type circle_events_; + beach_line_type beach_line_; + circle_formation_predicate_type circle_formation_predicate_; + std::size_t index_; + + // Disallow copy constructor and operator= + voronoi_builder(const voronoi_builder&); + void operator=(const voronoi_builder&); +}; + +typedef voronoi_builder default_voronoi_builder; +} // polygon +} // boost + +#endif // BOOST_POLYGON_VORONOI_BUILDER diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_diagram.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_diagram.hpp new file mode 100644 index 0000000..a8eb933 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_diagram.hpp @@ -0,0 +1,620 @@ +// Boost.Polygon library voronoi_diagram.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_VORONOI_DIAGRAM +#define BOOST_POLYGON_VORONOI_DIAGRAM + +#include +#include + +#include "detail/voronoi_ctypes.hpp" +#include "detail/voronoi_structures.hpp" + +#include "voronoi_geometry_type.hpp" + +namespace boost { +namespace polygon { + +// Forward declarations. +template +class voronoi_edge; + +// Represents Voronoi cell. +// Data members: +// 1) index of the source within the initial input set +// 2) pointer to the incident edge +// 3) mutable color member +// Cell may contain point or segment site inside. +template +class voronoi_cell { + public: + typedef T coordinate_type; + typedef std::size_t color_type; + typedef voronoi_edge voronoi_edge_type; + typedef std::size_t source_index_type; + typedef SourceCategory source_category_type; + + voronoi_cell(source_index_type source_index, + source_category_type source_category) : + source_index_(source_index), + incident_edge_(NULL), + color_(source_category) {} + + // Returns true if the cell contains point site, false else. + bool contains_point() const { + source_category_type source_category = this->source_category(); + return belongs(source_category, GEOMETRY_CATEGORY_POINT); + } + + // Returns true if the cell contains segment site, false else. + bool contains_segment() const { + source_category_type source_category = this->source_category(); + return belongs(source_category, GEOMETRY_CATEGORY_SEGMENT); + } + + source_index_type source_index() const { + return source_index_; + } + + source_category_type source_category() const { + return static_cast(color_ & SOURCE_CATEGORY_BITMASK); + } + + // Degenerate cells don't have any incident edges. + bool is_degenerate() const { return incident_edge_ == NULL; } + + voronoi_edge_type* incident_edge() { return incident_edge_; } + const voronoi_edge_type* incident_edge() const { return incident_edge_; } + void incident_edge(voronoi_edge_type* e) { incident_edge_ = e; } + + color_type color() const { return color_ >> BITS_SHIFT; } + void color(color_type color) const { + color_ &= BITS_MASK; + color_ |= color << BITS_SHIFT; + } + + private: + // 5 color bits are reserved. + enum Bits { + BITS_SHIFT = 0x5, + BITS_MASK = 0x1F + }; + + source_index_type source_index_; + voronoi_edge_type* incident_edge_; + mutable color_type color_; +}; + +// Represents Voronoi vertex. +// Data members: +// 1) vertex coordinates +// 2) pointer to the incident edge +// 3) mutable color member +template +class voronoi_vertex { + public: + typedef T coordinate_type; + typedef std::size_t color_type; + typedef voronoi_edge voronoi_edge_type; + + voronoi_vertex(const coordinate_type& x, const coordinate_type& y) : + x_(x), + y_(y), + incident_edge_(NULL), + color_(0) {} + + const coordinate_type& x() const { return x_; } + const coordinate_type& y() const { return y_; } + + bool is_degenerate() const { return incident_edge_ == NULL; } + + voronoi_edge_type* incident_edge() { return incident_edge_; } + const voronoi_edge_type* incident_edge() const { return incident_edge_; } + void incident_edge(voronoi_edge_type* e) { incident_edge_ = e; } + + color_type color() const { return color_ >> BITS_SHIFT; } + void color(color_type color) const { + color_ &= BITS_MASK; + color_ |= color << BITS_SHIFT; + } + + private: + // 5 color bits are reserved. + enum Bits { + BITS_SHIFT = 0x5, + BITS_MASK = 0x1F + }; + + coordinate_type x_; + coordinate_type y_; + voronoi_edge_type* incident_edge_; + mutable color_type color_; +}; + +// Half-edge data structure. Represents Voronoi edge. +// Data members: +// 1) pointer to the corresponding cell +// 2) pointer to the vertex that is the starting +// point of the half-edge +// 3) pointer to the twin edge +// 4) pointer to the CCW next edge +// 5) pointer to the CCW prev edge +// 6) mutable color member +template +class voronoi_edge { + public: + typedef T coordinate_type; + typedef voronoi_cell voronoi_cell_type; + typedef voronoi_vertex voronoi_vertex_type; + typedef voronoi_edge voronoi_edge_type; + typedef std::size_t color_type; + + voronoi_edge(bool is_linear, bool is_primary) : + cell_(NULL), + vertex_(NULL), + twin_(NULL), + next_(NULL), + prev_(NULL), + color_(0) { + if (is_linear) + color_ |= BIT_IS_LINEAR; + if (is_primary) + color_ |= BIT_IS_PRIMARY; + } + + voronoi_cell_type* cell() { return cell_; } + const voronoi_cell_type* cell() const { return cell_; } + void cell(voronoi_cell_type* c) { cell_ = c; } + + voronoi_vertex_type* vertex0() { return vertex_; } + const voronoi_vertex_type* vertex0() const { return vertex_; } + void vertex0(voronoi_vertex_type* v) { vertex_ = v; } + + voronoi_vertex_type* vertex1() { return twin_->vertex0(); } + const voronoi_vertex_type* vertex1() const { return twin_->vertex0(); } + + voronoi_edge_type* twin() { return twin_; } + const voronoi_edge_type* twin() const { return twin_; } + void twin(voronoi_edge_type* e) { twin_ = e; } + + voronoi_edge_type* next() { return next_; } + const voronoi_edge_type* next() const { return next_; } + void next(voronoi_edge_type* e) { next_ = e; } + + voronoi_edge_type* prev() { return prev_; } + const voronoi_edge_type* prev() const { return prev_; } + void prev(voronoi_edge_type* e) { prev_ = e; } + + // Returns a pointer to the rotation next edge + // over the starting point of the half-edge. + voronoi_edge_type* rot_next() { return prev_->twin(); } + const voronoi_edge_type* rot_next() const { return prev_->twin(); } + + // Returns a pointer to the rotation prev edge + // over the starting point of the half-edge. + voronoi_edge_type* rot_prev() { return twin_->next(); } + const voronoi_edge_type* rot_prev() const { return twin_->next(); } + + // Returns true if the edge is finite (segment, parabolic arc). + // Returns false if the edge is infinite (ray, line). + bool is_finite() const { return vertex0() && vertex1(); } + + // Returns true if the edge is infinite (ray, line). + // Returns false if the edge is finite (segment, parabolic arc). + bool is_infinite() const { return !vertex0() || !vertex1(); } + + // Returns true if the edge is linear (segment, ray, line). + // Returns false if the edge is curved (parabolic arc). + bool is_linear() const { + return (color_ & BIT_IS_LINEAR) ? true : false; + } + + // Returns true if the edge is curved (parabolic arc). + // Returns false if the edge is linear (segment, ray, line). + bool is_curved() const { + return (color_ & BIT_IS_LINEAR) ? false : true; + } + + // Returns false if edge goes through the endpoint of the segment. + // Returns true else. + bool is_primary() const { + return (color_ & BIT_IS_PRIMARY) ? true : false; + } + + // Returns true if edge goes through the endpoint of the segment. + // Returns false else. + bool is_secondary() const { + return (color_ & BIT_IS_PRIMARY) ? false : true; + } + + color_type color() const { return color_ >> BITS_SHIFT; } + void color(color_type color) const { + color_ &= BITS_MASK; + color_ |= color << BITS_SHIFT; + } + + private: + // 5 color bits are reserved. + enum Bits { + BIT_IS_LINEAR = 0x1, // linear is opposite to curved + BIT_IS_PRIMARY = 0x2, // primary is opposite to secondary + + BITS_SHIFT = 0x5, + BITS_MASK = 0x1F + }; + + voronoi_cell_type* cell_; + voronoi_vertex_type* vertex_; + voronoi_edge_type* twin_; + voronoi_edge_type* next_; + voronoi_edge_type* prev_; + mutable color_type color_; +}; + +template +struct voronoi_diagram_traits { + typedef T coordinate_type; + typedef voronoi_cell cell_type; + typedef voronoi_vertex vertex_type; + typedef voronoi_edge edge_type; + class vertex_equality_predicate_type { + public: + enum { ULPS = 128 }; + bool operator()(const vertex_type& v1, const vertex_type& v2) const { + return (ulp_cmp(v1.x(), v2.x(), ULPS) == + detail::ulp_comparison::EQUAL) && + (ulp_cmp(v1.y(), v2.y(), ULPS) == + detail::ulp_comparison::EQUAL); + } + private: + typename detail::ulp_comparison ulp_cmp; + }; +}; + +// Voronoi output data structure. +// CCW ordering is used on the faces perimeter and around the vertices. +template > +class voronoi_diagram { + public: + typedef typename TRAITS::coordinate_type coordinate_type; + typedef typename TRAITS::cell_type cell_type; + typedef typename TRAITS::vertex_type vertex_type; + typedef typename TRAITS::edge_type edge_type; + + typedef std::vector cell_container_type; + typedef typename cell_container_type::const_iterator const_cell_iterator; + + typedef std::vector vertex_container_type; + typedef typename vertex_container_type::const_iterator const_vertex_iterator; + + typedef std::vector edge_container_type; + typedef typename edge_container_type::const_iterator const_edge_iterator; + + voronoi_diagram() {} + + void clear() { + cells_.clear(); + vertices_.clear(); + edges_.clear(); + } + + const cell_container_type& cells() const { + return cells_; + } + + const vertex_container_type& vertices() const { + return vertices_; + } + + const edge_container_type& edges() const { + return edges_; + } + + std::size_t num_cells() const { + return cells_.size(); + } + + std::size_t num_edges() const { + return edges_.size(); + } + + std::size_t num_vertices() const { + return vertices_.size(); + } + + void _reserve(std::size_t num_sites) { + cells_.reserve(num_sites); + vertices_.reserve(num_sites << 1); + edges_.reserve((num_sites << 2) + (num_sites << 1)); + } + + template + void _process_single_site(const detail::site_event& site) { + cells_.push_back(cell_type(site.initial_index(), site.source_category())); + } + + // Insert a new half-edge into the output data structure. + // Takes as input left and right sites that form a new bisector. + // Returns a pair of pointers to a new half-edges. + template + std::pair _insert_new_edge( + const detail::site_event& site1, + const detail::site_event& site2) { + // Get sites' indexes. + std::size_t site_index1 = site1.sorted_index(); + std::size_t site_index2 = site2.sorted_index(); + + bool is_linear = is_linear_edge(site1, site2); + bool is_primary = is_primary_edge(site1, site2); + + // Create a new half-edge that belongs to the first site. + edges_.push_back(edge_type(is_linear, is_primary)); + edge_type& edge1 = edges_.back(); + + // Create a new half-edge that belongs to the second site. + edges_.push_back(edge_type(is_linear, is_primary)); + edge_type& edge2 = edges_.back(); + + // Add the initial cell during the first edge insertion. + if (cells_.empty()) { + cells_.push_back(cell_type( + site1.initial_index(), site1.source_category())); + } + + // The second site represents a new site during site event + // processing. Add a new cell to the cell records. + cells_.push_back(cell_type( + site2.initial_index(), site2.source_category())); + + // Set up pointers to cells. + edge1.cell(&cells_[site_index1]); + edge2.cell(&cells_[site_index2]); + + // Set up twin pointers. + edge1.twin(&edge2); + edge2.twin(&edge1); + + // Return a pointer to the new half-edge. + return std::make_pair(&edge1, &edge2); + } + + // Insert a new half-edge into the output data structure with the + // start at the point where two previously added half-edges intersect. + // Takes as input two sites that create a new bisector, circle event + // that corresponds to the intersection point of the two old half-edges, + // pointers to those half-edges. Half-edges' direction goes out of the + // new Voronoi vertex point. Returns a pair of pointers to a new half-edges. + template + std::pair _insert_new_edge( + const detail::site_event& site1, + const detail::site_event& site3, + const detail::circle_event& circle, + void* data12, void* data23) { + edge_type* edge12 = static_cast(data12); + edge_type* edge23 = static_cast(data23); + + // Add a new Voronoi vertex. + vertices_.push_back(vertex_type(circle.x(), circle.y())); + vertex_type& new_vertex = vertices_.back(); + + // Update vertex pointers of the old edges. + edge12->vertex0(&new_vertex); + edge23->vertex0(&new_vertex); + + bool is_linear = is_linear_edge(site1, site3); + bool is_primary = is_primary_edge(site1, site3); + + // Add a new half-edge. + edges_.push_back(edge_type(is_linear, is_primary)); + edge_type& new_edge1 = edges_.back(); + new_edge1.cell(&cells_[site1.sorted_index()]); + + // Add a new half-edge. + edges_.push_back(edge_type(is_linear, is_primary)); + edge_type& new_edge2 = edges_.back(); + new_edge2.cell(&cells_[site3.sorted_index()]); + + // Update twin pointers. + new_edge1.twin(&new_edge2); + new_edge2.twin(&new_edge1); + + // Update vertex pointer. + new_edge2.vertex0(&new_vertex); + + // Update Voronoi prev/next pointers. + edge12->prev(&new_edge1); + new_edge1.next(edge12); + edge12->twin()->next(edge23); + edge23->prev(edge12->twin()); + edge23->twin()->next(&new_edge2); + new_edge2.prev(edge23->twin()); + + // Return a pointer to the new half-edge. + return std::make_pair(&new_edge1, &new_edge2); + } + + void _build() { + // Remove degenerate edges. + edge_iterator last_edge = edges_.begin(); + for (edge_iterator it = edges_.begin(); it != edges_.end(); it += 2) { + const vertex_type* v1 = it->vertex0(); + const vertex_type* v2 = it->vertex1(); + if (v1 && v2 && vertex_equality_predicate_(*v1, *v2)) { + remove_edge(&(*it)); + } else { + if (it != last_edge) { + edge_type* e1 = &(*last_edge = *it); + edge_type* e2 = &(*(last_edge + 1) = *(it + 1)); + e1->twin(e2); + e2->twin(e1); + if (e1->prev()) { + e1->prev()->next(e1); + e2->next()->prev(e2); + } + if (e2->prev()) { + e1->next()->prev(e1); + e2->prev()->next(e2); + } + } + last_edge += 2; + } + } + edges_.erase(last_edge, edges_.end()); + + // Set up incident edge pointers for cells and vertices. + for (edge_iterator it = edges_.begin(); it != edges_.end(); ++it) { + it->cell()->incident_edge(&(*it)); + if (it->vertex0()) { + it->vertex0()->incident_edge(&(*it)); + } + } + + // Remove degenerate vertices. + vertex_iterator last_vertex = vertices_.begin(); + for (vertex_iterator it = vertices_.begin(); it != vertices_.end(); ++it) { + if (it->incident_edge()) { + if (it != last_vertex) { + *last_vertex = *it; + vertex_type* v = &(*last_vertex); + edge_type* e = v->incident_edge(); + do { + e->vertex0(v); + e = e->rot_next(); + } while (e != v->incident_edge()); + } + ++last_vertex; + } + } + vertices_.erase(last_vertex, vertices_.end()); + + // Set up next/prev pointers for infinite edges. + if (vertices_.empty()) { + if (!edges_.empty()) { + // Update prev/next pointers for the line edges. + edge_iterator edge_it = edges_.begin(); + edge_type* edge1 = &(*edge_it); + edge1->next(edge1); + edge1->prev(edge1); + ++edge_it; + edge1 = &(*edge_it); + ++edge_it; + + while (edge_it != edges_.end()) { + edge_type* edge2 = &(*edge_it); + ++edge_it; + + edge1->next(edge2); + edge1->prev(edge2); + edge2->next(edge1); + edge2->prev(edge1); + + edge1 = &(*edge_it); + ++edge_it; + } + + edge1->next(edge1); + edge1->prev(edge1); + } + } else { + // Update prev/next pointers for the ray edges. + for (cell_iterator cell_it = cells_.begin(); + cell_it != cells_.end(); ++cell_it) { + if (cell_it->is_degenerate()) + continue; + // Move to the previous edge while + // it is possible in the CW direction. + edge_type* left_edge = cell_it->incident_edge(); + while (left_edge->prev() != NULL) { + left_edge = left_edge->prev(); + // Terminate if this is not a boundary cell. + if (left_edge == cell_it->incident_edge()) + break; + } + + if (left_edge->prev() != NULL) + continue; + + edge_type* right_edge = cell_it->incident_edge(); + while (right_edge->next() != NULL) + right_edge = right_edge->next(); + left_edge->prev(right_edge); + right_edge->next(left_edge); + } + } + } + + private: + typedef typename cell_container_type::iterator cell_iterator; + typedef typename vertex_container_type::iterator vertex_iterator; + typedef typename edge_container_type::iterator edge_iterator; + typedef typename TRAITS::vertex_equality_predicate_type + vertex_equality_predicate_type; + + template + bool is_primary_edge(const SEvent& site1, const SEvent& site2) const { + bool flag1 = site1.is_segment(); + bool flag2 = site2.is_segment(); + if (flag1 && !flag2) { + return (site1.point0() != site2.point0()) && + (site1.point1() != site2.point0()); + } + if (!flag1 && flag2) { + return (site2.point0() != site1.point0()) && + (site2.point1() != site1.point0()); + } + return true; + } + + template + bool is_linear_edge(const SEvent& site1, const SEvent& site2) const { + if (!is_primary_edge(site1, site2)) { + return true; + } + return !(site1.is_segment() ^ site2.is_segment()); + } + + // Remove degenerate edge. + void remove_edge(edge_type* edge) { + // Update the endpoints of the incident edges to the second vertex. + vertex_type* vertex = edge->vertex0(); + edge_type* updated_edge = edge->twin()->rot_next(); + while (updated_edge != edge->twin()) { + updated_edge->vertex0(vertex); + updated_edge = updated_edge->rot_next(); + } + + edge_type* edge1 = edge; + edge_type* edge2 = edge->twin(); + + edge_type* edge1_rot_prev = edge1->rot_prev(); + edge_type* edge1_rot_next = edge1->rot_next(); + + edge_type* edge2_rot_prev = edge2->rot_prev(); + edge_type* edge2_rot_next = edge2->rot_next(); + + // Update prev/next pointers for the incident edges. + edge1_rot_next->twin()->next(edge2_rot_prev); + edge2_rot_prev->prev(edge1_rot_next->twin()); + edge1_rot_prev->prev(edge2_rot_next->twin()); + edge2_rot_next->twin()->next(edge1_rot_prev); + } + + cell_container_type cells_; + vertex_container_type vertices_; + edge_container_type edges_; + vertex_equality_predicate_type vertex_equality_predicate_; + + // Disallow copy constructor and operator= + voronoi_diagram(const voronoi_diagram&); + void operator=(const voronoi_diagram&); +}; +} // polygon +} // boost + +#endif // BOOST_POLYGON_VORONOI_DIAGRAM diff --git a/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_geometry_type.hpp b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_geometry_type.hpp new file mode 100644 index 0000000..1de80e5 --- /dev/null +++ b/PCUT/MinkowskiCpp/boost_1_84_0/boost/polygon/voronoi_geometry_type.hpp @@ -0,0 +1,48 @@ +// Boost.Polygon library voronoi_geometry_type.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_VORONOI_GEOMETRY_TYPE +#define BOOST_POLYGON_VORONOI_GEOMETRY_TYPE + +#include + +namespace boost { +namespace polygon { +// Represents topology type of the voronoi site. +enum GeometryCategory { + GEOMETRY_CATEGORY_POINT = 0x0, + GEOMETRY_CATEGORY_SEGMENT = 0x1 +}; + +// Represents category of the input source that forms Voronoi cell. +enum SourceCategory { + // Point subtypes. + SOURCE_CATEGORY_SINGLE_POINT = 0x0, + SOURCE_CATEGORY_SEGMENT_START_POINT = 0x1, + SOURCE_CATEGORY_SEGMENT_END_POINT = 0x2, + + // Segment subtypes. + SOURCE_CATEGORY_INITIAL_SEGMENT = 0x8, + SOURCE_CATEGORY_REVERSE_SEGMENT = 0x9, + + SOURCE_CATEGORY_GEOMETRY_SHIFT = 0x3, + SOURCE_CATEGORY_BITMASK = 0x1F +}; + +inline bool belongs( + SourceCategory source_category, + GeometryCategory geometry_category) { + return (static_cast(source_category) >> + SOURCE_CATEGORY_GEOMETRY_SHIFT) == + static_cast(geometry_category); +} +} // polygon +} // boost + +#endif // BOOST_POLYGON_VORONOI_GEOMETRY_TYPE diff --git a/PCUT/MinkowskiCpp/pch.cpp b/PCUT/MinkowskiCpp/pch.cpp new file mode 100644 index 0000000..bcb5590 --- /dev/null +++ b/PCUT/MinkowskiCpp/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/PCUT/MinkowskiCpp/pch.h b/PCUT/MinkowskiCpp/pch.h new file mode 100644 index 0000000..10fe677 --- /dev/null +++ b/PCUT/MinkowskiCpp/pch.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/PCUT/PCUT.API/ApiClient.cs b/PCUT/PCUT.API/ApiClient.cs new file mode 100644 index 0000000..75be7c5 --- /dev/null +++ b/PCUT/PCUT.API/ApiClient.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Net.Http.Headers; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; +using System.Text; + +namespace PCUT.API +{ + public static class ApiClient + { + private static HttpClient httpClient { get;set; } + private static string baseApiUrl { get; set; } + + public static void Initial(string baseURL) + { + if (httpClient != null) return; + httpClient = new HttpClient(); + baseApiUrl = baseURL; + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + } + + public static void SetAuthToken(string token) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + + } + + public static async Task> GetAllAsync(string path) + { + HttpResponseMessage response = await httpClient.GetAsync($"{baseApiUrl}/{path}"); + response.EnsureSuccessStatusCode(); + string content = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject>(content); + } + + public static async Task GetByIdAsync(string path, int id) + { + HttpResponseMessage response = await httpClient.GetAsync($"{baseApiUrl}/{path}/{id}"); + response.EnsureSuccessStatusCode(); + string content = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(content); + } + + public static async Task CreateAsync(string path, T entity) + { + string json = JsonConvert.SerializeObject(entity); + StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); + + HttpResponseMessage response = await httpClient.PostAsync($"{baseApiUrl}/{path}", content); + response.EnsureSuccessStatusCode(); + + string responseContent = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(responseContent); + } + + public static async Task UpdateAsync(string path, int id, T entity) + { + string json = JsonConvert.SerializeObject(entity); + StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); + + HttpResponseMessage response = await httpClient.PutAsync($"{baseApiUrl}/{path}/{id}", content); + response.EnsureSuccessStatusCode(); + + string responseContent = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(responseContent); + } + + public static async Task DeleteAsync(string path, int id) + { + HttpResponseMessage response = await httpClient.DeleteAsync($"{baseApiUrl}/{path}/{id}"); + response.EnsureSuccessStatusCode(); + return response.IsSuccessStatusCode; + } + } + +} + + diff --git a/PCUT/PCUT.API/Managers/CategoriesBase.cs b/PCUT/PCUT.API/Managers/CategoriesBase.cs new file mode 100644 index 0000000..7839953 --- /dev/null +++ b/PCUT/PCUT.API/Managers/CategoriesBase.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.API.Managers +{ + public class CategoriesBase + { + public CategoriesBase() + { + ApiClient.Initial("http://192.168.1.106:3000/api/categories"); + } + } +} diff --git a/PCUT/PCUT.API/Managers/CategoryManager.cs b/PCUT/PCUT.API/Managers/CategoryManager.cs new file mode 100644 index 0000000..db7ebeb --- /dev/null +++ b/PCUT/PCUT.API/Managers/CategoryManager.cs @@ -0,0 +1,47 @@ +using PCUT.Entities; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.API.Managers +{ + public class CategoryManager : ManagerBase, ICategoriesBase + { + string path = "http://192.168.1.106:3000/api/categories"; + + public async Task CreateAsync(Categories entity) + { + try + { + return await ApiClient.CreateAsync(path, entity); + } + catch { + + return null; + } + } + + public Task DeleteAsync(int id) + { + throw new NotImplementedException(); + } + + public Task> GetAllAsync() + { + throw new NotImplementedException(); + } + + public Task GetByIdAsync(int id) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(int id, Categories entity) + { + throw new NotImplementedException(); + } + } +} diff --git a/PCUT/PCUT.API/Managers/ICategoriesBase.cs b/PCUT/PCUT.API/Managers/ICategoriesBase.cs new file mode 100644 index 0000000..4c45805 --- /dev/null +++ b/PCUT/PCUT.API/Managers/ICategoriesBase.cs @@ -0,0 +1,12 @@ +using PCUT.Entities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.API.Managers +{ + public interface ICategoriesBase : IManager + { + + } +} diff --git a/PCUT/PCUT.API/Managers/IManager.cs b/PCUT/PCUT.API/Managers/IManager.cs new file mode 100644 index 0000000..55fe84a --- /dev/null +++ b/PCUT/PCUT.API/Managers/IManager.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.API.Managers +{ + public interface IManager + { + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task CreateAsync(T entity); + Task UpdateAsync(int id, T entity); + Task DeleteAsync(int id); + + } +} diff --git a/PCUT/PCUT.API/Managers/IUserManager.cs b/PCUT/PCUT.API/Managers/IUserManager.cs new file mode 100644 index 0000000..0c7f4f7 --- /dev/null +++ b/PCUT/PCUT.API/Managers/IUserManager.cs @@ -0,0 +1,12 @@ +using PCUT.Entities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.API.Managers +{ + public interface IUserManager: IManager + { + + } +} diff --git a/PCUT/PCUT.API/Managers/ManagerBase.cs b/PCUT/PCUT.API/Managers/ManagerBase.cs new file mode 100644 index 0000000..fea5a7e --- /dev/null +++ b/PCUT/PCUT.API/Managers/ManagerBase.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.API.Managers +{ + public class ManagerBase + { + public ManagerBase() { + ApiClient.Initial("http://192.168.1.106:3000/api/users"); + } + + } +} diff --git a/PCUT/PCUT.API/Managers/UserManager.cs b/PCUT/PCUT.API/Managers/UserManager.cs new file mode 100644 index 0000000..155091e --- /dev/null +++ b/PCUT/PCUT.API/Managers/UserManager.cs @@ -0,0 +1,72 @@ +using PCUT.Entities; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace PCUT.API.Managers +{ + public class UserManager: ManagerBase, IUserManager + { + string path = "users"; + public async Task CreateAsync(User entity) + { + try + { + return await ApiClient.CreateAsync(path, entity); + } + catch { + return null; + } + } + + public async Task DeleteAsync(int id) + { + try + { + return await ApiClient.DeleteAsync(path, id); + } + catch + { + return false; + } + } + + public async Task> GetAllAsync() + { + try + { + return await ApiClient.GetAllAsync(path); + } + catch + { + return null; + } + } + + public async Task GetByIdAsync(int id) + { + try + { + return await ApiClient.GetByIdAsync(path, id); + } + catch + { + return null; + } + } + + public async Task UpdateAsync(int id, User entity) + { + try + { + return await ApiClient.UpdateAsync(path, id, entity); + } + catch + { + return null; + } + } + } +} diff --git a/PCUT/PCUT.API/PCUT.API.csproj b/PCUT/PCUT.API/PCUT.API.csproj new file mode 100644 index 0000000..590a7de --- /dev/null +++ b/PCUT/PCUT.API/PCUT.API.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/PCUT/PCUT.Entities/ApiResponse/MessageResponse.cs b/PCUT/PCUT.Entities/ApiResponse/MessageResponse.cs new file mode 100644 index 0000000..9cc6ec6 --- /dev/null +++ b/PCUT/PCUT.Entities/ApiResponse/MessageResponse.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities.ApiResponse +{ + public class MessageResponse + { + [JsonProperty("message")] + public string ErrorMessage { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/ApiResponse/PaginationResponse.cs b/PCUT/PCUT.Entities/ApiResponse/PaginationResponse.cs new file mode 100644 index 0000000..9e3f386 --- /dev/null +++ b/PCUT/PCUT.Entities/ApiResponse/PaginationResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities.ApiResponse +{ + public class DataResponse + { + [JsonProperty("data")] + public TData Data { get; set; } + } + + public class PaginationResponse : DataResponse + { + [JsonProperty("pagination")] + public Pagination Pagination { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/ApiResponse/UserCredentialDto.cs b/PCUT/PCUT.Entities/ApiResponse/UserCredentialDto.cs new file mode 100644 index 0000000..5926932 --- /dev/null +++ b/PCUT/PCUT.Entities/ApiResponse/UserCredentialDto.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Http.Core.Models +{ + public class UserCredentialDto + { + [JsonProperty("access_token")] + public string AccessToken { get; set; } + + [JsonProperty("refresh_token")] + public string RefreshToken { get; set; } + + [JsonProperty("deviceId")] + public string DeviceId { get; set; } + + [JsonProperty("username")] + public string Username { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/Category.cs b/PCUT/PCUT.Entities/Category.cs new file mode 100644 index 0000000..e8b937f --- /dev/null +++ b/PCUT/PCUT.Entities/Category.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class Category + { + [JsonProperty("_id")] + public string Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/CategoryMetadata.cs b/PCUT/PCUT.Entities/CategoryMetadata.cs new file mode 100644 index 0000000..1c3220b --- /dev/null +++ b/PCUT/PCUT.Entities/CategoryMetadata.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class CategoryMetadata + { + [JsonProperty("_id")] + public string Id { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("categoryId")] + public string CategoryId { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/CdrFile.cs b/PCUT/PCUT.Entities/CdrFile.cs new file mode 100644 index 0000000..f69663f --- /dev/null +++ b/PCUT/PCUT.Entities/CdrFile.cs @@ -0,0 +1,49 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class CdrFile + { + [JsonProperty("_id")] + public string Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("area")] + public string Area { get; set; } + + [JsonProperty("remarks")] + public string Remarks { get; set; } + + [JsonProperty("file")] + public FileData File { get; set; } + + [JsonProperty("thumbnail")] + public FileData Thumbnail { get; set; } + + [JsonProperty("category")] + public Category Category { get; set; } + + [JsonProperty("brand")] + public CategoryMetadata Brand { get; set; } + + [JsonProperty("type")] + public CategoryMetadata Type { get; set; } + + [JsonProperty("_model")] + public CategoryMetadata Model { get; set; } + + [JsonProperty("series")] + public CategoryMetadata Series { get; set; } + + [JsonProperty("year")] + public CategoryMetadata Year { get; set; } + + [JsonProperty("subType")] + public CategoryMetadata SubType { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/FileData.cs b/PCUT/PCUT.Entities/FileData.cs new file mode 100644 index 0000000..89c50d7 --- /dev/null +++ b/PCUT/PCUT.Entities/FileData.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class FileData + { + [JsonProperty("_id")] + public string Id { get; set; } + [JsonProperty("filename")] + public string Filename { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("createdBy")] + public string CreatedBy { get; set; } + + [JsonProperty("updatedAt")] + public DateTime UpdatedAt { get; set; } + + [JsonProperty("createdAt")] + public DateTime CreatedAt { get; set; } + + [JsonProperty("__v")] + public int Version { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/PCUT.Entities.csproj b/PCUT/PCUT.Entities/PCUT.Entities.csproj new file mode 100644 index 0000000..eeda231 --- /dev/null +++ b/PCUT/PCUT.Entities/PCUT.Entities.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/PCUT/PCUT.Entities/Pagination.cs b/PCUT/PCUT.Entities/Pagination.cs new file mode 100644 index 0000000..b4b3cf3 --- /dev/null +++ b/PCUT/PCUT.Entities/Pagination.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class Pagination + { + [JsonProperty("page")] + public int Page { get; set; } + + [JsonProperty("pageSize")] + public int PageSize { get; set; } + + [JsonProperty("totalPages")] + public int TotalPages { get; set; } + + [JsonProperty("totalRecords")] + public int TotalRecords { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/PasswordChange.cs b/PCUT/PCUT.Entities/PasswordChange.cs new file mode 100644 index 0000000..e274c46 --- /dev/null +++ b/PCUT/PCUT.Entities/PasswordChange.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class PasswordChange + { + [JsonProperty("password")] + public string Password { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/UpsertCategory.cs b/PCUT/PCUT.Entities/UpsertCategory.cs new file mode 100644 index 0000000..5220fb9 --- /dev/null +++ b/PCUT/PCUT.Entities/UpsertCategory.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace PCUT.Entities +{ + public class UpsertCategory + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/UpsertFile.cs b/PCUT/PCUT.Entities/UpsertFile.cs new file mode 100644 index 0000000..a9ca9ab --- /dev/null +++ b/PCUT/PCUT.Entities/UpsertFile.cs @@ -0,0 +1,46 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class UpsertFile + { + [JsonProperty("file")] + public string FileId { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("category")] + public string Category { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("brand")] + public string Brand { get; set; } + + [JsonProperty("year")] + public string Year { get; set; } + + [JsonProperty("series")] + public string Series { get; set; } + + [JsonProperty("_model")] + public string Model { get; set; } + + [JsonProperty("subType")] + public string SubType { get; set; } + + [JsonProperty("area")] + public string Area { get; set; } + + [JsonProperty("remarks")] + public string Remarks { get; set; } + + [JsonProperty("thumbnail")] + public string Thumbnail { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/UpsertMetadata.cs b/PCUT/PCUT.Entities/UpsertMetadata.cs new file mode 100644 index 0000000..7ca2ecc --- /dev/null +++ b/PCUT/PCUT.Entities/UpsertMetadata.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class UpsertMetadata + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("categoryId")] + public string CategoryId { get; set; } + + } +} diff --git a/PCUT/PCUT.Entities/UpsertUser.cs b/PCUT/PCUT.Entities/UpsertUser.cs new file mode 100644 index 0000000..50c495e --- /dev/null +++ b/PCUT/PCUT.Entities/UpsertUser.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class UpsertUser + { + [JsonProperty("accountName")] + public string AccountName { get; set; } + [JsonProperty("username")] + public string Username { get; set; } + [JsonProperty("role")] + public string Role { get; set; } + [JsonProperty("description")] + public string Description { get; set; } + [JsonProperty("isActive")] + public bool IsActive { get; set; } + [JsonProperty("isPermittedEdit")] + public bool IsPermittedEdit { get; set; } + [JsonProperty("phone")] + public string Phone { get; set; } + [JsonProperty("expiredAt")] + public string ExpiredAt { get; set; } + [JsonProperty("permissions")] + public List Permissions { get; set; } + [JsonProperty("password")] + public string Password { get; set; } + } +} diff --git a/PCUT/PCUT.Entities/UserProfile.cs b/PCUT/PCUT.Entities/UserProfile.cs new file mode 100644 index 0000000..afb13f4 --- /dev/null +++ b/PCUT/PCUT.Entities/UserProfile.cs @@ -0,0 +1,59 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PCUT.Entities +{ + public class UserProfile + { + [JsonProperty("_id")] + public string Id { get; set; } + + [JsonProperty("role")] + public string Role { get; set; } + + [JsonProperty("permissions")] + public string[] Permissions { get; set; } + + [JsonProperty("accountName")] + public string AccountName { get; set; } + + [JsonProperty("username")] + public string UserName { get; set; } + + + [JsonProperty("phone")] + public string Phone { get; set; } + + [JsonProperty("isActive")] + public bool IsActive { get; set; } + + [JsonProperty("isPermittedEdit")] + public bool? IsPermittedEdit { get; set; } + + [JsonProperty("expiredAt")] + public DateTimeOffset? ExpiredAt { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("updatedAt")] + public DateTime UpdatedAt { get; set; } + + [JsonProperty("createdAt")] + public DateTime CreatedAt { get; set; } + + [JsonProperty("__v")] + public int V { get; set; } + + [JsonProperty("deviceId")] + public string DeviceId { get; set; } + + [JsonProperty("age")] + public int Age { get; set; } + + [JsonProperty("requestedAt")] + public DateTime RequestedAt { get; set; } + } +} diff --git a/PCUT/PCUT.sln b/PCUT/PCUT.sln new file mode 100644 index 0000000..adb4df3 --- /dev/null +++ b/PCUT/PCUT.sln @@ -0,0 +1,169 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCUT", "PCUT\PCUT.csproj", "{2E7B481D-6F70-41BE-8224-A9D69499E6EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PCUT.Entities", "PCUT.Entities\PCUT.Entities.csproj", "{C65442D8-797E-4A98-AD7D-1A1068D8020E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Clipper.Core", "Clipper.Core\Clipper.Core.csproj", "{6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeepNestLib.Core", "DeepNestLib.Core\DeepNestLib.Core.csproj", "{09ABFBB8-78BE-4920-8C86-3CBA7693076D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinkowskiCpp", "MinkowskiCpp\MinkowskiCpp.vcxproj", "{71998B3C-E08F-4324-A087-2EDEFF1E3256}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Http.Core", "Http.Core\Http.Core.csproj", "{01F0F3EA-71DE-436C-96B0-934E566BF04F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|Any CPU.ActiveCfg = Debug|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|Any CPU.Build.0 = Debug|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|Any CPU.Deploy.0 = Debug|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|ARM.ActiveCfg = Debug|ARM + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|ARM.Build.0 = Debug|ARM + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|ARM.Deploy.0 = Debug|ARM + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|ARM64.Build.0 = Debug|ARM64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|x64.ActiveCfg = Debug|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|x64.Build.0 = Debug|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|x64.Deploy.0 = Debug|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|x86.ActiveCfg = Debug|x86 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|x86.Build.0 = Debug|x86 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Debug|x86.Deploy.0 = Debug|x86 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|Any CPU.ActiveCfg = Release|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|Any CPU.Build.0 = Release|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|Any CPU.Deploy.0 = Release|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|ARM.ActiveCfg = Release|ARM + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|ARM.Build.0 = Release|ARM + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|ARM.Deploy.0 = Release|ARM + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|ARM64.ActiveCfg = Release|ARM64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|ARM64.Build.0 = Release|ARM64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|ARM64.Deploy.0 = Release|ARM64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|x64.ActiveCfg = Release|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|x64.Build.0 = Release|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|x64.Deploy.0 = Release|x64 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|x86.ActiveCfg = Release|x86 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|x86.Build.0 = Release|x86 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE}.Release|x86.Deploy.0 = Release|x86 + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|ARM.Build.0 = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|ARM64.Build.0 = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|x64.ActiveCfg = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|x64.Build.0 = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|x86.ActiveCfg = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Debug|x86.Build.0 = Debug|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|Any CPU.Build.0 = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|ARM.ActiveCfg = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|ARM.Build.0 = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|ARM64.ActiveCfg = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|ARM64.Build.0 = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|x64.ActiveCfg = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|x64.Build.0 = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|x86.ActiveCfg = Release|Any CPU + {C65442D8-797E-4A98-AD7D-1A1068D8020E}.Release|x86.Build.0 = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|ARM.Build.0 = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|ARM64.Build.0 = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|x64.ActiveCfg = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|x64.Build.0 = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|x86.ActiveCfg = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Debug|x86.Build.0 = Debug|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|Any CPU.Build.0 = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|ARM.ActiveCfg = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|ARM.Build.0 = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|ARM64.ActiveCfg = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|ARM64.Build.0 = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|x64.ActiveCfg = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|x64.Build.0 = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|x86.ActiveCfg = Release|Any CPU + {6C87A9D3-A2E3-41E0-B112-CD1DCE45D53C}.Release|x86.Build.0 = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|ARM.ActiveCfg = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|ARM.Build.0 = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|ARM64.Build.0 = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|x64.ActiveCfg = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|x64.Build.0 = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|x86.ActiveCfg = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Debug|x86.Build.0 = Debug|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|Any CPU.Build.0 = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|ARM.ActiveCfg = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|ARM.Build.0 = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|ARM64.ActiveCfg = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|ARM64.Build.0 = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|x64.ActiveCfg = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|x64.Build.0 = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|x86.ActiveCfg = Release|Any CPU + {09ABFBB8-78BE-4920-8C86-3CBA7693076D}.Release|x86.Build.0 = Release|Any CPU + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|Any CPU.ActiveCfg = Debug|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|Any CPU.Build.0 = Debug|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|ARM.ActiveCfg = Debug|ARM + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|ARM.Build.0 = Debug|ARM + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|ARM64.Build.0 = Debug|ARM64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|x64.ActiveCfg = Debug|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|x64.Build.0 = Debug|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|x86.ActiveCfg = Debug|Win32 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Debug|x86.Build.0 = Debug|Win32 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|Any CPU.ActiveCfg = Release|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|Any CPU.Build.0 = Release|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|ARM.ActiveCfg = Release|ARM + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|ARM.Build.0 = Release|ARM + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|ARM64.ActiveCfg = Release|ARM64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|ARM64.Build.0 = Release|ARM64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|x64.ActiveCfg = Release|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|x64.Build.0 = Release|x64 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|x86.ActiveCfg = Release|Win32 + {71998B3C-E08F-4324-A087-2EDEFF1E3256}.Release|x86.Build.0 = Release|Win32 + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|ARM.Build.0 = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|ARM64.Build.0 = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|x64.ActiveCfg = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|x64.Build.0 = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|x86.ActiveCfg = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Debug|x86.Build.0 = Debug|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|Any CPU.Build.0 = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|ARM.ActiveCfg = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|ARM.Build.0 = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|ARM64.ActiveCfg = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|ARM64.Build.0 = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|x64.ActiveCfg = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|x64.Build.0 = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|x86.ActiveCfg = Release|Any CPU + {01F0F3EA-71DE-436C-96B0-934E566BF04F}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {648CD40F-25EB-4FEB-82C0-A5CDF711F313} + EndGlobalSection +EndGlobal diff --git a/PCUT/PCUT/App.xaml b/PCUT/PCUT/App.xaml new file mode 100644 index 0000000..634b383 --- /dev/null +++ b/PCUT/PCUT/App.xaml @@ -0,0 +1,33 @@ + + + + #FF7700 + /Assets/FontAwesome.ttf#FontAwesome + 1500 + + + + + + diff --git a/PCUT/PCUT/App.xaml.cs b/PCUT/PCUT/App.xaml.cs new file mode 100644 index 0000000..da8cf40 --- /dev/null +++ b/PCUT/PCUT/App.xaml.cs @@ -0,0 +1,133 @@ +using Http.Core; +using Http.Core.Handlers; +using Microsoft.Extensions.DependencyInjection; +using PCUT.Pages; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Core; +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using static Http.Core.Constants.HttpConstants; + +namespace PCUT +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + sealed partial class App : Application + { + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public static NavigationTarget NextPage { get; set; } = NavigationTarget.None; + public enum NavigationTarget + { + None, + DesignCenter, + DataCenter, + AdminCenter + } + public App() + { + this.InitializeComponent(); + this.Suspending += OnSuspending; + AppContainer = ConfigDI(); + HttpClientFactory + .AddHttpClient(ClientNames.AuthClient) + .AddConfigureClient(client => { client.BaseAddress = new Uri(Auth.BaseUrl); }) + .AddHttpMessageHandler(() => new DeviceAuthHandler()); + HttpClientFactory + .AddHttpClient(ClientNames.ApiClient) + .AddConfigureClient(client => { client.BaseAddress = new Uri(Api.BaseUrl); }) + .AddHttpMessageHandler(() => new DeviceAuthHandler()) + .AddHttpMessageHandler(() => new AuthenticationHandler()); + } + public IServiceProvider AppContainer { get; } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs e) + { + Frame rootFrame = Window.Current.Content as Frame; + ApplicationView.GetForCurrentView().IsScreenCaptureEnabled = false; + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == null) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + rootFrame.NavigationFailed += OnNavigationFailed; + + if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) + { + //TODO: Load state from previously suspended application + } + + // Place the frame in the current Window + Window.Current.Content = rootFrame; + } + + if (e.PrelaunchActivated == false) + { + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Navigate(typeof(LoginPage), e.Arguments); + } + // Ensure the current window is active + Window.Current.Activate(); + } + } + + + /// + /// Invoked when Navigation to a certain page fails + /// + /// The Frame which failed navigation + /// Details about the navigation failure + void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + { + throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + } + + /// + /// Invoked when application execution is being suspended. Application state is saved + /// without knowing whether the application will be terminated or resumed with the contents + /// of memory still intact. + /// + /// The source of the suspend request. + /// Details about the suspend request. + private void OnSuspending(object sender, SuspendingEventArgs e) + { + var deferral = e.SuspendingOperation.GetDeferral(); + //TODO: Save application state and stop any background activity + deferral.Complete(); + } + + IServiceProvider ConfigDI() + { + var serviceCollection = new ServiceCollection(); + return serviceCollection.BuildServiceProvider(); + } + } +} diff --git a/PCUT/PCUT/Assets/Backgound.jpg b/PCUT/PCUT/Assets/Backgound.jpg new file mode 100644 index 0000000000000000000000000000000000000000..45e06ebc275e7d55735717f176bea808ed512738 GIT binary patch literal 138034 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

<>! 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≥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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/LargeTile.scale-125.png b/PCUT/PCUT/Assets/LargeTile.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..6fa0733191d0d451e52d421b296b26e78ae5853f GIT binary patch literal 8767 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/LargeTile.scale-150.png b/PCUT/PCUT/Assets/LargeTile.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..8c643c1e0e07a7a716daa9247ce68b7b4c807c27 GIT binary patch literal 10855 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 QQ4q)jWi6$gyVk+~2Y8XTMgRZ+ literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/LockScreenLogo.scale-200.png b/PCUT/PCUT/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..735f57adb5dfc01886d137b4e493d7e97cf13af3 GIT binary patch literal 1430 zcmaJ>TTC2P7~aKltDttVHYH6u8Io4i*}3fO&d$gd*bA_<3j~&e7%8(eXJLfhS!M@! zKrliY>>6yT4+Kr95$!DoD(Qn-5TP|{V_KS`k~E6(LGS@#`v$hQo&^^BKsw3HIsZBT z_y6C2n`lK@apunKojRQ^(_P}Mgewt$(^BBKCTZ;*xa?J3wQ7~@S0lUvbcLeq1Bg4o zH-bvQi|wt~L7q$~a-gDFP!{&TQfc3fX*6=uHv* zT&1&U(-)L%Xp^djI2?~eBF2cxC@YOP$+9d?P&h?lPy-9M2UT9fg5jKm1t$m#iWE{M zIf%q9@;fyT?0UP>tcw-bLkz;s2LlKl2qeP0w zECS7Ate+Awk|KQ+DOk;fl}Xsy4o^CY=pwq%QAAKKl628_yNPsK>?A>%D8fQG6IgdJ ztnxttBz#NI_a@fk7SU`WtrpsfZsNs9^0(2a z@C3#YO3>k~w7?2hipBf{#b6`}Xw1hlG$yi?;1dDs7k~xDAw@jiI*+tc;t2Lflg&bM)0!Y;0_@=w%`LW^8DsYpS#-bLOklX9r?Ei}TScw|4DbpW%+7 zFgAI)f51s}{y-eWb|vrU-Ya!GuYKP)J7z#*V_k^Xo>4!1Yqj*m)x&0L^tg3GJbVAJ zJ-Pl$R=NAabouV=^z_t;^K*0AvFs!vYU>_<|I^#c?>>CR<(T?=%{;U=aI*SbZADLH z&(f2wz_Y0??Tf|g;?|1Znw6}6U43Q#qNRwv1vp9uFn1)V#*4p&%$mP9x&15^OaBiDS(XppT|z^>;B{PLVEbS3IFYV yGvCsSX*m literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Logo-Pcut.jpg b/PCUT/PCUT/Assets/Logo-Pcut.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f11e4368a92a75e0ce5510cea959eae968a8e021 GIT binary patch literal 84165 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 z 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 XJKrAyYShXjhrdOb{+++)&Sw7`>+LWl literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SmallTile.scale-100.png b/PCUT/PCUT/Assets/SmallTile.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..a9c085c0d9aececea15fc4c0ce8e3ab51bea8bf3 GIT binary patch literal 2189 zcma);`9Bj30Ed?|fESS^Zkp?>P$+iXq$4|`SkQ%w7=b)uZ8BT0(hIWP-a1OAz95G zz;KU@i1=0-o7Ee-J-)fQvH6TJuuDG8TPR}SG*;;gKN#`Z8j=DToQ;cuojJKVUyY2j zm^mOka?83o|1XdlA(P?bDCItf5O?BCjGiYS%EZRY3YbI@1+(kd;o>jbAHR8;wJq}X z*$*TMf!JYgS(FtbPKi3_PX_Ur{$BV*&?f-k#V^cEjnfmG>G`n!vo4mZ;>OXgD`PayrXQ0SX9O6xODf>Fe^ zlfjO+jKc-@d^I3$S~${JwMR09Yy+kTCQbH&a=jehnGaChrltA?>j|r8PH3YKlb!zQ zfh(_rZYhir;udL^gC$yd3A-_eW7`+X!kxlg9nXp$_Mv?GdTFtI4Q1fG_{3eeDLB?P zQ->Id@fvBXky`_f5nl zg}T}yeL{YBQBMb2rf069aCv`bpf{;B%_q*|&VeY&jHgIj%tR!!5{}wr^^4KzS)m)N z9tqs8mhW;vY+>HCSM5zUq5F3XU^q8u$HS9rV9)0AbDp) zOA_nZBn=3;(Tw%WL{Q~cz{P8N`=v8~E9dVoC75hSi_+fKY|8J^#AzK1tO&{&&Y8IM zJ!|PeYnQhn;|}^EPxWXw7Y{xF=9nlbawHtZ zM8U@Ud_9Dn)R|NwPej)b#~e&qg?TE3=^)g$~kH&}y%0>$9m39aBSvmt5 z<%1c9>-@0@!9`J?o+`N_AKsu+w05F;Yle|3OPBf&QPc_{2uF^%q)`MCHxB@ zJ$s4A;5>h~ksuN`1ZU?bJzElP(0z^;g-~r++Eo5f8c|8ITq|7SThW1)$Xw%sN6KP01o>L9mpbwCRS~+px1w9;XE*-R z9(`TCMuCJ}m&M0@liz*XARA(U-r;gv;5outFNY+^CN{%L0tL+$LV+}KeN{+XP(^M~ zyz_4|!USI#v#8z1H)Z_{(C5~?(Y+z(Akbk+oua>?<(mmJ0Xh>utS-Znt0mo65NkGc zBwN$;pUE#1(MH>vrY~*T(0PO93DkH*1CrbzCNu0cYOl?rFIB7AN-c)J zwUQgNO5ygiC;3|b##+d?SQ#RPx3BXsms0eh>Y$1YX?F57JGK)kzfkIqx>y*%x=_Cc zIN=sj(=d%*xK$|l5WC(CfcsX2}*F+G%o~sI81AHWSme2W%PYn_u2tIQkV86Er`}ZH1i(OkwpJ(9F z#Eq6fSS8x{9Pf!5<+Ywpho)vqyxjAzaKpS?-DR3{8(QFLBtmNwS(9)1K5NRIre z*FE6G@7&jwHGiame;zAiJ%Wf3hmwRiEjiwnrj1GJ2i#$93zJyN-{?GiImAXp9SnbW z6l7b8L*q4bD7B0o#enXK?Hkcg0y9&=^rktrYWrbL&w7cWJJ#N;F|kQYS|i(_;7^`> ztJRVr%C|q1(N}$n?Xc`q7-ne9=d_J^r#TXkCDckrf@n-xM;3sKucC{;eZA+;(Isz9 zS`MXMcsYnVFzQ*C@KX%j)D8bK!5tc4?0RanoVAd*mtDTkkoe)3=IWgBa_CZ|!VGRl^#_5Ed3 zp2al@az2Q_;gEhG6%>I`!r(X#{3A?0lzk~ab*HLw_%8-E=ez3pX`LMz?I&5V3xr6R k4+l$w$q$ny{%@SI54j&XgC*7Izy1Zl!W02}W$d2#FSpt#&Hw-a literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SmallTile.scale-125.png b/PCUT/PCUT/Assets/SmallTile.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..74c9f6555a891ea2558406409b6a75f016f51591 GIT binary patch literal 2780 zcmbuB={FP%8^*^nX2gt1lU>N3Nn~x9EFok~p|XtZ$&#^-b&w@u>_kQikH;`1G|867 zmJucU*difIGxlY8J?H%o-uJ_GU+4aEpZmjgo%2hwFuMskBX9-)06>fk^=_Z~y?+H} zJDqnq5=p1V?q_%p0{}of{uNWV?_Vwez?oDdJ)Jutg=+<2es?>CyQ7+Z6^3OB`pCeI zE#F_V2xq;8*SREcVMI0nk$V4c*NGJqf^(&baPbcj?+m2tibdzdtA=_Duk!lRXyf5_ z*)#y1oAsyUbF@Tn;xm|`f99e3ug9l1rN{#dMYH2Vfvrls8JlBuzv{wT4-eOT0xalu z2ii8XL!!WYF>n|5+jvPsot*z^slcn*#0I@oT)P$IZ1}v2pM=c)~ zo00_$jmVf(*L`)dVn;>LyS~5MsE2TgU#T3W@e%Ti#od4cb7-0Dv4&%k6fgV4`{I|0zBc3nNIkhgyw^47PT|IQ*!OdBTa?Je2}n8MaTw6yj6e&%@UM0OjB^_2=_q^@_R-c!fF3nOIyKD*$2HJwG zowEjGzTUtQQ@K3&vTe5u+I*-S2fT_-?GCpR^xl5npm{Mk)MD&I%a?}V1k@z>7RXkh z@}YvijtWV;55*;RQIN$iTFe?4-7Tz4uRz*2&Cz`VGBVY*Aj}%K-TIu*G0Hp+7hdB$ z-uyyMqHFfz3SNd>RCr~vlecjO1y0&d#;H79EOUZeASHHNT-@)26IwToN~$k>X+0<< zF!7)uy^vw_g^07v7!jU)r!Ptzol_T#{R7g=LC){+;bflG#zq!?WlsOH+-r94fC6Oe zIPB2{O!ql-Yh53X(orW*Q-Ej%b;y-aW~VA&I`gBoXn0H~a^N-U<8#Nb^>q7aqf6g^ zYbW!wAPKE`>H|jkmJ?(77e5&KL`r>nkfOH zSI4FrpPpEj3^OIf%kH2-cN)9dbc;=oc?M}}uM$p}C^jcQ^seaLl4B_hm;%v33y5TN zlG$K66^J7pUyYLZb{>>)QyU$mQe!I@6Ztvcaa}%RAxp!8&c1+n!nK~Nhi!ru2CQM` zBZj;-dkJ_lg4gS10~JrHI^r0A4qu3T_WqAme=Di|=llej|J{*d+xd(uV&_)#M`*~w zvT%^ILlGnWs6zeox@N-imXzbJThs^z>@?EA!d>YDVtf#ZR`rZ@IIMASyu659wMvMd zMe+am+68zsxW8cDV`sqrNrN*fkV9L8UXr)<%DFt*DyYLOJ!{C;hT~*0u8Z2!~JblHzBbWvUY_B!b@Q5q~KF4xc%yBYS+3k4d-Ce z+U*45xydsmyD$ky-NnkG@K@PTLAf1^n%y*{oTNPqzd8AOyzFI`s$09)d>qZH`|a0G4qquN7&_N z$~@|1+FM;>5UX?Of3>(~J)1FI?fR8VpRGqtc%o|Knnrxg-1Q>yW6!4O7dx^WqAbc= zCq5%}S$f3^Q`PZpeB;y;u3G;1<|va@*d?svQ<+abt5NocIqrBxfhiy9aqi<_ft)^B z))+kT#_wncyQH~sNd5z+e}JgwLXy99<8`(j8q&Q=LBnWS|DzAZ<>Px~uu6dRfT;Au zfbVaFoYsAw@6{_y6X5A)S;_u0bXRoV*L&sASjK2*vFUr$$G~E7X_rs>Gb;PX4Y30c zlXupd_TkDhdh^y1Df|W07J^_XTj<`~&9$of*r?P9*D9R7RQorCsQtdwlRx)sr?My8 zaPA7mV~C*X%_`dzCv3!0sDh(Ckhq;yCtQ4Gre7R&_dFXBYwO+7J(NlQTq!tOKVlGh z4(gdK3TNB=gK7IB1ad3A{or^zwUOnwN3WJhic{`b+F4&9W{A}O10Cu-2j~{;9-H>&-1k?f6jU-Q5fyb*UW(KW9JM(5e_HH#j!0)MENS=;ut=~Re+JcnO?OnI_5uLFgi2< literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SmallTile.scale-150.png b/PCUT/PCUT/Assets/SmallTile.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..d7d9ec62bf8f139507f4868d7e72c5812ecba716 GIT binary patch literal 3267 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 X;*6$DhIG!q62L8ED_E@&D(3$H+Ug$9 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SmallTile.scale-200.png b/PCUT/PCUT/Assets/SmallTile.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..be6e221e407e1d203b0641773829bfc0b4f793ae GIT binary patch literal 4394 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 P`~W=GHP&g=LPY)-OYdIr literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SmallTile.scale-400.png b/PCUT/PCUT/Assets/SmallTile.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..d85279556b573a0c643b1b1c1150a935d9b10ff4 GIT binary patch literal 8955 zcmeI2S5#9^^zTtndhbQ5p(?$HBGLo|q&E@i0!ARz2pH)d1f+)|NUwr4>4K2Zkq)7U zCcT#=aPzy*_y2TX{|~dyTIbA~*|X1lXZC#dM8DG0Bqw1Z!NbEN*LtC5h=+HN?Y~0w z5ch`ujC2L}Aoh4+;f04s(eq!q*Y95HfQQFjrKP4~><`;tB!qpPYL_{>gADuxeJ-6W z;?L&?1M=U7yG^=DUOaAe(<|roI-aMxV-ZR$!t@J;-07ld0zHN}|gkaBQ zt`KXn+HZM6@0(dL^wWRd-ZXY+Bg;skK2>*dp2atl^K%z*cVk3H&wB=RjQ*|v;9H)* zun4do_T_;UEIZ9*6Lts8jq*OYb;@FXmp;NMnuu=+c8n6T3gT1 zge~Dt@pb>qv&kFgxF60@ZH&pv7WGf|PE2VC^pd3X_o4ZJhGLmYPS?fin_klPZ+xY9 z?+AS-kh|ix>%R|`R>PYsPWt)O7k0JlDc6rgO8czvd>CnNx+zX2RTXl5pt3@zb?T#f zA)b6d`Ls_s_D{T}ewa@2u;xBMt$?wVsmS{r%RAu7l}H7P zSq)_?Y=bP=h+vo;+ezQlUe*YrNLNW2xw2&>R*H;0Ld2|mBP+>3xEhN2Htb%N{CrvF zUYT3>Z?$&NteQ%8@!j<$;ne=zM5o*b)^w|a+XNO}NtD_VyvcZIc1H9{wJIJz5t-~+ z!+Lajp4}sC)uOWEol-@Pz0@+ttojeJ^UvCfuPmo)(KK%5jE>6?VQ<^(PrHXd|_3j&=Ycb-xSeoZ8rZM8v)}&N*Hmz^V zz=QkFG7XYvk3d;_`ahUhKX+uEa6)b2hmL|D$N>GHex37S^~-*F?zhzrnov;5wsM{3 z;YTF9C{QP|;{OG0ut~2moUyIxSsgjgtq+jd`G&xMY7GYpsP0q}<%Erk3|93wSu0<; z`N-J*UO9NavR80!c9?Twx7S@BCRR}CCjB9sgmv0YG&6|ears;}+)o_{=DFTwpjrNv z1*iJP(^Eo*uz6he{YTs4UX6vqBUv0^D@51RdaQ>7#YJ-?4? zzk|X43Wb!!3=}~%Kh@YjcIL0oYrM5vIV*P)z#^I*6{xbqo1 zMqjew3AOoV2Q4DUi=(CfkgLPRVyx3%?XG{Aei@zdN7yw>e!J7n-MmG1??g@a40>M@ zoZ+dii>!>rP|04JM2gOp<>zfiA$mVf6Q=vhXhA*&kOKvj4mTVU(JY+Y8FN-noqD08%~6@DMr7Y<`~8*wu<>2iE!$ zE>mxAtz4(ymN|s4&+B&sLIp`GZeK>5_$+(5Y{nF)YGL-O$j_V3-o0*6z*$Fpza+&H zE?>rLdys@s|LMEF*=rSHzKoAhx*>F6_tro+6f{smI?OVo-vYndG2RBZh3h9t*s~l< zotluxR+=Z}w)(T>85vG`L$WV@j%R>4E5G8bR9TX!iVHHr%DK|RevA6)+SK*g=%($J zXq^w)Oq|)tlNAl-OGE~lobSul9|D$s=4Sj`7CZl1#7g%j;p&L|6N|3>=qoiS=}u(V zPFV3S`p`{WelmViwi$U;q<+A!H{DDMm&bs`FIaLr=?q;Na%C(uFL3|=3l=DG@YuaO zMGdrr!Q{LnD+G3r{+>+>B|Oj^t$ai03n{oCJV6}sr?1$qQjj(EsmewcpWKk*d&4nL zAf{q}M*CX~FEGtfqT*7C4!AFaN>oZ=lZ(4O7V3VHmP6JW2Hc2lD~n-{F0NG8!_Ws$ zXc58mM5E)K3u4x{v{#(4;^5$-(kZ5ZL@KGQJ5IMDxiuggWp(QFG4IK5nT}+NA#0(0 zU(gr!u50<7wq-22eD&UFKa_6~xWaeBwr&O+Tsufhk85+K_d;aH^cUOah%{}{1*er{ zPNk}dv6owj8-PJF&x}9MHu*gD6i(A1<~)K$jkKhf`WU%T7RHJI*MH_&DsaT7z1^mg z1$MhLQMm(_l&?}vT}cM1Uy5ty=uI0e^A+B0V~Px)9H#SsZ7QamvuPJL=M}@6o-ARF zJaj1v(`T)pCSI<_-MGKo<|2vgxsb;XuBUZLzsQ$meFet;smtpdEQd%=8<-`x+|7ja zw+1N1e+YM^35d_1Z8`uP=1ah-ho5w~e&~Rfc&BN>^N6W~gWUGB6scu%Lc8XOC!BnQ zj|sK_Cks=4QS+A3=j__kSBJ?yaP_q&=&i#_>6B~7D>UX&OZ6a|bTqPsG8x?mP3rVy z{-NgbBxR*ML*c;DBC&RmhJF<~qYZPuGU?ToI6D<3#Y!S_9rR=)_X=yc3;RD?zrLiS z4xe!Zz;iu_GYMgpuD~UXrjA2qn5BXfi*&!mQBD}noKZ&sxm3WUm1T7RjZ9k0GO@!W z$OC3o3M0zHi(e(B@S0J(h9G|5zoYPUED(Gc0pWlgDvNyBmg4r$dhy_8fyTb)`+>!K{m{s`toDsSK-F&j zq{N?!&_AVhP)+IjrKG{fStnQ^;O{u)0+)w&c#44(t37<(Hv=ubd*7F`%)dcPa5_`Q z>FklWyj%??8Ix#ZvsMTiStbOwT1nc@>>MtdUf4^61(Xrw7>wC0T zoI{QLi5N}1se9|wltgx4V=@q?eD{K2bizTmO+(V_wxQkkn}ol^T>{a+t1%vU_9c;= z_!DIdx|gi{tBy3O1i`cnEr@2eAnkK9SHj33eUOH5_;{e9`pp?W*e0(yMAgUK&-c6$ z7U$fLu#(HhM2_%!O_p_Uz6sc8#N0KwhuTK*?sTSjc4-wvTv&}iZp-sOeQ0-bUV58D z!n`P?Ld3Q|udn%K;$Xry% zTrw{%cpaYQEtScW;MU3ZSF{N|!Vy}8zsnG1sV()xVRR9VEGR3|n4E7+Iz6fS2WOfp z18(lU%Cc}}Y9Dv{8a^0G8t9*w|Lk^x1j%s9VX!}`MEZVAf2+JbDEN(^HpI@Ra^~2iBpM=j{)D z#Mf+>=o!J#r>(9Du1lqhw;#!;mxc^0W#=@lwKPUQ4k#+hT`j#`N|3(NW_-kV@ExJF zd6xwEt%2S)`o_%j`%flX88w1=bZuq&@KLGj-`rwx_|}QF4_oc!s(0N6WFzvmnnr;J z^a#aAev|hUuvL^tXhV9o^DekcK)PjWYl-I%`GsP;jA`U8jr;NP)RH)u_;aOa?VZF> zRdlIV>6V^5_m;adm*1+9%3VV9b;HFWDspVY$ck+3BBboE|Z8K#RHypbDrg zzsk;NyTqbNRF+hmAi)yEZaNm23sd7?La}8cX_oCb@>%uA-5Jf*lZPB>OW~7APLn#b~?vs7vI!9Sww;;|OiR_`Lg`Y|07{n5AX#N?%_%ipB$k09x95wR+Ot5G3P@_g$mHCW^$y(p zXqL}BYrhznDGygCtMI4mz;APyn^HQrMY@lB)`e%|y+(kerTdHoHWaKEJD+h5>fv-^ z*d0W6uytGA=!0#rW^1~$~hFG9tv`iIX>zX6wa;e@y_p4WG!mvy|N?Gvv z5u92ur{|2GBDheUTH_fBR+2R>D*rn5mkPp%+b07_fq;mu4ckbigDLZLHt*RSin6vc zb$`wuCl02S3Kvb1fH=P-#Ysn+K!sMMFn$ZQn}h`(DyT1M)|9qqZ@iL6oI4pT^LK=B zc@#Y6kX}k=ghP&rA1m8P-aXz%raH-AT;#%Y2bLQ4`by5yp@Q z(~2En`X^1Pz~bL7t|h_mtP?h0fYmf-;OVb%TPO{%{C!HA1NW0UF+L5Wah+g2Dm6>q z5zljlHjt2<2sUJGOPPK0CzX6sa91=D6w7^LHbmFCvGI$8Tqa{a`jzCu$3vazt5^hE z06X)K^8L~G@k%LiNqosNchn6^vCu}z()|qIB+|Yq2W@8WTfW1jad+>eJj!3R_xbKm zviIyVlqx!zr_1#HTu8L;!B?txWe znUA6CZ!V#=O6!&f6<3y5Ld%bh2q^x^jN`NfxxSV@@n;wIv0alu9jNWrreE22kcRViSjYl$1z^%?04c?B^#g@dFy;(NWoGvxAU3=re=~3`p z@1M~0u+XK*1=!(?Lw9;L1;G}Ci%WBs;QL#jS@HW`?d_rA0hbnFo6Z!BlsjQ|ZG~?2 zugpD}KIs!doI{U{j*qhVc0EPX}d{%O$4xTqq*8ZJhQH{(wA2KNU(sHN<-co#V zuon=D^|Mo#%@-b!C>Nr;UqVAqfsCCYknm5;s7xrE`vme+Zu(kjG%W6g(n7U)GD1ji znb->WLQCk(szVuwU@^aqC$tVW+{l2uL}9;!0}TBxLQ#Ix_5ozyW)V$EGXKt2Dk}hY zWDMz9VCIK6)`H~sOOoNWKT;exp!gQ+oeN970;Z8}{Z7+gdf;pdxk$Oo3XtNIFO@pS z^GL(ct}?olO~zmz&6K_4WnyQ1uko+Rqr;U<8@GWC{;$_hi1>+#VA`vW(ff+Ym7${) zt4ynHm^Z&Sx7}$1PVcSlGTMo(lHgjQLV6*-KI%C(o>e#Kzt7qpncV86B=)mhkmP=e z-O*dUmu0W8GTV~Xl7KT>B6TzEdQY9QC;xBoBuK&leBMkYn@Ujsk;?(s`|tKz9Xy=S z{|78plKk~=t*rF3nioVclI~t85)ok?=X*kA8>aV1WyP3eg15s~>^M!gxBQZ*fnQ-l z?KT3?TU+A!e?JOo1Wt&9osoJM#73tjCIh4_mqmz~cgUGTq7iP9zI%{Yf2m~YRgrtX zA+XF&6F!iorj2@Kqx+Hn51F0;LUQ-pugIALPQMF9LA>$rEuJ7L2e1J3(7&Nw!M@y& zQQ4ig)YtkpT1%~?$w8B;4rIBtp|;+QLHH`r4K|$jwQ$R8NUQQ2-9L-gUkGg{++LxV zxoD_lkDzk`CZR{^n9w>Q=d!E>pV>?Jc6(xTLnvp`A$E8WkppRB2`*@?x*q2v$4chv zRF*SlhVpW{a^oN-kT+EIqTOE(?7S@GDssnpYSSU$3XCjsyB(NAwPR(EhlN5u$1wi5 zK9f^ecs_m4hOcpk2sBE(h#!n^Gg2E(BYnI%3eUXNvHP)H9to*V&7hxA9?Aik>I%56 zO|{BoJ^c1;<=K|G7`5n+w>A!*iS=P@kiW7<=Dsw}*-=vzoamwD*B|egeMF!`L7sRYDX)5o~18 zY{|em!xcUg_5~}+e2#I|i7SLYbLtHJ)hOQHs{Qv@belSqhqvh;9zB%Z(Wes5yj5fyDrLSv!-rPJm9v-X>iqQCP(fN*Gk_@(Ue#vuK zxP$6A5o*}Yv-GgoyCq#V8Ro;0E(=;=E-&PR-pLs7-`5o`Hme71fvgbsC64!G4{-on zkbqG}v0_)V(phnCEkZ_;B@kCK?3lIz_hul3v}Mlb9#YRtj__BlKU*M=ZueK29+#?t|VE=0AX$y#cb z^ls;yLYFbE@~(w9E`NJ(61mXuLB!YAhrWS&*7Dvlt+BU)Zx(9cHi{zt*0XR$Nt{o3 z?>|%j+${IhU_vZ1C>1GlXojXnTbU7JpJ?zw)dKY=xwGKgVo{ZzTPbp@G~&rVTyaW1 zv(X-l&%rj=07&ee9tuK~Mf15U_+mwC@(UnKc{7+V9>PMq{AhKxMq9>IWt&vV{&fXPAKFR zJI#)jL%ZGN;@)guetGm8Z1K67JDtm1GbVrD&+c~q!tW#9jSFgrwk~fozBS4nBRd;i zo59+mn$G31;^p6AW+7RRo4lJ}dJxuEWDwF^;}WNV5i_5VlIBc{Wqjk3dZ`<-<1h5{ zfh-8@Ha*yJt4YB_UmAZrP*97AZT81L7WibVyG{DSSF` zzScax4sxi-=xq+QIHi1!Q<%uiH+u_;qQR!SAi3|h2SW^CR5UgCK|6=^>v#R18n8S8}lKoDt4h;_E4;_Fgy4~M~W^>yI+_C3Oy z7IRDbDO`Ttw65;#(DnXa)CCh@O8*q#zz*)9J0$~B;wrBZx-vil>$)dP)zpqhYB0jZ z6n-U5D0T3L=w*LRIf%!c_+lcETJ_O&*G6x@g>5=OTYH7C_Uu~B?}OX4UjI{V^#+c} zOwR`EP#nt;M<;MsCk&;Uoz(=L$ULE72p7Im_24xw@J73ILaZgmI6c{kP2g2|O(mMS^zciweV*@lk_xr&X1@R?L;Jk^XU(gtU=){}?~uAKElLEodM zAw9ueJC9TPb9I_pWcombX4CNwYhOqnP4K4u(+Zpb58Upc9XiltbF|R>gxR74S0|tC zl4G+W9G-oOi#y7jRdGAKZZJqx+ZpTBzcCtNomPso&#>I{`d?aKaAKBp_RA!47=;=r zGMXj+ZrppI8m7D1?HtWA>eze1rqMbz^v^>+1cp0CpK3Prd&+@WZT~Pe6k+=VkZn#D zxQWX`pvUMx)K*_LtgP(xWB#>VSOOW|oUM@GK(-ghH3ufO@_3orJ)LUz->2Si5D&Nj z!JE!A)Dd~r*w8IN<>)>3ddT4`=+p{+;ZBL0rD0t=A-`BnpMYH0JluabellkWHhOEW z()AO>vlY%d^8E;=7k4oqRGgk9HsY?hJoG@fIYm!Nvg-kn;bXE**(uW@RY~ z?L{IFyXm@Z4@O?h#fdXR<87Oj|FbK+Yp3Aft{F5rcm7V^(@*IGGYpxNkX(O|Sy1DZ z)yby5rgW`yt2ak4fndRs<4~w}<> z7|O$bb|?!z7{X{q?~8pk2if>!%2e@mKpe-oDt#A)mM5rDW{M0lrGWiqn~CO||Cp~M z5zP8$t~=VG^JQXag<&7p&Jd%q7n#um^#hTar17mTOsF*|Ld<00h~X~O{7IN%fa6^P z8Lh#?j$>6HaHyT73TEvH7*539@z|kHz9~3!K8i*jFkqvg%)BX;c9^u&D?`p%m>E~%)d3jdL%^!4T zH==)$ppSRnMEB8ie86hm)%OlxRbF-PrH_14(dA3~S1dd!zWZI+Q}8_-R5e>?Yg($h zk0#>7=dL+j#SgE6?pF5868ww-k5eThoWy}U6TvTD0MuqtnAEBc=%<=KDRb&2U0j+l zzuFeN7f+6_U+^SAHdC?>rKan=RcQ7q1|@jpKa~G|8rqM5Y@Ppd*$=QyFN5LIKw9nw z8*diIwwiN0u=Qb}*KGBui|?hoYFdH}m4LyY*Ovhu5YVu0MXk>}8PyfB zE}7y-=gp3iaNabb=X_gS{@R(Zt2v#i0unk4hQF~^lvMr_*%MC{BG98Y*0;c}^hI=t zm|RaMl7(773E=~T_8-gM_-Tjj^){WVqzrz{e9yX=J;Ks-{tL2EUOQ+yLM5yIx78DC zdAwRaU3N}+B7L_IH{OB>8&~Dk8(BpSrp~|Ms6sX7M_g(2?ybmXy&rMEsJkr$3Eu@J z_DOm52-^Ujtd$GwUbbk}A@gDe_dQQ{c??MB zt)JYz(Hly$u~Tj5X(06E-P)Q5zh{ux769*rtF8Y7tfn>Aae7FWxxGIAcE!1T;F>G% z;lR6_cpe=uL*CKZOc@ciP}uZ>Et|H!H{LLpG+8n!RkJTJU2GF2yZr9s`|z#PKgBBb z3Y(oKIaX+;R^k@+hXi^=t{=?XUj%+}ryQ^xPUE1g*5&orA9e8Q*qRbOutLgssP5f& zx>_4_{up74Lq45P86{*c+kDlHGWW3b5F*%@2Qt57c#o?EPz7IWI3lg__@Djn#fJay eyB)X6Oiko3Jl-*y;BK?vX{qa}RX=|l@_zuV|1mEB literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SplashScreen.scale-100.png b/PCUT/PCUT/Assets/SplashScreen.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..dd55654f2c8b844c2afdf5675b981fd03b4a86a4 GIT binary patch literal 7484 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SplashScreen.scale-125.png b/PCUT/PCUT/Assets/SplashScreen.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..7e8dade025151fb90dee1912d25445c9fa41db81 GIT binary patch literal 9442 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 VnVb6tIQRp)1hccMxZv~Pe*n&DjFbQX literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SplashScreen.scale-150.png b/PCUT/PCUT/Assets/SplashScreen.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..11f69da36c28a2a0aa57da6d81b8266b7da3cb94 GIT binary patch literal 11770 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 Zigkd!?5!;ZZbyU8!7tfYpT73j{{TW@x|RR{ literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/SplashScreen.scale-200.png b/PCUT/PCUT/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..37776238c7c9e7f30468a8bbbe63827d5439f8f4 GIT binary patch literal 16378 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 b*!f}kO6IRhtI1y>PhB^>V^DJC-XH%5GTLJx literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square150x150Logo.scale-100.png b/PCUT/PCUT/Assets/Square150x150Logo.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..20e93f5f222620a8217b13df2e403e538b6cec0a GIT binary patch literal 3397 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 Vz`T1B_G=fhS(sRZUxU07{sUAfaBctq literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square150x150Logo.scale-125.png b/PCUT/PCUT/Assets/Square150x150Logo.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..1e4a3bb87e1e7cbf347a0ada9e1c18905a5f2324 GIT binary patch literal 4090 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square150x150Logo.scale-150.png b/PCUT/PCUT/Assets/Square150x150Logo.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..c53a1a9753b76fdfbfde4ee508999a2e7fbcc269 GIT binary patch literal 5081 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 QR>uM}v@odDyZ8Km0NxLP&j0`b literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square150x150Logo.scale-200.png b/PCUT/PCUT/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..f7caf6194af04d34c807fddb9098439caf0aba9a GIT binary patch literal 6880 zcmeHM=T}qPx5X<`uTmr+SWr4hQ8ZKmDM}|G1OlN+5do$r6VOET|@{ageFx& zqy#RY^df{Jksdms1(Lkn_wDx&yzxH#KJ0PEIAf1>#$J1!IoF&k*3?AzBAWmk0|UcF zeLbBg3=HR_|6MFh^fw{!q*wF@tFNB*O9qBZo&TLiTum7?Umf=p95#0ODFlE+cPxE zx4#(P`S!!lF${qj*fOP6sQ68s&Yy*MXa%~!W;6_N?A+R_VqFL>IVfNi^h z&2krub*q_T8s3rZgss#iQ^JfWfgA_M4-}xT@o{6n3;lBk3R< zL2*A7yk)5E&MDowBp`0`a`X6h%0D&0@xVm3jJKN79u}gNUc(I6o_OVX*Huz=6C1jf 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 N002ovPDHLkV1ioq;!pqp literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png b/PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png new file mode 100644 index 0000000000000000000000000000000000000000..a712613ab2db1db9caed427b6e8ae956d9bcc58b GIT binary patch literal 1360 zcmV-W1+V&vP)pK0!c(cRCt`7R%vWiMHK$d%)Hl@C2VDf7Rn+7XhD`r6bU2@=6vUT=bZ1)KpPr1kdN-k1epB)nj z+@v&)#!Q?09d4+mFW~$)!&{)P)r?*Wj@N5aV@ZxHEqC}!K%Dg^K)nY71$}_19h_^E zcQyqI`#cEB#gD}XydqaIdJUI1ZlyP1O|Gt_(mAXx$j~^)VI!=+$!@l>IGhCyo&;UF z8m8<4*oOUZhc?5>lcVb(o%JpS5Jdv1XjBwq?R8Uf324}5gpMVoc#eR;cCun4aZG~- zl|fHf2s7tdP;puKqz8_Mm@6QRd11QqrLhD}9w`TIzY zeg%7AgY4NWv|4DRJ=LSrBs1sO^ONlZGjk=(?59BkO7hji;B(c8k8FcIvK44+0>rSH z@g%f3poUKi+su@uFyrPU-t#u>{*T4moRCFn6{VpIEdz%lv*2anvM^2Xl}6aw?_dvp z3fEj4%GA?HaSqAvAHde{hN*l8deV~AApy=TS_@bI3vj+#=qf-rKqvu~8uyO3okO(a zHMqK+0o)r_gM}KQ$F-akz+=FrdPLjTK-ca>cG+8!myzKtWsCAX!nXkADIp>_p~BVv zkgtD9xP-4~JOMpsE^Olg*uz`kj{gYcTH%g;12^}17y)2TZQQtS2Iy+dB&`^sN==xB z>?7-dqW)=_md%46H51Xc7hwPRJWpv#U%uz=0>JN-Fxd)-lq899zQHrf_5$@Cl6JZ7 z?&hA>e*oNh`7URPl=7wgzW}@pa7#VhWVi5s*8s@$f@`Wq z^yN!Z`zxSIDnqj+hdv8W-NLKzW1ph4?d#KoHGWLi!DZ znZ6t}RBn&7hu2SzdB{ zJ^WN7D#CT+awmR9{M&n?qE#~Ku8Cn8PGUTe%4kXfm}_JvJZd~bCyHrxi7on#< zCKdA_?CC048JT?Lbm(qBQac^<5~t-RBePBh&5i)j=WOxr_K|jdN6VRY`rY}(k`qND z5xTG_J>uQDB4e>iB)WQ282U;E5R6GHDzBQIIu}{UEavsu7TTF6`NP8rzdmncE~=?< z7mlv9$W)M4{b35Zs}wwtH|qbJ@br1ZOnENSpqZ&v=>L;gZ7Mo9V6V69Kl}^d5G`Cp S$({lL0000OMH4xn0-DMb@JKwGQa;yG_ z|6%G()toK8r%(6lwfcNlQ<1|!B}D}Q02m7L(&_*J4D>4u02vW_F?1`lhF(xyYm7?FD~YgRBcv3<+LS6el$7Nz zQK@fr2F*mjR+J`HzIyMB=%BS^U~v%H%J=u zfMBk_*UO@}>RNK{9-%3vi_0s8au3aM=$fYEIxx@&G#`lwjNL?W?h#hdxR(DRTnGNN zoT^qh4yBu?A;PRh`r+v8=xE46HW2xgnMUQ(>gtQ0MC#FS>Mnx~%L>>z4JfSAXlM$t zux)spE7I7rN3RT&-;g$2#mA@nO$Kw2_ za&Oz4xmh-oXq&4Xhc4js+HO*Ckzo=c!Se+7m!z)bC)>{6zuVaDe@QD|PAGQEc}w!X zn2&fCzsv9i6lGTuB7Un^2M6htVeg=BHwY5cbh+6AYfBwk2DNo|+Kf4zV>hr7iw?BloQxh-Y&pfM?=Ns0!x>lYQVLi!7-kyLum& zwetwojO5hM=VW~^O@$+1P-{}S+7JMDC z{=ZT!hAuH4<5+ET#A!);0ojcmy2&Q~KETcA9r`$T;6^9o2Ib<^0j%0>CyWtk+ig2C zk%?6fR)K)YPwGw6sYLn@;H3=dfd=+H^|zrIxj+s6&dJLa3&#Rwgn~feK74OZEo){6 zy|uyMbt;#C*tXA9m5#b+Yy^%rv-;U{@>mitH}Iv<@#zAxa&y`89WYgx^DxkLI9mK* zrU3ISuTIj%6&SWX$HOG_bz9twr%zNXqwaZbWA!i(0d%=GX7kT;b?37A4`AdtrApti zJ*jlh+wq6;mk;!Y7SD+Nc2XMKlFQ1ew?8xp-<%7VZt~q$>PnaXHJ2~E8C0_XPP;c4 z^(R~WXJEVVwuvRmRfZlgBGc*oJHD?tdW@!VJ6Jh=f7lcavOkxRt!4n+a6}L2{GBu) z-_#HZ+Du)2p+QHK8k=6OJ5iDd$I_%YzKgWigC6l4hQr{XOf@awCdAcicjYx7vA^l( zI4e^;SBo=ej>vINNFh@#b~>n3do&dlDR5DLyGIW#*dG`4?L35xW*LG;_m8-{cM5SJ zWe=sUhB@QiFV4V(k*X(uS!XNf{5h`~Fz0;ao;L0(W<#9g5qFJ5$qZP$esj|AvxZ5x zM~$@s&!96h{t{e-%?5jYewH|Y8}09Dv6;LVnN$Rvl<353kb3Ay&`_-;Vq4>=swJ@} zDl%oOh7fqbp9%u1>S9MLY>P6$&omIH;4kE`KkzHGak$N~#YaHul7*b3h{dFj-4tD| zmm8Q6r(AEoOWz?_HSZuRtb?6w!v11eio+*53A{aTD$Hx^CG+)TNv z)+@Ja{4g$JmPaqEv@SJvKALgE!)8&aneitqw~?~1QU?m31O`Gzx%GuyWMS3Dsw>ex zzIpzrpJ}5r%aRRt!4>SX3E&78*`BR?GARVUY>d@{bBF z73+VY=~33jh9}iJG$7y2l^K0Je98=1AXKf?Uz>s0CIOgqipoTd-E9WUQ*5Uuu>rO` zChgBvWrieg)pfGW-rW!Z=U`PTvHq)ntmQTRh|i#)smo*bd<2^}De(r%X#C7N>anSH zMxj_;e{;oAA;|i=S->I{RgJ@6znm6;nqOJI9(RO-Z}yYAGO zEuiD8s`N8sj}>TSDay-b+w%I&2bWwDqe_^xan$umRGUi{Jdv;Q?di8E1(28G8+gaw zjwU*-j}(aDGnus2zd5rJyjK4Y4MiYrieEAjgXx{Y`Z}gK(R+kTYNNjj+@S*TRy6?7 zV&>bs8*+T1e>;=7=!&@|A=&wyAIy8*$Z|lH;|hw)j|~hWAWQ{^i(F{1u>C~#UN+fg z55R5kYGs54u1q0I_({ol72@#mZu1yE4;3o}f&4FyKT?T)gjwS44ARm0lh0D;t6(uY z@OF+#(&f=xIC?d_R<-NDV1chIQ62;mp@F9#&HWNA-VB zAPG^<3yJ$Q`cgwy7e$ydqt1pL?!W-H(7ri64iU~ALZCOw&6Xy|Y5;N7b$%r818SX0 zK)f&`^24G;p(RFEVU0KZ8o0JM;7|v9-XOra7g?gC;G)^89#;OX{>ca_Ph0G>m2$5J zs(32DaoNhYR{YqP%gqD%&2=4DuO@i^J7vMlCD#y-t#%@5o>G_Ogo zwp>-ebo#PfkcitC>T%zF#4*HDO6;UC7o3o3HQp)IQGPz(Pi`~OOl_lM&>WM%Ng0SI zP#1!N+pzhmvW&0whe?DgRlVL65U3ZnFtuX>%1;kupT`u6yNO73WM@6GX^H%C7OotE z`!Gok_m;+0qYxDT=_Vv?ExsZEd&1=&XO>3a8unRl-m{pY+$y zXI%Wfq#h(|`#~}%>n1Sxgl!7oM>+;JTdSiQgB(aQdp2z;IM2HQ*PIbQI4i7UjP_ri zIoHs*dH2GA>K;wMTBk7h{(e9Ryp_p5`b!mM7Q;D@uh_#XS6kThQ%<{BRL4EN^EEC3 z)3ipEEV4eFy=eN0lsbCKX>yWDCDpmebUPy^3~tgd_F;mYh$<0lyp!u=YeJ^}OJ2f- z2$TC6?{WYV&H-8Oo8##2_Ie~WuwRvWq64qIH4wEP495`^9G=t){QU%yDzx)F{D3cj zj|d7ZbWiYW-ET5W0#*h#xT&D^tud@(A)WoCulu%fQ|o*yRig!mC&d2cLE1$w@B zW#S5V=_bAFS0ySRaJ6nb1rbYxm-Xm6vtXKJO&d_*4)U1L1!@4~rr%+$9xeXTkEK{I z^BoCnj>YcgBKDAa5mce%FF0Hz2ryobAfut$`EOb425^Mm_Fit+1Y+JyVeaGbu+T(j z)@`9HaG>BM$H^oUR9vc<0Agqa+fRrxUEe3kNw*5^ItwR&FmW>V&Vxr+=vU2%B$XI? zQF+l{Ub~EE&Ooo5*kqfbj#bz321#c|7V$f(Y|KkXe|{>l4q{SVf-K;JDHU?YP!b)H zV*yR%=NBEoeez3D^85Du5Z)Wapk=ld>t^Gcl=ic>XEkZ(0Kml`oox_=dLW)&ykUZj zjB^+6W7IG8^?8&N!dk&RZqG9ct?Ta8Yn&6qG2$S!sff?Gf`L}+k-a5%>(}{ntPs$y zvI#mPt%#_2obyrI)+>8x#}OUjA+`t7UiU=q%r5icUH-(0XVVdgCRvPae7{*gus{Yv`QFxV|Npk6wvk zX@Bl&*CXv0L;Hk?+JPy%camA>RUOO?A;LD;2&-D(jexiI-x`<_2)2)9x1{QD*mZTD zm zEpn(#_RW_{en`Z*CmdmT>y$AKy|M|je&S>m4r*{LppN*o^czssvqSkjzvvpx<;1vr zn90a_lQMu#x3ufc)xCQq7M-R}E)e~`hgrT8qSRJt61vtg5sttpvEF_o9X`Ew`&5*$ zESDSI9}0{fyLNM)o(ger2?djI6(~ z4|I8s;?(_nLm+t=YcNIGm^G*iC^n;_0R*+9yx6x<{B|S#l5+pcPNekf;A5^SMoyK( zg+agQivJpMkk{3v(4~lRca^ex($iLJqENad`38zCt!X-ipS;yWtY^=M{e{Uv9JJ>I z?BG>Vl!3+iXuH`fQeqO(G|)*9SJn1npWvZ(XTk5|Hk#)LjVS-E57K^d*DX8-H$>4` zo6}^4p?pH)hjl%*yDTKEy>+hW?iu8+4-Fy@-LfyE=8YW zKp9>!>kpF2S$IeN>QeIn7Qb;M}+bylhpRY4dH z!0bbez3rx7b9yXHOQC-(N)+R>5XyE54`pC|`QYU-q8k38DuSQ<9R_%}q{L7OKiZ3a z8=)wkQ(?RGhABMRXAeT07`5rNm%e_@!`_>2q8W#;s2!X(n)r)gY7l2?G^)o_n{Jx4 zt%1=IGhH{dQDQif-K+S=cQ|(YkaPUIbW;W_mB8}qvCs7`_NpD0@<9UD-d}D)TJ5i{ zX9)?)2-d!zUM~|a+$;3tWCWsA5H8*Gy#Emg`p8Ei4Cdne%1vb%>M?p3Tq9_t@nSc z@JZVojA8A)j_0!<_Z~|Aa=^&*+P5Yobm1%{vU-GIo{;dEeR~P)U-+za7o#J!a0%(7 zVlC8Ro9Q7LV*}s~Jtrk8+fL7Y+i!P4`V!iBR*QZ5e(`a4qS3MM1slMKQcf!5WG0hs zzjC++YrMeyG7BUT$y#cctS#yPHKz-=qqzxmQ?il57W3m615?wgGQj+J|8M>9WqQ1exW(~!&T+h zzvt>2)iNUD2+r?J!mwMar(d-wSCkjEu@Ve}l*`6keY?8840)onE_7plZ5AJVeAWz$ z%QHi*5|gWuWUBWR9e7jzBb(2ABD;^{&v3U3C&ej<*WTJu#1i;lFj*#M8AbfDgonu$8$TjmF`;c&YgK< zpL!=Q`0BK_J2KPX>z&<1iZoY^D0cjGqZDczfcfBpPP8>%4!H07oBgBfcKzh|faA+* zB&VyUX+*H>Bh)CcS(N$9?@?m!>5(@x=am@4x0dh?6JJB1f59I`EMoP23j<$xftglJPJ6fO48# z!8;o?Th2MrLq6Sr39XHuA1)iO1eWWf-F!6b!D~37YuEsa!%E}L_zGFCk7sCI1%?gN zqp3sN*e6c&#?Kg{xL(YqmS!t$Hr#9Klj zJo95b;OEMSn-2pA?RBynW$3%CC!f=zybCzj@8=q~I)`Xy zeE%5BUi^E`)v5l8SdvMQbLasZ$_kh}W!<%*7Xht%*k$C-d?Oi_S}5Qlf%9jXAY!%d z)q5$<@73AG7(!@hh-+Qt@U^?y(xxEm$UdYl6y_bJvc*I@+DsH_y|b5Exd|8{5KI~| zdgMRH8G)nX!=Ob>Ger&FV9aA(v`3}YdFdcJEF}wM!k`*=4XnU%do~w9qgv=>ZeM!R zW!L45Z>%n1*#rS@0Cousl)gwaWT1A9A4fq=6vec^-PNi8n6}%%@oMc}OEn}bWjXYS#8)ZsE)m^aE4gs_n-()rn8cC)hWJi4kI=H8 zCO2E0GM4wsO}EKPe==#CimHGhJ@<*$?HsFD=w8D9jS2+Y2!kJDYJ}7NnqHmaOPjHd zfgjZq7{UwEEUxvF;xrfNs3!& z-T!_sbe6H-xCA-v!M3z93BXc!7Nf-!(Q0&^lLRf1Ml$qYrFC4^_(}}^NMPzWIVWJD z;cu@#yj@MXJN#wW|4K$HD{2BoD52^EumJ=+TiOJepgDXN_y_<> zu-dhT_fksQ^(<2mF`4vJZ-+v()an-K*&+9DDfj4I6QxM}7Eipk3|6?pI-2^8y4wQ3 zmaYr)nbI2x$lRmxDB`?*_K^7m{<)ze6;Knl*2=7AEY1lZY#3BAv$ME~MWGzSp_S7u z)6qW>vmERe*Zg(QJSRw&Qw%db57Ky{)wW95S3>*oFo?3MinhrNrrE(DLabcJ`r%xJ z*cTQC2T<-lT3yfYSM>VJ_nu@Hh%xWN--Q4P6RV(fA=xSh8FPtuN<44-30Wdvhq-J# z{;m--&4qm4Con7tFYs#%4;BprE@*~MWWLKe139UI`2jHUL>2{~=R8D%i1De{gY7ae zN?Nxk-osAL34J0rQwK8+)i}ybBq#Bsmp3B~eA#J7jYu1qRE0joHJ078818x*)-;g}KtQ}EDT+yU$n$?BT?Jfr6i zM)J7N<9?@!rugw1I~2|JLf8v>9wf0WL@Zx?gM$_qphEIaMjtSen_EDLiD_|N(S~2L zdP$i0Jpv%lp}3V}jQ7*-O9cY9(mSlp{ATL5(&Rjr3#QMO0!L-Dh2obCn#U!Z-YAnI z;n3O&V~;kI2YRGoq*TU{7{O=Q#8*ygY}iBt#dzy}qIi{Wzxv28k{PM(b`?&pKUzFg zBd$B3V$jI|GdAbq>FaD+TTxmoeSn7XkqEu*3&F_ZHHlo+auJ-+RA;k13*7!eSvK>R zZD`#Y?`MYom#}hZ-2sk89j`QyVn;fi6Ekc?IFH5&o~2)(b*f_}w-j7LXIuv|UQ7$@XV}6{qusx!~8(>GimO zpx4x@w;ncCSJd9ZW8Lru>}k$w&vV`)=wt%9mzfLJ<~i8SdL9@{avPh}5QKv3vxSdA3lCX!9_ z^xFpAOV#pimu0778zL;!&j<}8_qswC;5qIDd$G-~N`CcaH2T-eey4Lv0*e-5aYA27 zSW#yRbN2BP8rkxDp1<&H^q_El6ijKlcJ`$z*SQEQSPLLscb2A}5>q zGpQWXLn_F-szV?j3m}iE1Y)?++4W3T-sXj&C`KOCnNI2#8)wOisnNPbT*JoqA&ZmF zKmX`F-2z=()E+MWZk4isNiF;m!7|G_^FkWfEHLR|0L$WTfR^e`wE*uw^S@+dTC$0@n$mc z?X{U-+f-WI0=`Fe>({#Tx@WDgEN%<4=+l>Put&Nm4pVX--@h3fAJ6Y9(bHf#M=fTMe3h=hI)T1S`U&+q{xn-)q_T_9SIACNX6moX8EL30!T$Q)6Us;z7Z&% zsx5e;tk73ioQWFGltkpW7z`m#JFi56_hkokJ)7`SHEg>G0E1X~dOzz?L|H z4?Zo@RJ1->jk`51s|n1yV7ncDRf`9wf=NGS<^ir)zCBjrRP9K3;J zpO#_dvH3KW_C_(kd&(lLh%VjzW2?qr+xZEl!nur!Mrib7_L%Op-|WJf|2gZ1_;%FgG{T5Y8Hc_G| z499rNV_>r_B@Xsdg?F=V3HF7k9!e3zaKZw*?+w|-C1vUZ?f7*^k*jeQ`%c8l+jD>h z6Am_K{z?kcFNL+xKc@&3a!AcLex0sx8mHn$!)E%~E!QalC)hAJHUV7>H;mUdCg#=| zK*d8+)#h+}eGek+i|->9MgT1mN5f>WBy2fjXK#jlnR}K@q$|uiB!)r$1RS=UKb0zb z@B^`X{3k(FS;*W{qArhG-!9_R6{;vEik38@tv8b~cZ`4$*ml)449JafRSOs?WACDq~;7Mj9>5WVix?qwUP!>VRhwDhx%-K^ua3Q_O z;tNTFL?Ovg92A6I$(=|-x1>AErLwtSVq@T%8V&5cr|(VbA7J<|^Nxx0-Co)~6?foF^8V-Kz}5(MD{|jsteP}pgp^IpI&+!s%RpeP2`0xiD7lm0?&zPI>I0eK#vq2LJsat z9EQ3j@eMH@08xq(8tzO-EMft~=)SQyfH0Z_E?85}J+A-n<`EWL-)^DbG?oADWbt$I*VV?ixqT+nNM6UmJ`9!jr!&4h@ zA|5kPlT~!a08XB#oUv-I7e$LnJFBOErmc_th0hHV}1LKKasOA5hO`k_h!0KAJEW4f*lc&$@aD4+Ve6!$Qq)Lp^-x5Txh* zi#Hw-3f6*jOUU(Gptd`u;*5kDBm!@T>m0oLNdpPgG%x(%JS5}MBvu~Cu=bs<0UIi8;i}p|($NIj;bFc` zb`x29gYTEzGB2XzNhWu0&x)6{f07^;um`&m*8-|DeKwCaV%`3hSsK1Y zs@q=!&w4T!XcpqU)Ub`c1`dkSy`^1Kb`RFfQ741RV|5J-mMr_v+cKskO;2&W&h4Fj z%u(4{|E z#-_@rDE8^y39MD$dcr1&JTcTwz-1jS8%QB%m4L}dPFMuS@H89SU4WmIkps>_AS_d105r=k4L_ie`^`troa2|q*1B7Ez5bClRZ=ynz-`47Z6lS*y zO2`eeAVe%sH<8OquKTbC*o&96&g#ClE{3`tn&AO+$vg2L@)bY_FpB{CXYpy~1uu2L z_>Xne*#^KUlX)FgJLVl9T>d8WB>*x_5ILAcR;~r|EU@smb4IPd7OBzTDt~RbpzHBd z9l4)}M}$rj3A~q2niwSFvFUcgfpP{=jRF324vS46J+mrGxJfyItc`3#)AbF;S&&OV z^}N#AD3!5av7?{VwVFTPOfU#~2>!uCNapl{Rv8WV&};@nQf5OPlweVu?*Ev|zf_LaXn-exMqweJ=QfZq>`y1#oqOR26AWGyd#L-x*U?fc2bVSK^66VueZ$@w4 zi<9%7n7mRMTqq7es;$6SY#!?H82%5T@^j%@IBa=W!~O+f=$1Y0$BeuxO462f(G_I(bOtwB_d#ah{ypURLX@p)jg)*jto+V#&|dA!3*YbI0)5$ zB#mW(D%*99_`RGH)^tQ}7(zYEuCNZvw&{vSXSQ?oy3(zo7Qu$zt-s>yD)8&>iXr&* zmG+K4TjwNK4q;8cm1o`N%^PRAiT6Q0Es|}Qtm_k|wi=@79m~?;2Ihu)vCf!yesX_g zen@fk>_X>BkPUoK;$>o`D#tvT{wqhQ=bIRCP_ZnrT$B>CUzP6kUQ+Ypm8gz59~A7E z+6IjrMNFmn`W#AZ;fcZd&4Epu;M`_E(|ihq9`T1Yf}pTDUgFT{XFORzL)_(?eS#X% z9lFY{t>-7i2embl`y~Ujqz+ZruD3JfjBUKGdbE{`fMpZmZKa?EfGIrnY9gyV@52aZ zGT3&djn?sj#9!^AlXs79^y)Hv-d)o@;H{3NYl!Nxb|Z4mK6`I<@rxTJR}Co?HC5N4#O>L!_)th5@^dIhAE^ton%WKp*6-~iU-z^u zBWQjTi&l1i^b*xNzVC1Uh>B19QGczBFz?9Rg_846y*34B#1WQw!b`IGkVLGuX zO>P?;uEmVB=OZX~Q2DAHjV!6h0pI>pb6-d}!J4#c-kg(m)Xmgl@+4Y}TeOWpYm z9h$>AinxM@RG}DzPSNvn>Q*?y5#2P&g)PQOn>ZO#_{)? ztZY&9PE1v3-SA4^^)`q_>7SS;$BpiyPrx>(ccxFyhcmEP(cg?0u|}h{Sj5Yehk5>F!T`ezD5c$)mHl z(Y@zjY9#{gUx+Qn5F%MLC=t381$eK*POdL@27ddg{msy-YX+Z?b3Qwr`qS|#aB3>E z5)=~W4!VZ1kJ9$LTshr8o6X^4Jv}Bk6o52VK&S%Fnxo=M1iB@_Pg*V+B5>Z<{fS^G zm7Q+UPA&^@3qhxWw!6>(ci)fJ{niN@J?zV?H0nzT9P*9jCJ#GIWpaD}U_pbD*Oi$D zSTsz|&|zt0P2zD&Ti^$k&d|G#$av|nwVMZ$6&;{mIucvAs7B{k@9-9zzh(qt(99}u zRx}8BTGKS@3A(Hg)RT@FkCH_hoV>7#O2Ahl5vL&gQ5H3zS z*;S^P;adqz!1AQL%L19gmRnA&U0VvJRh2?qk*fnw70NTXL&Q5~-F-X=2<3X)J{ zRhPu4zF#tvBTN-YFfeMf9r(a*3HF+Abed1I%R;O@^3Sr4C3Miq_tdP7aW`ef*%pzd ze9SZZ09E{%2?Wapf(E&v5(pNLre7pgad;&k6oKeoBcb{phHRyDBb=^Ho-j+*TYM)P zW3Yd*7p@2yj_DbF4)G>KHRi&s(#Vg6ATyJ|`ItjkCf7|K`JuR1*jvm!PBt1^3))-G z3Q)9Pr{5qtf@!5K;=1MOcbw{f0|hg$9ez=4(T(hWeMgSDT?7^1GOtx%R$`Y=k#YQ` zgJLMOKumEo8F~_9;$Vn z{A^Trs1hFDaiIl;R|IdVdq%bS-~UUV*Xse09_!pcN*7&g-P33Rs5gy^lLOGqlw+B? zS17Ql_xx$#z#9~zy1siDNND2VQU#EAw?Y5><>2Tol7E}c7x!W%1CaGpEX_c;70TlD zJ$1PGln%b2A$APNppnS787eJBd=FQ2F=XM6%-b*FT*u2k$5sqc)zH-Kck+pjT|RmCs~lj%ZO;a;gU80cY|s zOIl1;mrrd^7r|=laCKM)FgW-;K=s!6@kUd7}iV^gFG%KmF1AhKiq3Ey7 zr2NFgKZ8v@^6ywVqV_Er`y z0#2@A8e~h&eb+@$!4H=hCp7Bv7m|-F-@~2b0v&b|l7rKQ>z@;Nk1TEg#V(@oKhDw< zUt{5k8)Sz@Iy+?%3H~}T?5e)1sn~`XRk3S=m0Vi^(#J-j%m2Bv3Ss= z{Hfwb?FmZoZdCIf6dH% zoo>~Z|9;=N4Iywgk!1QlYoT?zBN$b+5qUUPZ6rrAho!xN&_OKD= z8n!D$j%?qJd0CAcdr6jwF0rXs`qRc;fN1KDeJFbV!Xd{a_dp0%wzml8*YQ`!{-hAtDb|L-t^F6}7rRj;9RK;V%6~T05vND+$qCJtsM@mPIAPcjCJbyIU z1(u(9irjExbz3$9K3A703-?h&E5K4yk6||fD&ex*d#hIoj)}=>W!^B%buYP|q(tYt z$Nbzl=-vhhLVWBoibbMAf>I-zG*IC^+_i|eY61|<7ZsN*L@^y+N<9PKtatzRX6GMY zR2t{;F)55c!+kh!kkaxkTXarXkG;kIbE`QWrttVJz{r2_?}W%U9#a~H!^t$~Fo7G( zxngdZGN+vBMnT3WR9{;%^~)FBi7X)4aou>`Guba$<>^s8@wvs!<-XSONl}xJx=vN5A z#NaoD>lzHvY{YBTLvNKa)m?7UHKKQk%VF}6F}8Sf$j_C+$oqbi5<9un=K$<-{x`IX zlS9+o_k=oVuZQ+ZCm33+UxZ7dvI@2xrAPL{}wbBkRUk z0_t~ruY~A_#J-5+;d02(oe?e$z`zvv79<+FUcY!#M+V|AEE)7Z=OJk?O$(hnh8O(3 zs-(HEiO{{r&#_WH4N=Jb;~;wv7gn7oGeh9~>92`dqjjzqQNRjp&)O=a@rW*g!O%4@ zWRNwb1}8+kpfltrpNIFpf!lmh_S@qEQB`CNSsNx3?nrw%% zf4wn+%my~f5{4^h-~*gEUSO~)l?m&5?-6)B76xtL7^^&bO28!e@JX zgD1+5c~x=a4=yq}m92+yR!YP@7EwhG^_TH3T?yKfyUMewS5Y*Q!~(#Yln8zdGjx!~Z(| zc@wIe=bWFbRQx;=PG>@t{!I3k3-+{mGPcR7)w`qWs64T`d$k)SAfS(UNsb)i{(@JM zE5H6hJ?LGvjvY=b_A9U?W6rG&q1@0?uS2L?wgvnWK)FDRjuOEY?-QnL?*8K4+u5;T zv69)ns7LQeq`E2CZ4Io=FHSxpsP}p2&LjPR6HOKT;^HlI*sSEN&-$;kn?`fMF;Y|U z0z3J&?E%SPv+u6aue;H>{_9HPDjHY_%gfa8rHpfwbwWVvXR`i3SlqDfwnQxgIbu?y zGo{ZvUFv!-iwi!RjV%JuhV%FbU_K-1pOKkVRvUa7JsWNjTh1m*;2%|zZaT~6lh+`1 z)W_1yNO+ooyRjgR81`#_%2urUO6m8&MCw=| zL(GG7M-W6Uz~L=U8{<#Mq@e*vDTZI&MtQj>+v%p`gj_P5g>;XpEeY^D5)v-6cM;v- zyAbtb|7*9wyuCvu!U#YTAs%~mJ;gHBMJfLPx=AG{$mO5CN6?oex5&Q z2zZe6`=dADvQD~y({<~Lf zOH1{b??+|SmpXJ_j$4wCCd=LoO-cvKkW3yo-=p@Y<20dNlrY^5h3a!`nXe%O+1(SB3j*GWZU)t{(c-SWU zISGer+02Qu$mlfEi#nzo$lafS1gAd+ztkaNyP7aPzTM|8v()}<-g{2=YdP;aqy=B-z+8gVSXDtcTnqE$Pfxdw z6BcV6QMC?>zXyAwLur0klKQS3%{poL)?@DetPK>*kN{~gE`@&kHgA02Z_2wYjx5gb zTTHNyNZ_jB!FtfZb@d{6XO4;HabLaRuc=$?pTC+y{|^3ben&M987uiMCyS5n1aNrT zAp?E$Hdgs@MM{lfH9S9=p~QTM!mxRu2*4~&j`AB{T2Jg`75f~57tFJ^x|n0^SJ9<+ z{cX4g;hn-0k0Rm+3hV+x=y#gr`nMCVJ9U1siarEPh34v);SZkI1{(K6F4|LFtnx%( z2`eJFSc>KOIMUE2y<8diDoR>#B-xriUQ5id2 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png b/PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png new file mode 100644 index 0000000000000000000000000000000000000000..d3fd438c3d01ed67b0b7d91f1e08ffc8bfb425ba GIT binary patch literal 1893 zcmV-r2b%baP)pM7fD1xRCt`NS8I$_R~7#D-sjvI<^h!9B@cm7W&(xcU@clEbwpbF zrw?K+Em41{e@v`N6HRP_ijDr#sFB2IVymgOjYf)TN~w)fTB|8c0jK3r-q2y%sT~|L zz4N;FoW1?l-sj%Ab7yo0>j~UB_uPHf`qoU(3WBl+n3Iimb`7vHO)z5wky z_+D%jv{Gs^&wx#N%fHI-EAK%t#!P}B1eQ9qD`gW`Z20V;sek;!xADY^hcAzM0J0$e z@X`)?`)O>@B;Rc?nSE4ROBlF?ifW+et%SO68Qj%w*nggaD-07)3}_R)R$$f)hW2wX z*i=Z>hWW}3xE**(G7$~zT)r|=$=4&JnyjF$c3KL<7JbBO?uQVyZh={UKUC8SxPe|I zhaN?G{231h#R9{s$+8s^CUC?yOM#q0ePY$u3bo750++g?2)4s1wMo`a>O7@EiMqZC zx@9w>4PS&_w2tiQP`wrRNJl^wY9`^DPBu9TwXM35ykuj!6IkqL;WUze z(D7?eam&30#C0^l*9t@YnV`uP&jAuPJ@qF>`4Xb_+Yx>I%N2o8+GZ{wUuI7b%xK&< z@6?4YCWPtlLE*Wbu;&g0Hs|WSW5l)4E82q&h!9;m#H^3P!3IgZ@cT~KqD zl`~P|*nb94UGw*PVK4qYq+6KEt*PcV!>qXr(MRrs=DodonWcF$WT?M8C}G_!*faZK z`_3RDfXUj#lmI;QMF(UHNo1_Mn!gJ17K%>G<}6`Ukl9NRf9f01%Qm36kJ;)y#!kX5 z7lpF?H|JAkpxo)iSuzZ>v<(rRyWl-bQz%)~d<*RIPr&vZ%?Mf!>?0YHf5`rkxx{|^YH({DwQYx`Cg(%1h;z26V84J&CnrEx8; zncD2M0f<3i_virk{BF;7Q9AdpOKeVOL!+TABjNmzjzV952dAdiDjO4vb8RG)$Yc)8 z#XcLBj{O+srPz6`(9%WM-VM`wn;%xjlvR{{WP%z_AAqP}<9r{fyK>(51ep!JL6PB_ z+0_3paa7w5J-?+Q=n%ENOnn%tr0C-|KWfeMPcApYQ^+AR6L*?a$uJ~ywIrcP%lrv$X7M@fLrDDN) zUaB`j2An(2r>QCdsB}_67)59H;wwnHcEfOsg@`i8+?tlr%KT@$kC2pueyH`?nIr~| z|D1HpF%Q7P9?(wxmCrXB56_N)kkPz!i4bx4DDHiTs<#{QCm)1f(uS0v(u0qBagq## zwY8HbP^(NNt?RV5c)%}hNPA8U#7lo{5#Llh)YoA5J;eHGe*{bWFH(YYw)Wp~Y7)AMR50ncalf$rz?O^Y8K9US&d0?rcik!GarjpckYULikYSTT9+rr7y zTi3f#a65V)ac<%Bg9`U(qt+!Bt+ZapT2(jy%!xS6RLxdApAfv_DcGJP*^ef$f0)(P fbD@6E*s=HD5();&`C|CA00000NkvXXu0mjf!g{N~ literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png b/PCUT/PCUT/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png new file mode 100644 index 0000000000000000000000000000000000000000..c82dc0e29f7dccd854cd227bf0cfbc8489399876 GIT binary patch literal 2872 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 N002ovPDHLkV1ioq;!pqp literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-256.png new file mode 100644 index 0000000000000000000000000000000000000000..c68a0202c03d25aafb36e047b4ecc9bc44a8fcfb GIT binary patch literal 15559 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-32.png new file mode 100644 index 0000000000000000000000000000000000000000..d3fd438c3d01ed67b0b7d91f1e08ffc8bfb425ba GIT binary patch literal 1893 zcmV-r2b%baP)pM7fD1xRCt`NS8I$_R~7#D-sjvI<^h!9B@cm7W&(xcU@clEbwpbF zrw?K+Em41{e@v`N6HRP_ijDr#sFB2IVymgOjYf)TN~w)fTB|8c0jK3r-q2y%sT~|L zz4N;FoW1?l-sj%Ab7yo0>j~UB_uPHf`qoU(3WBl+n3Iimb`7vHO)z5wky z_+D%jv{Gs^&wx#N%fHI-EAK%t#!P}B1eQ9qD`gW`Z20V;sek;!xADY^hcAzM0J0$e z@X`)?`)O>@B;Rc?nSE4ROBlF?ifW+et%SO68Qj%w*nggaD-07)3}_R)R$$f)hW2wX z*i=Z>hWW}3xE**(G7$~zT)r|=$=4&JnyjF$c3KL<7JbBO?uQVyZh={UKUC8SxPe|I zhaN?G{231h#R9{s$+8s^CUC?yOM#q0ePY$u3bo750++g?2)4s1wMo`a>O7@EiMqZC zx@9w>4PS&_w2tiQP`wrRNJl^wY9`^DPBu9TwXM35ykuj!6IkqL;WUze z(D7?eam&30#C0^l*9t@YnV`uP&jAuPJ@qF>`4Xb_+Yx>I%N2o8+GZ{wUuI7b%xK&< z@6?4YCWPtlLE*Wbu;&g0Hs|WSW5l)4E82q&h!9;m#H^3P!3IgZ@cT~KqD zl`~P|*nb94UGw*PVK4qYq+6KEt*PcV!>qXr(MRrs=DodonWcF$WT?M8C}G_!*faZK z`_3RDfXUj#lmI;QMF(UHNo1_Mn!gJ17K%>G<}6`Ukl9NRf9f01%Qm36kJ;)y#!kX5 z7lpF?H|JAkpxo)iSuzZ>v<(rRyWl-bQz%)~d<*RIPr&vZ%?Mf!>?0YHf5`rkxx{|^YH({DwQYx`Cg(%1h;z26V84J&CnrEx8; zncD2M0f<3i_virk{BF;7Q9AdpOKeVOL!+TABjNmzjzV952dAdiDjO4vb8RG)$Yc)8 z#XcLBj{O+srPz6`(9%WM-VM`wn;%xjlvR{{WP%z_AAqP}<9r{fyK>(51ep!JL6PB_ z+0_3paa7w5J-?+Q=n%ENOnn%tr0C-|KWfeMPcApYQ^+AR6L*?a$uJ~ywIrcP%lrv$X7M@fLrDDN) zUaB`j2An(2r>QCdsB}_67)59H;wwnHcEfOsg@`i8+?tlr%KT@$kC2pueyH`?nIr~| z|D1HpF%Q7P9?(wxmCrXB56_N)kkPz!i4bx4DDHiTs<#{QCm)1f(uS0v(u0qBagq## zwY8HbP^(NNt?RV5c)%}hNPA8U#7lo{5#Llh)YoA5J;eHGe*{bWFH(YYw)Wp~Y7)AMR50ncalf$rz?O^Y8K9US&d0?rcik!GarjpckYULikYSTT9+rr7y zTi3f#a65V)ac<%Bg9`U(qt+!Bt+ZapT2(jy%!xS6RLxdApAfv_DcGJP*^ef$f0)(P fbD@6E*s=HD5();&`C|CA00000NkvXXu0mjf!g{N~ literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-48.png b/PCUT/PCUT/Assets/Square44x44Logo.altform-unplated_targetsize-48.png new file mode 100644 index 0000000000000000000000000000000000000000..c82dc0e29f7dccd854cd227bf0cfbc8489399876 GIT binary patch literal 2872 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.scale-125.png b/PCUT/PCUT/Assets/Square44x44Logo.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..491a89475b10676924922d05af41b862e1e40a3a GIT binary patch literal 2469 zcmV;W30n4vP)pOS4l)cRCt{2T77U-)fNBUd*9p5E(8dl6-_XF1t9`T_>d1Nwxvox zoPw<*?bxED9UVH-cJvRYm?lG~GtRXBW$P%_PTOfqZKJgxwkp!0Kn&0-5@iUrq~W7z z&onS3g~>Ke6?kZwNlANP?*jbN*S0tl1eEk&J^neQ|*HDx8ia4U}^K2L+m*e zG>>m^o%Q7lBXxM#w%KhZ0R)xH4OssgC-q|7$HUC~ODo4XgR7VmSz7VCj>YWzWp)2K z=66b8;4bgH>PDg1lc6xLO2l16dkR=U4a(^TM9};0L;*XjHTU}hSR7WUMBE;YIV(D9 zue`gg?%X~?&VIz#b1;yebxR;on&Fq!Tyb_^egn*K9IZ-BWuZ|Fw@SV$G~a|7uoiEDSg{R!>KsqGWPq~JMA+-L)BP{NX?zm$^t@3e)CJWM+UvVwO z4fjGU`W#syoB@jUg_gdSx=s`5cnp-+K+gXpoP$4x``>1m7uqElEFO(c40G~mmJgho zHfX?8T&YCNxSdsVC&aQZgN>V#0SbMrXyQjW!({m{!dh_`#I$*7>yrdYKt$zQh>8_R{OvI~`yV6s4HU5X7XnVl zOLz*b)nE634P>p@CC{B+|0Ue_TD*7(jO+g%iZAn_cy$i^2=2*O$hC=Gt3OW+I@4RE z^mrtTSu5`{TZZaJ|nL$(WOA7d(p@w>j{*2S8hur()ep z!IVzHDr#sQ1l%+K@UEw_605|#*FEX`s%SwnH`Nr>1R#T1mA0DIyiuJj20c@C0M!wA; z9k9u>DaKZK-!m)PbdXoC1~0oB?s=*|d1%B{DN+xGc`=tybh4dH6pJB60_T$n2$*Kl zB4H~MxXd^lOo%`jv85`g8RK7y0FKO%pn{ciV-S`6oApfcUq$rquW3}cj667PiOc6H zx7-bD-8ZNVnu^|(N=uRr_5SEI{lq+5NJ>?Rj667#B!x@nViPb){3PhV2mZKV`BGQ}P_4#Jc4rk16}L6qLzm zpbOscq!Cf0c-o2D)P*8?;5|w5={N&K-!GU|i;#X%(oCx_uD=8B=|F{#&8*Fmhl zAJ)Q6eU=%Zs@L5x=jB~cv5vk3+oy~?I1Pl`#v*LhJ*FqADc@~xF>c6bEP_?@6+zpsY~Y||M@FZ*(+V7`shk$O77O48e(Fk0qRV@bcSh5@OhtrOO?7VXLS26 zy#&hYmXT$hH-8J+^o*BKi$Ts5l*+4H?(P=~I}ghm?I}Se&Rhuk^j;mP|3c5X@9alH zTlv(hw)`;Gb zrkkkV&8J50rmVv0g3~@dp!*V0eALsOv21Ej#T8ZI&XJ!{MgA-;<4JGt3k(h_eG10d zJNcf=2~Kdaw>$1WtPlSjzZsFvEhFuS|K?uUx|_9NWB=ZY76L9${F#V`;_B>C&qFhx z8B`}4?kLF-&v^)q*)NO_i_Ko`$@(&jl)IW%U54w@(yI4PApZOgs zl=L2Z)|?XT)`DKXP=M?6 zxzmb?M_K)ckzs+;GOn(zsk3h8{Z2wW9k#k=#rJ*>36-kRQr%&9;0@NKUgM~|>G00q zwCJO$)FFsZ(XOa17Kyp%WI{bKaaZepKjVCD;-$6cU+t)!b*qZXog8vYm{!pANi{Ie z@}7tq_6rui2{V(LGQ<-Kv8elkqugDScAY)qcf9WmMgK&i`4QYP%dS`(bFj{J)wMMD zbbb6f;(Y>U&X*+Z(-(!namRHJUUb#pE9=kp6!Mya(y!`z5CgR7pGo-osl-PuV?U1? jtuh8~w8|K`(JKD~G6%rmB=qqP00000NkvXXu0mjfUR}_c literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.scale-150.png b/PCUT/PCUT/Assets/Square44x44Logo.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..74f7ca661a3d9ec5a71bf9db989f55368e5ef4cc GIT binary patch literal 2977 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 XpOR6sj#kqB00000NkvXXu0mjf$NZ1V literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.scale-200.png b/PCUT/PCUT/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..c19b19d1c45cead9531ff2ec59732e77365d5f44 GIT binary patch literal 3918 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.targetsize-16.png b/PCUT/PCUT/Assets/Square44x44Logo.targetsize-16.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d029561d183adf244bdc609f0dc6e74a8e6400 GIT binary patch literal 592 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 Bquu}j literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.targetsize-32.png b/PCUT/PCUT/Assets/Square44x44Logo.targetsize-32.png new file mode 100644 index 0000000000000000000000000000000000000000..ef488a44fcb5f4ea4833bacdfa50878e60c422a5 GIT binary patch literal 1354 zcmV-Q1-1H#P)pJ{7FPXRCt{2S7~fjMHK#K?!0%~_ozjzrL+Zu8=|(Bl%k?Wl%U2f zaSJs-z!(za5>5Q&f|Zb}KQuA1h{hi#C^YpCF~%6w*eG@Ztrc65%?h-A)b4G%y?5`- zc<$V1UkhDc8;m4+l9|^#%Q@d!=TzWdCh=mV@a9@ntV?x)}i&C|UHl9b_fKVhaP7hSCY{Mz9+Lf;Lz<3woe~ zzDEGd_JPESGb~|@-6n*+cCcjO>*1}<+mix7hYK0Qt3t5cp%FEH(~TMcJi7qaij5G9 zS3;gV09o@sROA-|U;%-YF&8vKDLluKY|GHL+@C{RTB@c6pgD>|_8G#p1fqjrX;~zA z69f&GHs9D<%v%iW$~ADS--E2*4c!+Z>x6tTL>?dFH-BIR>*#ZO01P@$|1Dcd-@L&v ztmW$=iZ+2SEF7a>{7!ft0&8A`bgrWRr?XR`$)*IpOKaW012`Ripeb-!E*`-NPsMH zAFL&J!2P}q&OVAge^*rR^y}+LbiH|*zs3{5&w-Unp+YsCHED5Rf(~!){ zDEN5hQs|DOK<9Bf6M<9tCUi?3f)DQi(lUKMWRa<8asXZ+(4bs~>TZHM_O+ku#86yy zC%NZIlaA_i1DSd|IGWm| zGo_gUu!7JOaB8pE`l~V|D(^FKI?;6bzFKG2h|G5L8sq~6#?+3qZ72I#%hVD5< zR=5=6xFV23B8M-D#U|9GO?iCd7|213Zxo_ zoSE&;qyTWR1}eX+M~ja+74+qcROr@vQ-0YP;VNaXGj24qyf>LkDBM#>jS*z!~Ln8&NTZ0O3lky3fUY(QPl}Y zRU`!m(Bn_rgwzYvnbl(Qe?ow}9$0aOxd`o?BAG&=MtW&$WpWlEO5fU2*8Rf8KZg6eHn^NG)7-jGsuiU0rr literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Square44x44Logo.targetsize-48.png b/PCUT/PCUT/Assets/Square44x44Logo.targetsize-48.png new file mode 100644 index 0000000000000000000000000000000000000000..8f162524d084d4c80a921f7ae4f1d5b0f0b9c9d5 GIT binary patch literal 2150 zcmV-s2$}bZP)pN7)eAyRCt{2SqqF@RT=)yB6 z*`1xa_nhN@&Yin6yW4r}?1W0}pUmUVx%Zs!`_KP8Bk?X5dAb3NSI!VP-ExM&>6Wqy z{A=~ua}qLkwPEZcx;)z$Bp736ya0@$85vUa^=-k;Ef7?PkLgTRnBCVfF zQnU{3HsT(?{-j^CV)bf#Z_S#*tP2uI-Zf=DDZSllelREop2mUiIr~;a|;QRqvTtZy16q11RDf5SqGdAj)O*jYM;yWSc ze+I#e>!9|%0A!N1SR&k&k;~BfG|SvV7>g3nFwLW)jRj&Il)!MZbGh(li1#n1Sz^P1 z+n1&pf>fyGh)i~zPo9g|(g&gTZ$hx;e&{1RLg$z9VZ`0-FijN^P%b>@GYaU(0PMZ9 zAW7?@^>(nD`rJxZvQOw^uRy)@GpINJ2;Q;~a?!2e(-(&0dH0I*J_%0#p>{rrVAnG+ z$M%N1k#APKc%UgbNbuFrh0{(A0lAHUe+j&)<&?mY?1A6;V{+CfVET_cvruH~>b2jI zbKeU2!OwwBoSA(#d}HwjGQqDv%)8!2t6=97p|T>AGR2c+S((`QDaZ?#!MoXTm7MY4KQXZh}k-{-1sTy>A1=qFac_P1z_E?Fjit<79Z(-tEGyzxn&L zyA9<)E3=eP85h$nT$8VLGAVWF&rqF@LcQ?^2h_!Rt3A7GQ6?6WeyW4+*$@4~D(KgK z4?*Ep%>Gz7Zs&CRApe-Mz|t8aRviLP^1`VFPrKrGJV4RpzA%AVjn>rVGO>tJ)MYmprrau%WvB+HP;-qyp z`Fwbn-VQ$XB68@x@V7kz;}Vz%iP*?Q_Xb^9R1?5VN^~aPn>>Iec{W3af}Ff zyopTXX6Njz{w(*iIBT{GxxsddN;@8h8R*Rwc$t2xmHtyP_=*XHELdKo&~At1Cst8z z_(JZUD1p^Zf_LRNUB>pGUuh!?oEqHo<(o@&3FM0fN{%XKU7ownAG2cP{>;k|#Oq_`5XNHfvKwQ-&t4JVQRu>cHle^d*7@yteOLYiX z%~MkIw?Bs1)oo$Cx00WWC>I1fex8l<)vDffUh9`BbY7k-?hd{P zf9u24m34$$OaS83W(Ie7WkuFwlB&8+q*2Y~tm z);Mik%U0Qm37jplZEK7I%IIUg7?M~Of%LaIw3oeXOt zs)NEJ4Y|6;0=G5M#OaXpZ*(nYuCyMRH_DGx)k|aqn9sTby8Bh&WH)KNA!qAU_}M03 z%>6Xvhi;?h=91yhM!u3f4)x0VoS(dN1jrGYMC=A?506uk*Kr^8q0Z1r$%gohMUWr) zI(1-+M+46SRpdLtHgfbs+e2R|Qwn!vFnmn$+SdLQA|(}Wd>U1u2R1>yzS(^j=Pv=T zo0RQ?vavPNI#sCwghvKxX0fLf$~OJ>n30GeUbydd zJ1MLo>wOgwpdh$DlhNOZOZFajx*DbbNRvG8Q5!YL2mIfB3x;}R+wXGf%7(yn?;|_^ zDJ8V$$lZr@8gi1;WSlgfuzf=(cJH?KF9J&9z zsV%LE_=KgR<6fk;{3utT*`(x-1oI;X2eZ+J>LGvg+R#L7a!F%l>t&GEx}?X zfkz!`+BZEaFb~n!hk5(Klck<3naC!5r~5d4+?!lAc|*XxC1gGe1T#%aLo#EEXvgX? zDw_-6X8&TMdo^X9UT(~8Ut@h!`;nccaFtCUlKOj&?V*o7<=+2J$^Xy4l>e)8hQR5T ccRhjs0(aOjS@6!03;+NC07*qoM6N<$g4#PQa{vGU literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/StoreLogo.backup.png b/PCUT/PCUT/Assets/StoreLogo.backup.png new file mode 100644 index 0000000000000000000000000000000000000000..7385b56c0e4d3c6b0efe3324aa1194157d837826 GIT binary patch literal 1451 zcmaJ>eN5D57_Z|bH;{0+1#mbl)eTU3{h)Wf7EZV?;HD@XL@{B`Ui%(2aMxQ~xdXSv z5nzWi(LW)U2=Vc-cY@s7nPt{i0hc6!7xN4NNHI#EQl>YNBy8l4%x9gr_W-j zEZMQmmTIy(>;lblRfh`dIyTgc9W5d!VP$L4(kKrN1c5G~(O_#xG zAJCNTstD^5SeXFB+&$h=ToJP2H>xr$iqPs-#O*;4(!Fjw25-!gEb*)mU}=)J;Iu>w zxK(5XoD0wrPSKQ~rbL^Cw6O_03*l*}i=ydbu7adJ6y;%@tjFeXIXT+ms30pmbOP%Q zX}S;+LBh8Tea~TSkHzvX6$rYb)+n&{kSbIqh|c7hmlxmwSiq5iVhU#iEQ<>a18|O^Sln-8t&+t`*{qBWo5M?wFM(JuimAOb5!K#D}XbslM@#1ZVz_;!9U zpfEpLAOz=0g@bd6Xj_ILi-x^!M}73h^o@}hM$1jflTs|Yuj9AL@A3<-?MV4!^4q`e z)fO@A;{9K^?W?DbnesnPr6kK>$zaKo&;FhFd(GYFCIU^T+OIMb%Tqo+P%oq(IdX7S zf6+HLO?7o0m+p>~Tp5UrXWh!UH!wZ5kv!E`_w)PTpI(#Iw{AS`gH4^b(bm^ZCq^FZ zY9DD7bH}rq9mg88+KgA$Zp!iWncuU2n1AuIa@=sWvUR-s`Qb{R*kk(SPU^`$6BXz8 zn#7yaFOIK%qGxyi`dYtm#&qqox0$h=pNi#u=M8zUG@bpiZ=3sT=1}Trr}39cC)H|v zbL?W)=&s4zrh)7>L(|cc%$1#!zfL?HjpeP%T+x_a+jZ16b^iKOHxFEX$7d|8${H-* zIrOJ5w&i$>*D>AKaIoYg`;{L@jM((Kt?$N$5OnuPqVvq**Nm}(f0wwOF%iX_Pba;V z;m@wxX&NcV3?<1+u?A{y_DIj7#m3Af1rCE)o`D&Y3}0%7E;iX1yMDiS)sh0wKi!36 zL!Wmq?P^Ku&rK~HJd97KkLTRl>ScGFYZNlYytWnhmuu|)L&ND8_PmkayQb{HOY640 bno1(wj@u8DCVuFR|31B*4ek@pZJqxCDDe1x literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/StoreLogo.scale-100.png b/PCUT/PCUT/Assets/StoreLogo.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..ab74642a6b7bf39e951d5cb08430c9d1a1cd9313 GIT binary patch literal 2971 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 RZAJh9002ovPDHLkV1kmkp!fg) literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/StoreLogo.scale-125.png b/PCUT/PCUT/Assets/StoreLogo.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..7afc28f031a7aec7d6e5bf1cafe1d8b419dc7998 GIT binary patch literal 3708 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Wide310x150Logo.scale-125.png b/PCUT/PCUT/Assets/Wide310x150Logo.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..982f45aab68aadabbff47148270a148b85a490a4 GIT binary patch literal 4461 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Wide310x150Logo.scale-200.png b/PCUT/PCUT/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..dd55654f2c8b844c2afdf5675b981fd03b4a86a4 GIT binary patch literal 7484 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/Wide310x150Logo.scale-400.png b/PCUT/PCUT/Assets/Wide310x150Logo.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..37776238c7c9e7f30468a8bbbe63827d5439f8f4 GIT binary patch literal 16378 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 Eg3lG|mjD0& literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/back.jpg b/PCUT/PCUT/Assets/back.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0fafb73ce2bb1b794b59dfe4125c415ee76cf8a2 GIT binary patch literal 58836 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 OB?DL-ly4PCUjHBT@dO3{ literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/home.png b/PCUT/PCUT/Assets/home.png new file mode 100644 index 0000000000000000000000000000000000000000..1abd400380445c8f02a95d85904d7471e2304d6d GIT binary patch literal 1185 zcmV;S1YY}zP)lYEt_kDy{Ylk`_}8+1-LZ_@MN`7fV_sG})vz$)5gZ zVUx3y?7cgiz4=&fI4Ad<-<&xgGjnI=E>Z9oj6g5~D>?#^G;fcjxXte*96tlpr}%)w zG=Lz3koWr;3o-n0U!UYtO!O_h*J_5`2kazdU&xn2qA7ll6@7w?L*P7xfPETE^1FGU zl227hXT`ZLS4bE-xCBSBkvNrfP}ElBPc1bq@LX_Ot> zHQA&_BpAKPTk%%tnR`OwkpBhm%p}zBc$P&@S-sFHp5k^CF#xfk2qyY_HcQ*hB49@v z({0*D>w>k9!JJP4+K6QpQlIA6u-#lipa!s{w3?;%`&-p52})VFX&YcS!IB!XL|3$L z36?5b7ZOeL32dKd5ug?<=~A?gUd(J$wIVr{N{%ME zQz`0$zX(`TN`>jwi7jeWf_b@M8uQ@#ByZvx8iH;Xf!U`XzluOS(!MdeJz6$6k)5HT zx)I)J;5pUF{TKf*BF!@n4Z+za0ZUOYE2;Y-FB$_mZ4QO#hR)FpT2KhqXa}Zs5bLzj zG+Z@0Q=`rixxOrOnkxhiT_`}m6LD#%Z7e#n~fn5S5Zzk{|P zypnVdJLCbk1m$;J;ps`lWc>Q|dS#cadH8f`trI>o!IGz@3#Gq;l340AVewh0MKayGBkFd_i zOy9t+KEZtr2(#F2yiexRw%Y00000NkvXXu0mjf>IWyRMP)}L1w-5T^R=j@zw=FIt0rBf5!8;dXYL0YZ5LhC1Qbt5@u)@gB)A7@(UUj%a{T0;$Sz0YgxsgfeXTo&o za&|s91qQuNfJ2O*@WAgDESZ zdm*<`9mo%MLXEo1OI^vbyAgbcGHA6}6xQ4R$A z8R!HPQ|#YL=~KiOVjkExlHpdZn6rAX)FJf>``RKJ`a3stVW z^1h}^*v^;YROKV^O*Dfis?aa18dxH;o7JDr90r)o>-3l1r!@h6lnCj=YH&90-14Qt#&! z-kk4IKUQN{RjRx;E**;sqtxF{Y$VnbvP$8=Vg!5VyVdDR9Ic$lVTsrs*e$?|G}uHe z2M#+=5NCMrIK@w==SiO)(MN@4ImQ<&xFg|~8ow}UzW@(cbgy#McHLObc|T4FKezm*ktXuf13 z_EC9&cj+Tq$-sFg@Jgx?`_Qc%Lmn8}eq8MI0-r^}vosnd=7HPx{TF-HL|$MJ>@zr% zv`{<~zl)3hR7~xzR}$L&*)ElGdeo=@Teup|XqkuH&5C34x==C^J{Py=wMdX3OZTa1 zl2--vmr53H%yg#kkB@=*?$Y0%fPyWS7o}KhQg-D>UIl`ugkH{!>8T(lv!h%hrXDyW zLwpd23WRlL=(A!EC6`vk7FLPke5LO_pfd2~o1@9|F UJyTnMWB>pF07*qoM6N<$f~)Oyl>h($ literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/img_order.png b/PCUT/PCUT/Assets/img_order.png new file mode 100644 index 0000000000000000000000000000000000000000..3b90571b416abf9c00210a907317d8e10f5fb25c GIT binary patch literal 891 zcmV->1BCpEP)pLH(WjU&uV2v9=Wo^`7FBY@Tj6TUR3h37}YKakL?I2ewG{Z zzH3|*A6WM|V&cR?$r6+ZcH~HEQY$r%#7-?(SYhhSGbLR5xB;)dE=wFRHOn%1S* zj+92^4To!bsN{2}DbD_}$%<{EOAMsOk=Ut)_d3v5#W8nbq!-nn=VjTI9p3j&ha4VN z_SUfa?SlM>Y8Qm(f|Tv(1~)LS7G~;wHvT?G&eUaAdMqrFMN-wmZ8mzy(l%Lb_vq3N znIqk{FtPVYsUMFI)Nt5!tfzun*%h{UZqGKpr3z$g{ip;2Ehu9t(}wa z&G00!Y>y-n#UyR`Qj0G^Gdu(i8H1R0LYNvT>V$zOR^_|QgHm2*J2B{U1U61XFxi>jiE>vegv@@13xzx}CY;}s{r{f@0D3>LMYEA!UhJ*Gw*&S3mQiO;BK z+?R8@h^w@Ig~gxK~z`??O4A{TR|NEe3MGW#KED(wUdJZf20oGTClT|-MhHd!NEmA z=-SPpT{?+V!9T&$3Z+od)Xl*~ONWjfi~$kz^%F6d%ga3;1iYM&JMMDdANSt(=Uwi7 zf!Lx4q6eY}9+C&drR?Z|PN-l7f@uYYTx)#OsOVKwa8xW{^L$!PlJd##CY2Agq6O@e zI>HfRZ-`*7J}bwzas?Y{!HmQ@=TMEHY8X*POUD*$Mvoy(r%A&N7pNpvdz2(VQKT`3 z%-s9j1Zsvb0D#`Ea<48K(TX0udkAK68&G_0JeQp|I*7S^sEc%N#}J1SG}b|VfTY|Q zl9^gO#nwu{_3a>o#8YZKOMa#ye8CJ408U}|+@w}+3~{B_FDB7Hjw;6($8G01ej$p> zTg1~3_vd2)N|w0)gj@eit_>B2088~i)>#!K8aE?llx<;|eUB290 zLYRmHH2DEaXjI0cfnIPBbyrcNVbpsuJD^$f>jn0hSN-xB7Uavvglq7 z;YZPSmO}mV3;jMA+H<)vBt5khtX*4vSG?wTeA%MhKiI?z)_gm1k~(!by;W_)6Z~cI z!Jw>YED&EX1P=gZCZ(JqrKP5pTw@D?H7dJ>lt!MLQ=gMPmSWx`kjCh2oLA*XnmUrk zXw$hb=6MHtL!+LxX+CEGPATsTYii#(#~wWpJrF&R6A#<~v@>a<8ye}&00000NkvXX Hu0mjfBh4%O literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/img_setting.png b/PCUT/PCUT/Assets/img_setting.png new file mode 100644 index 0000000000000000000000000000000000000000..0ad62293359f9786bf18181be8fcad236ff813be GIT binary patch literal 2244 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 literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/shutdown_mo.png b/PCUT/PCUT/Assets/shutdown_mo.png new file mode 100644 index 0000000000000000000000000000000000000000..70881636dfe5898b110030769dfb82d1bb914479 GIT binary patch literal 945 zcmV;i15W&jP)6ASd?Q$i%88D_Apx4 zCaXn(8t(94!u?W|0R-&VjUA z;wyrl7M87wi0LVUay3&^G*Wm9JS8}l6-^H_(>?eEudX`S-gO<^1I#acM8WoMPYoD( zFcPXpUOf^-303$zx|_j0NP6O-U0c_q=}knTAg62!Us@nZg6%yG2sj5(c=Fpr#oO0` zdyv%FwsajLa!BZYswahSR=}IM#w1M$&b4{07TN>l9u1Wgti`wzGOT=J?ih6bP?B*L z*&~pnW_Lg*;Aw1=Is%WeHTj?Sd<9<+vOC^>!L&U=5lfS;I01SfA}q7}xGr89pTy%>@^lsS2V zt#sG(xd-4jgedd3<>+ZI(ut`Nn&J0LDPJ}@Wu41p_|(Sc?((B{RvrRALec_zb$42) z%dsploJTgELwLYtPI}e-#);-t{)OBD4?sqUq?wr8M7TF-XZO>{2*><- z1GZlXYhyqBWr)jUq8Rk?Kznx!bV?v5r9EMc?+vZ7$5aiooP=@efdi8VqnMssmROhk z9q&8`h9?RGhRh0s`wCX+F;&LQ2Qh#7NLmd6r2%I8gnim#w#?MkYhsq=cEbk- Ti;AJ<00000NkvXXu0mjf)l97E literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/Assets/unnamed.jpg b/PCUT/PCUT/Assets/unnamed.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7b70cadde47b874574dc438f4308c5eadc6043c7 GIT binary patch literal 79469 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

$UHnlIH;qmj8J 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 L|LfQPx0!zdmZdpr literal 0 HcmV?d00001 diff --git a/PCUT/PCUT/BundleArtifacts/PCUT.appinstaller.xml b/PCUT/PCUT/BundleArtifacts/PCUT.appinstaller.xml new file mode 100644 index 0000000..4fd4ca9 --- /dev/null +++ b/PCUT/PCUT/BundleArtifacts/PCUT.appinstaller.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/PCUT/PCUT/BundleArtifacts/x64.txt b/PCUT/PCUT/BundleArtifacts/x64.txt new file mode 100644 index 0000000..f52260e --- /dev/null +++ b/PCUT/PCUT/BundleArtifacts/x64.txt @@ -0,0 +1,6 @@ +MainPackage=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\bin\x64\Release\PCUT_1.0.111.0_x64.msix +SymbolPackage=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\obj\x64\Release\Symbols\PCUT_1.0.111.0_x64.appxsym +ResourcePack=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\bin\x64\Release\PCUT_1.0.111.0_scale-100.msix +ResourcePack=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\bin\x64\Release\PCUT_1.0.111.0_scale-125.msix +ResourcePack=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\bin\x64\Release\PCUT_1.0.111.0_scale-150.msix +ResourcePack=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\bin\x64\Release\PCUT_1.0.111.0_scale-400.msix diff --git a/PCUT/PCUT/BundleArtifacts/x86.txt b/PCUT/PCUT/BundleArtifacts/x86.txt new file mode 100644 index 0000000..7832f54 --- /dev/null +++ b/PCUT/PCUT/BundleArtifacts/x86.txt @@ -0,0 +1,2 @@ +MainPackage=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\bin\x86\Release\PCUT_1.0.111.0_x86.msix +SymbolPackage=C:\Users\Admin\Desktop\CODE\PCUT\PCUT\obj\x86\Release\Symbols\PCUT_1.0.111.0_x86.appxsym diff --git a/PCUT/PCUT/Converters/BooleanConverter.cs b/PCUT/PCUT/Converters/BooleanConverter.cs new file mode 100644 index 0000000..ed7ed1d --- /dev/null +++ b/PCUT/PCUT/Converters/BooleanConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + internal class BooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + switch(value) + { + case int intValue: + { + return intValue != 0; + } + case double doubleValue: + { + return doubleValue != 0; + } + default: + return false; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/PCUT/PCUT/Converters/BooleanInverter.cs b/PCUT/PCUT/Converters/BooleanInverter.cs new file mode 100644 index 0000000..eeac175 --- /dev/null +++ b/PCUT/PCUT/Converters/BooleanInverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class BooleanInverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return !(bool)value; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return !(bool)value; + } + } +} diff --git a/PCUT/PCUT/Converters/BooleanToStringConverter.cs b/PCUT/PCUT/Converters/BooleanToStringConverter.cs new file mode 100644 index 0000000..25b8543 --- /dev/null +++ b/PCUT/PCUT/Converters/BooleanToStringConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class BooleanToStringConverter : IValueConverter + { + public object Convert(object value, Type targetType , object parameter, string language) + { + if(value is bool BoolValue) + { + return BoolValue ? "True" : "False"; + } + return "False"; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/PCUT/PCUT/Converters/BooleanToVisibilityConverter.cs b/PCUT/PCUT/Converters/BooleanToVisibilityConverter.cs new file mode 100644 index 0000000..b795a02 --- /dev/null +++ b/PCUT/PCUT/Converters/BooleanToVisibilityConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class BooleanToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is bool boolValue) + { + return boolValue ? Visibility.Visible : Visibility.Collapsed; + } + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/PCUT/PCUT/Converters/OpacityConverter.cs b/PCUT/PCUT/Converters/OpacityConverter.cs new file mode 100644 index 0000000..0d2dc4b --- /dev/null +++ b/PCUT/PCUT/Converters/OpacityConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class OpacityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return (bool)value ? 0.3 : 1; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return (double)value < 1; + } + } +} diff --git a/PCUT/PCUT/Converters/UserRoleToBooleanConverter.cs b/PCUT/PCUT/Converters/UserRoleToBooleanConverter.cs new file mode 100644 index 0000000..e8e87ab --- /dev/null +++ b/PCUT/PCUT/Converters/UserRoleToBooleanConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class UserRoleToBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return value?.ToString().ToLower() == "user"; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/PCUT/PCUT/Converters/UserStatusConverter.cs b/PCUT/PCUT/Converters/UserStatusConverter.cs new file mode 100644 index 0000000..9ce4ee5 --- /dev/null +++ b/PCUT/PCUT/Converters/UserStatusConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class UserStatusConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return (bool)value ? "Active" : "Inactive"; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return value?.ToString() == "Active"; + } + } +} diff --git a/PCUT/PCUT/Converters/VisibilityConverter.cs b/PCUT/PCUT/Converters/VisibilityConverter.cs new file mode 100644 index 0000000..49e6bd1 --- /dev/null +++ b/PCUT/PCUT/Converters/VisibilityConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class VisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return (bool)value ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } + + public class VisibilityInverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return (bool)value ? Visibility.Collapsed : Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/PCUT/PCUT/Converters/VisibilityToMarginConverter.cs b/PCUT/PCUT/Converters/VisibilityToMarginConverter.cs new file mode 100644 index 0000000..950a6f1 --- /dev/null +++ b/PCUT/PCUT/Converters/VisibilityToMarginConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; + +namespace PCUT.Converters +{ + public class VisibilityToMarginConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + Visibility visibility = (Visibility)value; + return visibility == Visibility.Visible ? new Thickness(0) : new Thickness(0, 0, 885, 0); + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/PCUT/PCUT/DeepNestApi/Background.cs b/PCUT/PCUT/DeepNestApi/Background.cs new file mode 100644 index 0000000..08a8fa4 --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/Background.cs @@ -0,0 +1,1802 @@ +using ClipperLib; +using DeepNestLib; +using MinkowskiCpp; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Windows.Foundation; + +namespace PCUT.DeepNestApi +{ + public class Background + { + public static bool EnableCaches = true; + + public static NFP shiftPolygon(NFP p, PlacementItem shift) + { + NFP shifted = new NFP(); + for (var i = 0; i < p.length; i++) + { + shifted.AddPoint(new SvgPoint(p[i].x + shift.x, p[i].y + shift.y) { exact = p[i].exact }); + } + if (p.children != null /*&& p.children.Count*/) + { + shifted.children = new List(); + for (int i = 0; i < p.children.Count; i++) + { + shifted.children.Add(shiftPolygon(p.children[i], shift)); + } + } + + return shifted; + } + + + // returns the square of the length of any merged lines + // filter out any lines less than minlength long + public static MergedResult mergedLength(NFP[] parts, NFP p, double minlength, double tolerance) + { + // var min2 = minlength * minlength; + // var totalLength = 0; + // var segments = []; + + // for (var i = 0; i < p.length; i++) + // { + // var A1 = p[i]; + + // if (i + 1 == p.length) + // { + // A2 = p[0]; + // } + // else + // { + // var A2 = p[i + 1]; + // } + + // if (!A1.exact || !A2.exact) + // { + // continue; + // } + + // var Ax2 = (A2.x - A1.x) * (A2.x - A1.x); + // var Ay2 = (A2.y - A1.y) * (A2.y - A1.y); + + // if (Ax2 + Ay2 < min2) + // { + // continue; + // } + + // var angle = Math.atan2((A2.y - A1.y), (A2.x - A1.x)); + + // var c = Math.cos(-angle); + // var s = Math.sin(-angle); + + // var c2 = Math.cos(angle); + // var s2 = Math.sin(angle); + + // var relA2 = { x: A2.x - A1.x, y: A2.y - A1.y}; + // var rotA2x = relA2.x * c - relA2.y * s; + + // for (var j = 0; j < parts.length; j++) + // { + // var B = parts[j]; + // if (B.length > 1) + // { + // for (var k = 0; k < B.length; k++) + // { + // var B1 = B[k]; + + // if (k + 1 == B.length) + // { + // var B2 = B[0]; + // } + // else + // { + // var B2 = B[k + 1]; + // } + + // if (!B1.exact || !B2.exact) + // { + // continue; + // } + // var Bx2 = (B2.x - B1.x) * (B2.x - B1.x); + // var By2 = (B2.y - B1.y) * (B2.y - B1.y); + + // if (Bx2 + By2 < min2) + // { + // continue; + // } + + // // B relative to A1 (our point of rotation) + // var relB1 = { x: B1.x - A1.x, y: B1.y - A1.y}; + // var relB2 = { x: B2.x - A1.x, y: B2.y - A1.y}; + + + // // rotate such that A1 and A2 are horizontal + // var rotB1 = { x: relB1.x* c -relB1.y * s, y: relB1.x* s +relB1.y * c}; + // var rotB2 = { x: relB2.x* c -relB2.y * s, y: relB2.x* s +relB2.y * c}; + + // if(!GeometryUtil.almostEqual(rotB1.y, 0, tolerance) || !GeometryUtil.almostEqual(rotB2.y, 0, tolerance)){ + // continue; + // } + + // var min1 = Math.min(0, rotA2x); + // var max1 = Math.max(0, rotA2x); + + // var min2 = Math.min(rotB1.x, rotB2.x); + // var max2 = Math.max(rotB1.x, rotB2.x); + + // // not overlapping + // if(min2 >= max1 || max2 <= min1){ + // continue; + // } + + // var len = 0; + // var relC1x = 0; + // var relC2x = 0; + + // // A is B + // if(GeometryUtil.almostEqual(min1, min2) && GeometryUtil.almostEqual(max1, max2)){ + // len = max1-min1; + // relC1x = min1; + // relC2x = max1; + // } + // // A inside B + // else if(min1 > min2 && max1 min1 && max2 min2){ + // totalLength += len; + + // var relC1 = { x: relC1x * c2, y: relC1x * s2 }; + //var relC2 = { x: relC2x * c2, y: relC2x * s2 }; + + //var C1 = { x: relC1.x + A1.x, y: relC1.y + A1.y }; + //var C2 = { x: relC2.x + A1.x, y: relC2.y + A1.y }; + + //segments.push([C1, C2]); + // } + // } + // } + + // if(B.children && B.children.length > 0){ + // var child = mergedLength(B.children, p, minlength, tolerance); + //totalLength += child.totalLength; + // segments = segments.concat(child.segments); + // } + // } + // } + + // return {totalLength: totalLength, segments: segments}; + throw new NotImplementedException(); + } + + public class MergedResult + { + public double totalLength; + public object segments; + } + + + public static NFP[] cloneNfp(NFP[] nfp, bool inner = false) + { + if (!inner) + { + return new[] { clone(nfp.First()) }; + } + + // inner nfp is actually an array of nfps + List newnfp = new List(); + for (var i = 0; i < nfp.Count(); i++) + { + newnfp.Add(clone(nfp[i])); + } + + return newnfp.ToArray(); + } + + public static NFP clone(NFP nfp) + { + NFP newnfp = new NFP(); + newnfp.source = nfp.source; + for (var i = 0; i < nfp.length; i++) + { + newnfp.AddPoint(new SvgPoint(nfp[i].x, nfp[i].y)); + } + + if (nfp.children != null && nfp.children.Count > 0) + { + newnfp.children = new List(); + for (int i = 0; i < nfp.children.Count; i++) + { + var child = nfp.children[i]; + NFP newchild = new NFP(); + for (var j = 0; j < child.length; j++) + { + newchild.AddPoint(new SvgPoint(child[j].x, child[j].y)); + } + newnfp.children.Add(newchild); + } + } + + return newnfp; + } + + + public static int callCounter = 0; + + public static Dictionary cacheProcess = new Dictionary(); + public static NFP[] Process2(NFP A, NFP B, int type) + { + var key = A.source + ";" + B.source + ";" + A.rotation + ";" + B.rotation; + bool cacheAllow = type != 1; + if (cacheProcess.ContainsKey(key) && cacheAllow) + { + return cacheProcess[key]; + } + + Stopwatch swg = Stopwatch.StartNew(); + Dictionary> dic1 = new Dictionary>(); + Dictionary> dic2 = new Dictionary>(); + dic2.Add("A", new List()); + foreach (var item in A.Points) + { + var target = dic2["A"]; + target.Add(item.x); + target.Add(item.y); + } + dic2.Add("B", new List()); + foreach (var item in B.Points) + { + var target = dic2["B"]; + target.Add(item.x); + target.Add(item.y); + } + + + List hdat = new List(); + + foreach (var item in A.children) + { + foreach (var pitem in item.Points) + { + hdat.Add(pitem.x); + hdat.Add(pitem.y); + } + } + + var aa = dic2["A"]; + var bb = dic2["B"]; + var arr1 = A.children.Select(z => z.Points.Count() * 2).ToArray(); + + Minkowski.setData(aa.Count, aa.ToArray(), A.children.Count, arr1, hdat.ToArray(), bb.Count, bb.ToArray()); + Minkowski.calculateNFP(); + + callCounter++; + + int[] sizes = new int[2]; + Minkowski.getSizes1(sizes); + int[] sizes1 = new int[sizes[0]]; + int[] sizes2 = new int[sizes[1]]; + Minkowski.getSizes2(sizes1, sizes2); + double[] dat1 = new double[sizes1.Sum()]; + double[] hdat1 = new double[sizes2.Sum()]; + + Minkowski.getResults(dat1, hdat1); + + if (sizes1.Count() > 1) + { + throw new ArgumentException("sizes1 cnt >1"); + } + + + //convert back to answer here + bool isa = true; + List Apts = new List(); + + + + List> holesval = new List>(); + bool holes = false; + + for (int i = 0; i < dat1.Length; i += 2) + { + var x1 = (float)dat1[i]; + var y1 = (float)dat1[i + 1]; + Apts.Add(new Point(x1, y1)); + } + + int index = 0; + for (int i = 0; i < sizes2.Length; i++) + { + holesval.Add(new List()); + for (int j = 0; j < sizes2[i]; j++) + { + holesval.Last().Add(hdat1[index]); + index++; + } + } + + List> holesout = new List>(); + foreach (var item in holesval) + { + holesout.Add(new List()); + for (int i = 0; i < item.Count; i += 2) + { + var x = (float)item[i]; + var y = (float)item[i + 1]; + holesout.Last().Add(new Point(x, y)); + } + } + + NFP ret = new NFP(); + ret.Points = new SvgPoint[] { }; + foreach (var item in Apts) + { + ret.AddPoint(new SvgPoint(item.X, item.Y)); + } + + + foreach (var item in holesout) + { + if (ret.children == null) + ret.children = new List(); + + ret.children.Add(new NFP()); + ret.children.Last().Points = new SvgPoint[] { }; + foreach (var hitem in item) + { + ret.children.Last().AddPoint(new SvgPoint(hitem.X, hitem.Y)); + } + } + + swg.Stop(); + var msg = swg.ElapsedMilliseconds; + var res = new NFP[] { ret }; + + if (cacheAllow) + { + cacheProcess.Add(key, res); + } + return res; + } + + public static NFP getFrame(NFP A) + { + var bounds = GeometryUtil.getPolygonBounds(A); + + // expand bounds by 10% + bounds.width *= 1.1; + bounds.height *= 1.1; + bounds.x -= 0.5 * (bounds.width - (bounds.width / 1.1)); + bounds.y -= 0.5 * (bounds.height - (bounds.height / 1.1)); + + var frame = NFP.Create( + new SvgPoint(bounds.x, bounds.y), + new SvgPoint(bounds.x + bounds.width, bounds.y), + new SvgPoint(bounds.x + bounds.width, bounds.y + bounds.height), + new SvgPoint(bounds.x, bounds.y + bounds.height) + ); + + + frame.children = new List() { (NFP)A }; + frame.source = A.source; + frame.rotation = 0; + + return frame; + } + + public static NFP[] getInnerNfp(NFP A, NFP B, int type, SvgNestConfig config) + { + if (A.source != null && B.source != null) + { + + var key = new DbCacheKey() + { + A = A.source.Value, + B = B.source.Value, + ARotation = 0, + BRotation = B.rotation, + //Inside =true?? + }; + //var doc = window.db.find({ A: A.source, B: B.source, Arotation: 0, Brotation: B.rotation }, true); + var res = window.db.find(key, true); + if (res != null) + { + return res; + } + } + + + var frame = getFrame(A); + + var nfp = getOuterNfp(frame, B, type, true); + + if (nfp == null || nfp.children == null || nfp.children.Count == 0) + { + return null; + } + List holes = new List(); + if (A.children != null && A.children.Count > 0) + { + for (var i = 0; i < A.children.Count; i++) + { + var hnfp = getOuterNfp(A.children[i], B, 1); + if (hnfp != null) + { + holes.Add(hnfp); + } + } + } + + if (holes.Count == 0) + { + return nfp.children.ToArray(); + } + var clipperNfp = innerNfpToClipperCoordinates(nfp.children.ToArray(), config); + var clipperHoles = innerNfpToClipperCoordinates(holes.ToArray(), config); + + List> finalNfp = new List>(); + var clipper = new ClipperLib.Clipper(); + + clipper.AddPaths(clipperHoles.Select(z => z.ToList()).ToList(), ClipperLib.PolyType.ptClip, true); + clipper.AddPaths(clipperNfp.Select(z => z.ToList()).ToList(), ClipperLib.PolyType.ptSubject, true); + + if (!clipper.Execute(ClipperLib.ClipType.ctDifference, finalNfp, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)) + { + return nfp.children.ToArray(); + } + + if (finalNfp.Count == 0) + { + return null; + } + + List f = new List(); + for (var i = 0; i < finalNfp.Count; i++) + { + f.Add(toNestCoordinates(finalNfp[i].ToArray(), config.clipperScale)); + } + + if (A.source != null && B.source != null) + { + // insert into db + //console.log('inserting inner: ', A.source, B.source, B.rotation, f); + var doc = new DbCacheKey() + { + A = A.source.Value, + B = B.source.Value, + ARotation = 0, + BRotation = B.rotation, + nfp = f.ToArray() + + + }; + window.db.insert(doc, true); + } + + return f.ToArray(); + + } + public static NFP rotatePolygon(NFP polygon, float degrees) + { + NFP rotated = new NFP(); + + var angle = degrees * Math.PI / 180; + List pp = new List(); + for (var i = 0; i < polygon.length; i++) + { + var x = polygon[i].x; + var y = polygon[i].y; + var x1 = (x * Math.Cos(angle) - y * Math.Sin(angle)); + var y1 = (x * Math.Sin(angle) + y * Math.Cos(angle)); + + pp.Add(new SvgPoint(x1, y1)); + } + rotated.Points = pp.ToArray(); + + if (polygon.children != null && polygon.children.Count > 0) + { + rotated.children = new List(); ; + for (var j = 0; j < polygon.children.Count; j++) + { + rotated.children.Add(rotatePolygon(polygon.children[j], degrees)); + } + } + + return rotated; + } + + public static SheetPlacement placeParts(NFP[] sheets, NFP[] parts, SvgNestConfig config, int nestindex) + { + if (sheets == null || sheets.Count() == 0) return null; + + + int i, j, k, m, n; + double totalsheetarea = 0; + + NFP part = null; + // total length of merged lines + double totalMerged = 0; + + // rotate paths by given rotation + var rotated = new List(); + for (i = 0; i < parts.Length; i++) + { + var r = rotatePolygon(parts[i], parts[i].rotation); + r.Rotation = parts[i].rotation; + r.source = parts[i].source; + r.Id = parts[i].Id; + rotated.Add(r); + } + + parts = rotated.ToArray(); + + List allplacements = new List(); + + double fitness = 0; + + string key; + NFP nfp; + double sheetarea = -1; + int totalPlaced = 0; + int totalParts = parts.Count(); + + while (parts.Length > 0) + { + + List placed = new List(); + + List placements = new List(); + + // open a new sheet + var sheet = sheets.First(); + sheets = sheets.Skip(1).ToArray(); + sheetarea = Math.Abs(GeometryUtil.polygonArea(sheet)); + totalsheetarea += sheetarea; + + fitness += sheetarea; // add 1 for each new sheet opened (lower fitness is better) + + string clipkey = ""; + Dictionary clipCache = new Dictionary(); + var clipper = new ClipperLib.Clipper(); + var combinedNfp = new List>(); + var error = false; + IntPoint[][] clipperSheetNfp = null; + double? minwidth = null; + PlacementItem position = null; + double? minarea = null; + for (i = 0; i < parts.Length; i++) + { + float prog = 0.66f + 0.34f * (totalPlaced / (float)totalParts); + DisplayProgress(prog); + + part = parts[i]; + // inner NFP + NFP[] sheetNfp = null; + // try all possible rotations until it fits + // (only do this for the first part of each sheet, to ensure that all parts that can be placed are, even if we have to to open a lot of sheets) + for (j = 0; j < (360f / config.rotations); j++) + { + sheetNfp = getInnerNfp(sheet, part, 0, config); + + if (sheetNfp != null && sheetNfp.Count() > 0) + { + if (sheetNfp[0].length == 0) + { + throw new ArgumentException(); + } + else + { + break; + } + } + + var r = rotatePolygon(part, 360f / config.rotations); + r.rotation = part.rotation + (360f / config.rotations); + r.source = part.source; + r.id = part.id; + + // rotation is not in-place + part = r; + parts[i] = r; + + if (part.rotation > 360f) + { + part.rotation = part.rotation % 360f; + } + } + // part unplaceable, skip + if (sheetNfp == null || sheetNfp.Count() == 0) + { + continue; + } + + position = null; + + if (placed.Count == 0) + { + // first placement, put it on the top left corner + for (j = 0; j < sheetNfp.Count(); j++) + { + for (k = 0; k < sheetNfp[j].length; k++) + { + if (position == null || + ((sheetNfp[j][k].x - part[0].x) < position.x) || + ( + GeometryUtil._almostEqual(sheetNfp[j][k].x - part[0].x, position.x) + && ((sheetNfp[j][k].y - part[0].y) < position.y)) + ) + { + position = new PlacementItem() + { + x = sheetNfp[j][k].x - part[0].x, + y = sheetNfp[j][k].y - part[0].y, + id = part.id, + rotation = part.rotation, + source = part.source.Value + + }; + + + } + } + } + + if (position == null) + { + throw new Exception("position null"); + //console.log(sheetNfp); + } + placements.Add(position); + placed.Add(part); + totalPlaced++; + + continue; + } + + clipperSheetNfp = innerNfpToClipperCoordinates(sheetNfp, config); + + clipper = new ClipperLib.Clipper(); + combinedNfp = new List>(); + + error = false; + + // check if stored in clip cache + //var startindex = 0; + clipkey = "s:" + part.source + "r:" + part.rotation; + var startindex = 0; + if (EnableCaches && clipCache.ContainsKey(clipkey)) + { + var prevNfp = clipCache[clipkey].nfpp; + clipper.AddPaths(prevNfp.Select(z => z.ToList()).ToList(), ClipperLib.PolyType.ptSubject, true); + startindex = clipCache[clipkey].index; + } + + for (j = startindex; j < placed.Count; j++) + { + nfp = getOuterNfp(placed[j], part, 0); + // minkowski difference failed. very rare but could happen + if (nfp == null) + { + error = true; + break; + } + // shift to placed location + for (m = 0; m < nfp.length; m++) + { + nfp[m].x += placements[j].x; + nfp[m].y += placements[j].y; + } + if (nfp.children != null && nfp.children.Count > 0) + { + for (n = 0; n < nfp.children.Count; n++) + { + for (var o = 0; o < nfp.children[n].length; o++) + { + nfp.children[n][o].x += placements[j].x; + nfp.children[n][o].y += placements[j].y; + } + } + } + + var clipperNfp = nfpToClipperCoordinates(nfp, config.clipperScale); + + clipper.AddPaths(clipperNfp.Select(z => z.ToList()).ToList(), ClipperLib.PolyType.ptSubject, true); + } + //TODO: a lot here to insert + + if (error || !clipper.Execute(ClipperLib.ClipType.ctUnion, combinedNfp, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)) + { + //console.log('clipper error', error); + continue; + } + + + if (EnableCaches) + { + clipCache[clipkey] = new ClipCacheItem() + { + index = placed.Count - 1, + nfpp = combinedNfp.Select(z => z.ToArray()).ToArray() + }; + } + + //console.log('save cache', placed.length - 1); + + // difference with sheet polygon + List> _finalNfp = new List>(); + clipper = new ClipperLib.Clipper(); + + clipper.AddPaths(combinedNfp, ClipperLib.PolyType.ptClip, true); + + clipper.AddPaths(clipperSheetNfp.Select(z => z.ToList()).ToList(), ClipperLib.PolyType.ptSubject, true); + + + if (!clipper.Execute(ClipperLib.ClipType.ctDifference, _finalNfp, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftNonZero)) + { + continue; + } + + if (_finalNfp == null || _finalNfp.Count == 0) + { + continue; + } + + + List f = new List(); + for (j = 0; j < _finalNfp.Count; j++) + { + // back to normal scale + f.Add(Background.toNestCoordinates(_finalNfp[j].ToArray(), config.clipperScale)); + } + var finalNfp = f; + //finalNfp = f; + + // choose placement that results in the smallest bounding box/hull etc + // todo: generalize gravity direction + /*var minwidth = null; + var minarea = null; + var minx = null; + var miny = null; + var nf, area, shiftvector;*/ + minwidth = null; + minarea = null; + double? minx = null; + double? miny = null; + NFP nf; + double area = 0; + PlacementItem shiftvector = null; + + + NFP allpoints = new NFP(); + for (m = 0; m < placed.Count; m++) + { + for (n = 0; n < placed[m].length; n++) + { + allpoints.AddPoint( + new SvgPoint( + placed[m][n].x + placements[m].x, placed[m][n].y + placements[m].y)); + } + } + + PolygonBounds allbounds = null; + PolygonBounds partbounds = null; + if (config.placementType == PlacementTypeEnum.gravity || config.placementType == PlacementTypeEnum.box) + { + allbounds = GeometryUtil.getPolygonBounds(allpoints); + + NFP partpoints = new NFP(); + for (m = 0; m < part.length; m++) + { + partpoints.AddPoint(new SvgPoint(part[m].x, part[m].y)); + } + partbounds = GeometryUtil.getPolygonBounds(partpoints); + } + else + { + allpoints = getHull(allpoints); + } + for (j = 0; j < finalNfp.Count; j++) + { + nf = finalNfp[j]; + //console.log('evalnf',nf.length); + for (k = 0; k < nf.length; k++) + { + shiftvector = new PlacementItem() + { + id = part.id, + x = nf[k].x - part[0].x, + y = nf[k].y - part[0].y, + source = part.source.Value, + rotation = part.rotation + }; + PolygonBounds rectbounds = null; + if (config.placementType == PlacementTypeEnum.gravity || config.placementType == PlacementTypeEnum.box) + { + NFP poly = new NFP(); + poly.AddPoint(new SvgPoint(allbounds.x, allbounds.y)); + poly.AddPoint(new SvgPoint(allbounds.x + allbounds.width, allbounds.y)); + poly.AddPoint(new SvgPoint(allbounds.x + allbounds.width, allbounds.y + allbounds.height)); + poly.AddPoint(new SvgPoint(allbounds.x, allbounds.y + allbounds.height)); + /* + [ + // allbounds points + { x: allbounds.x, y: allbounds.y}, + { x: allbounds.x + allbounds.width, y: allbounds.y}, + { x: allbounds.x + allbounds.width, y: allbounds.y + allbounds.height}, + { x: allbounds.x, y: allbounds.y + allbounds.height},*/ + + poly.AddPoint(new SvgPoint(partbounds.x + shiftvector.x, partbounds.y + shiftvector.y)); + poly.AddPoint(new SvgPoint(partbounds.x + partbounds.width + shiftvector.x, partbounds.y + shiftvector.y)); + poly.AddPoint(new SvgPoint(partbounds.x + partbounds.width + shiftvector.x, partbounds.y + partbounds.height + shiftvector.y)); + poly.AddPoint(new SvgPoint(partbounds.x + shiftvector.x, partbounds.y + partbounds.height + shiftvector.y)); + /* + [ + + // part points + { x: partbounds.x + shiftvector.x, y: partbounds.y + shiftvector.y}, + { x: partbounds.x + partbounds.width + shiftvector.x, y: partbounds.y + shiftvector.y}, + { x: partbounds.x + partbounds.width + shiftvector.x, y: partbounds.y + partbounds.height + shiftvector.y}, + { x: partbounds.x + shiftvector.x, y: partbounds.y + partbounds.height + shiftvector.y} + ]*/ + rectbounds = GeometryUtil.getPolygonBounds(poly); + + // weigh width more, to help compress in direction of gravity + if (config.placementType == PlacementTypeEnum.gravity) + { + area = rectbounds.width * 2 + rectbounds.height; + } + else + { + area = rectbounds.width * rectbounds.height; + } + } + else + { + // must be convex hull + var localpoints = clone(allpoints); + + for (m = 0; m < part.length; m++) + { + localpoints.AddPoint(new SvgPoint(part[m].x + shiftvector.x, part[m].y + shiftvector.y)); + } + + area = Math.Abs(GeometryUtil.polygonArea(getHull(localpoints))); + shiftvector.hull = getHull(localpoints); + shiftvector.hullsheet = getHull(sheet); + } + //console.timeEnd('evalbounds'); + //console.time('evalmerge'); + MergedResult merged = null; + if (config.mergeLines) + { + throw new NotImplementedException(); + // if lines can be merged, subtract savings from area calculation + var shiftedpart = shiftPolygon(part, shiftvector); + List shiftedplaced = new List(); + + for (m = 0; m < placed.Count; m++) + { + shiftedplaced.Add(shiftPolygon(placed[m], placements[m])); + } + + // don't check small lines, cut off at about 1/2 in + double minlength = 0.5 * config.scale; + merged = mergedLength(shiftedplaced.ToArray(), shiftedpart, minlength, 0.1 * config.curveTolerance); + area -= merged.totalLength * config.timeRatio; + } + + //console.timeEnd('evalmerge'); + if ( + minarea == null || + area < minarea || + (GeometryUtil._almostEqual(minarea, area) && (minx == null || shiftvector.x < minx)) || + (GeometryUtil._almostEqual(minarea, area) && (minx != null && GeometryUtil._almostEqual(shiftvector.x, minx) && shiftvector.y < miny)) + ) + { + minarea = area; + + minwidth = rectbounds != null ? rectbounds.width : 0; + position = shiftvector; + if (minx == null || shiftvector.x < minx) + { + minx = shiftvector.x; + } + if (miny == null || shiftvector.y < miny) + { + miny = shiftvector.y; + } + + if (config.mergeLines) + { + position.mergedLength = merged.totalLength; + position.mergedSegments = merged.segments; + } + } + } + + } + + if (position != null) + { + placed.Add(part); + totalPlaced++; + placements.Add(position); + if (position.mergedLength != null) + { + totalMerged += position.mergedLength.Value; + } + } + // send placement progress signal + var placednum = placed.Count; + for (j = 0; j < allplacements.Count; j++) + { + placednum += allplacements[j].sheetplacements.Count; + } + //console.log(placednum, totalnum); + //ipcRenderer.send('background-progress', { index: nestindex, progress: 0.5 + 0.5 * (placednum / totalnum)}); + + //console.timeEnd('placement'); + } + //if(minwidth){ + if (!minwidth.HasValue) + { + fitness = double.NaN; + } + else + { + fitness += (minwidth.Value / sheetarea) + minarea.Value; + } + + //} + for (i = 0; i < placed.Count; i++) + { + var index = Array.IndexOf(parts, placed[i]); + if (index >= 0) + { + parts = parts.splice(index, 1); + } + } + if (placements != null && placements.Count > 0) + { + allplacements.Add(new SheetPlacementItem() + { + sheetId = sheet.id, + sheetSource = sheet.source.Value, + sheetplacements = placements + }); + //allplacements.Add({ sheet: sheet.source, sheetid: sheet.id, sheetplacements: placements}); + } + else + { + break; // something went wrong + } + + if (sheets.Count() == 0) + { + break; + } + } + + // there were parts that couldn't be placed + // scale this value high - we really want to get all the parts in, even at the cost of opening new sheets + for (i = 0; i < parts.Count(); i++) + { + fitness += 100000000 * (Math.Abs(GeometryUtil.polygonArea(parts[i])) / totalsheetarea); + } + // send finish progerss signal + //ipcRenderer.send('background-progress', { index: nestindex, progress: -1}); + + + return new SheetPlacement() + { + placements = new[] { allplacements.ToList() }, + fitness = fitness, + // paths = paths, + area = sheetarea, + mergedLength = totalMerged + + + }; + //return { placements: allplacements, fitness: fitness, area: sheetarea, mergedLength: totalMerged }; + } + // jsClipper uses X/Y instead of x/y... + public DataInfo data; + NFP[] parts; + + + + int index; + // run the placement synchronously + + + public static windowUnk window = new windowUnk(); + + public Action ResponseAction; + + public static long LastPlacePartTime = 0; + public void sync() + { + //console.log('starting synchronous calculations', Object.keys(window.nfpCache).length); + //console.log('in sync'); + var c = 0; + foreach (var key in window.nfpCache) + { + c++; + } + //console.log('nfp cached:', c); + Stopwatch sw = Stopwatch.StartNew(); + var placement = placeParts(data.sheets.ToArray(), parts, data.config, index); + sw.Stop(); + LastPlacePartTime = sw.ElapsedMilliseconds; + + placement.index = data.index; + ResponseAction(placement); + //ipcRenderer.send('background-response', placement); + } + public void BackgroundStart(DataInfo data) + { + this.data = data; + var index = data.index; + var individual = data.individual; + + var parts = individual.placements; + var rotations = individual.Rotation; + var ids = data.ids; + var sources = data.sources; + var children = data.children; + + for (var i = 0; i < parts.Count; i++) + { + parts[i].rotation = rotations[i]; + parts[i].id = ids[i]; + parts[i].source = sources[i]; + if (!data.config.simplify) + { + parts[i].children = children[i]; + } + } + + for (int i = 0; i < data.sheets.Count; i++) + { + data.sheets[i].id = data.sheetids[i]; + data.sheets[i].source = data.sheetsources[i]; + data.sheets[i].children = data.sheetchildren[i]; + } + + // preprocess + List pairs = new List(); + + if (Background.UseParallel) + { + object lobj = new object(); + Parallel.For(0, parts.Count, i => + { + { + var B = parts[i]; + for (var j = 0; j < i; j++) + { + var A = parts[j]; + var key = NfpPair.Create(A, B); + //var key = new NfpPair() + //{ + // A = A, + // B = B, + // ARotation = A.rotation, + // BRotation = B.rotation, + // Asource = A.source.Value, + // Bsource = B.source.Value + + //}; + var doc = new DbCacheKey() + { + A = A.source.Value, + B = B.source.Value, + + ARotation = A.rotation, + BRotation = B.rotation + + }; + lock (lobj) + { + if (!inpairs(key, pairs.ToArray()) && !window.db.has(doc)) + { + pairs.Add(key); + } + } + } + } + }); + } + else + { + for (var i = 0; i < parts.Count; i++) + { + var B = parts[i]; + for (var j = 0; j < i; j++) + { + var A = parts[j]; + var key = NfpPair.Create(A, B); + //var key = new NfpPair() + //{ + // A = A, + // B = B, + // ARotation = A.rotation, + // BRotation = B.rotation, + // Asource = A.source.Value, + // Bsource = B.source.Value + + //}; + var doc = new DbCacheKey() + { + A = A.source.Value, + B = B.source.Value, + + ARotation = A.rotation, + BRotation = B.rotation + }; + if (!inpairs(key, pairs.ToArray()) && !window.db.has(doc)) + { + pairs.Add(key); + } + } + } + } + + //console.log('pairs: ', pairs.length); + //console.time('Total'); + + this.parts = parts.ToArray(); + if (pairs.Count > 0) + { + + var ret1 = pmapDeepNest(pairs); + thenDeepNest(ret1, parts); + } + else + { + sync(); + } + } + public NFP getPart(int source, List parts) + { + for (var k = 0; k < parts.Count; k++) + { + if (parts[k].source == source) + { + return parts[k]; + } + } + return null; + } + + public void thenIterate(NfpPair processed, List parts) + { + + // returned data only contains outer nfp, we have to account for any holes separately in the synchronous portion + // this is because the c++ addon which can process interior nfps cannot run in the worker thread + var A = getPart(processed.Asource, parts); + var B = getPart(processed.Bsource, parts); + + List Achildren = new List(); + + + if (A.children != null) + { + for (int j = 0; j < A.children.Count; j++) + { + Achildren.Add(rotatePolygon(A.children[j], processed.ARotation)); + } + } + + if (Achildren.Count > 0) + { + var Brotated = rotatePolygon(B, processed.BRotation); + var bbounds = GeometryUtil.getPolygonBounds(Brotated); + List cnfp = new List(); + + for (int j = 0; j < Achildren.Count; j++) + { + var cbounds = GeometryUtil.getPolygonBounds(Achildren[j]); + if (cbounds.width > bbounds.width && cbounds.height > bbounds.height) + { + var n = getInnerNfp(Achildren[j], Brotated, 1, data.config); + if (n != null && n.Count() > 0) + { + cnfp.AddRange(n); + } + } + } + + processed.nfp.children = cnfp; + } + DbCacheKey doc = new DbCacheKey() + { + A = processed.Asource, + B = processed.Bsource, + ARotation = processed.ARotation, + BRotation = processed.BRotation, + nfp = new[] { processed.nfp } + }; + + /*var doc = { + A: processed[i].Asource, + B: processed[i].Bsource, + Arotation: processed[i].Arotation, + Brotation: processed[i].Brotation, + nfp: processed[i].nfp + + };*/ + window.db.insert(doc); + } + + public static Action displayProgress; + public static void DisplayProgress(double p) + { + if (displayProgress != null) + { + displayProgress(p); + } + } + public void thenDeepNest(NfpPair[] processed, List parts) + { + int cnt = 0; + if (UseParallel) + { + Parallel.For(0, processed.Count(), (i) => + { + float progress = 0.33f + 0.33f * (cnt / (float)processed.Count()); + cnt++; + DisplayProgress(progress); + thenIterate(processed[i], parts); + }); + + } + else + { + for (var i = 0; i < processed.Count(); i++) + { + float progress = 0.33f + 0.33f * (cnt / (float)processed.Count()); + cnt++; + DisplayProgress(progress); + thenIterate(processed[i], parts); + } + } + + //console.timeEnd('Total'); + //console.log('before sync'); + sync(); + } + + + public bool inpairs(NfpPair key, NfpPair[] p) + { + for (var i = 0; i < p.Length; i++) + { + if (p[i].Asource == key.Asource && p[i].Bsource == key.Bsource && p[i].ARotation == key.ARotation && p[i].BRotation == key.BRotation) + { + return true; + } + } + return false; + } + + public static bool UseParallel = false; + public NfpPair[] pmapDeepNest(List pairs) + { + + + NfpPair[] ret = new NfpPair[pairs.Count()]; + int cnt = 0; + if (UseParallel) + { + Parallel.For(0, pairs.Count, (i) => + { + ret[i] = process(pairs[i]); + float progress = 0.33f * (cnt / (float)pairs.Count); + cnt++; + DisplayProgress(progress); + }); + } + else + { + for (int i = 0; i < pairs.Count; i++) + { + var item = pairs[i]; + ret[i] = process(item); + float progress = 0.33f * (cnt / (float)pairs.Count); + cnt++; + DisplayProgress(progress); + } + } + return ret.ToArray(); + } + public NfpPair process(NfpPair pair) + { + var A = rotatePolygon(pair.A, pair.ARotation); + var B = rotatePolygon(pair.B, pair.BRotation); + + /////////////////// + var Ac = _Clipper.ScaleUpPaths(A, 10000000); + + var Bc = _Clipper.ScaleUpPaths(B, 10000000); + for (var i = 0; i < Bc.Length; i++) + { + Bc[i].X *= -1; + Bc[i].Y *= -1; + } + var solution = ClipperLib.Clipper.MinkowskiSum(new List(Ac), new List(Bc), true); + NFP clipperNfp = null; + + double? largestArea = null; + for (int i = 0; i < solution.Count(); i++) + { + var n = toNestCoordinates(solution[i].ToArray(), 10000000); + var sarea = -GeometryUtil.polygonArea(n); + if (largestArea == null || largestArea < sarea) + { + clipperNfp = n; + largestArea = sarea; + } + } + + for (var i = 0; i < clipperNfp.length; i++) + { + clipperNfp[i].x += B[0].x; + clipperNfp[i].y += B[0].y; + } + + //return new SvgNestPort.NFP[] { new SvgNestPort.NFP() { Points = clipperNfp.Points } }; + + ////////////// + + pair.A = null; + pair.B = null; + pair.nfp = clipperNfp; + return pair; + + + } + + public static NFP toNestCoordinates(IntPoint[] polygon, double scale) + { + var clone = new List(); + + for (var i = 0; i < polygon.Count(); i++) + { + clone.Add(new SvgPoint( + polygon[i].X / scale, + polygon[i].Y / scale + )); + } + return new NFP() { Points = clone.ToArray() }; + } + + public static NFP getHull(NFP polygon) + { + // convert to hulljs format + /*var hull = new ConvexHullGrahamScan(); + for(var i=0; i clipperNfp = new List(); + + // children first + if (nfp.children != null && nfp.children.Count > 0) + { + for (var j = 0; j < nfp.children.Count; j++) + { + if (GeometryUtil.polygonArea(nfp.children[j]) < 0) + { + nfp.children[j].reverse(); + } + //var childNfp = SvgNest.toClipperCoordinates(nfp.children[j]); + var childNfp = _Clipper.ScaleUpPaths(nfp.children[j], clipperScale); + clipperNfp.Add(childNfp); + } + } + + if (GeometryUtil.polygonArea(nfp) > 0) + { + nfp.reverse(); + } + + + //var outerNfp = SvgNest.toClipperCoordinates(nfp); + + // clipper js defines holes based on orientation + + var outerNfp = _Clipper.ScaleUpPaths(nfp, clipperScale); + + //var cleaned = ClipperLib.Clipper.CleanPolygon(outerNfp, 0.00001*config.clipperScale); + + clipperNfp.Add(outerNfp); + //var area = Math.abs(ClipperLib.Clipper.Area(cleaned)); + + return clipperNfp.ToArray(); + } + // inner nfps can be an array of nfps, outer nfps are always singular + public static IntPoint[][] innerNfpToClipperCoordinates(NFP[] nfp, SvgNestConfig config) + { + List clipperNfp = new List(); + for (var i = 0; i < nfp.Count(); i++) + { + var clip = nfpToClipperCoordinates(nfp[i], config.clipperScale); + clipperNfp.AddRange(clip); + //clipperNfp = clipperNfp.Concat(new[] { clip }).ToList(); + } + + return clipperNfp.ToArray(); + } + + static object lockobj = new object(); + + public static NFP[] NewMinkowskiSum(NFP pattern, NFP path, int type, bool useChilds = false, bool takeOnlyBiggestArea = true) + { + var key = pattern.source + ";" + path.source + ";" + pattern.rotation + ";" + path.rotation; + bool cacheAllow = type != 1; + if (cacheProcess.ContainsKey(key) && cacheAllow) + { + return cacheProcess[key]; + } + + var ac = _Clipper.ScaleUpPaths(pattern, 10000000); + List> solution = null; + if (useChilds) + { + var bc = Background.nfpToClipperCoordinates(path, 10000000); + for (var i = 0; i < bc.Length; i++) + { + for (int j = 0; j < bc[i].Length; j++) + { + bc[i][j].X *= -1; + bc[i][j].Y *= -1; + } + } + + solution = ClipperLib.Clipper.MinkowskiSum(new List(ac), new List>(bc.Select(z => z.ToList())), true); + } + else + { + var bc = _Clipper.ScaleUpPaths(path, 10000000); + for (var i = 0; i < bc.Length; i++) + { + bc[i].X *= -1; + bc[i].Y *= -1; + } + solution = Clipper.MinkowskiSum(new List(ac), new List(bc), true); + } + NFP clipperNfp = null; + + double? largestArea = null; + int largestIndex = -1; + + for (int i = 0; i < solution.Count(); i++) + { + var n = toNestCoordinates(solution[i].ToArray(), 10000000); + var sarea = Math.Abs(GeometryUtil.polygonArea(n)); + if (largestArea == null || largestArea < sarea) + { + clipperNfp = n; + largestArea = sarea; + largestIndex = i; + } + } + if (!takeOnlyBiggestArea) + { + for (int j = 0; j < solution.Count; j++) + { + if (j == largestIndex) continue; + var n = toNestCoordinates(solution[j].ToArray(), 10000000); + if (clipperNfp.children == null) + clipperNfp.children = new List(); + clipperNfp.children.Add(n); + } + } + for (var i = 0; i < clipperNfp.Length; i++) + { + + clipperNfp[i].x *= -1; + clipperNfp[i].y *= -1; + clipperNfp[i].x += pattern[0].x; + clipperNfp[i].y += pattern[0].y; + + } + if (clipperNfp.children != null) + foreach (var nFP in clipperNfp.children) + { + for (int j = 0; j < nFP.Length; j++) + { + + nFP.Points[j].x *= -1; + nFP.Points[j].y *= -1; + nFP.Points[j].x += pattern[0].x; + nFP.Points[j].y += pattern[0].y; + } + } + var res = new[] { clipperNfp }; + if (cacheAllow) + { + cacheProcess.Add(key, res); + } + return res; + } + public static void ExecuteSTA(Action act) + { + if (!Debugger.IsAttached) return; + Thread thread = new Thread(() => { act(); }); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + } + + public static bool UseExternalDll = false; + public static NFP getOuterNfp(NFP A, NFP B, int type, bool inside = false)//todo:?inside def? + { + NFP[] nfp = null; + + + var key = new DbCacheKey() + { + A = A.source, + B = B.source, + ARotation = A.rotation, + BRotation = B.rotation, + //Type = type + }; + + var doc = window.db.find(key); + if (doc != null) + { + return doc.First(); + } + + /* + + // try the file cache if the calculation will take a long time + var doc = window.db.find({ A: A.source, B: B.source, Arotation: A.rotation, Brotation: B.rotation }); + + if (doc) + { + return doc; + }*/ + + + // not found in cache + if (inside || (A.children != null && A.children.Count > 0)) + { + lock (lockobj) + { + if (UseExternalDll) + { + nfp = Process2(A, B, type); + + } + else + { + nfp = NewMinkowskiSum(B, A, type, true, takeOnlyBiggestArea: false); + } + } + } + else + { + var Ac = _Clipper.ScaleUpPaths(A, 10000000); + + var Bc = _Clipper.ScaleUpPaths(B, 10000000); + for (var i = 0; i < Bc.Length; i++) + { + Bc[i].X *= -1; + Bc[i].Y *= -1; + } + var solution = ClipperLib.Clipper.MinkowskiSum(new List(Ac), new List(Bc), true); + NFP clipperNfp = null; + + double? largestArea = null; + for (int i = 0; i < solution.Count(); i++) + { + var n = Background.toNestCoordinates(solution[i].ToArray(), 10000000); + var sarea = GeometryUtil.polygonArea(n); + if (largestArea == null || largestArea > sarea) + { + clipperNfp = n; + largestArea = sarea; + } + } + + for (var i = 0; i < clipperNfp.length; i++) + { + clipperNfp[i].x += B[0].x; + clipperNfp[i].y += B[0].y; + } + nfp = new NFP[] { new NFP() { Points = clipperNfp.Points } }; + + + } + + if (nfp == null || nfp.Length == 0) + { + //console.log('holy shit', nfp, A, B, JSON.stringify(A), JSON.stringify(B)); + return null; + } + + NFP nfps = nfp.First(); + /* + nfp = nfp.pop(); + */ + if (nfps == null || nfps.Length == 0) + { + return null; + } + /* + if (!nfp || nfp.length == 0) + { + return null; + } + */ + if (!inside && A.source != null && B.source != null) + { + var doc2 = new DbCacheKey() + { + A = A.source.Value, + B = B.source.Value, + ARotation = A.rotation, + BRotation = B.rotation, + nfp = nfp + }; + window.db.insert(doc2); + + + } + /* + if (!inside && typeof A.source !== 'undefined' && typeof B.source !== 'undefined') + { + // insert into db + doc = { + A: A.source, + B: B.source, + Arotation: A.rotation, + Brotation: B.rotation, + nfp: nfp + + }; + window.db.insert(doc); + } + */ + return nfps; + + + + } + } + + public class ClipCacheItem + { + public int index; + public IntPoint[][] nfpp; + } + + public class dbCache + { + public dbCache(windowUnk w) + { + window = w; + } + public bool has(DbCacheKey obj) + { + lock (lockobj) + { + var key = getKey(obj); + //var key = "A" + obj.A + "B" + obj.B + "Arot" + (int)Math.Round(obj.ARotation) + "Brot" + (int)Math.Round(obj.BRotation); + if (window.nfpCache.ContainsKey(key)) + { + return true; + } + return false; + } + + } + + public windowUnk window; + public object lockobj = new object(); + + string getKey(DbCacheKey obj) + { + var key = "A" + obj.A + "B" + obj.B + "Arot" + (int)Math.Round(obj.ARotation * 10000) + "Brot" + (int)Math.Round((obj.BRotation * 10000)) + ";" + obj.Type; + return key; + } + internal void insert(DbCacheKey obj, bool inner = false) + { + + var key = getKey(obj); + //if (window.performance.memory.totalJSHeapSize < 0.8 * window.performance.memory.jsHeapSizeLimit) + { + lock (lockobj) + { + if (!window.nfpCache.ContainsKey(key)) + { + window.nfpCache.Add(key, Background.cloneNfp(obj.nfp, inner).ToList()); + } + else + { + throw new Exception("trouble .cache allready has such key"); + // window.nfpCache[key] = Background.cloneNfp(new[] { obj.nfp }, inner).ToList(); + } + } + //console.log('cached: ',window.cache[key].poly); + //console.log('using', window.performance.memory.totalJSHeapSize/window.performance.memory.jsHeapSizeLimit); + } + } + public NFP[] find(DbCacheKey obj, bool inner = false) + { + lock (lockobj) + { + var key = getKey(obj); + //var key = "A" + obj.A + "B" + obj.B + "Arot" + (int)Math.Round(obj.ARotation) + "Brot" + (int)Math.Round((obj.BRotation)); + + //console.log('key: ', key); + if (window.nfpCache.ContainsKey(key)) + { + return Background.cloneNfp(window.nfpCache[key].ToArray(), inner); + } + + return null; + } + } + + } + public class windowUnk + { + public windowUnk() + { + db = new dbCache(this); + } + public Dictionary> nfpCache = new Dictionary>(); + public dbCache db; + } +} diff --git a/PCUT/PCUT/DeepNestApi/DraftElement.cs b/PCUT/PCUT/DeepNestApi/DraftElement.cs new file mode 100644 index 0000000..1e676e5 --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/DraftElement.cs @@ -0,0 +1,19 @@ +using Windows.Foundation; + +namespace PCUT.DeepNestApi +{ + public abstract class DraftElement + { + public object Tag; + public Point Start; + public Point End; + + public abstract double Length { get; } + + public abstract void Reverse(); + public abstract Point[] GetPoints(); + + internal abstract void Mult(double mult); + + } +} diff --git a/PCUT/PCUT/DeepNestApi/LineElement.cs b/PCUT/PCUT/DeepNestApi/LineElement.cs new file mode 100644 index 0000000..cbfbeef --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/LineElement.cs @@ -0,0 +1,26 @@ +using PCUT.Extensions; +using Windows.Foundation; + +namespace PCUT.DeepNestApi +{ + public class LineElement : DraftElement + { + public override Point[] GetPoints() + { + return new[] { Start, End }; + } + public override double Length => Start.DistTo(End); + public override void Reverse() + { + var temp = End; + End = Start; + Start = temp; + } + + internal override void Mult(double mult) + { + Start = Start.Mult(mult); + } + } + +} diff --git a/PCUT/PCUT/DeepNestApi/LocalContour.cs b/PCUT/PCUT/DeepNestApi/LocalContour.cs new file mode 100644 index 0000000..4b819f4 --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/LocalContour.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using Windows.Foundation; + +namespace PCUT.DeepNestApi +{ + public class LocalContour + { + public double Len + { + get + { + double len = 0; + for (int i = 1; i <= Points.Count; i++) + { + var p1 = Points[i - 1]; + var p2 = Points[i % Points.Count]; + len += Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2)); + } + return len; + } + } + public List Points = new List(); + public bool Enable = true; + public List Childrens = new List(); + public LocalContour Parent; + public object Tag; + + public void Scale(double v) + { + for (int i = 0; i < Points.Count; i++) + { + Points[i] = new Point((Points[i].X * v), (Points[i].Y * v)); + } + } + } +} diff --git a/PCUT/PCUT/DeepNestApi/NestingContext.cs b/PCUT/PCUT/DeepNestApi/NestingContext.cs new file mode 100644 index 0000000..dad6370 --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/NestingContext.cs @@ -0,0 +1,357 @@ +using DeepNestLib; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace PCUT.DeepNestApi +{ + public class NestingContext + { + public List Polygons { get; private set; } = new List(); + public List Sheets { get; private set; } = new List(); + + + public double MaterialUtilization { get; private set; } = 0; + public int PlacedPartsCount { get; private set; } = 0; + + + SheetPlacement current = null; + public SheetPlacement Current { get { return current; } } + public SvgNest Nest { get; private set; } + + public int Iterations { get; private set; } = 0; + + public void StartNest() + { + current = null; + Nest = new SvgNest(); + Background.cacheProcess = new Dictionary(); + Background.window = new windowUnk(); + Background.callCounter = 0; + Iterations = 0; + } + + bool offsetTreePhase = true; + public void NestIterate() + { + List lsheets = new List(); + List lpoly = new List(); + + for (int i = 0; i < Polygons.Count; i++) + { + Polygons[i].id = i; + } + for (int i = 0; i < Sheets.Count; i++) + { + Sheets[i].id = i; + } + foreach (var item in Polygons) + { + NFP clone = new NFP(); + clone.id = item.id; + clone.source = item.source; + clone.Points = item.Points.Select(z => new SvgPoint(z.x, z.y) { exact = z.exact }).ToArray(); + if (item.children != null) + { + clone.children = new List(); + foreach (var citem in item.children) + { + clone.children.Add(new NFP()); + var l = clone.children.Last(); + l.id = citem.id; + l.source = citem.source; + l.Points = citem.Points.Select(z => new SvgPoint(z.x, z.y) { exact = z.exact }).ToArray(); + } + } + lpoly.Add(clone); + } + + + foreach (var item in Sheets) + { + NFP clone = new NFP(); + clone.id = item.id; + clone.source = item.source; + clone.Points = item.Points.Select(z => new SvgPoint(z.x, z.y) { exact = z.exact }).ToArray(); + if (item.children != null) + { + clone.children = new List(); + foreach (var citem in item.children) + { + clone.children.Add(new NFP()); + var l = clone.children.Last(); + l.id = citem.id; + l.source = citem.source; + l.Points = citem.Points.Select(z => new SvgPoint(z.x, z.y) { exact = z.exact }).ToArray(); + } + } + lsheets.Add(clone); + } + + if (offsetTreePhase) + { + var grps = lpoly.GroupBy(z => z.source).ToArray(); + if (Background.UseParallel) + { + Parallel.ForEach(grps, (item) => + { + SvgNest.offsetTree(item.First(), 0.5 * SvgNest.Config.spacing, SvgNest.Config); + foreach (var zitem in item) + { + zitem.Points = item.First().Points.ToArray(); + } + + }); + + } + else + { + foreach (var item in grps) + { + SvgNest.offsetTree(item.First(), 0.5 * SvgNest.Config.spacing, SvgNest.Config); + foreach (var zitem in item) + { + zitem.Points = item.First().Points.ToArray(); + } + } + } + + foreach (var item in lsheets) + { + var gap = SvgNest.Config.sheetSpacing - SvgNest.Config.spacing / 2; + SvgNest.offsetTree(item, -gap, SvgNest.Config, true); + } + } + + List partsLocal = new List(); + var p1 = lpoly.GroupBy(z => z.source).Select(z => new NestItem() + { + Polygon = z.First(), + IsSheet = false, + Quanity = z.Count() + }); + + var p2 = lsheets.GroupBy(z => z.source).Select(z => new NestItem() + { + Polygon = z.First(), + IsSheet = true, + Quanity = z.Count() + }); + + + partsLocal.AddRange(p1); + partsLocal.AddRange(p2); + int srcc = 0; + foreach (var item in partsLocal) + { + item.Polygon.source = srcc++; + } + + + Nest.launchWorkers(partsLocal.ToArray()); + var plcpr = Nest.nests.First(); + + if (current == null || plcpr.fitness < current.fitness) + { + AssignPlacement(plcpr); + } + Iterations++; + } + + public void AssignPlacement(SheetPlacement plcpr) + { + current = plcpr; + double totalSheetsArea = 0; + double totalPartsArea = 0; + + PlacedPartsCount = 0; + List placed = new List(); + foreach (var item in Polygons) + { + item.sheet = null; + } + List sheetsIds = new List(); + + foreach (var item in plcpr.placements) + { + foreach (var zitem in item) + { + var sheetid = zitem.sheetId; + if (!sheetsIds.Contains(sheetid)) + { + sheetsIds.Add(sheetid); + } + + var sheet = Sheets.First(z => z.id == sheetid); + totalSheetsArea += GeometryUtil.polygonArea(sheet); + + foreach (var ssitem in zitem.sheetplacements) + { + PlacedPartsCount++; + var poly = Polygons.First(z => z.id == ssitem.id); + totalPartsArea += GeometryUtil.polygonArea(poly); + placed.Add(poly); + poly.sheet = sheet; + poly.x = ssitem.x + sheet.x; + poly.y = ssitem.y + sheet.y; + poly.rotation = ssitem.rotation; + } + } + } + + var emptySheets = Sheets.Where(z => !sheetsIds.Contains(z.id)).ToArray(); + + MaterialUtilization = Math.Abs(totalPartsArea / totalSheetsArea); + + var ppps = Polygons.Where(z => !placed.Contains(z)); + foreach (var item in ppps) + { + item.x = -1000; + item.y = 0; + } + } + + public void ReorderSheets() + { + double x = 0; + double y = 0; + int gap = 10; + for (int i = 0; i < Sheets.Count; i++) + { + Sheets[i].x = x; + Sheets[i].y = y; + if (Sheets[i] is Sheet) + { + var r = Sheets[i] as Sheet; + x += r.Width + gap; + } + else + { + var maxx = Sheets[i].Points.Max(z => z.x); + var minx = Sheets[i].Points.Min(z => z.x); + var w = maxx - minx; + x += w + gap; + } + } + } + + public void AddSheet(int w, int h, int src) + { + var tt = new RectangleSheet(); + tt.Name = "sheet" + (Sheets.Count + 1); + Sheets.Add(tt); + + tt.source = src; + tt.Height = h; + tt.Width = w; + tt.Rebuild(); + ReorderSheets(); + } + + public void AddSheet(NFP nfp, int src) + { + Sheet sheet = new Sheet(); + var ns = Background.clone(nfp); + sheet.Points = ns.Points; + sheet.children = ns.children; + + sheet.Width = sheet.WidthCalculated; + sheet.Height = sheet.HeightCalculated; + sheet.source = src; + + sheet.Name = "sheet" + (Sheets.Count + 1); + Sheets.Add(sheet); + ReorderSheets(); + } + + public void LoadSampleData() + { + Console.WriteLine("Adding sheets.."); + //add sheets + for (int i = 0; i < 5; i++) + { + AddSheet(3000, 1500, 0); + } + + Console.WriteLine("Adding parts.."); + //add parts + int src1 = GetNextSource(); + for (int i = 0; i < 200; i++) + { + AddRectanglePart(src1, 250, 220); + } + + } + //public void LoadInputData(string path, int count) + //{ + // var dir = new DirectoryInfo(path); + // foreach (var item in dir.GetFiles("*.svg")) + // { + // try + // { + // var dets = SvgParser.LoadSvg(item.FullName); + // foreach (var r in dets) + // { + // var src = GetNextSource(); + // for (int i = 0; i < count; i++) + // { + // ImportFromRawDetail(r, src); + // } + // } + // } + // catch (Exception ex) + // { + // Console.WriteLine("Error loading " + item.FullName + ". skip"); + // } + // } + //} + + public NFP ImportFromRawDetail(RawDetail raw, int src) + { + var d = raw.ToNfp(); + if (d == null) + return null; + + d.source = src; + Polygons.Add(d); + return d; + } + + public int GetNextSource() + { + if (Polygons.Any()) + { + return Polygons.Max(z => z.source.Value) + 1; + } + return 0; + } + + public int GetNextSheetSource() + { + if (Sheets.Any()) + { + return Sheets.Max(z => z.source.Value) + 1; + } + return 0; + } + public void AddRectanglePart(int src, int ww = 50, int hh = 80) + { + int xx = 0; + int yy = 0; + NFP pl = new NFP(); + + Polygons.Add(pl); + pl.source = src; + pl.Points = new SvgPoint[] { }; + pl.AddPoint(new SvgPoint(xx, yy)); + pl.AddPoint(new SvgPoint(xx + ww, yy)); + pl.AddPoint(new SvgPoint(xx + ww, yy + hh)); + pl.AddPoint(new SvgPoint(xx, yy + hh)); + } + + } +} diff --git a/PCUT/PCUT/DeepNestApi/PolylineElement.cs b/PCUT/PCUT/DeepNestApi/PolylineElement.cs new file mode 100644 index 0000000..32fccdc --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/PolylineElement.cs @@ -0,0 +1,46 @@ +using System.Linq; +using PCUT.Extensions; +using Windows.Foundation; + +namespace PCUT.DeepNestApi +{ + public class PolylineElement : DraftElement + { + public Point[] Points; + + public override double Length + { + get + { + double len = 0; + for (int i = 1; i < Points.Length; i++) + { + len += Points[i - 1].DistTo(Points[i]); + } + return len; + } + } + + public override Point[] GetPoints() + { + return Points; + } + + public override void Reverse() + { + Points = Points.Reverse().ToArray(); + Start = Points[0]; + End = Points[Points.Length - 1]; + } + + internal override void Mult(double mult) + { + for (int i = 0; i < Points.Length; i++) + { + Points[i] = Points[i].Mult(mult); + } + Start = Points[0]; + End = Points[Points.Length - 1]; + } + } +} diff --git a/PCUT/PCUT/DeepNestApi/RawDetail.cs b/PCUT/PCUT/DeepNestApi/RawDetail.cs new file mode 100644 index 0000000..975c800 --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/RawDetail.cs @@ -0,0 +1,91 @@ +using DeepNestLib; +using System.Collections.Generic; +using System.Linq; +using PCUT.Extensions; +using Windows.Foundation; +using Windows.UI.Xaml.Media; + +namespace PCUT.DeepNestApi +{ + public class RawDetail + { + public List Outers = new List(); + public List Holes = new List(); + public object Tag; + public bool Hidden; + + public string Name { get; set; } + + public NFP ToNfp() + { + + NFP po = null; + List nfps = new List(); + foreach (var item in Outers) + { + var nn = new NFP(); + nfps.Add(nn); + foreach (var pitem in item.Points) + { + nn.AddPoint(new SvgPoint(pitem.X, pitem.Y)); + } + foreach (var ch in item.Childrens) + { + nn = new NFP(); + nfps.Add(nn); + foreach (var pitem in ch.Points) + { + nn.AddPoint(new SvgPoint(pitem.X, pitem.Y)); + } + } + } + + if (nfps.Any()) + { + var tt = nfps.OrderByDescending(z => z.Area).First(); + po = tt; + po.Name = Name; + + foreach (var r in nfps) + { + if (r == tt) + continue; + + if (po.children == null) + { + po.children = new List(); + } + po.children.Add(r); + } + + + + } + return po; + } + + public Rect BoundingBox() + { + GeometryGroup gp = new GeometryGroup(); + //GraphicsPath gp = new GraphicsPath(); + foreach (var item in Outers) + { + //gp.AddPolygon(item.Points.ToArray());) + gp.Children.Add(item.Points.BuildPath()); + } + return gp.Bounds; + } + + public void Scale(double v) + { + foreach (var item in Outers) + { + item.Scale(v); + } + foreach (var item in Holes) + { + item.Scale(v); + } + } + } +} \ No newline at end of file diff --git a/PCUT/PCUT/DeepNestApi/SvgNest.cs b/PCUT/PCUT/DeepNestApi/SvgNest.cs new file mode 100644 index 0000000..42fc5cf --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/SvgNest.cs @@ -0,0 +1,1069 @@ +using ClipperLib; +using DeepNestLib; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace PCUT.DeepNestApi +{ + public class SvgNest + { + + public SvgNest() + { + + } + public class InrangeItem + { + public SvgPoint point; + public double distance; + } + + #region experimental features + public static SvgPoint RotatePoint(SvgPoint p, double cx, double cy, double angle) + { + return new SvgPoint(Math.Cos(angle) * (p.x - cx) - Math.Sin(angle) * (p.y - cy) + cx, + Math.Sin(angle) * (p.x - cx) + Math.Cos(angle) * (p.y - cy) + cy); + } + + public static NFP GetMinimumBox(NFP vv) + { + var hull = Background.getHull(new NFP() { Points = vv.Points.Select(z => new SvgPoint(z.x, z.y)).ToArray() }); + double minArea = double.MaxValue; + List rect = new List(); + for (int i = 0; i < hull.Length; i++) + { + var p0 = hull.Points[i]; + var p1 = hull.Points[(i + 1) % hull.Length]; + var dx = p1.x - p0.x; + var dy = p1.y - p0.y; + var atan = Math.Atan2(dy, dx); + + List dd = new List(); + for (int j = 0; j < vv.Length; j++) + { + var r = RotatePoint(new SvgPoint(vv[j].x, vv[j].y), 0, 0, -atan); + dd.Add(r); + } + var maxx = dd.Max(z => z.x); + var maxy = dd.Max(z => z.y); + var minx = dd.Min(z => z.x); + var miny = dd.Min(z => z.y); + + var area = (maxx - minx) * (maxy - miny); + + if (area < minArea) + { + minArea = area; + rect.Clear(); + + rect.Add(new SvgPoint(minx, miny)); + rect.Add(new SvgPoint(maxx, miny)); + rect.Add(new SvgPoint(maxx, maxy)); + rect.Add(new SvgPoint(minx, maxy)); + for (int j = 0; j < rect.Count; j++) + { + rect[j] = RotatePoint(new SvgPoint(rect[j].x, rect[j].y), 0, 0, atan); + } + } + } + + NFP ret = new NFP(); + ret.Points = rect.ToArray(); + return ret; + } + static NFP boundingBox(NFP offset) + { + NFP ret = new NFP(); + var maxx = offset.Points.Max(z => z.x); + var maxy = offset.Points.Max(z => z.y); + var minx = offset.Points.Min(z => z.x); + var miny = offset.Points.Min(z => z.y); + ret.AddPoint(new SvgPoint(minx, miny)); + ret.AddPoint(new SvgPoint(maxx, miny)); + ret.AddPoint(new SvgPoint(maxx, maxy)); + ret.AddPoint(new SvgPoint(minx, maxy)); + return ret; + } + ///

+ /// Clip the subject so it stays inside the clipBounds. + /// + /// + /// + /// + /// + internal static NFP ClipSubject(NFP subject, NFP clipBounds, double clipperScale) + { + var clipperSubject = Background.innerNfpToClipperCoordinates(new NFP[] { subject }, SvgNest.Config); + var clipperClip = Background.innerNfpToClipperCoordinates(new NFP[] { clipBounds }, SvgNest.Config); + + var clipper = new Clipper(); + clipper.AddPaths(clipperClip.Select(z => z.ToList()).ToList(), PolyType.ptClip, true); + clipper.AddPaths(clipperSubject.Select(z => z.ToList()).ToList(), PolyType.ptSubject, true); + + List> finalNfp = new List>(); + if (clipper.Execute(ClipType.ctIntersection, finalNfp, PolyFillType.pftNonZero, PolyFillType.pftNonZero) && finalNfp != null && finalNfp.Count > 0) + { + return Background.toNestCoordinates(finalNfp[0].ToArray(), clipperScale); + } + + return subject; + } + #endregion + + public static SvgPoint getTarget(SvgPoint o, NFP simple, double tol) + { + List inrange = new List(); + // find closest points within 2 offset deltas + for (var j = 0; j < simple.length; j++) + { + var s = simple[j]; + var d2 = (o.x - s.x) * (o.x - s.x) + (o.y - s.y) * (o.y - s.y); + if (d2 < tol * tol) + { + inrange.Add(new InrangeItem() { point = s, distance = d2 }); + } + } + + SvgPoint target = null; + if (inrange.Count > 0) + { + var filtered = inrange.Where((p) => + { + return p.point.exact; + }).ToList(); + + // use exact points when available, normal points when not + inrange = filtered.Count > 0 ? filtered : inrange; + + + inrange = inrange.OrderBy((b) => + { + return b.distance; + }).ToList(); + + target = inrange[0].point; + } + else + { + double? mind = null; + for (int j = 0; j < simple.length; j++) + { + var s = simple[j]; + var d2 = (o.x - s.x) * (o.x - s.x) + (o.y - s.y) * (o.y - s.y); + if (mind == null || d2 < mind) + { + target = s; + mind = d2; + } + } + } + + return target; + } + + public static SvgNestConfig Config = new SvgNestConfig(); + + + public static NFP clone(NFP p) + { + var newp = new NFP(); + for (var i = 0; i < p.length; i++) + { + newp.AddPoint(new SvgPoint( + + p[i].x, + p[i].y + + )); + } + + return newp; + } + + + public static bool pointInPolygon(SvgPoint point, NFP polygon) + { + // scaling is deliberately coarse to filter out points that lie *on* the polygon + + var p = svgToClipper2(polygon, 1000); + var pt = new ClipperLib.IntPoint(1000 * point.x, 1000 * point.y); + + return ClipperLib.Clipper.PointInPolygon(pt, p.ToList()) > 0; + } + + // returns true if any complex vertices fall outside the simple polygon + public static bool exterior(NFP simple, NFP complex, bool inside) + { + // find all protruding vertices + for (var i = 0; i < complex.length; i++) + { + var v = complex[i]; + if (!inside && !pointInPolygon(v, simple) && find(v, simple) == null) + { + return true; + } + if (inside && pointInPolygon(v, simple) && find(v, simple) != null) + { + return true; + } + } + return false; + } + + public static NFP simplifyFunction(NFP polygon, bool inside) + { + return simplifyFunction(polygon, inside, SvgNest.Config); + } + public static NFP simplifyFunction(NFP polygon, bool inside, SvgNestConfig config) + { + var tolerance = 4 * config.curveTolerance; + + // give special treatment to line segments above this length (squared) + var fixedTolerance = 40 * config.curveTolerance * 40 * config.curveTolerance; + int i, j, k; + + var hull = Background.getHull(polygon); + if (config.simplify) + { + /* + // use convex hull + var hull = new ConvexHullGrahamScan(); + for(var i=0; i 1) + { + polygon = cleaned; + } + else + { + return polygon; + } + // polygon to polyline + var copy = polygon.slice(0); + copy.push(copy[0]); + // mark all segments greater than ~0.25 in to be kept + // the PD simplification algo doesn't care about the accuracy of long lines, only the absolute distance of each point + // we care a great deal + for (i = 0; i < copy.length - 1; i++) + { + var p1 = copy[i]; + var p2 = copy[i + 1]; + var sqd = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); + if (sqd > fixedTolerance) + { + p1.marked = true; + p2.marked = true; + } + } + + var simple = Simplify.simplify(copy, tolerance, true); + // now a polygon again + //simple.pop(); + simple.Points = simple.Points.Take(simple.Points.Count() - 1).ToArray(); + + // could be dirty again (self intersections and/or coincident points) + simple = cleanPolygon2(simple); + + // simplification process reduced poly to a line or point + if (simple == null) + { + simple = polygon; + } + + var offsets = polygonOffsetDeepNest(simple, inside ? -tolerance : tolerance); + + NFP offset = null; + double offsetArea = 0; + List holes = new List(); + for (i = 0; i < offsets.Length; i++) + { + var area = GeometryUtil.polygonArea(offsets[i]); + if (offset == null || area < offsetArea) + { + offset = offsets[i]; + offsetArea = area; + } + if (area > 0) + { + holes.Add(offsets[i]); + } + } + + // mark any points that are exact + for (i = 0; i < simple.length; i++) + { + var seg = new NFP(); + seg.AddPoint(simple[i]); + seg.AddPoint(simple[i + 1 == simple.length ? 0 : i + 1]); + + var index1 = find(seg[0], polygon); + var index2 = find(seg[1], polygon); + + if (index1 + 1 == index2 || index2 + 1 == index1 || (index1 == 0 && index2 == polygon.length - 1) || (index2 == 0 && index1 == polygon.length - 1)) + { + seg[0].exact = true; + seg[1].exact = true; + } + } + var numshells = 4; + NFP[] shells = new NFP[numshells]; + + for (j = 1; j < numshells; j++) + { + var delta = j * (tolerance / numshells); + delta = inside ? -delta : delta; + var shell = polygonOffsetDeepNest(simple, delta); + if (shell.Count() > 0) + { + shells[j] = shell.First(); + } + else + { + //shells[j] = shell; + } + } + + if (offset == null) + { + return polygon; + } + // selective reversal of offset + for (i = 0; i < offset.length; i++) + { + var o = offset[i]; + var target = getTarget(o, simple, 2 * tolerance); + + // reverse point offset and try to find exterior points + var test = clone(offset); + test.Points[i] = new SvgPoint(target.x, target.y); + + if (!exterior(test, polygon, inside)) + { + o.x = target.x; + o.y = target.y; + } + else + { + // a shell is an intermediate offset between simple and offset + for (j = 1; j < numshells; j++) + { + if (shells[j] != null) + { + var shell = shells[j]; + var delta = j * (tolerance / numshells); + target = getTarget(o, shell, 2 * delta); + test = clone(offset); + test.Points[i] = new SvgPoint(target.x, target.y); + if (!exterior(test, polygon, inside)) + { + o.x = target.x; + o.y = target.y; + break; + } + } + } + } + } + + // straighten long lines + // a rounded rectangle would still have issues at this point, as the long sides won't line up straight + + var straightened = false; + + for (i = 0; i < offset.length; i++) + { + var p1 = offset[i]; + var p2 = offset[i + 1 == offset.length ? 0 : i + 1]; + + var sqd = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); + + if (sqd < fixedTolerance) + { + continue; + } + for (j = 0; j < simple.length; j++) + { + var s1 = simple[j]; + var s2 = simple[j + 1 == simple.length ? 0 : j + 1]; + + var sqds = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); + + if (sqds < fixedTolerance) + { + continue; + } + + if ((GeometryUtil._almostEqual(s1.x, s2.x) || GeometryUtil._almostEqual(s1.y, s2.y)) && // we only really care about vertical and horizontal lines + GeometryUtil._withinDistance(p1, s1, 2 * tolerance) && + GeometryUtil._withinDistance(p2, s2, 2 * tolerance) && + (!GeometryUtil._withinDistance(p1, s1, config.curveTolerance / 1000) || + !GeometryUtil._withinDistance(p2, s2, config.curveTolerance / 1000))) + { + p1.x = s1.x; + p1.y = s1.y; + p2.x = s2.x; + p2.y = s2.y; + straightened = true; + } + } + } + + //if (straightened) + { + + var Ac = _Clipper.ScaleUpPaths(offset, 10000000); + var Bc = _Clipper.ScaleUpPaths(polygon, 10000000); + + var combined = new List>(); + var clipper = new ClipperLib.Clipper(); + + clipper.AddPath(Ac.ToList(), ClipperLib.PolyType.ptSubject, true); + clipper.AddPath(Bc.ToList(), ClipperLib.PolyType.ptSubject, true); + + // the line straightening may have made the offset smaller than the simplified + if (clipper.Execute(ClipperLib.ClipType.ctUnion, combined, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)) + { + double? largestArea = null; + for (i = 0; i < combined.Count; i++) + { + var n = Background.toNestCoordinates(combined[i].ToArray(), 10000000); + var sarea = -GeometryUtil.polygonArea(n); + if (largestArea == null || largestArea < sarea) + { + offset = n; + largestArea = sarea; + } + } + } + } + + cleaned = cleanPolygon2(offset); + if (cleaned != null && cleaned.length > 1) + { + offset = cleaned; + } + + #region experimental + if (config.clipByHull) + { + offset = ClipSubject(offset, hull, config.clipperScale); + } + else if (config.clipByRects) + { + NFP rect1 = boundingBox(hull); + offset = ClipSubject(offset, rect1, config.clipperScale); + var mbox = GetMinimumBox(hull); + offset = ClipSubject(offset, mbox, config.clipperScale); + } + #endregion + + // mark any points that are exact (for line merge detection) + for (i = 0; i < offset.length; i++) + { + var seg = new SvgPoint[] { offset[i], offset[i + 1 == offset.length ? 0 : i + 1] }; + var index1 = find(seg[0], polygon); + var index2 = find(seg[1], polygon); + if (index1 == null) + { + index1 = 0; + } + if (index2 == null) + { + index2 = 0; + } + if (index1 + 1 == index2 || index2 + 1 == index1 + || (index1 == 0 && index2 == polygon.length - 1) || + (index2 == 0 && index1 == polygon.length - 1)) + { + seg[0].exact = true; + seg[1].exact = true; + } + } + + if (!inside && holes != null && holes.Count > 0) + { + offset.children = holes; + } + + return offset; + + } + public static int? find(SvgPoint v, NFP p) + { + for (var i = 0; i < p.length; i++) + { + if (GeometryUtil._withinDistance(v, p[i], Config.curveTolerance / 1000)) + { + return i; + } + } + return null; + } + // offset tree recursively + public static void offsetTree(NFP t, double offset, SvgNestConfig config, bool? inside = null) + { + var simple = simplifyFunction(t, (inside == null) ? false : inside.Value); + var offsetpaths = new NFP[] { simple }; + if (Math.Abs(offset) > 0) + { + offsetpaths = polygonOffsetDeepNest(simple, offset); + } + + if (offsetpaths.Count() > 0) + { + + List rett = new List(); + rett.AddRange(offsetpaths[0].Points); + rett.AddRange(t.Points.Skip(t.length)); + t.Points = rett.ToArray(); + + // replace array items in place + + //Array.prototype.splice.apply(t, [0, t.length].concat(offsetpaths[0])); + } + + if (t != simple && simple.children != null && simple.children.Count > 0) + { + if (t.children == null) + { + t.children = new List(); + } + + for (var i = 0; i < simple.children.Count; i++) + { + t.children.Add(simple.children[i]); + } + } + + if (t.children != null && t.children.Count > 0) + { + for (var i = 0; i < t.children.Count; i++) + { + + offsetTree(t.children[i], -offset, config, (inside == null) ? true : (!inside)); + } + } + } + + + // use the clipper library to return an offset to the given polygon. Positive offset expands the polygon, negative contracts + // note that this returns an array of polygons + public static NFP[] polygonOffsetDeepNest(NFP polygon, double offset) + { + + if (offset == 0 || GeometryUtil._almostEqual(offset, 0)) + { + return new[] { polygon }; + } + + var p = svgToClipper(polygon).ToList(); + + var miterLimit = 4; + var co = new ClipperLib.ClipperOffset(miterLimit, Config.curveTolerance * Config.clipperScale); + co.AddPath(p.ToList(), ClipperLib.JoinType.jtMiter, ClipperLib.EndType.etClosedPolygon); + + var newpaths = new List>(); + co.Execute(ref newpaths, offset * Config.clipperScale); + + + var result = new List(); + for (var i = 0; i < newpaths.Count; i++) + { + result.Add(clipperToSvg(newpaths[i])); + } + + + return result.ToArray(); + } + + + + // converts a polygon from normal float coordinates to integer coordinates used by clipper, as well as x/y -> X/Y + public static IntPoint[] svgToClipper2(NFP polygon, double? scale = null) + { + + + var d = _Clipper.ScaleUpPaths(polygon, scale == null ? Config.clipperScale : scale.Value); + return d.ToArray(); + + } + + // converts a polygon from normal float coordinates to integer coordinates used by clipper, as well as x/y -> X/Y + public static ClipperLib.IntPoint[] svgToClipper(NFP polygon) + { + + + + var d = _Clipper.ScaleUpPaths(polygon, Config.clipperScale); + return d.ToArray(); + + return polygon.Points.Select(z => new IntPoint((long)z.x, (long)z.y)).ToArray(); + } + // returns a less complex polygon that satisfies the curve tolerance + public static NFP cleanPolygon(NFP polygon) + { + var p = svgToClipper2(polygon); + // remove self-intersections and find the biggest polygon that's left + var simple = ClipperLib.Clipper.SimplifyPolygon(p.ToList(), ClipperLib.PolyFillType.pftNonZero); + + if (simple == null || simple.Count == 0) + { + return null; + } + + var biggest = simple[0]; + var biggestarea = Math.Abs(ClipperLib.Clipper.Area(biggest)); + for (var i = 1; i < simple.Count; i++) + { + var area = Math.Abs(ClipperLib.Clipper.Area(simple[i])); + if (area > biggestarea) + { + biggest = simple[i]; + biggestarea = area; + } + } + + // clean up singularities, coincident points and edges + var clean = ClipperLib.Clipper.CleanPolygon(biggest, 0.01 * + Config.curveTolerance * Config.clipperScale); + + if (clean == null || clean.Count == 0) + { + return null; + } + return clipperToSvg(clean); + + } + + public static NFP cleanPolygon2(NFP polygon) + { + var p = svgToClipper(polygon); + // remove self-intersections and find the biggest polygon that's left + var simple = ClipperLib.Clipper.SimplifyPolygon(p.ToList(), ClipperLib.PolyFillType.pftNonZero); + + if (simple == null || simple.Count == 0) + { + return null; + } + + var biggest = simple[0]; + var biggestarea = Math.Abs(ClipperLib.Clipper.Area(biggest)); + for (var i = 1; i < simple.Count; i++) + { + var area = Math.Abs(ClipperLib.Clipper.Area(simple[i])); + if (area > biggestarea) + { + biggest = simple[i]; + biggestarea = area; + } + } + + // clean up singularities, coincident points and edges + var clean = ClipperLib.Clipper.CleanPolygon(biggest, 0.01 * + Config.curveTolerance * Config.clipperScale); + + if (clean == null || clean.Count == 0) + { + return null; + } + var cleaned = clipperToSvg(clean); + + // remove duplicate endpoints + var start = cleaned[0]; + var end = cleaned[cleaned.length - 1]; + if (start == end || (GeometryUtil._almostEqual(start.x, end.x) + && GeometryUtil._almostEqual(start.y, end.y))) + { + cleaned.Points = cleaned.Points.Take(cleaned.Points.Count() - 1).ToArray(); + } + + return cleaned; + + } + + public static NFP clipperToSvg(IList polygon) + { + List ret = new List(); + + for (var i = 0; i < polygon.Count; i++) + { + ret.Add(new SvgPoint(polygon[i].X / Config.clipperScale, polygon[i].Y / Config.clipperScale)); + } + + return new NFP() { Points = ret.ToArray() }; + } + + + public int toTree(PolygonTreeItem[] list, int idstart = 0) + { + List parents = new List(); + int i, j; + + // assign a unique id to each leaf + //var id = idstart || 0; + var id = idstart; + + for (i = 0; i < list.Length; i++) + { + var p = list[i]; + + var ischild = false; + for (j = 0; j < list.Length; j++) + { + if (j == i) + { + continue; + } + if (GeometryUtil.pointInPolygon(p.Polygon.Points[0], list[j].Polygon).Value) + { + if (list[j].Childs == null) + { + list[j].Childs = new List(); + } + list[j].Childs.Add(p); + p.Parent = list[j]; + ischild = true; + break; + } + } + + if (!ischild) + { + parents.Add(p); + } + } + + for (i = 0; i < list.Length; i++) + { + if (parents.IndexOf(list[i]) < 0) + { + list = list.Skip(i).Take(1).ToArray(); + i--; + } + } + + for (i = 0; i < parents.Count; i++) + { + parents[i].Polygon.Id = id; + id++; + } + + for (i = 0; i < parents.Count; i++) + { + if (parents[i].Childs != null) + { + id = toTree(parents[i].Childs.ToArray(), id); + } + } + + return id; + } + + public static NFP cloneTree(NFP tree) + { + NFP newtree = new NFP(); + foreach (var t in tree.Points) + { + newtree.AddPoint(new SvgPoint(t.x, t.y) { exact = t.exact }); + } + + + if (tree.children != null && tree.children.Count > 0) + { + newtree.children = new List(); + foreach (var c in tree.children) + { + newtree.children.Add(cloneTree(c)); + } + + } + + return newtree; + } + + + public Background background = new Background(); + + + PopulationItem individual = null; + NFP[] placelist; + GeneticAlgorithm ga; + + public List nests = new List(); + + public void ResponseProcessor(SheetPlacement payload) + { + //console.log('ipc response', payload); + if (ga == null) + { + // user might have quit while we're away + return; + } + ga.population[payload.index].processing = null; + ga.population[payload.index].fitness = payload.fitness; + + // render placement + if (this.nests.Count == 0 || this.nests[0].fitness > payload.fitness) + { + this.nests.Insert(0, payload); + + if (this.nests.Count > Config.populationSize) + { + this.nests.RemoveAt(nests.Count - 1); + } + //if (displayCallback) + { + // displayCallback(); + } + } + } + + public void launchWorkers(NestItem[] parts) + { + background.ResponseAction = ResponseProcessor; + if (ga == null) + { + List adam = new List(); + var id = 0; + for (int i = 0; i < parts.Count(); i++) + { + if (!parts[i].IsSheet) + { + for (int j = 0; j < parts[i].Quanity; j++) + { + var poly = cloneTree(parts[i].Polygon); // deep copy + poly.id = id; // id is the unique id of all parts that will be nested, including cloned duplicates + poly.source = i; // source is the id of each unique part from the main part list + + adam.Add(poly); + id++; + } + } + } + + adam = adam.OrderByDescending(z => Math.Abs(GeometryUtil.polygonArea(z))).ToList(); + /*List shuffle = new List(); + Random r = new Random(DateTime.Now.Millisecond); + while (adam.Any()) + { + var rr = r.Next(adam.Count); + shuffle.Add(adam[rr]); + adam.RemoveAt(rr); + } + adam = shuffle;*/ + + /*#region special case + var temp = adam[1]; + adam.RemoveAt(1); + adam.Insert(9, temp); + + #endregion*/ + ga = new GeneticAlgorithm(adam.ToArray(), Config); + } + individual = null; + + // check if current generation is finished + var finished = true; + for (int i = 0; i < ga.population.Count; i++) + { + if (ga.population[i].fitness == null) + { + finished = false; + break; + } + } + if (finished) + { + //console.log('new generation!'); + // all individuals have been evaluated, start next generation + ga.generation(); + } + + var running = ga.population.Where((p) => + { + return p.processing != null; + }).Count(); + + List sheets = new List(); + List sheetids = new List(); + List sheetsources = new List(); + List> sheetchildren = new List>(); + var sid = 0; + for (int i = 0; i < parts.Count(); i++) + { + if (parts[i].IsSheet) + { + var poly = parts[i].Polygon; + for (int j = 0; j < parts[i].Quanity; j++) + { + var cln = cloneTree(poly); + cln.id = sid; // id is the unique id of all parts that will be nested, including cloned duplicates + cln.source = poly.source; // source is the id of each unique part from the main part list + + sheets.Add(cln); + sheetids.Add(sid); + sheetsources.Add(i); + sheetchildren.Add(poly.children); + sid++; + } + } + } + for (int i = 0; i < ga.population.Count; i++) + { + //if(running < config.threads && !GA.population[i].processing && !GA.population[i].fitness){ + // only one background window now... + if (running < 1 && ga.population[i].processing == null && ga.population[i].fitness == null) + { + ga.population[i].processing = true; + + // hash values on arrays don't make it across ipc, store them in an array and reassemble on the other side.... + List ids = new List(); + List sources = new List(); + List> children = new List>(); + + for (int j = 0; j < ga.population[i].placements.Count; j++) + { + var id = ga.population[i].placements[j].id; + var source = ga.population[i].placements[j].source; + var child = ga.population[i].placements[j].children; + //ids[j] = id; + ids.Add(id); + //sources[j] = source; + sources.Add(source.Value); + //children[j] = child; + children.Add(child); + } + + DataInfo data = new DataInfo() + { + index = i, + sheets = sheets, + sheetids = sheetids.ToArray(), + sheetsources = sheetsources.ToArray(), + sheetchildren = sheetchildren, + individual = ga.population[i], + config = Config, + ids = ids.ToArray(), + sources = sources.ToArray(), + children = children + + }; + + background.BackgroundStart(data); + //ipcRenderer.send('background-start', { index: i, sheets: sheets, sheetids: sheetids, sheetsources: sheetsources, sheetchildren: sheetchildren, individual: GA.population[i], config: config, ids: ids, sources: sources, children: children}); + running++; + } + } + } + + public PolygonTreeItem[] tree; + + + public bool useHoles; + public bool searchEdges; + } + + public class DataInfo + { + + public int index; + public List sheets; + public int[] sheetids; + public int[] sheetsources; + public List> sheetchildren; + public PopulationItem individual; + public SvgNestConfig config; + public int[] ids; + public int[] sources; + public List> children; + //ipcRenderer.send('background-start', { index: i, sheets: sheets, sheetids: sheetids, sheetsources: sheetsources, sheetchildren: sheetchildren, + //individual: GA.population[i], config: config, ids: ids, sources: sources, children: children}); + } + + public class PolygonTreeItem + { + public NFP Polygon; + public PolygonTreeItem Parent; + public List Childs = new List(); + } + + public class DbCacheKey + { + public int? A; + public int? B; + public float ARotation; + public float BRotation; + public NFP[] nfp; + public int Type; + } + + public class NfpPair + { + public NFP A; + public NFP B; + public NfpKey Key; + public NFP nfp; + + public float ARotation; + public float BRotation; + + public int Asource { get; internal set; } + public int Bsource { get; internal set; } + + public static NfpPair Create(NFP A, NFP B) + { + return new NfpPair() + { + A = A, + B = B, + ARotation = A.rotation, + BRotation = B.rotation, + Asource = A.source.Value, + Bsource = B.source.Value + + }; + } + } + + public class NonameReturn + { + public NfpKey key; + public NFP[] nfp; + public NFP[] value + { + get + { + return nfp; + } + } + public NonameReturn(NfpKey key, NFP[] nfp) + { + this.key = key; + this.nfp = nfp; + } + } + + public class NestItem + { + public NFP Polygon; + public int Quanity; + public bool IsSheet; + } +} \ No newline at end of file diff --git a/PCUT/PCUT/DeepNestApi/SvgParser.cs b/PCUT/PCUT/DeepNestApi/SvgParser.cs new file mode 100644 index 0000000..0a79e12 --- /dev/null +++ b/PCUT/PCUT/DeepNestApi/SvgParser.cs @@ -0,0 +1,558 @@ +using DeepNestLib; +using SvgPathProperties; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using PCUT.Extensions; +using Windows.Foundation; +using static PCUT.Extensions.PointExtensions; +using static PCUT.Extensions.XmlExtensions; +using PCUT.Extensions.Transforms; +using PCUT.ViewModels; + +namespace PCUT.DeepNestApi +{ + public class SvgParser + { + public static (RawDetail[] Details, double Scale) LoadSvg(XDocument doc, bool split = false) + { + RawDetail s = new RawDetail(); + s.Name = "temp"; + //List paths = new List(); + var ns = doc.Descendants().First().Name.Namespace.NamespaceName; + double scale = 1; + double rightMax = 0; + List ret = new List(); + foreach (var item in doc.Descendants().Where(z => z.Name.LocalName == "path")) + { + var id = item.GetId(); + + var dd = item.Attribute("d").Value; + SvgPath p = new SvgPath(dd, unarc: true); + var bbox = p.GetBBox(); + rightMax = Math.Max(bbox.Right, rightMax); + List pp = new List(); + List cntrs2 = new List(); + foreach (var ss in p.Segments) + { + var len = ss.Length; + if (len > 0 || ss is MoveCommand) + { + try + { + for (double t = 0; t <= 1.0; t += 0.0625) + { + var p1 = ss.GetPointAtLength(t * len); + pp.Add(new SvgPoint(p1.X, p1.Y)); + } + } + catch (Exception ex) + { + + } + } + if (ss is LineCommand lc && lc.ClosePath) + { + cntrs2.Add(new LocalContour() { Points = pp.Select(z => new Point((float)z.x, (float)z.y)).ToList() }); + pp.Clear(); + } + } + // var top = nfps.SingleOrDefault(z => z.Parent == null); + //if (top != null) + { + // var inners = nfps.Where(z => z.Parent != null); + //s.Outers.Add(new LocalContour() { Points = top.Points.ToList() }); + s.Outers.AddRange(cntrs2.Select(z => new LocalContour() { Points = z.Points.ToList(), Tag = new[] { id } })); + } + } + + foreach (var item in doc.Descendants("rect")) + { + var id = item.GetId(); + if (id == null && item.Parent != null) + id = item.Parent.GetId(); + if (id.StartsWith("PCUT_BOUNDING")) + continue; + + float xx = 0; + float yy = 0; + if (item.Attribute("x") != null) + { + xx = float.Parse(item.Attribute("x").Value); + } + if (item.Attribute("y") != null) + { + yy = float.Parse(item.Attribute("y").Value); + } + var ww = float.Parse(item.Attribute("width").Value); + var hh = float.Parse(item.Attribute("height").Value); + //GraphicsPath p = new GraphicsPath(); + //p.AddRectangle(new RectangleF(xx, yy, ww, hh)); + var rect = new Rect(xx, yy, ww, hh); + + //s.Outers.Add(new LocalContour() { Points = p.PathPoints.ToList() }); + s.Outers.Add(new LocalContour() { Points = rect.PathPoints().ToList(), Tag = new[] { id } }); + rightMax = Math.Max(rightMax, xx + ww); + } + + foreach (var item in doc.Descendants(XName.Get("polygon", ns))) + { + var id = item.GetId(); + if (id == null && item.Parent != null) + id = item.Parent.GetId(); + + var str = item.Attribute("points").Value.ToString(); + var spl = str.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); + List points = new List(); + foreach (var sitem in spl) + { + var spl2 = sitem.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + var ar = spl2.Select(z => float.Parse(z, CultureInfo.InvariantCulture)).ToArray(); + points.Add(new Point(ar[0], ar[1])); + rightMax = Math.Max(rightMax, ar[0]); + } + s.Outers.Add(new LocalContour() { Points = points.ToList(), Tag = new[] { id } }); + } + + if (split) + { + //split + //var nfps = s.Outers; + //for (int i = 0; i < nfps.Count; i++) + //{ + // for (int j = 0; j < nfps.Count; j++) + // { + // if (i != j) + // { + // var d2 = nfps[i]; + // var d3 = nfps[j]; + // var f0 = d3.Points[0]; + + // if (GeometryUtil.pnpoly(d2.Points.Select(p => (p.X, p.Y)).ToArray(), f0.X, f0.Y)) + // { + // d3.Parent = d2; + // if (!d2.Childrens.Contains(d3)) + // { + // d2.Childrens.Add(d3); + // } + // } + // } + // } + //} + //var tops = nfps.Where(z => z.Parent == null).ToArray(); + + // split + var nfps = s.Outers; + + // build a forest of local contour trees + var tops = new List(); + for (int i = 0; i < nfps.Count; i++) + { + var nfp = nfps[i]; + foreach (var root in tops) + { + var root0 = root.Points[0]; + if (IsContains(nfp, root)) // nfp is root parent + { + root.Parent = nfp; + nfp.Childrens.Add(root); + } + else if (IsContains(root, nfp)) + { + RecursiveInsert(root, nfp); + break; + } + } + if (nfp.Parent == null) + { + foreach (var child in nfp.Childrens) + tops.Remove(child); + tops.Add(nfp); + } + } + + // move all even level children to roots + for (int i = 0; i < tops.Count; i++) + { + var root = tops[i]; + foreach (var child in root.Childrens) + { + foreach (var grandChild in child.Childrens) + { + grandChild.Parent = null; + tops.Add(grandChild); + } + child.Childrens.Clear(); + } + } + + var names = new HashSet(); + for (int i = 0; i < tops.Count; i++) + { + LocalContour item = tops[i]; + RawDetail rr = new RawDetail(); + var name = $"{(item.Tag as object[])[0]}"; + if (names.Contains(name)) + name = $"{name}_{Guid.NewGuid()}"; + names.Add(name); + rr.Name = name; + rr.Outers.Add(item); + rr.Holes.AddRange(item.Childrens); + ret.Add(rr); + } + } + else + { + ret.Add(s); + } + + //scale /= rightMax; + foreach (var item in ret) + { + item.Scale(scale); + } + + return (ret.ToArray(), scale); + } + + // return true if a contains b + private static bool IsContains(LocalContour a, LocalContour b) + { + var b0 = b.Points[0]; + return GeometryUtil.pnpoly(a.Points.Select(p => (p.X, p.Y)).ToArray(), b0.X, b0.Y); + } + + // assuming root contains b + private static void RecursiveInsert(LocalContour root, LocalContour b) + { + LocalContour parent = root; + var queue = new Queue(); + foreach (var child in parent.Childrens) + { + queue.Enqueue(child); + } + + while (queue.Count > 0) + { + var item = queue.Dequeue(); + if (IsContains(b, item)) + { + parent.Childrens.Remove(item); + item.Parent = b; + b.Childrens.Add(item); + } + else if (IsContains(item, b)) + { + parent = item; + queue.Clear(); + foreach (var child in parent.Childrens) + { + queue.Enqueue(child); + } + } + } + b.Parent = parent; + parent.Childrens.Add(b); + } + + public static SvgPathProperties.Base.Rect GetBBox(XDocument doc) + { + double left = double.PositiveInfinity, top = double.PositiveInfinity, right = double.NegativeInfinity, bottom = double.NegativeInfinity; + foreach (var item in doc.Descendants().Where(z => z.Name.LocalName == "path")) + { + var dd = item.Attribute("d").Value; + SvgPath p = new SvgPath(dd, unarc: true); + var bbox = p.GetBBox(); + left = Math.Min(left, bbox.Left); + top = Math.Min(top, bbox.Top); + right = Math.Max(right, bbox.Right); + bottom = Math.Max(bottom, bbox.Bottom); + } + return new SvgPathProperties.Base.Rect(left, top, right, bottom); + } + + //public static void Export(XDocument doc, IEnumerable polygons, double scale) + //{ + // var builder = new StringBuilder(); + // foreach (var polygon in polygons) + // { + // builder.Clear(); + // builder.Append("translate(").Append(polygon.x * scale).Append(" ").Append(polygon.y * scale).Append(") ") + // .Append("rotate(").Append(polygon.rotation).Append(")"); + // foreach (var tag in polygon.Tag as object[]) + // { + // doc.ApplyTransform(tag as string, builder.ToString()); + // } + // } + //} + + public static (XDocument Data, double Width, double Height) Export(IEnumerable polygons) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(""); + var width = 0d; + var height = 0d; + foreach (var item in polygons.OrderByDescending(x => x.WidthCalculated * x.HeightCalculated)) + { + //if (!sheets.Contains(item)) + //{ + // if (!item.fitted) continue; + //} + //var m = new Matrix(); + //m.Translate((float)item.x, (float)item.y); + //m.Rotate(item.rotation); + SvgTransform m = new TranslateTransform(item.x, item.y); + var rotate = new RotateTransform(item.rotation); + rotate.SetNext(m); + m = rotate; + + var points = item.Points.Select(z => { var (x, y) = m.Transform(z.x, z.y); return new SvgPoint(x, y); }).ToArray(); + width = Math.Max(width, points.Max(point => point.x)); + height = Math.Max(height, points.Max(point => point.y)); + + string fill = "lightblue"; + //if (sheets.Contains(item)) + //{ + // fill = "none"; + //} + + sb.Append($""); + + if (item.children != null && item.children.Any()) + { + foreach (var citem in item.children) + { + sb.Append($" { var (x, y) = m.Transform(z.x, z.y); return new SvgPoint(x, y); }).Reverse().ToArray(); + + for (int i = 0; i < points.Count(); i++) + { + var p = points[i]; + string coord = p.x.ToString().Replace(",", ".") + " " + p.y.ToString().Replace(",", "."); + if (i == 0) + { + sb.Append("M" + coord + " "); + continue; + } + + sb.Append("L" + coord + " "); + } + sb.AppendLine("z \"/>"); + } + } + } + sb.AppendLine(""); + return (XDocument.Parse(sb.ToString()), width, height); + } + + //public static SvgConfig Conf = new SvgConfig(); + //// return a polygon from the given SVG element in the form of an array of points + //public static NFP polygonify(XElement element) + //{ + // List poly = new List(); + // int i; + + // switch (element.Name.LocalName) + // { + // case "polygon": + // case "polyline": + // { + // var pp = element.Attribute("points").Value; + // var spl = pp.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + // foreach (var item in spl) + // { + // var spl2 = item.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + // var x = float.Parse(spl2[0], CultureInfo.InvariantCulture); + // var y = float.Parse(spl2[1], CultureInfo.InvariantCulture); + // poly.Add(new SvgPoint(x, y)); + // } + + // } + // break; + // case "rect": + // { + // float x = 0; + // float y = 0; + // if (element.Attribute("x") != null) + // { + // x = float.Parse(element.Attribute("x").Value, CultureInfo.InvariantCulture); + // } + // if (element.Attribute("y") != null) + // { + // y = float.Parse(element.Attribute("y").Value, CultureInfo.InvariantCulture); + // } + // var w = float.Parse(element.Attribute("width").Value, CultureInfo.InvariantCulture); + // var h = float.Parse(element.Attribute("height").Value, CultureInfo.InvariantCulture); + // poly.Add(new SvgPoint(x, y)); + // poly.Add(new SvgPoint(x + w, y)); + // poly.Add(new SvgPoint(x + w, y + h)); + // poly.Add(new SvgPoint(x, y + h)); + // } + + + // break; + // case "circle": + // throw new NotImplementedException(); + + // break; + // case "ellipse": + // throw new NotImplementedException(); + + // break; + // case "path": + // throw new NotImplementedException(); + + // // // we'll assume that splitpath has already been run on this path, and it only has one M/m command + // // var seglist = element.pathSegList; + + // // var firstCommand = seglist.getItem(0); + // // var lastCommand = seglist.getItem(seglist.numberOfItems - 1); + + // // var x = 0, y = 0, x0 = 0, y0 = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0, prevx = 0, prevy = 0, prevx1 = 0, prevy1 = 0, prevx2 = 0, prevy2 = 0; + + // // for (var i = 0; i < seglist.numberOfItems; i++) + // // { + // // var s = seglist.getItem(i); + // // var command = s.pathSegTypeAsLetter; + + // // prevx = x; + // // prevy = y; + + // // prevx1 = x1; + // // prevy1 = y1; + + // // prevx2 = x2; + // // prevy2 = y2; + + // // if (/[MLHVCSQTA] /.test(command)) + // // { + // // if ('x1' in s) x1 = s.x1; + // // if ('x2' in s) x2 = s.x2; + // // if ('y1' in s) y1 = s.y1; + // // if ('y2' in s) y2 = s.y2; + // // if ('x' in s) x = s.x; + // // if ('y' in s) y = s.y; + // // } + // // else{ + // // if ('x1' in s) x1=x+s.x1; + // // if ('x2' in s) x2=x+s.x2; + // // if ('y1' in s) y1=y+s.y1; + // // if ('y2' in s) y2=y+s.y2; + // // if ('x' in s) x+=s.x; + // // if ('y' in s) y+=s.y; + // // } + // // switch(command){ + // // // linear line types + // // case 'm': + // // case 'M': + // // case 'l': + // // case 'L': + // // case 'h': + // // case 'H': + // // case 'v': + // // case 'V': + // // var point = { }; + // // point.x = x; + // // point.y = y; + // // poly.push(point); + // // break; + // // // Quadratic Beziers + // // case 't': + // // case 'T': + // // // implicit control point + // // if(i > 0 && /[QqTt]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){ + // // x1 = prevx + (prevx-prevx1); + // // y1 = prevy + (prevy-prevy1); + // // } + // // else{ + // // x1 = prevx; + // // y1 = prevy; + // // } + // // case 'q': + // // case 'Q': + // // var pointlist = GeometryUtil.QuadraticBezier.linearize({x: prevx, y: prevy}, {x: x, y: y}, {x: x1, y: y1}, this.conf.tolerance); + // //pointlist.shift(); // firstpoint would already be in the poly + // // for(var j=0; j 0 && /[CcSs]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){ + // // x1 = prevx + (prevx - prevx2); + // // y1 = prevy + (prevy - prevy2); + // //} + // // else{ + // // x1 = prevx; + // // y1 = prevy; + // //} + // // case 'c': + // // case 'C': + // // var pointlist = GeometryUtil.CubicBezier.linearize({ x: prevx, y: prevy}, { x: x, y: y}, { x: x1, y: y1}, { x: x2, y: y2}, this.conf.tolerance); + // //pointlist.shift(); // firstpoint would already be in the poly + // // for(var j=0; j 0 && GeometryUtil._almostEqual(poly[0].x, poly[poly.Count - 1].x, Conf.toleranceSvg) + // && GeometryUtil._almostEqual(poly[0].y, poly[poly.Count - 1].y, Conf.toleranceSvg)) + // { + // poly.RemoveAt(0); + // } + + // return new NFP() { Points = poly.ToArray() }; + //} + } + + public class SvgConfig + { + public float tolerance = 2f; // max bound for bezier->line segment conversion, in native SVG units + public float toleranceSvg = 0.005f;// fudge factor for browser inaccuracy in SVG unit handling + } +} diff --git a/PCUT/PCUT/Extensions/BindExtensions.cs b/PCUT/PCUT/Extensions/BindExtensions.cs new file mode 100644 index 0000000..085e605 --- /dev/null +++ b/PCUT/PCUT/Extensions/BindExtensions.cs @@ -0,0 +1,35 @@ +namespace PCUT.Extensions +{ + public static class BindExtensions + { + public static bool And(bool condition1, bool condition2) + { + return condition1 && condition2; + } + + public static bool Or(bool condition1, bool condition2) + { + return condition1 || condition2; + } + + public static bool NotAnd(bool condition1, bool condition2) + { + return !(condition1 && condition2); + } + + public static bool NotOr(bool condition1, bool condition2) + { + return !(condition1 || condition2); + } + + public static bool AlternateAnd(bool condition1, bool condition2) + { + return condition1 && !condition2; + } + + public static bool AlternateOr(bool condition1, bool condition2) + { + return condition1 || !condition2; + } + } +} diff --git a/PCUT/PCUT/Extensions/Converters/CircleElementConverter.cs b/PCUT/PCUT/Extensions/Converters/CircleElementConverter.cs new file mode 100644 index 0000000..e91f51c --- /dev/null +++ b/PCUT/PCUT/Extensions/Converters/CircleElementConverter.cs @@ -0,0 +1,35 @@ +using SvgPathProperties; +using System.Collections.Generic; +using System.Text; +using System.Xml.Linq; + +namespace PCUT.Extensions.Converters +{ + internal class CircleElementConverter : PathConverter + { + private static readonly string[] CircleExcludedAttributes = new[] { "transform", "cx", "cy", "r" }; + protected override IEnumerable ExcludedAttributes => CircleExcludedAttributes; + + internal CircleElementConverter() + { + } + + protected override bool CanApply(XElement element) => element.Name.LocalName == "circle"; + + protected override SvgPath GetPathFromElement(XElement element) + { + var cx = double.Parse(element.Attribute("cx")?.Value ?? "0"); + var cy = double.Parse(element.Attribute("cy")?.Value ?? "0"); + var r = double.Parse(element.Attribute("r").Value); + var arcCommonParam = $"{r} {r} 0 0 1 "; + var builder = new StringBuilder(); + builder.Append("M ").Append($"{cx + r} {cy} ") + .Append("A ").Append($"{arcCommonParam}").Append($"{cx} {cy + r} ") + .Append($"{arcCommonParam}").Append($"{cx - r} {cy} ") + .Append($"{arcCommonParam}").Append($"{cx} {cy - r} ") + .Append($"{arcCommonParam}").Append($"{cx + r} {cy} ") + .Append("Z"); + return new SvgPath(builder.ToString(), unarc: true); + } + } +} diff --git a/PCUT/PCUT/Extensions/Converters/EllipseElementConverter.cs b/PCUT/PCUT/Extensions/Converters/EllipseElementConverter.cs new file mode 100644 index 0000000..a7a3b36 --- /dev/null +++ b/PCUT/PCUT/Extensions/Converters/EllipseElementConverter.cs @@ -0,0 +1,36 @@ +using SvgPathProperties; +using System.Collections.Generic; +using System.Text; +using System.Xml.Linq; + +namespace PCUT.Extensions.Converters +{ + internal class EllipseElementConverter : PathConverter + { + private static readonly string[] EllipseExcludedAttributes = new[] { "transform", "cx", "cy", "rx", "ry" }; + protected override IEnumerable ExcludedAttributes => EllipseExcludedAttributes; + + internal EllipseElementConverter() + { + } + + protected override bool CanApply(XElement element) => element.Name.LocalName == "ellipse"; + + protected override SvgPath GetPathFromElement(XElement element) + { + var cx = double.Parse(element.Attribute("cx")?.Value ?? "0"); + var cy = double.Parse(element.Attribute("cy")?.Value ?? "0"); + var rx = double.Parse(element.Attribute("rx").Value); + var ry = double.Parse(element.Attribute("ry").Value); + var arcCommonParam = $"{rx} {ry} 0 0 1 "; + var builder = new StringBuilder(); + builder.Append("M ").Append($"{cx + rx} {cy} ") + .Append("A ").Append($"{arcCommonParam}").Append($"{cx} {cy + ry} ") + .Append($"{arcCommonParam}").Append($"{cx - rx} {cy} ") + .Append($"{arcCommonParam}").Append($"{cx} {cy - ry} ") + .Append($"{arcCommonParam}").Append($"{cx + rx} {cy} ") + .Append("Z"); + return new SvgPath(builder.ToString(), unarc: true); + } + } +} diff --git a/PCUT/PCUT/Extensions/Converters/PathConverter.cs b/PCUT/PCUT/Extensions/Converters/PathConverter.cs new file mode 100644 index 0000000..d5c6d1d --- /dev/null +++ b/PCUT/PCUT/Extensions/Converters/PathConverter.cs @@ -0,0 +1,94 @@ +using SvgPathProperties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace PCUT.Extensions.Converters +{ + internal abstract class PathConverter + { + private PathConverter _next; + + private void SetNext(PathConverter next) + { + _next = next; + } + + public SvgPath GetSvgPath(XElement element) + { + if (CanApply(element)) + { + return GetPathFromElement(element); + } + else if (_next != null) + { + return _next.GetSvgPath(element); + } + else + { + return null; + } + } + + protected abstract bool CanApply(XElement element); + protected abstract SvgPath GetPathFromElement(XElement element); + + public void CopyAttribute(XElement source, XElement target) + { + foreach (var attribute in source.Attributes().Where(x => !ExcludedAttributes.Contains(x.Name.LocalName))) + { + target.SetAttributeValue(attribute.Name, attribute.Value); + } + } + protected abstract IEnumerable ExcludedAttributes { get; } + + public static PathConverterBuilder Builder() => new PathConverterBuilder(); + public class PathConverterBuilder + { + private readonly Stack _pathConverters = new Stack(); + + public PathConverterBuilder AddPath() + { + _pathConverters.Push(new PathElementConverter()); + return this; + } + + public PathConverterBuilder AddRect() + { + _pathConverters.Push(new RectElementConverter()); + return this; + } + + public PathConverterBuilder AddPolygon() + { + _pathConverters.Push(new PolygonElementConverter()); + return this; + } + + public PathConverterBuilder AddCircle() + { + _pathConverters.Push(new CircleElementConverter()); + return this; + } + + public PathConverterBuilder AddEllipse() + { + _pathConverters.Push(new EllipseElementConverter()); + return this; + } + + public PathConverter Build() + { + PathConverter current = null; + while (_pathConverters.Count > 0) + { + var converter = _pathConverters.Pop(); + converter.SetNext(current); + current = converter; + } + return current; + } + } + } +} diff --git a/PCUT/PCUT/Extensions/Converters/PathElementConverter.cs b/PCUT/PCUT/Extensions/Converters/PathElementConverter.cs new file mode 100644 index 0000000..0f161bb --- /dev/null +++ b/PCUT/PCUT/Extensions/Converters/PathElementConverter.cs @@ -0,0 +1,23 @@ +using SvgPathProperties; +using System.Collections.Generic; +using System.Xml.Linq; + +namespace PCUT.Extensions.Converters +{ + internal class PathElementConverter : PathConverter + { + private static readonly string[] PathExcludedAttributes = new[] { "transform", "d" }; + protected override IEnumerable ExcludedAttributes => PathExcludedAttributes; + + internal PathElementConverter() + { + } + + protected override bool CanApply(XElement element) => element.Name.LocalName == "path"; + + protected override SvgPath GetPathFromElement(XElement element) + { + return new SvgPath(element.Attribute("d").Value, unarc: true); + } + } +} diff --git a/PCUT/PCUT/Extensions/Converters/PolygonElementConverter.cs b/PCUT/PCUT/Extensions/Converters/PolygonElementConverter.cs new file mode 100644 index 0000000..b6db0e2 --- /dev/null +++ b/PCUT/PCUT/Extensions/Converters/PolygonElementConverter.cs @@ -0,0 +1,52 @@ +using SvgPathProperties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace PCUT.Extensions.Converters +{ + internal class PolygonElementConverter : PathConverter + { + private static readonly string[] PolygonExcludedAttributes = new[] { "transform", "points" }; + protected override IEnumerable ExcludedAttributes => PolygonExcludedAttributes; + + internal PolygonElementConverter() + { + } + + protected override bool CanApply(XElement element) => element.Name.LocalName == "polygon"; + + protected override SvgPath GetPathFromElement(XElement element) + { + var points = GetPolygonPoints(element.Attribute("points")?.Value); + if (!points.Any()) + return null; + + var builder = new StringBuilder(); + foreach (var (x, y) in points) + { + builder.Append(builder.Length == 0 ? "M " : "L ").Append($"{x} {y}"); + } + builder.Append("Z"); + return new SvgPath(builder.ToString(), unarc: true); + } + + private IEnumerable<(double X, double Y)> GetPolygonPoints(string pointsValue) + { + if (pointsValue == null) + yield break; + + var points = pointsValue.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (points.Length % 2 != 0) + yield break; + + for (int i = 0; i < points.Length; i += 2) + { + yield return (double.Parse(points[i]), double.Parse(points[i + 1])); + } + yield break; + } + } +} diff --git a/PCUT/PCUT/Extensions/Converters/RectElementConverter.cs b/PCUT/PCUT/Extensions/Converters/RectElementConverter.cs new file mode 100644 index 0000000..8b4df11 --- /dev/null +++ b/PCUT/PCUT/Extensions/Converters/RectElementConverter.cs @@ -0,0 +1,76 @@ +using SvgPathProperties; +using System.Collections.Generic; +using System.Text; +using System.Xml.Linq; + +namespace PCUT.Extensions.Converters +{ + internal class RectElementConverter : PathConverter + { + private static readonly string[] RectExcludedAttributes = new[] { "transform", "x", "y", "width", "height", "rx", "ry" }; + protected override IEnumerable ExcludedAttributes => RectExcludedAttributes; + + internal RectElementConverter() + { + } + + protected override bool CanApply(XElement element) => element.Name.LocalName == "rect"; + + protected override SvgPath GetPathFromElement(XElement element) + { + var x = double.Parse(element.Attribute("x")?.Value ?? "0"); + var y = double.Parse(element.Attribute("y")?.Value ?? "0"); + var width = double.Parse(element.Attribute("width")?.Value ?? "0"); + var height = double.Parse(element.Attribute("height")?.Value ?? "0"); + var (rx, ry) = CalculateRoundedCorner(element.Attribute("rx")?.Value, element.Attribute("ry")?.Value); + if (width == 0 || height == 0) + return null; + + var hasRoundCorner = rx > 0 && ry > 0; + var arcCommonParam = $"{rx} {ry} 0 0 1 "; + var builder = new StringBuilder(); + builder.Append("M ").Append($"{x + rx} {y} "); + + builder.Append("H ").Append($"{x + width - rx}"); + if (hasRoundCorner) + builder.Append("A ").Append($"{arcCommonParam}").Append($"{x + width} {y + ry} "); + + builder.Append("V ").Append($"{y + height - ry} "); + if (hasRoundCorner) + builder.Append("A ").Append($"{arcCommonParam}").Append($"{x + width - rx} {y + height} "); + + builder.Append("H ").Append($"{x + rx}"); + if (hasRoundCorner) + builder.Append("A ").Append($"{arcCommonParam}").Append($"{x} {y + height - ry} "); + + builder.Append("V ").Append($"{y + ry} "); + if (hasRoundCorner) + builder.Append("A ").Append($"{arcCommonParam}").Append($"{x + rx} {y} "); + + builder.Append("Z"); + return new SvgPath(builder.ToString(), unarc: true); + } + + private (double Rx, double Ry) CalculateRoundedCorner(string rxValue, string ryValue) + { + if (rxValue == null) rxValue = "auto"; + if (ryValue == null) ryValue = "auto"; + + if (rxValue == "auto" && ryValue == "auto") + return (0, 0); + + double? rx = null, ry = null; + if (rxValue != "auto") + rx = double.Parse(rxValue); + if (ryValue != "auto") + ry = double.Parse(ryValue); + + if (rx == null) + rx = ry; + else if (ry == null) + ry = rx; + + return (rx.Value, ry.Value); + } + } +} diff --git a/PCUT/PCUT/Extensions/EnumerableExtensions.cs b/PCUT/PCUT/Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000..8c63ac4 --- /dev/null +++ b/PCUT/PCUT/Extensions/EnumerableExtensions.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions +{ + public static class EnumerableExtension + { + public static void Filter(this ObservableCollection collection, IList data, Predicate filter, int startIndex = 0) + { + int currentIndex = startIndex; + var current = currentIndex < collection.Count ? collection[currentIndex] : default; + int i = startIndex; + while (i < data.Count() && currentIndex < collection.Count) + { + var item = data[i]; + var isValidItem = filter(item); + var isCurrentItem = EqualityComparer.Default.Equals(item, current); + if (isValidItem) + { + currentIndex++; + if (!isCurrentItem) + collection.Insert(currentIndex - 1, item); + else + current = currentIndex < collection.Count ? collection[currentIndex] : default; + } + else if (isCurrentItem) + { + collection.RemoveAt(currentIndex); + current = currentIndex < collection.Count ? collection[currentIndex] : default; + } + i++; + } + while (i < data.Count) + { + var item = data[i]; + if (filter(item)) + collection.Add(item); + i++; + } + } + } +} diff --git a/PCUT/PCUT/Extensions/HpglExtensions.cs b/PCUT/PCUT/Extensions/HpglExtensions.cs new file mode 100644 index 0000000..63ed203 --- /dev/null +++ b/PCUT/PCUT/Extensions/HpglExtensions.cs @@ -0,0 +1,173 @@ +using PCUT.Extensions.Transforms; +using SvgPathProperties; +using SvgPathProperties.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace PCUT.Extensions +{ + public static class HpglExtensions + { + private const double MinimumCurveSegmentLength = 1; // mm + private const double UnitLength = 0.025; // mm + private const int BufferLength = 15000; // byte + + class StartPointComparer : IComparer + { + public static StartPointComparer Instance = new StartPointComparer(); + public int Compare(Point x, Point y) + { + return x.X != y.X ? x.X.CompareTo(y.X) : y.Y.CompareTo(x.Y); + } + } + + public static IEnumerable<(string Path, TimeSpan EstimateTime)> ToHPGL( + this XDocument svg, + (double Width, double Height) size, + (double Width, double Height) sheetSize, + int speed, // mm/s + int pen = 0) + { + var minCurveSegmentLength = MinimumCurveSegmentLength / UnitLength; + + var widthUnit = (int)Math.Ceiling(size.Width / UnitLength); + var heightUnit = (int)Math.Ceiling(size.Height / UnitLength); + + SvgTransform transform = new TranslateTransform(0, (int)Math.Ceiling(sheetSize.Height / UnitLength)); + + var flipTransform = new ScaleTransform(1, -1); + flipTransform.SetNext(transform); + transform = flipTransform; + + var scaleTransform = new ScaleTransform(1 / UnitLength, 1 / UnitLength); + scaleTransform.SetNext(transform); + transform = scaleTransform; + + var paths = new List(); + svg.GetPaths(paths, transform); + + yield return ($"IN;SP{pen};", TimeSpan.FromMilliseconds(200)); + var builder = new StringBuilder(); + TimeSpan timeSpan = TimeSpan.Zero; + var maxX = 0d; + var length = 0d; + foreach (var data in paths.OrderBy(x => x.GetPointAtLength(0), StartPointComparer.Instance)) + { + foreach (var segment in data.Segments) + { + switch (segment) + { + case MoveCommand moveCommand: + { + var lengthX = Math.Abs(maxX - moveCommand.X); + builder.AddHpglMove(moveCommand.X, moveCommand.Y, ref maxX); + length += (lengthX + (sheetSize.Height / UnitLength)) * 0.75; + break; + } + case LineCommand lineCommand: + { + builder.AddHpglLine(lineCommand.ToX, lineCommand.ToY, ref maxX); + length += lineCommand.Length; + break; + } + case BezierCommand bezierCommand: + { + var iterationCount = (int)Math.Ceiling(bezierCommand.Length / minCurveSegmentLength); + for (int i = 1; i < iterationCount; i++) + { + var point = bezierCommand.GetPointAtLength(i * minCurveSegmentLength); + builder.AddHpglLine(point.X, point.Y, ref maxX); + length += minCurveSegmentLength; + if (builder.Length > BufferLength) + { + timeSpan = CalculateWaitTime(length, speed); + yield return (builder.ToString(), timeSpan); + builder.Clear(); + length = 0; + } + } + var end = bezierCommand.IsQuadratic ? bezierCommand.Cp2OrEnd : bezierCommand.End; + builder.AddHpglLine(end.X, end.Y, ref maxX); + length += minCurveSegmentLength; + break; + } + case ArcCommand arcCommand: + { + var iterationCount = (int)Math.Ceiling(arcCommand.Length / minCurveSegmentLength); + for (int i = 1; i < iterationCount; i++) + { + var point = arcCommand.GetPointAtLength(i * minCurveSegmentLength); + builder.AddHpglLine(point.X, point.Y, ref maxX); + length += minCurveSegmentLength; + if (builder.Length > BufferLength) + { + timeSpan = CalculateWaitTime(length, speed); + yield return (builder.ToString(), timeSpan); + builder.Clear(); + length = 0; + } + } + builder.AddHpglLine(arcCommand.ToX, arcCommand.ToY, ref maxX); + length += minCurveSegmentLength; + break; + } + } + if (builder.Length > BufferLength) + { + timeSpan = CalculateWaitTime(length, speed); + yield return (builder.ToString(), timeSpan); + builder.Clear(); + length = 0; + } + } + timeSpan = CalculateWaitTime(length, speed); + yield return (builder.ToString(), timeSpan); + builder.Clear(); + length = 0; + } + yield return ($"PU{maxX + 2000},0;IN;", TimeSpan.Zero); + } + + private static void GetPaths(this XDocument document, List paths, SvgTransform transform = null) + { + foreach (var component in document.Root.Descendants().Where(z => z.Name.LocalName == "path")) + { + if (!component.IsHidden()) + { + var path = transform.Apply(new SvgPath(component.Attribute("d").Value, unarc: true)); + paths.Add(path); + } + } + } + + private static TimeSpan CalculateWaitTime(double length, int speed) + { + return TimeSpan.FromSeconds((int)Math.Ceiling((length * UnitLength) / speed)); + } + + private static StringBuilder AddHpglMove(this StringBuilder builder, double x, double y, ref double maxX) + { + x = Math.Round(x); + y = Math.Round(y); + maxX = Math.Max(x, maxX); + return builder + .Append("PU") + .Append(x).Append(',') + .Append(y).Append(";"); + } + + private static StringBuilder AddHpglLine(this StringBuilder builder, double x, double y, ref double maxX) + { + x = Math.Round(x); + y = Math.Round(y); + maxX = Math.Max(x, maxX); + return builder + .Append("PD") + .Append(x).Append(',') + .Append(y).Append(";"); + } + } +} diff --git a/PCUT/PCUT/Extensions/ImageSourceExtensions.cs b/PCUT/PCUT/Extensions/ImageSourceExtensions.cs new file mode 100644 index 0000000..23f98ce --- /dev/null +++ b/PCUT/PCUT/Extensions/ImageSourceExtensions.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using System.Xml.Linq; +using Windows.Graphics.Imaging; +using Windows.Storage.Streams; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; + +namespace PCUT.Extensions +{ + public static class ImageSourceExtensions + { + public static async Task LoadStreamAsync(this ImageSource source, IRandomAccessStream data) + { + switch (source) + { + case SvgImageSource svgSource: + { + await svgSource.SetSourceAsync(data); + break; + } + case BitmapSource bitmapSource: + { + await bitmapSource.SetSourceAsync(data); + break; + } + default: + return; + } + } + + public static async Task LoadSvgAsync(this ImageSource source, XDocument svg) + { + if (source is SvgImageSource svgSource) + { + using (var stream = new MemoryStream()) + { + await svg.SaveAsync(stream, SaveOptions.None, default); + stream.Seek(0, SeekOrigin.Begin); + await svgSource.SetSourceAsync(stream.AsRandomAccessStream()); + } + } + } + + public static async Task CreateThumbnailAsync(this Image image) + { + var stream = new InMemoryRandomAccessStream(); + var bitmap = new RenderTargetBitmap(); + await bitmap.RenderAsync(image); + var pixelBuffer = await bitmap.GetPixelsAsync(); + var sb = SoftwareBitmap.CreateCopyFromBuffer(pixelBuffer, BitmapPixelFormat.Bgra8, bitmap.PixelWidth, bitmap.PixelHeight); + try + { + var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream); + encoder.SetSoftwareBitmap(sb); + var scale = bitmap.PixelHeight > bitmap.PixelWidth + ? bitmap.PixelHeight > 480 ? 480m / bitmap.PixelHeight : 1 + : bitmap.PixelWidth > 640 ? 640m / bitmap.PixelWidth : 1; + encoder.BitmapTransform.ScaledWidth = (uint)(scale * bitmap.PixelWidth); + encoder.BitmapTransform.ScaledHeight = (uint)(scale * bitmap.PixelHeight); + await encoder.FlushAsync(); + stream.Seek(0); + return stream; + } + catch + { + stream.Dispose(); + return null; + } + } + } +} diff --git a/PCUT/PCUT/Extensions/ManipulationExtensions.cs b/PCUT/PCUT/Extensions/ManipulationExtensions.cs new file mode 100644 index 0000000..64eb34a --- /dev/null +++ b/PCUT/PCUT/Extensions/ManipulationExtensions.cs @@ -0,0 +1,15 @@ +using System; +using Windows.UI.Input; + +namespace PCUT.Extensions +{ + public static class ManipulationExtensions + { + public static (double OffsetX, double OffsetY) CalculateTranslation(this ManipulationDelta d, ManipulationVelocities v) + { + var vx_factor = Math.Max(v.Linear.X * v.Linear.X, 1); + var vy_factor = Math.Max(v.Linear.Y * v.Linear.Y, 1); + return (d.Translation.X * vx_factor, d.Translation.Y * vy_factor); + } + } +} diff --git a/PCUT/PCUT/Extensions/NumericExtensions.cs b/PCUT/PCUT/Extensions/NumericExtensions.cs new file mode 100644 index 0000000..af37561 --- /dev/null +++ b/PCUT/PCUT/Extensions/NumericExtensions.cs @@ -0,0 +1,49 @@ +namespace PCUT.Extensions +{ + public static class NumericExtensions + { + public static double GetValueByUnit(this string value) + { + if (value == null) + return 0; + + double scale = 1; + if (value.EndsWith("in")) + { + value = value.Replace("in", string.Empty); + scale = 25.4; + } + else if (value.EndsWith("cm")) + { + value = value.Replace("cm", string.Empty); + scale = 10; + } + else if (value.EndsWith("mm")) + { + value = value.Replace("mm", string.Empty); + scale = 1; + } + else if (value.EndsWith("pc")) + { + value = value.Replace("pc", string.Empty); + scale = (25.4 / 16); + } + else if (value.EndsWith("pt")) + { + value = value.Replace("pt", string.Empty); + scale = (25.4 / 72); + } + else if (value.EndsWith("px")) + { + value = value.Replace("px", string.Empty); + scale = (25.4 / 96); + } + + if (double.TryParse(value, out var doubleValue)) + { + return doubleValue * scale; + } + return 0; + } + } +} diff --git a/PCUT/PCUT/Extensions/PointExtensions.cs b/PCUT/PCUT/Extensions/PointExtensions.cs new file mode 100644 index 0000000..6455d31 --- /dev/null +++ b/PCUT/PCUT/Extensions/PointExtensions.cs @@ -0,0 +1,50 @@ +using DeepNestLib; +using System; +using System.Collections.Generic; +using System.Linq; +using Windows.Foundation; +using Windows.UI.Xaml.Media; + +namespace PCUT.Extensions +{ + public static class PointExtensions + { + public static double DistTo(this SvgPoint p, SvgPoint p2) + { + return Math.Sqrt(Math.Pow(p.x - p2.x, 2) + Math.Pow(p.y - p2.y, 2)); + } + + public static double DistTo(this Point p, Point p2) + { + return Math.Sqrt(Math.Pow(p.X - p2.X, 2) + Math.Pow(p.Y - p2.Y, 2)); + } + + public static Point Mult(this Point p, double m) + { + return new Point((p.X * m), (p.Y * m)); + } + + public static PathGeometry BuildPath(this IEnumerable points) + { + if (points.Count() < 2) + return null; + + var figure = new PathFigure { StartPoint = points.First() }; + foreach (var point in points) + figure.Segments.Add(new LineSegment { Point = point }); + + var path = new PathGeometry(); + path.Figures.Add(figure); + return path; + } + + public static IEnumerable PathPoints(this Rect rect) + { + yield return new Point(rect.Top, rect.Left); + yield return new Point(rect.Top, rect.Right); + yield return new Point(rect.Bottom, rect.Right); + yield return new Point(rect.Bottom, rect.Left); + yield break; + } + } +} diff --git a/PCUT/PCUT/Extensions/SettingExtensions.cs b/PCUT/PCUT/Extensions/SettingExtensions.cs new file mode 100644 index 0000000..44ebcc7 --- /dev/null +++ b/PCUT/PCUT/Extensions/SettingExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions +{ + public static class SettingExtensions + { + public static bool TryGetValue (this Windows.Storage.ApplicationDataContainer settings, string key, out T value) + { + if (settings.Values.TryGetValue(key, out var obj) && !(obj is null) && obj is T tValue) + { + value = tValue; + return true; + } + value = default; + return false; + } + } +} diff --git a/PCUT/PCUT/Extensions/SvgExtensions.cs b/PCUT/PCUT/Extensions/SvgExtensions.cs new file mode 100644 index 0000000..54b3f42 --- /dev/null +++ b/PCUT/PCUT/Extensions/SvgExtensions.cs @@ -0,0 +1,295 @@ +using PCUT.Extensions.Converters; +using PCUT.Extensions.Transforms; +using SvgPathProperties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.Linq; + +namespace PCUT.Extensions +{ + public static class SvgExtensions + { + private static readonly PathConverter _pathConverter = PathConverter.Builder() + .AddPath() + .AddRect() + .AddPolygon() + .AddCircle() + .AddEllipse().Build(); + + public static XDocument PreProcessSvg(this XDocument document, out double width, out double height) + { + var data = document.ScaleToUnit(); + width = data.Width; + height = data.Height; + var queue = new Queue<(XElement, SvgTransform)>(); + queue.Enqueue((document.Root, data.ScaleTransform)); + ConvertToPath(queue); + return document.ApplyCssStyle(data.ScaleTransform).SetAutoSize(); + } + + private static void ConvertToPath(Queue<(XElement Element, SvgTransform RootTransform)> queue) + { + while (queue.TryDequeue(out var item)) + { + var element = item.Element; + var rootTransform = item.RootTransform; + + var svgPath = _pathConverter.GetSvgPath(element); + if (svgPath != null) + { + var transform = element.GetTransform(rootTransform); + transform.SetNext(rootTransform); + + var newElement = GetTransformedPath(svgPath, transform); + + _pathConverter.CopyAttribute(element, newElement); + + newElement.SetAttributeValue("id", $"P_{Guid.NewGuid():N}"); + element.ReplaceWith(newElement); + } + + if (element.Name.LocalName == "g" || element.Name.LocalName == "svg") + { + var transform = element.GetTransform(rootTransform); + transform.SetNext(rootTransform); + element.RemoveAttribute("transform"); + if (element.Name.LocalName == "g") + element.SetAttributeValue("id", $"G_{Guid.NewGuid():N}"); + + foreach (var childElement in element.Elements()) + queue.Enqueue((childElement, transform)); + } + } + } + + private static XElement GetTransformedPath(SvgPath path, SvgTransform transform) + { + var builder = new StringBuilder(); + path = transform.Apply(path); + foreach (var segment in path.Segments) + { + switch (segment) + { + case MoveCommand moveCommand: + { + builder.Append("M ") + .Append(moveCommand.X).Append(',').Append(moveCommand.Y) + .Append(' '); + break; + } + case LineCommand lineCommand: + { + if (lineCommand.ClosePath) + builder.Append('Z'); + else + builder.Append("L ") + .Append(lineCommand.ToX).Append(',').Append(lineCommand.ToY) + .Append(' '); + break; + } + case BezierCommand curveCommand: + { + builder.Append(curveCommand.IsQuadratic ? "Q " : "C ") + .Append(curveCommand.Cp1.X).Append(',').Append(curveCommand.Cp1.Y) + .Append(' ') + .Append(curveCommand.Cp2OrEnd.X).Append(',').Append(curveCommand.Cp2OrEnd.Y) + .Append(' '); + if (!curveCommand.IsQuadratic) + builder.Append(curveCommand.End.X).Append(',').Append(curveCommand.End.Y) + .Append(' '); + break; + } + default: + { + break; + } + } + } + var transformedPath = new XElement(XName.Get("path", "http://www.w3.org/2000/svg")); + transformedPath.SetAttributeValue("d", builder.ToString()); + return transformedPath; + } + + public static SvgTransform GetTransform(this XElement element, SvgTransform rootTransform = null) + { + if (rootTransform == null) + rootTransform = SvgTransform.Default; + + var transforms = new List<(int Index, SvgTransform Transform)>(); + + var transformAttribute = element.Attribute("transform")?.Value; + if (transformAttribute != null) + { + foreach (var (i, translateData) in transformAttribute.GetTransform("translate", 2)) + { + var dx = double.Parse(translateData[0]); + var dy = translateData.Length > 1 ? double.Parse(translateData[1]) : 0; + var translate = new TranslateTransform(dx, dy); + transforms.Add((i, translate)); + } + + foreach (var (i, rotateData) in transformAttribute.GetTransform("rotate", 3)) + { + var degree = double.Parse(rotateData[0]); + var originX = rotateData.Length > 2 ? double.Parse(rotateData[1]) : 0; + var originY = rotateData.Length > 2 ? double.Parse(rotateData[2]) : 0; + var rotate = new RotateTransform(degree, originX, originY); + transforms.Add((i, rotate)); + } + + foreach (var (i, scaleData) in transformAttribute.GetTransform("scale", 2)) + { + var scaleX = double.Parse(scaleData[0]); + var scaleY = scaleData.Length > 1 ? double.Parse(scaleData[1]) : scaleX; + var scale = new ScaleTransform(scaleX, scaleY); + transforms.Add((i, scale)); + } + + foreach (var (i, scaleData) in transformAttribute.GetTransform("matrix", 6)) + { + var a = double.Parse(scaleData[0]); + var b = double.Parse(scaleData[1]); + var c = double.Parse(scaleData[2]); + var d = double.Parse(scaleData[3]); + var e = double.Parse(scaleData[4]); + var f = double.Parse(scaleData[5]); + var matrix = new MatrixTransform(a, b, c, d, e, f); + transforms.Add((i, matrix)); + } + } + + SvgTransform transform; + if (!transforms.Any()) + { + transform = rootTransform; + } + else + { + transforms.Sort((x, y) => x.Index.CompareTo(y.Index)); + transform = transforms.First().Transform; + transform.SetNext(rootTransform); + + foreach (var item in transforms.Skip(1)) + { + item.Transform.SetNext(transform); + transform = item.Transform; + } + } + return transform; + } + + private static IEnumerable<(int Index, string[] Data)> GetTransform(this string transformAttribute, string key, int maxLength) + { + var index = transformAttribute.IndexOf(key); + while (index != -1) + { + var s = transformAttribute.IndexOf('(', index); + var e = transformAttribute.IndexOf(')', index); + yield return (index, transformAttribute.Substring(s + 1, e - s - 1).Split(new[] { ' ', ',' }, maxLength, StringSplitOptions.RemoveEmptyEntries)); + index = transformAttribute.IndexOf(key, e); + } + yield break; + } + + public static (double Width, double Height, SvgTransform ScaleTransform) ScaleToUnit(this XDocument document) + { + var width = ConvertToUnit(document, "width"); + var height = ConvertToUnit(document, "height"); + var (scaleX, scaleY) = ScaleViewBox(document, width, height); + + SvgTransform scaleTransform; + if (scaleX == 1 && scaleY == 1) + scaleTransform = SvgTransform.Default; + else + scaleTransform = new ScaleTransform(scaleX, scaleY); + return (width, height, scaleTransform); + } + + private static double ConvertToUnit(XDocument document, string dimension) + { + var dimensionAttribute = document.Root.Attribute(dimension); + var dimensionValue = dimensionAttribute.Value; + var match = Regex.Match(dimensionValue, "^(?[\\d]+(?:[\\.,][\\d]*)?)(?mm|cm|in|pt|pc|px)?$"); + double scale; + switch (match.Groups["unit"].Value) + { + case "in": { scale = 25.4; break; } + case "cm": { scale = 10; break; } + case "pc": { scale = 25.4 / 16; break; } + case "pt": { scale = 25.4 / 72; break; } + case "px": { scale = 25.4 / 96; break; } + default: { scale = 1; break; } + } + var value = double.Parse(match.Groups["value"].Value) * scale; + document.Root.SetAttributeValue(dimension, value); + return value; + } + + private static (double ScaleX, double ScaleY) ScaleViewBox(XDocument document, double width, double height) + { + var viewBoxAttribute = document.Root.Attribute("viewBox"); + var viewBox = viewBoxAttribute.Value; + var viewBoxData = viewBox.Split(' ', 4); + var minX = double.Parse(viewBoxData[0]); + var minY = double.Parse(viewBoxData[1]); + var viewWidth = double.Parse(viewBoxData[2]); + var viewHeight = double.Parse(viewBoxData[3]); + document.Root.SetAttributeValue("viewBox", $"{minX} {minY} {width} {height}"); + return (width / viewWidth, height / viewHeight); + } + + public static XDocument ApplyCssStyle(this XDocument doc, SvgTransform rootTransform) + { + var styles = doc.GetCssStyleDefinitions(); + var scale = rootTransform is ScaleTransform scaleTransform ? scaleTransform.ScaleX : 1d; + if (styles.Any() && scale != 1d) + { + foreach (var key in styles.Keys.ToArray()) + { + styles[key] = Regex.Replace(styles[key], "stroke-width:(?[\\d]+(?:[\\.,][\\d]*)?)", match => $"stroke-width:{double.Parse(match.Groups["value"].Value) * scale}"); + } + } + foreach (var element in doc.Descendants(XName.Get("path", "http://www.w3.org/2000/svg"))) + { + var classes = element.Attribute("class")?.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(x => $".{x}"); + if (classes != null) + { + var style = string.Join(';', classes.Select(@class => styles.TryGetValue(@class, out var value) ? value : null).Where(x => x != null)); + element.SetAttributeValue("style", style); + } + else + { + var styleAttribute = element.Attribute("style"); + if (styleAttribute != null) + { + var style = Regex.Replace(styleAttribute.Value, "stroke-width:(?[\\d]+(?:[\\.,][\\d]*)?)", match => $"stroke-width:{double.Parse(match.Groups["value"].Value) * scale}"); + styleAttribute.SetValue(style); + } + } + } + return doc; + } + + public static IDictionary GetCssStyleDefinitions(this XDocument doc) + { + var cdata = doc.DescendantNodes() + .OfType() + .Where(x => x.Parent.Name.LocalName == "style" && x.Parent.Attribute("type")?.Value == "text/css") + .FirstOrDefault(); + if (cdata == null) + return new Dictionary(); + + var styles = cdata.Value.Replace("\n", "") + .Split(' ', StringSplitOptions.RemoveEmptyEntries) + .Where(x => !string.IsNullOrEmpty(x)) + .Select((x, i) => (Value: x, Index: i / 2)) + .GroupBy(x => x.Index, x => x.Value) + .ToDictionary(x => x.ElementAt(0), x => x.ElementAt(1).Trim('{', '}')); + cdata.Remove(); + return styles; + } + } +} diff --git a/PCUT/PCUT/Extensions/SvgTransformExtensions.cs b/PCUT/PCUT/Extensions/SvgTransformExtensions.cs new file mode 100644 index 0000000..84c68bd --- /dev/null +++ b/PCUT/PCUT/Extensions/SvgTransformExtensions.cs @@ -0,0 +1,88 @@ +using PCUT.Extensions.Transforms; +using SvgPathProperties; +using SvgPathProperties.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions +{ + public static class SvgTransformExtensions + { + public static SvgPath Apply(this SvgTransform transform, SvgPath path) + { + if (transform == null || path == null) + return path; + + var transformedPath = new SvgPath(); + foreach (var segment in path.Segments) + { + switch (segment) + { + case MoveCommand moveSegment: + var moveCommand = transform.Apply(moveSegment); + transformedPath.AddMoveTo(moveCommand.X, moveCommand.Y); + break; + case LineCommand lineSegment: + if (lineSegment.ClosePath) + { + transformedPath.AddClosePath(); + } + else + { + var lineCommand = transform.Apply(lineSegment); + transformedPath.AddLineTo(lineCommand.ToX, lineCommand.ToY); + } + break; + case BezierCommand bezierSegment: + var bezierCommand = transform.Apply(bezierSegment); + if (!bezierCommand.IsQuadratic) + transformedPath.AddCubicBezierCurve(bezierCommand.Cp1.X, bezierCommand.Cp1.Y, bezierCommand.Cp2OrEnd.X, bezierCommand.Cp2OrEnd.Y, bezierCommand.End.X, bezierCommand.End.Y); + else + transformedPath.AddQuadraticBezierCurve(bezierCommand.Cp1.X, bezierCommand.Cp1.Y, bezierCommand.Cp2OrEnd.X, bezierCommand.Cp2OrEnd.Y); + break; + case ArcCommand arcSegment: + var arcCommand = transform.Apply(arcSegment); + transformedPath.AddArc(arcCommand.Rx, arcCommand.Ry, arcCommand.XAxisRotate, arcCommand.LargeArcFlag, arcCommand.SweepFlag, arcCommand.ToX, arcCommand.ToY); + break; + } + } + return transformedPath; + } + + private static MoveCommand Apply(this SvgTransform transform, MoveCommand command) + { + var (x, y) = transform.Transform(command.X, command.Y); + return new MoveCommand(x, y); + } + + private static LineCommand Apply(this SvgTransform transform, LineCommand command) + { + var (fromX, fromY) = transform.Transform(command.FromX, command.FromY); + var (toX, toY) = transform.Transform(command.ToX, command.ToY); + return new LineCommand(fromX, toX, fromY, toY, command.ClosePath); + } + + private static BezierCommand Apply(this SvgTransform transform, BezierCommand command) + { + var from = transform.Apply(command.From); + var cp1 = transform.Apply(command.Cp1); + var cp2 = transform.Apply(command.Cp2OrEnd); + (double? X, double? Y) end = command.IsQuadratic ? default : transform.Transform(command.End.X, command.End.Y); + return new BezierCommand(from.X, from.Y, cp1.X, cp1.Y, cp2.X, cp2.Y, end.X, end.Y); + } + + private static ArcCommand Apply(this SvgTransform transform, ArcCommand command) + { + throw new NotImplementedException(); + } + + private static Point Apply(this SvgTransform transform, Point point) + { + var (x, y) = transform.Transform(point.X, point.Y); + return new Point(x, y); + } + } +} diff --git a/PCUT/PCUT/Extensions/Transforms/MatrixTransform.cs b/PCUT/PCUT/Extensions/Transforms/MatrixTransform.cs new file mode 100644 index 0000000..6c015bd --- /dev/null +++ b/PCUT/PCUT/Extensions/Transforms/MatrixTransform.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions.Transforms +{ + public class MatrixTransform : SvgTransform + { + public double A { get; private set; } + public double B { get; private set; } + public double C { get; private set; } + public double D { get; private set; } + public double E { get; private set; } + public double F { get; private set; } + + public MatrixTransform(double a, double b, double c, double d, double e, double f) + { + A = a; + B = b; + C = c; + D = d; + E = e; + F = f; + } + + protected override (double X, double Y) InternalTransform(double x, double y) + { + var x1 = A * x + C * y + E; + var y1 = B * x + D * y + F; + return (x1, y1); + } + } +} diff --git a/PCUT/PCUT/Extensions/Transforms/RotateTransform.cs b/PCUT/PCUT/Extensions/Transforms/RotateTransform.cs new file mode 100644 index 0000000..66e09e7 --- /dev/null +++ b/PCUT/PCUT/Extensions/Transforms/RotateTransform.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions.Transforms +{ + public class RotateTransform : SvgTransform + { + private readonly double _sin; + private readonly double _cos; + + public double Degree { get; private set; } + public double OriginX { get; private set; } + public double OriginY { get; private set; } + + public RotateTransform(double degree, double originX, double originY) + { + Degree = degree; + OriginX = originX; + OriginY = originY; + + var angle = degree * DToRFactor; + _sin = Math.Sin(angle); + _cos = Math.Cos(angle); + } + + public RotateTransform(double degree) : this(degree, 0, 0) + { + } + + protected override (double X, double Y) InternalTransform(double x, double y) + { + var x1 = (x - OriginX) * _cos - (y - OriginY) * _sin + OriginX; + var y1 = (x - OriginX) * _sin + (y - OriginY) * _cos + OriginY; + return (x1, y1); + } + } +} diff --git a/PCUT/PCUT/Extensions/Transforms/ScaleTransform.cs b/PCUT/PCUT/Extensions/Transforms/ScaleTransform.cs new file mode 100644 index 0000000..670c601 --- /dev/null +++ b/PCUT/PCUT/Extensions/Transforms/ScaleTransform.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions.Transforms +{ + public class ScaleTransform : SvgTransform + { + public double ScaleX { get; private set; } + public double ScaleY { get; private set; } + + public ScaleTransform(double scaleX, double scaleY) + { + ScaleX = scaleX; + ScaleY = scaleY; + } + + protected override (double X, double Y) InternalTransform(double x, double y) + { + var x1 = ScaleX * x; + var y1 = ScaleY * y; + return (x1, y1); + } + } +} diff --git a/PCUT/PCUT/Extensions/Transforms/SkewTransform.cs b/PCUT/PCUT/Extensions/Transforms/SkewTransform.cs new file mode 100644 index 0000000..d502c81 --- /dev/null +++ b/PCUT/PCUT/Extensions/Transforms/SkewTransform.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions.Transforms +{ + public abstract class SkewTransform : SvgTransform + { + protected readonly double _tan; + + public double Degree { get; set; } + + public SkewTransform(double degree) + { + Degree = degree; + var angle = degree * DToRFactor; + _tan = Math.Tan(angle); + } + } + + public class SkewXTransform : SkewTransform + { + public SkewXTransform(double degree) : base(degree) + { + } + + protected override (double X, double Y) InternalTransform(double x, double y) + { + var x1 = x + y * _tan; + var y1 = y; + return (x1, y1); + } + } + + public class SkewYTransform : SkewTransform + { + public SkewYTransform(double degree) : base(degree) + { + } + + protected override (double X, double Y) InternalTransform(double x, double y) + { + var x1 = x; + var y1 = y + x * _tan; + return (x1, y1); + } + } +} diff --git a/PCUT/PCUT/Extensions/Transforms/SvgTransform.cs b/PCUT/PCUT/Extensions/Transforms/SvgTransform.cs new file mode 100644 index 0000000..bd058fd --- /dev/null +++ b/PCUT/PCUT/Extensions/Transforms/SvgTransform.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions.Transforms +{ + public abstract class SvgTransform + { + protected static readonly double DToRFactor = Math.PI / 180; + protected SvgTransform _nextTransform { get; private set; } + public void SetNext(SvgTransform nextTransform) + { + if (this != nextTransform) + _nextTransform = nextTransform; + } + + public (double X, double Y) Transform(double x, double y) + { + (x, y) = InternalTransform(x, y); + if (_nextTransform != null) + (x, y) = _nextTransform.Transform(x, y); + + return (x, y); + } + + protected abstract (double X, double Y) InternalTransform(double x, double y); + + public static readonly SvgTransform Default = new DefaultTransform(); + private class DefaultTransform : SvgTransform + { + protected override (double X, double Y) InternalTransform(double x, double y) + { + return (x, y); + } + } + } +} diff --git a/PCUT/PCUT/Extensions/Transforms/TranslateTransform.cs b/PCUT/PCUT/Extensions/Transforms/TranslateTransform.cs new file mode 100644 index 0000000..aba34fb --- /dev/null +++ b/PCUT/PCUT/Extensions/Transforms/TranslateTransform.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Extensions.Transforms +{ + public class TranslateTransform : SvgTransform + { + public double Dx { get; private set; } + public double Dy { get; private set; } + + public TranslateTransform(double dx, double dy) + { + Dx = dx; + Dy = dy; + } + + protected override (double X, double Y) InternalTransform(double x, double y) + { + var x1 = x + Dx; + var y1 = y + Dy; + return (x1, y1); + } + } +} diff --git a/PCUT/PCUT/Extensions/XmlExtensions.cs b/PCUT/PCUT/Extensions/XmlExtensions.cs new file mode 100644 index 0000000..d1cb682 --- /dev/null +++ b/PCUT/PCUT/Extensions/XmlExtensions.cs @@ -0,0 +1,253 @@ +using PCUT.DeepNestApi; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace PCUT.Extensions +{ + public static class XmlExtensions + { + public static string GetId(this XElement element) + { + return element.Attribute("id")?.Value; + } + + public static void SetId(this XElement element, string id) + { + element.SetAttributeValue("id", id); + } + + public static XElement GetDescendantById(this XDocument doc, string id) + { + return doc.Root.GetDescendantById(id); + } + + public static XElement GetDescendantById(this XElement element, string id) + { + return element.Descendants().FirstOrDefault(x => x.GetId() == id); + } + + public static string GetViewBox(this XDocument doc) + { + return doc.Root.Attribute("viewBox")?.Value; + } + + public static XElement RemoveAttribute(this XElement element, string attributeName) + { + var attribute = element.Attribute(attributeName); + attribute?.Remove(); + return element; + } + + public static int Count(this XElement element, params string[] names) + { + return names.Sum(name => element.Elements(XName.Get(name, element.GetDefaultNamespace().NamespaceName)).Count()); + } + + public static XDocument SetAutoSize(this XDocument doc) + { + doc.Root.SetAttributeValue("height", "auto"); + doc.Root.SetAttributeValue("width", "auto"); + return doc; + } + + public static void SetSize(this XDocument doc, double width, double height) + { + doc.Root.SetAttributeValue("width", width); + doc.Root.SetAttributeValue("height", height); + } + + public static XElement GetChild(this XDocument doc, string tag) + { + return doc.Root.GetChild(tag); + } + + public static XElement GetChild(this XElement root, string tag) + { + var name = XName.Get(tag, root.GetDefaultNamespace().NamespaceName); + return root.Element(name); + } + + //public static IEnumerable GetChildren(this XDocument doc, string path = null, params string[] tags) + //{ + // var root = doc.Root; + // if (path != null) + // foreach (var tag in path.TrimStart('.').Split("/")) + // root = root.GetChild(tag); + // return root.GetChildren(tags); + //} + + public static IEnumerable GetChildren(this XElement root, params string[] tags) + { + return root.Elements().Where(x => tags.Contains(x.Name.LocalName)); + } + + public static bool IsHidden(this XElement element) + { + if (element.Attribute("visibility")?.Value == "hidden") + return true; + + while (element.Parent != null) + { + element = element.Parent; + if (element.Attribute("visibility")?.Value == "hidden") + return true; + } + return false; + } + + public static void HideById(this XDocument doc, string id) + { + var element = doc.GetDescendantById(id); + element?.SetAttributeValue("visibility", "hidden"); + } + + public static void RestoreById(this XDocument doc, string id) + { + var element = doc.GetDescendantById(id); + element?.Attribute("visibility")?.Remove(); + } + + public static void RemoveHidden(this XDocument doc) + { + var elements = doc.Descendants().Where(element => element.IsHidden()).ToArray(); + foreach (var element in elements) + { + element.Remove(); + } + } + + public static void ApplyTransform(this XDocument doc, string id, string transform) + { + var element = doc.GetDescendantById(id); + if (element.Attribute("transform") == null) + { + element.SetAttributeValue("transform", transform); + } + else + { + var parent = element.Parent; + + var transformElement = XElement.Parse($""); + transformElement.SetId(id); + if (element.IsHidden()) + transformElement.SetAttributeValue("visibility", "hidden"); + transformElement.SetAttributeValue("transform", transform); + + element.Attribute("id")?.Remove(); + element.Attribute("visibility")?.Remove(); + element.Remove(); + transformElement.Add(element); + parent.Add(transformElement); + } + } + + public static IEnumerable<(string Id, bool Deleted, XDocument Component, double BoundWidth, double BoundHeight)> SplitDocument(this XDocument doc, double width, double height) + { + var root = doc.Root; + while (root.Count("g") == 1 && root.Count("path") == 0) + { + root = root.GetChild("g"); + } + foreach (var element in root.GetChildren("g", "path")) + { + var componentElement = new XElement(element); + var deleted = componentElement.IsHidden(); + componentElement.Attribute("visibility")?.Remove(); + var id = componentElement.GetId(); + + var rootElement = CreateRoot(doc); + if (element.Name.LocalName != "g") + { + var groupElement = new XElement(XName.Get("g", root.GetDefaultNamespace().NamespaceName)); + groupElement.Add(componentElement); + componentElement = groupElement; + } + rootElement.Add(componentElement); + var componentDoc = new XDocument(rootElement); + + var (bWidth, bHeight) = componentDoc.ProcessComponent(width, height); + yield return (id, deleted, componentDoc, bWidth, bHeight); + } + yield break; + } + + private static XElement CreateRoot(XDocument doc) + { + var newRoot = new XElement(XName.Get("svg", doc.Root.GetDefaultNamespace().NamespaceName)); + newRoot.SetAttributeValue("width", "auto"); + newRoot.SetAttributeValue("height", "auto"); + newRoot.SetAttributeValue("viewBox", doc.Root.Attribute("viewBox")?.Value); + return newRoot; + } + + private static (double BoundWidth, double BoundHeight) ProcessComponent(this XDocument component, double width, double height) + { + var bbox = SvgParser.GetBBox(component); + var boundWidth = bbox.Width; + var boundHeight = bbox.Height; + var size = bbox.Width > bbox.Height ? bbox.Width : bbox.Height; + var scale = 0.8 * (bbox.Width > bbox.Height ? width : height) / size; + var tx = (width - (scale * bbox.Width)) / 2 - (scale * bbox.Left); + var ty = (height - (scale * bbox.Height)) / 2 - (scale * bbox.Top); + var element = component.Root.Elements().First(); + element.SetAttributeValue("transform", $"translate({tx} {ty}) scale({scale})"); + return (boundWidth, boundHeight); + } + + public static (double MinX, double MinY, double Width, double Height) GetViewBoxData(this string viewBox) + { + var viewBoxData = viewBox.Split(' ', 4); + return ( + double.Parse(viewBoxData[0]), + double.Parse(viewBoxData[1]), + double.Parse(viewBoxData[2]), + double.Parse(viewBoxData[3]) + ); + } + + //public static void IdentifyComponents(this XDocument document, string path, params string[] tags) + //{ + // foreach (var component in document.GetChildren(path: path, tags)) + // { + // if (component.GetId() == null) + // { + // component.SetId($"_{Guid.NewGuid():N}"); + // } + // } + //} + + //public static void PropagateTransform(this XElement element, string previousTransform = null) + //{ + // if (element.Name.LocalName == "g" || element.Name.LocalName == "path") + // { + // var transformAttribute = element.Attribute("transform"); + // var transformAttributeValue = transformAttribute?.Value; + // transformAttributeValue = $"{previousTransform} {transformAttributeValue}".Trim(); + // if (transformAttribute != null) + // transformAttribute.Remove(); + // if (element.Name.LocalName == "path") + // { + // if (!string.IsNullOrWhiteSpace(transformAttributeValue)) + // element.SetAttributeValue("transform", transformAttributeValue); + // return; + // } + // else if (element.Name.LocalName == "g") + // { + // foreach (var child in element.Elements()) + // { + // PropagateTransform(child, transformAttributeValue); + // } + // } + // } + // else if (element.Name.LocalName == "svg") + // { + // foreach (var child in element.Elements()) + // { + // PropagateTransform(child, previousTransform); + // } + // } + //} + } +} diff --git a/PCUT/PCUT/Factories/FileFactory.cs b/PCUT/PCUT/Factories/FileFactory.cs new file mode 100644 index 0000000..f6390f9 --- /dev/null +++ b/PCUT/PCUT/Factories/FileFactory.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Http.Core; +using Http.Core.Extensions; +using PCUT.Extensions; +using Windows.Security.Cryptography.Certificates; +using Windows.Storage; +using Windows.Storage.Pickers; +using Windows.Storage.Streams; +using static Http.Core.Constants.HttpConstants; + +namespace PCUT.Factories +{ + class FileFactory + { + public static Task GetFileAsync() + { + return LocalFileFactory.Instance.GetFileAsync(); + } + + public static Task GetFileAsync(string id) + { + return RemoteFileFactory.Instance.GetFileAsync(id); + } + + public static async Task<(XDocument Data, double Width, double Height)> PreProcessSvgAsync(Stream stream) + { + try + { + var document = await XDocument.LoadAsync(stream, LoadOptions.None, default); + //document.IdentifyComponents(path: "g", "path", "g"); + //document.ApplyCssStyle(); + //document.Root.PropagateTransform(); + //var (width, height) = document.SetAutoSize(); + document.PreProcessSvg(out var width, out var height); + return (document, width, height); + } + catch + { + return default; + } + } + } + + interface IFileFactory + { + Task GetFileAsync(string id = null); + } + + class LocalFileFactory : IFileFactory + { + public static readonly IFileFactory Instance = new LocalFileFactory(); + public async Task GetFileAsync(string id = null) + { + try + { + var file = await CreatePicker().PickSingleFileAsync(); + if (file == null) + return null; + var stream = await file.OpenReadAsync(); + return stream.AsStream(); + } + catch + { + return null; + } + } + + private static FileOpenPicker CreatePicker() + { + var picker = new FileOpenPicker(); + picker.ViewMode = PickerViewMode.Thumbnail; + picker.SuggestedStartLocation = PickerLocationId.Downloads; + picker.FileTypeFilter.Add(".svg"); + return picker; + } + } + class RemoteFileFactory : IFileFactory + { + public static readonly IFileFactory Instance = new RemoteFileFactory(); + public async Task GetFileAsync(string id = null) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var stream = new MemoryStream(); + var response = await httpClient.DownloadAsync(Api.Download.FormatRoute(id), stream); + if (response) + { + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + else + { + stream.Dispose(); + } + } + catch (Exception ex) + { + } + } + return null; + } + } +} diff --git a/PCUT/PCUT/Helpers/Converters/BooleanNegationConverter.cs b/PCUT/PCUT/Helpers/Converters/BooleanNegationConverter.cs new file mode 100644 index 0000000..ea81d4a --- /dev/null +++ b/PCUT/PCUT/Helpers/Converters/BooleanNegationConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace PCUT.Helpers.Converters +{ + public class BooleanNegationConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return (bool)value ? "Active" : "Inactive"; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return (string)value == "Active"; + } + } +} diff --git a/PCUT/PCUT/Helpers/Converters/DateTimeToFormattedStringConverter.cs b/PCUT/PCUT/Helpers/Converters/DateTimeToFormattedStringConverter.cs new file mode 100644 index 0000000..e0e0f95 --- /dev/null +++ b/PCUT/PCUT/Helpers/Converters/DateTimeToFormattedStringConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Helpers.Converters +{ + public class DateTimeToFormattedStringConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is DateTimeOffset dateTimeOffset) + { + return dateTimeOffset.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture); + } + return string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (value is string stringValue) + { + if (DateTimeOffset.TryParseExact(stringValue, "MM/dd/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)) + { + return result; + } + + } + return value; + } + } +} diff --git a/PCUT/PCUT/Helpers/RelayCommand.cs b/PCUT/PCUT/Helpers/RelayCommand.cs new file mode 100644 index 0000000..8aabae6 --- /dev/null +++ b/PCUT/PCUT/Helpers/RelayCommand.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace PCUT.Helpers +{ + public class RelayCommand : ICommand + { + private readonly Action _execute; + private readonly Func _executeAsync; + private readonly Func _canExecute; + private readonly Action _executeWithParam; + + + // Constructor for synchronous action without parameters + public RelayCommand(Action execute, Func canExecute = null) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + + // Constructor for asynchronous action without parameters + public RelayCommand(Func executeAsync, Func canExecute = null) + { + _executeAsync = executeAsync ?? throw new ArgumentNullException(nameof(executeAsync)); + _canExecute = canExecute; + } + + // Added constructor for synchronous action with a parameter + public RelayCommand(Action executeWithParam, Func canExecute = null) + { + _executeWithParam = executeWithParam ?? throw new ArgumentNullException(nameof(executeWithParam)); + _canExecute = canExecute; + } + + public bool CanExecute(object parameter) + { + return _canExecute == null || _canExecute(); + } + + public async void Execute(object parameter) + { + if (_execute != null) + { + _execute(); + } + else if (_executeWithParam != null) + { + _executeWithParam(parameter); + } + else if (_executeAsync != null) + { + await _executeAsync(); + } + } + + public event EventHandler CanExecuteChanged; + + public void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/PCUT/PCUT/Models/Categories/CategoryModel.cs b/PCUT/PCUT/Models/Categories/CategoryModel.cs new file mode 100644 index 0000000..39d4d31 --- /dev/null +++ b/PCUT/PCUT/Models/Categories/CategoryModel.cs @@ -0,0 +1,61 @@ +using PCUT.Entities; +using PCUT.ViewModels; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Models.Categories +{ + public class CategoryModel : Category + { + public string DisplayId { get; set; } + public ObservableCollection CategoryMetadata { get; } + + public CategoryModel() + { + CategoryMetadata = new ObservableCollection + { + new MetadataTypeModel(this, 1, MetadataViewModel.TypeHeader), + new MetadataTypeModel(this, 2, MetadataViewModel.BrandHeader), + new MetadataTypeModel(this, 3, MetadataViewModel.SerieHeader), + new MetadataTypeModel(this, 4, MetadataViewModel.ModelHeader), + new MetadataTypeModel(this, 5, MetadataViewModel.SubTypeHeader), + new MetadataTypeModel(this, 6, MetadataViewModel.YearHeader) + }; + } + } + + public class MetadataTypeModel : NotificationBase + { + public CategoryModel Category { get; } + public int DisplayId { get; set; } + public string Name { get; } + public bool IsLoaded { get; set; } + + private bool _isExpanded; + public bool IsExpanded + { + get { return _isExpanded; } + set { SetProperty(ref _isExpanded, value); } + } + + public ObservableCollection Metadata { get; } = new ObservableCollection(); + + public MetadataTypeModel(CategoryModel category, int displayId, string name) + { + Category = category; + DisplayId = displayId; + Name = name; + IsLoaded = false; + } + } + + public class CategoryMetadataModel : CategoryMetadata + { + public MetadataTypeModel MetadataType { get; set; } + public int DisplayId { get; set; } + } +} diff --git a/PCUT/PCUT/Models/Categories/CategoryTemplateSelector.cs b/PCUT/PCUT/Models/Categories/CategoryTemplateSelector.cs new file mode 100644 index 0000000..70a0698 --- /dev/null +++ b/PCUT/PCUT/Models/Categories/CategoryTemplateSelector.cs @@ -0,0 +1,26 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace PCUT.Models.Categories +{ + public class CategoryTemplateSelector : DataTemplateSelector + { + public DataTemplate CategoryTemplate { get; set; } + public DataTemplate MetadataTypeTemplate { get; set; } + public DataTemplate MetadataTemplate { get; set; } + protected override DataTemplate SelectTemplateCore(object item) + { + switch (item) + { + case CategoryModel _: + return CategoryTemplate; + case MetadataTypeModel _: + return MetadataTypeTemplate; + case CategoryMetadataModel _: + return MetadataTemplate; + default: + return null; + } + } + } +} diff --git a/PCUT/PCUT/Models/NotificationBase.cs b/PCUT/PCUT/Models/NotificationBase.cs new file mode 100644 index 0000000..702b5e3 --- /dev/null +++ b/PCUT/PCUT/Models/NotificationBase.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.Models +{ + public class NotificationBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + // SetField (Name, value); // where there is a data member + protected bool SetProperty(ref T field, T value, [CallerMemberName] String property = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + RaisePropertyChanged(property); + return true; + } + + // SetField(()=> somewhere.Name = value; somewhere.Name, value) // Advanced case where you rely on another property + protected bool SetProperty(T currentValue, T newValue, Action DoSet, [CallerMemberName] String property = null) + { + if (EqualityComparer.Default.Equals(currentValue, newValue)) return false; + DoSet.Invoke(); + RaisePropertyChanged(property); + return true; + } + + protected void RaisePropertyChanged(string property) + { + if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } + } + } + + //public class NotificationBase : NotificationBase where T : class, new() + //{ + // protected T This; + + // public static implicit operator T(NotificationBase thing) { return thing.This; } + + // public NotificationBase(T thing = null) + // { + // This = (thing == null) ? new T() : thing; + // } + //} +} diff --git a/PCUT/PCUT/Models/SelectableCollection.cs b/PCUT/PCUT/Models/SelectableCollection.cs new file mode 100644 index 0000000..4bb2afe --- /dev/null +++ b/PCUT/PCUT/Models/SelectableCollection.cs @@ -0,0 +1,132 @@ +using PCUT.Extensions; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; + +namespace PCUT.Models +{ + public abstract class SelectableCollection : NotificationBase + { + protected readonly bool _withDefault; + + protected int _selectedIndex; + public int SelectedIndex + { + get => _selectedIndex; + set + { + if (_withDefault && value == 0) + { + _selectedIndex = 0; + } + else + { + SetProperty(ref _selectedIndex, value); + } + } + } + + protected SelectableCollection(bool withDefault) + { + _withDefault = withDefault; + _selectedIndex = -1; + } + + private void Selector_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + var selector = sender as Selector; + if (_withDefault && selector.SelectedIndex == 0) + { + selector.SelectedIndex = -1; + } + } + + public void Bind(Selector selector) + { + if (selector != null) + selector.SelectionChanged += Selector_SelectionChanged; + } + } + + public class SelectableCollection : SelectableCollection + { + private readonly T DefaultItem; + + private readonly List _items; + public ObservableCollection Items { get; } + public T SelectedItem => _selectedIndex >= 0 ? Items[_selectedIndex] : default; + + public SelectableCollection(T defaultItem) : base(true) + { + DefaultItem = defaultItem; + _selectedIndex = -1; + _items = new List(); + Items = new ObservableCollection(); + } + + public SelectableCollection(bool withDefault = false) : base(withDefault) + { + if (withDefault) + DefaultItem = default; + _selectedIndex = -1; + _items = new List(); + Items = new ObservableCollection(); + } + + public SelectableCollection(params T[] items) : this() + { + AddRange(items); + } + + public SelectableCollection(bool withDefault = false, params T[] items) : this(withDefault) + { + AddRange(items); + } + + public SelectableCollection(T defaultItem, params T[] items) : this(defaultItem) + { + AddRange(items); + } + + public void SelectBy(Func predicate) + { + var item = Items.FirstOrDefault(predicate); + var index = (item == null) ? -1 : Items.IndexOf(item); + SelectedIndex = index; + } + + public void Load(IEnumerable items) + { + SelectedIndex = -1; + AddRange(items); + } + + private void AddRange(IEnumerable items) + { + _items.Clear(); + Items.Clear(); + if (_withDefault) + { + _items.Add(DefaultItem); + Items.Add(DefaultItem); + } + foreach (var item in items) + { + _items.Add(item); + Items.Add(item); + } + } + + public void Filter(Predicate filter) + { + var start = _withDefault ? 1 : 0; + Items.Filter(_items, filter, start); + } + } +} diff --git a/PCUT/PCUT/Models/Users/UsersModel.cs b/PCUT/PCUT/Models/Users/UsersModel.cs new file mode 100644 index 0000000..6b9186e --- /dev/null +++ b/PCUT/PCUT/Models/Users/UsersModel.cs @@ -0,0 +1,9 @@ +using PCUT.Entities; + +namespace PCUT.Models.Users +{ + public class UsersModel : UserProfile + { + public string DisplayId { get; set; } + } +} diff --git a/PCUT/PCUT/PCUT.csproj b/PCUT/PCUT/PCUT.csproj new file mode 100644 index 0000000..fb7e32a --- /dev/null +++ b/PCUT/PCUT/PCUT.csproj @@ -0,0 +1,479 @@ + + + + + Debug + x86 + {2E7B481D-6F70-41BE-8224-A9D69499E6EE} + AppContainerExe + Properties + PCUT + PCUT + en-US + UAP + 10.0.22621.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + True + True + False + True + Always + x86|x64 + C:/PCUT + 0 + C:\PCUT\ + pcut.pfx + SHA256 + + + False + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + true + + + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + true + true + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + true + true + + + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM64 + false + prompt + true + true + + + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM64 + false + prompt + true + true + + + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x64 + false + prompt + true + false + + + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + true + true + + + PackageReference + + + + App.xaml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CategoryUpsertDialog.xaml + + + MetadataUpsertDialog.xaml + + + AdminCenterPage.xaml + + + CategoriesViewPage.xaml + + + UserGuidePage.xaml + + + ChangePasswordDialog.xaml + + + CutFilesDialog.xaml + + + DataCenterPage.xaml + + + DesignCenterPage.xaml + + + EditPage.xaml + + + FileAddDialog.xaml + + + FileEditDialog.xaml + + + FileManagerPage.xaml + + + LoginPage.xaml + + + MainMenuPage.xaml + + + PortSettingDialog.xaml + + + SvgNestPage.xaml + + + SvgNestSettingDialog.xaml + + + UpsertUserPage.xaml + + + UserListPage.xaml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + + + 8.0.0 + + + 6.2.14 + + + 7.1.3 + + + + + {6c87a9d3-a2e3-41e0-b112-cd1dce45d53c} + Clipper.Core + + + {09abfbb8-78be-4920-8c86-3cba7693076d} + DeepNestLib.Core + + + {01f0f3ea-71de-436c-96b0-934e566bf04f} + Http.Core + + + {71998b3c-e08f-4324-a087-2edeff1e3256} + MinkowskiCpp + + + {C65442D8-797E-4A98-AD7D-1A1068D8020E} + PCUT.Entities + + + + + + + + + + 14.0 + + + + \ No newline at end of file diff --git a/PCUT/PCUT/Package.appxmanifest b/PCUT/PCUT/Package.appxmanifest new file mode 100644 index 0000000..f2dc745 --- /dev/null +++ b/PCUT/PCUT/Package.appxmanifest @@ -0,0 +1,54 @@ + + + + + + + + + + PCUT + ADMINN + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PCUT/PCUT/Pages/AdminCenterPage.xaml b/PCUT/PCUT/Pages/AdminCenterPage.xaml new file mode 100644 index 0000000..55ea1c2 --- /dev/null +++ b/PCUT/PCUT/Pages/AdminCenterPage.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/AdminCenterPage.xaml.cs b/PCUT/PCUT/Pages/AdminCenterPage.xaml.cs new file mode 100644 index 0000000..5318e06 --- /dev/null +++ b/PCUT/PCUT/Pages/AdminCenterPage.xaml.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel.UserDataTasks; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class AdminCenterPage : Page + { + public AdminCenterPage() + { + this.InitializeComponent(); + } + private string _currentSelectedNavItemTag; + + private void NavigationViewItem_Invoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) + { + var item = args.InvokedItemContainer as NavigationViewItem; + var itemTag = item.Tag.ToString(); + + if (_currentSelectedNavItemTag == itemTag) + { + return; + } + + _currentSelectedNavItemTag = itemTag; + + switch (itemTag) + { + case "Categories": + ContentFrame.Navigate(typeof(CategoriesViewPage)); + break; + case "Files": + ContentFrame.Navigate(typeof(FileManagerPage)); + break; + case "Users": + ContentFrame.Navigate(typeof(UserListPage)); + break; + } + sender.IsPaneOpen = true; + } + + private void AdminCenterPage_BackRequested(object sender, BackRequestedEventArgs e) + { + if (ContentFrame.CanGoBack) + { + ContentFrame.GoBack(); + e.Handled = true; + } + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible; + SystemNavigationManager.GetForCurrentView().BackRequested += Page_BackRequested; + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + base.OnNavigatedFrom(e); + SystemNavigationManager.GetForCurrentView().BackRequested -= Page_BackRequested; + } + + private void Page_BackRequested(object sender, BackRequestedEventArgs e) + { + Frame rootFrame = this.Frame; + if (rootFrame.CanGoBack) + { + rootFrame.GoBack(); + e.Handled = true; + } + } + } +} diff --git a/PCUT/PCUT/Pages/CategoriesManagement/CategoriesViewPage.xaml b/PCUT/PCUT/Pages/CategoriesManagement/CategoriesViewPage.xaml new file mode 100644 index 0000000..dcec63b --- /dev/null +++ b/PCUT/PCUT/Pages/CategoriesManagement/CategoriesViewPage.xaml @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/DesignCenter/CutFilesDialog.xaml.cs b/PCUT/PCUT/Pages/DesignCenter/CutFilesDialog.xaml.cs new file mode 100644 index 0000000..4b53ac9 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/CutFilesDialog.xaml.cs @@ -0,0 +1,207 @@ +using PCUT.Extensions; +using PCUT.Factories; +using PCUT.ViewModels; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.IO.Ports; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + public sealed partial class CutFilesDialog : ContentDialog + { + private SvgData _svgData => SvgData.Instance; + public ImageViewModel DisplayModel { get; set; } + private readonly string _portName; + private readonly PortSettingViewModel _portSettings; + private readonly DesignCenterViewModel _designCenterViewModel; + + public CutFilesDialog( + string portName, + PortSettingViewModel portSetting, + DesignCenterViewModel designCenterViewModel) + { + this.InitializeComponent(); + DisplayModel = new ImageViewModel(); + _portName = portName; + _portSettings = portSetting; + _designCenterViewModel = designCenterViewModel; + StartButton.IsEnabled = !(portName is null); + } + + private Task _printTask; + private void ContentDialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + var data = _designCenterViewModel.UseOrigin ? _svgData.Data : _svgData.NestedData; + PreviewWidthDisplay.Text = Math.Ceiling(_designCenterViewModel.UseOrigin ? _svgData.OriginWidth.Value : _svgData.NestedWidth.Value).ToString(); + PreviewHeightDisplay.Text = Math.Ceiling(_designCenterViewModel.UseOrigin ? _svgData.OriginHeight.Value : _svgData.NestedHeight.Value).ToString(); + DisplayModel.SetSource(data); + } + + private void MainScroll_Loading(FrameworkElement sender, object args) + { + var borderRatio = MainColumnDefinition.ActualWidth / PreviewRowDefinition.ActualHeight; + var width = Math.Ceiling(_designCenterViewModel.UseOrigin ? _svgData.OriginWidth.Value : _svgData.NestedWidth.Value); + var height = Math.Ceiling(_designCenterViewModel.UseOrigin ? _svgData.OriginHeight.Value : _svgData.NestedHeight.Value); + var ratio = width / height; + if (ratio < borderRatio) + ((ComponentStyle)Resources["previewStyle"]).SetSize(PreviewRowDefinition, ratio); + else + ((ComponentStyle)Resources["previewStyle"]).SetSize(MainColumnDefinition, ratio); + } + + private CancellationTokenSource _cancellationTokenSource; + private async Task Print(XDocument nested) + { + if (nested == null) + { + CancelButton.Content = "Done"; + StartButton.IsEnabled = true; + return; + } + + _cancellationTokenSource = new CancellationTokenSource(); + using (var serialPort = new SerialPort(_portName, _portSettings.BaudRate) + { + DataBits = _portSettings.Databits, + Parity = _portSettings.ParityList[_portSettings.SelectedParity], + StopBits = _portSettings.StopbitsList[_portSettings.SelectedStopbits] + }) + { + var errorMessage = string.Empty; + try + { + await Task.Factory.StartNew(() => + { + var data = nested.ToHPGL( + (_svgData.OriginWidth.Value, _svgData.OriginHeight.Value), + (_designCenterViewModel.Width, _designCenterViewModel.Height), + _portSettings.Speed); + serialPort.Open(); + Task waitTask = Task.CompletedTask; + foreach (var (path, waitTime) in data) + { + _cancellationTokenSource.Token.ThrowIfCancellationRequested(); + waitTask.Wait(); + try + { + serialPort.Write(path); + } + catch + { + throw new TimeoutException(); + } + waitTask = Task.Delay(waitTime, _cancellationTokenSource.Token); + } + }, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } + catch (Exception ex) + { + if (ex is AggregateException) + ex = ex.InnerException; + + switch (ex) + { + case UnauthorizedAccessException unauthorizedAccessException: + errorMessage = "Access Denied. Port is currently in used."; + break; + case ArgumentOutOfRangeException argumentOutOfRangeException: + errorMessage = "Wrong port settings. Please verify settings value is valid."; + break; + case IOException iOException: + errorMessage = "Port is in invalid state or port settings is not matched. Please check port connection and settings"; + break; + case InvalidOperationException invalidOperationException: + errorMessage = "Port is currently in used."; + break; + case TimeoutException timeoutException: + errorMessage = "Failed to send data to port."; + break; + } + } + finally + { + try + { + serialPort.Close(); + Task.Delay(1000).Wait(); + } + catch + { + } + } + + if (!string.IsNullOrEmpty(errorMessage)) + { + DispatchAsync(async () => + { + var dialog = new MessageDialog(errorMessage); + await dialog.ShowAsync(); + }); + } + } + _printTask = null; + StartButton.IsEnabled = true; + CancelButton.Content = "Done"; + } + + private async void DispatchAsync(Action action) + { + await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => action.Invoke()); + } + + private void MainScroll_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + var (offsetX, offsetY) = e.Delta.CalculateTranslation(e.Velocities); + var scrollViewer = sender as ScrollViewer; + _ = scrollViewer.ChangeView(scrollViewer.HorizontalOffset - offsetX, scrollViewer.VerticalOffset - offsetY, scrollViewer.ZoomFactor); + } + + private async void CancelButton_Click(object sender, RoutedEventArgs e) + { + CancelButton.IsEnabled = false; + StartButton.IsEnabled = false; + CloseButton.IsEnabled = false; + _cancellationTokenSource?.Cancel(); + if (_printTask != null) + { + await _printTask; + CancelButton.IsEnabled = true; + StartButton.IsEnabled = true; + CloseButton.IsEnabled = true; + if (sender == CloseButton) + Hide(); + } + else + { + Hide(); + } + } + + private void StartButton_Click(object sender, RoutedEventArgs e) + { + StartButton.IsEnabled = false; + CancelButton.Content = "Cancel"; + var data = _designCenterViewModel.UseOrigin ? _svgData.Data : _svgData.NestedData; + _printTask = Print(data); + } + } +} diff --git a/PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml b/PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml new file mode 100644 index 0000000..aa6e72c --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml.cs b/PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml.cs new file mode 100644 index 0000000..0dec773 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/DesignCenterPage.xaml.cs @@ -0,0 +1,196 @@ +using Http.Core.Contexts; +using PCUT.ViewModels; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading; +using System.Threading.Tasks; +using Windows.Devices.Enumeration; +using Windows.Devices.SerialCommunication; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Graphics.Display; +using Windows.UI.Core; +using Windows.UI.Popups; +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using static PCUT.Pages.DataCenterPage; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class DesignCenterPage : Page + { + private SvgData _svgData => SvgData.Instance; + DesignCenterViewModel viewModel { get; set; } + PortSettingViewModel SettingModel { get; set; } + public DesignCenterPage() + { + this.InitializeComponent(); + viewModel = new DesignCenterViewModel(); + SettingModel = new PortSettingViewModel(); + this.DataContext = viewModel; + + var isPermittedEdit = UserContext.Instance.IsPermittedEdit; + WidthInput.IsEnabled = isPermittedEdit; + HeightInput.IsEnabled = isPermittedEdit; + HeightSelector.IsEnabled = !isPermittedEdit; + if (isPermittedEdit) + { + HeightInput.Visibility = Visibility.Visible; + HeightSelector.Visibility = Visibility.Collapsed; + } + else + { + HeightInput.Visibility = Visibility.Collapsed; + HeightSelector.Visibility = Visibility.Visible; + } + } + + protected async override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + string itemId = null; + if (e.Parameter is string id && !string.IsNullOrEmpty(id)) + { + itemId = id; + if (itemId != _svgData.ItemId) + _svgData.SetData(null); + } + else if (_svgData.Data != null) + { + itemId = _svgData.ItemId; + } + + if (!string.IsNullOrEmpty(itemId)) + { + var loadSuccess = await viewModel.GetCdrFileAsync(itemId); + if (!loadSuccess) + { + var messageDialog = new MessageDialog("File doesn't exist!"); + await messageDialog.ShowAsync(); + return; + } + NavigateContentFrame(typeof(EditPage), viewModel); + } + var deviceInfo = await GetPortInfos(); + foreach (var info in deviceInfo) + viewModel.Ports.Add(info); + if (deviceInfo.Any()) + viewModel.SelectedPortIndex = 0; + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + base.OnNavigatedFrom(e); + //SvgData.Instance.SetData(null); + } + + private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + CuttingMachines.Text = viewModel.SelectedDeviceName; + SettingModel.LoadSetting(viewModel.SelectedPortName); + } + + private void SortFiles_Button(object sender, RoutedEventArgs e) + { + NavigateContentFrame(typeof(SvgNestPage), viewModel); + } + + private void NavigateContentFrame(Type pageType, object param = null, bool isGoBack = false) + { + var currentPageType = ContentFrame.Content?.GetType(); + if ((!isGoBack || currentPageType != null) && currentPageType != pageType) + { + BackButton.Visibility = pageType == typeof(EditPage) ? Visibility.Collapsed : Visibility.Visible; + ContentFrame.Navigate(pageType, param); + } + } + + private async void CutFiles_Button(object sender, RoutedEventArgs e) + { + //if (_svgData.NestedData == null) + // NavigateContentFrame(typeof(SvgNestPage), viewModel); + //else + //{ + try + { + string selectedPortName = viewModel.SelectedPortName; + var dialog = new CutFilesDialog(selectedPortName, SettingModel, viewModel); + await dialog.ShowAsync(); + } + catch + { + + } + //} + } + + private void Back_Click(object sender, RoutedEventArgs e) + { + NavigateContentFrame(typeof(EditPage), viewModel, isGoBack: true); + } + + private async Task> GetPortInfos() + { + var infos = await DeviceInformation.FindAllAsync(SerialDevice.GetDeviceSelector()); + if (infos != null && infos.Any()) + { + var portInfos = new List(); + foreach (var info in infos) + { + using (var deviceInfo = await SerialDevice.FromIdAsync(info.Id)) + { + if (deviceInfo != null) + { + portInfos.Add(new PortInfo(deviceInfo.PortName, info.Name)); + } + } + } + return portInfos; + } + return Array.Empty(); + } + + private async void SettingPort_Click(object sender, RoutedEventArgs e) + { + try + { + var categoryEditDialog = new PortSettingDialog(SettingModel); + await categoryEditDialog.ShowAsync(); + SettingModel.SaveSetting(viewModel.SelectedPortName); + } + catch + { + + } + } + + private async void Refresh_Click(object sender, RoutedEventArgs e) + { + var deviceInfo = await GetPortInfos(); + viewModel.Ports.Clear(); + foreach (var info in deviceInfo) + viewModel.Ports.Add(info); + if (deviceInfo.Any()) + viewModel.SelectedPortIndex = 0; + } + + private void TextBox_BeforeTextChanging(TextBox sender, TextBoxBeforeTextChangingEventArgs args) + { + args.Cancel = args.NewText.Any(ch => !char.IsDigit(ch) && ch != '.'); + } + } +} diff --git a/PCUT/PCUT/Pages/DesignCenter/EditPage.xaml b/PCUT/PCUT/Pages/DesignCenter/EditPage.xaml new file mode 100644 index 0000000..74a2468 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/EditPage.xaml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/DesignCenter/EditPage.xaml.cs b/PCUT/PCUT/Pages/DesignCenter/EditPage.xaml.cs new file mode 100644 index 0000000..eead20a --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/EditPage.xaml.cs @@ -0,0 +1,263 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using Http.Core.Contexts; +using PCUT.Extensions; +using PCUT.Factories; +using PCUT.ViewModels; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Navigation; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class EditPage : Page + { + private SvgData _svgData => SvgData.Instance; + private LoadingModel LoadingModel { get; set; } + public ImageViewModel PreviewModel { get; set; } + public ComponentImageModel ComponentModel { get; set; } + private DesignCenterViewModel _designCenterModel; + + public EditPage() + { + this.InitializeComponent(); + LoadingModel = new LoadingModel(); + PreviewModel = new ImageViewModel(); + ComponentModel = new ComponentImageModel(); + LoadingModel.PropertyChanged += AllImageLoaded; + } + + protected override async void OnNavigatedTo(NavigationEventArgs e) + { + _designCenterModel = e.Parameter as DesignCenterViewModel; + _designCenterModel.UseOrigin = true; + if (_svgData.Data != null && _designCenterModel != null && _svgData.FileId != _designCenterModel?.FileId) + _svgData.SetData(null); + + int dataUnloaded = 0; + if (_svgData.Data == null) + { + if (_designCenterModel == null) + { + _svgData.SetData(null); + return; + } + dataUnloaded = 1; + XDocument data; double width; double height; + using (var fileStream = await FileFactory.GetFileAsync(_designCenterModel.FileId)) + { + if (fileStream == null) + { + LoadingRing.IsActive = false; + LoadingRing.Visibility = Visibility.Collapsed; + await ShowDialog("File Error!", "File has been deleted or its data was corrupted!\nPlease contact admin to re-upload the file"); + return; + } + (data, width, height) = await FileFactory.PreProcessSvgAsync(fileStream); + } + var components = data.SplitDocument(width, height).ToArray(); + data.Root.Add(XElement.Parse($"")); + + _svgData.SetData(data, width, height); + _svgData.ItemId = _designCenterModel.ItemId; + _svgData.FileId = _designCenterModel.FileId; + foreach (var (id, deleted, component, boundWidth, boundHeight) in components) + _svgData.AddComponent(id, deleted, component, boundWidth, boundHeight); + } + + if (UserContext.Instance.IsPermittedEdit) + { + _designCenterModel.Width = _svgData.OriginWidth.Value; + _designCenterModel.Height = _svgData.OriginHeight.Value; + } + _designCenterModel.PropertyChanged += UpdateSizeChanged; + + if (_svgData.Data != null) + { + LoadingModel.SetCount(_svgData.ComponentCount + dataUnloaded); + PreviewModel.SetSource(_svgData.Data); + foreach (var (id, deleted, component) in _svgData.ComponentData) + ComponentModel.AddSource(id, deleted, component); + } + if (!UserContext.Instance.IsPermittedEdit) + _designCenterModel.SelectedHeightIndex = 0; + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + //if (e.SourcePageType != typeof(SvgNestPage)) + // _svgData.SetData(null); + + if (_designCenterModel != null) + _designCenterModel.PropertyChanged -= UpdateSizeChanged; + } + + private static readonly object _boundingSizeLock = new object(); + private void UpdateSizeChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(DesignCenterViewModel.Width)) + { + var width = (sender as DesignCenterViewModel).Width; + _svgData.SetResult(null); + _designCenterModel.MinSize = 0; + _ = HandleSizeChangedAsync(width: width); + } + else if (e.PropertyName == nameof(DesignCenterViewModel.Height)) + { + var height = (sender as DesignCenterViewModel).Height; + _svgData.SetResult(null); + _designCenterModel.MinSize = 0; + _ = HandleSizeChangedAsync(height: height); + } + } + + private async Task HandleSizeChangedAsync(double? width = null, double? height = null) + { + if (Monitor.TryEnter(_boundingSizeLock, 100)) + { + try + { + var bound = _svgData.Data.GetDescendantById("PCUT_BOUNDING"); + if (width != null) + { + bound.SetAttributeValue("width", width.Value); + } + if (height != null) + { + bound.SetAttributeValue("height", height.Value); + } + _svgData.CleanUpContext(); + await PreviewModel.Source.LoadSvgAsync(_svgData.Data); + } + finally + { + Monitor.Exit(_boundingSizeLock); + } + } + } + + private void MainScroll_Loading(FrameworkElement sender, object args) + { + ((ComponentStyle)Resources["previewStyle"]).SetSize(PreviewRowDefinition, PreviewColumnDefinition); + } + + private void ComponentPreview_Loading(FrameworkElement sender, object args) + { + ((ComponentStyle)Resources["componentStyle"]).SetSize(ComponentColumnDefinition, rate: 3); + } + + private void Image_Loaded(object sender, RoutedEventArgs e) + { + LoadingModel.DecreaseCount(); + } + + private void AllImageLoaded(object sender, PropertyChangedEventArgs e) + { + LoadingRing.IsActive = false; + LoadingRing.Visibility = Visibility.Collapsed; + } + + private void MainScroll_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + var (offsetX, offsetY) = e.Delta.CalculateTranslation(e.Velocities); + var scrollViewer = sender as ScrollViewer; + _ = scrollViewer.ChangeView(scrollViewer.HorizontalOffset - offsetX, scrollViewer.VerticalOffset - offsetY, scrollViewer.ZoomFactor); + } + + private void MainPreviewRefresh_Click(object sender, RoutedEventArgs e) + { + MainScroll.ChangeView(0, 0, 1); + } + + private void MainPreviewSelectAll_Click(object sender, RoutedEventArgs e) + { + foreach (var component in ComponentModel.ComponentList.Where(x => x.Deleted)) + { + _svgData.Data.RestoreById(component.Id); + component.Deleted = false; + } + PreviewModel.SetSource(_svgData.Data); + } + + private void MainPreviewDeleteAll_Click(object sender, RoutedEventArgs e) + { + foreach (var component in ComponentModel.ComponentList.Where(x => !x.Deleted)) + { + _svgData.Data.HideById(component.Id); + component.Deleted = true; + } + PreviewModel.SetSource(_svgData.Data); + } + + private void Image_Tapped(object sender, TappedRoutedEventArgs e) + { + var image = (sender as FrameworkElement); + var id = image.Tag as string; + + var component = ComponentModel.ComponentList.FirstOrDefault(x => x.Id == id); + if (component == null) + return; + + if (component.Deleted) + RestoreComponent(id, component); + else + DeleteComponent(id, component); + } + + private void ComponentDelete(object sender, RoutedEventArgs e) + { + var item = sender as FrameworkElement; + var id = item.Tag as string; + + DeleteComponent(id, item.DataContext as ComponentImageSource); + } + + private void ComponentRestore(object sender, RoutedEventArgs e) + { + var item = sender as FrameworkElement; + var id = item.Tag as string; + + RestoreComponent(id, item.DataContext as ComponentImageSource); + } + + private void DeleteComponent(string id, ComponentImageSource component) + { + if (component == null) return; + _svgData.Data.HideById(id); + component.Deleted = true; + PreviewModel.SetSource(_svgData.Data); + } + + private void RestoreComponent(string id, ComponentImageSource component) + { + if (component == null) return; + _svgData.Data.RestoreById(id); + component.Deleted = false; + PreviewModel.SetSource(_svgData.Data); + } + + private async Task ShowDialog(string title, string content) + { + ContentDialog dialog = new ContentDialog + { + Title = title, + Content = content, + CloseButtonText = "OK" + }; + dialog.CloseButtonStyle = Application.Current.Resources["CloseDialogButton"] as Style; + + await dialog.ShowAsync(); + } + } +} diff --git a/PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml b/PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml new file mode 100644 index 0000000..77aa409 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml.cs b/PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml.cs new file mode 100644 index 0000000..38b0b0e --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/PortSettingDialog.xaml.cs @@ -0,0 +1,38 @@ +using PCUT.ViewModels; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.IO.Ports; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + public sealed partial class PortSettingDialog : ContentDialog + { + private PortSettingViewModel ViewModel { get; set; } + + public PortSettingDialog(PortSettingViewModel viewModel) + { + this.InitializeComponent(); + ViewModel = viewModel; + } + + private void TextBox_BeforeTextChanging(TextBox sender, TextBoxBeforeTextChangingEventArgs args) + { + args.Cancel = args.NewText.Any(ch => !char.IsDigit(ch)); + } + } +} diff --git a/PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml b/PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml new file mode 100644 index 0000000..a66b0f3 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml.cs b/PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml.cs new file mode 100644 index 0000000..abf1234 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml.cs @@ -0,0 +1,425 @@ +using DeepNestLib; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using PCUT.Extensions; +using PCUT.DeepNestApi; +using PCUT.ViewModels; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Navigation; +using System.ComponentModel; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class SvgNestPage : Page + { + private SvgData _svgData => SvgData.Instance; + private SvgNestSettings _settings => SvgNestSettings.Instance; + private DesignCenterViewModel _designCenterModel; + public ImageViewModel PreviewModel { get; set; } + public NestingProgressModel ProgressModel { get; set; } + private ConcurrentQueue<(XDocument Svg, double Width, double Height, double MaterialUtilization)> NestedResult { get; set; } + = new ConcurrentQueue<(XDocument, double, double, double)>(); + public SvgNestPage() + { + this.InitializeComponent(); + PreviewModel = new ImageViewModel(); + ProgressModel = new NestingProgressModel(); + DeepNestApi.Background.UseParallel = true; + _ = _settings; + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + if (_svgData.Data == null) + return; + + _designCenterModel = e.Parameter as DesignCenterViewModel; + _designCenterModel.PropertyChanged += UpdateSizeChanged; + DeepNestApi.Background.displayProgress = (progress) => DispatchAsync(DisplayNestingProgress, progress); + _ = Initialize(); + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + //if (e.SourcePageType != typeof(EditPage)) + // _svgData.SetData(null); + StopNesting(); + _designCenterModel.PropertyChanged -= UpdateSizeChanged; + DeepNestApi.Background.displayProgress = null; + } + + private static readonly object _boundingSizeLock = new object(); + private void UpdateSizeChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(DesignCenterViewModel.Width)) + { + var model = sender as DesignCenterViewModel; + StopNesting(); + _svgData.SetResult(null); + _designCenterModel.MinSize = 0; + _ = HandleSizeChangedAsync(width: model.Width); + } + else if (e.PropertyName == nameof(DesignCenterViewModel.Height)) + { + var model = sender as DesignCenterViewModel; + StopNesting(); + _svgData.SetResult(null); + _designCenterModel.MinSize = 0; + _ = HandleSizeChangedAsync(height: model.Height); + } + } + + private async Task HandleSizeChangedAsync(double? width = null, double? height = null) + { + if (Monitor.TryEnter(_boundingSizeLock, 100)) + { + try + { + var bound = _svgData.Data.GetDescendantById("PCUT_BOUNDING"); + if (width != null) + { + bound.SetAttributeValue("width", width.Value); + } + if (height != null) + { + bound.SetAttributeValue("height", height.Value); + } + PreviewModel.ShowDefault(); + _currentResult = null; + _svgData.CleanUpContext(); + await PreviewModel.Source.LoadSvgAsync(_svgData.Data); + } + finally + { + Monitor.Exit(_boundingSizeLock); + } + } + } + + private void MainScroll_Loading(FrameworkElement sender, object args) + { + var borderRatio = PreviewColumnDefinition.ActualWidth / PreviewRowDefinition.ActualHeight; + var ratio = _svgData.OriginWidth.Value / _svgData.OriginHeight.Value; + if (ratio < borderRatio) + ((ComponentStyle)Resources["previewStyle"]).SetSize(PreviewRowDefinition, ratio); + else + ((ComponentStyle)Resources["previewStyle"]).SetSize(PreviewColumnDefinition, ratio); + } + + private void MainPreview_ImageOpened(object sender, RoutedEventArgs e) + { + HideProgressRing(); + } + + private void MainScroll_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + var (offsetX, offsetY) = e.Delta.CalculateTranslation(e.Velocities); + var scrollViewer = sender as ScrollViewer; + _ = scrollViewer.ChangeView(scrollViewer.HorizontalOffset - offsetX, scrollViewer.VerticalOffset - offsetY, scrollViewer.ZoomFactor); + } + + private void MainPreviewRefresh_Click(object sender, RoutedEventArgs e) + { + MainScroll.ChangeView(0, 0, 1); + } + + private void MainPreviewDefault_Click(object sender, RoutedEventArgs e) + { + PreviewModel.ShowDefault(); + MainScroll.ChangeView(0, 0, 1); + _designCenterModel.UseOrigin = true; + } + + private void NestStart_Click(object sender, RoutedEventArgs e) + { + _ = StartAsync(StartNesting); + } + + private void NestStop_Click(object sender, RoutedEventArgs e) + { + StopNesting(); + } + + private void NestResult_Click(object sender, RoutedEventArgs e) + { + ShowNestedResult(); + } + + private async void NestSettings_Click(object sender, RoutedEventArgs e) + { + var nestSettingDialog = new SvgNestSettingDialog(); + await nestSettingDialog.ShowAsync(); + } + + // private nesting logic + //private double _maxOfMinSize; + //private double _totalArea; + private async Task Initialize() + { + await StartAsync(LoadDetail); + _svgData.ComputeDetailBounds(); + PreviewModel.SetSource(_svgData.Data); + } + + private void LoadDetail() + { + if (_svgData.NestingContext == null) + { + if (_svgData.ComponentDetails == null || !_svgData.ComponentDetails.Any()) + { + var doc = new XDocument(_svgData.Data); + doc.SetSize(_svgData.OriginWidth.Value, _svgData.OriginHeight.Value); + doc.RemoveHidden(); + var (details, scale) = SvgParser.LoadSvg(doc, true); + foreach (var detail in details) + _svgData.AddDetail(detail); + _svgData.DetailScaleFactor = scale; + } + } + if (_svgData.NestedData != null) + { + DispatchAsync(UpdateCurrentSource); + } + } + + private List _sheets => _svgData.NestingContext.Sheets; + private List _polygons => _svgData.NestingContext.Polygons; + private void StartNesting() + { + DispatchAsync(ShowProgressRing); + + var requireStartNest = _svgData.NestingContext == null; + if (requireStartNest) + { + var totalArea = 0d; + var maxOfMinSize = 0d; + foreach (var (cWidth, cHeight) in _svgData.DetailBounds) + { + var minSize = Math.Ceiling(Math.Min(cWidth, cHeight)); + if (maxOfMinSize < minSize) + maxOfMinSize = minSize; + totalArea += cWidth * cHeight; + } + + var width = _designCenterModel.Width; + var height = _designCenterModel.Height; + + // check if width/height (which one is smaller) can accomodate the largest component, based on its smaller dimension. + if (width < height) + { + if (width < maxOfMinSize) + width = maxOfMinSize; + + if (totalArea > width * height) + height = Math.Ceiling(totalArea / width); + } + else + { + if (height < maxOfMinSize) + height = maxOfMinSize; + + if (totalArea > width * height) + width = Math.Ceiling(totalArea / height); + } + + _svgData.NewSheetInfo(width, height); + _svgData.NestingContext = new NestingContext(); + int src = 0; + if (_svgData.SheetInfo != null) + { + src = _svgData.NestingContext.GetNextSheetSource(); + for (int i = 0; i < _svgData.SheetInfo.Quantity; i++) + { + var ns = DeepNestApi.Background.clone(_svgData.SheetInfo.Nfp); + Sheet sheet = new Sheet(); + sheet.Points = ns.Points; + sheet.children = ns.children; + _sheets.Add(sheet); + sheet.Width = sheet.WidthCalculated; + sheet.Height = sheet.HeightCalculated; + sheet.source = src; + } + } + + _svgData.NestingContext.ReorderSheets(); + _svgData.DxfCache.Clear(); + + src = 0; + if (_svgData.ComponentDetails != null && _svgData.ComponentDetails.Any()) + { + foreach (var detail in _svgData.ComponentDetails) + { + var t1 = detail.Outers.Where(z => z.Tag is object[]).Select(z => z.Tag as object[]).SelectMany(z => z).ToArray(); + foreach (var outter in detail.Outers) + { + t1 = t1.Union(outter.Childrens.Where(z => z.Tag is object[]).Select(z => z.Tag as object[]).SelectMany(z => z).ToArray()).ToArray(); + } + _svgData.DxfCache.Add(detail.Name, t1); + } + foreach (var detail in _svgData.ComponentDetails) + { + _svgData.NestingContext.ImportFromRawDetail(detail, src); + src++; + } + } + } + + if (_sheets.Count == 0 || _polygons.Count == 0) + { + DispatchAsync(HideProgressRing); + DispatchAsync(StopNesting); + return; + } + + //menu.SetTab(menu.NestTab); + _svgData.NestingContext.ReorderSheets(); + RunDeepnest(_settings.MaxNumberOfRun, requireStartNest); + } + + private ImageSource _currentResult; + + private readonly ConcurrentQueue _runQueue = new ConcurrentQueue(); + private static readonly object _runCommand = new object(); + private void RunDeepnest(int maxNumberOfRun, bool requiredStartNest) + { + while (maxNumberOfRun > 0) + { + _runQueue.Enqueue(_runCommand); + maxNumberOfRun--; + } + DispatchAsync(HideProgressRing); + DispatchAsync(() => ProgressModel.Running = true); + if (requiredStartNest) + _svgData.NestingContext.StartNest(); + + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(_settings.MaxRuntimeInMinute)); + while (!cancellationTokenSource.Token.IsCancellationRequested) + { + if (!_runQueue.TryDequeue(out _)) + break; + + double materialUtilization = _svgData.MaterialUtilization; + DispatchAsync(DisplayNestingProgress, 0.0d); + _svgData.NestingContext.NestIterate(); + DispatchAsync(DisplayNestingProgress, 1.0d); + + if (_polygons.Count(z => z.fitted) == _svgData.DetailCount && _svgData.NestingContext.MaterialUtilization <= materialUtilization) + { + materialUtilization = _svgData.NestingContext.MaterialUtilization; + var (doc, width, height) = Export(); + NestedResult.Enqueue((doc, width, height, materialUtilization)); + DispatchAsync(UpdatePreview); + } + } + + if (ProgressModel?.Running ?? false) + DispatchAsync(StopNesting); + } + + private void DisplayNestingProgress(double progress) + { + if (ProgressModel != null) + ProgressModel.Progress = Math.Floor(progress * 100); + } + + private void UpdatePreview() + { + if (NestedResult.TryDequeue(out var result)) + { + _svgData.SetResult(result.Svg, result.Width, result.Height, result.MaterialUtilization); + if (ProgressModel?.Running ?? false) + { + var minSize = Math.Min(_svgData.SheetInfo.Width, _svgData.SheetInfo.Height); + if (minSize > Math.Min(_designCenterModel.Width, _designCenterModel.Height)) + _designCenterModel.MinSize = Math.Ceiling(minSize); + else + _designCenterModel.MinSize = 0; + + var switchToResult = _currentResult == null && PreviewModel.DefaultMode; + UpdateCurrentSource(); + if (switchToResult) + ShowNestedResult(); + } + } + } + + private void UpdateCurrentSource() + { + if (_currentResult == null) + _currentResult = new SvgImageSource(); + _ = _currentResult.LoadSvgAsync(_svgData.NestedData); + } + + private void ShowNestedResult() + { + PreviewModel.SetSource(_currentResult); + _designCenterModel.UseOrigin = false; + } + + private void StopNesting() + { + if (ProgressModel != null) + ProgressModel.Running = false; + _runQueue.Clear(); + } + + private void ShowProgressRing() + { + LoadingRing.IsActive = true; + LoadingRing.Visibility = Visibility.Visible; + } + + private void HideProgressRing() + { + LoadingRing.IsActive = false; + LoadingRing.Visibility = Visibility.Collapsed; + } + + private (XDocument NestedDoc, double Width, double Height) Export() + { + var polygons = _polygons.Where(x => x.fitted).ToArray(); + foreach (var item in polygons) + { + item.Tag = _svgData.DxfCache[item.Name]; + } + var (nestedDoc, nestedWidth, nestedHeight) = SvgParser.Export(polygons); + var width = _svgData.SheetInfo.Width; + var height = _svgData.SheetInfo.Height; + var viewBox = _svgData.OriginViewBox.GetViewBoxData(); + nestedDoc.Root.SetAttributeValue("viewBox", $"{viewBox.MinX - 100} {viewBox.MinY - 100} {nestedWidth + 100} {nestedHeight + 100}"); + nestedDoc.Root.Add(XElement.Parse($"")); + nestedDoc.Root.Add(XElement.Parse($"")); + return (nestedDoc, nestedWidth, nestedHeight); + } + + private Task StartAsync(Action action) + { + return Task.Factory.StartNew(action, TaskCreationOptions.LongRunning); + } + + private async void DispatchAsync(Action action) + { + await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => action.Invoke()); + } + + private async void DispatchAsync(Action action, T param) + { + await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => action.Invoke(param)); + } + } +} diff --git a/PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml b/PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml new file mode 100644 index 0000000..50186a8 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml.cs b/PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml.cs new file mode 100644 index 0000000..3db0115 --- /dev/null +++ b/PCUT/PCUT/Pages/DesignCenter/SvgNestSettingDialog.xaml.cs @@ -0,0 +1,41 @@ +using PCUT.ViewModels; +using System.Linq; +using Windows.UI.Xaml.Controls; + +// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + public sealed partial class SvgNestSettingDialog : ContentDialog + { + public SvgNestSettings NestSettings { get; set; } + + public SvgNestSettingDialog() + { + this.InitializeComponent(); + NestSettings = SvgNestSettings.Instance; + } + + private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + NestSettings.SaveSettings(); + NestSettings.ApplySettings(); + } + + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + NestSettings.LoadSettings(); + NestSettings.ApplySettings(); + } + + private void TextBox_ValidateInt(TextBox sender, TextBoxBeforeTextChangingEventArgs args) + { + args.Cancel = args.NewText.Any(ch => !char.IsDigit(ch)); + } + + private void TextBox_ValidateDouble(TextBox sender, TextBoxBeforeTextChangingEventArgs args) + { + args.Cancel = args.NewText.Any(ch => !char.IsDigit(ch) && ch != '.'); + } + } +} diff --git a/PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml b/PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml new file mode 100644 index 0000000..bbd565d --- /dev/null +++ b/PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml.cs b/PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml.cs new file mode 100644 index 0000000..342303b --- /dev/null +++ b/PCUT/PCUT/Pages/FileManagement/FileAddDialog.xaml.cs @@ -0,0 +1,78 @@ +using PCUT.Extensions; +using PCUT.Factories; +using PCUT.ViewModels; +using System; +using System.IO; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Storage; +using Windows.Storage.Pickers; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Imaging; + +// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + public sealed partial class FileAddDialog : ContentDialog + { + private StorageFile _file; + private FileAddViewModel ViewModel { get; set; } + public FileAddDialog() + { + this.InitializeComponent(); + ViewModel = new FileAddViewModel(); + this.DataContext = ViewModel; + } + + private async void ContentDialog_Loaded(object sender, RoutedEventArgs e) + { + ViewModel.MetadataModel.BindAll(Type, Brand, Series, Model, SubType, Year); + ViewModel.Categories.Bind(Category); + + await ViewModel.GetCategoriesAsync(); + await ViewModel.MetadataModel.LoadAllMetadataAsync(); + } + + private void Category_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + ViewModel.MetadataModel.UnselectAll(); + } + + private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + var deferral = args.GetDeferral(); + var success = await ViewModel.AddFileAsync(_file, ThumbnailPreview); + args.Cancel = !success; + deferral.Complete(); + } + + private async void UploadFile_CLick(object sender, RoutedEventArgs e) + { + + FileOpenPicker openPicker = new FileOpenPicker + { + ViewMode = PickerViewMode.List, + SuggestedStartLocation = PickerLocationId.DocumentsLibrary + }; + openPicker.FileTypeFilter.Add(".svg"); + _file = await openPicker.PickSingleFileAsync(); + + if (_file == null) + { + var noFileDialog = new MessageDialog("No file was selected"); + await noFileDialog.ShowAsync(); + return; + } + + using (var stream = await _file.OpenReadAsync()) + { + var (doc, _, _) = await FileFactory.PreProcessSvgAsync(stream.AsStream()); + var source = new SvgImageSource(); + ViewModel.PreviewSource = source; + _ = source.LoadSvgAsync(doc); + } + } + } +} diff --git a/PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml b/PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml new file mode 100644 index 0000000..8920b84 --- /dev/null +++ b/PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml.cs b/PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml.cs new file mode 100644 index 0000000..a98daf9 --- /dev/null +++ b/PCUT/PCUT/Pages/FileManagement/FileEditDialog.xaml.cs @@ -0,0 +1,95 @@ +using PCUT.Entities; +using PCUT.Extensions; +using PCUT.Factories; +using PCUT.ViewModels; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Storage; +using Windows.Storage.Pickers; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Navigation; +using static System.Net.WebRequestMethods; + +// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + public sealed partial class FileEditDialog : ContentDialog + { + private FileEditViewModel ViewModel { get; set; } + private StorageFile _file; + private readonly string _id; + public FileEditDialog(string id) + { + this.InitializeComponent(); + ViewModel = new FileEditViewModel(); + + _id = id; + + this.DataContext = ViewModel; + } + + private async void ContentDialog_Loaded(object sender, RoutedEventArgs e) + { + ViewModel.MetadataModel.BindAll(Type, Brand, Series, Model, SubType, Year); + ViewModel.Categories.Bind(Category); + + await ViewModel.GetCategoriesAsync(); + await ViewModel.MetadataModel.LoadAllMetadataAsync(); + + await ViewModel.LoadFileData(_id); + } + + private void Category_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + //ViewModel.MetadataModel.UnselectAll(); + } + + private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + var deferral = args.GetDeferral(); + var success = await ViewModel.UpdateFileAsync(_id, _file, ThumbnailPreview); + args.Cancel = !success; + deferral.Complete(); + } + + private async void UploadFile_CLick(object sender, RoutedEventArgs e) + { + FileOpenPicker openPicker = new FileOpenPicker + { + ViewMode = PickerViewMode.List, + SuggestedStartLocation = PickerLocationId.DocumentsLibrary + }; + openPicker.FileTypeFilter.Add(".svg"); + _file = await openPicker.PickSingleFileAsync(); + + if (_file == null) + { + var noFileDialog = new MessageDialog("No file was selected"); + await noFileDialog.ShowAsync(); + return; + } + + using (var stream = await _file.OpenReadAsync()) + { + var (doc, _, _) = await FileFactory.PreProcessSvgAsync(stream.AsStream()); + var source = new SvgImageSource(); + ViewModel.PreviewSource = source; + _ = source.LoadSvgAsync(doc); + } + } + } +} diff --git a/PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml b/PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml new file mode 100644 index 0000000..84a449e --- /dev/null +++ b/PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml.cs b/PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml.cs new file mode 100644 index 0000000..a63de8c --- /dev/null +++ b/PCUT/PCUT/Pages/FileManagement/FileManagerPage.xaml.cs @@ -0,0 +1,218 @@ +using Http.Core; +using Microsoft.Toolkit; +using PCUT.Entities; +using PCUT.ViewModels; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Storage; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using static PCUT.App; +using Windows.UI.Core; +using System.Threading; +using PCUT.Models; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class FileManagerPage : Page + { + private FileListViewModel ViewModels { get; set; } + public FileManagerPage() + { + this.InitializeComponent(); + ViewModels = DataContext as FileListViewModel; + + ViewModels.SetFilterMetadataOnFileChange(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + ViewModels.Categories = new SelectableCollection(new Category { Name = "-None-" }); + base.OnNavigatedTo(e); + } + + private async void Page_Loaded(object sender, RoutedEventArgs e) + { + ViewModels.MetadataModel.BindAll(Type, Brand, null, null, null, Year); + ViewModels.Categories.Bind(Categories); + + await ViewModels.GetCategoriesAsync(); + await ViewModels.MetadataModel.LoadAllMetadataAsync(); + ViewModels.Pagination.Page = 1; + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + ViewModels.Pagination.Dispose(); + } + + private async void AddFile_Click(object sender, RoutedEventArgs e) + { + var dialog = new FileAddDialog(); + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + await ViewModels.Reload(); + } + + private async void EditFile_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.CommandParameter is string fileId) + { + if (CanEditFile()) + { + var dialog = new FileEditDialog(fileId); + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + await ViewModels.Reload(); + _editFileFlag = false; + } + } + } + + private static readonly object _editFileLock = new object(); + private static bool _editFileFlag = false; + private bool CanEditFile() + { + if (!Monitor.TryEnter(_editFileLock)) + return false; + + try + { + if (_editFileFlag) + return false; + + _editFileFlag = true; + return true; + } + finally + { + Monitor.Exit(_editFileLock); + } + } + + private async void DeleteFile_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.CommandParameter is string fileId) + { + if (CanDeleteFile()) + { + //Confirm diaglog before delete + ContentDialog deleteDialog = new ContentDialog + { + Title = "Confirm Delete?", + Content = "Are you sure want delete this file?", + PrimaryButtonText = "Delete", + CloseButtonText = "Cancel" + }; + deleteDialog.PrimaryButtonStyle = Application.Current.Resources["PrimaryDialogButton"] as Style; + deleteDialog.CloseButtonStyle = Application.Current.Resources["CloseDialogButton"] as Style; + + var result = await deleteDialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + await DeleteFileAsync(fileId); + } + _deleteFileFlag = false; + } + } + } + + private static readonly object _deleteFileLock = new object(); + private static bool _deleteFileFlag = false; + private bool CanDeleteFile() + { + if (!Monitor.TryEnter(_deleteFileLock)) + return false; + + try + { + if (_deleteFileFlag) + return false; + + _deleteFileFlag = true; + return true; + } + finally + { + Monitor.Exit(_deleteFileLock); + } + } + + private async Task DeleteFileAsync(string fileId) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.DeleteAsync(Api.CdrFileById.FormatRoute(fileId)); + if (response.IsSuccessStatusCode) + { + await ShowDialog("Delete Success", "File Deleted!"); + await ViewModels.Reload(); + } + else + { + await ShowDialog("Error!", "Cant delete file"); + } + } + catch (Exception ex) + { + await ShowDialog("Error!", $"Got exception: {ex.Message}"); + } + } + } + + + private async Task ShowDialog(string title, string content) + { + ContentDialog dialog = new ContentDialog + { + Title = title, + Content = content, + CloseButtonText = "OK" + }; + dialog.CloseButtonStyle = Application.Current.Resources["CloseDialogButton"] as Style; + + await dialog.ShowAsync(); + } + + private void btnFirst_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page = 1; + } + + private void btnLast_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page = ViewModels.Pagination.TotalPage; + } + + private void btnPrevious_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page--; + } + + private void btnNext_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page++; + } + } +} diff --git a/PCUT/PCUT/Pages/LoginPage.xaml b/PCUT/PCUT/Pages/LoginPage.xaml new file mode 100644 index 0000000..c466a8a --- /dev/null +++ b/PCUT/PCUT/Pages/LoginPage.xaml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/MainMenuPage.xaml.cs b/PCUT/PCUT/Pages/MainMenuPage.xaml.cs new file mode 100644 index 0000000..92698e4 --- /dev/null +++ b/PCUT/PCUT/Pages/MainMenuPage.xaml.cs @@ -0,0 +1,156 @@ +using Http.Core; +using PCUT.ViewModels; +using System; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Navigation; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using Windows.Graphics.Display; +using Http.Core.Contexts; +using PCUT.Pages.UserGuide; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MainMenuPage : Page + { + private MainMenuPageViewModel ViewModel { get; set; } + public MainMenuPage() + { + this.InitializeComponent(); + ViewModel = DataContext as MainMenuPageViewModel; + } + + private void Page_Loading(FrameworkElement sender, object args) + { + var view = ApplicationView.GetForCurrentView(); + var height = DisplayInformation.GetForCurrentView().ScreenHeightInRawPixels; + var width = DisplayInformation.GetForCurrentView().ScreenWidthInRawPixels; + var size = new Size(width - 15 - 1, height - 40 - 1); + _ = view.TryResizeView(size); + ViewModel.UserName = UserContext.Instance.Profile.UserName; + ViewModel.Expiry = UserContext.Instance.Profile.ExpiredAt?.ToLocalTime().ToString("dd/MM/yyyy"); + } + + private async void Page_Loaded(object sender, RoutedEventArgs e) + { + ViewModel.Categories.Bind(CategoryList); + await ViewModel.GetCategoriesAsync(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + NavigateContentFrame(ViewModel); + } + + private void NavigationViewItem_Invoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) + { + var item = args.InvokedItemContainer as NavigationViewItem; + var itemTag = item.Tag?.ToString(); + if (itemTag == "Menu") + ViewModel.ToggleMenuDisplay(); + else + { + ViewModel.HideMenuDisplay(); + switch (itemTag) + { + case "DataCenter": + NavigateContentFrame(ViewModel); + break; + case "DesignCenter": + NavigateContentFrame(); + break; + case "UserGuide": + NavigateContentFrame(); + break; + case "AdminCenter": + NavigateContentFrame(); + break; + } + } + } + + private void NavigateContentFrame(object param = null) + { + var destPageType = typeof(TPage); + if (ContentFrame.Content?.GetType() != destPageType) + { + ViewModel.IsMenuEnabled = destPageType == typeof(DataCenterPage); + ContentFrame.Navigate(destPageType, param); + } + } + + private void CategoryList_Tapped(object sender, TappedRoutedEventArgs e) + { + ViewModel.Categories.SelectedIndex = CategoryList.SelectedIndex; + } + + private void LogOutNavItem_Tapped(object sender, TappedRoutedEventArgs e) + { + ShowLogoutConfirmationDialog(); + } + + private async void ShowLogoutConfirmationDialog() + { + ContentDialog logoutConfirmationDialog = new ContentDialog + { + Title = "Confirm Logout", + Content = "Are you sure you want to log out?", + PrimaryButtonText = "Log Out", + CloseButtonText = "Cancel" + }; + logoutConfirmationDialog.PrimaryButtonStyle = Application.Current.Resources["PrimaryDialogButton"] as Style; + logoutConfirmationDialog.CloseButtonStyle = Application.Current.Resources["CloseDialogButton"] as Style; + + ContentDialogResult result = await logoutConfirmationDialog.ShowAsync(); + + if (result == ContentDialogResult.Primary) + { + await PerformLogoutAsync(); + } + } + + private async Task PerformLogoutAsync() + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var loginSuccess = await httpClient.LogOutAsync(); + } + Frame.Navigate(typeof(LoginPage)); + } + + private void ContentFrame_Navigated(object sender, NavigationEventArgs e) + { + if (e.SourcePageType == typeof(DesignCenterPage)) + { + NavigationView.SelectedItem = DesignCenterNavItem; + } + else if (e.SourcePageType == typeof(DataCenterPage)) + { + NavigationView.SelectedItem = DataCenterNavItem; + } + else if (e.SourcePageType == typeof(UserGuidePage)) + { + NavigationView.SelectedItem = UserGuideNavItem; + } + else if (e.SourcePageType == typeof(AdminCenterPage)) + { + NavigationView.SelectedItem = AdminCenterNavItem; + } + else + { + NavigationView.SelectedItem = null; + } + } + } +} diff --git a/PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml b/PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml new file mode 100644 index 0000000..7063bec --- /dev/null +++ b/PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml.cs b/PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml.cs new file mode 100644 index 0000000..28372c1 --- /dev/null +++ b/PCUT/PCUT/Pages/UserGuide/UserGuidePage.xaml.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages.UserGuide +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class UserGuidePage : Page + { + public UserGuidePage() + { + this.InitializeComponent(); + } + } +} diff --git a/PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml b/PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml new file mode 100644 index 0000000..50cc0e8 --- /dev/null +++ b/PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml.cs b/PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml.cs new file mode 100644 index 0000000..d1747a8 --- /dev/null +++ b/PCUT/PCUT/Pages/UserManagement/ChangePasswordDialog.xaml.cs @@ -0,0 +1,73 @@ +using PCUT.ViewModels; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + public sealed partial class ChangePasswordDialog : ContentDialog + { + private ChangePasswordViewModel ViewModel { get; set; } + public ChangePasswordDialog(string userId) + { + this.InitializeComponent(); + ViewModel = new ChangePasswordViewModel(); + ViewModel.UserId = userId; + this.DataContext = ViewModel; + } + + private async void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + var confirmDialog = new MessageDialog("Are you sure you want to save the new password?"); + confirmDialog.Commands.Add(new UICommand("Yes") { Id = 1 }); + confirmDialog.Commands.Add(new UICommand("No") { Id = 0 }); + confirmDialog.DefaultCommandIndex = 0; + confirmDialog.CancelCommandIndex = 1; + + var result = await confirmDialog.ShowAsync(); + + if ((int)result.Id == 1) + { + await ViewModel.ChangePassword(); + } + else + { + this.Hide(); + } + } + + private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + var confirmDialog = new MessageDialog("Are you sure you want to reset?"); + confirmDialog.Commands.Add(new UICommand("Yes") { Id = 1 }); + confirmDialog.Commands.Add(new UICommand("No") { Id = 0 }); + confirmDialog.DefaultCommandIndex = 0; + confirmDialog.CancelCommandIndex = 1; + + var result = await confirmDialog.ShowAsync(); + + if ((int)result.Id == 1) + { + await ViewModel.ChangeDefaultPassword(); + } + else + { + this.Hide(); + } + } + } +} diff --git a/PCUT/PCUT/Pages/UserManagement/UpsertUserPage.xaml b/PCUT/PCUT/Pages/UserManagement/UpsertUserPage.xaml new file mode 100644 index 0000000..f571bd7 --- /dev/null +++ b/PCUT/PCUT/Pages/UserManagement/UpsertUserPage.xaml @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Allow Custom Paper Size: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCUT/PCUT/Pages/UserManagement/UserListPage.xaml.cs b/PCUT/PCUT/Pages/UserManagement/UserListPage.xaml.cs new file mode 100644 index 0000000..6049743 --- /dev/null +++ b/PCUT/PCUT/Pages/UserManagement/UserListPage.xaml.cs @@ -0,0 +1,176 @@ +using PCUT.ViewModels; +using System; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; +using Http.Core; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace PCUT.Pages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class UserListPage : Page + { + private UserViewModel ViewModels { get; set; } + public UserListPage() + { + this.InitializeComponent(); + ViewModels = DataContext as UserViewModel; + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + ViewModels.Pagination.Page = 1; + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + ViewModels.Pagination.Dispose(); + } + + private void AddButton_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button) + { + button.IsEnabled = false; + Frame.Navigate(typeof(UpsertUserPage)); + } + } + + private void EditButton_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.CommandParameter is string userId) + { + button.IsEnabled = false; + if (!string.IsNullOrEmpty(userId)) + Frame.Navigate(typeof(UpsertUserPage), userId); + else + button.IsEnabled = true; + } + } + + private async void PasswordButton_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.CommandParameter is string userId) + { + if (!string.IsNullOrEmpty(userId)) + { + var dialog = new ChangePasswordDialog(userId); + await dialog.ShowAsync(); + } + } + } + + private async void DeleteButton_Click(object sender, RoutedEventArgs e) + { + if(sender is Button button && button.CommandParameter is string userId) + { + //Confirm diaglog before delete + ContentDialog deleteDialog = new ContentDialog + { + Title = "Confirm Delete?", + Content = "Are you sure want delete this user?", + PrimaryButtonText = "Delete", + CloseButtonText = "Cancel" + }; + deleteDialog.PrimaryButtonStyle = Application.Current.Resources["PrimaryDialogButton"] as Style; + deleteDialog.CloseButtonStyle = Application.Current.Resources["CloseDialogButton"] as Style; + + var result = await deleteDialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + await DeleteUserAsync(userId); + } + } + } + + private async Task DeleteUserAsync(string userId) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.DeleteAsync(Api.UserById.FormatRoute(userId)); + if (response.IsSuccessStatusCode) + { + await ShowDialog("Delete Success", "User Deleted!"); + await ViewModels.Reload(); + } + else + { + await ShowDialog("Error!", "Cant delete user"); + } + } + catch (Exception ex) + { + await ShowDialog("Error!", $"Got exception: {ex.Message}"); + } + } + } + + private async void UnlockDevice_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.CommandParameter is string userId) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.DeleteAsync(Api.UserUnlockDevice.FormatRoute(userId)); + if (response.IsSuccessStatusCode) + { + await ShowDialog("Device Unlock Success", "User's device unlocked!"); + } + else + { + await ShowDialog("Error!", "Failed to unlock user's device"); + } + } + catch (Exception ex) + { + await ShowDialog("Error!", $"Got exception: {ex.Message}"); + } + } + } + } + + private async Task ShowDialog(string title, string content) + { + ContentDialog dialog = new ContentDialog + { + Title = title, + Content = content, + CloseButtonText = "OK" + }; + dialog.CloseButtonStyle = Application.Current.Resources["CloseDialogButton"] as Style; + + await dialog.ShowAsync(); + } + + private void btnFirst_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page = 1; + } + + private void btnLast_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page = ViewModels.Pagination.TotalPage; + } + + private void btnPrevious_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page--; + } + + private void btnNext_Click(object sender, RoutedEventArgs e) + { + ViewModels.Pagination.Page++; + } + } +} diff --git a/PCUT/PCUT/Properties/AssemblyInfo.cs b/PCUT/PCUT/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..54a4a95 --- /dev/null +++ b/PCUT/PCUT/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PCUT")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PCUT")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/PCUT/PCUT/Properties/Default.rd.xml b/PCUT/PCUT/Properties/Default.rd.xml new file mode 100644 index 0000000..af00722 --- /dev/null +++ b/PCUT/PCUT/Properties/Default.rd.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/PCUT/PCUT/ViewModels/CategoriesViewModel.cs b/PCUT/PCUT/ViewModels/CategoriesViewModel.cs new file mode 100644 index 0000000..8e07dd1 --- /dev/null +++ b/PCUT/PCUT/ViewModels/CategoriesViewModel.cs @@ -0,0 +1,127 @@ +using PCUT.Entities; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Http.Core; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using PCUT.Entities.ApiResponse; +using PCUT.Models.Categories; +using Windows.ApplicationModel.Core; + +namespace PCUT.ViewModels +{ + public class CategoriesViewModel : NotificationBase + { + public PaginationViewModel Pagination { get; } = new PaginationViewModel(withSearch: true); + public SelectableCollection FilteredCategories { get; } + public CategoriesViewModel() + { + FilteredCategories = new SelectableCollection(); + + Pagination.TextSearched += async (sender, args) => await SearchCategoryAsync(args.Text); + Pagination.PageChanged += OnPageChanged; + } + + private async void OnPageChanged(object sender, PageChangedEventArgs args) + { + await LoadCategoryAsync(args.Page, args.PageSize, Pagination.SearchText); + } + + public async Task SearchCategoryAsync(string searchText) + { + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => + { + await LoadCategoryAsync(Pagination.Page, Pagination.PageSize, searchText); + }); + } + + public async Task Reload() + { + await LoadCategoryAsync(Pagination.Page, Pagination.PageSize, Pagination.SearchText); + } + + public async Task LoadCategoryAsync(int page, int pageSize, string searchText) + { + var url = CreateFilterUrl(page, pageSize, searchText); + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var apiResponse = await response.DeserializeObjectAsync>>(); + var data = apiResponse.Data; + Pagination.Page = apiResponse.Pagination.Page; + Pagination.TotalPage = apiResponse.Pagination.TotalPages; + Pagination.TotalRecords = apiResponse.Pagination.TotalRecords; + + int displayId = (apiResponse.Pagination.Page - 1) * pageSize + 1; + var categories = data.Select(category => + { + category.DisplayId = displayId.ToString(); + displayId++; + return category; + }); + FilteredCategories.Load(categories); + } + } + catch (Exception ex) + { + + } + } + } + + public async Task LoadMetadataAsync(MetadataTypeModel metadataType, bool reload = false) + { + if (reload || !metadataType.IsLoaded) + { + metadataType.IsLoaded = true; + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var url = PathBuilder.FromRouteTemplate(Api.Metadata) + .AddQuery("filter", $"[\"type:eq:{metadataType.Name.ToUpper()}\", \"categoryId:eq:{metadataType.Category.Id}\"]") + .AddQuery("sort", "createdAt:desc") + .Build(); + var response = await client.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var content = await response.DeserializeObjectAsync>>(); + metadataType.Metadata.Clear(); + var id = 0; + foreach (var model in content.Data) + { + model.DisplayId = ++id; + model.MetadataType = metadataType; + metadataType.Metadata.Add(model); + } + } + } + catch + { + } + } + } + } + + private string CreateFilterUrl(int page, int pageSize, string searchText) + { + var builder = PathBuilder.FromRouteTemplate(Api.Categories) + .AddQuery("page", page.ToString()) + .AddQuery("pageSize", pageSize.ToString()); + if (!string.IsNullOrEmpty(searchText)) + { + var filter = new StringBuilder().AppendFilter("name", "cn", searchText).BuildFilter().ToString(); + builder.AddQuery("filter", filter); + } + return builder.Build(); + } + } +} diff --git a/PCUT/PCUT/ViewModels/ChangePasswordViewModel.cs b/PCUT/PCUT/ViewModels/ChangePasswordViewModel.cs new file mode 100644 index 0000000..3d68a06 --- /dev/null +++ b/PCUT/PCUT/ViewModels/ChangePasswordViewModel.cs @@ -0,0 +1,157 @@ +using Newtonsoft.Json; +using PCUT.Entities; +using PCUT.Helpers; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Net.Http; +using System.ServiceModel.Channels; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; +using Windows.Storage; +using Windows.UI.Popups; +using Http.Core; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using Microsoft.IdentityModel.Tokens; +using PCUT.Models.Users; + +namespace PCUT.ViewModels +{ + public class ChangePasswordViewModel : NotificationBase + { + + public ICommand ChangePasswordCommand { get; private set; } + public event PropertyChangedEventHandler PropertyChanged; + public ObservableCollection PasswordChanges { get; private set; } + private ObservableCollection _users; + public ObservableCollection Users + { + get { return _users; } + set + { + if (_users != value) + { + _users = value; + OnPropertyChanged(nameof(Users)); + } + } + } + protected void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private string _userId; + public string UserId + { + get { return _userId; } + set { SetProperty(ref _userId, value); } + } + private string _password; + public string Password + { + get { return _password; } + set + { + SetProperty(ref _password, value); + } + } + private string _confirmpassword; + public string ConfirmPassword + { + get { return _confirmpassword; } + set + { + SetProperty(ref _confirmpassword, value); + } + } + + public ChangePasswordViewModel() + { + ChangePasswordCommand = new RelayCommand(async () => await ChangePassword()); + PasswordChanges = new ObservableCollection(); + } + + public async Task ChangePassword() + { + if (Password != ConfirmPassword) + { + var errorDialog = new MessageDialog("Confirm Password is incorrect!"); + await errorDialog.ShowAsync(); + return; + } + + if (string.IsNullOrWhiteSpace(Password) || string.IsNullOrWhiteSpace(ConfirmPassword)) + { + var errorDialog = new MessageDialog("Password and Confirm Password cannot be empty!"); + await errorDialog.ShowAsync(); + return; + } + else + { + try + { + var passwordChange = new PasswordChange + { + Password = this.Password, + }; + await ChangePasswordAsync(UserId, passwordChange); + } + catch (Exception ex) + { + var failDialog = new MessageDialog(ex.Message.ToString()); + await failDialog.ShowAsync(); + } + } + } + + public async Task ChangeDefaultPassword() + { + try + { + var passwordChange = new PasswordChange + { + Password = "123456", + }; + await ChangePasswordAsync(UserId, passwordChange); + } + catch (Exception ex) + { + var failDialog = new MessageDialog(ex.Message.ToString()); + await failDialog.ShowAsync(); + } + } + + private async Task ChangePasswordAsync(string userId, PasswordChange password) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.PutAsJsonAsync(Api.UserById.FormatRoute(userId), password); + if (response.IsSuccessStatusCode) + { + var successDialog = new MessageDialog("Password changed successfully!"); + await successDialog.ShowAsync(); + } + else + { + var failDialog = new MessageDialog("Failed to change password!"); + await failDialog.ShowAsync(); + } + } + catch (Exception ex) + { + var errorDialog = new MessageDialog($"error: {ex.Message}"); + await errorDialog.ShowAsync(); + } + } + } + + } +} diff --git a/PCUT/PCUT/ViewModels/ComponentImageModel.cs b/PCUT/PCUT/ViewModels/ComponentImageModel.cs new file mode 100644 index 0000000..71e493f --- /dev/null +++ b/PCUT/PCUT/ViewModels/ComponentImageModel.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Xml.Linq; +using PCUT.Extensions; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; + +namespace PCUT.ViewModels +{ + public class ComponentImageModel + { + public ObservableCollection ComponentList { get; } = new ObservableCollection(); + + public void AddSource(string id, bool deleted, XDocument doc) + { + var source = new SvgImageSource(); + ComponentList.Add(new ComponentImageSource { Id = id, Deleted = deleted, Source = source }); + _ = source.LoadSvgAsync(doc); + } + } + + public class ComponentImageSource : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public ImageSource Source { get; set; } + public string Id { get; set; } + + private bool _deleted; + public bool Deleted + { + get => _deleted; + set + { + _deleted = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Deleted))); + } + } + } + + public class ComponentStyle : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + + private double _width; + public double Width + { + get => _width; + set + { + _width = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Width))); + } + } + + private double _height; + public double Height + { + get => _height; + set + { + _height = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Height))); + } + } + + public void SetSize(RowDefinition rowDefinition, ColumnDefinition columnDefinition) + { + Height = rowDefinition.ActualHeight; + Width = columnDefinition.ActualWidth; + } + + public void SetSize(RowDefinition rowDefinition) + { + Width = rowDefinition.ActualHeight; + Height = rowDefinition.ActualHeight; + } + + public void SetSize(RowDefinition rowDefinition, double ratio) + { + Width = rowDefinition.ActualHeight * ratio; + Height = rowDefinition.ActualHeight; + } + + public void SetSize(ColumnDefinition columnDefinition, int rate = 1) + { + Width = rate <= 1 ? columnDefinition.ActualWidth : Math.Floor((columnDefinition.ActualWidth - 25) / rate); + Height = rate <= 1 ? columnDefinition.ActualWidth : Math.Floor((columnDefinition.ActualWidth - 25) / rate); + } + + public void SetSize(ColumnDefinition columnDefinition, double ratio) + { + Width = columnDefinition.ActualWidth; + Height = columnDefinition.ActualWidth / ratio; + } + } +} diff --git a/PCUT/PCUT/ViewModels/CutFilesViewModel.cs b/PCUT/PCUT/ViewModels/CutFilesViewModel.cs new file mode 100644 index 0000000..1a7ef2d --- /dev/null +++ b/PCUT/PCUT/ViewModels/CutFilesViewModel.cs @@ -0,0 +1,64 @@ + using Microsoft.Toolkit.Collections; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.ViewModels +{ + internal class CutFilesViewModel : NotificationBase + { + private ObservableCollection _ports; + public ObservableCollection Ports + { + get { return _ports; } + set + { + SetProperty(ref _ports, value); + } + } + + private double _width; + public double Width + { + get { return _width; } + set + { + SetProperty(ref _width, value); + } + } + + private double _height; + public double Height + { + get { return _height; } + set + { + SetProperty(ref _height, value); + } + } + + private string _selectedPortName; + public string SelectedPortName + { + get { return _selectedPortName; } + set + { + if (_selectedPortName != value) + { + SetProperty(ref _selectedPortName, value); + } + } + } + + + public CutFilesViewModel() + { + + } + } +} diff --git a/PCUT/PCUT/ViewModels/DesignCenterViewModel.cs b/PCUT/PCUT/ViewModels/DesignCenterViewModel.cs new file mode 100644 index 0000000..5466b25 --- /dev/null +++ b/PCUT/PCUT/ViewModels/DesignCenterViewModel.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Http.Core; +using Http.Core.Extensions; +using Newtonsoft.Json; +using PCUT.Entities; +using PCUT.Entities.ApiResponse; +using PCUT.Models; +using static Http.Core.Constants.HttpConstants; + +namespace PCUT.ViewModels +{ + public class DesignCenterViewModel : NotificationBase + { + public ObservableCollection Ports { get; set; } + public ObservableCollection PaperSizes { get; } + + private double _width; + public double Width + { + get { return _width; } + set + { + SetProperty(ref _width, value); + } + } + + private double _height; + public double Height + { + get { return _height; } + set + { + SetProperty(ref _height, value); + } + } + + private int _selectedHeightIndex = -1; + public int SelectedHeightIndex + { + get { return _selectedHeightIndex; } + set + { + if (_selectedHeightIndex != value && value >= 0 && value < PaperSizes.Count) + { + SetProperty(ref _selectedHeightIndex, value); + Width = PaperSizes[value].Width; + Height = PaperSizes[value].Height; + } + } + } + + private int _selectedPortIndex = -1; + public int SelectedPortIndex + { + get { return _selectedPortIndex; } + set + { + if (_selectedPortIndex != value) + { + SetProperty(ref _selectedPortIndex, value); + } + } + } + + public string SelectedPortName => Ports.Any() && _selectedPortIndex >= 0 && _selectedPortIndex < Ports.Count ? Ports[_selectedPortIndex].PortName : null; + public string SelectedDeviceName => Ports.Any() && _selectedPortIndex >= 0 && _selectedPortIndex < Ports.Count ? Ports[_selectedPortIndex].DeviceName : null; + + private string _name; + public string Name + { + get { return _name; } + set { SetProperty(ref _name, value); } + } + + private string _type; + public string Type + { + get { return _type; } + set { SetProperty(ref _type, value); } + } + + private string _brand; + public string Brand + { + get { return _brand; } + set + { + SetProperty(ref _brand, value); + } + } + + private string _year; + public string Year + { + get { return _year; } + set + { + SetProperty(ref _year, value); + } + } + + private string _model; + public string Model + { + get { return _model; ; } + set + { + SetProperty(ref _model, value); ; + } + } + + private string _series; + public string Series + { + get { return _series; } + set + { + SetProperty(ref _series, value); + } + } + + public string ItemId { get; set; } + public string FileId { get; set; } + public DesignCenterViewModel() + { + Ports = new ObservableCollection(); + PaperSizes = new ObservableCollection + { + new PaperSize(7000, 360), + new PaperSize(10000, 550), + }; + } + + public async Task GetCdrFileAsync(string fileId) + { + using(var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.GetAsync(Api.CdrFileById.FormatRoute(fileId)); + if (response.IsSuccessStatusCode) + { + var apiResponse = await response.DeserializeObjectAsync>(); + if (apiResponse?.Data != null) + { + Name = apiResponse.Data.Name ?? string.Empty; + Type = apiResponse.Data.Type?.Value ?? string.Empty; + Brand = apiResponse.Data.Brand?.Value ?? string.Empty; + Year = apiResponse.Data.Year?.Value ?? string.Empty; + Model = apiResponse.Data.Model?.Value ?? string.Empty; + Series = apiResponse.Data.Series?.Value ?? string.Empty; + + ItemId = fileId; + FileId = apiResponse.Data.File?.Id; + return true; + } + } + } + catch + { + + } + } + return false; + } + + private double _minSize = 0; + public double MinSize + { + get => _minSize; + set + { + SetProperty(ref _minSize, value); + } + } + + public bool UseOrigin { get; set; } = true; + } + + public class PortInfo + { + public string PortName { get; set; } + public string DeviceName { get; set; } + + public PortInfo(string portName, string deviceName) + { + PortName = portName; + DeviceName = deviceName; + } + } + + public class PaperSize + { + public double Width { get; set; } + public double Height { get; set; } + + public PaperSize(double width, double height) + { + Width = width; + Height = height; + } + } +} diff --git a/PCUT/PCUT/ViewModels/FileAddViewModel.cs b/PCUT/PCUT/ViewModels/FileAddViewModel.cs new file mode 100644 index 0000000..402632f --- /dev/null +++ b/PCUT/PCUT/ViewModels/FileAddViewModel.cs @@ -0,0 +1,187 @@ +using Newtonsoft.Json; +using PCUT.Entities; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net.Http.Headers; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Windows.Storage; +using Windows.Storage.Pickers; +using Windows.UI.Popups; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Controls; +using Windows.Storage.Streams; +using PCUT.Extensions; +using PCUT.Pages; +using Http.Core; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using PCUT.Entities.ApiResponse; + +namespace PCUT.ViewModels +{ + public class FileAddViewModel : NotificationBase + { + private string _name; + public string Name + { + get { return _name; } + set { SetProperty(ref _name, value); } + } + + private string _area; + public string Area + { + get { return _area; } + set { SetProperty(ref _area, value); } + } + + private string _remarks; + public string Remarks + { + get { return _remarks; } + set { SetProperty(ref _remarks, value); } + } + + private ImageSource _previewSource; + public ImageSource PreviewSource + { + get { return _previewSource; } + set { SetProperty(ref _previewSource, value); } + } + + public SelectableCollection Categories { get; } + public MetadataViewModel MetadataModel { get; } + + public FileAddViewModel() + { + Categories = new SelectableCollection(); + MetadataModel = new MetadataViewModel(); + + MetadataModel.SetFilterChangedHandle(); + } + + public async Task GetCategoriesAsync() + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var categories = await client.GetCategoriesAsync(filterByRole: true); + Categories.Load(categories); + } + } + + public async Task AddFileAsync(StorageFile file, Image image) + { + if (file == null) + { + var noFileSelectedDialog = new MessageDialog("Please select a file to upload!"); + await noFileSelectedDialog.ShowAsync(); + return false; + } + string fileId; + using (var stream = await file.OpenReadAsync()) + { + fileId = await UpLoadFileAsync(file.Name, stream.AsStreamForRead()); + } + + if (string.IsNullOrEmpty(fileId)) + { + var uploadAgainDialog = new MessageDialog("Please select a file to upload!"); + await uploadAgainDialog.ShowAsync(); + return false; + } + + string thumbnailId; + using (var stream = await image.CreateThumbnailAsync()) + { + thumbnailId = await UpLoadFileAsync(Path.GetFileNameWithoutExtension(file.Name) + ".png", stream.AsStreamForRead()); + } + if (string.IsNullOrEmpty(thumbnailId)) + { + var uploadAgainDialog = new MessageDialog("Please select a file to upload!"); + await uploadAgainDialog.ShowAsync(); + return false; + } + + try + { + var fileInfo = new UpsertFile + { + FileId = fileId, + Name = this.Name, + Thumbnail = thumbnailId, + Category = Categories.SelectedItem?.Id, + Type = MetadataModel.Types.SelectedItem?.Id, + Brand = MetadataModel.Brands.SelectedItem?.Id, + Series = MetadataModel.Series.SelectedItem?.Id, + Model = MetadataModel.Models.SelectedItem?.Id, + SubType = MetadataModel.SubTypes.SelectedItem?.Id, + Year = MetadataModel.Years.SelectedItem?.Id, + Area = this.Area, + Remarks = this.Remarks + }; + using (HttpClient client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var response = await client.PostAsJsonAsync(Api.CdrFiles, fileInfo); + if (!response.IsSuccessStatusCode) + { + var failDialog = new MessageDialog("An error occurred while adding the file!"); + await failDialog.ShowAsync(); + return false; + } + } + } + catch (Exception ex) + { + var errorDialog = new MessageDialog($"Error: {ex.Message}"); + await errorDialog.ShowAsync(); + return false; + } + var successDialog = new MessageDialog("File added successfully!"); + await successDialog.ShowAsync(); + return true; + } + + private async Task UpLoadFileAsync(string name, Stream stream) + { + try + { + using (HttpClient client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var content = new MultipartFormDataContent(); + var streamContent = new StreamContent(stream); + streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") + { + Name = "file", + FileName = name + }; + content.Add(streamContent); + + var response = await client.PostAsync(Api.Upload, content); + if (response.IsSuccessStatusCode) + { + var uploadResponse = await response.DeserializeObjectAsync>(); + return uploadResponse.Data.Id; + } + else + { + var errorDialog = new MessageDialog("Error uploading file!"); + await errorDialog.ShowAsync(); + } + } + } + catch (Exception ex) + { + var exceptionDialog = new MessageDialog($"Error: {ex.Message}"); + await exceptionDialog.ShowAsync(); + } + return null; + } + } +} diff --git a/PCUT/PCUT/ViewModels/FileEditViewModel.cs b/PCUT/PCUT/ViewModels/FileEditViewModel.cs new file mode 100644 index 0000000..b65e25f --- /dev/null +++ b/PCUT/PCUT/ViewModels/FileEditViewModel.cs @@ -0,0 +1,249 @@ + + +using Http.Core; +using Newtonsoft.Json; +using PCUT.Entities; +using PCUT.Entities.ApiResponse; +using PCUT.Extensions; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Windows.Storage; +using Windows.Storage.Pickers; +using Windows.Storage.Streams; +using Windows.UI.Popups; +using Windows.UI.Xaml.Controls; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using Windows.Graphics.Imaging; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Media; + +namespace PCUT.ViewModels +{ + public class FileEditViewModel : NotificationBase + { + private string _name; + public string Name + { + get { return _name; } + set { SetProperty(ref _name, value); } + } + + private string _area; + public string Area + { + get { return _area; } + set { SetProperty(ref _area, value); } + } + + private string _remarks; + public string Remarks + { + get { return _remarks; } + set { SetProperty(ref _remarks, value); } + } + + private ImageSource _previewSource; + public ImageSource PreviewSource + { + get { return _previewSource; } + set { SetProperty(ref _previewSource, value); } + } + + public SelectableCollection Categories { get; } + public MetadataViewModel MetadataModel { get; } + + private string _fileId; + private string _thumbnailId; + + public FileEditViewModel() + { + Categories = new SelectableCollection(); + MetadataModel = new MetadataViewModel(); + PreviewSource = new BitmapImage(); + + //MetadataModel.SetFilterChangedHandle(); + } + + public async Task GetCategoriesAsync() + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var categories = await client.GetCategoriesAsync(filterByRole: true); + Categories.Load(categories); + } + } + + public async Task LoadFileData(string id) + { + if (string.IsNullOrEmpty(id)) return; + + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.GetAsync(Api.CdrFileById.FormatRoute(id)); + if (response.IsSuccessStatusCode) + { + var json = await response.Content.ReadAsStringAsync(); + var fileData = JsonConvert.DeserializeObject>(json); + if (fileData?.Data != null) + { + var fileApi = fileData.Data; + Name = fileApi.Name; + Area = fileApi.Area; + Remarks = fileApi.Remarks; + Categories.SelectBy(x => x.Id == fileApi.Category?.Id); + MetadataModel.Types.SelectBy(x => x.Id == fileApi.Type?.Id); + MetadataModel.Brands.SelectBy(x => x.Id == fileApi.Brand?.Id); + MetadataModel.Series.SelectBy(x => x.Id == fileApi.Series?.Id); + MetadataModel.Models.SelectBy(x => x.Id == fileApi.Model?.Id); + MetadataModel.SubTypes.SelectBy(x => x.Id == fileApi.SubType?.Id); + MetadataModel.Years.SelectBy(x => x.Id == fileApi.Year?.Id); + + _thumbnailId = fileApi.Thumbnail?.Id; + _fileId = fileApi.File?.Id; + await LoadThumbnail(_thumbnailId); + } + } + } + catch (Exception ex) + { + } + } + } + + public async Task LoadThumbnail(string thumbnailId) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + using (var stream = new MemoryStream()) + { + var response = await httpClient.DownloadAsync(Api.Download.FormatRoute(thumbnailId), stream); + if (response) + { + stream.Seek(0, SeekOrigin.Begin); + await PreviewSource.LoadStreamAsync(stream.AsRandomAccessStream()); + } + } + } + } + + public async Task UpdateFileAsync(string id, StorageFile file, Image image) + { + string fileId = _fileId; + string thumbnailId = _thumbnailId; + if (file != null) + { + using (var stream = await file.OpenReadAsync()) + { + fileId = await UpLoadFileAsync(file.Name, stream.AsStreamForRead()); + } + if (string.IsNullOrEmpty(fileId)) + { + var uploadAgainDialog = new MessageDialog("Please select a file to upload!"); + await uploadAgainDialog.ShowAsync(); + return false; + } + + using (var stream = await image.CreateThumbnailAsync()) + { + thumbnailId = await UpLoadFileAsync(Path.GetFileNameWithoutExtension(file.Name) + ".png", stream.AsStreamForRead()); + } + if (string.IsNullOrEmpty(thumbnailId)) + { + var uploadAgainDialog = new MessageDialog("Please upload a Image first!"); + await uploadAgainDialog.ShowAsync(); + return false; + } + } + if (string.IsNullOrEmpty(Name)) + { + await new MessageDialog("Name cannot be null").ShowAsync(); + return false; + } + try + { + var fileInfo = new UpsertFile + { + FileId = fileId, + Name = this.Name, + Thumbnail = thumbnailId, + Category = Categories.SelectedItem?.Id, + Type = MetadataModel.Types.SelectedItem?.Id, + Brand = MetadataModel.Brands.SelectedItem?.Id, + Series = MetadataModel.Series.SelectedItem?.Id, + Model = MetadataModel.Models.SelectedItem?.Id, + SubType = MetadataModel.SubTypes.SelectedItem?.Id, + Year = MetadataModel.Years.SelectedItem?.Id, + Area = this.Area, + Remarks = this.Remarks + }; + using (HttpClient client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var response = await client.PutAsJsonAsync(Api.CdrFileById.FormatRoute(id), fileInfo); + if (!response.IsSuccessStatusCode) + { + var failDialog = new MessageDialog("Error editing the file while editing!"); + await failDialog.ShowAsync(); + return false; + } + } + } + catch (Exception ex) + { + var errorDialog = new MessageDialog($"Error: {ex.Message}"); + await errorDialog.ShowAsync(); + return false; + } + var successDialog = new MessageDialog("File edited successfully!"); + await successDialog.ShowAsync(); + return true; + } + + private async Task UpLoadFileAsync(string name, Stream stream) + { + try + { + using (HttpClient client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var content = new MultipartFormDataContent(); + var streamContent = new StreamContent(stream); + streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") + { + Name = "file", + FileName = name + }; + content.Add(streamContent); + + var response = await client.PostAsync(Api.Upload, content); + if (response.IsSuccessStatusCode) + { + var uploadResponse = await response.DeserializeObjectAsync>(); + return uploadResponse.Data.Id; + } + else + { + var errorDialog = new MessageDialog("Error uploading file!"); + await errorDialog.ShowAsync(); + } + } + } + catch (Exception ex) + { + var exceptionDialog = new MessageDialog($"Error: {ex.Message}"); + await exceptionDialog.ShowAsync(); + } + return null; + } + } +} diff --git a/PCUT/PCUT/ViewModels/FileListViewModel.cs b/PCUT/PCUT/ViewModels/FileListViewModel.cs new file mode 100644 index 0000000..5be7c61 --- /dev/null +++ b/PCUT/PCUT/ViewModels/FileListViewModel.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Storage; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; +using Newtonsoft.Json; +using PCUT.Entities; +using PCUT.Entities.ApiResponse; +using PCUT.Extensions; +using PCUT.Models; +using Http.Core; +using Http.Core.Contexts; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using System.ComponentModel; +using Windows.ApplicationModel.Core; + +namespace PCUT.ViewModels +{ + internal class FileListViewModel : NotificationBase + { + public PaginationViewModel Pagination { get; } + public MetadataViewModel MetadataModel { get; } + + private SelectableCollection _categories; + public SelectableCollection Categories + { + get => _categories; + set + { + if (_categories != null) + MetadataModel.UnbindPropertyChangedHandle(_categories); + SetProperty(ref _categories, value ?? new SelectableCollection(new Category { Name = "-None-" })); + MetadataModel.BindPropertyChangedHandle(_categories); + } + } + + public SelectableCollection FilteredFiles { get; } + private readonly IDictionary _thumbnailSources = new Dictionary(); + private readonly FilterModel _filterModel; + + private bool _filterMetadata; + private readonly Predicate[] _metadataFilters = new Predicate[6]; + + public FileListViewModel() + { + FilteredFiles = new SelectableCollection(); + MetadataModel = new MetadataViewModel(); + + _filterModel = new FilterModel(); + + _metadataFilters[0] = type => _filterModel.TypeIds.Contains(type.Id); + _metadataFilters[1] = brand => _filterModel.BrandIds.Contains(brand.Id); + _metadataFilters[2] = serie => _filterModel.SeriesIds.Contains(serie.Id); + _metadataFilters[3] = model => _filterModel.ModelIds.Contains(model.Id); + _metadataFilters[4] = subType => _filterModel.SubTypeIds.Contains(subType.Id); + _metadataFilters[5] = year => _filterModel.YearIds.Contains(year.Id); + + MetadataModel.MetadataChanged += OnMetadataChanged; + MetadataModel.SetFilterChangedHandle(); + + Pagination = new PaginationViewModel(withSearch: true); + Pagination.TextSearched += async (sender, args) => await SearchFileAsync(args.Text); + Pagination.PageChanged += OnPageChanged; + } + + public void SetFilterMetadataOnFileChange() + { + _filterMetadata = true; + } + + public async Task GetCategoriesAsync() + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var categories = await client.GetCategoriesAsync(filterByRole: true); + Categories.Load(categories); + } + } + + public async Task Reload() + { + await GetFilesByFilterAsync(Pagination.Page, Pagination.PageSize, Pagination.SearchText); + var loadThumbnailTask = LoadThumbnailsAsync(); + if (_filterMetadata) + FilterMetadata(null); + await loadThumbnailTask; + } + + private async void OnPageChanged(object sender, PageChangedEventArgs args) + { + await GetFilesByFilterAsync(args.Page, args.PageSize, Pagination.SearchText); + var loadThumbnailTask = LoadThumbnailsAsync(); + //if (_filterMetadata) + // FilterMetadata(null); + await loadThumbnailTask; + } + + private async void OnMetadataChanged(object sender, MetadataChangedEventArgs args) + { + Pagination.PageChanged -= OnPageChanged; + Pagination.Page = 1; + _filterModel.Clear(); + await GetFilesAsync(args.MetadataType); + Pagination.PageChanged += OnPageChanged; + } + + public async Task GetFilesAsync(string metadataType = null) + { + await GetFilesByFilterAsync(Pagination.Page, Pagination.PageSize, Pagination.SearchText); + var loadThumbnailTask = LoadThumbnailsAsync(); + if (_filterMetadata) + FilterMetadata(metadataType); + await loadThumbnailTask; + } + + public async Task SearchFileAsync(string searchText) + { + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => + { + await GetFilesByFilterAsync(Pagination.Page, Pagination.PageSize, searchText); + await LoadThumbnailsAsync(); + }); + } + + private async Task GetFilesByFilterAsync(int page, int pageSize, string searchText) + { + var url = CreateFilterUrl(page, pageSize, searchText); + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var apiResponse = await response.DeserializeObjectAsync>>(); + var data = apiResponse.Data; + int totalPages; + if (_filterModel.IsLoaded) + { + totalPages = apiResponse.Pagination.TotalPages; + } + else + { + totalPages = (int)Math.Ceiling((double)apiResponse.Pagination.TotalRecords / pageSize); + _filterModel.Load(data); + data = data.Take(pageSize); + } + Pagination.Page = apiResponse.Pagination.Page; + Pagination.TotalPage = totalPages; + Pagination.TotalRecords = apiResponse.Pagination.TotalRecords; + + int displayId = (apiResponse.Pagination.Page - 1) * pageSize + 1; + var files = data.Select(file => + { + file.FileId = file.File?.Id; + file.ThumbnailSource = GetOrCreateThumbnailSource(file); + file.DisplayId = displayId.ToString(); + + displayId++; + return file; + }); + FilteredFiles.Load(files); + } + } + catch (Exception ex) + { + + } + } + } + + private string CreateFilterUrl(int page, int pageSize, string searchText) + { + if (!_filterModel.IsLoaded) + pageSize = int.MaxValue; + + var filterBuilder = new StringBuilder(); + if (Categories.SelectedItem != default) + filterBuilder.AppendFilter("category", Categories.SelectedItem.Id); + if (MetadataModel.Brands.SelectedItem != default) + filterBuilder.AppendFilter("brand", MetadataModel.Brands.SelectedItem.Id); + if (MetadataModel.Types.SelectedItem != default) + filterBuilder.AppendFilter("type", MetadataModel.Types.SelectedItem.Id); + if (MetadataModel.Models.SelectedItem != default) + filterBuilder.AppendFilter("_model", MetadataModel.Models.SelectedItem.Id); + if (MetadataModel.Series.SelectedItem != default) + filterBuilder.AppendFilter("series", MetadataModel.Series.SelectedItem.Id); + if (MetadataModel.Years.SelectedItem != default) + filterBuilder.AppendFilter("year", MetadataModel.Years.SelectedItem.Id); + if (MetadataModel.SubTypes.SelectedItem != default) + filterBuilder.AppendFilter("subType", MetadataModel.SubTypes.SelectedItem.Id); + + if (!string.IsNullOrEmpty(searchText)) + filterBuilder.AppendFilter("name", "cn", searchText); + + var filter = filterBuilder.BuildFilter().ToString(); + + return PathBuilder.FromRouteTemplate(Api.CdrFiles) + .AddQuery("filter", filter) + .AddQuery("page", page.ToString()) + .AddQuery("pageSize", pageSize.ToString()) + .AddQuery("sort", "createdAt:desc") + .Build(); + } + + private void FilterMetadata(string metadataType) + { + MetadataModel.FilterAll(metadataType, _metadataFilters); + } + + private ImageSource GetOrCreateThumbnailSource(CdrFileModel file) + { + if (file.Thumbnail == null) + return null; + + if (_thumbnailSources.TryGetValue(file.Thumbnail.Id, out var thumbnail)) + return thumbnail.Source; + + thumbnail = new ThumbnailSource(new BitmapImage()); + _thumbnailSources[file.Thumbnail.Id] = thumbnail; + return thumbnail.Source; + } + + private async Task LoadThumbnailsAsync() + { + try + { + await Task.WhenAll( + _thumbnailSources.Where(x => !x.Value.IsLoaded).Select(x => x.Value.LoadAsync(x.Key))); + } + catch + { + } + } + + public class CdrFileModel : CdrFile + { + public ImageSource ThumbnailSource { get; set; } + public string FileId { get; set; } + public string DisplayId { get; set; } + } + + private class FilterModel + { + public readonly List TypeIds = new List(); + public readonly List BrandIds = new List(); + public readonly List SeriesIds = new List(); + public readonly List ModelIds = new List(); + public readonly List SubTypeIds = new List(); + public readonly List YearIds = new List(); + + public bool IsLoaded { get; set; } + + public void Clear() + { + TypeIds.Clear(); + BrandIds.Clear(); + SeriesIds.Clear(); + ModelIds.Clear(); + SubTypeIds.Clear(); + YearIds.Clear(); + IsLoaded = false; + } + + public void Load(IEnumerable data) + { + TypeIds.AddRange(data.Select(x => x.Type?.Id).Where(x => x != null).Distinct()); + BrandIds.AddRange(data.Select(x => x.Brand?.Id).Where(x => x != null).Distinct()); + SeriesIds.AddRange(data.Select(x => x.Series?.Id).Where(x => x != null).Distinct()); + ModelIds.AddRange(data.Select(x => x.Model?.Id).Where(x => x != null).Distinct()); + SubTypeIds.AddRange(data.Select(x => x.SubType?.Id).Where(x => x != null).Distinct()); + YearIds.AddRange(data.Select(x => x.Year?.Id).Where(x => x != null).Distinct()); + IsLoaded = true; + } + } + + private class ThumbnailSource + { + public ImageSource Source { get; set; } + public bool IsLoaded { get; set; } + + public ThumbnailSource(ImageSource source) + { + Source = source; + IsLoaded = false; + } + + public async Task LoadAsync(string id) + { + if (!IsLoaded) + { + IsLoaded = true; + try + { + await LoadThumbnailAsync(id); + } + catch + { + IsLoaded = false; + throw; + } + } + } + + private async Task LoadThumbnailAsync(string thumbnailId) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + using (var stream = new MemoryStream()) + { + var response = await httpClient.DownloadAsync(Api.Download.FormatRoute(thumbnailId), stream); + if (response) + { + stream.Seek(0, SeekOrigin.Begin); + await Source.LoadStreamAsync(stream.AsRandomAccessStream()); + } + } + } + } + } + } +} diff --git a/PCUT/PCUT/ViewModels/ImageViewModel.cs b/PCUT/PCUT/ViewModels/ImageViewModel.cs new file mode 100644 index 0000000..7709d35 --- /dev/null +++ b/PCUT/PCUT/ViewModels/ImageViewModel.cs @@ -0,0 +1,57 @@ +using System.ComponentModel; +using System.Xml.Linq; +using PCUT.Extensions; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; + +namespace PCUT.ViewModels +{ + public class ImageViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + private ImageSource _source; + private ImageSource _defaultSource = new SvgImageSource(); + + public ImageSource Source + { + get => _source; + private set + { + _source = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Source))); + DefaultMode = value == _defaultSource; + } + } + + public void SetSource(ImageSource source) + { + if (source == null) + return; + Source = source; + } + + public void SetSource(XDocument doc) + { + Source = _defaultSource; + if (doc != null) + _ = _source.LoadSvgAsync(doc); + } + + public void ShowDefault() + { + Source = _defaultSource; + } + + private bool _defaultMode = true; + public bool DefaultMode + { + get => _defaultMode; + private set + { + _defaultMode = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DefaultMode))); + } + } + } +} diff --git a/PCUT/PCUT/ViewModels/LoadingModel.cs b/PCUT/PCUT/ViewModels/LoadingModel.cs new file mode 100644 index 0000000..ff62af0 --- /dev/null +++ b/PCUT/PCUT/ViewModels/LoadingModel.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; +using System.Threading; + +namespace PCUT.ViewModels +{ + public class LoadingModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + private int _count; + public int Count { get; } + + public void SetCount(int count) + { + _ = Interlocked.Exchange(ref _count, count); + } + + public void DecreaseCount() + { + var count = Interlocked.Decrement(ref _count); + if (count <= 0) + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); + } + } +} diff --git a/PCUT/PCUT/ViewModels/LogOutViewModel.cs b/PCUT/PCUT/ViewModels/LogOutViewModel.cs new file mode 100644 index 0000000..c43efda --- /dev/null +++ b/PCUT/PCUT/ViewModels/LogOutViewModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCUT.ViewModels +{ + public class LogOutViewModel + { + } +} diff --git a/PCUT/PCUT/ViewModels/LoginViewModel.cs b/PCUT/PCUT/ViewModels/LoginViewModel.cs new file mode 100644 index 0000000..44944d5 --- /dev/null +++ b/PCUT/PCUT/ViewModels/LoginViewModel.cs @@ -0,0 +1,215 @@ +using Http.Core; +using Http.Core.Contexts; +using Http.Core.Extensions; +using Http.Core.Models; +using PCUT.Extensions; +using PCUT.Helpers; +using PCUT.Models; +using PCUT.Pages; +using System; +using System.Security.Cryptography; +using System.Threading.Tasks; +using System.Windows.Input; +using Windows.Security.Credentials; +using Windows.Security.Cryptography; +using Windows.System.Profile; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using static Http.Core.Constants.HttpConstants; +using static Http.Core.Extensions.HttpExtensions; + +namespace PCUT.ViewModels +{ + public class LoginViewModel : NotificationBase + { + private const string REMEMBER_ME_KEY = "RememberMe"; + private const string REMEMBER_USER_KEY = "RememberMeUser"; + private const string DEVICE_ID_KEY = "DeviceId"; + + private bool _rememberMe; + public bool RememberMe + { + get { return _rememberMe; } + set + { + SetProperty(ref _rememberMe, value); + var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings; + localSettings.Values[REMEMBER_ME_KEY] = value; + } + } + + private string _username; + public string Username + { + get { return _username; } + set + { + SetProperty(ref _username, value); + LoginEnabled = CheckLoginEnabled(); + } + } + + private string _password; + public string Password + { + get { return _password; } + set + { + SetProperty(ref _password, value); + LoginEnabled = CheckLoginEnabled(); + } + } + + private bool _loginEnabled = false; + public bool LoginEnabled + { + get { return _loginEnabled; } + private set + { + if (_loginEnabled != value) + SetProperty(ref _loginEnabled, value); + } + } + #region Commands + public ICommand NavigateMainCommand { get; private set; } + #endregion + + private bool _success = false; + private async Task LoginAsync() + { + LoginEnabled = false; + try + { + var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings; + localSettings.Values.Remove(REMEMBER_USER_KEY); + + bool loginSuccess = false; + string errorMessage = null; + using (var client = HttpClientFactory.CreateClient(ClientNames.AuthClient)) + { + try + { + var loginResult = await client.LoginAsync(new LoginRequest(Username, Password), Username); + loginSuccess = loginResult.Success; + if (!loginSuccess) + errorMessage = loginResult.Message; + } + catch (Exception ex) + { + errorMessage = ex.Message; + } + } + + if (!loginSuccess) + { + await new MessageDialog(errorMessage).ShowAsync(); + return; + } + + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + await client.LoadUserProfileAsync(); + } + catch { } + } + + if (RememberMe) + { + localSettings.Values[REMEMBER_USER_KEY] = Username; + UpsertRememberMe(Username, Password); + } + + _success = true; + await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + ((Frame)Window.Current.Content).Navigate(typeof(MainMenuPage)); + }); + } + finally + { + if (!_success) + LoginEnabled = CheckLoginEnabled(); + } + } + + public LoginViewModel() + { + var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings; + if (localSettings.TryGetValue(REMEMBER_ME_KEY, out var rememberMe) && rememberMe) + { + RememberMe = true; + if (localSettings.TryGetValue(REMEMBER_USER_KEY, out var userName)) + (Username, Password) = LoadRememberMe(userName); + } + if (!localSettings.TryGetValue(DEVICE_ID_KEY, out var deviceId) || string.IsNullOrEmpty(deviceId)) + { + var sysInfo = SystemIdentification.GetSystemIdForPublisher(); + if (sysInfo != null) + { + deviceId = CryptographicBuffer.EncodeToBase64String(sysInfo.Id); + } + else + { + // random int64 number + var salt = new byte[8]; + RandomNumberGenerator.Create().GetNonZeroBytes(salt); + // random guid + var uuid = Guid.NewGuid().ToByteArray(); + // random timestamp + var timestamp = BitConverter.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); + + var data = new byte[uuid.Length + timestamp.Length + salt.Length]; + Array.Copy(salt, 0, data, 0, salt.Length); + Array.Copy(uuid, 0, data, 0 + salt.Length, uuid.Length); + Array.Copy(timestamp, 0, data, 0 + salt.Length + uuid.Length, timestamp.Length); + + deviceId = Convert.ToBase64String(data); + } + localSettings.Values[DEVICE_ID_KEY] = deviceId; + } + UserContext.Instance.SetDeviceId(deviceId); + NavigateMainCommand = new RelayCommand(LoginAsync, () => !_success); + } + + private bool CheckLoginEnabled() + { + return !string.IsNullOrWhiteSpace(_username) && !string.IsNullOrWhiteSpace(_password); + } + + private (string UserName, string Password) LoadRememberMe(string userName) + { + if (string.IsNullOrWhiteSpace(userName)) + return default; + + var vault = new PasswordVault(); + try + { + var credential = vault.Retrieve(REMEMBER_ME_KEY, userName); + return (credential?.UserName, credential?.Password); + } + catch + { + return default; + } + } + + private void UpsertRememberMe(string userName, string password) + { + var vault = new PasswordVault(); + PasswordCredential credential; + try + { + credential = vault.Retrieve(REMEMBER_ME_KEY, userName); + credential.Password = password; + } + catch + { + credential = new PasswordCredential(REMEMBER_ME_KEY, userName, password); + } + vault.Add(credential); + } + } +} diff --git a/PCUT/PCUT/ViewModels/MainMenuPageViewModel.cs b/PCUT/PCUT/ViewModels/MainMenuPageViewModel.cs new file mode 100644 index 0000000..873a685 --- /dev/null +++ b/PCUT/PCUT/ViewModels/MainMenuPageViewModel.cs @@ -0,0 +1,103 @@ +using Http.Core; +using PCUT.Entities; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Http.Core; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using Newtonsoft.Json; +using Windows.UI.Popups; +using Http.Core.Contexts; +using System.Collections.ObjectModel; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml; + +namespace PCUT.ViewModels +{ + public class MainMenuPageViewModel : NotificationBase + { + private string _userName; + public string UserName + { + get => _userName; + set => SetProperty(ref _userName, value); + } + + private string _expiry; + public string Expiry + { + get => _expiry; + set => SetProperty(ref _expiry, value); + } + + private bool _isUserRoleAdmin = false; + public bool IsUserRoleAdmin + { + get => _isUserRoleAdmin; + set + { + if (_isUserRoleAdmin != value) + { + _isUserRoleAdmin = value; + RaisePropertyChanged(nameof(IsUserRoleAdmin)); + } + } + } + + private bool _isMenuEnabled = false; + public bool IsMenuEnabled + { + get => _isMenuEnabled; + set + { + SetProperty(ref _isMenuEnabled, value); + } + } + + private Visibility _isMenuDisplay = Visibility.Collapsed; + public Visibility IsMenuDisplay + { + get => _isMenuDisplay; + set + { + if (_isMenuDisplay != value) + { + SetProperty(ref _isMenuDisplay, value); + } + } + } + + public SelectableCollection Categories { get; } + + public MainMenuPageViewModel() + { + IsUserRoleAdmin = UserContext.Instance.Profile?.Role?.Equals("admin", StringComparison.OrdinalIgnoreCase) ?? false; + Categories = new SelectableCollection(new Category { Name = "-None-" }); + } + + public void ToggleMenuDisplay() + { + IsMenuDisplay = IsMenuDisplay == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed; + } + + public void HideMenuDisplay() + { + IsMenuDisplay = Visibility.Collapsed; + } + + public async Task GetCategoriesAsync() + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + var categories = await client.GetCategoriesAsync(filterByRole: true); + Categories.Load(categories); + } + } + } +} diff --git a/PCUT/PCUT/ViewModels/MetadataViewModel.cs b/PCUT/PCUT/ViewModels/MetadataViewModel.cs new file mode 100644 index 0000000..4ee07e4 --- /dev/null +++ b/PCUT/PCUT/ViewModels/MetadataViewModel.cs @@ -0,0 +1,226 @@ +using Http.Core; +using PCUT.Entities; +using PCUT.Entities.ApiResponse; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; +using Http.Core.Extensions; +using Windows.UI.Xaml.Controls.Primitives; +using static Http.Core.Constants.HttpConstants; + +namespace PCUT.ViewModels +{ + public class MetadataViewModel : NotificationBase + { + public event MetadataChangedEventHandler MetadataChanged; + + public const string TypeHeader = "Type"; + public const string BrandHeader = "Brand"; + public const string SerieHeader = "Series"; + public const string ModelHeader = "Model"; + public const string SubTypeHeader = "SubType"; + public const string YearHeader = "Year"; + + public SelectableCollection MetadataTypes { get; } + + public SelectableCollection Types { get; set; } + public SelectableCollection Brands { get; set; } + public SelectableCollection Series { get; set; } + public SelectableCollection Models { get; set; } + public SelectableCollection SubTypes { get; set; } + public SelectableCollection Years { get; set; } + + private readonly List> _collections; + + public MetadataViewModel() + { + MetadataTypes = new SelectableCollection( + withDefault: false, + TypeHeader, + BrandHeader, + SerieHeader, + ModelHeader, + SubTypeHeader, + YearHeader + ); + + var defaultMetadata = new CategoryMetadata { Value = "-None-" }; + Types = new SelectableCollection(defaultMetadata); + Brands = new SelectableCollection(defaultMetadata); + Series = new SelectableCollection(defaultMetadata); + Models = new SelectableCollection(defaultMetadata); + SubTypes = new SelectableCollection(defaultMetadata); + Years = new SelectableCollection(defaultMetadata); + _collections = new List> { Types, Brands, Series, Models, SubTypes, Years }; + + MetadataChanged += OnMetadataChanged; + } + + public void BindAll(params Selector[] selectors) + { + for (int i = 0; i < selectors.Length && i < _collections.Count; i++) + _collections[i].Bind(selectors[i]); + } + + public void FilterAll(string type = null, params Predicate[] filters) + { + var index = type == null ? -1 : MetadataTypes.Items.IndexOf(type); + for (int i = index + 1; i < _collections.Count; i++) + _collections[i].Filter(filters[i]); + } + + public void UnselectAll(string type = null) + { + var index = type == null ? -1 : MetadataTypes.Items.IndexOf(type); + for (int i = index + 1; i < _collections.Count; i++) + _collections[i].SelectedIndex = -1; + } + + public void SetFilterChangedHandle() + { + AssignHandler(OnPropertyChanged); + } + + public void UnsetFilterChangedHandle() + { + RemoveHandler(OnPropertyChanged); + } + + public void BindPropertyChangedHandle(SelectableCollection collection) + { + collection.PropertyChanged += OnPropertyChanged; + } + + public void UnbindPropertyChangedHandle(SelectableCollection collection) + { + collection.PropertyChanged -= OnPropertyChanged; + } + + private void AssignHandler(PropertyChangedEventHandler handler, string type = null) + { + var index = type == null ? -1 : MetadataTypes.Items.IndexOf(type); + for (int i = index + 1; i < _collections.Count; i++) + _collections[i].PropertyChanged += handler; + } + + private void RemoveHandler(PropertyChangedEventHandler handler, string type = null) + { + var index = type == null ? -1 : MetadataTypes.Items.IndexOf(type); + for (int i = index + 1; i < _collections.Count; i++) + _collections[i].PropertyChanged -= handler; + } + + private void OnPropertyChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == nameof(SelectableCollection.SelectedIndex)) + { + var metadataType = sender == Types ? TypeHeader + : sender == Brands ? BrandHeader + : sender == Series ? SerieHeader + : sender == Models ? ModelHeader + : sender == SubTypes ? SubTypeHeader + : sender == Years ? YearHeader + : null; + RemoveHandler(OnPropertyChanged, metadataType); + MetadataChanged?.Invoke(sender, new MetadataChangedEventArgs(metadataType, args)); + AssignHandler(OnPropertyChanged, metadataType); + } + } + + private void OnMetadataChanged(object sender, MetadataChangedEventArgs args) + { + UnselectAll(args.MetadataType); + } + + public async Task LoadAllMetadataAsync() + { + await Task.WhenAll( + LoadMetadataAsync(TypeHeader), + LoadMetadataAsync(BrandHeader), + LoadMetadataAsync(SerieHeader), + LoadMetadataAsync(ModelHeader), + LoadMetadataAsync(SubTypeHeader), + LoadMetadataAsync(YearHeader) + ); + } + + private async Task LoadMetadataAsync(string type) + { + var metadataList = await GetMetadataAsync(type); + SelectableCollection collections = null; + switch (type) + { + case TypeHeader: + collections = Types; + break; + case BrandHeader: + collections = Brands; + break; + case SerieHeader: + collections = Series; + break; + case ModelHeader: + collections = Models; + break; + case SubTypeHeader: + collections = SubTypes; + break; + case YearHeader: + collections = Years; + break; + default: + break; + } + collections?.Load(metadataList); + } + + private static async Task> GetMetadataAsync(string type) + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var url = PathBuilder.FromRouteTemplate(Api.Metadata) + .AddQuery("filter", $"[\"type:eq:{type.ToUpper()}\"]") + .AddQuery("sort", "createdAt:desc") + .Build(); + var response = await client.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var content = await response.DeserializeObjectAsync>>(); + return content.Data; + } + } + catch + { + } + } + return Enumerable.Empty(); + } + } + + public delegate void MetadataChangedEventHandler(object sender, MetadataChangedEventArgs args); + public class MetadataChangedEventArgs : EventArgs + { + private readonly string _metadataType; + public virtual string MetadataType + { + get => _metadataType; + } + + private readonly PropertyChangedEventArgs _propertyChangedArgs; + public virtual PropertyChangedEventArgs PropertyChangedArgs + { + get => _propertyChangedArgs; + } + + public MetadataChangedEventArgs(string metadataType, PropertyChangedEventArgs propertyChangedArgs) + { + _metadataType = metadataType; + _propertyChangedArgs = propertyChangedArgs; + } + } +} diff --git a/PCUT/PCUT/ViewModels/NestingProgressModel.cs b/PCUT/PCUT/ViewModels/NestingProgressModel.cs new file mode 100644 index 0000000..5a4955b --- /dev/null +++ b/PCUT/PCUT/ViewModels/NestingProgressModel.cs @@ -0,0 +1,31 @@ +using System.ComponentModel; + +namespace PCUT.ViewModels +{ + public class NestingProgressModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + private double _progress = 0; + public double Progress + { + get => _progress; + set + { + _progress = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Progress))); + } + } + + private bool _running = false; + public bool Running + { + get => _running; + set + { + _running = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Running))); + } + } + } +} diff --git a/PCUT/PCUT/ViewModels/PaginationViewModel.cs b/PCUT/PCUT/ViewModels/PaginationViewModel.cs new file mode 100644 index 0000000..389fd44 --- /dev/null +++ b/PCUT/PCUT/ViewModels/PaginationViewModel.cs @@ -0,0 +1,139 @@ +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Timers; +using PCUT.Extensions; + +namespace PCUT.ViewModels +{ + public class PaginationViewModel : NotificationBase, IDisposable + { + private const string PageSizeKey = "PCUT_list_pagesize"; + private const int DefaultPageSize = 20; + + public event PageChangedEventHandler PageChanged; + public event TextSearchedEventHandler TextSearched; + + private readonly Timer _timer; + + public PaginationViewModel(bool withSearch = false) + { + if (withSearch) + { + _timer = new Timer(500); + _timer.AutoReset = false; + _timer.Enabled = true; + _timer.Elapsed += (sender, args) => { + TextSearched?.Invoke(this, new TextSearchEventArgs(SearchText)); + }; + _timer.Stop(); + } + _pageSize = GetPageSize(); + PropertyChanged += (sender, args) => + { + if (args.PropertyName == nameof(Page) || args.PropertyName == nameof(PageSize)) + { + PageChanged?.Invoke(this, new PageChangedEventArgs(Page, PageSize)); + } + }; + } + + public List AvailablePageSizes { get; } = new List { 20, 50, 100, 200 }; + + private string _searchText; + public string SearchText + { + get => _searchText; + set + { + if (_searchText != value) + { + _searchText = value; + RaisePropertyChanged(nameof(SearchText)); + _timer.Stop(); + _timer.Start(); + } + } + } + + private int _page = 0; + public int Page + { + get { return _page; } + set + { + if (_page != value && value > 0 && value <= _totalPage) + SetProperty(ref _page, value); + } + } + + private int _pageSize; + public int PageSize + { + get { return _pageSize; } + set + { + SetProperty(ref _pageSize, value); + SetPageSize(value); + } + } + + private int _totalPage = 1; + public int TotalPage + { + get { return _totalPage; } + set { SetProperty(ref _totalPage, value); } + + } + + private int _totalRecords = 0; + public int TotalRecords + { + get { return _totalRecords; } + set { SetProperty(ref _totalRecords, value); } + } + + public void Dispose() + { + _timer?.Dispose(); + } + + private static int GetPageSize() + { + var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings; + if (localSettings.TryGetValue(PageSizeKey, out var value)) + return value; + else + return DefaultPageSize; + } + + private static void SetPageSize(int pageSize) + { + var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings; + localSettings.Values[PageSizeKey] = pageSize; + } + } + + public delegate void TextSearchedEventHandler(object sender, TextSearchEventArgs args); + public class TextSearchEventArgs : EventArgs + { + public string Text { get; } + public TextSearchEventArgs(string text) + { + Text = text; + } + } + + public delegate void PageChangedEventHandler(object sender, PageChangedEventArgs args); + public class PageChangedEventArgs : EventArgs + { + public int Page { get; } + public int PageSize { get; } + + public PageChangedEventArgs(int page, int pageSize) + { + Page = page; + PageSize = pageSize; + } + } +} diff --git a/PCUT/PCUT/ViewModels/PortSettingViewModel.cs b/PCUT/PCUT/ViewModels/PortSettingViewModel.cs new file mode 100644 index 0000000..7825dd9 --- /dev/null +++ b/PCUT/PCUT/ViewModels/PortSettingViewModel.cs @@ -0,0 +1,128 @@ +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO.Ports; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Storage; + +namespace PCUT.ViewModels +{ + public class PortSettingViewModel : NotificationBase + { + private int _baudRate; + public int BaudRate + { + get { return _baudRate; } + set { SetProperty(ref _baudRate, value); } + } + + private int _databits; + public int Databits + { + get { return _databits; } + set { SetProperty(ref _databits, value); } + } + + private int _parity; + public int SelectedParity + { + get { return _parity; } + set { SetProperty(ref _parity, value); } + } + + private int _stopbits; + public int SelectedStopbits + { + get { return _stopbits; } + set { SetProperty(ref _stopbits, value); } + } + + private int _speed; + public int Speed + { + get { return _speed; } + set { SetProperty(ref _speed, value); } + } + + private int _pen; + public int SelectedPen + { + get { return _pen; } + set { SetProperty(ref _pen, value); } + } + + public ObservableCollection ParityList = new ObservableCollection() + { + Parity.None, + Parity.Odd, + Parity.Even, + Parity.Mark, + Parity.Space + }; + + public ObservableCollection StopbitsList = new ObservableCollection() + { + StopBits.None, + StopBits.One, + StopBits.Two, + StopBits.OnePointFive, + }; + + public ObservableCollection PenList = new ObservableCollection() + { + 0, + 1, + 2, + 3 + }; + + public void LoadSetting(string portName) + { + var settings = ApplicationData.Current.LocalSettings; + if (settings.Values.TryGetValue($"{portName}_PortBaudrate", out var baudrate)) + BaudRate = (int)baudrate; + else + BaudRate = 9600; + + if (settings.Values.TryGetValue($"{portName}_PortDatabits", out var databits)) + Databits = (int)databits; + else + Databits = 8; + + if (settings.Values.TryGetValue($"{portName}_PortParity", out var parity)) + SelectedParity = (int)parity; + else + SelectedParity = 0; + + if (settings.Values.TryGetValue($"{portName}_PortStopbits", out var stopbits)) + SelectedStopbits = (int)stopbits; + else + SelectedStopbits = 1; + + if (settings.Values.TryGetValue($"{portName}_Speed", out var speed)) + Speed = (int)speed; + else + Speed = 82; + + if (settings.Values.TryGetValue($"{portName}_PortPen", out var pen)) + SelectedPen = (int)pen; + else + SelectedPen = 0; + + } + + public void SaveSetting(string portName) + { + var settings = ApplicationData.Current.LocalSettings; + settings.Values[$"{portName}_PortBaudrate"] = BaudRate; + settings.Values[$"{portName}_PortDatabits"] = Databits; + settings.Values[$"{portName}_PortParity"] = SelectedParity; + settings.Values[$"{portName}_PortStopbits"] = SelectedStopbits; + settings.Values[$"{portName}_Speed"] = Speed; + settings.Values[$"{portName}_PortPen"] = SelectedPen; + } + } +} diff --git a/PCUT/PCUT/ViewModels/SvgData.cs b/PCUT/PCUT/ViewModels/SvgData.cs new file mode 100644 index 0000000..cfbd8ca --- /dev/null +++ b/PCUT/PCUT/ViewModels/SvgData.cs @@ -0,0 +1,318 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using PCUT.Extensions; +using PCUT.DeepNestApi; +using DeepNestLib; + +namespace PCUT.ViewModels +{ + public class SvgData + { + // singleton implementation + private SvgData() { } + private static readonly object _instanceLock = new object(); + private static SvgData _instance; + public static SvgData Instance + { + get + { + if (_instance == null) + { + lock (_instanceLock) + { + if (_instance == null) + _instance = new SvgData(); + } + } + return _instance; + } + } + + // Svg Data + private readonly object _dataLock = new object(); + public XDocument Data { get; private set; } + public string ItemId { get; set; } + public string FileId { get; set; } + + private double? _originWidth; + public double? OriginWidth + { + get => _originWidth; + private set + { + _originWidth = value; + } + } + + private double? _originHeight; + public double? OriginHeight + { + get => _originHeight; + private set + { + _originHeight = value; + } + } + + public string OriginViewBox { get; set; } + + // Only set data once. If required to set data again, need to clear (set to null) first + public void SetData(XDocument data, double? originalWidth = null, double? originalHeight = null) + { + if (Data == null || data == null) + { + lock (_dataLock) + { + if (data == null) + { + if (Data != null) + { + Data.Changing -= DataChangingHandle; + Data.Changed -= DataChangedHandle; + } + CleanUpData(); + } + else if (Data == null) + { + Data = data; + OriginWidth = originalWidth; + OriginHeight = originalHeight; + OriginViewBox = data.GetViewBox(); + data.Changing += DataChangingHandle; + data.Changed += DataChangedHandle; + } + } + } + } + + private readonly ConcurrentDictionary _componentData = new ConcurrentDictionary(); + public int ComponentCount => _componentData.Count; + + public IEnumerable<(string Id, bool Deleted, XDocument Data)> ComponentData => _componentData?.Where(x => x.Value.Data != null) + .OrderBy(x => x.Value.Order) + .Select(x => (x.Key, x.Value.Hidden, x.Value.Data)); + + public IEnumerable<(double Width, double Height)> ComponentBounds => _componentData?.Where(x => x.Value?.Data != null) + .Select(x => (x.Value.Width, x.Value.Height)); + public void AddComponent(string id, bool deleted, XDocument data, double boundWidth, double boundHeight) + { + _ = _componentData.AddOrUpdate( + id, + (_, param) => new SvgComponentData(param.Data, param.Hidden, boundWidth, boundHeight, _componentData.Count), + (_, component, param) => { component.UpdateData(param.Data); return component; }, + (Data: data, Hidden: deleted)); + } + + // Svg Nest Data + private readonly object _resultLock = new object(); + public XDocument NestedData { get; private set; } + public double? NestedWidth { get; private set; } + public double? NestedHeight { get; private set; } + public double MaterialUtilization { get; private set; } = 1; + public void SetResult(XDocument result, double? nestedWidth = null, double? nestedHeight = null, double materialUtilization = 1) + { + lock (_resultLock) + { + NestedData = result; + NestedWidth = nestedWidth; + NestedHeight = nestedHeight; + MaterialUtilization = materialUtilization; + } + } + + private readonly ConcurrentDictionary _detailData = new ConcurrentDictionary(); + public int DetailCount => _detailData.Count; + + public IEnumerable ComponentDetails => _detailData?.Where(x => x.Value?.Detail != null && !x.Value.Hidden) + .Select(x => x.Value.Detail); + + public IEnumerable<(double Width, double Height)> DetailBounds => _detailData?.Where(x => x.Value?.Detail != null && !x.Value.Hidden) + .Select(x => (x.Value.Width, x.Value.Height)); + public void AddDetail(RawDetail detail) + { + _ = _detailData.AddOrUpdate( + detail.Name, + (_, param) => new SvgDetailData(param), + (_, component, param) => { component.UpdateDetail(param); return component; }, + detail); + } + + public void ComputeDetailBounds() + { + foreach (var data in _detailData.Values.Where(x => x.Detail != null)) + { + var bbox = data.Detail.BoundingBox(); + data.Width = bbox.Width; + data.Height = bbox.Height; + } + } + + private readonly object _scaleLock = new object(); + private double? _detailScaleFactor; + public double DetailScaleFactor + { + get => _detailScaleFactor ?? 1; + set + { + if (_detailScaleFactor == null) + { + lock(_scaleLock) + { + if (_detailScaleFactor == null) + _detailScaleFactor = value; + } + } + } + } + + public NestingContext NestingContext { get; set; } + public SheetLoadInfo SheetInfo { get; private set; } + public Dictionary DxfCache { get; } = new Dictionary(); + + public void NewSheetInfo(double width, double height) + { + SheetInfo = new SheetLoadInfo + { + Nfp = NewSheet(width, height), + Width = width, + Height = height, + Quantity = 1 + }; + } + + private Sheet NewSheet(double w = 3000, double h = 1500) + { + var tt = new RectangleSheet(); + tt.Name = "rectSheet"; + tt.Height = h; + tt.Width = w; + tt.Rebuild(); + return tt; + } + + // Clean up + private void CleanUpData() + { + Data = null; + FileId = null; + NestedData = null; + MaterialUtilization = 1; + OriginWidth = null; + OriginHeight = null; + OriginViewBox = null; + _detailScaleFactor = null; + _componentData?.Clear(); + _detailData?.Clear(); + CleanUpContext(); + } + + public void CleanUpContext() + { + NestingContext = null; + SheetInfo = null; + DxfCache?.Clear(); + } + + // Handler + private void DataChangingHandle(object sender, XObjectChangeEventArgs e) + { + switch (sender) + { + case XAttribute attribute: + { + if (attribute.Name.LocalName == "visibility" && e.ObjectChange == XObjectChange.Remove) + { + var id = attribute.Parent.GetId(); + if (_componentData != null && _componentData.ContainsKey(id)) + { + _componentData[id].Hidden = false; + _detailData?.Clear(); + } + } + NestedData = null; + MaterialUtilization = 1; + CleanUpContext(); + break; + } + } + } + + private void DataChangedHandle(object sender, XObjectChangeEventArgs e) + { + switch (sender) + { + case XAttribute attribute: + { + if (attribute.Name.LocalName == "visibility" && e.ObjectChange == XObjectChange.Add && attribute.Value == "hidden") + { + var id = attribute.Parent.GetId(); + if (_componentData != null && _componentData.ContainsKey(id)) + { + _componentData[id].Hidden = true; + _detailData?.Clear(); + } + } + NestedData = null; + MaterialUtilization = 1; + CleanUpContext(); + break; + } + } + } + + public class SvgComponentData + { + public bool Hidden { get; set; } + public XDocument Data { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public int Order { get; set; } + + public SvgComponentData(XDocument data, bool hidden, double width, double height, int order) + { + Data = data; + Hidden = hidden; + Width = width; + Height = height; + Order = order; + } + + public void UpdateData(XDocument data) + { + Data = data; + } + } + + public class SvgDetailData + { + public RawDetail Detail { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public bool Hidden { get; set; } + + public SvgDetailData(RawDetail detail) + { + Detail = detail; + } + + public void UpdateDetail(RawDetail detail) + { + Detail = detail; + } + } + + public class SheetLoadInfo + { + public NFP Nfp; + + public string Info { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public int Quantity { get; set; } + + public double BoundScale { get; set; } + } + } +} diff --git a/PCUT/PCUT/ViewModels/SvgNestSettings.cs b/PCUT/PCUT/ViewModels/SvgNestSettings.cs new file mode 100644 index 0000000..06d301d --- /dev/null +++ b/PCUT/PCUT/ViewModels/SvgNestSettings.cs @@ -0,0 +1,203 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using Windows.Storage; + +namespace PCUT.ViewModels +{ + public class SvgNestSettings : INotifyPropertyChanged + { + private SvgNestSettings() { } + private static readonly object _instanceLock = new object(); + private static SvgNestSettings _instance; + public static SvgNestSettings Instance + { + get + { + if (_instance == null) + { + lock (_instanceLock) + { + if (_instance == null) + { + _instance = new SvgNestSettings(); + _instance.LoadSettings(); + _instance.ApplySettings(); + } + } + } + return _instance; + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + private int _maxNumberOfRun = 10; + public int MaxNumberOfRun + { + get => _maxNumberOfRun; + set + { + _maxNumberOfRun = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MaxNumberOfRun))); + } + } + + private int _maxRuntimeInMinutes = 5; + public int MaxRuntimeInMinute + { + get => _maxRuntimeInMinutes; + set + { + _maxRuntimeInMinutes = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MaxRuntimeInMinute))); + } + } + + private double _curveTolerance = 0.3; + public double CurveTolerance + { + get => _curveTolerance; + set + { + _curveTolerance = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurveTolerance))); + } + } + + private double _spacing = 0; + public double Spacing + { + get => _spacing; + set + { + _spacing = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Spacing))); + } + } + + public ObservableCollection ValidRotations = new ObservableCollection { 2, 4, 8 }; + private int _rotations = 4; + public int Rotations + { + get => _rotations; + set + { + if (_rotations != value) + { + _rotations = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Rotations))); + } + } + } + + private int _populationSize = 10; + public int PopulationSize + { + get => _populationSize; + set + { + _populationSize = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PopulationSize))); + } + } + + private int _mutationRate = 10; + public int MutationRate + { + get => _mutationRate; + set + { + _mutationRate = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MutationRate))); + } + } + + + private bool _useHoles = true; + public bool UseHoles + { + get => _useHoles; + set + { + _useHoles = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UseHoles))); + } + } + + private bool _exploreConcave = true; + public bool ExploreConcave + { + get => _exploreConcave; + set + { + _exploreConcave = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ExploreConcave))); + } + } + + private object this[string key] + { + get + { + return GetType().GetProperty(key).GetValue(this); + } + set + { + GetType().GetProperty(key).SetValue(this, value); + } + } + + private static readonly string[] Keys = new string[] + { + nameof(MaxNumberOfRun), + nameof(MaxRuntimeInMinute), + nameof(CurveTolerance), + nameof(Spacing), + nameof(Rotations), + nameof(PopulationSize), + nameof(MutationRate), + nameof(UseHoles), + nameof(ExploreConcave) + }; + + public void SaveSettings() + { + foreach (var key in Keys) + { + SaveSetting($"Nest{key}", this[key]); + } + } + + public void LoadSettings() + { + foreach (var key in Keys) + { + var value = LoadSetting($"Nest{key}"); + if (value != null) + this[key] = value; + } + } + + public void ApplySettings() + { + var config = DeepNestApi.SvgNest.Config; + config.curveTolerance = CurveTolerance; + config.spacing = Spacing; + config.rotations = Rotations; + config.populationSize = PopulationSize; + config.mutationRate = MutationRate; + config.useHoles = UseHoles; + config.exploreConcave = ExploreConcave; + } + + private static object LoadSetting(string key) + { + return ApplicationData.Current.LocalSettings.Values[key]; + } + + private static void SaveSetting(string key, object value) + { + ApplicationData.Current.LocalSettings.Values[key] = value; + } + } +} diff --git a/PCUT/PCUT/ViewModels/UpsertCategoryViewModel.cs b/PCUT/PCUT/ViewModels/UpsertCategoryViewModel.cs new file mode 100644 index 0000000..386ffe3 --- /dev/null +++ b/PCUT/PCUT/ViewModels/UpsertCategoryViewModel.cs @@ -0,0 +1,126 @@ +using Newtonsoft.Json; +using PCUT.Entities; +using System; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Http.Core; +using static Http.Core.Constants.HttpConstants; +using Http.Core.Extensions; +using Windows.UI.Popups; +using PCUT.Models; +using PCUT.Helpers; +using System.Windows.Input; +using PCUT.Entities.ApiResponse; + +namespace PCUT.ViewModels +{ + public class UpsertCategoryViewModel : NotificationBase + { + private string _name; + public string Name + { + get { return _name; } + + set { SetProperty(ref _name, value); } + } + + private string _description; + public string Description + { + get { return _description; } + + set { SetProperty(ref _description, value); } + } + + private string _categoryId; + public ICommand Command { get; } + + public UpsertCategoryViewModel() + { + Command = new RelayCommand(async () => await UpsertCategoryAsync()); + } + + public void Initialize(string categoryId) + { + _categoryId = categoryId; + } + + public async Task LoadAsync() + { + if (_categoryId is null) + return; + + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await client.GetAsync(Api.CategoryById.FormatRoute(_categoryId)); + if (response.IsSuccessStatusCode) + { + var categoryData = await response.DeserializeObjectAsync>(); + Name = categoryData.Data.Name; + Description = categoryData.Data.Description; + } + } + catch { } + } + } + + public async Task UpsertCategoryAsync() + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + string message; + if (_categoryId is null) + message = await AddAsync(client); + else + message = await EditAsync(client); + + await new MessageDialog(message).ShowAsync(); + } + } + + private async Task AddAsync(HttpClient client) + { + var category = new UpsertCategory + { + Name = this.Name, + Description = this.Description + }; + try + { + var response = await client.PostAsJsonAsync(Api.Categories, category); + if (response.IsSuccessStatusCode) + return "Category added successfully!"; + else + return "Failed to add the category!"; + } + catch(Exception ex) + { + return $"error: {ex.Message}"; + } + } + + private async Task EditAsync(HttpClient client) + { + var category = new UpsertCategory + { + Name = this.Name, + Description = this.Description + }; + try + { + var response = await client.PutAsJsonAsync(Api.CategoryById.FormatRoute(_categoryId), category); + if (response.IsSuccessStatusCode) + return "Category edited successfully!"; + else + return "Failed to edit the category!"; + } + catch (Exception ex) + { + return $"error: {ex.Message}"; + } + } + } +} diff --git a/PCUT/PCUT/ViewModels/UpsertMetadataViewModel.cs b/PCUT/PCUT/ViewModels/UpsertMetadataViewModel.cs new file mode 100644 index 0000000..9d0fd1f --- /dev/null +++ b/PCUT/PCUT/ViewModels/UpsertMetadataViewModel.cs @@ -0,0 +1,130 @@ +using Http.Core; +using PCUT.Entities; +using System; +using System.Threading.Tasks; +using Http.Core.Extensions; +using static Http.Core.Constants.HttpConstants; +using Windows.UI.Popups; +using System.Text; +using PCUT.Entities.ApiResponse; +using PCUT.Models.Categories; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; + +namespace PCUT.ViewModels +{ + public class UpsertMetadataViewModel : UpsertViewModel + { + public UpsertMetadataViewModel() : base("Metadata") + { + } + + private string _categoryId; + public string CategoryId + { + get { return _categoryId; } + set { SetProperty(ref _categoryId, value); } + } + + private string _type; + public string Type + { + get { return _type; } + set + { + SetProperty(ref _type, value); + Entity = value; + } + } + + private string _name; + public string Name + { + get { return _name; } + set { SetProperty(ref _name, value); } + } + + public async Task UpsertMetadataAsync() + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + string message; + if (Mode == AddMode) + message = await AddAsync(client); + else + message = await EditAsync(client); + + await new MessageDialog(message).ShowAsync(); + } + } + + private async Task AddAsync(HttpClient client) + { + var request = new UpsertMetadata + { + Type = Type.ToUpper(), + Value = Name, + CategoryId = CategoryId + }; + try + { + var response = await client.PostAsJsonAsync(Api.Metadata, request); + return response.IsSuccessStatusCode ? "Metadata added successfully!" : "Failed to add the metadata!"; + } + catch (Exception ex) + { + return $"error: {ex.Message}"; + } + } + + private async Task EditAsync(HttpClient client) + { + try + { + var request = new UpsertMetadata + { + Value = Name + }; + var response = await client.PutAsJsonAsync(Api.MetadataById.FormatRoute(Id), request); + return response.IsSuccessStatusCode ? "Metadata edited successfully!" : "Failed to edit the metadata!"; + } + catch (Exception ex) + { + return $"error: {ex.Message}"; + } + } + + protected override async Task LoadAsync(string id) + { + using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var url = CreateFilterUrl(id); + var response = await client.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var content = await response.DeserializeObjectAsync>>(); + if (content.Data != null && content.Data.Any()) + { + var metadata = content.Data.First(); + Name = metadata.Value; + } + } + } + catch { } + } + } + + private string CreateFilterUrl(string id) + { + var filter = new StringBuilder().AppendFilter("_id", id) + .BuildFilter() + .ToString(); + return PathBuilder.FromRouteTemplate(Api.Metadata) + .AddQuery("filter", filter) + .Build(); + } + } +} diff --git a/PCUT/PCUT/ViewModels/UpsertUserViewModel.cs b/PCUT/PCUT/ViewModels/UpsertUserViewModel.cs new file mode 100644 index 0000000..08a3c43 --- /dev/null +++ b/PCUT/PCUT/ViewModels/UpsertUserViewModel.cs @@ -0,0 +1,345 @@ + +using Http.Core; +using Http.Core.Extensions; +using PCUT.Entities; +using PCUT.Helpers; +using PCUT.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.Windows.Input; +using static Http.Core.Constants.HttpConstants; +using PCUT.Entities.ApiResponse; +using Windows.Foundation; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml; +using System.ComponentModel; +using Newtonsoft.Json.Linq; + +namespace PCUT.ViewModels +{ + internal class UpsertUserViewModel : NotificationBase + { + public event UpsertProcessedEventHandler Processed; + + private const string AdminRole = "admin"; + private const string UserRole = "user"; + private const string ActiveStatus = "Active"; + private const string InactiveStatus = "Inactive"; + private const string AddTitle = "ADD USER"; + private const string EditTitle = "EDIT USER"; + + public List Roles { get; } = new List { AdminRole, UserRole }; + public List Statuses { get; } = new List { ActiveStatus, InactiveStatus }; + + public DateTimeOffset MinimumDate { get; } + public ICommand Command { get; } + + private UserProfile _profile; + + public UpsertUserViewModel() + { + _isActive = true; + var minimumDate = DateTimeOffset.Now.AddDays(1); + minimumDate -= minimumDate.TimeOfDay; + MinimumDate = minimumDate; + _expiredAt = minimumDate; + Command = new RelayCommand(async () => await UpsertUserAsync()); + } + + private bool _addMode; // true: add, false: edit + public bool AddMode + { + get { return _addMode; } + set + { + SetProperty(ref _addMode, value); + Title = value ? AddTitle : EditTitle; + } + } + + private string _title; + public string Title + { + get { return _title; } + private set { SetProperty(ref _title, value); } + } + + private string _accountName; + public string AccountName + { + get { return _accountName; } + set { SetProperty(ref _accountName, value); } + } + + private string _username; + public string Username + { + get { return _username; } + set { SetProperty(ref _username, value); } + } + + private string _phone; + public string Phone + { + get { return _phone; } + set { SetProperty(ref _phone, value); } + } + + private string _role; + public string Role + { + get { return _role; } + set { SetProperty(ref _role, value); } + } + + private bool _isActive; + public bool IsActive + { + get { return _isActive; } + set { SetProperty(ref _isActive, value); } + } + + private bool _isPermittedEdit; + public bool IsPermittedEdit + { + get { return _isPermittedEdit; } + set { SetProperty(ref _isPermittedEdit, value); } + } + + private DateTimeOffset? _expiredAt; + public DateTimeOffset? ExpiredAt + { + get { return _expiredAt; } + set + { + if (value != null) + value -= value?.TimeOfDay; + SetProperty(ref _expiredAt, value); + } + } + + private string _description; + public string Description + { + get { return _description; } + set { SetProperty(ref _description, value); } + } + + private string _password; + public string Password + { + get { return _password; } + set { SetProperty(ref _password, value); } + } + + public ObservableCollection Permissions { get; } = new ObservableCollection(); + private readonly HashSet _currentPermission = new HashSet(); + + private void OnRoleChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == nameof(Role)) + { + foreach (var permission in Permissions) + { + permission.PropertyChanged -= OnPermissionSelectedChanged; + permission.IsSelected = Role == AdminRole || _currentPermission.Contains(permission.Category.Id); + permission.PropertyChanged += OnPermissionSelectedChanged; + } + IsPermittedEdit = Role == AdminRole || (_profile.IsPermittedEdit ?? false); + } + } + + private void OnPermissionSelectedChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == nameof(Permission.IsSelected)) + { + var permission = sender as Permission; + if (permission.IsSelected) + _currentPermission.Add(permission.Category.Id); + else + _currentPermission.Remove(permission.Category.Id); + } + } + + public async Task LoadCategoriesAsync() + { + var categories = await GetCategoryAsync(); + foreach (var permission in categories.Select(category => new Permission(category))) + { + permission.PropertyChanged += OnPermissionSelectedChanged; + Permissions.Add(permission); + } + PropertyChanged += OnRoleChanged; + } + + private static async Task> GetCategoryAsync() + { + using (HttpClient client = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + return await client.GetCategoriesAsync(); + } + } + + public async Task LoadUserAsync(string userId) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.GetAsync(Api.UserById.FormatRoute(userId)); + if (response.IsSuccessStatusCode) + { + var apiResponse = await response.DeserializeObjectAsync>(); + _profile = apiResponse.Data; + + foreach (var permissionId in _profile.Permissions) + _currentPermission.Add(permissionId); + + AccountName = _profile.AccountName; + Username = _profile.UserName; + Phone = _profile.Phone; + IsActive = _profile.IsActive; + Role = _profile.Role; + IsPermittedEdit = _profile.Role == AdminRole || (_profile.IsPermittedEdit ?? false); + ExpiredAt = _profile.ExpiredAt?.ToLocalTime(); + Description = _profile.Description; + } + } + catch (Exception ex) + { + } + } + } + + private async Task UpsertUserAsync() + { + var success = false; + if (await ValidateUserAsync()) + { + var user = new UpsertUser + { + AccountName = AccountName, + Username = Username, + Phone = Phone, + Role = Role, + IsActive = IsActive, + IsPermittedEdit = IsPermittedEdit, + ExpiredAt = ExpiredAt?.ToUniversalTime().ToString("o"), + Password = !string.IsNullOrEmpty(Password) ? Password : null, + Permissions = Permissions.Where(c => c.IsSelected).Select(c => c.Category.Id).ToList(), + Description = Description, + }; + success = await UpsertUserAsync(_addMode ? Api.Users : Api.UserById.FormatRoute(_profile?.Id), user); + } + Processed?.Invoke(this, new UpsertProcessedEventArgs(_addMode ? "add" : "edit", success, _profile?.Id)); + } + + private async Task ValidateUserAsync() + { + if (string.IsNullOrEmpty(Username)) + { + await ShowDialogAsync("Cần nhập username!"); + return false; + } + + if (_addMode && string.IsNullOrEmpty(Password)) + { + await ShowDialogAsync("Cần nhập password!"); + return false; + } + + if (string.IsNullOrEmpty(Role)) + { + await ShowDialogAsync("Cần chọn role!"); + return false; + } + + var selectedCategories = Permissions.Where(c => c.IsSelected); + if (Role == UserRole && !selectedCategories.Any()) + { + await ShowDialogAsync("Chọn ít nhất 1 danh mục cho user!"); + return false; + } + + return true; + } + + private async Task UpsertUserAsync(string path, object user) + { + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await (_addMode ? httpClient.PostAsJsonAsync(path, user) : httpClient.PutAsJsonAsync(path, user, ignoreNull: true)); + if (response.IsSuccessStatusCode) + { + await ShowDialogAsync($"Account {(_addMode ? "created" : "updated")} successfully!", $"{(_addMode ? "Add" : "Edit")} successful!"); + } + else + { + var errorMessage = await response.DeserializeObjectAsync(ensureSuccess: false); + await ShowDialogAsync(errorMessage.ErrorMessage); + return false; + } + } + catch (Exception ex) + { + await ShowDialogAsync(ex.Message); + return false; + } + } + return true; + } + + private IAsyncOperation ShowDialogAsync(string message, string title = null) + { + var errorDialog = new ContentDialog() + { + Title = title, + Content = message, + PrimaryButtonText = "OK" + }; + errorDialog.PrimaryButtonStyle = Application.Current.Resources["PrimaryDialogButton"] as Style; + return errorDialog.ShowAsync(); + } + } + + public delegate void UpsertProcessedEventHandler(object sender, UpsertProcessedEventArgs args); + public class UpsertProcessedEventArgs : EventArgs + { + public string Mode { get; } + public bool Successed { get; } + public string Id { get; } + + public UpsertProcessedEventArgs(string mode, bool successed, string id = null) + { + Mode = mode; + Successed = successed; + Id = id; + } + } + + public class Permission : NotificationBase + { + public readonly Category Category; + + public string Name { get => Category.Name; } + + private bool _isSelected; + public bool IsSelected + { + get { return _isSelected; } + set { SetProperty(ref _isSelected, value); } + } + + public Permission(Category category, bool isSelected = false) + { + Category = category; + _isSelected = isSelected; + } + } +} diff --git a/PCUT/PCUT/ViewModels/UpsertViewModel.cs b/PCUT/PCUT/ViewModels/UpsertViewModel.cs new file mode 100644 index 0000000..4f9a326 --- /dev/null +++ b/PCUT/PCUT/ViewModels/UpsertViewModel.cs @@ -0,0 +1,66 @@ +using PCUT.Models; +using System.Threading.Tasks; + +namespace PCUT.ViewModels +{ + public abstract class UpsertViewModel : NotificationBase + { + protected const string AddMode = "ADD"; + protected const string EditMode = "EDIT"; + + public string Title => $"{Mode} {Entity.ToUpper()}"; + + private string _mode; + public string Mode + { + get { return _mode; } + private set + { + SetProperty(ref _mode, value); + RaisePropertyChanged(nameof(Title)); + } + } + + private string _entity; + public string Entity + { + get { return _entity; } + set + { + SetProperty(ref _entity, value); + RaisePropertyChanged(nameof(Title)); + } + } + + private TKey _id; + public TKey Id + { + get { return _id; } + set + { + SetProperty(ref _id, value); + Mode = value == null ? AddMode : EditMode; + } + } + + protected UpsertViewModel(string entity) + { + Entity = entity; + } + + public void Initialize(TKey id) + { + Id = id; + } + + public async Task LoadAsync() + { + if (Mode == EditMode) + { + await LoadAsync(Id); + } + } + + protected abstract Task LoadAsync(TKey id); + } +} diff --git a/PCUT/PCUT/ViewModels/UserViewModel.cs b/PCUT/PCUT/ViewModels/UserViewModel.cs new file mode 100644 index 0000000..3b9991f --- /dev/null +++ b/PCUT/PCUT/ViewModels/UserViewModel.cs @@ -0,0 +1,94 @@ +using Http.Core; +using Http.Core.Extensions; +using PCUT.Entities.ApiResponse; +using PCUT.Extensions; +using PCUT.Models; +using PCUT.Models.Users; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.ApplicationModel.Core; +using static Http.Core.Constants.HttpConstants; + +namespace PCUT.ViewModels +{ + public class UserViewModel : NotificationBase + { + public PaginationViewModel Pagination { get; } = new PaginationViewModel(withSearch: true); + public SelectableCollection FilteredUsers { get; } + public UserViewModel() + { + FilteredUsers = new SelectableCollection(); + + Pagination.TextSearched += async (sender, args) => await SearchCategoryAsync(args.Text); + Pagination.PageChanged += OnPageChanged; + } + + private async void OnPageChanged(object sender, PageChangedEventArgs args) + { + await LoadUserAsync(args.Page, args.PageSize, Pagination.SearchText); + } + + public async Task SearchCategoryAsync(string searchText) + { + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => + { + await LoadUserAsync(Pagination.Page, Pagination.PageSize, searchText); + }); + } + + public async Task Reload() + { + await LoadUserAsync(Pagination.Page, Pagination.PageSize, Pagination.SearchText); + } + + public async Task LoadUserAsync(int page, int pageSize, string searchText) + { + var url = CreateFilterUrl(page, pageSize, searchText); + using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient)) + { + try + { + var response = await httpClient.GetAsync(url); + if (response.IsSuccessStatusCode) + { + var apiResponse = await response.DeserializeObjectAsync>>(); + var data = apiResponse.Data; + Pagination.Page = apiResponse.Pagination.Page; + Pagination.TotalPage = apiResponse.Pagination.TotalPages; + Pagination.TotalRecords = apiResponse.Pagination.TotalRecords; + + int displayId = (apiResponse.Pagination.Page - 1) * pageSize + 1; + var users = data.Select(user => + { + user.DisplayId = displayId.ToString(); + displayId++; + return user; + }); + FilteredUsers.Load(users); + } + } + catch (Exception ex) + { + + } + } + } + + private string CreateFilterUrl(int page, int pageSize, string searchText) + { + var builder = PathBuilder.FromRouteTemplate(Api.Users) + .AddQuery("page", page.ToString()) + .AddQuery("pageSize", pageSize.ToString()); + if (!string.IsNullOrEmpty(searchText)) + { + var filter = new StringBuilder().AppendFilter("username", "cn", searchText).BuildFilter().ToString(); + builder.AddQuery("filter", filter); + } + return builder.Build(); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..a5b9864 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# pcut_uwp + +# require: +- Visual Studio: Universal Windows Platform development + - C++ Universal Windows Platform tools \ No newline at end of file